From 87120dc1029d1a02ae2e71179a39d43c08fc705e Mon Sep 17 00:00:00 2001 From: Daniel Radl Date: Mon, 11 May 2026 15:51:00 +0000 Subject: [PATCH 1/4] fix(hr): allow zero allocations for negative leave types --- .../leave_allocation/leave_allocation.py | 1 + .../leave_policy_assignment.py | 6 +++++- .../test_leave_policy_assignment.py | 17 ++++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.py b/hrms/hr/doctype/leave_allocation/leave_allocation.py index 85298cbfe6..9b0719d92b 100755 --- a/hrms/hr/doctype/leave_allocation/leave_allocation.py +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.py @@ -283,6 +283,7 @@ def set_total_leaves_allocated(self): not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory") + and not frappe.db.get_value("Leave Type", self.leave_type, "allow_negative") ): frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}").format(self.leave_type)) diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py index 91689c96be..9359024e0c 100644 --- a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -147,7 +147,11 @@ def create_leave_allocation(self, annual_allocation, leave_details, date_of_join else [] ) - if new_leaves_allocated == 0 and not leave_details.is_earned_leave: + if ( + new_leaves_allocated == 0 + and not leave_details.is_earned_leave + and not leave_details.allow_negative + ): text = _( "Leave allocation is skipped for {0}, because number of leaves to be allocated is 0." ).format(frappe.bold(leave_details.name)) diff --git a/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py index 703b91cad7..9cf6bc17dd 100644 --- a/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py +++ b/hrms/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py @@ -258,6 +258,8 @@ def test_skip_zero_allocation_leaves(self): sick = create_leave_type( leave_type_name="_Test Sick Leave", non_encashable_leaves=0, max_leaves_allowed=2 ) + sick.allow_negative = 1 + sick.save() casual = create_leave_type( leave_type_name="_Test Casual Leave", non_encashable_leaves=0, max_leaves_allowed=12 ) @@ -304,17 +306,18 @@ def test_skip_zero_allocation_leaves(self): fields=["content"], ) - self.assertEqual(len(comments), 2) + self.assertEqual(len(comments), 1) self.assertIn(casual.name, comments[0]["content"]) - self.assertIn(sick.name, comments[1]["content"]) allocations = frappe.get_all( "Leave Allocation", filters={"leave_policy_assignment": assignment.name}, - fields=["leave_type", "new_leaves_allocated"], + fields=["leave_type", "new_leaves_allocated", "total_leaves_allocated"], ) + allocations = {allocation["leave_type"]: allocation for allocation in allocations} - self.assertEqual(allocations[0]["leave_type"], compoff.name) - self.assertEqual(allocations[0]["new_leaves_allocated"], 3) - self.assertEqual(allocations[1]["leave_type"], annual.name) - self.assertEqual(allocations[1]["new_leaves_allocated"], 3) + self.assertEqual(allocations[sick.name]["new_leaves_allocated"], 0) + self.assertEqual(allocations[sick.name]["total_leaves_allocated"], 0) + self.assertEqual(allocations[compoff.name]["new_leaves_allocated"], 3) + self.assertEqual(allocations[annual.name]["new_leaves_allocated"], 3) + self.assertNotIn(casual.name, allocations) From 6d9d865cb2723f2d8f90ba6da31626dc10e12342 Mon Sep 17 00:00:00 2001 From: Daniel Radl Date: Mon, 11 May 2026 16:41:20 +0000 Subject: [PATCH 2/4] fix(leave_policy_assignment): add allow_negative to get_leave_type_details --- .../doctype/leave_policy_assignment/leave_policy_assignment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py index 9359024e0c..2b58c4c388 100644 --- a/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ b/hrms/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -511,6 +511,7 @@ def get_leave_type_details(): "is_lwp", "is_earned_leave", "is_compensatory", + "allow_negative", "allocate_on_day", "is_carry_forward", "expire_carry_forwarded_leaves_after_days", From a7d01fe5f01a17dc9db00f165379c03ebe16770b Mon Sep 17 00:00:00 2001 From: Krishna Shirsath Date: Wed, 13 May 2026 11:19:24 +0530 Subject: [PATCH 3/4] refactor: streamline condition handling and optimize data retrieval in salary payments report --- .../salary_payments_via_ecs.py | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py index 6858e270da..9321f6a051 100644 --- a/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py +++ b/hrms/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -4,6 +4,8 @@ import frappe from frappe import _ +from frappe.query_builder import DocType +from frappe.query_builder.functions import Extract import erpnext @@ -65,28 +67,26 @@ def get_columns(filters): def get_conditions(filters): - conditions = [""] + SalarySlip = DocType("Salary Slip") + filter_clauses = [] if filters.get("department"): - conditions.append("department = '%s' " % (filters["department"])) - + filter_clauses.append(SalarySlip.department == filters["department"]) if filters.get("branch"): - conditions.append("branch = '%s' " % (filters["branch"])) - + filter_clauses.append(SalarySlip.branch == filters["branch"]) if filters.get("company"): - conditions.append("company = '%s' " % (filters["company"])) - + filter_clauses.append(SalarySlip.company == filters["company"]) if filters.get("month"): - conditions.append("month(start_date) = '%s' " % (filters["month"])) - + filter_clauses.append(Extract("month", SalarySlip.start_date) == int(filters["month"])) if filters.get("year"): - conditions.append("year(start_date) = '%s' " % (filters["year"])) + filter_clauses.append(Extract("year", SalarySlip.start_date) == int(filters["year"])) - return " and ".join(conditions) + return filter_clauses def get_data(filters): - data = [] + SalarySlip = DocType("Salary Slip") + filter_clauses = get_conditions(filters) fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"] if erpnext.get_region() == "India": @@ -108,16 +108,19 @@ def get_data(filters): }, ) - conditions = get_conditions(filters) + base_where = SalarySlip.docstatus == 1 + for clause in filter_clauses: + base_where = base_where & clause - entry = frappe.db.sql( - """ select employee, employee_name, gross_pay, net_pay - from `tabSalary Slip` - where docstatus = 1 %s """ - % (conditions), - as_dict=1, + entry = ( + frappe.qb.from_(SalarySlip) + .select(SalarySlip.employee, SalarySlip.employee_name, SalarySlip.gross_pay, SalarySlip.net_pay) + .where(base_where) + .run(as_dict=True) ) + data = [] + for d in entry: employee = { "branch": employee_data_dict.get(d.employee).get("branch"), From 8e80dae3fdb0091f02a96d918813da670f9ef266 Mon Sep 17 00:00:00 2001 From: Raheel Khan Date: Wed, 13 May 2026 11:53:03 +0530 Subject: [PATCH 4/4] fix(employee): Employee naming series visibility based on HR Settings (#4521) fix(employee): Employee naming series visibility based on HR Settings --- hrms/public/js/erpnext/employee.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hrms/public/js/erpnext/employee.js b/hrms/public/js/erpnext/employee.js index 99550db019..65d795935a 100644 --- a/hrms/public/js/erpnext/employee.js +++ b/hrms/public/js/erpnext/employee.js @@ -27,6 +27,11 @@ frappe.ui.form.on("Employee", { }); } frm.set_df_property("holiday_list", "hidden", 1); + + // hide naming series field based on hr settings + frappe.db.get_single_value("HR Settings", "emp_created_by").then((value) => { + frm.toggle_display("naming_series", value === "Naming Series"); + }); }, date_of_birth(frm) {