Merge pull request #31054 from marination/jc-excess-transfer-config

fix: Job Card excess transfer behaviour
diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 0f655e3..7c0f0db 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -7,7 +7,7 @@
 from frappe.model.document import Document
 from frappe.utils import getdate, nowdate
 
-from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
+from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
 from erpnext.hr.utils import set_employee_name, validate_active_employee
 from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
@@ -107,7 +107,10 @@
 		self.leave_balance = (
 			allocation.total_leaves_allocated
 			- allocation.carry_forwarded_leaves_count
-			- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
+			# adding this because the function returns a -ve number
+			+ get_leaves_for_period(
+				self.employee, self.leave_type, allocation.from_date, self.encashment_date
+			)
 		)
 
 		encashable_days = self.leave_balance - frappe.db.get_value(
@@ -126,14 +129,25 @@
 		return True
 
 	def get_leave_allocation(self):
-		leave_allocation = frappe.db.sql(
-			"""select name, to_date, total_leaves_allocated, carry_forwarded_leaves_count from `tabLeave Allocation` where '{0}'
-		between from_date and to_date and docstatus=1 and leave_type='{1}'
-		and employee= '{2}'""".format(
-				self.encashment_date or getdate(nowdate()), self.leave_type, self.employee
-			),
-			as_dict=1,
-		)  # nosec
+		date = self.encashment_date or getdate()
+
+		LeaveAllocation = frappe.qb.DocType("Leave Allocation")
+		leave_allocation = (
+			frappe.qb.from_(LeaveAllocation)
+			.select(
+				LeaveAllocation.name,
+				LeaveAllocation.from_date,
+				LeaveAllocation.to_date,
+				LeaveAllocation.total_leaves_allocated,
+				LeaveAllocation.carry_forwarded_leaves_count,
+			)
+			.where(
+				((LeaveAllocation.from_date <= date) & (date <= LeaveAllocation.to_date))
+				& (LeaveAllocation.docstatus == 1)
+				& (LeaveAllocation.leave_type == self.leave_type)
+				& (LeaveAllocation.employee == self.employee)
+			)
+		).run(as_dict=True)
 
 		return leave_allocation[0] if leave_allocation else None
 
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index 83eb969..d06b6a3 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -4,26 +4,42 @@
 import unittest
 
 import frappe
-from frappe.utils import add_months, today
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, get_year_ending, get_year_start, getdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
 from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
 from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
 	create_assignment_for_multiple_employees,
 )
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
-test_dependencies = ["Leave Type"]
+test_records = frappe.get_test_records("Leave Type")
 
 
-class TestLeaveEncashment(unittest.TestCase):
+class TestLeaveEncashment(FrappeTestCase):
 	def setUp(self):
-		frappe.db.sql("""delete from `tabLeave Period`""")
-		frappe.db.sql("""delete from `tabLeave Policy Assignment`""")
-		frappe.db.sql("""delete from `tabLeave Allocation`""")
-		frappe.db.sql("""delete from `tabLeave Ledger Entry`""")
-		frappe.db.sql("""delete from `tabAdditional Salary`""")
+		frappe.db.delete("Leave Period")
+		frappe.db.delete("Leave Policy Assignment")
+		frappe.db.delete("Leave Allocation")
+		frappe.db.delete("Leave Ledger Entry")
+		frappe.db.delete("Additional Salary")
+		frappe.db.delete("Leave Encashment")
+
+		if not frappe.db.exists("Leave Type", "_Test Leave Type Encashment"):
+			frappe.get_doc(test_records[2]).insert()
+
+		date = getdate()
+		year_start = getdate(get_year_start(date))
+		year_end = getdate(get_year_ending(date))
+
+		make_holiday_list("_Test Leave Encashment", year_start, year_end)
 
 		# create the leave policy
 		leave_policy = create_leave_policy(
@@ -32,9 +48,9 @@
 		leave_policy.submit()
 
 		# create employee, salary structure and assignment
-		self.employee = make_employee("test_employee_encashment@example.com")
+		self.employee = make_employee("test_employee_encashment@example.com", company="_Test Company")
 
-		self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3))
+		self.leave_period = create_leave_period(year_start, year_end, "_Test Company")
 
 		data = {
 			"assignment_based_on": "Leave Period",
@@ -53,27 +69,15 @@
 			other_details={"leave_encashment_amount_per_day": 50},
 		)
 
