fix: added process loss in job card
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index 5305db3..4a46d57 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -83,7 +83,7 @@
 			// and if stock mvt for WIP is required
 			if (frm.doc.work_order) {
 				frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => {
-					if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0) {
+					if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0 || !frm.doc.items.length) {
 						frm.trigger("prepare_timer_buttons");
 					}
 				});
@@ -411,6 +411,16 @@
 			}
 		});
 
+		if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) {
+			let flt_precision = precision('for_quantity', frm.doc);
+			let process_loss_qty = (
+				flt(frm.doc.for_quantity, flt_precision)
+				- flt(frm.doc.total_completed_qty, flt_precision)
+			);
+
+			frm.set_value('process_loss_qty', process_loss_qty);
+		}
+
 		refresh_field("total_completed_qty");
 	}
 });
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index f49f018..5d912fa 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -39,6 +39,7 @@
   "time_logs",
   "section_break_13",
   "total_completed_qty",
+  "process_loss_qty",
   "column_break_15",
   "total_time_in_mins",
   "section_break_8",
@@ -448,11 +449,17 @@
    "no_copy": 1,
    "options": "Serial and Batch Bundle",
    "print_hide": 1
+  },
+  {
+   "fieldname": "process_loss_qty",
+   "fieldtype": "Float",
+   "label": "Process Loss Qty",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2023-05-23 09:56:43.826602",
+ "modified": "2023-06-09 12:04:55.534264",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Job Card",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index fcaa3fd..496cbfd 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -161,7 +161,7 @@
 			self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
 
 		for row in self.sub_operations:
-			self.total_completed_qty += row.completed_qty
+			self.c += row.completed_qty
 
 	def get_overlap_for(self, args, check_next_available_slot=False):
 		production_capacity = 1
@@ -451,6 +451,9 @@
 					},
 				)
 
+	def before_save(self):
+		self.set_process_loss()
+
 	def on_submit(self):
 		self.validate_transfer_qty()
 		self.validate_job_card()
@@ -487,19 +490,35 @@
 				)
 			)
 
-		if self.for_quantity and self.total_completed_qty != self.for_quantity:
+		precision = self.precision("total_completed_qty")
+		total_completed_qty = flt(
+			flt(self.total_completed_qty, precision) + flt(self.process_loss_qty, precision)
+		)
+
+		if self.for_quantity and flt(total_completed_qty, precision) != flt(
+			self.for_quantity, precision
+		):
 			total_completed_qty = bold(_("Total Completed Qty"))
 			qty_to_manufacture = bold(_("Qty to Manufacture"))
 
 			frappe.throw(
 				_("The {0} ({1}) must be equal to {2} ({3})").format(
 					total_completed_qty,
-					bold(self.total_completed_qty),
+					bold(flt(total_completed_qty, precision)),
 					qty_to_manufacture,
 					bold(self.for_quantity),
 				)
 			)
 
+	def set_process_loss(self):
+		precision = self.precision("total_completed_qty")
+
+		self.process_loss_qty = 0.0
+		if self.total_completed_qty and self.for_quantity > self.total_completed_qty:
+			self.process_loss_qty = flt(self.for_quantity, precision) - flt(
+				self.total_completed_qty, precision
+			)
+
 	def update_work_order(self):
 		if not self.work_order:
 			return
@@ -511,7 +530,7 @@
 		):
 			return
 
-		for_quantity, time_in_mins = 0, 0
+		for_quantity, time_in_mins, process_loss_qty = 0, 0, 0
 		from_time_list, to_time_list = [], []
 
 		field = "operation_id"
@@ -519,6 +538,7 @@
 		if data and len(data) > 0:
 			for_quantity = flt(data[0].completed_qty)
 			time_in_mins = flt(data[0].time_in_mins)
+			process_loss_qty = flt(data[0].process_loss_qty)
 
 		wo = frappe.get_doc("Work Order", self.work_order)
 
@@ -526,8 +546,8 @@
 			self.update_corrective_in_work_order(wo)
 
 		elif self.operation_id:
-			self.validate_produced_quantity(for_quantity, wo)
-			self.update_work_order_data(for_quantity, time_in_mins, wo)
+			self.validate_produced_quantity(for_quantity, process_loss_qty, wo)
+			self.update_work_order_data(for_quantity, process_loss_qty, time_in_mins, wo)
 
 	def update_corrective_in_work_order(self, wo):
 		wo.corrective_operation_cost = 0.0
