From 85ba96e0f3377b48da6ac9085de7a7becf70a814 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 15:11:01 +0530 Subject: [PATCH 01/12] fix: slow stock transactions (backport #45025) (#45027) fix: slow stock transactions (#45025) (cherry picked from commit e92af10f146491b6e559a61db66dca29311ae7c4) Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_balance.py | 2 +- erpnext/stock/stock_ledger.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 23c21ed096e..6a2f818f7ae 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -80,7 +80,7 @@ def get_balance_qty_from_sle(item_code, warehouse): balance_qty = frappe.db.sql( """select qty_after_transaction from `tabStock Ledger Entry` where item_code=%s and warehouse=%s and is_cancelled=0 - order by posting_date desc, posting_time desc, creation desc + order by posting_datetime desc, creation desc limit 1""", (item_code, warehouse), ) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 5aaf30c9889..1b299b52dd6 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1570,7 +1570,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc and ( posting_datetime {operator} %(posting_datetime)s ) - order by posting_date desc, posting_time desc, creation desc + order by posting_datetime desc, creation desc limit 1 for update""", { @@ -1664,7 +1664,7 @@ def get_stock_ledger_entries( where item_code = %(item_code)s and is_cancelled = 0 {conditions} - order by posting_date {order}, posting_time {order}, creation {order} + order by posting_datetime {order}, creation {order} {limit} {for_update}""".format( conditions=conditions, limit=limit or "", @@ -1786,7 +1786,7 @@ def get_valuation_rate( AND valuation_rate >= 0 AND is_cancelled = 0 AND NOT (voucher_no = %s AND voucher_type = %s) - order by posting_date desc, posting_time desc, name desc limit 1""", + order by posting_datetime desc, creation desc limit 1""", (item_code, warehouse, voucher_no, voucher_type), ): return flt(last_valuation_rate[0][0]) @@ -2037,7 +2037,7 @@ def get_future_sle_with_negative_qty(sle_args): and posting_datetime >= %(posting_datetime)s and is_cancelled = 0 and qty_after_transaction < 0 - order by posting_date asc, posting_time asc + order by posting_datetime asc, creation asc limit 1 """, sle_args, @@ -2051,14 +2051,14 @@ def get_future_sle_with_negative_batch_qty(sle_args): with batch_ledger as ( select posting_date, posting_time, posting_datetime, voucher_type, voucher_no, - sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total + sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total from `tabStock Ledger Entry` where item_code = %(item_code)s and warehouse = %(warehouse)s and batch_no=%(batch_no)s and is_cancelled = 0 - order by posting_date, posting_time, creation + order by posting_datetime, creation ) select * from batch_ledger where From 66544bfa109bd65763cc682211fed2ae00affec3 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 31 Dec 2024 12:53:30 +0530 Subject: [PATCH 02/12] fix: Added patch and fallback code to prevent future issues similiar to helpdesk ticket 28246 (cherry picked from commit 65dc3505c405b4fe8c237bcb55c38d8cef6bf684) # Conflicts: # erpnext/patches.txt --- .../controllers/subcontracting_controller.py | 10 +++++++ erpnext/patches.txt | 5 ++++ .../patches/set_sc_conversion_factor.py | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 75cb5516348..5a8fa03d814 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -103,6 +103,16 @@ class SubcontractingController(StockController): _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name) ) + if ( + not item.sc_conversion_factor + ): # this condition will only be true if user has recently updated from develop branch + service_item_qty = frappe.get_value( + "Subcontracting Order Service Item", + filters={"purchase_order_item": item.purchase_order_item, "parent": self.name}, + fieldname=["qty"], + ) + item.sc_conversion_factor = service_item_qty / item.qty + if ( self.doctype not in "Subcontracting Receipt" and item.qty diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7e03ef9394c..075a1f4b8a7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -386,3 +386,8 @@ erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions erpnext.patches.v15_0.enable_allow_existing_serial_no erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts +<<<<<<< HEAD +======= +erpnext.patches.v15_0.refactor_closing_stock_balance #5 +erpnext.subcontracting.doctype.subcontracting_order.patches.set_sc_conversion_factor +>>>>>>> 65dc3505c4 (fix: Added patch and fallback code to prevent future issues similiar to helpdesk ticket 28246) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py new file mode 100644 index 00000000000..1b23c9565bf --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py @@ -0,0 +1,26 @@ +import frappe + + +def execute(): + # Calculate and set sc_conversion_factor for draft Subcontracting Orders if value is 0 + + subcontracting_order_items = frappe.get_all( + "Subcontracting Order Item", + filters={"docstatus": 0, "sc_conversion_factor": 0}, + fields=["name", "parent", "purchase_order_item", "qty"], + ) + for subcontracting_order_item in subcontracting_order_items: + service_item_qty = frappe.get_value( + "Subcontracting Order Service Item", + filters={ + "purchase_order_item": subcontracting_order_item.purchase_order_item, + "parent": subcontracting_order_item.parent, + }, + fieldname=["qty"], + ) + frappe.set_value( + "Subcontracting Order Item", + subcontracting_order_item.name, + "sc_conversion_factor", + service_item_qty / subcontracting_order_item.qty, + ) From b3b808335f34ee73df494d8ef015001388d0bca1 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 31 Dec 2024 20:33:05 +0530 Subject: [PATCH 03/12] fix: Fixed logic in if condition causing tests to fail (cherry picked from commit 575fb43f9cd5852b37f1a3fe17db3206df596608) --- erpnext/controllers/subcontracting_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 5a8fa03d814..0f9431ab440 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -104,7 +104,7 @@ class SubcontractingController(StockController): ) if ( - not item.sc_conversion_factor + self.doctype == "Subcontracting Order" and not item.sc_conversion_factor ): # this condition will only be true if user has recently updated from develop branch service_item_qty = frappe.get_value( "Subcontracting Order Service Item", From 163af91c377bfd2398b892e7e3abd9033e401f49 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 1 Jan 2025 13:49:49 +0530 Subject: [PATCH 04/12] fix: Removed patch as instructed by mentor (cherry picked from commit d1d01482dfb006a956e010f0e27b322632072804) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 4 +++ .../patches/set_sc_conversion_factor.py | 26 ------------------- 2 files changed, 4 insertions(+), 26 deletions(-) delete mode 100644 erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 075a1f4b8a7..d712933a898 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -387,7 +387,11 @@ erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions erpnext.patches.v15_0.enable_allow_existing_serial_no erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts <<<<<<< HEAD +<<<<<<< HEAD ======= erpnext.patches.v15_0.refactor_closing_stock_balance #5 erpnext.subcontracting.doctype.subcontracting_order.patches.set_sc_conversion_factor >>>>>>> 65dc3505c4 (fix: Added patch and fallback code to prevent future issues similiar to helpdesk ticket 28246) +======= +erpnext.patches.v15_0.refactor_closing_stock_balance #5 +>>>>>>> d1d01482df (fix: Removed patch as instructed by mentor) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py deleted file mode 100644 index 1b23c9565bf..00000000000 --- a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py +++ /dev/null @@ -1,26 +0,0 @@ -import frappe - - -def execute(): - # Calculate and set sc_conversion_factor for draft Subcontracting Orders if value is 0 - - subcontracting_order_items = frappe.get_all( - "Subcontracting Order Item", - filters={"docstatus": 0, "sc_conversion_factor": 0}, - fields=["name", "parent", "purchase_order_item", "qty"], - ) - for subcontracting_order_item in subcontracting_order_items: - service_item_qty = frappe.get_value( - "Subcontracting Order Service Item", - filters={ - "purchase_order_item": subcontracting_order_item.purchase_order_item, - "parent": subcontracting_order_item.parent, - }, - fieldname=["qty"], - ) - frappe.set_value( - "Subcontracting Order Item", - subcontracting_order_item.name, - "sc_conversion_factor", - service_item_qty / subcontracting_order_item.qty, - ) From ab87265395c99b12ef0a2fd764930fa909ce8ab7 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 31 Dec 2024 12:53:30 +0530 Subject: [PATCH 05/12] fix: Added patch and fallback code to prevent future issues similiar to helpdesk ticket 28246 --- erpnext/patches.txt | 8 ------ .../patches/set_sc_conversion_factor.py | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d712933a898..cb240acf4b8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -386,12 +386,4 @@ erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions erpnext.patches.v15_0.enable_allow_existing_serial_no erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts -<<<<<<< HEAD -<<<<<<< HEAD -======= erpnext.patches.v15_0.refactor_closing_stock_balance #5 -erpnext.subcontracting.doctype.subcontracting_order.patches.set_sc_conversion_factor ->>>>>>> 65dc3505c4 (fix: Added patch and fallback code to prevent future issues similiar to helpdesk ticket 28246) -======= -erpnext.patches.v15_0.refactor_closing_stock_balance #5 ->>>>>>> d1d01482df (fix: Removed patch as instructed by mentor) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py new file mode 100644 index 00000000000..1b23c9565bf --- /dev/null +++ b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py @@ -0,0 +1,26 @@ +import frappe + + +def execute(): + # Calculate and set sc_conversion_factor for draft Subcontracting Orders if value is 0 + + subcontracting_order_items = frappe.get_all( + "Subcontracting Order Item", + filters={"docstatus": 0, "sc_conversion_factor": 0}, + fields=["name", "parent", "purchase_order_item", "qty"], + ) + for subcontracting_order_item in subcontracting_order_items: + service_item_qty = frappe.get_value( + "Subcontracting Order Service Item", + filters={ + "purchase_order_item": subcontracting_order_item.purchase_order_item, + "parent": subcontracting_order_item.parent, + }, + fieldname=["qty"], + ) + frappe.set_value( + "Subcontracting Order Item", + subcontracting_order_item.name, + "sc_conversion_factor", + service_item_qty / subcontracting_order_item.qty, + ) From 3049027f437ade49db2fa0cf9fcc1c67c11fb750 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 1 Jan 2025 13:49:49 +0530 Subject: [PATCH 06/12] fix: Removed patch as instructed by mentor --- .../patches/set_sc_conversion_factor.py | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py diff --git a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py b/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py deleted file mode 100644 index 1b23c9565bf..00000000000 --- a/erpnext/subcontracting/doctype/subcontracting_order/patches/set_sc_conversion_factor.py +++ /dev/null @@ -1,26 +0,0 @@ -import frappe - - -def execute(): - # Calculate and set sc_conversion_factor for draft Subcontracting Orders if value is 0 - - subcontracting_order_items = frappe.get_all( - "Subcontracting Order Item", - filters={"docstatus": 0, "sc_conversion_factor": 0}, - fields=["name", "parent", "purchase_order_item", "qty"], - ) - for subcontracting_order_item in subcontracting_order_items: - service_item_qty = frappe.get_value( - "Subcontracting Order Service Item", - filters={ - "purchase_order_item": subcontracting_order_item.purchase_order_item, - "parent": subcontracting_order_item.parent, - }, - fieldname=["qty"], - ) - frappe.set_value( - "Subcontracting Order Item", - subcontracting_order_item.name, - "sc_conversion_factor", - service_item_qty / subcontracting_order_item.qty, - ) From 00102a15e3870888bcf74a5215141e77d1aaf8b0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Jan 2025 09:44:32 +0530 Subject: [PATCH 07/12] fix: BOM cost update issue (cherry picked from commit 28ea3ddd51e3ec3c0043722b5477386a77b5357e) --- .../doctype/bom_update_tool/bom_update_tool.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 2e8dba1ccfe..983dd2d4cd8 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -9,6 +9,7 @@ if TYPE_CHECKING: import frappe from frappe.model.document import Document +from frappe.utils import date_diff, get_datetime, now class BOMUpdateTool(Document): @@ -50,13 +51,21 @@ def auto_update_latest_price_in_all_boms() -> None: if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"): wip_log = frappe.get_all( "BOM Update Log", - {"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]}, + fields=["creation", "status"], + filters={"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]}, limit_page_length=1, + order_by="creation desc", ) - if not wip_log: + + if not wip_log or is_older_log(wip_log[0]): create_bom_update_log(update_type="Update Cost") +def is_older_log(log: dict) -> bool: + no_of_days = date_diff(get_datetime(now()), get_datetime(log.creation)) + return no_of_days > 10 + + def create_bom_update_log( boms: dict[str, str] | None = None, update_type: Literal["Replace BOM", "Update Cost"] = "Replace BOM", From f9d038ee4a255dc1e1e34b178d119b7e79936b62 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 2 Jan 2025 10:51:11 +0530 Subject: [PATCH 08/12] fix: removed unknown patch? --- erpnext/patches.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cb240acf4b8..7e03ef9394c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -386,4 +386,3 @@ erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions erpnext.patches.v15_0.enable_allow_existing_serial_no erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts -erpnext.patches.v15_0.refactor_closing_stock_balance #5 From d31b0a507f6b6793890f3984be3d03207e3e67c6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Jan 2025 10:26:51 +0530 Subject: [PATCH 09/12] fix: removed unused code (cherry picked from commit dc5f2d35ac4367e7e7c17e4b16edf57b169d0aba) --- .../stock/doctype/stock_entry/stock_entry.py | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 78f69b1ce62..8232cbe8e5f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -201,7 +201,6 @@ class StockEntry(StockController): self.validate_purpose() self.validate_item() self.validate_customer_provided_item() - self.validate_qty() self.set_transfer_qty() self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("stock_uom", "transfer_qty") @@ -463,40 +462,6 @@ class StockEntry(StockController): flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item) ) - def validate_qty(self): - manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"] - - if self.purpose in manufacture_purpose and self.work_order: - if not frappe.get_value("Work Order", self.work_order, "skip_transfer"): - item_code = [] - for item in self.items: - if cstr(item.t_warehouse) == "": - req_items = frappe.get_all( - "Work Order Item", - filters={"parent": self.work_order, "item_code": item.item_code}, - fields=["item_code"], - ) - - transferred_materials = frappe.db.sql( - """ - select - sum(sed.qty) as qty - from `tabStock Entry` se,`tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and - (se.purpose='Material Transfer for Manufacture' or se.purpose='Manufacture') - and sed.item_code=%s and se.work_order= %s and ifnull(sed.t_warehouse, '') != '' - """, - (item.item_code, self.work_order), - as_dict=1, - ) - - stock_qty = flt(item.qty) - trans_qty = flt(transferred_materials[0].qty) - if req_items: - if stock_qty > trans_qty: - item_code.append(item.item_code) - def validate_fg_completed_qty(self): item_wise_qty = {} if self.purpose == "Manufacture" and self.work_order: From b5f6926140711407d83516b7aa1081e59f640fac Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Jan 2025 13:11:19 +0530 Subject: [PATCH 10/12] fix: validate components and their qty as per BOM in the stock entry (cherry picked from commit b1de82ddad62bc19c249e26c910654384fbfdb2c) --- .../manufacturing_settings.json | 6 +-- .../doctype/work_order/test_work_order.py | 50 +++++++++++++++++++ .../stock/doctype/stock_entry/stock_entry.py | 31 ++++++++---- 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index 26cbc03eeb2..618ccdf8fc8 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -242,14 +242,14 @@ "depends_on": "eval:doc.backflush_raw_materials_based_on == \"BOM\"", "fieldname": "validate_components_quantities_per_bom", "fieldtype": "Check", - "label": "Validate Components Quantities Per BOM" + "label": "Validate Components and Quantities Per BOM" } ], "icon": "icon-wrench", "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-09-02 12:12:03.132567", + "modified": "2025-01-02 12:46:33.520853", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing Settings", @@ -267,4 +267,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index f8ddf007428..86a03e7919c 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2401,6 +2401,56 @@ class TestWorkOrder(FrappeTestCase): frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0) + def test_components_as_per_bom_for_manufacture_entry(self): + frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM") + frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 1) + + fg_item = "Test FG Item For Component Validation 1" + source_warehouse = "Stores - _TC" + raw_materials = ["Test Component Validation RM Item 11", "Test Component Validation RM Item 12"] + + make_item(fg_item, {"is_stock_item": 1}) + for item in raw_materials: + make_item(item, {"is_stock_item": 1}) + test_stock_entry.make_stock_entry( + item_code=item, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials) + + wo = make_wo_order_test_record( + item=fg_item, + qty=10, + source_warehouse=source_warehouse, + ) + + transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + transfer_entry.remove(transfer_entry.items[0]) + + self.assertRaises(frappe.ValidationError, transfer_entry.save) + + transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + transfer_entry.submit() + + manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10)) + manufacture_entry.save() + + manufacture_entry.remove(manufacture_entry.items[0]) + + self.assertRaises(frappe.ValidationError, manufacture_entry.save) + manufacture_entry.delete() + + manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10)) + manufacture_entry.save() + manufacture_entry.submit() + + frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0) + def make_operation(**kwargs): kwargs = frappe._dict(kwargs) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8232cbe8e5f..4b4ee6ad0f1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -231,7 +231,7 @@ class StockEntry(StockController): self.validate_serialized_batch() self.calculate_rate_and_amount() self.validate_putaway_capacity() - self.validate_component_quantities() + self.validate_component_and_quantities() if not self.get("purpose") == "Manufacture": # ignore scrap item wh difference and empty source/target wh @@ -713,7 +713,7 @@ class StockEntry(StockController): title=_("Insufficient Stock"), ) - def validate_component_quantities(self): + def validate_component_and_quantities(self): if self.purpose not in ["Manufacture", "Material Transfer for Manufacture"]: return @@ -726,20 +726,31 @@ class StockEntry(StockController): raw_materials = self.get_bom_raw_materials(self.fg_completed_qty) precision = frappe.get_precision("Stock Entry Detail", "qty") - for row in self.items: - if not row.s_warehouse: - continue - - if details := raw_materials.get(row.item_code): - if flt(details.get("qty"), precision) != flt(row.qty, precision): + for item_code, details in raw_materials.items(): + if matched_item := self.get_matched_items(item_code): + if flt(details.get("qty"), precision) != flt(matched_item.qty, precision): frappe.throw( _("For the item {0}, the quantity should be {1} according to the BOM {2}.").format( - frappe.bold(row.item_code), - flt(details.get("qty"), precision), + frappe.bold(item_code), + flt(details.get("qty")), get_link_to_form("BOM", self.bom_no), ), title=_("Incorrect Component Quantity"), ) + else: + frappe.throw( + _("According to the BOM {0}, the Item '{1}' is missing in the stock entry.").format( + get_link_to_form("BOM", self.bom_no), frappe.bold(item_code) + ), + title=_("Missing Item"), + ) + + def get_matched_items(self, item_code): + for row in self.items: + if row.item_code == item_code: + return row + + return {} @frappe.whitelist() def get_stock_and_rate(self): From 6247d5aadb73c216f36646919b6883f7a582fa87 Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 2 Jan 2025 17:37:46 +0530 Subject: [PATCH 11/12] fix(style): set image width in BOM (cherry picked from commit b634ba7f54f9cfe5481097ba8392af8082f1de12) --- erpnext/manufacturing/doctype/bom/bom_item_preview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html index eb4135e03ac..2c0f091da58 100644 --- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html +++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html @@ -3,7 +3,7 @@
{% if data.image %}
- +
{% endif %}
From 4dfc5a664a728aa54f892f6207a1c8f1696ddf6e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Jan 2025 20:50:28 +0530 Subject: [PATCH 12/12] chore: partial revert #44989 (cherry picked from commit 63d547fb4af311cdb061a029a4e6ca0ae41bbaed) --- erpnext/public/js/controllers/transaction.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index db866bd3b76..f6d124de23d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -813,7 +813,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } validate() { - this.apply_pricing_rule() this.calculate_taxes_and_totals(false); }