Merge pull request #41085 from deepeshgarg007/ldc_permission

fix: Permission for lower deduction certificate
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 3a5acbe..278da1b 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -305,6 +305,7 @@
 						"serial_nos": args.serial_no,
 						"posting_date": today(),
 						"posting_time": nowtime(),
+						"do_not_submit": 1,
 					}
 				)
 			).name
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index fb1ed79..fc66345 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -539,9 +539,6 @@
 		return serial_nos
 
 	def __add_supplied_item(self, item_row, bom_item, qty):
-		if bom_item.get("qty"):
-			bom_item.pop("qty")
-
 		bom_item.conversion_factor = item_row.conversion_factor
 		rm_obj = self.append(self.raw_material_table, bom_item)
 		if rm_obj.get("qty"):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 0813961..b5c6cd9 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -948,6 +948,21 @@
 		if self.qty <= 0:
 			frappe.throw(_("Quantity to Manufacture must be greater than 0."))
 
+		if (
+			self.stock_uom
+			and frappe.get_cached_value("UOM", self.stock_uom, "must_be_whole_number")
+			and abs(cint(self.qty) - flt(self.qty, self.precision("qty"))) > 0.0000001
+		):
+			frappe.throw(
+				_(
+					"Qty To Manufacture ({0}) cannot be a fraction for the UOM {2}. To allow this, disable '{1}' in the UOM {2}."
+				).format(
+					flt(self.qty, self.precision("qty")),
+					frappe.bold(_("Must be Whole Number")),
+					frappe.bold(self.stock_uom),
+				),
+			)
+
 		if self.production_plan and self.production_plan_item and not self.production_plan_sub_assembly_item:
 			qty_dict = frappe.db.get_value(
 				"Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1
diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js
index d165d42..7a1efa8 100755
--- a/erpnext/setup/doctype/employee/employee.js
+++ b/erpnext/setup/doctype/employee/employee.js
@@ -18,18 +18,6 @@
 	refresh() {
 		erpnext.toggle_naming_series();
 	}
-
-	salutation() {
-		if (this.frm.doc.salutation) {
-			this.frm.set_value(
-				"gender",
-				{
-					Mr: "Male",
-					Ms: "Female",
-				}[this.frm.doc.salutation]
-			);
-		}
-	}
 };
 
 frappe.ui.form.on("Employee", {
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 3f37b7b..3ef0e57 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -86,6 +86,7 @@
 					"batches": frappe._dict({batch_no: 20}),
 					"type_of_transaction": "Inward",
 					"company": receipt.company,
+					"do_not_submit": 1,
 				}
 			)
 			.make_serial_and_batch_bundle()
@@ -176,6 +177,7 @@
 					"batches": frappe._dict({batch_no: batch_qty}),
 					"type_of_transaction": "Outward",
 					"company": receipt.company,
+					"do_not_submit": 1,
 				}
 			)
 			.make_serial_and_batch_bundle()
@@ -249,6 +251,7 @@
 					"batches": frappe._dict({batch_no: batch_qty}),
 					"type_of_transaction": "Outward",
 					"company": receipt.company,
+					"do_not_submit": 1,
 				}
 			)
 			.make_serial_and_batch_bundle()
@@ -341,6 +344,7 @@
 				"batches": frappe._dict({batch_name: 90}),
 				"type_of_transaction": "Inward",
 				"company": "_Test Company",
+				"do_not_submit": 1,
 			}
 		).make_serial_and_batch_bundle()
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 5ae0841..e507a20 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -239,8 +239,7 @@
 		self.po_required()
 		self.validate_items_quality_inspection()
 		self.validate_with_previous_doc()
-		self.validate_uom_is_integer("uom", ["qty", "received_qty"])
-		self.validate_uom_is_integer("stock_uom", "stock_qty")
+		self.validate_uom_is_integer()
 		self.validate_cwip_accounts()
 		self.validate_provisional_expense_account()
 
@@ -254,6 +253,10 @@
 		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
 		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
 
+	def validate_uom_is_integer(self):
+		super().validate_uom_is_integer("uom", ["qty", "received_qty"], "Purchase Receipt Item")
+		super().validate_uom_is_integer("stock_uom", "stock_qty", "Purchase Receipt Item")
+
 	def validate_cwip_accounts(self):
 		for item in self.get("items"):
 			if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 705b0f9..6b4002f 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -3009,6 +3009,7 @@
 					"serial_nos": serial_nos,
 					"posting_date": args.posting_date or today(),
 					"posting_time": args.posting_time,
+					"do_not_submit": 1,
 				}
 			)
 		).name
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 286a220..9195183 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -596,6 +596,13 @@
 
 		serial_batches = {}
 		for row in self.entries:
+			if not row.qty and row.batch_no and not row.serial_no:
+				frappe.throw(
+					_("At row {0}: Qty is mandatory for the batch {1}").format(
+						bold(row.idx), bold(row.batch_no)
+					)
+				)
+
 			if self.has_serial_no and not row.serial_no:
 				frappe.throw(
 					_("At row {0}: Serial No is mandatory for Item {1}").format(
@@ -831,7 +838,12 @@
 		for batch in batches:
 			frappe.db.set_value("Batch", batch.name, {"reference_name": None, "reference_doctype": None})
 
+	def validate_serial_and_batch_data(self):
+		if not self.voucher_no:
+			frappe.throw(_("Voucher No is mandatory"))
+
 	def before_submit(self):
+		self.validate_serial_and_batch_data()
 		self.validate_serial_and_batch_no_for_returned()
 		self.set_purchase_document_no()
 
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index ce50155..069192f 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -1483,7 +1483,7 @@
 					"posting_date": dn.posting_date,
 					"posting_time": dn.posting_time,
 					"voucher_type": "Delivery Note",
-					"do_not_submit": dn.name,
+					"do_not_submit": 1,
 				}
 			)
 		).name
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 0ffcdd5..b31ca30 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -1085,7 +1085,7 @@
 	)
 
 	bundle_id = None
-	if not args.use_serial_batch_fields and (args.batch_no or args.serial_no):
+	if not args.use_serial_batch_fields and (args.batch_no or args.serial_no) and args.qty:
 		batches = frappe._dict({})
 		if args.batch_no:
 			batches[args.batch_no] = args.qty
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index d89095e..3b7812f 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -30,8 +30,8 @@
 			except ValueError:
 				frappe.throw(_("Invalid Posting Time"))
 
-	def validate_uom_is_integer(self, uom_field, qty_fields):
-		validate_uom_is_integer(self, uom_field, qty_fields)
+	def validate_uom_is_integer(self, uom_field, qty_fields, child_dt=None):
+		validate_uom_is_integer(self, uom_field, qty_fields, child_dt)
 
 	def validate_with_previous_doc(self, ref):
 		self.exclude_fields = ["conversion_factor", "uom"] if self.get("is_return") else []
@@ -210,12 +210,13 @@
 			for f in qty_fields:
 				qty = d.get(f)
 				if qty:
-					if abs(cint(qty) - flt(qty, d.precision(f))) > 0.0000001:
+					precision = d.precision(f)
+					if abs(cint(qty) - flt(qty, precision)) > 0.0000001:
 						frappe.throw(
 							_(
 								"Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}."
 							).format(
-								flt(qty, d.precision(f)),
+								flt(qty, precision),
 								d.idx,
 								frappe.bold(_("Must be Whole Number")),
 								frappe.bold(d.get(uom_field)),