diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 2ebad5b1fb94d790c19d2d059fbc855481022ca4..0277b7cb31f51fb8366bb755ae258bafa4adcdd6 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -937,6 +937,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, @@ -944,6 +945,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")) @@ -1028,7 +1032,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" @@ -1038,11 +1042,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, @@ -1055,7 +1069,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 19fccb0ef04bc2abd9fe8f04c6c5b8a9f5fddb49..77580a33af2daa41ae58ef8efeb69002cee09891 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -255,10 +255,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 3c52a703115aa5de5805f6f8128bd1d6ca2786b2..a6ec4f72a01051c08277b541bd069769c1e1ab0d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2935,6 +2935,55 @@ class TestPurchaseReceipt(FrappeTestCase): 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