-	def tearDown(self):
-		for dt in [
-			"Leave Period",
-			"Leave Allocation",
-			"Leave Ledger Entry",
-			"Additional Salary",
-			"Leave Encashment",
-			"Salary Structure",
-			"Leave Policy",
-		]:
-			frappe.db.sql("delete from `tab%s`" % dt)
-
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
 	def test_leave_balance_value_and_amount(self):
-		frappe.db.sql("""delete from `tabLeave Encashment`""")
 		leave_encashment = frappe.get_doc(
 			dict(
 				doctype="Leave Encashment",
 				employee=self.employee,
 				leave_type="_Test Leave Type Encashment",
 				leave_period=self.leave_period.name,
-				payroll_date=today(),
+				encashment_date=self.leave_period.to_date,
 				currency="INR",
 			)
 		).insert()
@@ -88,15 +92,46 @@
 		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
 		self.assertTrue(add_sal)
 
-	def test_creation_of_leave_ledger_entry_on_submit(self):
-		frappe.db.sql("""delete from `tabLeave Encashment`""")
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_leave_balance_value_with_leaves_and_amount(self):
+		date = self.leave_period.from_date
+		leave_application = make_leave_application(
+			self.employee, date, add_days(date, 3), "_Test Leave Type Encashment"
+		)
+		leave_application.reload()
+
 		leave_encashment = frappe.get_doc(
 			dict(
 				doctype="Leave Encashment",
 				employee=self.employee,
 				leave_type="_Test Leave Type Encashment",
 				leave_period=self.leave_period.name,
-				payroll_date=today(),
+				encashment_date=self.leave_period.to_date,
+				currency="INR",
+			)
+		).insert()
+
+		self.assertEqual(leave_encashment.leave_balance, 10 - leave_application.total_leave_days)
+		# encashable days threshold is 5, total leaves are 6, so encashable days = 6-5 = 1
+		# with charge of 50 per day
+		self.assertEqual(leave_encashment.encashable_days, leave_encashment.leave_balance - 5)
+		self.assertEqual(leave_encashment.encashment_amount, 50)
+
+		leave_encashment.submit()
+
+		# assert links
+		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
+		self.assertTrue(add_sal)
+
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_creation_of_leave_ledger_entry_on_submit(self):
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				employee=self.employee,
+				leave_type="_Test Leave Type Encashment",
+				leave_period=self.leave_period.name,
+				encashment_date=self.leave_period.to_date,
 				currency="INR",
 			)
 		).insert()
diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html
index 3283987..5b073e6 100644
--- a/erpnext/templates/emails/request_for_quotation.html
+++ b/erpnext/templates/emails/request_for_quotation.html
@@ -1,24 +1,29 @@
 <h4>{{_("Request for Quotation")}}</h4>
 <p>{{ supplier_salutation if supplier_salutation else ''}} {{ supplier_name }},</p>
 <p>{{ message }}</p>
-
 <p>{{_("The Request for Quotation can be accessed by clicking on the following button")}}:</p>
-<p>
-	<button style="border: 1px solid #15c; padding: 6px; border-radius: 5px; background-color: white;">
-		<a href="{{ rfq_link }}" style="color: #15c; text-decoration:none;" target="_blank">Submit your Quotation</a>
-	</button>
-</p><br>
-
-<p>{{_("Regards")}},<br>
-{{ user_fullname }}</p><br>
-
+<br>
+<a
+	href="{{ rfq_link }}"
+	class="btn btn-default btn-sm"
+	target="_blank">
+	{{ _("Submit your Quotation") }}
+</a>
+<br>
+<br>
 {% if update_password_link %}
