From f50d479bfd77bc27c3c309894474ff0d84160e8e Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:39:24 +0530 Subject: [PATCH] fix: exclude already consumed purchase receipt items from asset capitalization (#46329) * feat: link purchase receipt row item to capitalization * fix: avoid fetching already consumed stock and asset items during capitalization * fix(patch): added patch to link purchase receipt item to stock item child table * fix: added nosemgrep * refactor: rename to --- .../asset_capitalization.js | 11 +++- .../asset_capitalization.py | 66 +++++++++++++++---- .../asset_capitalization_stock_item.json | 17 +++-- .../asset_capitalization_stock_item.py | 1 + erpnext/patches.txt | 1 + ...t_row_item_to_capitalization_stock_item.py | 21 ++++++ 6 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 erpnext/patches/v15_0/set_purchase_receipt_row_item_to_capitalization_stock_item.py diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 4bda8f3d50a..68ba02c2c4e 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -143,14 +143,19 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } } - set_consumed_stock_items_tagged_to_wip_composite_asset(asset) { + set_consumed_stock_items_tagged_to_wip_composite_asset(target_asset) { var me = this; - if (asset) { + if (target_asset) { return me.frm.call({ method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_items_tagged_to_wip_composite_asset", args: { - asset: asset, + params: { + target_asset: target_asset, + finance_book: me.frm.doc.finance_book, + posting_date: me.frm.doc.posting_date, + posting_time: me.frm.doc.posting_time, + }, }, callback: function (r) { if (!r.exc && r.message) { diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index e6c017955c0..8ee96c1b1ae 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -846,7 +846,10 @@ def get_service_item_details(ctx): @frappe.whitelist() -def get_items_tagged_to_wip_composite_asset(asset): +def get_items_tagged_to_wip_composite_asset(params): + if isinstance(params, str): + params = json.loads(params) + fields = [ "item_code", "item_name", @@ -861,25 +864,66 @@ def get_items_tagged_to_wip_composite_asset(asset): "amount", "is_fixed_asset", "parent", + "name", ] pr_items = frappe.get_all( - "Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields + "Purchase Receipt Item", + filters={"wip_composite_asset": params.get("target_asset"), "docstatus": 1}, + fields=fields, ) stock_items = [] asset_items = [] + for d in pr_items: if not d.is_fixed_asset: - stock_items.append(frappe._dict(d)) + stock_item = process_stock_item(d) + if stock_item: + stock_items.append(stock_item) else: - asset_details = frappe.db.get_value( - "Asset", - {"item_code": d.item_code, "purchase_receipt": d.parent}, - ["name as asset", "asset_name"], - as_dict=1, - ) - d.update(asset_details) - asset_items.append(frappe._dict(d)) + asset_item = process_fixed_asset(d) + if asset_item: + asset_items.append(asset_item) return stock_items, asset_items + + +def process_stock_item(d): + stock_capitalized = frappe.db.exists( + "Asset Capitalization Stock Item", + { + "purchase_receipt_item": d.name, + "parentfield": "stock_items", + "parenttype": "Asset Capitalization", + "docstatus": 1, + }, + ) + + if stock_capitalized: + return None + + stock_item_data = frappe._dict(d) + stock_item_data.purchase_receipt_item = d.name + return stock_item_data + + +def process_fixed_asset(d): + asset_details = frappe.db.get_value( + "Asset", + { + "item_code": d.item_code, + "purchase_receipt": d.parent, + "status": ("not in", ["Draft", "Scrapped", "Sold", "Capitalized"]), + }, + ["name as asset", "asset_name", "company"], + as_dict=1, + ) + + if asset_details: + asset_details.update(d) + asset_details.update(get_consumed_asset_details(asset_details)) + d.update(asset_details) + + return frappe._dict(d) + return None diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json index 74a00e25a04..d5d0327916c 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json @@ -10,12 +10,13 @@ "column_break_3", "warehouse", "section_break_6", + "purchase_receipt_item", "stock_qty", - "stock_uom", "actual_qty", "column_break_9", "valuation_rate", "amount", + "stock_uom", "batch_and_serial_no_section", "serial_and_batch_bundle", "use_serial_batch_fields", @@ -53,14 +54,14 @@ { "fieldname": "section_break_6", "fieldtype": "Section Break", - "label": "Qty and Rate" + "label": "Purchase Details" }, { "columns": 1, "fieldname": "stock_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty", + "label": "Quantity", "non_negative": 1 }, { @@ -172,17 +173,25 @@ { "fieldname": "column_break_mbuv", "fieldtype": "Column Break" + }, + { + "fieldname": "purchase_receipt_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Receipt Item" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-06-26 17:06:22.564438", + "modified": "2025-03-05 12:46:01.074742", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Stock Item", "owner": "Administrator", "permissions": [], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py index 0f06cc7442e..c56f87b67fa 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py @@ -23,6 +23,7 @@ class AssetCapitalizationStockItem(Document): parent: DF.Data parentfield: DF.Data parenttype: DF.Data + purchase_receipt_item: DF.Data | None serial_and_batch_bundle: DF.Link | None serial_no: DF.Text | None stock_qty: DF.Float diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2dd0440c592..d83bfc6d1f3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -407,3 +407,4 @@ erpnext.patches.v15_0.recalculate_amount_difference_field erpnext.patches.v15_0.rename_sla_fields erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes erpnext.patches.v15_0.update_query_report +erpnext.patches.v15_0.set_purchase_receipt_row_item_to_capitalization_stock_item diff --git a/erpnext/patches/v15_0/set_purchase_receipt_row_item_to_capitalization_stock_item.py b/erpnext/patches/v15_0/set_purchase_receipt_row_item_to_capitalization_stock_item.py new file mode 100644 index 00000000000..f1d17d1da4d --- /dev/null +++ b/erpnext/patches/v15_0/set_purchase_receipt_row_item_to_capitalization_stock_item.py @@ -0,0 +1,21 @@ +import frappe + + +def execute(): + # nosemgrep + frappe.db.sql( + """ + UPDATE `tabAsset Capitalization Stock Item` ACSI + JOIN `tabAsset Capitalization` AC + ON ACSI.parent = AC.name + JOIN `tabPurchase Receipt Item` PRI + ON + PRI.item_code = ACSI.item_code + AND PRI.wip_composite_asset = AC.target_asset + SET + ACSI.purchase_receipt_item = PRI.name + WHERE + ACSI.purchase_receipt_item IS NULL + AND AC.docstatus = 1 + """ + )