From bc80f34d92bd5d8335243813d907f4b2919602c4 Mon Sep 17 00:00:00 2001 From: aaronmenezes Date: Tue, 8 Feb 2022 19:25:49 +0530 Subject: [PATCH 1/5] fix: Reserved for Production calculation considered closed work orders (cherry picked from commit 6a8b7eeffecba15e8a664449b6d92f5a8aa8d2cf) # Conflicts: # erpnext/stock/doctype/bin/bin.py --- .../doctype/work_order/test_work_order.py | 13 ++++++++++ erpnext/stock/doctype/bin/bin.py | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 0e150fee23a..7be816062ec 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -198,6 +198,19 @@ class TestWorkOrder(ERPNextTestCase): self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production), cint(bin1_on_start_production.reserved_qty_for_production)) + def test_reserved_qty_for_production(self): + self.bin1_at_start = get_bin(self.item, self.warehouse) + self.bin1_at_start.update_reserved_qty_for_production() + self.test_reserved_qty_for_production_submit() + self.test_reserved_qty_for_production_cancel() + self.test_close_work_order() + self.wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, + source_warehouse=self.warehouse) + self.bin1_on_submit = get_bin(self.item, self.warehouse) + bin1_on_end_production = get_bin(self.item, self.warehouse) + self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production), + cint(self.bin1_at_start.reserved_qty_for_production) + 2) + def test_backflush_qty_for_overpduction_manufacture(self): cancel_stock_entry = [] allow_overproduction("overproduction_percentage_for_work_order", 30) diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 1d874cd06fb..e6412e9426c 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -31,6 +31,7 @@ class Bin(Document): def update_reserved_qty_for_production(self): '''Update qty reserved for production from Production Item tables in open work orders''' +<<<<<<< HEAD self.reserved_qty_for_production = frappe.db.sql(''' SELECT SUM(CASE WHEN ifnull(skip_transfer, 0) = 0 THEN @@ -47,6 +48,30 @@ class Bin(Document): and pro.status not in ("Stopped", "Completed") and (item.required_qty > item.transferred_qty or item.required_qty > item.consumed_qty) ''', (self.item_code, self.warehouse))[0][0] +======= + + wo = frappe.qb.DocType("Work Order") + wo_item = frappe.qb.DocType("Work Order Item") + + self.reserved_qty_for_production = ( + frappe.qb + .from_(wo) + .from_(wo_item) + .select(Sum(Case() + .when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty) + .else_(wo_item.required_qty - wo_item.consumed_qty)) + ) + .where( + (wo_item.item_code == self.item_code) + & (wo_item.parent == wo.name) + & (wo.docstatus == 1) + & (wo_item.source_warehouse == self.warehouse) + & (wo.status.notin(["Stopped", "Completed", "Closed"])) + & ((wo_item.required_qty > wo_item.transferred_qty) + | (wo_item.required_qty > wo_item.consumed_qty)) + ) + ).run()[0][0] or 0.0 +>>>>>>> 6a8b7eeffe (fix: Reserved for Production calculation considered closed work orders) self.set_projected_qty() From bde3bece36e3753cf0386fc9a043b13bfe2a974d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 9 Feb 2022 11:19:01 +0530 Subject: [PATCH 2/5] test: remove dependency on other tests (cherry picked from commit d2cc5f2482727ee82ef914ea768311a5c1f94996) --- .../doctype/work_order/test_work_order.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 7be816062ec..975216d1bd9 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -198,18 +198,20 @@ class TestWorkOrder(ERPNextTestCase): self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production), cint(bin1_on_start_production.reserved_qty_for_production)) - def test_reserved_qty_for_production(self): - self.bin1_at_start = get_bin(self.item, self.warehouse) - self.bin1_at_start.update_reserved_qty_for_production() - self.test_reserved_qty_for_production_submit() - self.test_reserved_qty_for_production_cancel() - self.test_close_work_order() - self.wo_order = make_wo_order_test_record(item="_Test FG Item", qty=2, + def test_reserved_qty_for_production_closed(self): + + wo1 = make_wo_order_test_record(item="_Test FG Item", qty=2, source_warehouse=self.warehouse) - self.bin1_on_submit = get_bin(self.item, self.warehouse) - bin1_on_end_production = get_bin(self.item, self.warehouse) - self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production), - cint(self.bin1_at_start.reserved_qty_for_production) + 2) + item = wo1.required_items[0].item_code + bin_before = get_bin(item, self.warehouse) + bin_before.update_reserved_qty_for_production() + + make_wo_order_test_record(item="_Test FG Item", qty=2, + source_warehouse=self.warehouse) + close_work_order(wo1.name, "Closed") + + bin_after = get_bin(item, self.warehouse) + self.assertEqual(bin_before.reserved_qty_for_production, bin_after.reserved_qty_for_production) def test_backflush_qty_for_overpduction_manufacture(self): cancel_stock_entry = [] From 197bf8fbec45c9856d8bf120210cd01605cb5ebd Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 9 Feb 2022 11:26:03 +0530 Subject: [PATCH 3/5] refactor: move reserve quantity computation to work order (cherry picked from commit a8bf3a3f0d21ba8b841b69b2185c9d2bd46cd3f2) # Conflicts: # erpnext/stock/doctype/bin/bin.py --- .../doctype/work_order/work_order.py | 26 +++++++++++++++++++ erpnext/stock/doctype/bin/bin.py | 6 +++++ 2 files changed, 32 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c5daba58c3d..46e02b0cdd4 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -8,6 +8,8 @@ from dateutil.relativedelta import relativedelta from frappe import _ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe.query_builder import Case +from frappe.query_builder.functions import Sum from frappe.utils import ( cint, date_diff, @@ -1171,3 +1173,27 @@ def create_pick_list(source_name, target_doc=None, for_qty=None): doc.set_item_locations() return doc + +def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float: + """Get total reserved quantity for any item in specified warehouse""" + wo = frappe.qb.DocType("Work Order") + wo_item = frappe.qb.DocType("Work Order Item") + + return ( + frappe.qb + .from_(wo) + .from_(wo_item) + .select(Sum(Case() + .when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty) + .else_(wo_item.required_qty - wo_item.consumed_qty)) + ) + .where( + (wo_item.item_code == item_code) + & (wo_item.parent == wo.name) + & (wo.docstatus == 1) + & (wo_item.source_warehouse == warehouse) + & (wo.status.notin(["Stopped", "Completed", "Closed"])) + & ((wo_item.required_qty > wo_item.transferred_qty) + | (wo_item.required_qty > wo_item.consumed_qty)) + ) + ).run()[0][0] or 0.0 diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index e6412e9426c..fe897bc0168 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -31,6 +31,7 @@ class Bin(Document): def update_reserved_qty_for_production(self): '''Update qty reserved for production from Production Item tables in open work orders''' +<<<<<<< HEAD <<<<<<< HEAD self.reserved_qty_for_production = frappe.db.sql(''' SELECT @@ -72,6 +73,11 @@ class Bin(Document): ) ).run()[0][0] or 0.0 >>>>>>> 6a8b7eeffe (fix: Reserved for Production calculation considered closed work orders) +======= + from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production + + self.reserved_qty_for_production = get_reserved_qty_for_production(self.item_code, self.warehouse) +>>>>>>> a8bf3a3f0d (refactor: move reserve quantity computation to work order) self.set_projected_qty() From 0ac16f9e63a6daa9299d16021af9863259697c6c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 9 Feb 2022 11:38:52 +0530 Subject: [PATCH 4/5] fix: patch existing bins (cherry picked from commit e134524532db0473bef48b7e558e5d7a289f090e) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 8 +++++- .../v13_0/update_reserved_qty_closed_wo.py | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v13_0/update_reserved_qty_closed_wo.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0a7eb33f66b..3e91e9de4c9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -341,10 +341,16 @@ erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v13_0.update_tax_category_for_rcm erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template +<<<<<<< HEAD erpnext.patches.v13_0.agriculture_deprecation_warning erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v13_0.hospitality_deprecation_warning erpnext.patches.v13_0.delete_bank_reconciliation_detail erpnext.patches.v13_0.update_sane_transfer_against erpnext.patches.v13_0.enable_provisional_accounting -erpnext.patches.v13_0.update_disbursement_account \ No newline at end of file +erpnext.patches.v13_0.update_disbursement_account +======= +erpnext.patches.v13_0.shopping_cart_to_ecommerce +erpnext.patches.v13_0.update_disbursement_account +erpnext.patches.v13_0.update_reserved_qty_closed_wo +>>>>>>> e134524532 (fix: patch existing bins) diff --git a/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py new file mode 100644 index 00000000000..62f774ae06f --- /dev/null +++ b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py @@ -0,0 +1,28 @@ +import frappe + +from erpnext.stock.utils import get_bin + + +def execute(self): + + wo = frappe.qb.DocType("Work Order") + wo_item = frappe.qb.DocType("Work Order Item") + + incorrect_item_wh = ( + frappe.qb + .from_(wo) + .join(wo_item).on(wo.name == wo_item.parent) + .select(wo_item.item_code, wo.source_warehouse).distinct() + .where( + (wo.status == "Closed") + & (wo.docstatus == 1) + & (wo.source_warehouse.notnull()) + ) + ).run(debug=True) + + for item_code, warehouse in incorrect_item_wh: + if not (item_code and warehouse): + continue + + bin = get_bin(item_code, warehouse) + bin.update_reserved_qty_for_production() From 94fc5f95db39fd2caa1bbc8fa6e0a6c4677dad4f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 9 Feb 2022 11:53:14 +0530 Subject: [PATCH 5/5] fix: concflicts --- erpnext/patches.txt | 5 --- .../v13_0/update_reserved_qty_closed_wo.py | 4 +- erpnext/stock/doctype/bin/bin.py | 45 ------------------- 3 files changed, 2 insertions(+), 52 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3e91e9de4c9..4bbeb64ce1c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -341,7 +341,6 @@ erpnext.patches.v13_0.wipe_serial_no_field_for_0_qty erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v13_0.update_tax_category_for_rcm erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template -<<<<<<< HEAD erpnext.patches.v13_0.agriculture_deprecation_warning erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v13_0.hospitality_deprecation_warning @@ -349,8 +348,4 @@ erpnext.patches.v13_0.delete_bank_reconciliation_detail erpnext.patches.v13_0.update_sane_transfer_against erpnext.patches.v13_0.enable_provisional_accounting erpnext.patches.v13_0.update_disbursement_account -======= -erpnext.patches.v13_0.shopping_cart_to_ecommerce -erpnext.patches.v13_0.update_disbursement_account erpnext.patches.v13_0.update_reserved_qty_closed_wo ->>>>>>> e134524532 (fix: patch existing bins) diff --git a/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py index 62f774ae06f..00926b09241 100644 --- a/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py +++ b/erpnext/patches/v13_0/update_reserved_qty_closed_wo.py @@ -3,7 +3,7 @@ import frappe from erpnext.stock.utils import get_bin -def execute(self): +def execute(): wo = frappe.qb.DocType("Work Order") wo_item = frappe.qb.DocType("Work Order Item") @@ -18,7 +18,7 @@ def execute(self): & (wo.docstatus == 1) & (wo.source_warehouse.notnull()) ) - ).run(debug=True) + ).run() for item_code, warehouse in incorrect_item_wh: if not (item_code and warehouse): diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index fe897bc0168..b2ec15690c2 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -31,54 +31,9 @@ class Bin(Document): def update_reserved_qty_for_production(self): '''Update qty reserved for production from Production Item tables in open work orders''' -<<<<<<< HEAD -<<<<<<< HEAD - self.reserved_qty_for_production = frappe.db.sql(''' - SELECT - SUM(CASE WHEN ifnull(skip_transfer, 0) = 0 THEN - item.required_qty - item.transferred_qty - ELSE - item.required_qty - item.consumed_qty END) - END - FROM `tabWork Order` pro, `tabWork Order Item` item - WHERE - item.item_code = %s - and item.parent = pro.name - and pro.docstatus = 1 - and item.source_warehouse = %s - and pro.status not in ("Stopped", "Completed") - and (item.required_qty > item.transferred_qty or item.required_qty > item.consumed_qty) - ''', (self.item_code, self.warehouse))[0][0] -======= - - wo = frappe.qb.DocType("Work Order") - wo_item = frappe.qb.DocType("Work Order Item") - - self.reserved_qty_for_production = ( - frappe.qb - .from_(wo) - .from_(wo_item) - .select(Sum(Case() - .when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty) - .else_(wo_item.required_qty - wo_item.consumed_qty)) - ) - .where( - (wo_item.item_code == self.item_code) - & (wo_item.parent == wo.name) - & (wo.docstatus == 1) - & (wo_item.source_warehouse == self.warehouse) - & (wo.status.notin(["Stopped", "Completed", "Closed"])) - & ((wo_item.required_qty > wo_item.transferred_qty) - | (wo_item.required_qty > wo_item.consumed_qty)) - ) - ).run()[0][0] or 0.0 ->>>>>>> 6a8b7eeffe (fix: Reserved for Production calculation considered closed work orders) -======= from erpnext.manufacturing.doctype.work_order.work_order import get_reserved_qty_for_production self.reserved_qty_for_production = get_reserved_qty_for_production(self.item_code, self.warehouse) ->>>>>>> a8bf3a3f0d (refactor: move reserve quantity computation to work order) - self.set_projected_qty() self.db_set('reserved_qty_for_production', flt(self.reserved_qty_for_production))