@@ -542,11 +562,11 @@
 		wo.flags.ignore_validate_update_after_submit = True
 		wo.save()
 
-	def validate_produced_quantity(self, for_quantity, wo):
+	def validate_produced_quantity(self, for_quantity, process_loss_qty, wo):
 		if self.docstatus < 2:
 			return
 
-		if wo.produced_qty > for_quantity:
+		if wo.produced_qty > for_quantity + process_loss_qty:
 			first_part_msg = _(
 				"The {0} {1} is used to calculate the valuation cost for the finished good {2}."
 			).format(
@@ -561,7 +581,7 @@
 				_("{0} {1}").format(first_part_msg, second_part_msg), JobCardCancelError, title=_("Error")
 			)
 
-	def update_work_order_data(self, for_quantity, time_in_mins, wo):
+	def update_work_order_data(self, for_quantity, process_loss_qty, time_in_mins, wo):
 		workstation_hour_rate = frappe.get_value("Workstation", self.workstation, "hour_rate")
 		jc = frappe.qb.DocType("Job Card")
 		jctl = frappe.qb.DocType("Job Card Time Log")
@@ -582,6 +602,7 @@
 		for data in wo.operations:
 			if data.get("name") == self.operation_id:
 				data.completed_qty = for_quantity
+				data.process_loss_qty = process_loss_qty
 				data.actual_operation_time = time_in_mins
 				data.actual_start_time = time_data[0].start_time if time_data else None
 				data.actual_end_time = time_data[0].end_time if time_data else None
