mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-04 14:08:29 +00:00
Merge pull request #49990 from frappe/mergify/bp/version-15/pr-49984
fix: sales return for product bundle items (backport #49975) (backport #49984)
This commit is contained in:
@@ -849,13 +849,14 @@ def available_serial_batch_for_return(field, doctype, reference_ids, is_rejected
|
|||||||
|
|
||||||
def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False):
|
def get_available_serial_batches(field, doctype, reference_ids, is_rejected=False):
|
||||||
_bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected)
|
_bundle_ids = get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=is_rejected)
|
||||||
|
|
||||||
if not _bundle_ids:
|
if not _bundle_ids:
|
||||||
return frappe._dict({})
|
return frappe._dict({})
|
||||||
|
|
||||||
return get_serial_batches_based_on_bundle(field, _bundle_ids)
|
return get_serial_batches_based_on_bundle(doctype, field, _bundle_ids)
|
||||||
|
|
||||||
|
|
||||||
def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
def get_serial_batches_based_on_bundle(doctype, field, _bundle_ids):
|
||||||
available_dict = frappe._dict({})
|
available_dict = frappe._dict({})
|
||||||
batch_serial_nos = frappe.get_all(
|
batch_serial_nos = frappe.get_all(
|
||||||
"Serial and Batch Bundle",
|
"Serial and Batch Bundle",
|
||||||
@@ -867,6 +868,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
|||||||
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
|
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
|
||||||
"`tabSerial and Batch Bundle`.`voucher_type`",
|
"`tabSerial and Batch Bundle`.`voucher_type`",
|
||||||
"`tabSerial and Batch Bundle`.`voucher_no`",
|
"`tabSerial and Batch Bundle`.`voucher_no`",
|
||||||
|
"`tabSerial and Batch Bundle`.`item_code`",
|
||||||
],
|
],
|
||||||
filters=[
|
filters=[
|
||||||
["Serial and Batch Bundle", "name", "in", _bundle_ids],
|
["Serial and Batch Bundle", "name", "in", _bundle_ids],
|
||||||
@@ -880,6 +882,16 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
|||||||
if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"):
|
if frappe.get_cached_value(row.voucher_type, row.voucher_no, "is_return"):
|
||||||
key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field)
|
key = frappe.get_cached_value(row.voucher_type + " Item", row.voucher_detail_no, field)
|
||||||
|
|
||||||
|
if doctype == "Packed Item":
|
||||||
|
if key is None:
|
||||||
|
key = frappe.get_cached_value("Packed Item", row.voucher_detail_no, field)
|
||||||
|
if row.voucher_type == "Delivery Note":
|
||||||
|
key = frappe.get_cached_value("Delivery Note Item", key, "dn_detail")
|
||||||
|
elif row.voucher_type == "Sales Invoice":
|
||||||
|
key = frappe.get_cached_value("Sales Invoice Item", key, "sales_invoice_item")
|
||||||
|
|
||||||
|
key = (row.item_code, key)
|
||||||
|
|
||||||
if row.voucher_type in ["Sales Invoice", "Delivery Note"]:
|
if row.voucher_type in ["Sales Invoice", "Delivery Note"]:
|
||||||
row.qty = -1 * row.qty
|
row.qty = -1 * row.qty
|
||||||
|
|
||||||
@@ -908,6 +920,8 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
|||||||
|
|
||||||
def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False):
|
def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False):
|
||||||
filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
|
filters = {"docstatus": 1, "name": ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
|
||||||
|
if doctype == "Packed Item":
|
||||||
|
filters = get_filters_for_packed_item(field, reference_ids)
|
||||||
|
|
||||||
pluck_field = "serial_and_batch_bundle"
|
pluck_field = "serial_and_batch_bundle"
|
||||||
if is_rejected:
|
if is_rejected:
|
||||||
@@ -921,10 +935,14 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
|
|||||||
pluck=pluck_field,
|
pluck=pluck_field,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if _bundle_ids and doctype == "Packed Item":
|
||||||
|
return _bundle_ids
|
||||||
|
|
||||||
if not _bundle_ids:
|
if not _bundle_ids:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
del filters["name"]
|
if "name" in filters:
|
||||||
|
del filters["name"]
|
||||||
|
|
||||||
filters[field] = ("in", reference_ids)
|
filters[field] = ("in", reference_ids)
|
||||||
|
|
||||||
@@ -967,10 +985,29 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
|
|||||||
return _bundle_ids
|
return _bundle_ids
|
||||||
|
|
||||||
|
|
||||||
|
def get_filters_for_packed_item(field, reference_ids):
|
||||||
|
names = []
|
||||||
|
filters = {"docstatus": 1, "dn_detail": ("in", reference_ids)}
|
||||||
|
if dns := frappe.get_all("Delivery Note Item", filters=filters, pluck="name"):
|
||||||
|
names.extend(dns)
|
||||||
|
|
||||||
|
filters = {"docstatus": 1, "sales_invoice_item": ("in", reference_ids)}
|
||||||
|
if sis := frappe.get_all("Sales Invoice Item", filters=filters, pluck="name"):
|
||||||
|
names.extend(sis)
|
||||||
|
|
||||||
|
if names:
|
||||||
|
reference_ids.extend(names)
|
||||||
|
|
||||||
|
return {"docstatus": 1, field: ("in", reference_ids), "serial_and_batch_bundle": ("is", "set")}
|
||||||
|
|
||||||
|
|
||||||
def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None):
|
def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None):
|
||||||
if not qty_field:
|
if not qty_field:
|
||||||
qty_field = "stock_qty"
|
qty_field = "stock_qty"
|
||||||
|
|
||||||
|
if not hasattr(row, qty_field):
|
||||||
|
qty_field = "qty"
|
||||||
|
|
||||||
if not warehouse_field:
|
if not warehouse_field:
|
||||||
warehouse_field = "warehouse"
|
warehouse_field = "warehouse"
|
||||||
|
|
||||||
@@ -1060,6 +1097,9 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f
|
|||||||
if not qty_field:
|
if not qty_field:
|
||||||
qty_field = "stock_qty"
|
qty_field = "stock_qty"
|
||||||
|
|
||||||
|
if not hasattr(child_doc, qty_field):
|
||||||
|
qty_field = "qty"
|
||||||
|
|
||||||
warehouse = child_doc.get(warehouse_field)
|
warehouse = child_doc.get(warehouse_field)
|
||||||
if parent_doc.get("is_internal_customer"):
|
if parent_doc.get("is_internal_customer"):
|
||||||
warehouse = child_doc.get("target_warehouse")
|
warehouse = child_doc.get("target_warehouse")
|
||||||
|
|||||||
@@ -517,8 +517,15 @@ class SellingController(StockController):
|
|||||||
if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
item_details = frappe.get_cached_value(
|
||||||
|
"Item", d.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
|
||||||
|
)
|
||||||
|
|
||||||
if not self.get("return_against") or (
|
if not self.get("return_against") or (
|
||||||
get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
|
get_valuation_method(d.item_code) == "Moving Average"
|
||||||
|
and self.get("is_return")
|
||||||
|
and not item_details.has_serial_no
|
||||||
|
and not item_details.has_batch_no
|
||||||
):
|
):
|
||||||
# Get incoming rate based on original item cost based on valuation method
|
# Get incoming rate based on original item cost based on valuation method
|
||||||
qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty"))
|
qty = flt(d.get("stock_qty") or d.get("actual_qty") or d.get("qty"))
|
||||||
@@ -997,6 +1004,9 @@ def set_default_income_account_for_item(obj):
|
|||||||
def get_serial_and_batch_bundle(child, parent, delivery_note_child=None):
|
def get_serial_and_batch_bundle(child, parent, delivery_note_child=None):
|
||||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
|
if parent.get("is_return") and parent.get("packed_items"):
|
||||||
|
return
|
||||||
|
|
||||||
if child.get("use_serial_batch_fields"):
|
if child.get("use_serial_batch_fields"):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -335,10 +335,20 @@ class StockController(AccountsController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
child_doctype = self.doctype + " Item"
|
child_doctype = self.doctype + " Item"
|
||||||
|
if table_name == "packed_items":
|
||||||
|
field = "parent_detail_docname"
|
||||||
|
child_doctype = "Packed Item"
|
||||||
|
|
||||||
available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids)
|
available_dict = available_serial_batch_for_return(field, child_doctype, reference_ids)
|
||||||
|
|
||||||
for row in self.get(table_name):
|
for row in self.get(table_name):
|
||||||
if data := available_dict.get(row.get(field)):
|
value = row.get(field)
|
||||||
|
if table_name == "packed_items" and row.get("parent_detail_docname"):
|
||||||
|
value = self.get_value_for_packed_item(row)
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if data := available_dict.get(value):
|
||||||
data = filter_serial_batches(self, data, row)
|
data = filter_serial_batches(self, data, row)
|
||||||
bundle = make_serial_batch_bundle_for_return(data, row, self)
|
bundle = make_serial_batch_bundle_for_return(data, row, self)
|
||||||
row.db_set(
|
row.db_set(
|
||||||
@@ -354,6 +364,14 @@ class StockController(AccountsController):
|
|||||||
"incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate")
|
"incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_value_for_packed_item(self, row):
|
||||||
|
parent_items = self.get("items", {"name": row.parent_detail_docname})
|
||||||
|
if parent_items:
|
||||||
|
ref = parent_items[0].get("dn_detail")
|
||||||
|
return (row.item_code, ref)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]:
|
def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]:
|
||||||
field = {
|
field = {
|
||||||
"Sales Invoice": "sales_invoice_item",
|
"Sales Invoice": "sales_invoice_item",
|
||||||
@@ -388,6 +406,12 @@ class StockController(AccountsController):
|
|||||||
):
|
):
|
||||||
reference_ids.append(row.get(field))
|
reference_ids.append(row.get(field))
|
||||||
|
|
||||||
|
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):
|
||||||
|
reference_ids.append(d.get(field))
|
||||||
|
|
||||||
return field, reference_ids
|
return field, reference_ids
|
||||||
|
|
||||||
@frappe.request_cache
|
@frappe.request_cache
|
||||||
|
|||||||
@@ -2596,6 +2596,129 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(dn.per_billed, 100)
|
self.assertEqual(dn.per_billed, 100)
|
||||||
self.assertEqual(dn.per_returned, 100)
|
self.assertEqual(dn.per_returned, 100)
|
||||||
|
|
||||||
|
def test_sales_return_for_product_bundle(self):
|
||||||
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
rm_items = []
|
||||||
|
for item_code, properties in {
|
||||||
|
"_Packed Service Item": {"is_stock_item": 0},
|
||||||
|
"_Packed FG Item New 1": {
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "SN-PACKED-1-.#####",
|
||||||
|
},
|
||||||
|
"_Packed FG Item New 2": {
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "BATCH-PACKED-2-.#####",
|
||||||
|
},
|
||||||
|
"_Packed FG Item New 3": {
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"batch_number_series": "BATCH-PACKED-3-.#####",
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "SN-PACKED-3-.#####",
|
||||||
|
},
|
||||||
|
}.items():
|
||||||
|
if not frappe.db.exists("Item", item_code):
|
||||||
|
make_item(item_code, properties)
|
||||||
|
|
||||||
|
if item_code != "_Packed Service Item":
|
||||||
|
rm_items.append(item_code)
|
||||||
|
|
||||||
|
for rate in [100, 200]:
|
||||||
|
make_stock_entry(item=item_code, target="_Test Warehouse - _TC", qty=5, rate=rate)
|
||||||
|
|
||||||
|
make_product_bundle("_Packed Service Item", rm_items)
|
||||||
|
dn = create_delivery_note(
|
||||||
|
item_code="_Packed Service Item",
|
||||||
|
warehouse="_Test Warehouse - _TC",
|
||||||
|
qty=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
dn.reload()
|
||||||
|
|
||||||
|
serial_batch_map = {}
|
||||||
|
for row in dn.packed_items:
|
||||||
|
self.assertTrue(row.serial_and_batch_bundle)
|
||||||
|
if row.item_code not in serial_batch_map:
|
||||||
|
serial_batch_map[row.item_code] = frappe._dict(
|
||||||
|
{
|
||||||
|
"serial_nos": [],
|
||||||
|
"batches": defaultdict(int),
|
||||||
|
"serial_no_valuation": defaultdict(float),
|
||||||
|
"batch_no_valuation": defaultdict(float),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||||
|
for entry in doc.entries:
|
||||||
|
if entry.serial_no:
|
||||||
|
serial_batch_map[row.item_code].serial_nos.append(entry.serial_no)
|
||||||
|
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no] = entry.incoming_rate
|
||||||
|
if entry.batch_no:
|
||||||
|
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||||
|
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no] = entry.incoming_rate
|
||||||
|
|
||||||
|
dn1 = make_sales_return(dn.name)
|
||||||
|
dn1.items[0].qty = -2
|
||||||
|
dn1.submit()
|
||||||
|
dn1.reload()
|
||||||
|
|
||||||
|
for row in dn1.packed_items:
|
||||||
|
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||||
|
for entry in doc.entries:
|
||||||
|
if entry.serial_no:
|
||||||
|
self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos)
|
||||||
|
self.assertEqual(
|
||||||
|
entry.incoming_rate,
|
||||||
|
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no],
|
||||||
|
)
|
||||||
|
serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no)
|
||||||
|
serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no)
|
||||||
|
|
||||||
|
elif entry.batch_no:
|
||||||
|
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||||
|
self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches)
|
||||||
|
self.assertEqual(entry.qty, 2.0)
|
||||||
|
self.assertEqual(
|
||||||
|
entry.incoming_rate,
|
||||||
|
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no],
|
||||||
|
)
|
||||||
|
|
||||||
|
dn2 = make_sales_return(dn.name)
|
||||||
|
dn2.items[0].qty = -3
|
||||||
|
dn2.submit()
|
||||||
|
dn2.reload()
|
||||||
|
|
||||||
|
for row in dn2.packed_items:
|
||||||
|
doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||||
|
for entry in doc.entries:
|
||||||
|
if entry.serial_no:
|
||||||
|
self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos)
|
||||||
|
self.assertEqual(
|
||||||
|
entry.incoming_rate,
|
||||||
|
serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no],
|
||||||
|
)
|
||||||
|
serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no)
|
||||||
|
serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no)
|
||||||
|
|
||||||
|
elif entry.batch_no:
|
||||||
|
serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty
|
||||||
|
self.assertEqual(serial_batch_map[row.item_code].batches[entry.batch_no], 0.0)
|
||||||
|
|
||||||
|
self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches)
|
||||||
|
|
||||||
|
self.assertEqual(entry.qty, 3.0)
|
||||||
|
self.assertEqual(
|
||||||
|
entry.incoming_rate,
|
||||||
|
serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_delivery_note(**args):
|
def create_delivery_note(**args):
|
||||||
dn = frappe.new_doc("Delivery Note")
|
dn = frappe.new_doc("Delivery Note")
|
||||||
|
|||||||
@@ -638,6 +638,17 @@ class SerialandBatchBundle(Document):
|
|||||||
if not rate and self.voucher_detail_no and self.voucher_no:
|
if not rate and self.voucher_detail_no and self.voucher_no:
|
||||||
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
||||||
|
|
||||||
|
is_packed_item = False
|
||||||
|
if rate is None and child_table in ["Delivery Note Item", "Sales Invoice Item"]:
|
||||||
|
rate = frappe.db.get_value(
|
||||||
|
"Packed Item",
|
||||||
|
self.voucher_detail_no,
|
||||||
|
"incoming_rate",
|
||||||
|
)
|
||||||
|
|
||||||
|
if rate is not None:
|
||||||
|
is_packed_item = True
|
||||||
|
|
||||||
stock_queue = []
|
stock_queue = []
|
||||||
batches = []
|
batches = []
|
||||||
if prev_sle and prev_sle.stock_queue:
|
if prev_sle and prev_sle.stock_queue:
|
||||||
@@ -659,6 +670,9 @@ class SerialandBatchBundle(Document):
|
|||||||
elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference:
|
elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if is_packed_item and d.incoming_rate:
|
||||||
|
rate = d.incoming_rate
|
||||||
|
|
||||||
d.incoming_rate = flt(rate)
|
d.incoming_rate = flt(rate)
|
||||||
if d.qty:
|
if d.qty:
|
||||||
d.stock_value_difference = flt(d.qty) * d.incoming_rate
|
d.stock_value_difference = flt(d.qty) * d.incoming_rate
|
||||||
|
|||||||
@@ -209,7 +209,20 @@ class SerialBatchBundle:
|
|||||||
elif sn_doc.has_batch_no and len(sn_doc.entries) == 1:
|
elif sn_doc.has_batch_no and len(sn_doc.entries) == 1:
|
||||||
values_to_update["batch_no"] = sn_doc.entries[0].batch_no
|
values_to_update["batch_no"] = sn_doc.entries[0].batch_no
|
||||||
|
|
||||||
frappe.db.set_value(self.child_doctype, self.sle.voucher_detail_no, values_to_update)
|
if self.child_doctype == "Packed Item":
|
||||||
|
name = frappe.db.get_value(
|
||||||
|
"Packed Item",
|
||||||
|
{
|
||||||
|
"parent_detail_docname": sn_doc.voucher_detail_no,
|
||||||
|
"item_code": self.sle.item_code,
|
||||||
|
"serial_and_batch_bundle": ("is", "not set"),
|
||||||
|
},
|
||||||
|
"name",
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value(self.child_doctype, name, values_to_update)
|
||||||
|
else:
|
||||||
|
frappe.db.set_value(self.child_doctype, self.sle.voucher_detail_no, values_to_update)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def child_doctype(self):
|
def child_doctype(self):
|
||||||
@@ -227,6 +240,13 @@ class SerialBatchBundle:
|
|||||||
if self.sle.voucher_type == "Asset Repair":
|
if self.sle.voucher_type == "Asset Repair":
|
||||||
child_doctype = "Asset Repair Consumed Item"
|
child_doctype = "Asset Repair Consumed Item"
|
||||||
|
|
||||||
|
if self.sle.voucher_type in ["Delivery Note", "Sales Invoice"] and self.sle.voucher_detail_no:
|
||||||
|
if (
|
||||||
|
frappe.db.get_value(self.sle.voucher_type + " Item", self.sle.voucher_detail_no, "item_code")
|
||||||
|
!= self.sle.item_code
|
||||||
|
):
|
||||||
|
child_doctype = "Packed Item"
|
||||||
|
|
||||||
return child_doctype
|
return child_doctype
|
||||||
|
|
||||||
def is_rejected_entry(self):
|
def is_rejected_entry(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user