-
+<br>
 <p>{{_("Please click on the following button to set your new password")}}:</p>
-<p>
-	<button style="border: 1px solid #15c; padding: 4px; border-radius: 5px; background-color: white;">
-		<a href="{{ update_password_link }}" style="color: #15c; font-size: 12px; text-decoration:none;" target="_blank">{{_("Update Password") }}</a>
-	</button>
-</p>
-
+<a
+	href="{{ update_password_link }}"
+	class="btn btn-default btn-xs"
+	target="_blank">
+	{{_("Set Password") }}
+</a>
+<br>
+<br>
 {% endif %}
+<p>
+	{{_("Regards")}},<br>
+	{{ user_fullname }}
+</p>
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index ccd613d..8730c4e 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -1701,7 +1701,7 @@
 No Remarks,Keine Anmerkungen,
 No Result to submit,Kein Ergebnis zur Einreichung,
 No Salary Structure assigned for Employee {0} on given date {1},Keine Gehaltsstruktur für Mitarbeiter {0} am angegebenen Datum {1} zugewiesen,
-No Staffing Plans found for this Designation,Für diese Bezeichnung wurden keine Stellenpläne gefunden,
+No Staffing Plans found for this Designation,Für diese Position wurden keine Stellenpläne gefunden,
 No Student Groups created.,Keine Studentengruppen erstellt.,
 No Students in,Keine Studenten in,
 No Tax Withholding data found for the current Fiscal Year.,Keine Steuerverweigerungsdaten für das aktuelle Geschäftsjahr gefunden.,
@@ -2027,7 +2027,7 @@
 Please select Category first,Bitte zuerst Kategorie auswählen,
 Please select Charge Type first,Bitte zuerst Chargentyp auswählen,
 Please select Company,Bitte Unternehmen auswählen,
-Please select Company and Designation,Bitte wählen Sie Unternehmen und Stelle,
+Please select Company and Designation,Bitte wählen Sie Unternehmen und Position,
 Please select Company and Posting Date to getting entries,"Bitte wählen Sie Unternehmen und Buchungsdatum, um Einträge zu erhalten",
 Please select Company first,Bitte zuerst Unternehmen auswählen,
 Please select Completion Date for Completed Asset Maintenance Log,Bitte wählen Sie Fertigstellungsdatum für das abgeschlossene Wartungsprotokoll für den Vermögenswert,
@@ -2772,7 +2772,7 @@
 Split Batch,Split Batch,
 Split Issue,Split-Problem,
 Sports,Sport,
-Staffing Plan {0} already exist for designation {1},Personalplan {0} existiert bereits für Bezeichnung {1},
+Staffing Plan {0} already exist for designation {1},Personalplan {0} existiert bereits für Position {1},
 Standard,Standard,
 Standard Buying,Standard-Kauf,
 Standard Selling,Standard-Vertrieb,
@@ -3710,7 +3710,7 @@
 Delivery Notes,Lieferscheine,
 Depreciated Amount,Abschreibungsbetrag,
 Description,Beschreibung,
-Designation,Bezeichnung,
+Designation,Position,
 Difference Value,Differenzwert,
 Dimension Filter,Dimensionsfilter,
 Disabled,Deaktiviert,
@@ -3920,7 +3920,7 @@
 Please enter GSTIN and state for the Company Address {0},Bitte geben Sie GSTIN ein und geben Sie die Firmenadresse {0} an.,
 Please enter Item Code to get item taxes,"Bitte geben Sie den Artikelcode ein, um die Artikelsteuern zu erhalten",
 Please enter Warehouse and Date,Bitte geben Sie Lager und Datum ein,
