mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
fix(stock): return packed items against bundle if source has one
With the setting "Use Serial / Batch Fields", a packed item keeps its serial nos and also gets a bundle. A return of this packed item skipped the bundle and made a new bundle from all copied serial nos. A partial return then failed, because the bundle qty was more than the return qty. Now the return skips the bundle only if the original packed item has no bundle (e.g. delivered before v15).
This commit is contained in:
@@ -524,20 +524,33 @@ class StockController(AccountsController):
|
||||
):
|
||||
reference_ids.append(row.get(field))
|
||||
|
||||
# Packed items with serial / batch fields (e.g. delivered before v15) can have no bundle
|
||||
# to return against, make_bundle_using_old_serial_batch_fields handles them
|
||||
if (
|
||||
table_name == "packed_items"
|
||||
and row.get("parent_detail_docname")
|
||||
and not (row.serial_no or row.batch_no)
|
||||
):
|
||||
if table_name == "packed_items" and row.get("parent_detail_docname"):
|
||||
parent_rows = self.get("items", {"name": row.parent_detail_docname}) or []
|
||||
for d in parent_rows:
|
||||
if d.get(field) and not d.get(bundle_field):
|
||||
if (
|
||||
d.get(field)
|
||||
and not d.get(bundle_field)
|
||||
and not self.is_legacy_packed_item(row, d.get(field))
|
||||
):
|
||||
reference_ids.append(d.get(field))
|
||||
|
||||
return field, reference_ids
|
||||
|
||||
def is_legacy_packed_item(self, row, source_parent_detail_docname):
|
||||
"""Packed items delivered before v15 have serial / batch fields, but no bundle to return
|
||||
against. make_bundle_using_old_serial_batch_fields handles them."""
|
||||
if not (row.serial_no or row.batch_no):
|
||||
return False
|
||||
|
||||
return not frappe.db.exists(
|
||||
"Packed Item",
|
||||
{
|
||||
"parent_detail_docname": source_parent_detail_docname,
|
||||
"item_code": row.item_code,
|
||||
"serial_and_batch_bundle": ("is", "set"),
|
||||
},
|
||||
)
|
||||
|
||||
@frappe.request_cache
|
||||
def is_serial_batch_item(self, item_code) -> bool:
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
|
||||
@@ -3360,6 +3360,36 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
}
|
||||
self.assertEqual(returned_serial_nos, delivered_serial_nos)
|
||||
|
||||
@change_settings("Stock Settings", {"use_serial_batch_fields": 1})
|
||||
def test_partial_sales_return_for_product_bundle_with_serial_fields_and_bundle(self):
|
||||
bundle_item = make_item("_Packed Serial Fields Bundle Item", {"is_stock_item": 0}).name
|
||||
serial_item = make_item(
|
||||
"_Packed Serial Fields Serial Item",
|
||||
{"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-PACKED-FIELDS-.#####"},
|
||||
).name
|
||||
se = make_stock_entry(item=serial_item, target="_Test Warehouse - _TC", qty=2, rate=100)
|
||||
delivered_serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
make_product_bundle(bundle_item, [serial_item])
|
||||
|
||||
# With serial / batch fields enabled, the packed item keeps its serial nos and gets a bundle
|
||||
dn = create_delivery_note(
|
||||
item_code=bundle_item, warehouse="_Test Warehouse - _TC", qty=2, do_not_submit=True
|
||||
)
|
||||
dn.packed_items[0].serial_no = "\n".join(delivered_serial_nos)
|
||||
dn.submit()
|
||||
dn.reload()
|
||||
self.assertTrue(dn.packed_items[0].serial_no)
|
||||
self.assertTrue(dn.packed_items[0].serial_and_batch_bundle)
|
||||
|
||||
return_dn = make_sales_return(dn.name)
|
||||
return_dn.items[0].qty = -1
|
||||
return_dn.submit()
|
||||
return_dn.reload()
|
||||
|
||||
returned_serial_nos = get_serial_nos_from_bundle(return_dn.packed_items[0].serial_and_batch_bundle)
|
||||
self.assertEqual(len(returned_serial_nos), 1)
|
||||
self.assertIn(returned_serial_nos[0], delivered_serial_nos)
|
||||
|
||||
@change_settings("Stock Settings", {"allow_negative_stock": 0, "enable_stock_reservation": 1})
|
||||
def test_partial_delivery_note_against_reserved_stock(self):
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
|
||||
Reference in New Issue
Block a user