mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 11:25:09 +00:00
fix: validate work order consistency in stock entry
(cherry picked from commit ea392b2009)
# Conflicts:
# erpnext/stock/doctype/stock_entry/stock_entry.py
This commit is contained in:
@@ -848,6 +848,16 @@ class StockEntry(StockController):
|
||||
if not self.get("source_stock_entry"):
|
||||
return
|
||||
|
||||
if self.work_order:
|
||||
source_wo = frappe.db.get_value("Stock Entry", self.source_stock_entry, "work_order")
|
||||
if source_wo and source_wo != self.work_order:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Source Stock Entry {0} belongs to Work Order {1}, not {2}. Please use a manufacture entry from the same Work Order."
|
||||
).format(self.source_stock_entry, source_wo, self.work_order),
|
||||
title=_("Work Order Mismatch"),
|
||||
)
|
||||
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import get_disassembly_available_qty
|
||||
|
||||
available_qty = get_disassembly_available_qty(self.source_stock_entry, self.name)
|
||||
@@ -2401,7 +2411,161 @@ class StockEntry(StockController):
|
||||
if self.pro_doc and self.pro_doc.scrap_warehouse:
|
||||
item["to_warehouse"] = self.pro_doc.scrap_warehouse
|
||||
|
||||
<<<<<<< HEAD
|
||||
self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no)
|
||||
=======
|
||||
if (
|
||||
self.purpose not in ["Material Transfer for Manufacture"]
|
||||
and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")
|
||||
!= "BOM"
|
||||
and not skip_transfer
|
||||
):
|
||||
return
|
||||
|
||||
reservation_entries = self.get_available_reserved_materials()
|
||||
if not reservation_entries:
|
||||
return
|
||||
|
||||
new_items_to_add = []
|
||||
for d in self.items:
|
||||
if d.serial_and_batch_bundle or d.serial_no or d.batch_no:
|
||||
continue
|
||||
|
||||
key = (d.item_code, d.s_warehouse)
|
||||
if details := reservation_entries.get(key):
|
||||
original_qty = d.qty
|
||||
if batches := details.get("batch_no"):
|
||||
for batch_no, qty in batches.items():
|
||||
if original_qty <= 0:
|
||||
break
|
||||
|
||||
if qty <= 0:
|
||||
continue
|
||||
|
||||
if d.batch_no and original_qty > 0:
|
||||
new_row = frappe.copy_doc(d)
|
||||
new_row.name = None
|
||||
new_row.batch_no = batch_no
|
||||
new_row.qty = qty
|
||||
new_row.idx = d.idx + 1
|
||||
if new_row.batch_no and details.get("batchwise_sn"):
|
||||
new_row.serial_no = "\n".join(
|
||||
details.get("batchwise_sn")[new_row.batch_no][: cint(new_row.qty)]
|
||||
)
|
||||
|
||||
new_items_to_add.append(new_row)
|
||||
original_qty -= qty
|
||||
batches[batch_no] -= qty
|
||||
|
||||
if qty >= d.qty and not d.batch_no:
|
||||
d.batch_no = batch_no
|
||||
batches[batch_no] -= d.qty
|
||||
if d.batch_no and details.get("batchwise_sn"):
|
||||
d.serial_no = "\n".join(
|
||||
details.get("batchwise_sn")[d.batch_no][: cint(d.qty)]
|
||||
)
|
||||
elif not d.batch_no:
|
||||
d.batch_no = batch_no
|
||||
d.qty = qty
|
||||
original_qty -= qty
|
||||
batches[batch_no] = 0
|
||||
|
||||
if d.batch_no and details.get("batchwise_sn"):
|
||||
d.serial_no = "\n".join(
|
||||
details.get("batchwise_sn")[d.batch_no][: cint(d.qty)]
|
||||
)
|
||||
|
||||
if details.get("serial_no"):
|
||||
d.serial_no = "\n".join(details.get("serial_no")[: cint(d.qty)])
|
||||
|
||||
d.use_serial_batch_fields = 1
|
||||
|
||||
for new_row in new_items_to_add:
|
||||
self.append("items", new_row)
|
||||
|
||||
sorted_items = sorted(self.items, key=lambda x: x.item_code)
|
||||
if self.purpose == "Manufacture":
|
||||
# ensure finished item at last
|
||||
sorted_items = sorted(sorted_items, key=lambda x: x.t_warehouse)
|
||||
|
||||
idx = 0
|
||||
for row in sorted_items:
|
||||
idx += 1
|
||||
row.idx = idx
|
||||
self.set("items", sorted_items)
|
||||
|
||||
def get_available_reserved_materials(self):
|
||||
reserved_entries = self.get_reserved_materials()
|
||||
if not reserved_entries:
|
||||
return {}
|
||||
|
||||
itemwise_serial_batch_qty = frappe._dict()
|
||||
|
||||
for d in reserved_entries:
|
||||
key = (d.item_code, d.warehouse)
|
||||
if key not in itemwise_serial_batch_qty:
|
||||
itemwise_serial_batch_qty[key] = frappe._dict(
|
||||
{
|
||||
"serial_no": [],
|
||||
"batch_no": defaultdict(float),
|
||||
"batchwise_sn": defaultdict(list),
|
||||
}
|
||||
)
|
||||
|
||||
details = itemwise_serial_batch_qty[key]
|
||||
if d.batch_no:
|
||||
details.batch_no[d.batch_no] += d.qty
|
||||
if d.serial_no:
|
||||
details.batchwise_sn[d.batch_no].extend(d.serial_no.split("\n"))
|
||||
elif d.serial_no:
|
||||
details.serial_no.append(d.serial_no)
|
||||
|
||||
return itemwise_serial_batch_qty
|
||||
|
||||
def get_reserved_materials(self):
|
||||
doctype = frappe.qb.DocType("Stock Reservation Entry")
|
||||
serial_batch_doc = frappe.qb.DocType("Serial and Batch Entry")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(doctype)
|
||||
.inner_join(serial_batch_doc)
|
||||
.on(doctype.name == serial_batch_doc.parent)
|
||||
.select(
|
||||
serial_batch_doc.serial_no,
|
||||
serial_batch_doc.batch_no,
|
||||
serial_batch_doc.qty,
|
||||
doctype.item_code,
|
||||
doctype.warehouse,
|
||||
doctype.name,
|
||||
doctype.transferred_qty,
|
||||
doctype.consumed_qty,
|
||||
)
|
||||
.where(
|
||||
(doctype.docstatus == 1)
|
||||
& (doctype.voucher_no == (self.work_order or self.subcontracting_order))
|
||||
& (serial_batch_doc.delivered_qty < serial_batch_doc.qty)
|
||||
)
|
||||
.orderby(serial_batch_doc.idx)
|
||||
)
|
||||
|
||||
return query.run(as_dict=True)
|
||||
|
||||
def set_secondary_items(self):
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
secondary_items_dict = self.get_secondary_items(self.fg_completed_qty)
|
||||
for item in secondary_items_dict.values():
|
||||
if self.pro_doc and item.type:
|
||||
if self.pro_doc.scrap_warehouse and item.type == "Scrap":
|
||||
item["to_warehouse"] = self.pro_doc.scrap_warehouse
|
||||
|
||||
if item.process_loss_per:
|
||||
item["qty"] -= flt(
|
||||
item["qty"] * (item.process_loss_per / 100),
|
||||
self.precision("fg_completed_qty"),
|
||||
)
|
||||
|
||||
self.add_to_stock_entry_detail(secondary_items_dict, bom_no=self.bom_no)
|
||||
>>>>>>> ea392b2009 (fix: validate work order consistency in stock entry)
|
||||
|
||||
def set_process_loss_qty(self):
|
||||
if self.purpose not in ("Manufacture", "Repack"):
|
||||
|
||||
Reference in New Issue
Block a user