mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-20 15:32:14 +00:00
fix: service item capitalization (#42188)
* feat: capitalize with service expenses only
* chore: added test
* refactor: removed Capitalized In field from asset doc
(cherry picked from commit 81e0b96c30)
# Conflicts:
# erpnext/assets/doctype/asset/asset.py
This commit is contained in:
@@ -187,7 +187,7 @@ frappe.ui.form.on("Asset", {
|
|||||||
if (frm.doc.docstatus == 0) {
|
if (frm.doc.docstatus == 0) {
|
||||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||||
|
|
||||||
if (frm.doc.is_composite_asset && !frm.doc.capitalized_in) {
|
if (frm.doc.is_composite_asset) {
|
||||||
$(".primary-action").prop("hidden", true);
|
$(".primary-action").prop("hidden", true);
|
||||||
$(".form-message").text("Capitalize this asset to confirm");
|
$(".form-message").text("Capitalize this asset to confirm");
|
||||||
|
|
||||||
@@ -511,6 +511,8 @@ frappe.ui.form.on("Asset", {
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
args: {
|
args: {
|
||||||
asset: frm.doc.name,
|
asset: frm.doc.name,
|
||||||
|
asset_name: frm.doc.asset_name,
|
||||||
|
item_code: frm.doc.item_code,
|
||||||
},
|
},
|
||||||
method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization",
|
method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization",
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
|
|||||||
@@ -75,8 +75,7 @@
|
|||||||
"purchase_amount",
|
"purchase_amount",
|
||||||
"default_finance_book",
|
"default_finance_book",
|
||||||
"depr_entry_posting_status",
|
"depr_entry_posting_status",
|
||||||
"amended_from",
|
"amended_from"
|
||||||
"capitalized_in"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -222,7 +221,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!(doc.is_composite_asset && !doc.capitalized_in)",
|
"depends_on": "eval:!doc.is_composite_asset",
|
||||||
"fieldname": "gross_purchase_amount",
|
"fieldname": "gross_purchase_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Gross Purchase Amount",
|
"label": "Gross Purchase Amount",
|
||||||
@@ -508,14 +507,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Composite Asset"
|
"label": "Is Composite Asset"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "capitalized_in",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Capitalized In",
|
|
||||||
"options": "Asset Capitalization",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.docstatus > 0",
|
"depends_on": "eval:doc.docstatus > 0",
|
||||||
"fieldname": "total_asset_cost",
|
"fieldname": "total_asset_cost",
|
||||||
@@ -589,7 +580,7 @@
|
|||||||
"link_fieldname": "target_asset"
|
"link_fieldname": "target_asset"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-05-21 13:46:21.066483",
|
"modified": "2024-07-07 22:27:14.733839",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset",
|
"name": "Asset",
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ class Asset(AccountsController):
|
|||||||
available_for_use_date: DF.Date | None
|
available_for_use_date: DF.Date | None
|
||||||
booked_fixed_asset: DF.Check
|
booked_fixed_asset: DF.Check
|
||||||
calculate_depreciation: DF.Check
|
calculate_depreciation: DF.Check
|
||||||
capitalized_in: DF.Link | None
|
|
||||||
company: DF.Link
|
company: DF.Link
|
||||||
comprehensive_insurance: DF.Data | None
|
comprehensive_insurance: DF.Data | None
|
||||||
cost_center: DF.Link | None
|
cost_center: DF.Link | None
|
||||||
@@ -162,7 +161,11 @@ class Asset(AccountsController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_cancellation()
|
self.validate_cancellation()
|
||||||
self.cancel_movement_entries()
|
self.cancel_movement_entries()
|
||||||
|
<<<<<<< HEAD
|
||||||
self.cancel_capitalization()
|
self.cancel_capitalization()
|
||||||
|
=======
|
||||||
|
self.reload()
|
||||||
|
>>>>>>> 81e0b96c30 (fix: service item capitalization (#42188))
|
||||||
self.delete_depreciation_entries()
|
self.delete_depreciation_entries()
|
||||||
cancel_asset_depr_schedules(self)
|
cancel_asset_depr_schedules(self)
|
||||||
self.set_status()
|
self.set_status()
|
||||||
@@ -524,6 +527,7 @@ class Asset(AccountsController):
|
|||||||
movement = frappe.get_doc("Asset Movement", movement.get("name"))
|
movement = frappe.get_doc("Asset Movement", movement.get("name"))
|
||||||
movement.cancel()
|
movement.cancel()
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
def cancel_capitalization(self):
|
def cancel_capitalization(self):
|
||||||
asset_capitalization = frappe.db.get_value(
|
asset_capitalization = frappe.db.get_value(
|
||||||
"Asset Capitalization",
|
"Asset Capitalization",
|
||||||
@@ -534,6 +538,8 @@ class Asset(AccountsController):
|
|||||||
asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
|
asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
|
||||||
asset_capitalization.cancel()
|
asset_capitalization.cancel()
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> 81e0b96c30 (fix: service item capitalization (#42188))
|
||||||
def delete_depreciation_entries(self):
|
def delete_depreciation_entries(self):
|
||||||
if self.calculate_depreciation:
|
if self.calculate_depreciation:
|
||||||
for row in self.get("finance_books"):
|
for row in self.get("finance_books"):
|
||||||
@@ -872,10 +878,15 @@ def create_asset_repair(asset, asset_name):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_asset_capitalization(asset):
|
def create_asset_capitalization(asset, asset_name, item_code):
|
||||||
asset_capitalization = frappe.new_doc("Asset Capitalization")
|
asset_capitalization = frappe.new_doc("Asset Capitalization")
|
||||||
asset_capitalization.update(
|
asset_capitalization.update(
|
||||||
{"target_asset": asset, "capitalization_method": "Choose a WIP composite asset"}
|
{
|
||||||
|
"target_asset": asset,
|
||||||
|
"capitalization_method": "Choose a WIP composite asset",
|
||||||
|
"target_asset_name": asset_name,
|
||||||
|
"target_item_code": item_code,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
return asset_capitalization
|
return asset_capitalization
|
||||||
|
|
||||||
|
|||||||
@@ -138,22 +138,10 @@ class AssetCapitalization(StockController):
|
|||||||
"Asset",
|
"Asset",
|
||||||
"Asset Movement",
|
"Asset Movement",
|
||||||
)
|
)
|
||||||
self.cancel_target_asset()
|
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
self.restore_consumed_asset_items()
|
self.restore_consumed_asset_items()
|
||||||
|
|
||||||
def on_trash(self):
|
|
||||||
frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None)
|
|
||||||
super().on_trash()
|
|
||||||
|
|
||||||
def cancel_target_asset(self):
|
|
||||||
if self.entry_type == "Capitalization" and self.target_asset:
|
|
||||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
|
||||||
asset_doc.db_set("capitalized_in", None)
|
|
||||||
if asset_doc.docstatus == 1:
|
|
||||||
asset_doc.cancel()
|
|
||||||
|
|
||||||
def set_title(self):
|
def set_title(self):
|
||||||
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
|
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
|
||||||
|
|
||||||
@@ -329,8 +317,12 @@ class AssetCapitalization(StockController):
|
|||||||
if not self.target_is_fixed_asset and not self.get("asset_items"):
|
if not self.target_is_fixed_asset and not self.get("asset_items"):
|
||||||
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
|
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
|
||||||
|
|
||||||
if not self.get("stock_items") and not self.get("asset_items"):
|
if not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")):
|
||||||
frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization"))
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def validate_item(self, item):
|
def validate_item(self, item):
|
||||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||||
@@ -617,7 +609,6 @@ class AssetCapitalization(StockController):
|
|||||||
asset_doc.purchase_date = self.posting_date
|
asset_doc.purchase_date = self.posting_date
|
||||||
asset_doc.gross_purchase_amount = total_target_asset_value
|
asset_doc.gross_purchase_amount = total_target_asset_value
|
||||||
asset_doc.purchase_amount = total_target_asset_value
|
asset_doc.purchase_amount = total_target_asset_value
|
||||||
asset_doc.capitalized_in = self.name
|
|
||||||
asset_doc.flags.ignore_validate = True
|
asset_doc.flags.ignore_validate = True
|
||||||
asset_doc.flags.asset_created_via_asset_capitalization = True
|
asset_doc.flags.asset_created_via_asset_capitalization = True
|
||||||
asset_doc.insert()
|
asset_doc.insert()
|
||||||
@@ -653,7 +644,6 @@ class AssetCapitalization(StockController):
|
|||||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||||
asset_doc.gross_purchase_amount = total_target_asset_value
|
asset_doc.gross_purchase_amount = total_target_asset_value
|
||||||
asset_doc.purchase_amount = total_target_asset_value
|
asset_doc.purchase_amount = total_target_asset_value
|
||||||
asset_doc.capitalized_in = self.name
|
|
||||||
asset_doc.flags.ignore_validate = True
|
asset_doc.flags.ignore_validate = True
|
||||||
asset_doc.save()
|
asset_doc.save()
|
||||||
|
|
||||||
|
|||||||
@@ -386,6 +386,56 @@ class TestAssetCapitalization(unittest.TestCase):
|
|||||||
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
||||||
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
||||||
|
|
||||||
|
def test_capitalize_only_service_item(self):
|
||||||
|
company = "_Test Company"
|
||||||
|
# Variables
|
||||||
|
|
||||||
|
service_rate = 500
|
||||||
|
service_qty = 2
|
||||||
|
service_amount = 1000
|
||||||
|
|
||||||
|
total_amount = 1000
|
||||||
|
|
||||||
|
wip_composite_asset = create_asset(
|
||||||
|
asset_name="Asset Capitalization WIP Composite Asset",
|
||||||
|
is_composite_asset=1,
|
||||||
|
warehouse="Stores - TCP1",
|
||||||
|
company=company,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create and submit Asset Captitalization
|
||||||
|
asset_capitalization = create_asset_capitalization(
|
||||||
|
entry_type="Capitalization",
|
||||||
|
capitalization_method="Choose a WIP composite asset",
|
||||||
|
target_asset=wip_composite_asset.name,
|
||||||
|
target_asset_location="Test Location",
|
||||||
|
service_qty=service_qty,
|
||||||
|
service_rate=service_rate,
|
||||||
|
service_expense_account="Expenses Included In Asset Valuation - _TC",
|
||||||
|
company=company,
|
||||||
|
submit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(asset_capitalization.service_items[0].amount, service_amount)
|
||||||
|
self.assertEqual(asset_capitalization.service_items_total, service_amount)
|
||||||
|
|
||||||
|
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
|
||||||
|
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
|
||||||
|
self.assertEqual(target_asset.purchase_amount, total_amount)
|
||||||
|
|
||||||
|
expected_gle = {
|
||||||
|
"_Test Fixed Asset - _TC": 1000.0,
|
||||||
|
"Expenses Included In Asset Valuation - _TC": -1000.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||||
|
self.assertEqual(actual_gle, expected_gle)
|
||||||
|
|
||||||
|
# Cancel Asset Capitalization and make test entries and status are reversed
|
||||||
|
asset_capitalization.cancel()
|
||||||
|
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
|
||||||
|
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
|
||||||
|
|
||||||
|
|
||||||
def create_asset_capitalization_data():
|
def create_asset_capitalization_data():
|
||||||
create_item("Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
|
create_item("Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import frappe
|
|
||||||
|
|
||||||
|
|
||||||
def execute():
|
|
||||||
cancelled_asset_capitalizations = frappe.get_all(
|
|
||||||
"Asset Capitalization",
|
|
||||||
filters={"docstatus": 2},
|
|
||||||
fields=["name", "target_asset"],
|
|
||||||
)
|
|
||||||
for asset_capitalization in cancelled_asset_capitalizations:
|
|
||||||
frappe.db.set_value("Asset", asset_capitalization.target_asset, "capitalized_in", None)
|
|
||||||
Reference in New Issue
Block a user