Merge branch 'develop' into fix/scr-return-rejected-qty
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
index cf5fbe1..88f1c90 100644
--- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
@@ -45,21 +45,6 @@
frm.trigger("clear_child");
switch(frm.doc.voucher_type){
- case "Opening Entry":
- frm.set_value("is_opening", "Yes");
- frappe.call({
- type:"GET",
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
- args: {
- "company": frm.doc.company
- },
- callback: function(r) {
- if(r.message) {
- add_accounts(frm.doc, r.message);
- }
- }
- });
- break;
case "Bank Entry":
case "Cash Entry":
frappe.call({
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index b965a43..5a734d8 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -9,6 +9,7 @@
"sequence_id",
"operation",
"col_break1",
+ "workstation_type",
"workstation",
"time_in_mins",
"fixed_time",
@@ -40,9 +41,9 @@
"reqd": 1
},
{
+ "depends_on": "eval:!doc.workstation_type",
"fieldname": "workstation",
"fieldtype": "Link",
- "in_list_view": 1,
"label": "Workstation",
"oldfieldname": "workstation",
"oldfieldtype": "Link",
@@ -180,13 +181,20 @@
"fieldname": "set_cost_based_on_bom_qty",
"fieldtype": "Check",
"label": "Set Operating Cost Based On BOM Quantity"
+ },
+ {
+ "fieldname": "workstation_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Workstation Type",
+ "options": "Workstation Type"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2022-04-08 01:18:33.547481",
+ "modified": "2022-11-04 17:17:16.986941",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Operation",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 5a071f1..8506111 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -27,11 +27,14 @@
"operation",
"operation_row_number",
"column_break_18",
+ "workstation_type",
"workstation",
"employee",
"section_break_21",
"sub_operations",
"timing_detail",
+ "expected_start_date",
+ "expected_end_date",
"time_logs",
"section_break_13",
"total_completed_qty",
@@ -416,11 +419,27 @@
"fieldtype": "Link",
"label": "Quality Inspection Template",
"options": "Quality Inspection Template"
+ },
+ {
+ "fieldname": "workstation_type",
+ "fieldtype": "Link",
+ "label": "Workstation Type",
+ "options": "Workstation Type"
+ },
+ {
+ "fieldname": "expected_start_date",
+ "fieldtype": "Datetime",
+ "label": "Expected Start Date"
+ },
+ {
+ "fieldname": "expected_end_date",
+ "fieldtype": "Datetime",
+ "label": "Expected End Date"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-11-24 19:17:40.879235",
+ "modified": "2022-11-09 15:02:44.490731",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",
@@ -475,6 +494,7 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"title_field": "operation",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 17b7728..8226475 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -2,6 +2,7 @@
# For license information, please see license.txt
import datetime
import json
+from typing import Optional
import frappe
from frappe import _, bold
@@ -26,6 +27,7 @@
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import (
get_mins_between_operations,
)
+from erpnext.manufacturing.doctype.workstation_type.workstation_type import get_workstations
class OverlapError(frappe.ValidationError):
@@ -129,7 +131,7 @@
query = (
frappe.qb.from_(jctl)
.from_(jc)
- .select(jc.name.as_("name"), jctl.to_time)
+ .select(jc.name.as_("name"), jctl.to_time, jc.workstation, jc.workstation_type)
.where(
(jctl.parent == jc.name)
& (Criterion.any(time_conditions))
@@ -140,6 +142,9 @@
.orderby(jctl.to_time, order=frappe.qb.desc)
)
+ if self.workstation_type:
+ query = query.where(jc.workstation_type == self.workstation_type)
+
if self.workstation:
production_capacity = (
frappe.get_cached_value("Workstation", self.workstation, "production_capacity") or 1
@@ -156,8 +161,21 @@
if existing and production_capacity > len(existing):
return
+ if self.workstation_type:
+ if workstation := self.get_workstation_based_on_available_slot(existing):
+ self.workstation = workstation
+ return None
+
return existing[0] if existing else None
+ def get_workstation_based_on_available_slot(self, existing) -> Optional[str]:
+ workstations = get_workstations(self.workstation_type)
+ if workstations:
+ busy_workstations = [row.workstation for row in existing]
+ for workstation in workstations:
+ if workstation not in busy_workstations:
+ return workstation
+
def schedule_time_logs(self, row):
row.remaining_time_in_mins = row.time_in_mins
while row.remaining_time_in_mins > 0:
@@ -170,6 +188,9 @@
# get the last record based on the to time from the job card
data = self.get_overlap_for(args, check_next_available_slot=True)
if data:
+ if not self.workstation:
+ self.workstation = data.workstation
+
row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations())
def check_workstation_time(self, row):
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/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 6247618..4aff42c 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -446,7 +446,6 @@
frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true);
frm.toggle_reqd("transfer_material_against",
frm.doc.operations && frm.doc.operations.length > 0);
- frm.fields_dict.operations.grid.toggle_reqd("workstation", frm.doc.operations);
},
set_sales_order: function(frm) {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 8167385..52753a0 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -87,11 +87,18 @@
self.validate_transfer_against()
self.validate_operation_time()
self.status = self.get_status()
+ self.validate_workstation_type()
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
self.set_required_items(reset_only_qty=len(self.get("required_items")))
+ def validate_workstation_type(self):
+ for row in self.operations:
+ if not row.workstation and not row.workstation_type:
+ msg = f"Row {row.idx}: Workstation or Workstation Type is mandatory for an operation {row.operation}"
+ frappe.throw(_(msg))
+
def validate_sales_order(self):
if self.sales_order:
self.check_sales_order_on_hold_or_close()
@@ -491,11 +498,6 @@
def prepare_data_for_job_card(self, row, index, plan_days, enable_capacity_planning):
self.set_operation_start_end_time(index, row)
- if not row.workstation:
- frappe.throw(
- _("Row {0}: select the workstation against the operation {1}").format(row.idx, row.operation)
- )
-
original_start_time = row.planned_start_time
job_card_doc = create_job_card(
self, row, auto_create=True, enable_capacity_planning=enable_capacity_planning
@@ -662,6 +664,7 @@
"description",
"workstation",
"idx",
+ "workstation_type",
"base_hour_rate as hour_rate",
"time_in_mins",
"parent as bom",
@@ -1398,6 +1401,7 @@
doc.update(
{
"work_order": work_order.name,
+ "workstation_type": row.get("workstation_type"),
"operation": row.get("operation"),
"workstation": row.get("workstation"),
"posting_date": nowdate(),
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
index 4e1a464..31b9201 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -10,6 +10,7 @@
"completed_qty",
"column_break_4",
"bom",
+ "workstation_type",
"workstation",
"sequence_id",
"section_break_10",
@@ -196,12 +197,18 @@
{
"fieldname": "section_break_10",
"fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "workstation_type",
+ "fieldtype": "Link",
+ "label": "Workstation Type",
+ "options": "Workstation Type"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-11-29 16:37:18.824489",
+ "modified": "2022-11-09 01:37:56.563068",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",
@@ -209,5 +216,6 @@
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
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.json b/erpnext/manufacturing/doctype/workstation/workstation.json
index d130391..881cba0 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.json
+++ b/erpnext/manufacturing/doctype/workstation/workstation.json
@@ -1,26 +1,30 @@
{
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:workstation_name",
"creation": "2013-01-10 16:34:17",
"doctype": "DocType",
"document_type": "Setup",
+ "engine": "InnoDB",
"field_order": [
"workstation_name",
"production_capacity",
"column_break_3",
+ "workstation_type",
"over_heads",
"hour_rate_electricity",
"hour_rate_consumable",
"column_break_11",
"hour_rate_rent",
"hour_rate_labour",
+ "section_break_11",
"hour_rate",
+ "workstaion_description",
+ "description",
"working_hours_section",
"holiday_list",
- "working_hours",
- "workstaion_description",
- "description"
+ "working_hours"
],
"fields": [
{
@@ -44,7 +48,7 @@
},
{
"fieldname": "over_heads",
- "fieldtype": "Section Break",
+ "fieldtype": "Tab Break",
"label": "Operating Costs",
"oldfieldtype": "Section Break"
},
@@ -99,7 +103,7 @@
},
{
"fieldname": "working_hours_section",
- "fieldtype": "Section Break",
+ "fieldtype": "Tab Break",
"label": "Working Hours"
},
{
@@ -128,16 +132,29 @@
{
"collapsible": 1,
"fieldname": "workstaion_description",
- "fieldtype": "Section Break",
+ "fieldtype": "Tab Break",
"label": "Description"
+ },
+ {
+ "bold": 1,
+ "fieldname": "workstation_type",
+ "fieldtype": "Link",
+ "label": "Workstation Type",
+ "options": "Workstation Type"
+ },
+ {
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break"
}
],
"icon": "icon-wrench",
"idx": 1,
- "modified": "2019-11-26 12:39:19.742052",
+ "links": [],
+ "modified": "2022-11-04 17:39:01.549346",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -154,6 +171,8 @@
],
"quick_entry": 1,
"show_name_in_global_search": 1,
+ "sort_field": "modified",
"sort_order": "ASC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
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/__init__.py b/erpnext/manufacturing/doctype/workstation_type/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation_type/__init__.py
diff --git a/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py b/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
new file mode 100644
index 0000000..aa7a3ee
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation_type/test_workstation_type.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+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.js b/erpnext/manufacturing/doctype/workstation_type/workstation_type.js
new file mode 100644
index 0000000..419fa6c
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Workstation Type', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/manufacturing/doctype/workstation_type/workstation_type.json b/erpnext/manufacturing/doctype/workstation_type/workstation_type.json
new file mode 100644
index 0000000..7d9e36a
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.json
@@ -0,0 +1,133 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:workstation_type",
+ "creation": "2022-11-04 17:03:23.334818",
+ "default_view": "List",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "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_8",
+ "hour_rate",
+ "description_tab",
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "workstation_type",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Workstation Type",
+ "oldfieldname": "workstation_name",
+ "oldfieldtype": "Data",
+ "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",
+ "fieldtype": "Currency",
+ "label": "Net Hour Rate",
+ "oldfieldname": "hour_rate",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Description",
+ "oldfieldname": "description",
+ "oldfieldtype": "Text",
+ "width": "300px"
+ },
+ {
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "description_tab",
+ "fieldtype": "Tab Break",
+ "label": "Description"
+ },
+ {
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break"
+ }
+ ],
+ "icon": "icon-wrench",
+ "links": [],
+ "modified": "2022-11-16 23:11:36.224249",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Workstation Type",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/workstation_type/workstation_type.py b/erpnext/manufacturing/doctype/workstation_type/workstation_type.py
new file mode 100644
index 0000000..348f4f8
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+from frappe.utils import flt
+
+
+class WorkstationType(Document):
+ 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):
+ workstations = frappe.get_all("Workstation", filters={"workstation_type": workstation_type})
+
+ return [workstation.name for workstation in workstations]
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",
diff --git a/erpnext/public/js/bulk_transaction_processing.js b/erpnext/public/js/bulk_transaction_processing.js
index 101f50c..0e42b47 100644
--- a/erpnext/public/js/bulk_transaction_processing.js
+++ b/erpnext/public/js/bulk_transaction_processing.js
@@ -11,7 +11,7 @@
});
let count_of_rows = checked_items.length;
- frappe.confirm(__("Create {0} {1} ?", [count_of_rows, to_doctype]), ()=>{
+ frappe.confirm(__("Create {0} {1} ?", [count_of_rows, __(to_doctype)]), ()=>{
if (doc_name.length == 0) {
frappe.call({
method: "erpnext.utilities.bulk_transaction.transaction_processing",
@@ -20,11 +20,11 @@
});
if (count_of_rows > 10) {
- frappe.show_alert("Starting a background job to create {0} {1}", [count_of_rows, to_doctype]);
+ frappe.show_alert("Starting a background job to create {0} {1}", [count_of_rows, __(to_doctype)]);
}
} else {
frappe.msgprint(__("Selected document must be in submitted state"));
}
});
}
-});
\ No newline at end of file
+});