From a1aaffbebe1617af2a7d1a51441d66c1b7b4a5e2 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:33:10 +0530 Subject: [PATCH 1/6] fix: link Purchase Invoice and Receipt Items to Asset --- .../purchase_invoice/purchase_invoice.py | 6 +- erpnext/assets/doctype/asset/asset.js | 5 ++ erpnext/assets/doctype/asset/asset.json | 24 +++++-- erpnext/assets/doctype/asset/asset.py | 2 + erpnext/controllers/buying_controller.py | 2 + erpnext/patches.txt | 1 + .../v15_0/link_purchase_item_to_asset_doc.py | 68 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 6 +- 8 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 44d8a4070171..0abc925ec347 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1267,7 +1267,11 @@ def make_provisional_gl_entry(self, gl_entries, item): def update_gross_purchase_amount_for_linked_assets(self, item): assets = frappe.db.get_all( "Asset", - filters={"purchase_invoice": self.name, "item_code": item.item_code}, + filters={ + "purchase_invoice": self.name, + "item_code": item.item_code, + "purchase_invoice_item": ("in", [item.name, ""]), + }, fields=["name", "asset_quantity"], ) for asset in assets: diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index eea44697d638..9642403d7266 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -670,6 +670,11 @@ frappe.ui.form.on("Asset", { if (item.asset_location) { frm.set_value("location", item.asset_location); } + if (doctype === "Purchase Receipt") { + frm.set_value("purchase_receipt_item", item.name); + } else if (doctype === "Purchase Invoice") { + frm.set_value("purchase_invoice_item", item.name); + } }); }, diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 07228c5f2551..92537e0e3cc7 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -33,14 +33,16 @@ "dimension_col_break", "purchase_details_section", "purchase_receipt", + "purchase_receipt_item", "purchase_invoice", + "purchase_invoice_item", + "purchase_date", "available_for_use_date", - "total_asset_cost", - "additional_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", - "purchase_date", + "additional_asset_cost", + "total_asset_cost", "section_break_23", "calculate_depreciation", "column_break_33", @@ -536,6 +538,20 @@ "fieldname": "opening_number_of_booked_depreciations", "fieldtype": "Int", "label": "Opening Number of Booked Depreciations" + }, + { + "fieldname": "purchase_receipt_item", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Receipt Item", + "options": "Purchase Receipt Item" + }, + { + "fieldname": "purchase_invoice_item", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Invoice Item", + "options": "Purchase Invoice Item" } ], "idx": 72, @@ -579,7 +595,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2024-08-01 16:39:09.340973", + "modified": "2024-08-26 23:28:29.095139", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0fdead3470b4..82d607a1f4d1 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -94,7 +94,9 @@ class Asset(AccountsController): purchase_amount: DF.Currency purchase_date: DF.Date | None purchase_invoice: DF.Link | None + purchase_invoice_item: DF.Link | None purchase_receipt: DF.Link | None + purchase_receipt_item: DF.Link | None split_from: DF.Link | None status: DF.Literal[ "Draft", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 0f53322bbd8c..9b095df2d0c0 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -824,6 +824,8 @@ def make_asset(self, row, is_grouped_asset=False): "asset_quantity": asset_quantity, "purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None, "purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None, + "purchase_receipt_item": row.name if self.doctype == "Purchase Receipt" else None, + "purchase_invoice_item": row.name if self.doctype == "Purchase Invoice" else None, } ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4b62284979e4..50d9ebf299c1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -377,3 +377,4 @@ erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.set_standard_stock_entry_type +erpnext.patches.v15_0.link_purchase_item_to_asset_doc diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py new file mode 100644 index 000000000000..e4311a8879dc --- /dev/null +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -0,0 +1,68 @@ +import frappe + + +def execute(): + if frappe.db.has_column("Asset", "purchase_invoice_item") and frappe.db.has_column( + "Asset", "purchase_receipt_item" + ): + # Get all assets with their related Purchase Invoice and Purchase Receipt + assets = frappe.get_all( + "Asset", + filters={"docstatus": 0}, + fields=[ + "name", + "item_code", + "purchase_invoice", + "purchase_receipt", + "gross_purchase_amount", + "asset_quantity", + "purchase_invoice_item", + "purchase_receipt_item", + ], + ) + + for asset in assets: + # Get Purchase Invoice Items + if asset.purchase_invoice and not asset.purchase_invoice_item: + purchase_invoice_item = get_linked_item( + "Purchase Invoice Item", + asset.purchase_invoice, + asset.item_code, + asset.gross_purchase_amount, + asset.asset_quantity, + ) + frappe.db.set_value("Asset", asset.name, "purchase_invoice_item", purchase_invoice_item) + + # Get Purchase Receipt Items + if asset.purchase_receipt and not asset.purchase_receipt_item: + purchase_receipt_item = get_linked_item( + "Purchase Receipt Item", + asset.purchase_receipt, + asset.item_code, + asset.gross_purchase_amount, + asset.asset_quantity, + ) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_item", purchase_receipt_item) + + +def get_linked_item(doctype, parent, item_code, amount, quantity): + items = frappe.get_all( + doctype, + filters={ + "parenttype": doctype.replace(" Item", ""), + "parent": parent, + "item_code": item_code, + }, + fields=["name", "amount", "qty", "landed_cost_voucher_amount"], + ) + if len(items) == 1: + # If only one item exists, return it directly + return items[0].name + + for item in items: + landed_cost = item.get("landed_cost_voucher_amount", 0) + if item.amount + landed_cost == amount and item.qty == quantity: + return item.name + + # If no exact match, return None + return None diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index e611361a3e50..0970c65b9f7d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -828,7 +828,11 @@ def make_tax_gl_entries(self, gl_entries, via_landed_cost_voucher=False): def update_assets(self, item, valuation_rate): assets = frappe.db.get_all( "Asset", - filters={"purchase_receipt": self.name, "item_code": item.item_code}, + filters={ + "purchase_receipt": self.name, + "item_code": item.item_code, + "purchase_receipt_item": ("in", [item.name, ""]), + }, fields=["name", "asset_quantity"], ) From 83fe78bcda462905fc4e632db20140bfffed69a9 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:10:12 +0530 Subject: [PATCH 2/6] fix: improve asset item matching logic --- .../patches/v15_0/link_purchase_item_to_asset_doc.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py index e4311a8879dc..e5386c1de0ac 100644 --- a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -53,7 +53,7 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): "parent": parent, "item_code": item_code, }, - fields=["name", "amount", "qty", "landed_cost_voucher_amount"], + fields=["name", "rate", "amount", "qty", "landed_cost_voucher_amount"], ) if len(items) == 1: # If only one item exists, return it directly @@ -61,8 +61,13 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): for item in items: landed_cost = item.get("landed_cost_voucher_amount", 0) - if item.amount + landed_cost == amount and item.qty == quantity: - return item.name + # Check if the asset is grouped + if quantity > 1: + if item.amount + landed_cost == amount and item.qty == quantity: + return item.name + else: + if item.rate + landed_cost == amount: + return item.name # If no exact match, return None return None From 3baffffd1b97ef13c2bd1dc7a0a99f4f335ab22c Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:30:47 +0530 Subject: [PATCH 3/6] chore: resolved linter warnings with #nosemgrep --- erpnext/stock/stock_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 804dfe34b355..7724869ff454 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1533,7 +1533,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc operator = "<=" voucher_condition = f"and creation < '{creation}'" - sle = frappe.db.sql( + sle = frappe.db.sql( # nosemgrep f""" select *, posting_datetime as "timestamp" from `tabStock Ledger Entry` @@ -1630,6 +1630,7 @@ def get_stock_ledger_entries( if extra_cond: conditions += f"{extra_cond}" + # nosemgrep return frappe.db.sql( """ select *, posting_datetime as "timestamp" From 272f42eaf2f9ca34199547fc5aab92f810f8f7cc Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:40:01 +0530 Subject: [PATCH 4/6] chore: linters/semgrep check --- erpnext/stock/stock_ledger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7724869ff454..f985ea4b8125 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1746,7 +1746,7 @@ def get_valuation_rate( return batch_obj.get_incoming_rate() # Get valuation rate from last sle for the same item and warehouse - if last_valuation_rate := frappe.db.sql( + if last_valuation_rate := frappe.db.sql( # nosemgrep """select valuation_rate from `tabStock Ledger Entry` force index (item_warehouse) where @@ -1826,7 +1826,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): detail = next_stock_reco_detail[0] datetime_limit_condition = get_datetime_limit_condition(detail) - frappe.db.sql( + frappe.db.sql( # nosemgrep f""" update `tabStock Ledger Entry` set qty_after_transaction = qty_after_transaction + {qty_shift} @@ -1993,7 +1993,7 @@ def is_negative_with_precision(neg_sle, is_batch=False): def get_future_sle_with_negative_qty(args): - return frappe.db.sql( + return frappe.db.sql( # nosemgrep """ select qty_after_transaction, posting_date, posting_time, @@ -2015,7 +2015,7 @@ def get_future_sle_with_negative_qty(args): def get_future_sle_with_negative_batch_qty(args): - return frappe.db.sql( + return frappe.db.sql( # nosemgrep """ with batch_ledger as ( select From a84a5b43e29c7dc7dbf4ce254c4fd5912c10d5b2 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:48:41 +0530 Subject: [PATCH 5/6] refactor: rename to in SLE query functions --- erpnext/stock/stock_ledger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f985ea4b8125..9c27ce3f6f82 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1992,7 +1992,7 @@ def is_negative_with_precision(neg_sle, is_batch=False): return qty_deficit < 0 and abs(qty_deficit) > 0.0001 -def get_future_sle_with_negative_qty(args): +def get_future_sle_with_negative_qty(sle_args): return frappe.db.sql( # nosemgrep """ select @@ -2009,12 +2009,12 @@ def get_future_sle_with_negative_qty(args): order by posting_date asc, posting_time asc limit 1 """, - args, + sle_args, as_dict=1, ) -def get_future_sle_with_negative_batch_qty(args): +def get_future_sle_with_negative_batch_qty(sle_args): return frappe.db.sql( # nosemgrep """ with batch_ledger as ( @@ -2035,7 +2035,7 @@ def get_future_sle_with_negative_batch_qty(args): and posting_datetime >= %(posting_datetime)s limit 1 """, - args, + sle_args, as_dict=1, ) From 418f7e97b926a8ce9381c49b6f82810365ccd051 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 3 Sep 2024 03:35:19 +0530 Subject: [PATCH 6/6] chore: patch correction --- erpnext/patches.txt | 1 + erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 571b1b363a0d..8b155d70c6fa 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -378,4 +378,5 @@ erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type +erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment erpnext.patches.v15_0.link_purchase_item_to_asset_doc diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py index e5386c1de0ac..662858e52a45 100644 --- a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -65,9 +65,10 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): if quantity > 1: if item.amount + landed_cost == amount and item.qty == quantity: return item.name + elif item.qty == quantity: + return item.name else: - if item.rate + landed_cost == amount: + if item.rate + (landed_cost / item.qty) == amount: return item.name - # If no exact match, return None - return None + return items[0].name if items else None