From 69682cb06463864b5d2c4679a6e113c65e15d0c9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 17 Sep 2025 15:50:45 +0530 Subject: [PATCH] fix: operation cost for Semi FG item --- erpnext/manufacturing/doctype/bom/bom.py | 22 ++++++++++++------- .../doctype/job_card/job_card.py | 13 ++++++----- .../doctype/work_order/work_order.py | 9 ++++++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index f946e80280d..28da6a9e275 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1414,7 +1414,7 @@ def get_children(parent=None, is_root=False, **filters): return bom_items -def add_additional_cost(stock_entry, work_order): +def add_additional_cost(stock_entry, work_order, job_card=None): # Add non stock items cost in the additional cost stock_entry.additional_costs = [] company_account = frappe.db.get_value( @@ -1427,13 +1427,16 @@ def add_additional_cost(stock_entry, work_order): expense_account = ( company_account.default_operating_cost_account or company_account.default_expense_account ) - add_non_stock_items_cost(stock_entry, work_order, expense_account) - add_operations_cost(stock_entry, work_order, expense_account) + add_non_stock_items_cost(stock_entry, work_order, expense_account, job_card=job_card) + add_operations_cost(stock_entry, work_order, expense_account, job_card=job_card) -def add_non_stock_items_cost(stock_entry, work_order, expense_account): +def add_non_stock_items_cost(stock_entry, work_order, expense_account, job_card=None): bom = frappe.get_doc("BOM", work_order.bom_no) - table = "exploded_items" if work_order.get("use_multi_level_bom") else "items" + + table = "items" + if work_order and not job_card: + table = "exploded_items" if work_order.get("use_multi_level_bom") else "items" items = {} for d in bom.get(table): @@ -1464,13 +1467,16 @@ def add_non_stock_items_cost(stock_entry, work_order, expense_account): def add_operating_cost_component_wise( - stock_entry, work_order=None, operating_cost_per_unit=None, op_expense_account=None + stock_entry, work_order=None, operating_cost_per_unit=None, op_expense_account=None, job_card=None ): if not work_order: return False cost_added = False for row in work_order.operations: + if job_card and job_card.operation_id != row.name: + continue + workstation_cost = frappe.get_all( "Workstation Cost", fields=["operating_component", "operating_cost"], @@ -1511,14 +1517,14 @@ def get_component_account(parent): return frappe.db.get_value("Workstation Operating Component Account", parent, "expense_account") -def add_operations_cost(stock_entry, work_order=None, expense_account=None): +def add_operations_cost(stock_entry, work_order=None, expense_account=None, job_card=None): from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no) if operating_cost_per_unit: cost_added = add_operating_cost_component_wise( - stock_entry, work_order, operating_cost_per_unit, expense_account + stock_entry, work_order, operating_cost_per_unit, expense_account, job_card=job_card ) if not cost_added: diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index a20c2e12043..3de30c38881 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -23,6 +23,7 @@ from frappe.utils import ( time_diff_in_hours, ) +from erpnext.manufacturing.doctype.bom.bom import add_additional_cost from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import ( get_mins_between_operations, ) @@ -817,9 +818,6 @@ class JobCard(Document): ) def update_work_order(self): - if self.track_semi_finished_goods: - return - if not self.work_order: return @@ -849,9 +847,9 @@ class JobCard(Document): def update_semi_finished_good_details(self): if self.operation_id: - frappe.db.set_value( - "Work Order Operation", self.operation_id, "completed_qty", self.manufactured_qty - ) + qty = max(flt(self.manufactured_qty), flt(self.total_completed_qty)) + + frappe.db.set_value("Work Order Operation", self.operation_id, "completed_qty", qty) if ( self.finished_good and frappe.get_cached_value("Work Order", self.work_order, "production_item") @@ -1322,6 +1320,9 @@ class JobCard(Document): ste.make_stock_entry() ste.stock_entry.flags.ignore_mandatory = True + wo_doc = frappe.get_doc("Work Order", self.work_order) + add_additional_cost(ste.stock_entry, wo_doc, self) + ste.stock_entry.save() if auto_submit: diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 8f9991e21a2..55ce1f4dc16 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -355,6 +355,10 @@ class WorkOrder(Document): def calculate_operating_cost(self): self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0 for d in self.get("operations"): + if not d.hour_rate: + if d.workstation: + d.hour_rate = get_hour_rate(d.workstation) + d.planned_operating_cost = flt( flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0), d.precision("planned_operating_cost") ) @@ -2432,3 +2436,8 @@ def get_row_wise_serial_batch(work_order, purpose=None): details.batch_nos[entry.batch_no] += abs(entry.qty) return row_wise_serial_batch + + +@frappe.request_cache +def get_hour_rate(workstation): + return frappe.get_cached_value("Workstation", workstation, "hour_rate") or 0.0