diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 7787ea4878..32c176a857 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -66,7 +66,10 @@ cur_frm.cscript.workstation = function(doc,dt,dn) { frappe.model.with_doc("Workstation", d.workstation, function(name, r) { d.hour_rate = r.docs[0].hour_rate; refresh_field("hour_rate", dn, "bom_operations"); + d.fixed_cycle_cost = r.docs[0].fixed_cycle_cost; + refresh_field("fixed_cycle_cost", dn, "bom_operations"); erpnext.bom.calculate_op_cost(doc); + erpnext.bom.calculate_fixed_cost(doc); erpnext.bom.calculate_total(doc); }); } @@ -74,6 +77,7 @@ cur_frm.cscript.workstation = function(doc,dt,dn) { cur_frm.cscript.hour_rate = function(doc, dt, dn) { erpnext.bom.calculate_op_cost(doc); + erpnext.bom.calculate_fixed_cost(doc); erpnext.bom.calculate_total(doc); } @@ -116,7 +120,6 @@ var get_bom_material_detail= function(doc, cdt, cdn) { } } - cur_frm.cscript.qty = function(doc, cdt, cdn) { erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_total(doc); @@ -145,6 +148,17 @@ erpnext.bom.calculate_op_cost = function(doc) { refresh_field('operating_cost'); } +erpnext.bom.calculate_fixed_cost = function(doc) { + var op = doc.bom_operations || []; + var total_fixed_cost = 0; + for(var i=0;i
- {%= doc.get_formatted("total_cost") %} + {%= doc.get_formatted("total_variable_cost") %}
diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 71d54a20dc..085e2dd0ea 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -1,3 +1,3 @@ frappe.listview_settings['BOM'] = { - add_fields: ["is_active", "is_default", "total_cost"] + add_fields: ["is_active", "is_default", "total_variable_cost"] }; diff --git a/erpnext/manufacturing/doctype/bom/test_records.json b/erpnext/manufacturing/doctype/bom/test_records.json index efd26c243f..17c28d5d84 100644 --- a/erpnext/manufacturing/doctype/bom/test_records.json +++ b/erpnext/manufacturing/doctype/bom/test_records.json @@ -54,10 +54,20 @@ "is_default": 1, "item": "_Test FG Item", "quantity": 1.0 - }, + }, { + "bom_operations": [ + { + "operation_no": "1", + "opn_description": "_Test", + "workstation": "_Test Workstation 1", + "time_in_min": 60, + "operating_cost": 100 + } + ], "bom_materials": [ { + "operation_no": 1, "amount": 5000.0, "doctype": "BOM Item", "item_code": "_Test Item", @@ -67,6 +77,7 @@ "stock_uom": "_Test UOM" }, { + "operation_no": 1, "amount": 2000.0, "bom_no": "BOM/_Test Item Home Desktop Manufactured/001", "doctype": "BOM Item", @@ -82,6 +93,7 @@ "is_active": 1, "is_default": 1, "item": "_Test FG Item 2", - "quantity": 1.0 + "quantity": 1.0, + "with_operations": 1 } ] \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3bf5862c19..5d158b0f9d 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -1,5 +1,5 @@ { - "creation": "2013-02-22 01:27:49.000000", + "creation": "2013-02-22 01:27:49", "docstatus": 0, "doctype": "DocType", "fields": [ @@ -16,7 +16,7 @@ { "fieldname": "opn_description", "fieldtype": "Text", - "in_list_view": 1, + "in_list_view": 0, "label": "Operation Description", "oldfieldname": "opn_description", "oldfieldtype": "Text", @@ -69,13 +69,21 @@ "oldfieldtype": "Currency", "permlevel": 0, "reqd": 0 + }, + { + "fieldname": "fixed_cycle_cost", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Fixed Cycle Cost", + "permlevel": 0 } ], "idx": 1, "istable": 1, - "modified": "2014-02-03 12:53:03.000000", + "modified": "2014-09-08 16:29:22.293314", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", - "owner": "Administrator" + "owner": "Administrator", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py b/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py index 63030b588b..a5b2a53792 100644 --- a/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py +++ b/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py @@ -25,7 +25,7 @@ class BOMReplaceTool(Document): frappe.throw(_("Current BOM and New BOM can not be same")) def update_new_bom(self): - current_bom_unitcost = frappe.db.sql("""select total_cost/quantity + current_bom_unitcost = frappe.db.sql("""select total_variable_cost/quantity from `tabBOM` where name = %s""", self.current_bom) current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0 frappe.db.sql("""update `tabBOM Item` set bom_no=%s, diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js index a4bf14c8d7..89ef846212 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.js +++ b/erpnext/manufacturing/doctype/production_order/production_order.js @@ -123,3 +123,5 @@ cur_frm.set_query("bom_no", function(doc) { } } else msgprint(__("Please enter Production Item first")); }); + +cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost'); \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json index f5e43b0144..8ce29dc711 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.json +++ b/erpnext/manufacturing/doctype/production_order/production_order.json @@ -101,6 +101,13 @@ "read_only": 0, "reqd": 1 }, + { + "depends_on": "production_item", + "fieldname": "total_fixed_cost", + "fieldtype": "Float", + "label": "Total Fixed Cost", + "permlevel": 0 + }, { "depends_on": "eval:doc.docstatus==1", "description": "Automatically updated via Stock Entry of type Manufacture/Repack", @@ -225,7 +232,7 @@ "idx": 1, "in_create": 0, "is_submittable": 1, - "modified": "2014-06-23 07:55:50.092300", + "modified": "2014-09-01 11:45:48.591196", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Order", diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 99a248bf1b..03fdf79ec7 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -24,6 +24,7 @@ class ProductionOrder(Document): self.validate_bom_no() self.validate_sales_order() self.validate_warehouse() + self.set_fixed_cost() from erpnext.utilities.transaction_base import validate_uom_is_integer validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"]) @@ -55,6 +56,10 @@ class ProductionOrder(Document): for w in [self.fg_warehouse, self.wip_warehouse]: validate_warehouse_company(w, self.company) + def set_fixed_cost(self): + if self.total_fixed_cost==None: + self.total_fixed_cost = frappe.db.get_value("BOM", self.bom_no, "total_fixed_cost") + def validate_production_order_against_so(self): # already ordered qty ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order` @@ -156,11 +161,10 @@ def get_item_details(item): return {} res = res[0] - bom = frappe.db.sql("""select name from `tabBOM` where item=%s - and ifnull(is_default, 0)=1""", item) + bom = frappe.db.sql("""select name as bom_no,total_fixed_cost from `tabBOM` where item=%s + and ifnull(is_default, 0)=1""", item, as_dict=1) if bom: - res.bom_no = bom[0][0] - + res.update(bom[0]) return res @frappe.whitelist() diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index 2736be4efc..55125cf848 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -54,5 +54,4 @@ class TestProductionOrder(unittest.TestCase): self.assertRaises(StockOverProductionError, s.submit) - -test_records = frappe.get_test_records('Production Order') +test_records = frappe.get_test_records('Production Order') \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 547ca8b4ac..f0bb9377c4 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -153,7 +153,6 @@ class ProductionPlanningTool(Document): pi.so_pending_qty = flt(p['pending_qty']) pi.planned_qty = flt(p['pending_qty']) - def validate_data(self): self.validate_company() for d in self.get('pp_details'): diff --git a/erpnext/manufacturing/doctype/workstation/test_records.json b/erpnext/manufacturing/doctype/workstation/test_records.json new file mode 100644 index 0000000000..72123eb282 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doctype": "Workstation", + "name": "_Test Workstation 1", + "workstation_name": "_Test Workstation 1", + "warehouse": "_Test warehouse - _TC", + "fixed_cycle_cost": 1000, + "hour_rate":100 + } +] diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py new file mode 100644 index 0000000000..01746ebc44 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py @@ -0,0 +1,12 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and Contributors +# See license.txt + +import frappe +import unittest + +test_dependencies = ["Warehouse"] +test_records = frappe.get_test_records('Workstation') + + +class TestWorkstation(unittest.TestCase): + pass diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json index 278707e615..83ab4a80be 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.json +++ b/erpnext/manufacturing/doctype/workstation/workstation.json @@ -61,6 +61,12 @@ "permlevel": 0, "reqd": 0 }, + { + "fieldname": "fixed_cycle_cost", + "fieldtype": "Float", + "label": "Fixed Cycle Cost", + "permlevel": 0 + }, { "fieldname": "hour_rate_labour", "fieldtype": "Float", @@ -132,7 +138,7 @@ ], "icon": "icon-wrench", "idx": 1, - "modified": "2014-05-27 03:49:22.635046", + "modified": "2014-08-30 10:59:07.960814", "modified_by": "Administrator", "module": "Manufacturing", "name": "Workstation", diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index ec026c5c29..935e7509d4 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -20,4 +20,4 @@ class Workstation(Document): frappe.db.set(self, 'overhead', flt(self.hour_rate_electricity) + flt(self.hour_rate_consumable) + flt(self.hour_rate_rent)) frappe.db.set(self, 'hour_rate', flt(self.hour_rate_labour) + flt(self.overhead)) - self.update_bom_operation() \ No newline at end of file + self.update_bom_operation() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9146336fd5..2f0dd2ef5e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -80,3 +80,4 @@ execute:frappe.delete_doc("DocType", "Landed Cost Wizard") erpnext.patches.v4_2.default_website_style erpnext.patches.v4_2.set_company_country erpnext.patches.v4_2.update_sales_order_invoice_field_name +erpnext.patches.v4_2.cost_of_production_cycle \ No newline at end of file diff --git a/erpnext/patches/v4_2/cost_of_production_cycle.py b/erpnext/patches/v4_2/cost_of_production_cycle.py new file mode 100644 index 0000000000..26f0fcad5b --- /dev/null +++ b/erpnext/patches/v4_2/cost_of_production_cycle.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "bom") + frappe.db.sql("""update tabBOM set total_variable_cost = total_cost""") \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index af6493d27f..2faa28830c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -457,3 +457,4 @@ cur_frm.fields_dict.customer.get_query = function(doc, cdt, cdn) { cur_frm.fields_dict.supplier.get_query = function(doc, cdt, cdn) { return { query: "erpnext.controllers.queries.supplier_query" } } +cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost'); \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index b6ef6f35e2..97c4882e1f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -297,6 +297,14 @@ "reqd": 0, "search_index": 0 }, + { + "depends_on": "eval:doc.purpose==\"Manufacture/Repack\"", + "fieldname": "total_fixed_cost", + "fieldtype": "Float", + "label": "Total Fixed Cost", + "permlevel": 0, + "read_only": 0 + }, { "fieldname": "cb1", "fieldtype": "Column Break", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 7678757da7..c3aab6acd7 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -243,7 +243,6 @@ class StockEntry(StockController): incoming_rate = flt(self.get_incoming_rate(args), self.precision("incoming_rate", d)) if incoming_rate > 0: d.incoming_rate = incoming_rate - d.amount = flt(d.transfer_qty) * flt(d.incoming_rate) if not d.t_warehouse: raw_material_cost += flt(d.amount) @@ -258,7 +257,7 @@ class StockEntry(StockController): if d.bom_no: bom = frappe.db.get_value("BOM", d.bom_no, ["operating_cost", "quantity"], as_dict=1) operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) - d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty)) + d.incoming_rate = operation_cost_per_unit + (raw_material_cost + flt(self.total_fixed_cost)) / flt(d.transfer_qty) d.amount = flt(d.transfer_qty) * flt(d.incoming_rate) break diff --git a/erpnext/stock/doctype/stock_entry/test_records.json b/erpnext/stock/doctype/stock_entry/test_records.json index a87b635bd7..4a4ca0e65a 100644 --- a/erpnext/stock/doctype/stock_entry/test_records.json +++ b/erpnext/stock/doctype/stock_entry/test_records.json @@ -9,7 +9,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 100, + "incoming_rate": 100, "item_code": "_Test Item", "parentfield": "mtn_details", "qty": 50.0, diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 0f6a33fb73..b9a6abd7b7 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError class TestStockEntry(unittest.TestCase): + def tearDown(self): frappe.set_user("Administrator") set_perpetual_inventory(0) @@ -26,7 +27,6 @@ class TestStockEntry(unittest.TestCase): st1 = frappe.copy_doc(test_records[0]) st1.insert() st1.submit() - st2 = frappe.copy_doc(test_records[1]) st2.insert() st2.submit() @@ -821,6 +821,39 @@ class TestStockEntry(unittest.TestCase): se = frappe.copy_doc(test_records[0]).insert() self.assertRaises (StockFreezeError, se.submit) frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) + + def test_production_order(self): + bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", + "is_default": 1, "docstatus": 1}) + + production_order = frappe.new_doc("Production Order") + production_order.update({ + "company": "_Test Company", + "fg_warehouse": "_Test Warehouse 1 - _TC", + "production_item": "_Test FG Item 2", + "bom_no": bom_no, + "qty": 1.0, + "stock_uom": "Nos", + "wip_warehouse": "_Test Warehouse - _TC" + }) + production_order.insert() + production_order.submit() + + self._insert_material_receipt() + + stock_entry = frappe.new_doc("Stock Entry") + stock_entry.update({ + "purpose": "Manufacture/Repack", + "production_order": production_order.name, + "bom_no": bom_no, + "fg_completed_qty": "1", + "total_fixed_cost": 1000 + }) + stock_entry.get_items() + fg_rate = [d.amount for d in stock_entry.get("mtn_details") if d.item_code=="_Test FG Item 2"][0] + self.assertEqual(fg_rate, 1200.00) + fg_rate = [d.amount for d in stock_entry.get("mtn_details") if d.item_code=="_Test Item"][0] + self.assertEqual(fg_rate, 100.00) def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): se = frappe.copy_doc(test_records[0]) diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py index d2da54f8fa..6d75069cf9 100644 --- a/erpnext/stock/report/item_prices/item_prices.py +++ b/erpnext/stock/report/item_prices/item_prices.py @@ -114,7 +114,7 @@ def get_item_bom_rate(): item_bom_map = {} - for b in frappe.db.sql("""select item, (total_cost/quantity) as bom_rate + for b in frappe.db.sql("""select item, (total_variable_cost/quantity) as bom_rate from `tabBOM` where is_active=1 and is_default=1""", as_dict=1): item_bom_map.setdefault(b.item, flt(b.bom_rate))