diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 44d448760e7199a916989e3227f725bf2d43ff1e..d43a371c3cfd8e0c2b257e9894fc61b7cc9eb12c 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -546,8 +546,12 @@ class BuyingController(SubcontractingController): "actual_qty": flt(pr_qty), "serial_and_batch_bundle": ( d.serial_and_batch_bundle - if not self.is_internal_transfer() or self.is_return - else self.get_package_for_target_warehouse(d, type_of_transaction=type_of_transaction) + if not self.is_internal_transfer() + or self.is_return + or (self.is_internal_transfer() and self.docstatus == 2) + else self.get_package_for_target_warehouse( + d, type_of_transaction=type_of_transaction + ) ), }, ) @@ -583,6 +587,14 @@ class BuyingController(SubcontractingController): (not cint(self.is_return) and self.docstatus == 2) or (cint(self.is_return) and self.docstatus == 1) ): + serial_and_batch_bundle = None + if self.is_internal_transfer() and self.docstatus == 2: + serial_and_batch_bundle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": d.name, "warehouse": d.warehouse}, + "serial_and_batch_bundle", + ) + from_warehouse_sle = self.get_sl_entries( d, { @@ -592,7 +604,7 @@ class BuyingController(SubcontractingController): "serial_and_batch_bundle": ( self.get_package_for_target_warehouse(d, d.from_warehouse, "Inward") if self.is_internal_transfer() and self.is_return - else None + else serial_and_batch_bundle ), }, ) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 65ab41878e6d348cc522afe365f266c92a8b96a7..eeef6e4599e906bb42a15c7e8d2adc526d8751f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -539,7 +539,9 @@ class SellingController(StockController): self.make_sl_entries(sl_entries) def get_sle_for_source_warehouse(self, item_row): - serial_and_batch_bundle = item_row.serial_and_batch_bundle + serial_and_batch_bundle = ( + item_row.serial_and_batch_bundle if not self.is_internal_transfer() else None + ) if serial_and_batch_bundle and self.is_internal_transfer() and self.is_return: if self.docstatus == 1: serial_and_batch_bundle = self.make_package_for_transfer( diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index f682f3f8831b0a0c34fb2bbc5b9c7096bf13685a..b5f2beadf934fae2bca9b7644b343caa7540497b 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2976,6 +2976,122 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr_return.items[0].rejected_qty, 0.0) self.assertEqual(pr_return.items[0].rejected_warehouse, "") + def test_internal_transfer_for_batch_items_with_cancel(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) + + prepare_data_for_internal_transfer() + + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + batch_item_doc = make_item( + "_Test Batch Item For Stock Transfer Cancel Case", + {"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "USBF-BT-CANBIFST-.####"}, + ) + + serial_item_doc = make_item( + "_Test Serial No Item For Stock Transfer Cancel Case", + {"has_serial_no": 1, "serial_no_series": "USBF-BT-CANBIFST-.####"}, + ) + + inward_entry = make_purchase_receipt( + item_code=batch_item_doc.name, + qty=10, + rate=150, + warehouse="Stores - TCP1", + company="_Test Company with perpetual inventory", + use_serial_batch_fields=0, + do_not_submit=1, + ) + + inward_entry.append( + "items", + { + "item_code": serial_item_doc.name, + "qty": 15, + "rate": 250, + "item_name": serial_item_doc.item_name, + "conversion_factor": 1.0, + "uom": serial_item_doc.stock_uom, + "stock_uom": serial_item_doc.stock_uom, + "warehouse": "Stores - TCP1", + "use_serial_batch_fields": 0, + }, + ) + + inward_entry.submit() + inward_entry.reload() + + for row in inward_entry.items: + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_dn = create_delivery_note( + item_code=inward_entry.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=10, + rate=500, + warehouse="Stores - TCP1", + target_warehouse="Work In Progress - TCP1", + batch_no=get_batch_from_bundle(inward_entry.items[0].serial_and_batch_bundle), + use_serial_batch_fields=0, + do_not_submit=1, + ) + + inter_transfer_dn.append( + "items", + { + "item_code": serial_item_doc.name, + "qty": 15, + "rate": 350, + "item_name": serial_item_doc.item_name, + "conversion_factor": 1.0, + "uom": serial_item_doc.stock_uom, + "stock_uom": serial_item_doc.stock_uom, + "warehouse": "Stores - TCP1", + "target_warehouse": "Work In Progress - TCP1", + "serial_no": "\n".join( + get_serial_nos_from_bundle(inward_entry.items[1].serial_and_batch_bundle) + ), + "use_serial_batch_fields": 0, + }, + ) + + inter_transfer_dn.submit() + inter_transfer_dn.reload() + for row in inter_transfer_dn.items: + if row.item_code == batch_item_doc.name: + self.assertEqual(row.rate, 150.0) + else: + self.assertEqual(row.rate, 250.0) + + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_pr = make_inter_company_purchase_receipt(inter_transfer_dn.name) + for row in inter_transfer_pr.items: + row.from_warehouse = "Work In Progress - TCP1" + row.warehouse = "Stores - TCP1" + inter_transfer_pr.submit() + + for row in inter_transfer_pr.items: + if row.item_code == batch_item_doc.name: + self.assertEqual(row.rate, 150.0) + else: + self.assertEqual(row.rate, 250.0) + + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_pr.cancel() + inter_transfer_dn.cancel() + + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier