From a6cb6c6f4784194af96d15ece3499415be05d53e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 3 May 2023 09:51:58 +0530 Subject: [PATCH 1/4] fix: recalculate costs in SCR while reposting --- erpnext/stock/stock_ledger.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8b517bf1e0..aa272cbfa7 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -781,13 +781,19 @@ class update_entries_after(object): d.db_update() def update_rate_on_subcontracting_receipt(self, sle, outgoing_rate): - if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): - frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "rate", outgoing_rate) + if frappe.db.exists("Subcontracting Receipt Item", sle.voucher_detail_no): + frappe.db.set_value("Subcontracting Receipt Item", sle.voucher_detail_no, "rate", outgoing_rate) else: frappe.db.set_value( "Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate ) + scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True) + scr.set_missing_values() + scr.db_update() + for d in scr.items + scr.get("supplied_items", []): + d.db_update() + def get_serialized_values(self, sle): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) From e0b22edb2e4fba3224b3dfd26243bcf483a3d71c Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 4 May 2023 15:07:52 +0530 Subject: [PATCH 2/4] test: add test case --- .../test_subcontracting_receipt.py | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 72ed4d4e2e..dfb72c3356 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -6,7 +6,7 @@ import copy import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import cint, flt +from frappe.utils import add_days, cint, cstr, flt, today import erpnext from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -26,6 +26,9 @@ from erpnext.controllers.tests.test_subcontracting_controller import ( from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, +) from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( make_subcontracting_receipt, ) @@ -528,6 +531,69 @@ class TestSubcontractingReceipt(FrappeTestCase): # consumed_qty should be (accepted_qty * qty_consumed_per_unit) = (6 * 1) = 6 self.assertEqual(scr.supplied_items[0].consumed_qty, 6) + def test_supplied_items_cost_after_reposting(self): + # Set Backflush Based On as "BOM" + set_backflush_based_on("BOM") + + # Create Material Receipt for RM's + make_stock_entry( + item_code="_Test Item", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + posting_date=add_days(today(), -2), + ) + make_stock_entry( + item_code="_Test Item Home Desktop 100", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 10, + "rate": 100, + "fg_item": "_Test FG Item", + "fg_item_qty": 10, + }, + ] + + # Create Subcontracting Order + sco = get_subcontracting_order(service_items=service_items) + + # Transfer RM's + rm_items = get_rm_items(sco.supplied_items) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + # Create Subcontracting Receipt + scr = make_subcontracting_receipt(sco.name) + scr.save() + scr.submit() + + # Create Backdated Stock Reconciliation + sr = create_stock_reconciliation( + item_code=rm_items[0].get("item_code"), + warehouse="_Test Warehouse 1 - _TC", + qty=100, + rate=50, + posting_date=add_days(today(), -1), + ) + + # Cost should be updated in Subcontracting Receipt after reposting + prev_cost = scr.supplied_items[0].rate + scr.load_from_db() + self.assertNotEqual(scr.supplied_items[0].rate, prev_cost) + self.assertEqual(scr.supplied_items[0].rate, sr.items[0].valuation_rate) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From d6433f803b497511873acb7ff3889853e4b06e0e Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 12 May 2023 11:45:33 +0530 Subject: [PATCH 3/4] refactor(minor): rename function to be more descriptive --- erpnext/controllers/subcontracting_controller.py | 2 +- .../tests/test_subcontracting_controller.py | 2 +- .../subcontracting_order/subcontracting_order.py | 14 +++++++------- .../subcontracting_receipt.py | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 05754293b7..c3fa894e89 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -741,7 +741,7 @@ class SubcontractingController(StockController): sco_doc = frappe.get_doc("Subcontracting Order", sco) sco_doc.update_status() - def set_missing_values_in_additional_costs(self): + def calculate_additional_costs(self): self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) if self.total_additional_costs: diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 0e6fe95d45..4ea4fd11b4 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -36,7 +36,7 @@ class TestSubcontractingController(FrappeTestCase): sco.remove_empty_rows() self.assertEqual((len_before - 1), len(sco.service_items)) - def test_set_missing_values_in_additional_costs(self): + def test_calculate_additional_costs(self): sco = get_subcontracting_order(do_not_submit=1) rate_without_additional_cost = sco.items[0].rate diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index e6de72d494..39197332c1 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -77,22 +77,22 @@ class SubcontractingOrder(SubcontractingController): frappe.throw(_(msg)) def set_missing_values(self): - self.set_missing_values_in_additional_costs() - self.set_missing_values_in_service_items() - self.set_missing_values_in_supplied_items() - self.set_missing_values_in_items() + self.calculate_additional_costs() + self.calculate_service_costs() + self.calculate_supplied_items_qty_and_amount() + self.calculate_items_qty_and_amount() - def set_missing_values_in_service_items(self): + def calculate_service_costs(self): for idx, item in enumerate(self.get("service_items")): self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty - def set_missing_values_in_supplied_items(self): + def calculate_supplied_items_qty_and_amount(self): for item in self.get("items"): bom = frappe.get_doc("BOM", item.bom) rm_cost = sum(flt(rm_item.amount) for rm_item in bom.items) item.rm_cost_per_qty = rm_cost / flt(bom.quantity) - def set_missing_values_in_items(self): + def calculate_items_qty_and_amount(self): total_qty = total = 0 for item in self.items: item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4f8e045d70..2c84262273 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -113,9 +113,9 @@ class SubcontractingReceipt(SubcontractingController): @frappe.whitelist() def set_missing_values(self): - self.set_missing_values_in_additional_costs() - self.set_missing_values_in_supplied_items() - self.set_missing_values_in_items() + self.calculate_additional_costs() + self.calculate_supplied_items_qty_and_amount() + self.calculate_items_qty_and_amount() def set_available_qty_for_consumption(self): supplied_items_details = {} @@ -147,13 +147,13 @@ class SubcontractingReceipt(SubcontractingController): item.rm_item_code, 0 ) - def set_missing_values_in_supplied_items(self): + def calculate_supplied_items_qty_and_amount(self): for item in self.get("supplied_items") or []: item.amount = item.rate * item.consumed_qty self.set_available_qty_for_consumption() - def set_missing_values_in_items(self): + def calculate_items_qty_and_amount(self): rm_supp_cost = {} for item in self.get("supplied_items") or []: if item.reference_name in rm_supp_cost: From 9c72c2a6cb11197b292b0f589f312e038ac5cc3a Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 12 May 2023 11:46:32 +0530 Subject: [PATCH 4/4] refactor: use `calculate_items_qty_and_amount()` to update scr items rate --- erpnext/stock/stock_ledger.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index aa272cbfa7..c7f0acd453 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -785,13 +785,15 @@ class update_entries_after(object): frappe.db.set_value("Subcontracting Receipt Item", sle.voucher_detail_no, "rate", outgoing_rate) else: frappe.db.set_value( - "Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate + "Subcontracting Receipt Supplied Item", + sle.voucher_detail_no, + {"rate": outgoing_rate, "amount": abs(sle.actual_qty) * outgoing_rate}, ) scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True) - scr.set_missing_values() + scr.calculate_items_qty_and_amount() scr.db_update() - for d in scr.items + scr.get("supplied_items", []): + for d in scr.items: d.db_update() def get_serialized_values(self, sle):