fix: serial and batch no get removed on save of return DN (#37476)
* fix: serial and batch no get removed on save of return DN * test: add test case for DN return with product bundle
This commit is contained in:
@@ -144,6 +144,7 @@ class DeliveryNote(SellingController):
|
|||||||
|
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
|
||||||
|
self.set_product_bundle_reference_in_packed_items() # should be called before `make_packing_list`
|
||||||
make_packing_list(self)
|
make_packing_list(self)
|
||||||
|
|
||||||
if self._action != "submit" and not self.is_return:
|
if self._action != "submit" and not self.is_return:
|
||||||
@@ -430,6 +431,17 @@ class DeliveryNote(SellingController):
|
|||||||
else:
|
else:
|
||||||
serial_nos.append(serial_no)
|
serial_nos.append(serial_no)
|
||||||
|
|
||||||
|
def set_product_bundle_reference_in_packed_items(self):
|
||||||
|
if self.packed_items and ((self.is_return and self.return_against) or self.amended_from):
|
||||||
|
if items_ref_map := {
|
||||||
|
item.dn_detail or item.get("_amended_from"): item.name
|
||||||
|
for item in self.items
|
||||||
|
if item.dn_detail or item.get("_amended_from")
|
||||||
|
}:
|
||||||
|
for item in self.packed_items:
|
||||||
|
if item.parent_detail_docname in items_ref_map:
|
||||||
|
item.parent_detail_docname = items_ref_map[item.parent_detail_docname]
|
||||||
|
|
||||||
|
|
||||||
def update_billed_amount_based_on_so(so_detail, update_modified=True):
|
def update_billed_amount_based_on_so(so_detail, update_modified=True):
|
||||||
from frappe.query_builder.functions import Sum
|
from frappe.query_builder.functions import Sum
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
|
|||||||
|
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||||
automatically_fetch_payment_terms,
|
automatically_fetch_payment_terms,
|
||||||
@@ -268,8 +269,6 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(dn.items[0].returned_qty, 2)
|
self.assertEqual(dn.items[0].returned_qty, 2)
|
||||||
self.assertEqual(dn.per_returned, 40)
|
self.assertEqual(dn.per_returned, 40)
|
||||||
|
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
|
||||||
|
|
||||||
return_dn_2 = make_return_doc("Delivery Note", dn.name)
|
return_dn_2 = make_return_doc("Delivery Note", dn.name)
|
||||||
|
|
||||||
# Check if unreturned amount is mapped in 2nd return
|
# Check if unreturned amount is mapped in 2nd return
|
||||||
@@ -361,8 +360,6 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
dn.submit()
|
dn.submit()
|
||||||
self.assertEqual(dn.items[0].incoming_rate, 150)
|
self.assertEqual(dn.items[0].incoming_rate, 150)
|
||||||
|
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
|
||||||
|
|
||||||
return_dn = make_return_doc(dn.doctype, dn.name)
|
return_dn = make_return_doc(dn.doctype, dn.name)
|
||||||
return_dn.items[0].warehouse = return_warehouse
|
return_dn.items[0].warehouse = return_warehouse
|
||||||
return_dn.save().submit()
|
return_dn.save().submit()
|
||||||
@@ -1182,7 +1179,6 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_batch_expiry_for_delivery_note(self):
|
def test_batch_expiry_for_delivery_note(self):
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
|
||||||
item = make_item(
|
item = make_item(
|
||||||
@@ -1239,6 +1235,55 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
# Test - 1: ValidationError should be raised
|
# Test - 1: ValidationError should be raised
|
||||||
self.assertRaises(frappe.ValidationError, dn.submit)
|
self.assertRaises(frappe.ValidationError, dn.submit)
|
||||||
|
|
||||||
|
def test_packed_items_for_return_delivery_note(self):
|
||||||
|
# Step - 1: Create Items
|
||||||
|
product_bundle_item = make_item(properties={"is_stock_item": 0}).name
|
||||||
|
batch_item = make_item(
|
||||||
|
properties={
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "TEST-BATCH-.#####",
|
||||||
|
}
|
||||||
|
).name
|
||||||
|
serial_item = make_item(
|
||||||
|
properties={"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "TEST-SERIAL-.#####"}
|
||||||
|
).name
|
||||||
|
|
||||||
|
# Step - 2: Inward Stock
|
||||||
|
se1 = make_stock_entry(item_code=batch_item, target="_Test Warehouse - _TC", qty=3)
|
||||||
|
serial_nos = (
|
||||||
|
make_stock_entry(item_code=serial_item, target="_Test Warehouse - _TC", qty=3)
|
||||||
|
.items[0]
|
||||||
|
.serial_no
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step - 3: Create a Product Bundle
|
||||||
|
from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import (
|
||||||
|
create_product_bundle_item,
|
||||||
|
)
|
||||||
|
|
||||||
|
create_product_bundle_item(product_bundle_item, packed_items=[[batch_item, 1], [serial_item, 1]])
|
||||||
|
|
||||||
|
# Step - 4: Create a Delivery Note for the Product Bundle
|
||||||
|
dn = create_delivery_note(
|
||||||
|
item_code=product_bundle_item,
|
||||||
|
warehouse="_Test Warehouse - _TC",
|
||||||
|
qty=3,
|
||||||
|
do_not_submit=True,
|
||||||
|
)
|
||||||
|
dn.packed_items[1].serial_no = serial_nos
|
||||||
|
dn.save()
|
||||||
|
dn.submit()
|
||||||
|
|
||||||
|
# Step - 5: Create a Return Delivery Note(Sales Return)
|
||||||
|
return_dn = make_return_doc(dn.doctype, dn.name)
|
||||||
|
return_dn.save()
|
||||||
|
return_dn.submit()
|
||||||
|
|
||||||
|
self.assertEqual(return_dn.packed_items[0].batch_no, dn.packed_items[0].batch_no)
|
||||||
|
self.assertEqual(return_dn.packed_items[1].serial_no, dn.packed_items[1].serial_no)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
|
frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0)
|
||||||
|
|||||||
@@ -192,7 +192,6 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Parent Detail docname",
|
"label": "Parent Detail docname",
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "parent_detail_docname",
|
"oldfieldname": "parent_detail_docname",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@@ -259,7 +258,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-04-28 13:16:38.460806",
|
"modified": "2023-10-14 23:26:11.755425",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Packed Item",
|
"name": "Packed Item",
|
||||||
|
|||||||
Reference in New Issue
Block a user