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
+});