fix: use serial batch fields for packed items (#40140) (cherry picked from commit bc9c48024632198f726bd78fa6368ea9d0c26dce) Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
parent
57bb031602
commit
1860399ccb
@ -446,7 +446,11 @@ class SalesInvoice(SellingController):
|
|||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
if self.update_stock == 1:
|
if self.update_stock == 1:
|
||||||
self.make_bundle_using_old_serial_batch_fields()
|
for table_name in ["items", "packed_items"]:
|
||||||
|
if not self.get(table_name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.make_bundle_using_old_serial_batch_fields(table_name)
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
|
@ -707,6 +707,9 @@ def set_default_income_account_for_item(obj):
|
|||||||
def get_serial_and_batch_bundle(child, parent):
|
def get_serial_and_batch_bundle(child, parent):
|
||||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
|
if child.get("use_serial_batch_fields"):
|
||||||
|
return
|
||||||
|
|
||||||
if not frappe.db.get_single_value(
|
if not frappe.db.get_single_value(
|
||||||
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
|
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
|
||||||
):
|
):
|
||||||
|
@ -158,7 +158,7 @@ class StockController(AccountsController):
|
|||||||
# remove extra whitespace and store one serial no on each line
|
# remove extra whitespace and store one serial no on each line
|
||||||
row.serial_no = clean_serial_no_string(row.serial_no)
|
row.serial_no = clean_serial_no_string(row.serial_no)
|
||||||
|
|
||||||
def make_bundle_using_old_serial_batch_fields(self):
|
def make_bundle_using_old_serial_batch_fields(self, table_name=None):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
@ -169,7 +169,9 @@ class StockController(AccountsController):
|
|||||||
if frappe.flags.in_test and frappe.flags.use_serial_and_batch_fields:
|
if frappe.flags.in_test and frappe.flags.use_serial_and_batch_fields:
|
||||||
return
|
return
|
||||||
|
|
||||||
table_name = "items"
|
if not table_name:
|
||||||
|
table_name = "items"
|
||||||
|
|
||||||
if self.doctype == "Asset Capitalization":
|
if self.doctype == "Asset Capitalization":
|
||||||
table_name = "stock_items"
|
table_name = "stock_items"
|
||||||
|
|
||||||
@ -192,6 +194,12 @@ class StockController(AccountsController):
|
|||||||
qty = row.qty
|
qty = row.qty
|
||||||
type_of_transaction = "Inward"
|
type_of_transaction = "Inward"
|
||||||
warehouse = row.warehouse
|
warehouse = row.warehouse
|
||||||
|
elif table_name == "packed_items":
|
||||||
|
qty = row.qty
|
||||||
|
warehouse = row.warehouse
|
||||||
|
type_of_transaction = "Outward"
|
||||||
|
if self.is_return:
|
||||||
|
type_of_transaction = "Inward"
|
||||||
else:
|
else:
|
||||||
qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
|
qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
|
||||||
type_of_transaction = get_type_of_transaction(self, row)
|
type_of_transaction = get_type_of_transaction(self, row)
|
||||||
|
@ -399,7 +399,12 @@ class DeliveryNote(SellingController):
|
|||||||
elif self.issue_credit_note:
|
elif self.issue_credit_note:
|
||||||
self.make_return_invoice()
|
self.make_return_invoice()
|
||||||
|
|
||||||
self.make_bundle_using_old_serial_batch_fields()
|
for table_name in ["items", "packed_items"]:
|
||||||
|
if not self.get(table_name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.make_bundle_using_old_serial_batch_fields(table_name)
|
||||||
|
|
||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
@ -1067,6 +1067,8 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(si2.items[1].qty, 1)
|
self.assertEqual(si2.items[1].qty, 1)
|
||||||
|
|
||||||
def test_delivery_note_bundle_with_batched_item(self):
|
def test_delivery_note_bundle_with_batched_item(self):
|
||||||
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||||
|
|
||||||
batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0})
|
batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0})
|
||||||
batched_item = make_item(
|
batched_item = make_item(
|
||||||
"_Test Batched Item",
|
"_Test Batched Item",
|
||||||
@ -1088,6 +1090,8 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
batch_no = get_batch_from_bundle(dn.packed_items[0].serial_and_batch_bundle)
|
batch_no = get_batch_from_bundle(dn.packed_items[0].serial_and_batch_bundle)
|
||||||
self.assertTrue(batch_no)
|
self.assertTrue(batch_no)
|
||||||
|
|
||||||
|
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
||||||
|
|
||||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||||
create_payment_terms_template,
|
create_payment_terms_template,
|
||||||
@ -1540,6 +1544,53 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(so.items[0].rate, rate)
|
self.assertEqual(so.items[0].rate, rate)
|
||||||
self.assertEqual(dn.items[0].rate, so.items[0].rate)
|
self.assertEqual(dn.items[0].rate, so.items[0].rate)
|
||||||
|
|
||||||
|
def test_use_serial_batch_fields_for_packed_items(self):
|
||||||
|
bundle_item = make_item("Test _Packed Product Bundle Item ", {"is_stock_item": 0})
|
||||||
|
serial_item = make_item(
|
||||||
|
"Test _Packed Serial Item ",
|
||||||
|
{"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-TESTSERIAL-.#####"},
|
||||||
|
)
|
||||||
|
batch_item = make_item(
|
||||||
|
"Test _Packed Batch Item ",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"batch_no_series": "BATCH-TESTSERIAL-.#####",
|
||||||
|
"create_new_batch": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
make_product_bundle(parent=bundle_item.name, items=[serial_item.name, batch_item.name])
|
||||||
|
|
||||||
|
item_details = {}
|
||||||
|
for item in [serial_item, batch_item]:
|
||||||
|
se = make_stock_entry(
|
||||||
|
item_code=item.name, target="_Test Warehouse - _TC", qty=5, basic_rate=100
|
||||||
|
)
|
||||||
|
item_details[item.name] = se.items[0].serial_and_batch_bundle
|
||||||
|
|
||||||
|
dn = create_delivery_note(item_code=bundle_item.name, qty=1, do_not_submit=True)
|
||||||
|
serial_no = ""
|
||||||
|
for row in dn.packed_items:
|
||||||
|
row.use_serial_batch_fields = 1
|
||||||
|
|
||||||
|
if row.item_code == serial_item.name:
|
||||||
|
serial_and_batch_bundle = item_details[serial_item.name]
|
||||||
|
row.serial_no = get_serial_nos_from_bundle(serial_and_batch_bundle)[3]
|
||||||
|
serial_no = row.serial_no
|
||||||
|
else:
|
||||||
|
serial_and_batch_bundle = item_details[batch_item.name]
|
||||||
|
row.batch_no = get_batch_from_bundle(serial_and_batch_bundle)
|
||||||
|
|
||||||
|
dn.submit()
|
||||||
|
dn.load_from_db()
|
||||||
|
|
||||||
|
for row in dn.packed_items:
|
||||||
|
self.assertTrue(row.serial_no or row.batch_no)
|
||||||
|
self.assertTrue(row.serial_and_batch_bundle)
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
self.assertEqual(row.serial_no, serial_no)
|
||||||
|
|
||||||
|
|
||||||
def create_delivery_note(**args):
|
def create_delivery_note(**args):
|
||||||
dn = frappe.new_doc("Delivery Note")
|
dn = frappe.new_doc("Delivery Note")
|
||||||
|
@ -227,6 +227,9 @@ def update_packed_item_stock_data(main_item_row, pi_row, packing_item, item_data
|
|||||||
bin = get_packed_item_bin_qty(packing_item.item_code, pi_row.warehouse)
|
bin = get_packed_item_bin_qty(packing_item.item_code, pi_row.warehouse)
|
||||||
pi_row.actual_qty = flt(bin.get("actual_qty"))
|
pi_row.actual_qty = flt(bin.get("actual_qty"))
|
||||||
pi_row.projected_qty = flt(bin.get("projected_qty"))
|
pi_row.projected_qty = flt(bin.get("projected_qty"))
|
||||||
|
pi_row.use_serial_batch_fields = frappe.db.get_single_value(
|
||||||
|
"Stock Settings", "use_serial_batch_fields"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_packed_item_price_data(pi_row, item_data, doc):
|
def update_packed_item_price_data(pi_row, item_data, doc):
|
||||||
|
@ -257,9 +257,9 @@ class SerialandBatchBundle(Document):
|
|||||||
if sn_obj.batch_avg_rate.get(d.batch_no):
|
if sn_obj.batch_avg_rate.get(d.batch_no):
|
||||||
d.incoming_rate = abs(sn_obj.batch_avg_rate.get(d.batch_no))
|
d.incoming_rate = abs(sn_obj.batch_avg_rate.get(d.batch_no))
|
||||||
|
|
||||||
available_qty = flt(sn_obj.available_qty.get(d.batch_no))
|
available_qty = flt(sn_obj.available_qty.get(d.batch_no), d.precision("qty"))
|
||||||
if self.docstatus == 1:
|
if self.docstatus == 1:
|
||||||
available_qty += flt(d.qty)
|
available_qty += flt(d.qty, d.precision("qty"))
|
||||||
|
|
||||||
if not allow_negative_stock:
|
if not allow_negative_stock:
|
||||||
self.validate_negative_batch(d.batch_no, available_qty)
|
self.validate_negative_batch(d.batch_no, available_qty)
|
||||||
|
@ -1028,7 +1028,7 @@ class StockEntry(StockController):
|
|||||||
already_picked_serial_nos = []
|
already_picked_serial_nos = []
|
||||||
|
|
||||||
for row in self.items:
|
for row in self.items:
|
||||||
if row.use_serial_batch_fields and (row.serial_no or row.batch_no):
|
if row.use_serial_batch_fields:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not row.s_warehouse:
|
if not row.s_warehouse:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user