fix: disassemble qty calculation & max calculation to be allowed to create it
This commit is contained in:
@@ -877,7 +877,7 @@ erpnext.work_order = {
|
|||||||
get_max_transferable_qty: (frm, purpose) => {
|
get_max_transferable_qty: (frm, purpose) => {
|
||||||
let max = 0;
|
let max = 0;
|
||||||
if (purpose === "Disassemble") {
|
if (purpose === "Disassemble") {
|
||||||
return flt(frm.doc.produced_qty);
|
return flt(frm.doc.produced_qty - frm.doc.disassembled_qty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.skip_transfer) {
|
if (frm.doc.skip_transfer) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"qty",
|
"qty",
|
||||||
"material_transferred_for_manufacturing",
|
"material_transferred_for_manufacturing",
|
||||||
"produced_qty",
|
"produced_qty",
|
||||||
|
"disassembled_qty",
|
||||||
"process_loss_qty",
|
"process_loss_qty",
|
||||||
"project",
|
"project",
|
||||||
"track_semi_finished_goods",
|
"track_semi_finished_goods",
|
||||||
@@ -592,6 +593,14 @@
|
|||||||
"fieldname": "reserve_stock",
|
"fieldname": "reserve_stock",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": " Reserve Stock"
|
"label": " Reserve Stock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==1",
|
||||||
|
"fieldname": "disassembled_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Disassembled Qty",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
@@ -600,7 +609,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-04-25 11:46:38.739588",
|
"modified": "2025-06-21 00:55:45.916224",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order",
|
"name": "Work Order",
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class WorkOrder(Document):
|
|||||||
company: DF.Link
|
company: DF.Link
|
||||||
corrective_operation_cost: DF.Currency
|
corrective_operation_cost: DF.Currency
|
||||||
description: DF.SmallText | None
|
description: DF.SmallText | None
|
||||||
|
disassembled_qty: DF.Float
|
||||||
expected_delivery_date: DF.Date | None
|
expected_delivery_date: DF.Date | None
|
||||||
fg_warehouse: DF.Link | None
|
fg_warehouse: DF.Link | None
|
||||||
from_wip_warehouse: DF.Check
|
from_wip_warehouse: DF.Check
|
||||||
@@ -477,6 +478,18 @@ class WorkOrder(Document):
|
|||||||
self.set_produced_qty_for_sub_assembly_item()
|
self.set_produced_qty_for_sub_assembly_item()
|
||||||
self.update_production_plan_status()
|
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):
|
def get_transferred_or_manufactured_qty(self, purpose):
|
||||||
table = frappe.qb.DocType("Stock Entry")
|
table = frappe.qb.DocType("Stock Entry")
|
||||||
query = frappe.qb.from_(table).where(
|
query = frappe.qb.from_(table).where(
|
||||||
@@ -1964,7 +1977,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.to_warehouse = target_warehouse or work_order.source_warehouse
|
||||||
|
|
||||||
stock_entry.set_stock_entry_type()
|
stock_entry.set_stock_entry_type()
|
||||||
stock_entry.get_items()
|
stock_entry.get_items(qty, work_order.production_item)
|
||||||
|
|
||||||
if purpose != "Disassemble":
|
if purpose != "Disassemble":
|
||||||
stock_entry.set_serial_no_batch_for_finished_good()
|
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.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,
|
||||||
@@ -248,6 +249,7 @@ class StockEntry(StockController):
|
|||||||
self.validate_closed_subcontracting_order()
|
self.validate_closed_subcontracting_order()
|
||||||
self.make_bundle_using_old_serial_batch_fields()
|
self.make_bundle_using_old_serial_batch_fields()
|
||||||
self.update_work_order()
|
self.update_work_order()
|
||||||
|
self.update_disassembled_order()
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
self.make_stock_reserve_for_wip_and_fg()
|
self.make_stock_reserve_for_wip_and_fg()
|
||||||
|
|
||||||
@@ -278,6 +280,7 @@ class StockEntry(StockController):
|
|||||||
self.validate_work_order_status()
|
self.validate_work_order_status()
|
||||||
|
|
||||||
self.update_work_order()
|
self.update_work_order()
|
||||||
|
self.update_disassembled_order(is_cancel=True)
|
||||||
self.update_stock_ledger()
|
self.update_stock_ledger()
|
||||||
|
|
||||||
self.ignore_linked_doctypes = (
|
self.ignore_linked_doctypes = (
|
||||||
@@ -1650,6 +1653,13 @@ class StockEntry(StockController):
|
|||||||
if not pro_doc.operations:
|
if not pro_doc.operations:
|
||||||
pro_doc.set_actual_dates()
|
pro_doc.set_actual_dates()
|
||||||
|
|
||||||
|
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):
|
def make_stock_reserve_for_wip_and_fg(self):
|
||||||
if self.is_stock_reserve_for_work_order():
|
if self.is_stock_reserve_for_work_order():
|
||||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||||
@@ -1826,7 +1836,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"""
|
"""Get items for Disassembly Order"""
|
||||||
|
|
||||||
if not self.work_order:
|
if not self.work_order:
|
||||||
@@ -1834,9 +1844,9 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
items = self.get_items_from_manufacture_entry()
|
items = self.get_items_from_manufacture_entry()
|
||||||
|
|
||||||
s_warehouse = ""
|
s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse")
|
||||||
if self.work_order:
|
|
||||||
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:
|
for row in items:
|
||||||
child_row = self.append("items", {})
|
child_row = self.append("items", {})
|
||||||
@@ -1844,6 +1854,15 @@ class StockEntry(StockController):
|
|||||||
if value is not None:
|
if value is not None:
|
||||||
child_row.set(field, value)
|
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.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.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
|
child_row.is_finished_item = 0 if row.is_finished_item else 1
|
||||||
@@ -1876,12 +1895,13 @@ class StockEntry(StockController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_items(self):
|
def get_items(self, qty, production_item):
|
||||||
self.set("items", [])
|
self.set("items", [])
|
||||||
self.validate_work_order()
|
self.validate_work_order()
|
||||||
|
# print(qty, 'qty\n\n')
|
||||||
|
|
||||||
if self.purpose == "Disassemble":
|
if self.purpose == "Disassemble" and qty is not None:
|
||||||
return self.get_items_for_disassembly()
|
return self.get_items_for_disassembly(qty, production_item)
|
||||||
|
|
||||||
if not self.posting_date or not self.posting_time:
|
if not self.posting_date or not self.posting_time:
|
||||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||||
|
|||||||
Reference in New Issue
Block a user