fix: disassemble qty calculation & max calculation to be allowed to create it
(cherry picked from commit 3e4d160626)
# Conflicts:
# erpnext/manufacturing/doctype/work_order/work_order.json
# erpnext/stock/doctype/stock_entry/stock_entry.py
This commit is contained in:
@@ -803,7 +803,7 @@ erpnext.work_order = {
|
||||
get_max_transferable_qty: (frm, purpose) => {
|
||||
let max = 0;
|
||||
if (purpose === "Disassemble") {
|
||||
return flt(frm.doc.produced_qty);
|
||||
return flt(frm.doc.produced_qty - frm.doc.disassembled_qty);
|
||||
}
|
||||
|
||||
if (frm.doc.skip_transfer) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"qty",
|
||||
"material_transferred_for_manufacturing",
|
||||
"produced_qty",
|
||||
"disassembled_qty",
|
||||
"process_loss_qty",
|
||||
"project",
|
||||
"section_break_ndpq",
|
||||
@@ -585,7 +586,34 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ndpq",
|
||||
<<<<<<< HEAD
|
||||
"fieldtype": "Section Break"
|
||||
=======
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Required Items"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "bom_no.track_semi_finished_goods",
|
||||
"fieldname": "track_semi_finished_goods",
|
||||
"fieldtype": "Check",
|
||||
"label": "Track Semi Finished Goods",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reserve_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": " Reserve Stock"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"fieldname": "disassembled_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Disassembled Qty",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
>>>>>>> 3e4d160626 (fix: disassemble qty calculation & max calculation to be allowed to create it)
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cogs",
|
||||
@@ -593,7 +621,11 @@
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
<<<<<<< HEAD
|
||||
"modified": "2024-02-11 15:47:13.454422",
|
||||
=======
|
||||
"modified": "2025-06-21 00:55:45.916224",
|
||||
>>>>>>> 3e4d160626 (fix: disassemble qty calculation & max calculation to be allowed to create it)
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order",
|
||||
|
||||
@@ -88,6 +88,7 @@ class WorkOrder(Document):
|
||||
company: DF.Link
|
||||
corrective_operation_cost: DF.Currency
|
||||
description: DF.SmallText | None
|
||||
disassembled_qty: DF.Float
|
||||
expected_delivery_date: DF.Date | None
|
||||
fg_warehouse: DF.Link
|
||||
from_wip_warehouse: DF.Check
|
||||
@@ -406,6 +407,18 @@ class WorkOrder(Document):
|
||||
self.set_produced_qty_for_sub_assembly_item()
|
||||
self.update_production_plan_status()
|
||||
|
||||
def update_disassembled_qty(self, qty, is_cancel=False):
|
||||
if is_cancel:
|
||||
self.disassembled_qty = max(0, self.disassembled_qty - qty)
|
||||
else:
|
||||
if self.docstatus == 1:
|
||||
self.disassembled_qty += qty
|
||||
|
||||
if not is_cancel and self.disassembled_qty > self.produced_qty:
|
||||
frappe.throw(_("Cannot disassemble more than produced quantity."))
|
||||
|
||||
self.db_set("disassembled_qty", self.disassembled_qty)
|
||||
|
||||
def get_transferred_or_manufactured_qty(self, purpose):
|
||||
table = frappe.qb.DocType("Stock Entry")
|
||||
query = frappe.qb.from_(table).where(
|
||||
@@ -1475,7 +1488,7 @@ def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None):
|
||||
stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse
|
||||
|
||||
stock_entry.set_stock_entry_type()
|
||||
stock_entry.get_items()
|
||||
stock_entry.get_items(qty, work_order.production_item)
|
||||
|
||||
if purpose != "Disassemble":
|
||||
stock_entry.set_serial_no_batch_for_finished_good()
|
||||
|
||||
@@ -27,6 +27,7 @@ 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.manufacturing.doctype.bom.bom import (
|
||||
add_additional_cost,
|
||||
get_bom_items_as_dict,
|
||||
get_op_cost_from_sub_assemblies,
|
||||
get_scrap_items_from_sub_assemblies,
|
||||
validate_bom_no,
|
||||
@@ -243,6 +244,11 @@ class StockEntry(StockController):
|
||||
def on_submit(self):
|
||||
self.validate_closed_subcontracting_order()
|
||||
self.make_bundle_using_old_serial_batch_fields()
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
self.update_work_order()
|
||||
self.update_disassembled_order()
|
||||
>>>>>>> 3e4d160626 (fix: disassemble qty calculation & max calculation to be allowed to create it)
|
||||
self.update_stock_ledger()
|
||||
self.update_work_order()
|
||||
self.validate_subcontract_order()
|
||||
@@ -271,6 +277,7 @@ class StockEntry(StockController):
|
||||
self.validate_work_order_status()
|
||||
|
||||
self.update_work_order()
|
||||
self.update_disassembled_order(is_cancel=True)
|
||||
self.update_stock_ledger()
|
||||
|
||||
self.ignore_linked_doctypes = (
|
||||
@@ -1617,6 +1624,50 @@ class StockEntry(StockController):
|
||||
if not pro_doc.operations:
|
||||
pro_doc.set_actual_dates()
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
def update_disassembled_order(self, is_cancel=False):
|
||||
if not self.work_order:
|
||||
return
|
||||
if self.purpose == "Disassemble" and self.fg_completed_qty:
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
pro_doc.run_method("update_disassembled_qty", self.fg_completed_qty, is_cancel)
|
||||
|
||||
def make_stock_reserve_for_wip_and_fg(self):
|
||||
if self.is_stock_reserve_for_work_order():
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
if (
|
||||
self.purpose == "Manufacture"
|
||||
and not pro_doc.sales_order
|
||||
and not pro_doc.production_plan_sub_assembly_item
|
||||
):
|
||||
return
|
||||
|
||||
pro_doc.set_reserved_qty_for_wip_and_fg(self)
|
||||
|
||||
def cancel_stock_reserve_for_wip_and_fg(self):
|
||||
if self.is_stock_reserve_for_work_order():
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
if (
|
||||
self.purpose == "Manufacture"
|
||||
and not pro_doc.sales_order
|
||||
and not pro_doc.production_plan_sub_assembly_item
|
||||
):
|
||||
return
|
||||
|
||||
pro_doc.cancel_reserved_qty_for_wip_and_fg(self)
|
||||
|
||||
def is_stock_reserve_for_work_order(self):
|
||||
if (
|
||||
self.work_order
|
||||
and self.stock_entry_type in ["Material Transfer for Manufacture", "Manufacture"]
|
||||
and frappe.get_cached_value("Work Order", self.work_order, "reserve_stock")
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
>>>>>>> 3e4d160626 (fix: disassemble qty calculation & max calculation to be allowed to create it)
|
||||
@frappe.whitelist()
|
||||
def get_item_details(self, args=None, for_update=False):
|
||||
item = frappe.qb.DocType("Item")
|
||||
@@ -1759,7 +1810,7 @@ class StockEntry(StockController):
|
||||
},
|
||||
)
|
||||
|
||||
def get_items_for_disassembly(self):
|
||||
def get_items_for_disassembly(self, disassemble_qty, production_item):
|
||||
"""Get items for Disassembly Order"""
|
||||
|
||||
if not self.work_order:
|
||||
@@ -1767,9 +1818,9 @@ class StockEntry(StockController):
|
||||
|
||||
items = self.get_items_from_manufacture_entry()
|
||||
|
||||
s_warehouse = ""
|
||||
if self.work_order:
|
||||
s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse")
|
||||
s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse")
|
||||
|
||||
items_dict = get_bom_items_as_dict(self.bom_no, self.company, disassemble_qty)
|
||||
|
||||
for row in items:
|
||||
child_row = self.append("items", {})
|
||||
@@ -1777,6 +1828,15 @@ class StockEntry(StockController):
|
||||
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.item_code == production_item:
|
||||
child_row.qty = disassemble_qty
|
||||
|
||||
child_row.s_warehouse = (self.from_warehouse or s_warehouse) if row.is_finished_item else ""
|
||||
child_row.t_warehouse = self.to_warehouse if not row.is_finished_item else ""
|
||||
child_row.is_finished_item = 0 if row.is_finished_item else 1
|
||||
@@ -1809,12 +1869,13 @@ class StockEntry(StockController):
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_items(self):
|
||||
def get_items(self, qty, production_item):
|
||||
self.set("items", [])
|
||||
self.validate_work_order()
|
||||
# print(qty, 'qty\n\n')
|
||||
|
||||
if self.purpose == "Disassemble":
|
||||
return self.get_items_for_disassembly()
|
||||
if self.purpose == "Disassemble" and qty is not None:
|
||||
return self.get_items_for_disassembly(qty, production_item)
|
||||
|
||||
if not self.posting_date or not self.posting_time:
|
||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||
|
||||
Reference in New Issue
Block a user