test: test case to check workstation type
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 804f03d..694dc79 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -5,7 +5,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings, timeout
-from frappe.utils import add_days, add_months, cint, flt, now, today
+from frappe.utils import add_days, add_months, add_to_date, cint, flt, now, today
from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
@@ -1480,6 +1480,166 @@
for row in return_ste_doc.items:
self.assertEqual(row.qty, 2)
+ def test_workstation_type_for_work_order(self):
+ prepare_data_for_workstation_type_check()
+
+ workstation_types = ["Workstation Type 1", "Workstation Type 2", "Workstation Type 3"]
+ planned_start_date = "2022-11-14 10:00:00"
+
+ wo_order = make_wo_order_test_record(
+ item="Test FG Item For Workstation Type", planned_start_date=planned_start_date, qty=2
+ )
+
+ job_cards = frappe.get_all(
+ "Job Card",
+ fields=[
+ "`tabJob Card`.`name`",
+ "`tabJob Card`.`workstation_type`",
+ "`tabJob Card`.`workstation`",
+ "`tabJob Card Time Log`.`from_time`",
+ "`tabJob Card Time Log`.`to_time`",
+ "`tabJob Card Time Log`.`time_in_mins`",
+ ],
+ filters=[
+ ["Job Card", "work_order", "=", wo_order.name],
+ ["Job Card Time Log", "docstatus", "=", 1],
+ ],
+ order_by="`tabJob Card`.`creation` desc",
+ )
+
+ workstations_to_check = ["Workstation 1", "Workstation 3", "Workstation 5"]
+ for index, row in enumerate(job_cards):
+ if index != 0:
+ planned_start_date = add_to_date(planned_start_date, minutes=40)
+
+ self.assertEqual(row.workstation_type, workstation_types[index])
+ self.assertEqual(row.from_time, planned_start_date)
+ self.assertEqual(row.to_time, add_to_date(planned_start_date, minutes=30))
+ self.assertEqual(row.workstation, workstations_to_check[index])
+
+ planned_start_date = "2022-11-14 10:00:00"
+
+ wo_order = make_wo_order_test_record(
+ item="Test FG Item For Workstation Type", planned_start_date=planned_start_date, qty=2
+ )
+
+ job_cards = frappe.get_all(
+ "Job Card",
+ fields=[
+ "`tabJob Card`.`name`",
+ "`tabJob Card`.`workstation_type`",
+ "`tabJob Card`.`workstation`",
+ "`tabJob Card Time Log`.`from_time`",
+ "`tabJob Card Time Log`.`to_time`",
+ "`tabJob Card Time Log`.`time_in_mins`",
+ ],
+ filters=[
+ ["Job Card", "work_order", "=", wo_order.name],
+ ["Job Card Time Log", "docstatus", "=", 1],
+ ],
+ order_by="`tabJob Card`.`creation` desc",
+ )
+
+ workstations_to_check = ["Workstation 2", "Workstation 4", "Workstation 6"]
+ for index, row in enumerate(job_cards):
+ if index != 0:
+ planned_start_date = add_to_date(planned_start_date, minutes=40)
+
+ self.assertEqual(row.workstation_type, workstation_types[index])
+ self.assertEqual(row.from_time, planned_start_date)
+ self.assertEqual(row.to_time, add_to_date(planned_start_date, minutes=30))
+ self.assertEqual(row.workstation, workstations_to_check[index])
+
+
+def prepare_data_for_workstation_type_check():
+ from erpnext.manufacturing.doctype.operation.test_operation import make_operation
+ from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
+ from erpnext.manufacturing.doctype.workstation_type.test_workstation_type import (
+ create_workstation_type,
+ )
+
+ workstation_types = ["Workstation Type 1", "Workstation Type 2", "Workstation Type 3"]
+ for workstation_type in workstation_types:
+ create_workstation_type(workstation_type=workstation_type)
+
+ operations = ["Cutting", "Sewing", "Packing"]
+ for operation in operations:
+ make_operation(
+ {
+ "operation": operation,
+ }
+ )
+
+ workstations = [
+ {
+ "workstation": "Workstation 1",
+ "workstation_type": "Workstation Type 1",
+ },
+ {
+ "workstation": "Workstation 2",
+ "workstation_type": "Workstation Type 1",
+ },
+ {
+ "workstation": "Workstation 3",
+ "workstation_type": "Workstation Type 2",
+ },
+ {
+ "workstation": "Workstation 4",
+ "workstation_type": "Workstation Type 2",
+ },
+ {
+ "workstation": "Workstation 5",
+ "workstation_type": "Workstation Type 3",
+ },
+ {
+ "workstation": "Workstation 6",
+ "workstation_type": "Workstation Type 3",
+ },
+ ]
+
+ for row in workstations:
+ make_workstation(row)
+
+ fg_item = make_item(
+ "Test FG Item For Workstation Type",
+ {
+ "is_stock_item": 1,
+ },
+ )
+
+ rm_item = make_item(
+ "Test RM Item For Workstation Type",
+ {
+ "is_stock_item": 1,
+ },
+ )
+
+ if not frappe.db.exists("BOM", {"item": fg_item.name}):
+ bom_doc = make_bom(
+ item=fg_item.name,
+ source_warehouse="Stores - _TC",
+ raw_materials=[rm_item.name],
+ do_not_submit=True,
+ )
+
+ submit_bom = False
+ for index, operation in enumerate(operations):
+ if not frappe.db.exists("BOM Operation", {"parent": bom_doc.name, "operation": operation}):
+ bom_doc.append(
+ "operations",
+ {
+ "operation": operation,
+ "time_in_mins": 30,
+ "hour_rate": 100,
+ "workstation_type": workstation_types[index],
+ },
+ )
+
+ submit_bom = True
+
+ if submit_bom:
+ bom_doc.submit()
+
def prepare_data_for_backflush_based_on_materials_transferred():
batch_item_doc = make_item(
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.py b/erpnext/manufacturing/doctype/workstation/test_workstation.py
index 6db985c..1eb47ae 100644
--- a/erpnext/manufacturing/doctype/workstation/test_workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.py
@@ -107,6 +107,7 @@
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")
+ doc.workstation_type = args.get("workstation_type")
doc.insert()
return doc
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js
index 5b9cedb..f830b17 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.js
+++ b/erpnext/manufacturing/doctype/workstation/workstation.js
@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Workstation", {
- onload: function(frm) {
+ onload(frm) {
if(frm.is_new())
{
frappe.call({
@@ -15,6 +15,18 @@
}
})
}
+ },
+
+ workstation_type(frm) {
+ if (frm.doc.workstation_type) {
+ frm.call({
+ method: "set_data_based_on_workstation_type",
+ doc: frm.doc,
+ callback: function(r) {
+ frm.refresh_fields();
+ }
+ })
+ }
}
});
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py
index 3c25622..d5b6d37 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.py
+++ b/erpnext/manufacturing/doctype/workstation/workstation.py
@@ -32,7 +32,11 @@
class Workstation(Document):
- def validate(self):
+ def before_save(self):
+ self.set_data_based_on_workstation_type()
+ self.set_hour_rate()
+
+ def set_hour_rate(self):
self.hour_rate = (
flt(self.hour_rate_labour)
+ flt(self.hour_rate_electricity)
@@ -40,6 +44,30 @@
+ flt(self.hour_rate_rent)
)
+ @frappe.whitelist()
+ def set_data_based_on_workstation_type(self):
+ if self.workstation_type:
+ fields = [
+ "hour_rate_labour",
+ "hour_rate_electricity",
+ "hour_rate_consumable",
+ "hour_rate_rent",
+ "hour_rate",
+ "description",
+ ]
+
+ 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)
+
def on_update(self):
self.validate_overlap_for_operation_timings()
self.update_bom_operation()
diff --git a/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py b/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
index 9e7a54d..aa7a3ee 100644
--- a/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
+++ b/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
@@ -1,9 +1,21 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
-# import frappe
+import frappe
from frappe.tests.utils import FrappeTestCase
class TestWorkstationType(FrappeTestCase):
pass
+
+
+def create_workstation_type(**args):
+ args = frappe._dict(args)
+
+ if workstation_type := frappe.db.exists("Workstation Type", args.workstation_type):
+ return frappe.get_doc("Workstation Type", workstation_type)
+ else:
+ doc = frappe.new_doc("Workstation Type")
+ doc.update(args)
+ doc.insert()
+ return doc
diff --git a/erpnext/manufacturing/doctype/workstation_type/workstation_type.json b/erpnext/manufacturing/doctype/workstation_type/workstation_type.json
index 86321cf..7d9e36a 100644
--- a/erpnext/manufacturing/doctype/workstation_type/workstation_type.json
+++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.json
@@ -19,9 +19,7 @@
"section_break_8",
"hour_rate",
"description_tab",
- "description",
- "holiday_tab",
- "holiday_list"
+ "description"
],
"fields": [
{
@@ -82,12 +80,6 @@
"read_only": 1
},
{
- "fieldname": "holiday_list",
- "fieldtype": "Link",
- "label": "Holiday List",
- "options": "Holiday List"
- },
- {
"fieldname": "description",
"fieldtype": "Small Text",
"in_list_view": 1,
@@ -107,18 +99,13 @@
"label": "Description"
},
{
- "fieldname": "holiday_tab",
- "fieldtype": "Tab Break",
- "label": "Holiday"
- },
- {
"fieldname": "section_break_8",
"fieldtype": "Section Break"
}
],
"icon": "icon-wrench",
"links": [],
- "modified": "2022-11-04 17:30:33.397719",
+ "modified": "2022-11-16 23:11:36.224249",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation Type",
diff --git a/erpnext/manufacturing/doctype/workstation_type/workstation_type.py b/erpnext/manufacturing/doctype/workstation_type/workstation_type.py
index b097755..348f4f8 100644
--- a/erpnext/manufacturing/doctype/workstation_type/workstation_type.py
+++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.py
@@ -3,10 +3,20 @@
import frappe
from frappe.model.document import Document
+from frappe.utils import flt
class WorkstationType(Document):
- pass
+ 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)
+ )
def get_workstations(workstation_type):
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index 549f5af..c25f606 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -76,168 +76,6 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Bill of Materials",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Item",
- "link_count": 0,
- "link_to": "Item",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "Item",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Bill of Materials",
- "link_count": 0,
- "link_to": "BOM",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Workstation",
- "link_count": 0,
- "link_to": "Workstation",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Operation",
- "link_count": 0,
- "link_to": "Operation",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Routing",
- "link_count": 0,
- "link_to": "Routing",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Work Order",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Production Planning Report",
- "link_count": 0,
- "link_to": "Production Planning Report",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Work Order",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Work Order Summary",
- "link_count": 0,
- "link_to": "Work Order Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Quality Inspection",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Quality Inspection Summary",
- "link_count": 0,
- "link_to": "Quality Inspection Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Downtime Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Downtime Analysis",
- "link_count": 0,
- "link_to": "Downtime Analysis",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Job Card",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Job Card Summary",
- "link_count": 0,
- "link_to": "Job Card Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "BOM",
- "hidden": 0,
- "is_query_report": 1,
- "label": "BOM Search",
- "link_count": 0,
- "link_to": "BOM Search",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "BOM",
- "hidden": 0,
- "is_query_report": 1,
- "label": "BOM Stock Report",
- "link_count": 0,
- "link_to": "BOM Stock Report",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Work Order",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Production Analytics",
- "link_count": 0,
- "link_to": "Production Analytics",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "BOM",
- "hidden": 0,
- "is_query_report": 1,
- "label": "BOM Operations Time",
- "link_count": 0,
- "link_to": "BOM Operations Time",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
"label": "Tools",
"link_count": 0,
"onboard": 0,
@@ -400,9 +238,181 @@
"link_type": "Report",
"onboard": 0,
"type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bill of Materials",
+ "link_count": 15,
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Item",
+ "link_count": 0,
+ "link_to": "Item",
+ "link_type": "DocType",
+ "onboard": 1,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Item",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bill of Materials",
+ "link_count": 0,
+ "link_to": "BOM",
+ "link_type": "DocType",
+ "onboard": 1,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Workstation Type",
+ "link_count": 0,
+ "link_to": "Workstation Type",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Workstation",
+ "link_count": 0,
+ "link_to": "Workstation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Operation",
+ "link_count": 0,
+ "link_to": "Operation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Routing",
+ "link_count": 0,
+ "link_to": "Routing",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Production Planning Report",
+ "link_count": 0,
+ "link_to": "Production Planning Report",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Quality Inspection",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Work Order Summary",
+ "link_count": 0,
+ "link_to": "Work Order Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Downtime Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Quality Inspection Summary",
+ "link_count": 0,
+ "link_to": "Quality Inspection Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Job Card",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Downtime Analysis",
+ "link_count": 0,
+ "link_to": "Downtime Analysis",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Job Card Summary",
+ "link_count": 0,
+ "link_to": "Job Card Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "BOM Search",
+ "link_count": 0,
+ "link_to": "BOM Search",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Work Order",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "BOM Stock Report",
+ "link_count": 0,
+ "link_to": "BOM Stock Report",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "BOM",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Production Analytics",
+ "link_count": 0,
+ "link_to": "Production Analytics",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "BOM Operations Time",
+ "link_count": 0,
+ "link_to": "BOM Operations Time",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
}
],
- "modified": "2022-06-15 15:18:57.062935",
+ "modified": "2022-11-14 14:53:34.616862",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",