@@ -599,7 +620,11 @@
 	def get_current_operation_data(self):
 		return frappe.get_all(
 			"Job Card",
-			fields=["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
+			fields=[
+				"sum(total_time_in_mins) as time_in_mins",
+				"sum(total_completed_qty) as completed_qty",
+				"sum(process_loss_qty) as process_loss_qty",
+			],
 			filters={
 				"docstatus": 1,
 				"work_order": self.work_order,
@@ -777,7 +802,7 @@
 
 		data = frappe.get_all(
 			"Work Order Operation",
-			fields=["operation", "status", "completed_qty"],
+			fields=["operation", "status", "completed_qty", "sequence_id"],
 			filters={"docstatus": 1, "parent": self.work_order, "sequence_id": ("<", self.sequence_id)},
 			order_by="sequence_id, idx",
 		)
@@ -795,6 +820,16 @@
 					OperationSequenceError,
 				)
 
+			if row.completed_qty < current_operation_qty:
+				msg = f"""The completed quantity {bold(current_operation_qty)}
+					of an operation {bold(self.operation)} cannot be greater
+					than the completed quantity {bold(row.completed_qty)}
+					of a previous operation
+					{bold(row.operation)}.
+				"""
+
+				frappe.throw(_(msg))
+
 	def validate_work_order(self):
 		if self.is_work_order_closed():
 			frappe.throw(_("You can't make any changes to Job Card since Work Order is closed."))
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index d0c9966..c1a078d 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -139,7 +139,7 @@
 		}
 
 		if (frm.doc.status != "Closed") {
-			if (frm.doc.docstatus === 1
+			if (frm.doc.docstatus === 1 && frm.doc.status !== "Completed"
 				&& frm.doc.operations && frm.doc.operations.length) {
 
 				const not_completed = frm.doc.operations.filter(d => {
@@ -256,6 +256,12 @@
 					label: __('Batch Size'),
 					read_only: 1
 				},
+				{
+					fieldtype: 'Int',
+					fieldname: 'sequence_id',
+					label: __('Sequence Id'),
+					read_only: 1
+				},
 			],
 			data: operations_data,
 			in_place_edit: true,
@@ -280,8 +286,8 @@
 
 		var pending_qty = 0;
 		frm.doc.operations.forEach(data => {
-			if(data.completed_qty != frm.doc.qty) {
-				pending_qty = frm.doc.qty - flt(data.completed_qty);
+			if(data.completed_qty + data.process_loss_qty != frm.doc.qty) {
+				pending_qty = frm.doc.qty - flt(data.completed_qty) - flt(data.process_loss_qty);
 
 				if (pending_qty) {
 					dialog.fields_dict.operations.df.data.push({
@@ -290,7 +296,8 @@
 						'workstation': data.workstation,
 						'batch_size': data.batch_size,
 						'qty': pending_qty,
-						'pending_qty': pending_qty
+						'pending_qty': pending_qty,
+						'sequence_id': data.sequence_id
 					});
 				}
 			}
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index aecace6..a236f2a 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -46,8 +46,8 @@
   "required_items_section",
   "materials_and_operations_tab",
   "operations_section",
-  "operations",
   "transfer_material_against",
+  "operations",
   "time",
   "planned_start_date",
   "planned_end_date",
@@ -330,7 +330,6 @@
    "label": "Expected Delivery Date"
   },
   {
-   "collapsible": 1,
    "fieldname": "operations_section",
    "fieldtype": "Section Break",
    "label": "Operations",
@@ -591,7 +590,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2023-04-06 12:35:12.149827",
+ "modified": "2023-06-09 13:20:09.154362",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 3265b8f..bfdcf61 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -245,7 +245,9 @@
 				status = "Not Started"
 				if flt(self.material_transferred_for_manufacturing) > 0:
 					status = "In Process"
-				if flt(self.produced_qty) >= flt(self.qty):
+
+				total_qty = flt(self.produced_qty) + flt(self.process_loss_qty)
+				if flt(total_qty) >= flt(self.qty):
 					status = "Completed"
 		else:
 			status = "Cancelled"
@@ -761,13 +763,15 @@
 		max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage / 100 * flt(self.qty))
 
 		for d in self.get("operations"):
-			if not d.completed_qty:
+			precision = d.precision("completed_qty")
+			qty = flt(d.completed_qty, precision) + flt(d.process_loss_qty, precision)
+			if not qty:
 				d.status = "Pending"
-			elif flt(d.completed_qty) < flt(self.qty):
+			elif flt(qty) < flt(self.qty):
 				d.status = "Work in Progress"
-			elif flt(d.completed_qty) == flt(self.qty):
+			elif flt(qty) == flt(self.qty):
 				d.status = "Completed"
-			elif flt(d.completed_qty) <= max_allowed_qty_for_wo:
+			elif flt(qty) <= max_allowed_qty_for_wo:
 				d.status = "Completed"
 			else:
 				frappe.throw(_("Completed Qty cannot be greater than 'Qty to Manufacture'"))
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 31b9201..de1f67f 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -2,12 +2,14 @@
  "actions": [],
  "creation": "2014-10-16 14:35:41.950175",
  "doctype": "DocType",
+ "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
   "details",
   "operation",
   "status",
   "completed_qty",
+  "process_loss_qty",
   "column_break_4",
   "bom",
   "workstation_type",
@@ -36,6 +38,7 @@
    "fieldtype": "Section Break"
   },
   {
+   "columns": 2,
    "fieldname": "operation",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -46,6 +49,7 @@
    "reqd": 1
   },
   {
+   "columns": 2,
    "fieldname": "bom",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -62,7 +66,7 @@
    "oldfieldtype": "Text"
   },
   {
-   "columns": 1,
+   "columns": 2,
    "description": "Operation completed for how many finished goods?",
    "fieldname": "completed_qty",
    "fieldtype": "Float",
@@ -80,6 +84,7 @@
    "options": "Pending\nWork in Progress\nCompleted"
   },
   {
+   "columns": 1,
    "fieldname": "workstation",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -115,7 +120,7 @@
    "fieldname": "time_in_mins",
    "fieldtype": "Float",
    "in_list_view": 1,
-   "label": "Operation Time",
+   "label": "Time",
    "oldfieldname": "time_in_mins",
    "oldfieldtype": "Currency",
    "reqd": 1
@@ -203,12 +208,21 @@
    "fieldtype": "Link",
    "label": "Workstation Type",
    "options": "Workstation Type"
+  },
+  {
+   "columns": 2,
+   "fieldname": "process_loss_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Process Loss Qty",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-11-09 01:37:56.563068",
+ "modified": "2023-06-09 14:03:01.612909",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order Operation",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 2c8e7a7..00b4471 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -656,6 +656,21 @@
 			});
 		}
 	},
+
+	process_loss_qty(frm) {
+		if (frm.doc.process_loss_qty) {
+			frm.doc.process_loss_percentage = flt(frm.doc.process_loss_qty / frm.doc.fg_completed_qty * 100, precision("process_loss_qty", frm.doc));
+			refresh_field("process_loss_percentage");
+		}
+	},
+
+	process_loss_percentage(frm) {
+		debugger
+		if (frm.doc.process_loss_percentage) {
+			frm.doc.process_loss_qty = flt((frm.doc.fg_completed_qty * frm.doc.process_loss_percentage) / 100 , precision("process_loss_qty", frm.doc));
+			refresh_field("process_loss_qty");
+		}
+	}
 });
 
 frappe.ui.form.on('Stock Entry Detail', {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index bc5533f..9bf679b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -24,6 +24,7 @@
   "company",
   "posting_date",
   "posting_time",
+  "column_break_eaoa",
   "set_posting_time",
   "inspection_required",
   "apply_putaway_rule",
@@ -640,16 +641,16 @@
   },
   {
    "collapsible": 1,
+   "depends_on": "eval: doc.fg_completed_qty > 0 && in_list([\"Manufacture\", \"Repack\"], doc.purpose)",
    "fieldname": "section_break_7qsm",
    "fieldtype": "Section Break",
    "label": "Process Loss"
   },
   {
-   "depends_on": "process_loss_percentage",
+   "depends_on": "eval: doc.fg_completed_qty > 0 && in_list([\"Manufacture\", \"Repack\"], doc.purpose)",
    "fieldname": "process_loss_qty",
    "fieldtype": "Float",
-   "label": "Process Loss Qty",
-   "read_only": 1
+   "label": "Process Loss Qty"
   },
   {
    "fieldname": "column_break_e92r",
@@ -657,8 +658,6 @@
   },
   {
    "depends_on": "eval:doc.from_bom && doc.fg_completed_qty",
-   "fetch_from": "bom_no.process_loss_percentage",
-   "fetch_if_empty": 1,
    "fieldname": "process_loss_percentage",
    "fieldtype": "Percent",
    "label": "% Process Loss"
@@ -667,6 +666,10 @@
    "fieldname": "items_section",
    "fieldtype": "Section Break",
    "label": "Items"
+  },
+  {
+   "fieldname": "column_break_eaoa",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-file-text",
@@ -674,7 +677,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-04-06 12:42:56.673180",
+ "modified": "2023-06-09 15:46:28.418339",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index f19df83..816957c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -442,13 +442,16 @@
 		if self.purpose == "Manufacture" and self.work_order:
 			for d in self.items:
 				if d.is_finished_item:
+					if self.process_loss_qty:
+						d.qty = self.fg_completed_qty - self.process_loss_qty
+
 					item_wise_qty.setdefault(d.item_code, []).append(d.qty)
 
 		precision = frappe.get_precision("Stock Entry Detail", "qty")
 		for item_code, qty_list in item_wise_qty.items():
 			total = flt(sum(qty_list), precision)
 
-			if (self.fg_completed_qty - total) > 0:
+			if (self.fg_completed_qty - total) > 0 and not self.process_loss_qty:
 				self.process_loss_qty = flt(self.fg_completed_qty - total, precision)
 				self.process_loss_percentage = flt(self.process_loss_qty * 100 / self.fg_completed_qty)
 
@@ -1640,16 +1643,36 @@
 		if self.purpose not in ("Manufacture", "Repack"):
 			return
 
-		self.process_loss_qty = 0.0
-		if not self.process_loss_percentage:
+		precision = self.precision("process_loss_qty")
+		if self.work_order:
+			data = frappe.get_all(
+				"Work Order Operation",
+				filters={"parent": self.work_order},
+				fields=["max(process_loss_qty) as process_loss_qty"],
+			)
+
+			if data and data[0].process_loss_qty is not None:
+				process_loss_qty = data[0].process_loss_qty
+				if flt(self.process_loss_qty, precision) != flt(process_loss_qty, precision):
+					self.process_loss_qty = flt(process_loss_qty, precision)
+
+					frappe.msgprint(
+						_("The Process Loss Qty has reset as per job cards Process Loss Qty"), alert=True
+					)
+
+		if not self.process_loss_percentage and not self.process_loss_qty:
 			self.process_loss_percentage = frappe.get_cached_value(
 				"BOM", self.bom_no, "process_loss_percentage"
 			)
 
-		if self.process_loss_percentage:
+		if self.process_loss_percentage and not self.process_loss_qty:
 			self.process_loss_qty = flt(
 				(flt(self.fg_completed_qty) * flt(self.process_loss_percentage)) / 100
 			)
+		else:
+			self.process_loss_percentage = flt(
+				(flt(self.process_loss_qty) / flt(self.fg_completed_qty)) * 100
+			)
 
 	def set_work_order_details(self):
 		if not getattr(self, "pro_doc", None):