-Please enter the designation,Bitte geben Sie die Bezeichnung ein,
+Please enter the designation,Bitte geben Sie die Position ein,
 Please login as a Marketplace User to edit this item.,"Bitte melden Sie sich als Marketplace-Benutzer an, um diesen Artikel zu bearbeiten.",
 Please login as a Marketplace User to report this item.,"Bitte melden Sie sich als Marketplace-Benutzer an, um diesen Artikel zu melden.",
 Please select <b>Template Type</b> to download template,"Bitte wählen Sie <b>Vorlagentyp</b> , um die Vorlage herunterzuladen",
@@ -6243,7 +6243,7 @@
 Create Sample Collection document for Lab Test,Erstellen Sie ein Probensammeldokument für den Labortest,
 Checking this will create a Sample Collection document  every time you create a Lab Test,"Wenn Sie dies aktivieren, wird jedes Mal, wenn Sie einen Labortest erstellen, ein Probensammeldokument erstellt",
 Employee name and designation in print,Name und Bezeichnung des Mitarbeiters im Druck,
-Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.,"Aktivieren Sie diese Option, wenn Sie möchten, dass der Name und die Bezeichnung des Mitarbeiters, der dem Benutzer zugeordnet ist, der das Dokument einreicht, im Labortestbericht gedruckt werden.",
+Check this if you want the Name and Designation of the Employee associated with the User who submits the document to be printed in the Lab Test Report.,"Aktivieren Sie diese Option, wenn Sie möchten, dass der Name und die Position des Mitarbeiters, der dem Benutzer zugeordnet ist, der das Dokument einreicht, im Labortestbericht gedruckt werden.",
 Do not print or email Lab Tests without Approval,Drucken oder senden Sie Labortests nicht ohne Genehmigung per E-Mail,
 Checking this will restrict printing and emailing of Lab Test documents unless they have the status as Approved.,"Wenn Sie dies aktivieren, wird das Drucken und E-Mailen von Labortestdokumenten eingeschränkt, sofern diese nicht den Status &quot;Genehmigt&quot; haben.",
 Custom Signature in Print,Kundenspezifische Unterschrift im Druck,
@@ -6499,7 +6499,7 @@
 Approver,Genehmiger,
 Required Skills,Benötigte Fähigkeiten,
 Skills,Kompetenzen,
-Designation Skill,Bezeichnung Fähigkeit,
+Designation Skill,Positions Fähigkeit,
 Skill,Fertigkeit,
 Driver,Fahrer/-in,
 HR-DRI-.YYYY.-,HR-DRI-.YYYY.-,
@@ -6798,7 +6798,7 @@
 Employment Type (optional),Anstellungsart (optional),
 Branch (optional),Zweigstelle (optional),
 Department (optional),Abteilung (optional),
-Designation (optional),Bezeichnung (optional),
+Designation (optional),Position (optional),
 Employee Grade (optional),Dienstgrad (optional),
 Employee (optional),Mitarbeiter (optional),
 Allocate Leaves,Blätter zuweisen,
@@ -7769,7 +7769,7 @@
 Applicable To (Role),Anwenden auf (Rolle),
 Applicable To (Employee),Anwenden auf (Mitarbeiter),
 Applicable To (User),Anwenden auf (Benutzer),
-Applicable To (Designation),Anwenden auf (Bezeichnung),
+Applicable To (Designation),Anwenden auf (Position),
 Approving Role (above authorized value),Genehmigende Rolle (über dem autorisierten Wert),
 Approving User  (above authorized value),Genehmigender Benutzer (über dem autorisierten Wert),
 Brand Defaults,Markenstandards,
@@ -8946,7 +8946,7 @@
 Requesting Department,Abteilung anfordern,
 Employee (Lab Technician),Mitarbeiter (Labortechniker),
 Lab Technician Name,Name des Labortechnikers,
-Lab Technician Designation,Bezeichnung des Labortechnikers,
+Lab Technician Designation,Position des Labortechnikers,
 Compound Test Result,Zusammengesetztes Testergebnis,
 Organism Test Result,Organismustestergebnis,
 Sensitivity Test Result,Empfindlichkeitstestergebnis,