Skip to content

Commit

Permalink
fix: incorrect qty in serial batch bundle against pick list (backport #…
Browse files Browse the repository at this point in the history
…38964) (#38966)

fix: incorrect qty in serial batch bundle against pick list (#38964)

(cherry picked from commit 47ee801)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
  • Loading branch information
mergify[bot] and rohitwaghchaure authored Dec 27, 2023
1 parent ab9fce3 commit 5874be0
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 9 deletions.
1 change: 1 addition & 0 deletions erpnext/public/js/utils/serial_no_batch_selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {

set_data(data) {
data.forEach(d => {
d.qty = Math.abs(d.qty);
this.dialog.fields_dict.entries.df.data.push(d);
});

Expand Down
17 changes: 15 additions & 2 deletions erpnext/stock/doctype/delivery_note/delivery_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,13 @@ def validate_with_previous_doc(self):
)

def set_serial_and_batch_bundle_from_pick_list(self):
from erpnext.stock.serial_batch_bundle import SerialBatchCreation

if not self.pick_list:
return

for item in self.items:
if item.pick_list_item:
if item.pick_list_item and not item.serial_and_batch_bundle:
filters = {
"item_code": item.item_code,
"voucher_type": "Pick List",
Expand All @@ -326,7 +328,17 @@ def set_serial_and_batch_bundle_from_pick_list(self):
bundle_id = frappe.db.get_value("Serial and Batch Bundle", filters, "name")

if bundle_id:
item.serial_and_batch_bundle = bundle_id
cls_obj = SerialBatchCreation(
{
"type_of_transaction": "Outward",
"serial_and_batch_bundle": bundle_id,
"item_code": item.get("item_code"),
}
)

cls_obj.duplicate_package()

item.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle

def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
Expand Down Expand Up @@ -408,6 +420,7 @@ def on_cancel(self):
self.update_stock_ledger()

self.cancel_packing_slips()
self.update_pick_list_status()

self.make_gl_entries_on_cancel()
self.repost_future_sle_and_gle()
Expand Down
38 changes: 38 additions & 0 deletions erpnext/stock/doctype/pick_list/pick_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ frappe.ui.form.on('Pick List Item', {
});
}
},

uom: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
if (row.uom) {
Expand All @@ -291,13 +292,50 @@ frappe.ui.form.on('Pick List Item', {
});
}
},

qty: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor);
},

conversion_factor: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor);
},

pick_serial_and_batch(frm, cdt, cdn) {
let item = locals[cdt][cdn];
let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";

frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
.then((r) => {
if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
item.has_serial_no = r.message.has_serial_no;
item.has_batch_no = r.message.has_batch_no;
item.type_of_transaction = item.qty > 0 ? "Outward":"Inward";

item.title = item.has_serial_no ?
__("Select Serial No") : __("Select Batch No");

if (item.has_serial_no && item.has_batch_no) {
item.title = __("Select Serial and Batch");
}

frappe.require(path, function() {
new erpnext.SerialBatchPackageSelector(
frm, item, (r) => {
if (r) {
let qty = Math.abs(r.total_qty);
frappe.model.set_value(item.doctype, item.name, {
"serial_and_batch_bundle": r.name,
"qty": qty
});
}
}
);
});
}
});
}
});

Expand Down
15 changes: 12 additions & 3 deletions erpnext/stock/doctype/pick_list/pick_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_auto_batch_nos,
get_picked_serial_nos,
)
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
Expand Down Expand Up @@ -167,6 +168,9 @@ def linked_serial_and_batch_bundle(self):
"Serial and Batch Bundle", row.serial_and_batch_bundle
).set_serial_and_batch_values(self, row)

def on_trash(self):
self.remove_serial_and_batch_bundle()

