diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index a6063d52779a5603021a2c4ad3db7ccc19085094..1141ec8d3504feae69f9010ed24c4635e3232fae 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -952,6 +952,7 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False if is_rejected: fields.extend(["rejected_serial_and_batch_bundle", "return_qty_from_rejected_warehouse"]) + del filters["rejected_serial_and_batch_bundle"] data = frappe.get_all( doctype, fields=fields, @@ -959,6 +960,9 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False ) for d in data: + if not d.get("serial_and_batch_bundle") and not d.get("rejected_serial_and_batch_bundle"): + continue + if is_rejected: if d.get("return_qty_from_rejected_warehouse"): _bundle_ids.append(d.get("serial_and_batch_bundle")) @@ -1043,7 +1047,7 @@ def get_available_batch_qty(parent_doc, batch_no, warehouse): ) -def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_field=None): +def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_field=None, qty_field=None): from erpnext.stock.serial_batch_bundle import SerialBatchCreation type_of_transaction = "Outward" @@ -1053,11 +1057,21 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f if not warehouse_field: warehouse_field = "warehouse" + if not qty_field: + qty_field = "qty" + warehouse = child_doc.get(warehouse_field) if parent_doc.get("is_internal_customer"): warehouse = child_doc.get("target_warehouse") type_of_transaction = "Outward" + if not child_doc.get(qty_field): + frappe.throw( + _("For the {0}, the quantity is required to make the return entry").format( + frappe.bold(child_doc.item_code) + ) + ) + cls_obj = SerialBatchCreation( { "type_of_transaction": type_of_transaction, @@ -1070,7 +1084,7 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f "voucher_type": parent_doc.doctype, "voucher_no": parent_doc.name, "voucher_detail_no": child_doc.name, - "qty": child_doc.qty, + "qty": child_doc.get(qty_field), "company": parent_doc.company, "do_not_submit": True, } diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 0a3b12df14e23e9988c37b260f331a85df009961..c6524bfc385912cd248332cb8aeeb9eeedfecc30 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -258,10 +258,17 @@ class StockController(AccountsController): qty_field = "qty" warehouse_field = "warehouse" + if not data.get("qty"): + frappe.throw( + _("For the {0}, no stock is available for the return in the warehouse {1}.").format( + frappe.bold(row.item_code), row.get(warehouse_field) + ) + ) + data = filter_serial_batches( self, data, row, warehouse_field=warehouse_field, qty_field=qty_field ) - bundle = make_serial_batch_bundle_for_return(data, row, self, warehouse_field) + bundle = make_serial_batch_bundle_for_return(data, row, self, warehouse_field, qty_field) if row.get("return_qty_from_rejected_warehouse"): row.db_set( { diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 0fc5d4fe98b779a80ac63692756311a85b59018d..c0daf6637be4855e7bbff2d4179bddf6ca01e09a 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2824,6 +2824,99 @@ class TestPurchaseReceipt(FrappeTestCase): "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", old_value ) + def test_zero_valuation_rate_for_batched_item(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item = make_item( + "_Test Zero Valuation Rate For the Batch Item", + { + "is_purchase_item": 1, + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TZVRFORBATCH.#####", + "valuation_rate": 200, + }, + ) + + pi = make_purchase_receipt( + qty=10, + rate=0, + item_code=item.name, + ) + + pi.reload() + batch_no = get_batch_from_bundle(pi.items[0].serial_and_batch_bundle) + + se = make_stock_entry( + purpose="Material Issue", + item_code=item.name, + source=pi.items[0].warehouse, + qty=10, + batch_no=batch_no, + use_serial_batch_fields=0, + ) + + se.submit() + + se.reload() + + self.assertEqual(se.items[0].valuation_rate, 0) + self.assertEqual(se.items[0].basic_rate, 0) + + sabb_doc = frappe.get_doc("Serial and Batch Bundle", se.items[0].serial_and_batch_bundle) + for row in sabb_doc.entries: + self.assertEqual(row.incoming_rate, 0) + + def test_purchase_return_from_accepted_and_rejected_warehouse(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_return, + ) + + item = make_item( + "_Test PR Item With Return From Accepted and Rejected WH", + { + "is_purchase_item": 1, + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "SD-TZVRFORBATCH.#####", + "valuation_rate": 200, + }, + ) + + pr = make_purchase_receipt( + qty=10, + rejected_qty=5, + rate=100, + item_code=item.name, + ) + + pr.reload() + self.assertTrue(pr.items[0].serial_and_batch_bundle) + self.assertTrue(pr.items[0].rejected_serial_and_batch_bundle) + + return_pr = make_purchase_return(pr.name) + return_pr.submit() + + return_pr.reload() + self.assertTrue(return_pr.items[0].serial_and_batch_bundle) + self.assertTrue(return_pr.items[0].rejected_serial_and_batch_bundle) + + self.assertEqual( + return_pr.items[0].qty, + frappe.db.get_value( + "Serial and Batch Bundle", return_pr.items[0].serial_and_batch_bundle, "total_qty" + ), + ) + + self.assertEqual( + return_pr.items[0].rejected_qty, + frappe.db.get_value( + "Serial and Batch Bundle", return_pr.items[0].rejected_serial_and_batch_bundle, "total_qty" + ), + ) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier