feat: serial and batch bundle for pick list

This commit is contained in:
Rohit Waghchaure 2023-03-28 14:03:59 +05:30
parent 648efca940
commit 9b72845f0f
5 changed files with 72 additions and 62 deletions

View File

@ -108,6 +108,20 @@ class POSInvoice(SalesInvoice):
update_coupon_code_count(self.coupon_code, "cancelled")
self.delink_serial_and_batch_bundle()
def delink_serial_and_batch_bundle(self):
for row in self.items:
if row.serial_and_batch_bundle:
if not self.consolidated_invoice:
frappe.db.set_value(
"Serial and Batch Bundle",
row.serial_and_batch_bundle,
{"is_cancelled": 1, "voucher_no": ""},
)
row.db_set("serial_and_batch_bundle", None)
def submit_serial_batch_bundle(self):
for item in self.items:
if item.serial_and_batch_bundle:

View File

@ -3,6 +3,8 @@
frappe.ui.form.on('Pick List', {
setup: (frm) => {
frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle"];
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.stock_qty === 0) ? "red" : "green"; });

View File

@ -63,25 +63,6 @@ class PickList(Document):
# if the user has not entered any picked qty, set it to stock_qty, before submit
item.picked_qty = item.stock_qty
if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
continue
if not item.serial_no:
frappe.throw(
_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)
),
title=_("Serial Nos Required"),
)
if len(item.serial_no.split("\n")) != item.picked_qty:
frappe.throw(
_(
"For item {0} at row {1}, count of serial numbers does not match with the picked quantity"
).format(frappe.bold(item.item_code), frappe.bold(item.idx)),
title=_("Quantity Mismatch"),
)
def on_submit(self):
self.validate_serial_and_batch_bundle()
self.update_status()
@ -90,10 +71,24 @@ class PickList(Document):
self.update_sales_order_picking_status()
def on_cancel(self):
self.ignore_linked_doctypes = "Serial and Batch Bundle"
self.update_status()
self.update_bundle_picked_qty()
self.update_reference_qty()
self.update_sales_order_picking_status()
self.delink_serial_and_batch_bundle()
def delink_serial_and_batch_bundle(self):
for row in self.locations:
if row.serial_and_batch_bundle:
frappe.db.set_value(
"Serial and Batch Bundle",
row.serial_and_batch_bundle,
{"is_cancelled": 1, "voucher_no": ""},
)
row.db_set("serial_and_batch_bundle", None)
def on_update(self):
self.linked_serial_and_batch_bundle()
@ -546,11 +541,7 @@ def get_available_item_locations(
has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no")
has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no")
if has_batch_no and has_serial_no:
locations = get_available_item_locations_for_serial_and_batched_item(
item_code, from_warehouses, required_qty, company, total_picked_qty
)
elif has_serial_no:
if has_serial_no:
locations = get_available_item_locations_for_serialized_item(
item_code, from_warehouses, required_qty, company, total_picked_qty
)
@ -613,12 +604,39 @@ def get_available_item_locations_for_serialized_item(
serial_nos = query.run(as_list=True)
warehouse_serial_nos_map = frappe._dict()
picked_qty = required_qty
for serial_no, warehouse in serial_nos:
if picked_qty <= 0:
break
warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no)
picked_qty -= 1
locations = []
for warehouse, serial_nos in warehouse_serial_nos_map.items():
locations.append({"qty": len(serial_nos), "warehouse": warehouse, "serial_no": serial_nos})
qty = len(serial_nos)
bundle_doc = SerialBatchCreation(
{
"item_code": item_code,
"warehouse": warehouse,
"voucher_type": "Pick List",
"total_qty": qty * -1,
"serial_nos": serial_nos,
"type_of_transaction": "Outward",
"company": company,
"do_not_submit": True,
}
).make_serial_and_batch_bundle()
locations.append(
{
"qty": qty,
"warehouse": warehouse,
"item_code": item_code,
"serial_and_batch_bundle": bundle_doc.name,
}
)
return locations
@ -652,7 +670,7 @@ def get_available_item_locations_for_batched_item(
"item_code": item_code,
"warehouse": warehouse,
"voucher_type": "Pick List",
"total_qty": qty,
"total_qty": qty * -1,
"batches": batches,
"type_of_transaction": "Outward",
"company": company,
@ -672,40 +690,6 @@ def get_available_item_locations_for_batched_item(
return locations
def get_available_item_locations_for_serial_and_batched_item(
item_code, from_warehouses, required_qty, company, total_picked_qty=0
):
# Get batch nos by FIFO
locations = get_available_item_locations_for_batched_item(
item_code, from_warehouses, required_qty, company
)
if locations:
sn = frappe.qb.DocType("Serial No")
conditions = (sn.item_code == item_code) & (sn.company == company)
for location in locations:
location.qty = (
required_qty if location.qty > required_qty else location.qty
) # if extra qty in batch
serial_nos = (
frappe.qb.from_(sn)
.select(sn.name)
.where(
(conditions) & (sn.batch_no == location.batch_no) & (sn.warehouse == location.warehouse)
)
.orderby(sn.purchase_date)
.limit(cint(location.qty + total_picked_qty))
).run(as_dict=True)
serial_nos = [sn.name for sn in serial_nos]
location.serial_no = serial_nos
location.qty = len(serial_nos)
return locations
def get_available_item_locations_for_other_item(
item_code, from_warehouses, required_qty, company, total_picked_qty=0
):

View File

@ -78,6 +78,9 @@ class SerialandBatchBundle(Document):
def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
sle = self.get_sle_for_outward_transaction(row)
if not sle.actual_qty:
sle.actual_qty = sle.qty
if self.has_serial_no:
sn_obj = SerialNoValuation(
sle=sle,

View File

@ -5,7 +5,7 @@ import frappe
from frappe import _, bold
from frappe.model.naming import make_autoname
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.utils import cint, flt, now
from frappe.utils import cint, flt, now, today
from erpnext.stock.deprecated_serial_batch import (
DeprecatedBatchNoValuation,
@ -557,6 +557,7 @@ class SerialBatchCreation:
def __init__(self, args):
self.set(args)
self.set_item_details()
self.set_other_details()
def set(self, args):
self.__dict__ = {}
@ -585,6 +586,11 @@ class SerialBatchCreation:
self.__dict__.update(item_details)
def set_other_details(self):
if not self.get("posting_date"):
setattr(self, "posting_date", today())
self.__dict__["posting_date"] = self.posting_date
def duplicate_package(self):
if not self.serial_and_batch_bundle:
return
@ -611,6 +617,7 @@ class SerialBatchCreation:
self.set_auto_serial_batch_entries_for_inward()
self.set_serial_batch_entries(doc)
doc.set_incoming_rate()
doc.save()
if not hasattr(self, "do_not_submit") or not self.do_not_submit:
@ -633,7 +640,7 @@ class SerialBatchCreation:
if self.has_serial_no and not self.get("serial_nos"):
self.serial_nos = get_serial_nos_for_outward(kwargs)
elif self.has_batch_no and not self.get("batches"):
elif not self.has_serial_no and self.has_batch_no and not self.get("batches"):
self.batches = get_available_batches(kwargs)
def set_auto_serial_batch_entries_for_inward(self):