fix: calculate operating cost based on BOM Quantity (#27464)
* fix: calculate operating cost based on BOM Quantity
* fix: added test cases
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 28a84b2..232e3a0 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -510,8 +510,14 @@
if d.workstation:
self.update_rate_and_time(d, update_hour_rate)
- self.operating_cost += flt(d.operating_cost)
- self.base_operating_cost += flt(d.base_operating_cost)
+ operating_cost = d.operating_cost
+ base_operating_cost = d.base_operating_cost
+ if d.set_cost_based_on_bom_qty:
+ operating_cost = flt(d.cost_per_unit) * flt(self.quantity)
+ base_operating_cost = flt(d.base_cost_per_unit) * flt(self.quantity)
+
+ self.operating_cost += flt(operating_cost)
+ self.base_operating_cost += flt(base_operating_cost)
def update_rate_and_time(self, row, update_hour_rate = False):
if not row.hour_rate or update_hour_rate:
@@ -535,6 +541,8 @@
row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate)
row.operating_cost = flt(row.hour_rate) * flt(row.time_in_mins) / 60.0
row.base_operating_cost = flt(row.operating_cost) * flt(self.conversion_rate)
+ row.cost_per_unit = row.operating_cost / (row.batch_size or 1.0)
+ row.base_cost_per_unit = row.base_operating_cost / (row.batch_size or 1.0)
if update_hour_rate:
row.db_update()
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 7950dd9..706ea26 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -108,6 +108,24 @@
self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
+ def test_bom_cost_with_batch_size(self):
+ bom = frappe.copy_doc(test_records[2])
+ bom.docstatus = 0
+ op_cost = 0.0
+ for op_row in bom.operations:
+ op_row.docstatus = 0
+ op_row.batch_size = 2
+ op_row.set_cost_based_on_bom_qty = 1
+ op_cost += op_row.operating_cost
+
+ bom.save()
+
+ for op_row in bom.operations:
+ self.assertAlmostEqual(op_row.cost_per_unit, op_row.operating_cost / 2)
+
+ self.assertAlmostEqual(bom.operating_cost, op_cost/2)
+ bom.delete()
+
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index 4458e6d..ec617f3 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -8,15 +8,23 @@
"field_order": [
"sequence_id",
"operation",
- "workstation",
- "description",
"col_break1",
- "hour_rate",
+ "workstation",
"time_in_mins",
- "operating_cost",
+ "costing_section",
+ "hour_rate",
"base_hour_rate",
+ "column_break_9",
+ "operating_cost",
"base_operating_cost",
+ "column_break_11",
"batch_size",
+ "set_cost_based_on_bom_qty",
+ "cost_per_unit",
+ "base_cost_per_unit",
+ "more_information_section",
+ "description",
+ "column_break_18",
"image"
],
"fields": [
@@ -117,13 +125,59 @@
"fieldname": "sequence_id",
"fieldtype": "Int",
"label": "Sequence ID"
+ },
+ {
+ "depends_on": "eval:doc.batch_size > 0 && doc.set_cost_based_on_bom_qty",
+ "fieldname": "cost_per_unit",
+ "fieldtype": "Float",
+ "label": "Cost Per Unit",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "base_cost_per_unit",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "label": "Base Cost Per Unit",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "costing_section",
+ "fieldtype": "Section Break",
+ "label": "Costing"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "more_information_section",
+ "fieldtype": "Section Break",
+ "label": "More Information"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "set_cost_based_on_bom_qty",
+ "fieldtype": "Check",
+ "label": "Set Operating Cost Based On BOM Quantity"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-01-12 14:48:09.596843",
+ "modified": "2021-09-13 16:45:01.092868",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Operation",