def remove_serial_and_batch_bundle(self):
for row in self.locations:
if row.serial_and_batch_bundle:
Expand Down Expand Up @@ -723,13 +727,14 @@ def get_available_item_locations(
def get_available_item_locations_for_serialized_item(
item_code, from_warehouses, required_qty, company, total_picked_qty=0
):
picked_serial_nos = get_picked_serial_nos(item_code, from_warehouses)

sn = frappe.qb.DocType("Serial No")
query = (
frappe.qb.from_(sn)
.select(sn.name, sn.warehouse)
.where((sn.item_code == item_code) & (sn.company == company))
.orderby(sn.creation)
.limit(cint(required_qty + total_picked_qty))
)

if from_warehouses:
Expand All @@ -742,6 +747,9 @@ def get_available_item_locations_for_serialized_item(
warehouse_serial_nos_map = frappe._dict()
picked_qty = required_qty
for serial_no, warehouse in serial_nos:
if serial_no in picked_serial_nos:
continue

if picked_qty <= 0:
break

Expand Down Expand Up @@ -786,7 +794,8 @@ def get_available_item_locations_for_batched_item(
{
"item_code": item_code,
"warehouse": from_warehouses,
"qty": required_qty + total_picked_qty,
"qty": required_qty,
"is_pick_list": True,
}
)
)
Expand Down Expand Up @@ -1050,7 +1059,7 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte
@frappe.whitelist()
def target_document_exists(pick_list_name, purpose):
if purpose == "Delivery":
return frappe.db.exists("Delivery Note", {"pick_list": pick_list_name})
return frappe.db.exists("Delivery Note", {"pick_list": pick_list_name, "docstatus": 1})

return stock_entry_exists(pick_list_name)

Expand Down
118 changes: 117 additions & 1 deletion erpnext/stock/doctype/pick_list/test_pick_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,122 @@ def test_picklist_with_multi_uom(self):
so.reload()
self.assertEqual(so.per_picked, 50)

def test_picklist_for_batch_item(self):
warehouse = "_Test Warehouse - _TC"
item = make_item(
properties={"is_stock_item": 1, "has_batch_no": 1, "batch_no_series": "PICKLT-.######"}
).name

# create batch
for batch_id in ["PICKLT-000001", "PICKLT-000002"]:
if not frappe.db.exists("Batch", batch_id):
frappe.get_doc(
{
"doctype": "Batch",
"batch_id": batch_id,
"item": item,
}
).insert()

make_stock_entry(
item=item,
to_warehouse=warehouse,
qty=50,
basic_rate=100,
batches=frappe._dict({"PICKLT-000001": 30, "PICKLT-000002": 20}),
)

so = make_sales_order(item_code=item, qty=25.0, rate=100)
pl = create_pick_list(so.name)
# pick half the qty
for loc in pl.locations:
self.assertEqual(loc.qty, 25.0)
self.assertTrue(loc.serial_and_batch_bundle)

data = frappe.get_all(
"Serial and Batch Entry",
fields=["qty", "batch_no"],
filters={"parent": loc.serial_and_batch_bundle},
)

for d in data:
self.assertEqual(d.batch_no, "PICKLT-000001")
self.assertEqual(d.qty, 25.0 * -1)

pl.save()
pl.submit()

so1 = make_sales_order(item_code=item, qty=10.0, rate=100)
pl = create_pick_list(so1.name)
# pick half the qty
for loc in pl.locations:
self.assertEqual(loc.qty, 10.0)
self.assertTrue(loc.serial_and_batch_bundle)

data = frappe.get_all(
"Serial and Batch Entry",
fields=["qty", "batch_no"],
filters={"parent": loc.serial_and_batch_bundle},
)

for d in data:
self.assertTrue(d.batch_no in ["PICKLT-000001", "PICKLT-000002"])
if d.batch_no == "PICKLT-000001":
self.assertEqual(d.qty, 5.0 * -1)
elif d.batch_no == "PICKLT-000002":
self.assertEqual(d.qty, 5.0 * -1)

pl.save()
pl.submit()
pl.cancel()

def test_picklist_for_serial_item(self):
warehouse = "_Test Warehouse - _TC"
item = make_item(
properties={"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-PICKLT-.######"}
).name

make_stock_entry(item=item, to_warehouse=warehouse, qty=50, basic_rate=100)

so = make_sales_order(item_code=item, qty=25.0, rate=100)
pl = create_pick_list(so.name)
picked_serial_nos = []
# pick half the qty
for loc in pl.locations:
self.assertEqual(loc.qty, 25.0)
self.assertTrue(loc.serial_and_batch_bundle)

data = frappe.get_all(
"Serial and Batch Entry", fields=["serial_no"], filters={"parent": loc.serial_and_batch_bundle}
)

picked_serial_nos = [d.serial_no for d in data]
self.assertEqual(len(picked_serial_nos), 25)

pl.save()
pl.submit()

so1 = make_sales_order(item_code=item, qty=10.0, rate=100)
pl = create_pick_list(so1.name)
# pick half the qty
for loc in pl.locations:
self.assertEqual(loc.qty, 10.0)
self.assertTrue(loc.serial_and_batch_bundle)

data = frappe.get_all(
"Serial and Batch Entry",
fields=["qty", "batch_no"],
filters={"parent": loc.serial_and_batch_bundle},
)

self.assertEqual(len(data), 10)
for d in data:
self.assertTrue(d.serial_no not in picked_serial_nos)

pl.save()
pl.submit()
pl.cancel()

def test_picklist_with_bundles(self):
warehouse = "_Test Warehouse - _TC"

Expand Down Expand Up @@ -732,7 +848,7 @@ def test_pick_list_status(self):

dn.cancel()
pl.reload()
self.assertEqual(pl.status, "Completed")
self.assertEqual(pl.status, "Open")

pl.cancel()
pl.reload()
Expand Down
Loading

0 comments on commit 5874be0

Please sign in to comment.