From c9eeca22f5fb7fa4c2ba6b96b06f264e5bc6decd Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 12:51:22 +0530 Subject: [PATCH 01/10] fix: stock adjustment entry during reposting (backport #47878) (#47882) fix: stock adjustment entry during reposting (#47878) fix: stock adjustment entry (cherry picked from commit cbcd580daa3d2bc1779313887ed37d2c5aaaae49) Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_ledger.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 1cacb0172c4..cc0a9e0f04e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -655,8 +655,11 @@ class update_entries_after: if not sle.is_adjustment_entry: sle.stock_value_difference = stock_value_difference elif sle.is_adjustment_entry and not self.args.get("sle_id"): - sle.stock_value_difference = get_stock_value_difference( - sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time, sle.voucher_no + sle.stock_value_difference = ( + get_stock_value_difference( + sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time, sle.voucher_no + ) + * -1 ) sle.doctype = "Stock Ledger Entry" From f5ef376486a613e5ec62637eeaaec5b40adcd8e8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:18:22 +0530 Subject: [PATCH 02/10] fix: key-error for COGS By Item Group report (backport #47914) (#47916) fix: key-error for COGS By Item Group report (#47914) fix: keyerror for COGS By Item Group report (cherry picked from commit 997ce4eaa7f6ea5c09c07f1b0f40491dad0361ca) Co-authored-by: rohitwaghchaure --- .../stock/report/cogs_by_item_group/cogs_by_item_group.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py index 07119411304..000aca9f43e 100644 --- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py @@ -159,10 +159,11 @@ def assign_item_groups_to_svd_list(svd_list: SVDList) -> None: def get_item_groups_map(svd_list: SVDList) -> dict[str, str]: item_codes = set(i["item_code"] for i in svd_list) - ig_list = frappe.get_list( - "Item", fields=["item_code", "item_group"], filters=[("item_code", "in", item_codes)] + return frappe._dict( + frappe.get_all( + "Item", fields=["name", "item_group"], filters=[("name", "in", item_codes)], as_list=True + ) ) - return {i["item_code"]: i["item_group"] for i in ig_list} def get_item_groups_dict() -> ItemGroupsDict: From 6397c366fdcbc82c733b3de2a5a23e9343873a71 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:50:20 +0530 Subject: [PATCH 03/10] fix: stock reco qty with inventory dimension (backport #47918) (#47921) fix: stock reco qty with inventory dimension (#47918) (cherry picked from commit 342cebc778ac02cf51870f24a13d667d5f1f9db3) Co-authored-by: rohitwaghchaure --- .../doctype/stock_reconciliation/stock_reconciliation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index d1b81aa2ebe..6766fc66c44 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -489,6 +489,10 @@ class StockReconciliation(StockController): self.update_inventory_dimensions(row, data) + if self.docstatus == 1 and has_dimensions and (not row.batch_no or not row.serial_and_batch_bundle): + data.qty_after_transaction = data.actual_qty + data.actual_qty = 0.0 + return data def make_sle_on_cancel(self): From 3232310c0f2174369ba3affa0d46b2904e298070 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Jun 2025 12:30:03 +0530 Subject: [PATCH 04/10] chore: incorrect condition --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 6766fc66c44..79bbf0b502c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -489,7 +489,7 @@ class StockReconciliation(StockController): self.update_inventory_dimensions(row, data) - if self.docstatus == 1 and has_dimensions and (not row.batch_no or not row.serial_and_batch_bundle): + if self.docstatus == 1 and has_dimensions and not row.batch_no: data.qty_after_transaction = data.actual_qty data.actual_qty = 0.0 From 96c937bf6ab699f955287f8a14516ecd23a23e53 Mon Sep 17 00:00:00 2001 From: Aayush Dalal Date: Mon, 9 Jun 2025 22:58:46 +0530 Subject: [PATCH 05/10] fix: throw permission error (#47976) Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com> (cherry picked from commit 8b6a8d0c4fbb52884f07dcdf86855eb2067a921d) # Conflicts: # erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py # erpnext/stock/utils.py --- .../bank_statement_import.py | 44 +++++++++++++++++++ erpnext/stock/utils.py | 5 +++ 2 files changed, 49 insertions(+) diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py index 78a27c3b96d..bdba2af2700 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -216,6 +216,50 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None): @frappe.whitelist() +<<<<<<< HEAD +======= +def get_import_status(docname): + import_status = {} + + data_import = frappe.get_doc("Bank Statement Import", docname) + import_status["status"] = data_import.status + + logs = frappe.get_all( + "Data Import Log", + fields=["count(*) as count", "success"], + filters={"data_import": docname}, + group_by="success", + ) + + total_payload_count = 0 + + for log in logs: + total_payload_count += log.get("count", 0) + if log.get("success"): + import_status["success"] = log.get("count") + else: + import_status["failed"] = log.get("count") + + import_status["total_records"] = total_payload_count + + return import_status + + +@frappe.whitelist() +def get_import_logs(docname: str): + frappe.has_permission("Bank Statement Import", throw=True) + + return frappe.get_all( + "Data Import Log", + fields=["success", "docname", "messages", "exception", "row_indexes"], + filters={"data_import": docname}, + limit_page_length=5000, + order_by="log_index", + ) + + +@frappe.whitelist() +>>>>>>> 8b6a8d0c4f (fix: throw permission error (#47976)) def upload_bank_statement(**args): args = frappe._dict(args) bsi = frappe.new_doc("Bank Statement Import") diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 636d620fad3..7c3c38c0c13 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -109,6 +109,11 @@ def get_stock_balance( from erpnext.stock.stock_ledger import get_previous_sle +<<<<<<< HEAD +======= + frappe.has_permission("Item", "read", throw=True) + +>>>>>>> 8b6a8d0c4f (fix: throw permission error (#47976)) if posting_date is None: posting_date = nowdate() if posting_time is None: From 186173a21e4addc634623db409f447cb3813deda Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Jun 2025 16:14:43 +0530 Subject: [PATCH 06/10] fix: available qty in BOM Stock Report (cherry picked from commit ea689bbe3f6dd4ae50d0015cc119b816e8581a4e) --- .../manufacturing/report/bom_stock_report/bom_stock_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py index d233643c244..96a6822cd11 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py @@ -79,7 +79,7 @@ def get_bom_stock(filters): BOM_ITEM.stock_qty, BOM_ITEM.stock_uom, BOM_ITEM.stock_qty * qty_to_produce / BOM.quantity, - Sum(BIN.actual_qty).as_("actual_qty"), + BIN.actual_qty.as_("actual_qty"), Sum(Floor(BIN.actual_qty / (BOM_ITEM.stock_qty * qty_to_produce / BOM.quantity))), ) .where((BOM_ITEM.parent == filters.get("bom")) & (BOM_ITEM.parenttype == "BOM")) From 1d42c4a3052246ff37eefbc1a5be2e37a1f3d7af Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Tue, 10 Jun 2025 13:08:39 +0530 Subject: [PATCH 07/10] chore: fix conflicts --- .../bank_statement_import.py | 44 ------------------- erpnext/stock/utils.py | 3 -- 2 files changed, 47 deletions(-) diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py index bdba2af2700..78a27c3b96d 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -216,50 +216,6 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None): @frappe.whitelist() -<<<<<<< HEAD -======= -def get_import_status(docname): - import_status = {} - - data_import = frappe.get_doc("Bank Statement Import", docname) - import_status["status"] = data_import.status - - logs = frappe.get_all( - "Data Import Log", - fields=["count(*) as count", "success"], - filters={"data_import": docname}, - group_by="success", - ) - - total_payload_count = 0 - - for log in logs: - total_payload_count += log.get("count", 0) - if log.get("success"): - import_status["success"] = log.get("count") - else: - import_status["failed"] = log.get("count") - - import_status["total_records"] = total_payload_count - - return import_status - - -@frappe.whitelist() -def get_import_logs(docname: str): - frappe.has_permission("Bank Statement Import", throw=True) - - return frappe.get_all( - "Data Import Log", - fields=["success", "docname", "messages", "exception", "row_indexes"], - filters={"data_import": docname}, - limit_page_length=5000, - order_by="log_index", - ) - - -@frappe.whitelist() ->>>>>>> 8b6a8d0c4f (fix: throw permission error (#47976)) def upload_bank_statement(**args): args = frappe._dict(args) bsi = frappe.new_doc("Bank Statement Import") diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 7c3c38c0c13..0067c321049 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -109,11 +109,8 @@ def get_stock_balance( from erpnext.stock.stock_ledger import get_previous_sle -<<<<<<< HEAD -======= frappe.has_permission("Item", "read", throw=True) ->>>>>>> 8b6a8d0c4f (fix: throw permission error (#47976)) if posting_date is None: posting_date = nowdate() if posting_time is None: From 9ab80cfd6c4422848d03d02cfa1206674bdd6dbb Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 9 Jun 2025 22:15:41 +0200 Subject: [PATCH 08/10] refactor(Work Order): query_sales_order - Use `get_list` instead of `db.sql_list` The method is used for setting link options in the frontend and the Link field doesn't ignore permissions, so get_list should be fine here. - Added type hints to enable argument validation (cherry picked from commit 2dbdacf905c5898da843bf319420371a47c7acc9) --- .../doctype/work_order/work_order.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index f657967c0c5..167d0443df2 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1322,20 +1322,20 @@ def stop_unstop(work_order, status): @frappe.whitelist() -def query_sales_order(production_item): - out = frappe.db.sql_list( - """ - select distinct so.name from `tabSales Order` so, `tabSales Order Item` so_item - where so_item.parent=so.name and so_item.item_code=%s and so.docstatus=1 - union - select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item - where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1 - """, - (production_item, production_item), +def query_sales_order(production_item: str) -> list[str]: + return frappe.get_list( + "Sales Order", + filters=[ + ["Sales Order", "docstatus", "=", 1], + ], + or_filters=[ + ["Sales Order Item", "item_code", "=", production_item], + ["Packed Item", "item_code", "=", production_item], + ], + pluck="name", + distinct=True, ) - return out - @frappe.whitelist() def make_job_card(work_order, operations): From 2cac05e56cccc85644273031f0e0328feafd7634 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 10 Jun 2025 17:36:47 +0530 Subject: [PATCH 09/10] fix: incorrect warehouse in MR (cherry picked from commit 2b9ca79291400205cedb338208e1f7348d051b43) # Conflicts: # erpnext/manufacturing/doctype/production_plan/production_plan.py --- .../production_plan/production_plan.py | 27 +++++++++++++++++++ .../doctype/sales_order/sales_order.py | 7 ++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index f4822fe583a..7fe1364fb26 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1115,6 +1115,7 @@ def get_subitems( item_default.default_warehouse, item.purchase_uom, item_uom.conversion_factor, + bom.item.as_("main_bom_item"), ) .where( (bom.name == bom_no) @@ -1206,6 +1207,7 @@ def get_material_request_items( get_conversion_factor(row.item_code, item_details.purchase_uom).get("conversion_factor") or 1.0 ) +<<<<<<< HEAD if required_qty > 0: return { "item_code": row.item_code, @@ -1229,6 +1231,31 @@ def get_material_request_items( "description": row.get("description"), "uom": row.get("purchase_uom") or row.get("stock_uom"), } +======= + return { + "item_code": row.item_code, + "item_name": row.item_name, + "quantity": required_qty / conversion_factor, + "conversion_factor": conversion_factor, + "required_bom_qty": total_qty, + "stock_uom": row.get("stock_uom"), + "warehouse": warehouse + or row.get("source_warehouse") + or row.get("default_warehouse") + or item_group_defaults.get("default_warehouse"), + "safety_stock": row.safety_stock, + "actual_qty": bin_dict.get("actual_qty", 0), + "projected_qty": bin_dict.get("projected_qty", 0), + "ordered_qty": bin_dict.get("ordered_qty", 0), + "reserved_qty_for_production": bin_dict.get("reserved_qty_for_production", 0), + "min_order_qty": row["min_order_qty"], + "material_request_type": row.get("default_material_request_type"), + "sales_order": sales_order, + "description": row.get("description"), + "uom": row.get("purchase_uom") or row.get("stock_uom"), + "main_bom_item": row.get("main_bom_item"), + } +>>>>>>> 2b9ca79291 (fix: incorrect warehouse in MR) def get_sales_orders(self): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 6d135812358..7d06a487f46 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1266,6 +1266,11 @@ def make_raw_material_request(items, company, sales_order, project=None): items.update({"company": company, "sales_order": sales_order}) + item_wh = {} + for item in items.get("items"): + if item.get("warehouse"): + item_wh[item.get("item_code")] = item.get("warehouse") + raw_materials = get_items_for_material_requests(items) if not raw_materials: frappe.msgprint(_("Material Request not created, as quantity for Raw Materials already available.")) @@ -1290,7 +1295,7 @@ def make_raw_material_request(items, company, sales_order, project=None): "item_code": item.get("item_code"), "qty": item.get("quantity"), "schedule_date": schedule_date, - "warehouse": item.get("warehouse"), + "warehouse": item_wh.get(item.get("main_bom_item")) or item.get("warehouse"), "sales_order": sales_order, "project": project, }, From 85a0581145923d9604ad6643eb5e9ea38bc27033 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 10 Jun 2025 18:20:28 +0530 Subject: [PATCH 10/10] chore: fix conflicts --- .../production_plan/production_plan.py | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 7fe1364fb26..fa9e1cb3508 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1207,7 +1207,6 @@ def get_material_request_items( get_conversion_factor(row.item_code, item_details.purchase_uom).get("conversion_factor") or 1.0 ) -<<<<<<< HEAD if required_qty > 0: return { "item_code": row.item_code, @@ -1230,32 +1229,8 @@ def get_material_request_items( "sales_order": sales_order, "description": row.get("description"), "uom": row.get("purchase_uom") or row.get("stock_uom"), + "main_bom_item": row.get("main_bom_item"), } -======= - return { - "item_code": row.item_code, - "item_name": row.item_name, - "quantity": required_qty / conversion_factor, - "conversion_factor": conversion_factor, - "required_bom_qty": total_qty, - "stock_uom": row.get("stock_uom"), - "warehouse": warehouse - or row.get("source_warehouse") - or row.get("default_warehouse") - or item_group_defaults.get("default_warehouse"), - "safety_stock": row.safety_stock, - "actual_qty": bin_dict.get("actual_qty", 0), - "projected_qty": bin_dict.get("projected_qty", 0), - "ordered_qty": bin_dict.get("ordered_qty", 0), - "reserved_qty_for_production": bin_dict.get("reserved_qty_for_production", 0), - "min_order_qty": row["min_order_qty"], - "material_request_type": row.get("default_material_request_type"), - "sales_order": sales_order, - "description": row.get("description"), - "uom": row.get("purchase_uom") or row.get("stock_uom"), - "main_bom_item": row.get("main_bom_item"), - } ->>>>>>> 2b9ca79291 (fix: incorrect warehouse in MR) def get_sales_orders(self):