From 1121c6663f325502e867eea76a144dfe55c522b3 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/7] 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 78fc02999ef..de99c35e27e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1262,7 +1262,11 @@ class PurchaseInvoice(BuyingController): 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 145e2ba3b3e..2477e443c4a 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 e408cc24d1e..55932e9b787 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 8ede2670d38..f6846ea3cee 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 f797374d64e..31b6f391ba4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -822,6 +822,8 @@ class BuyingController(SubcontractingController): "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 feaac948099..2a97061a6a2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,3 +374,4 @@ 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.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 00000000000..e4311a8879d --- /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 afcb00141a1..42b70a08222 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -837,7 +837,11 @@ class PurchaseReceipt(BuyingController): 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 3bb186736d8adbf83cdb1d75bb4f5f9db8af8532 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/7] 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 e4311a8879d..e5386c1de0a 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 957eabf53edc962393fa6c03acfebd1bcd587ff7 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/7] 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 2aacd86e3d2..07d401de8c1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1532,7 +1532,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` @@ -1629,6 +1629,7 @@ def get_stock_ledger_entries( if extra_cond: conditions += f"{extra_cond}" + # nosemgrep return frappe.db.sql( """ select *, posting_datetime as "timestamp" From 193d7981eabeea0b57c83255e0a76fa01fc20b43 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/7] 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 07d401de8c1..8f9f39c7a7b 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1745,7 +1745,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 @@ -1825,7 +1825,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} @@ -1992,7 +1992,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, @@ -2014,7 +2014,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 e185a06a1519214269fa3941e8d38c2609001321 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/7] 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 8f9f39c7a7b..556aac09abd 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1991,7 +1991,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 @@ -2008,12 +2008,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 ( @@ -2034,7 +2034,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 f5a4ec129be3233bd98832900531414d31dd86b1 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/7] 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 2a97061a6a2..637ce3dbed1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,4 +374,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 e5386c1de0a..662858e52a4 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 From 5d5ec2ab7c2ecee1e616ca3c54e66b9f48243806 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 4 Sep 2024 03:08:30 +0530 Subject: [PATCH 7/7] chore: resolve test failing --- erpnext/patches.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 637ce3dbed1..2a97061a6a2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,5 +374,4 @@ 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