diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 6998255242d44cbc2c98bdf27215f96c94d1e49a..e198d0ca5ccb60e0e83ee04bda6b623b9472c1b7 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2110,6 +2110,92 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): return_pi.submit() self.assertEqual(return_pi.docstatus, 1) + def test_purchase_invoice_with_use_serial_batch_field_for_rejected_qty(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + batch_item = make_item( + "_Test Purchase Invoice Batch Item For Rejected Qty", + properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1}, + ).name + + serial_item = make_item( + "_Test Purchase Invoice Serial Item for Rejected Qty", + properties={"has_serial_no": 1, "is_stock_item": 1}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase INV Warehouse For Rejected Qty") + + batch_no = "BATCH-PI-BNU-TPRBI-0001" + serial_nos = ["SNU-PI-TPRSI-0001", "SNU-PI-TPRSI-0002", "SNU-PI-TPRSI-0003"] + + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": batch_item, + } + ).insert() + + for serial_no in serial_nos: + if not frappe.db.exists("Serial No", serial_no): + frappe.get_doc( + { + "doctype": "Serial No", + "item_code": serial_item, + "serial_no": serial_no, + } + ).insert() + + pi = make_purchase_invoice( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + update_stock=1, + rejected_warehouse=rej_warehouse, + use_serial_batch_fields=1, + batch_no=batch_no, + rate=100, + do_not_submit=1, + ) + + pi.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pi.items[0].warehouse, + "rejected_warehouse": rej_warehouse, + "use_serial_batch_fields": 1, + "serial_no": "\n".join(serial_nos[:2]), + "rejected_serial_no": serial_nos[2], + }, + ) + + pi.save() + pi.submit() + + pi.reload() + + for row in pi.items: + self.assertTrue(row.serial_and_batch_bundle) + self.assertTrue(row.rejected_serial_and_batch_bundle) + + if row.item_code == batch_item: + self.assertEqual(row.batch_no, batch_no) + else: + self.assertEqual(row.serial_no, "\n".join(serial_nos[:2])) + self.assertEqual(row.rejected_serial_no, serial_nos[2]) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( @@ -2217,7 +2303,7 @@ def make_purchase_invoice(**args): pi.cost_center = args.parent_cost_center bundle_id = None - if args.get("batch_no") or args.get("serial_no"): + if not args.use_serial_batch_fields and ((args.get("batch_no") or args.get("serial_no"))): batches = {} qty = args.qty or 5 item_code = args.item or args.item_code or "_Test Item" @@ -2264,6 +2350,9 @@ def make_purchase_invoice(**args): "rejected_warehouse": args.rejected_warehouse or "", "asset_location": args.location or "", "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0, + "use_serial_batch_fields": args.get("use_serial_batch_fields") or 0, + "batch_no": args.get("batch_no") if args.get("use_serial_batch_fields") else "", + "serial_no": args.get("serial_no") if args.get("use_serial_batch_fields") else "", }, ) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index eac35b0d39fdecfb99f839da2feca34a9412aba3..e66fe8bcdec27407d419a3a482800991fcd894cb 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -859,6 +859,7 @@ class SubcontractingController(StockController): item, { "warehouse": item.rejected_warehouse, + "serial_and_batch_bundle": item.get("rejected_serial_and_batch_bundle"), "actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor), "incoming_rate": 0.0, }, diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index f447af6319db966d0b079e3796a80fbb987b69ab..303eb03968b9127d8dbd6f542784ada73bcfff1e 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2431,6 +2431,88 @@ class TestPurchaseReceipt(FrappeTestCase): "Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 1 ) + def test_purchase_receipt_with_use_serial_batch_field_for_rejected_qty(self): + batch_item = make_item( + "_Test Purchase Receipt Batch Item For Rejected Qty", + properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1}, + ).name + + serial_item = make_item( + "_Test Purchase Receipt Serial Item for Rejected Qty", + properties={"has_serial_no": 1, "is_stock_item": 1}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty") + + batch_no = "BATCH-BNU-TPRBI-0001" + serial_nos = ["SNU-TPRSI-0001", "SNU-TPRSI-0002", "SNU-TPRSI-0003"] + + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": batch_item, + } + ).insert() + + for serial_no in serial_nos: + if not frappe.db.exists("Serial No", serial_no): + frappe.get_doc( + { + "doctype": "Serial No", + "item_code": serial_item, + "serial_no": serial_no, + } + ).insert() + + pr = make_purchase_receipt( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + rejected_warehouse=rej_warehouse, + use_serial_batch_fields=1, + batch_no=batch_no, + rate=100, + do_not_submit=1, + ) + + pr.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pr.items[0].warehouse, + "rejected_warehouse": rej_warehouse, + "use_serial_batch_fields": 1, + "serial_no": "\n".join(serial_nos[:2]), + "rejected_serial_no": serial_nos[2], + }, + ) + + pr.save() + pr.submit() + + pr.reload() + + for row in pr.items: + self.assertTrue(row.serial_and_batch_bundle) + self.assertTrue(row.rejected_serial_and_batch_bundle) + + if row.item_code == batch_item: + self.assertEqual(row.batch_no, batch_no) + else: + self.assertEqual(row.serial_no, "\n".join(serial_nos[:2])) + self.assertEqual(row.rejected_serial_no, serial_nos[2]) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2b62baf42b3d9fa027241dabc26a84a8d8107bf2..60053aae03c077e94e25bb60e3ec54e39a148b68 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -893,6 +893,9 @@ class update_entries_after(object): query.run() def calculate_valuation_for_serial_batch_bundle(self, sle): + if not frappe.db.exists("Serial and Batch Bundle", sle.serial_and_batch_bundle): + return + doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 16050eb6a1aee315d0ddaa73a4f30cc206318164..d69bbc57665f0ae2ddcdd03926f4ea481d3e604e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1131,6 +1131,86 @@ class TestSubcontractingReceipt(FrappeTestCase): scr.reload() self.assertTrue(scr.items[0].serial_and_batch_bundle) + def test_use_serial_batch_fields_for_subcontracting_receipt_with_rejected_qty(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + fg_item = make_item( + "Test Subcontracted Item With Batch No for Rejected Qty", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-REJ-BNGS-.####", + "is_sub_contracted_item": 1, + }, + ).name + + make_item( + "Test Subcontracted Item With Batch No Service Item 2", + properties={"is_stock_item": 0}, + ) + + make_bom( + item=fg_item, + raw_materials=[ + make_item( + "Test Subcontracted Item With Batch No RM Item 2", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-REJ-RM-BNGS-.####", + }, + ).name + ], + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Test Subcontracted Item With Batch No Service Item 2", + "qty": 10, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 10, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + 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), + ) + + batch_no = "BATCH-REJ-BNGS-0001" + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": fg_item, + } + ).insert() + + rej_warehouse = create_warehouse("_Test Subcontract Warehouse For Rejected Qty") + + scr = make_subcontracting_receipt(sco.name) + self.assertFalse(scr.items[0].serial_and_batch_bundle) + scr.items[0].use_serial_batch_fields = 1 + scr.items[0].batch_no = batch_no + scr.items[0].received_qty = 10 + scr.items[0].rejected_qty = 2 + scr.items[0].qty = 8 + scr.items[0].rejected_warehouse = rej_warehouse + + scr.save() + scr.submit() + scr.reload() + self.assertTrue(scr.items[0].serial_and_batch_bundle) + self.assertTrue(scr.items[0].rejected_serial_and_batch_bundle) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index 9636bf4bf396459f675b6e1761dcbc8c08acf769..27341b8455a0aacfdfd892a692fbd78a255b62b6 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -335,8 +335,7 @@ "fieldtype": "Small Text", "label": "Rejected Serial No", "no_copy": 1, - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "subcontracting_order_item", @@ -569,7 +568,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-02-04 16:23:30.374865", + "modified": "2024-03-07 11:43:38.954262", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item",