From 3e77c0b5644c28ed9b4eef22b228fdc4a1283020 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 14 Nov 2023 19:27:41 +0530 Subject: [PATCH] fix: valuation rate for the subcontracting receipt supplied items with Serial and Batch Bundle (#38094) fix: valuation rate for the subcontracting receipt supplied items with batch --- .../controllers/subcontracting_controller.py | 13 +++ .../stock_ledger_entry.json | 10 +- erpnext/stock/serial_batch_bundle.py | 10 +- erpnext/stock/stock_ledger.py | 4 +- .../subcontracting_receipt.js | 10 ++ .../test_subcontracting_receipt.py | 91 +++++++++++++++++++ 6 files changed, 135 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 5fa66b1a87..3d55a087bd 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -626,6 +626,18 @@ class SubcontractingController(StockController): (row.item_code, row.get(self.subcontract_data.order_field)) ] -= row.qty + def __set_rate_for_serial_and_batch_bundle(self): + if self.doctype != "Subcontracting Receipt": + return + + for row in self.get(self.raw_material_table): + if not row.get("serial_and_batch_bundle"): + continue + + row.rate = frappe.get_cached_value( + "Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate" + ) + def __modify_serial_and_batch_bundle(self): if self.is_new(): return @@ -681,6 +693,7 @@ class SubcontractingController(StockController): self.__remove_changed_rows() self.__set_supplied_items() self.__modify_serial_and_batch_bundle() + self.__set_rate_for_serial_and_batch_bundle() def __validate_batch_no(self, row, key): if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get( diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index dcbd9b2d06..be379940ca 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -12,6 +12,7 @@ "posting_date", "posting_time", "is_adjustment_entry", + "auto_created_serial_and_batch_bundle", "column_break_6", "voucher_type", "voucher_no", @@ -340,6 +341,13 @@ "fieldname": "is_adjustment_entry", "fieldtype": "Check", "label": "Is Adjustment Entry" + }, + { + "default": "0", + "depends_on": "serial_and_batch_bundle", + "fieldname": "auto_created_serial_and_batch_bundle", + "fieldtype": "Check", + "label": "Auto Created Serial and Batch Bundle" } ], "hide_toolbar": 1, @@ -348,7 +356,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-23 18:07:42.063615", + "modified": "2023-11-14 16:47:39.791967", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 5998274bb7..da98455b5c 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -129,7 +129,9 @@ class SerialBatchBundle: frappe.throw(_(error_msg)) def set_serial_and_batch_bundle(self, sn_doc): - self.sle.db_set("serial_and_batch_bundle", sn_doc.name) + self.sle.db_set( + {"serial_and_batch_bundle": sn_doc.name, "auto_created_serial_and_batch_bundle": 1} + ) if sn_doc.is_rejected: frappe.db.set_value( @@ -143,6 +145,12 @@ class SerialBatchBundle: @property def child_doctype(self): child_doctype = self.sle.voucher_type + " Item" + + if ( + self.sle.voucher_type == "Subcontracting Receipt" and self.sle.dependant_sle_voucher_detail_no + ): + child_doctype = "Subcontracting Receipt Supplied Item" + if self.sle.voucher_type == "Stock Entry": child_doctype = "Stock Entry Detail" diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 63908945c3..9142a27f4c 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -766,7 +766,9 @@ class update_entries_after(object): sle.doctype = "Stock Ledger Entry" frappe.get_doc(sle).db_update() - if not self.args.get("sle_id"): + if not self.args.get("sle_id") or ( + sle.serial_and_batch_bundle and sle.auto_created_serial_and_batch_bundle + ): self.update_outgoing_rate_on_transaction(sle) def reset_actual_qty_for_stock_reco(self, sle): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 19a1c939c3..36001eb78f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -13,6 +13,16 @@ frappe.ui.form.on('Subcontracting Receipt', { frm.trigger('set_queries'); }, + on_submit(frm) { + frm.events.refresh_serial_batch_bundle_field(frm); + }, + + refresh_serial_batch_bundle_field(frm) { + frappe.route_hooks.after_submit = (frm_obj) => { + frm_obj.reload_doc(); + } + }, + refresh: (frm) => { if (frm.doc.docstatus > 0) { frm.add_custom_button(__('Stock Ledger'), () => { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 96babf2b32..6191a8ca94 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -576,6 +576,97 @@ class TestSubcontractingReceipt(FrappeTestCase): self.assertEqual(scr.items[0].rm_cost_per_qty, 300) self.assertEqual(scr.items[0].service_cost_per_qty, 100) + def test_subcontracting_receipt_valuation_with_auto_created_serial_batch_bundle(self): + set_backflush_based_on("BOM") + + fg_item = make_item(properties={"is_stock_item": 1, "is_sub_contracted_item": 1}).name + rm_item1 = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BNGS-.####", + } + ).name + + rm_item2 = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "has_serial_no": 1, + "create_new_batch": 1, + "batch_number_series": "BNGS-.####", + "serial_no_series": "BNSS-.####", + } + ).name + + rm_item3 = make_item( + properties={ + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "BSSSS-.####", + } + ).name + + bom = make_bom(item=fg_item, raw_materials=[rm_item1, rm_item2, rm_item3]) + + rm_batch_no = None + for row in bom.items: + make_stock_entry( + item_code=row.item_code, + qty=1, + target="_Test Warehouse 1 - _TC", + rate=300, + ) + + make_stock_entry( + item_code=row.item_code, + qty=1, + target="_Test Warehouse 1 - _TC", + rate=400, + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 1, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 1, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1 + ) + scr = make_subcontracting_receipt(sco.name) + scr.save() + for row in scr.supplied_items: + self.assertNotEqual(row.rate, 300.00) + self.assertFalse(row.serial_and_batch_bundle) + + scr.submit() + scr.reload() + + for row in scr.supplied_items: + self.assertEqual(row.rate, 300.00) + self.assertTrue(row.serial_and_batch_bundle) + auto_created_serial_batch = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": scr.name, "voucher_detail_no": row.name}, + "auto_created_serial_and_batch_bundle", + ) + + self.assertTrue(auto_created_serial_batch) + + self.assertEqual(scr.items[0].rm_cost_per_qty, 900) + self.assertEqual(scr.items[0].service_cost_per_qty, 100) + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0 + ) + def test_subcontracting_receipt_raw_material_rate(self): # Step - 1: Set Backflush Based On as "BOM" set_backflush_based_on("BOM")