Merge pull request #49213 from rohitwaghchaure/feat-workstation-operating-component

refactor: workstation operating component
This commit is contained in:
rohitwaghchaure
2025-08-19 14:26:21 +05:30
committed by GitHub
24 changed files with 598 additions and 155 deletions

View File

@@ -1421,11 +1421,11 @@ def add_additional_cost(stock_entry, work_order):
as_dict=1,
)
expecnse_account = (
expense_account = (
company_account.default_operating_cost_account or company_account.default_expense_account
)
add_non_stock_items_cost(stock_entry, work_order, expecnse_account)
add_operations_cost(stock_entry, work_order, expecnse_account)
add_non_stock_items_cost(stock_entry, work_order, expense_account)
add_operations_cost(stock_entry, work_order, expense_account)
def add_non_stock_items_cost(stock_entry, work_order, expense_account):
@@ -1460,21 +1460,74 @@ 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
):
if not work_order:
return False
cost_added = False
for row in work_order.operations:
workstation_cost = frappe.get_all(
"Workstation Cost",
fields=["operating_component", "operating_cost"],
filters={
"parent": row.workstation,
"parenttype": "Workstation",
},
)
for wc in workstation_cost:
expense_account = get_component_account(wc.operating_component) or op_expense_account
actual_cp_operating_cost = flt(
flt(wc.operating_cost) * flt(flt(row.actual_operation_time) / 60.0),
row.precision("actual_operating_cost"),
)
per_unit_cost = flt(actual_cp_operating_cost) / flt(row.completed_qty)
if per_unit_cost and expense_account:
stock_entry.append(
"additional_costs",
{
"expense_account": expense_account,
"description": _("{0} Operating Cost for operation {1}").format(
wc.operating_component, row.operation
),
"amount": per_unit_cost * flt(stock_entry.fg_completed_qty),
},
)
cost_added = True
return cost_added
@frappe.request_cache
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):
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:
stock_entry.append(
"additional_costs",
{
"expense_account": expense_account,
"description": _("Operating Cost as per Work Order / BOM"),
"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty),
},
cost_added = add_operating_cost_component_wise(
stock_entry, work_order, operating_cost_per_unit, expense_account
)
if not cost_added:
stock_entry.append(
"additional_costs",
{
"expense_account": expense_account,
"description": _("Operating Cost as per Work Order / BOM"),
"amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty),
},
)
if work_order and work_order.additional_operating_cost and work_order.qty:
additional_operating_cost_per_unit = flt(work_order.additional_operating_cost) / flt(work_order.qty)

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import frappe
from frappe import _
from frappe.tests import IntegrationTestCase
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
@@ -75,14 +76,22 @@ class TestWorkstation(IntegrationTestCase):
bom_doc = setup_bom(item_code="_Testing Item", routing=routing_doc.name, currency="INR")
w1 = frappe.get_doc("Workstation", "_Test Workstation A")
# resets values
w1.hour_rate_rent = 300
w1.hour_rate_labour = 0
for row in w1.workstation_costs:
if row.operating_component == _("Rent"):
row.operating_cost = 300
break
w1.save()
bom_doc.update_cost()
bom_doc.reload()
self.assertEqual(w1.hour_rate, 300)
self.assertEqual(bom_doc.operations[0].hour_rate, 300)
w1.hour_rate_rent = 250
for row in w1.workstation_costs:
if row.operating_component == _("Rent"):
row.operating_cost = 250
break
w1.save()
# updating after setting new rates in workstations
bom_doc.update_cost()
@@ -102,8 +111,24 @@ def make_workstation(*args, **kwargs):
workstation_name = args.workstation_name or args.workstation
if not frappe.db.exists("Workstation", workstation_name):
doc = frappe.get_doc({"doctype": "Workstation", "workstation_name": workstation_name})
doc.hour_rate_rent = args.get("hour_rate_rent")
doc.hour_rate_labour = args.get("hour_rate_labour")
if args.get("hour_rate_rent"):
doc.append(
"workstation_costs",
{
"operating_component": _("Rent"),
"operating_cost": args.get("hour_rate_rent"),
},
)
if args.get("hour_rate_labour"):
doc.append(
"workstation_costs",
{
"operating_component": _("Wages"),
"operating_cost": args.get("hour_rate_labour"),
},
)
doc.workstation_type = args.get("workstation_type")
doc.insert()

View File

@@ -27,11 +27,8 @@
"column_break_etmc",
"off_status_image",
"over_heads",
"hour_rate_electricity",
"hour_rate_consumable",
"column_break_11",
"hour_rate_rent",
"hour_rate_labour",
"section_break_auzm",
"workstation_costs",
"section_break_11",
"hour_rate",
"workstaion_description",
@@ -68,50 +65,6 @@
"label": "Operating Costs",
"oldfieldtype": "Section Break"
},
{
"bold": 1,
"description": "per hour",
"fieldname": "hour_rate_electricity",
"fieldtype": "Currency",
"label": "Electricity Cost",
"non_negative": 1,
"oldfieldname": "hour_rate_electricity",
"oldfieldtype": "Currency"
},
{
"bold": 1,
"description": "per hour",
"fieldname": "hour_rate_consumable",
"fieldtype": "Currency",
"label": "Consumable Cost",
"non_negative": 1,
"oldfieldname": "hour_rate_consumable",
"oldfieldtype": "Currency"
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"bold": 1,
"description": "per hour",
"fieldname": "hour_rate_rent",
"fieldtype": "Currency",
"label": "Rent Cost",
"non_negative": 1,
"oldfieldname": "hour_rate_rent",
"oldfieldtype": "Currency"
},
{
"bold": 1,
"description": "Wages per hour",
"fieldname": "hour_rate_labour",
"fieldtype": "Currency",
"label": "Wages",
"non_negative": 1,
"oldfieldname": "hour_rate_labour",
"oldfieldtype": "Currency"
},
{
"description": "per hour",
"fieldname": "hour_rate",
@@ -252,6 +205,17 @@
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
},
{
"fieldname": "section_break_auzm",
"fieldtype": "Section Break",
"label": "Operating Costs (Per Hour)"
},
{
"fieldname": "workstation_costs",
"fieldtype": "Table",
"label": "Operating Components Cost",
"options": "Workstation Cost"
}
],
"hide_toolbar": 1,
@@ -259,7 +223,7 @@
"idx": 1,
"image_field": "on_status_image",
"links": [],
"modified": "2025-07-13 16:02:13.615001",
"modified": "2025-08-19 12:07:05.374386",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation",

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _
from frappe import _, bold
from frappe.model.document import Document
from frappe.utils import (
add_days,
@@ -44,6 +44,7 @@ class Workstation(Document):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.manufacturing.doctype.workstation_cost.workstation_cost import WorkstationCost
from erpnext.manufacturing.doctype.workstation_working_hour.workstation_working_hour import (
WorkstationWorkingHour,
)
@@ -52,10 +53,6 @@ class Workstation(Document):
disabled: DF.Check
holiday_list: DF.Link | None
hour_rate: DF.Currency
hour_rate_consumable: DF.Currency
hour_rate_electricity: DF.Currency
hour_rate_labour: DF.Currency
hour_rate_rent: DF.Currency
off_status_image: DF.AttachImage | None
on_status_image: DF.AttachImage | None
plant_floor: DF.Link | None
@@ -64,10 +61,26 @@ class Workstation(Document):
total_working_hours: DF.Float
warehouse: DF.Link | None
working_hours: DF.Table[WorkstationWorkingHour]
workstation_costs: DF.Table[WorkstationCost]
workstation_name: DF.Data
workstation_type: DF.Link | None
# end: auto-generated types
def validate(self):
self.validate_duplicate_operating_component()
def validate_duplicate_operating_component(self):
components = []
for row in self.workstation_costs:
if row.operating_component not in components:
components.append(row.operating_component)
else:
frappe.throw(
_("Duplicate Operating Component {0} found in Operating Components").format(
bold(row.operating_component)
)
)
def before_save(self):
self.set_data_based_on_workstation_type()
self.set_hour_rate()
@@ -95,36 +108,33 @@ class Workstation(Document):
frappe.throw(_("Row #{0}: Start Time must be before End Time").format(row.idx))
def set_hour_rate(self):
self.hour_rate = (
flt(self.hour_rate_labour)
+ flt(self.hour_rate_electricity)
+ flt(self.hour_rate_consumable)
+ flt(self.hour_rate_rent)
)
self.hour_rate = 0.0
for row in self.workstation_costs:
if row.operating_cost:
self.hour_rate += flt(row.operating_cost)
@frappe.whitelist()
def set_data_based_on_workstation_type(self):
if self.workstation_costs:
return
if self.workstation_type:
fields = [
"hour_rate_labour",
"hour_rate_electricity",
"hour_rate_consumable",
"hour_rate_rent",
"hour_rate",
"description",
]
data = frappe.get_all(
"Workstation Cost",
fields=["operating_component", "operating_cost", "idx"],
filters={"parent": self.workstation_type, "parenttype": "Workstation Type"},
order_by="idx",
)
data = frappe.get_cached_value("Workstation Type", self.workstation_type, fields, as_dict=True)
if not data:
return
for field in fields:
if self.get(field):
continue
if value := data.get(field):
self.set(field, value)
for row in data:
self.append(
"workstation_costs",
{
"operating_component": row.operating_component,
"operating_cost": row.operating_cost,
"idx": row.idx,
},
)
def on_update(self):
self.validate_overlap_for_operation_timings()

View File

@@ -0,0 +1,20 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class IntegrationTestWorkstationCost(IntegrationTestCase):
"""
Integration tests for WorkstationCost.
Use this class for testing interactions between multiple components.
"""
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Workstation Cost", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,43 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-08-17 16:43:13.542333",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"operating_component",
"operating_cost"
],
"fields": [
{
"fieldname": "operating_cost",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Operating Cost",
"reqd": 1
},
{
"fieldname": "operating_component",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Operating Component",
"options": "Workstation Operating Component",
"reqd": 1
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-08-17 19:21:02.725365",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation Cost",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,24 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class WorkstationCost(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
operating_component: DF.Link
operating_cost: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@@ -0,0 +1,20 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class IntegrationTestWorkstationOperatingComponent(IntegrationTestCase):
"""
Integration tests for WorkstationOperatingComponent.
Use this class for testing interactions between multiple components.
"""
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Workstation Operating Component", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,84 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:component_name",
"creation": "2025-08-17 16:49:30.711201",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"component_name",
"section_break_ewdg",
"accounts"
],
"fields": [
{
"fieldname": "component_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Component Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "section_break_ewdg",
"fieldtype": "Section Break"
},
{
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Component Expense Account",
"options": "Workstation Operating Component Account"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-08-17 19:23:47.510540",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation Operating Component",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Manufacturing Manager",
"share": 1,
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,25 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class WorkstationOperatingComponent(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.manufacturing.doctype.workstation_operating_component_account.workstation_operating_component_account import (
WorkstationOperatingComponentAccount,
)
accounts: DF.Table[WorkstationOperatingComponentAccount]
component_name: DF.Data
# end: auto-generated types
pass

View File

@@ -0,0 +1,20 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class IntegrationTestWorkstationOperatingComponentAccount(IntegrationTestCase):
"""
Integration tests for WorkstationOperatingComponentAccount.
Use this class for testing interactions between multiple components.
"""
pass

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Workstation Operating Component Account", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,43 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-08-17 19:21:36.356779",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"expense_account"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"fieldname": "expense_account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Expense Account",
"options": "Account"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-08-17 19:24:01.487406",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation Operating Component Account",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,24 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class WorkstationOperatingComponentAccount(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
company: DF.Link
expense_account: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@@ -10,12 +10,8 @@
"engine": "InnoDB",
"field_order": [
"workstation_type",
"over_heads",
"hour_rate_electricity",
"hour_rate_consumable",
"column_break_5",
"hour_rate_rent",
"hour_rate_labour",
"section_break_auzm",
"workstation_costs",
"section_break_8",
"hour_rate",
"description_tab",
@@ -32,44 +28,6 @@
"reqd": 1,
"unique": 1
},
{
"fieldname": "over_heads",
"fieldtype": "Section Break",
"label": "Operating Costs",
"oldfieldtype": "Section Break"
},
{
"description": "per hour",
"fieldname": "hour_rate_electricity",
"fieldtype": "Currency",
"label": "Electricity Cost",
"oldfieldname": "hour_rate_electricity",
"oldfieldtype": "Currency"
},
{
"description": "per hour",
"fieldname": "hour_rate_consumable",
"fieldtype": "Currency",
"label": "Consumable Cost",
"oldfieldname": "hour_rate_consumable",
"oldfieldtype": "Currency"
},
{
"description": "per hour",
"fieldname": "hour_rate_rent",
"fieldtype": "Currency",
"label": "Rent Cost",
"oldfieldname": "hour_rate_rent",
"oldfieldtype": "Currency"
},
{
"description": "Wages per hour",
"fieldname": "hour_rate_labour",
"fieldtype": "Currency",
"label": "Wages",
"oldfieldname": "hour_rate_labour",
"oldfieldtype": "Currency"
},
{
"description": "per hour",
"fieldname": "hour_rate",
@@ -88,10 +46,6 @@
"oldfieldtype": "Text",
"width": "300px"
},
{
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
{
"collapsible": 1,
"fieldname": "description_tab",
@@ -101,11 +55,22 @@
{
"fieldname": "section_break_8",
"fieldtype": "Section Break"
},
{
"fieldname": "section_break_auzm",
"fieldtype": "Section Break",
"label": "Operating Costs (Per Hour)"
},
{
"fieldname": "workstation_costs",
"fieldtype": "Table",
"label": "Operating Components Cost",
"options": "Workstation Cost"
}
],
"icon": "icon-wrench",
"links": [],
"modified": "2024-03-27 13:11:00.946367",
"modified": "2025-08-19 12:06:56.683558",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation Type",
@@ -125,9 +90,10 @@
}
],
"quick_entry": 1,
"row_format": "Dynamic",
"show_name_in_global_search": 1,
"sort_field": "creation",
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt
import frappe
from frappe import _, bold
from frappe.model.document import Document
from frappe.utils import flt
@@ -15,25 +16,38 @@ class WorkstationType(Document):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.manufacturing.doctype.workstation_cost.workstation_cost import WorkstationCost
description: DF.SmallText | None
hour_rate: DF.Currency
hour_rate_consumable: DF.Currency
hour_rate_electricity: DF.Currency
hour_rate_labour: DF.Currency
hour_rate_rent: DF.Currency
workstation_costs: DF.Table[WorkstationCost]
workstation_type: DF.Data
# end: auto-generated types
def validate(self):
self.validate_duplicate_operating_component()
def validate_duplicate_operating_component(self):
components = []
for row in self.workstation_costs:
if row.operating_component not in components:
components.append(row.operating_component)
else:
frappe.throw(
_("Duplicate Operating Component {0} found in Operating Components").format(
bold(row.operating_component)
)
)
def before_save(self):
self.set_hour_rate()
def set_hour_rate(self):
self.hour_rate = (
flt(self.hour_rate_labour)
+ flt(self.hour_rate_electricity)
+ flt(self.hour_rate_consumable)
+ flt(self.hour_rate_rent)
)
self.hour_rate = 0.0
for row in self.workstation_costs:
if row.operating_cost:
self.hour_rate += flt(row.operating_cost)
def get_workstations(workstation_type):

View File

@@ -435,3 +435,4 @@ erpnext.patches.v15_0.add_company_payment_gateway_account
erpnext.patches.v16_0.update_serial_no_reference_name
erpnext.patches.v16_0.set_invoice_type_in_pos_settings
erpnext.patches.v15_0.update_fieldname_in_accounting_dimension_filter
erpnext.patches.v16_0.make_workstation_operating_components #1

View File

@@ -0,0 +1,79 @@
import frappe
from frappe import _
def get_operating_cost_account(company):
company_details = frappe.db.get_value(
"Company", company, ["default_operating_cost_account", "default_expense_account"], as_dict=True
)
return company_details.get("default_operating_cost_account") or company_details.get(
"default_expense_account"
)
def execute():
components = [
"Electricity",
"Consumables",
"Rent",
"Wages",
]
companies = frappe.get_all("Company", filters={"is_group": 0}, pluck="name")
for component in components:
component = _(component)
if not frappe.db.exists("Workstation Operating Component", component):
doc = frappe.new_doc("Workstation Operating Component")
doc.component_name = component
for company in companies:
operating_cost_account = get_operating_cost_account(company)
doc.append("accounts", {"company": company, "expense_account": operating_cost_account})
doc.insert()
workstations = frappe.get_all("Workstation", filters={"hour_rate": (">", 0.0)}, pluck="name") or []
workstation_types = (
frappe.get_all("Workstation Type", filters={"hour_rate": (">", 0.0)}, pluck="name") or []
)
if not workstations and not workstation_types:
return
components_map = {
"hour_rate_electricity": _("Electricity"),
"hour_rate_consumable": _("Consumables"),
"hour_rate_rent": _("Rent"),
"hour_rate_labour": _("Wages"),
}
for workstation in workstations:
doc = frappe.get_doc("Workstation", workstation)
for field, component in components_map.items():
if doc.get(field):
doc.append(
"workstation_costs",
{
"operating_component": component,
"operating_cost": doc.get(field),
},
)
doc.save()
for workstation_type in workstation_types:
doc = frappe.get_doc("Workstation Type", workstation_type)
for field, component in components_map.items():
if doc.get(field):
doc.append(
"workstation_costs",
{
"operating_component": component,
"operating_cost": doc.get(field),
},
)
doc.save()

View File

@@ -294,6 +294,10 @@ def install(country=None):
{"doctype": "Market Segment", "market_segment": _("Upper Income")},
# Warehouse Type
{"doctype": "Warehouse Type", "name": "Transit"},
{"doctype": "Workstation Operating Component", "component_name": _("Electricity")},
{"doctype": "Workstation Operating Component", "component_name": _("Consumables")},
{"doctype": "Workstation Operating Component", "component_name": _("Rent")},
{"doctype": "Workstation Operating Component", "component_name": _("Wages")},
]
for doctype, title_field, filename in (