mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 03:15:07 +00:00
fix: add support to fetch items based on manufacture stock entry; fix how it's done from work order
(cherry picked from commit 1ed0124ad7)
# Conflicts:
# erpnext/stock/doctype/stock_entry/stock_entry.py
This commit is contained in:
@@ -28,7 +28,6 @@ from erpnext.buying.utils import check_on_hold_or_closed_status
|
|||||||
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
||||||
from erpnext.manufacturing.doctype.bom.bom import (
|
from erpnext.manufacturing.doctype.bom.bom import (
|
||||||
add_additional_cost,
|
add_additional_cost,
|
||||||
get_bom_items_as_dict,
|
|
||||||
get_op_cost_from_sub_assemblies,
|
get_op_cost_from_sub_assemblies,
|
||||||
get_scrap_items_from_sub_assemblies,
|
get_scrap_items_from_sub_assemblies,
|
||||||
validate_bom_no,
|
validate_bom_no,
|
||||||
@@ -1984,45 +1983,108 @@ class StockEntry(StockController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_items_for_disassembly(self):
|
def get_items_for_disassembly(self):
|
||||||
"""Get items for Disassembly Order"""
|
"""Get items for Disassembly Order.
|
||||||
|
|
||||||
|
Priority:
|
||||||
|
1. From a specific Manufacture Stock Entry (exact reversal)
|
||||||
|
2. From Work Order required_items (reflects WO changes)
|
||||||
|
3. From BOM (standalone disassembly)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.get("source_stock_entry"):
|
||||||
|
return self._add_items_for_disassembly_from_stock_entry()
|
||||||
|
|
||||||
if self.work_order:
|
if self.work_order:
|
||||||
return self._add_items_for_disassembly_from_work_order()
|
return self._add_items_for_disassembly_from_work_order()
|
||||||
|
|
||||||
return self._add_items_for_disassembly_from_bom()
|
return self._add_items_for_disassembly_from_bom()
|
||||||
|
|
||||||
|
def _add_items_for_disassembly_from_stock_entry(self):
|
||||||
|
source_fg_qty = frappe.db.get_value("Stock Entry", self.source_stock_entry, "fg_completed_qty")
|
||||||
|
if not source_fg_qty:
|
||||||
|
frappe.throw(
|
||||||
|
_("Source Stock Entry {0} has no finished goods quantity").format(self.source_stock_entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
scale_factor = flt(self.fg_completed_qty) / flt(source_fg_qty)
|
||||||
|
|
||||||
|
for source_row in self.get_items_from_manufacture_stock_entry(self.source_stock_entry):
|
||||||
|
if source_row.is_finished_item:
|
||||||
|
qty = flt(self.fg_completed_qty)
|
||||||
|
s_warehouse = self.from_warehouse or source_row.t_warehouse
|
||||||
|
t_warehouse = ""
|
||||||
|
else:
|
||||||
|
qty = flt(source_row.qty * scale_factor)
|
||||||
|
s_warehouse = ""
|
||||||
|
t_warehouse = self.to_warehouse or source_row.s_warehouse
|
||||||
|
|
||||||
|
use_serial_batch_fields = 1 if (source_row.batch_no or source_row.serial_no) else 0
|
||||||
|
|
||||||
|
self.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": source_row.item_code,
|
||||||
|
"item_name": source_row.item_name,
|
||||||
|
"description": source_row.description,
|
||||||
|
"stock_uom": source_row.stock_uom,
|
||||||
|
"uom": source_row.uom,
|
||||||
|
"conversion_factor": source_row.conversion_factor,
|
||||||
|
"basic_rate": source_row.basic_rate,
|
||||||
|
"qty": qty,
|
||||||
|
"s_warehouse": s_warehouse,
|
||||||
|
"t_warehouse": t_warehouse,
|
||||||
|
"is_finished_item": source_row.is_finished_item,
|
||||||
|
"against_stock_entry": self.source_stock_entry,
|
||||||
|
"ste_detail": source_row.name,
|
||||||
|
"batch_no": source_row.batch_no,
|
||||||
|
"serial_no": source_row.serial_no,
|
||||||
|
"use_serial_batch_fields": use_serial_batch_fields,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def _add_items_for_disassembly_from_work_order(self):
|
def _add_items_for_disassembly_from_work_order(self):
|
||||||
items = self.get_items_from_manufacture_entry()
|
wo = frappe.get_doc("Work Order", self.work_order)
|
||||||
|
|
||||||
s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse")
|
if not wo.required_items:
|
||||||
|
return self._add_items_for_disassembly_from_bom()
|
||||||
|
|
||||||
items_dict = get_bom_items_as_dict(
|
scale_factor = flt(self.fg_completed_qty) / flt(wo.qty) if flt(wo.qty) else 0
|
||||||
self.bom_no,
|
|
||||||
self.company,
|
# RMs
|
||||||
self.fg_completed_qty,
|
for ri in wo.required_items:
|
||||||
fetch_exploded=self.use_multi_level_bom,
|
self.append(
|
||||||
fetch_qty_in_stock_uom=False,
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": ri.item_code,
|
||||||
|
"item_name": ri.item_name,
|
||||||
|
"description": ri.description,
|
||||||
|
"qty": flt(ri.required_qty * scale_factor),
|
||||||
|
"stock_uom": ri.stock_uom,
|
||||||
|
"uom": ri.stock_uom,
|
||||||
|
"conversion_factor": 1,
|
||||||
|
"t_warehouse": ri.source_warehouse or wo.source_warehouse or self.to_warehouse,
|
||||||
|
"s_warehouse": "",
|
||||||
|
"is_finished_item": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# FG
|
||||||
|
self.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": wo.production_item,
|
||||||
|
"item_name": wo.item_name,
|
||||||
|
"description": wo.description,
|
||||||
|
"qty": flt(self.fg_completed_qty),
|
||||||
|
"stock_uom": wo.stock_uom,
|
||||||
|
"uom": wo.stock_uom,
|
||||||
|
"conversion_factor": 1,
|
||||||
|
"s_warehouse": self.from_warehouse or wo.fg_warehouse,
|
||||||
|
"t_warehouse": "",
|
||||||
|
"is_finished_item": 1,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in items:
|
|
||||||
child_row = self.append("items", {})
|
|
||||||
for field, value in row.items():
|
|
||||||
if value is not None:
|
|
||||||
child_row.set(field, value)
|
|
||||||
|
|
||||||
# update qty and amount from BOM items
|
|
||||||
bom_items = items_dict.get(row.item_code)
|
|
||||||
if bom_items:
|
|
||||||
child_row.qty = bom_items.get("qty", child_row.qty)
|
|
||||||
child_row.amount = bom_items.get("amount", child_row.amount)
|
|
||||||
|
|
||||||
if row.is_finished_item:
|
|
||||||
child_row.qty = self.fg_completed_qty
|
|
||||||
|
|
||||||
child_row.s_warehouse = (self.from_warehouse or s_warehouse) if row.is_finished_item else ""
|
|
||||||
child_row.t_warehouse = row.s_warehouse
|
|
||||||
child_row.is_finished_item = 0 if row.is_finished_item else 1
|
|
||||||
|
|
||||||
def _add_items_for_disassembly_from_bom(self):
|
def _add_items_for_disassembly_from_bom(self):
|
||||||
if not self.bom_no or not self.fg_completed_qty:
|
if not self.bom_no or not self.fg_completed_qty:
|
||||||
frappe.throw(_("BOM and Finished Good Quantity is mandatory for Disassembly"))
|
frappe.throw(_("BOM and Finished Good Quantity is mandatory for Disassembly"))
|
||||||
@@ -2040,6 +2102,7 @@ class StockEntry(StockController):
|
|||||||
# Finished goods
|
# Finished goods
|
||||||
self.load_items_from_bom()
|
self.load_items_from_bom()
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
def get_items_from_manufacture_entry(self):
|
def get_items_from_manufacture_entry(self):
|
||||||
return frappe.get_all(
|
return frappe.get_all(
|
||||||
"Stock Entry",
|
"Stock Entry",
|
||||||
@@ -2068,6 +2131,38 @@ class StockEntry(StockController):
|
|||||||
],
|
],
|
||||||
order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc",
|
order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc",
|
||||||
group_by="`tabStock Entry Detail`.`item_code`",
|
group_by="`tabStock Entry Detail`.`item_code`",
|
||||||
|
=======
|
||||||
|
def get_items_from_manufacture_stock_entry(self, stock_entry):
|
||||||
|
SE = frappe.qb.DocType("Stock Entry")
|
||||||
|
SED = frappe.qb.DocType("Stock Entry Detail")
|
||||||
|
|
||||||
|
return (
|
||||||
|
frappe.qb.from_(SED)
|
||||||
|
.join(SE)
|
||||||
|
.on(SED.parent == SE.name)
|
||||||
|
.select(
|
||||||
|
SED.name,
|
||||||
|
SED.item_code,
|
||||||
|
SED.item_name,
|
||||||
|
SED.description,
|
||||||
|
SED.qty,
|
||||||
|
SED.transfer_qty,
|
||||||
|
SED.stock_uom,
|
||||||
|
SED.uom,
|
||||||
|
SED.basic_rate,
|
||||||
|
SED.conversion_factor,
|
||||||
|
SED.is_finished_item,
|
||||||
|
SED.batch_no,
|
||||||
|
SED.serial_no,
|
||||||
|
SED.use_serial_batch_fields,
|
||||||
|
SED.s_warehouse,
|
||||||
|
SED.t_warehouse,
|
||||||
|
)
|
||||||
|
.where(SE.name == stock_entry)
|
||||||
|
.where(SE.docstatus == 1)
|
||||||
|
.orderby(SED.idx)
|
||||||
|
.run(as_dict=True)
|
||||||
|
>>>>>>> 1ed0124ad7 (fix: add support to fetch items based on manufacture stock entry; fix how it's done from work order)
|
||||||
)
|
)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
Reference in New Issue
Block a user