From 286eba0ccb91fc792fe3134091d21987cebb9382 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 21 Jul 2026 15:22:07 +0530 Subject: [PATCH 1/2] fix: Re-write queries using query builder --- .../employee_boarding_controller.py | 28 ++-- .../tests/test_employee_reminders.py | 20 +-- hrms/hr/doctype/attendance/attendance.py | 9 +- .../test_daily_work_summary.py | 27 ++-- .../department_approver.py | 43 +++--- .../expense_claim/test_expense_claim.py | 14 +- hrms/hr/doctype/interview/test_interview.py | 3 +- hrms/hr/doctype/job_offer/job_offer.py | 42 ++--- .../leave_allocation/leave_allocation.py | 19 ++- .../leave_application/leave_application.py | 143 +++++++++++------- .../test_leave_application.py | 8 +- .../leave_ledger_entry/leave_ledger_entry.py | 73 +++++---- .../hr/doctype/shift_request/shift_request.py | 11 +- .../shift_request/test_shift_request.py | 22 ++- .../hr/doctype/staffing_plan/staffing_plan.py | 113 +++++++++----- .../upload_attendance/upload_attendance.py | 20 ++- .../doctype/vehicle_log/test_vehicle_log.py | 5 +- .../employee_leave_balance_summary.py | 5 +- .../vehicle_expenses/test_vehicle_expenses.py | 5 +- .../vehicle_expenses/vehicle_expenses.py | 74 +++++---- hrms/hr/utils.py | 130 ++++++++-------- hrms/overrides/employee_master.py | 19 +-- .../doctype/payroll_entry/payroll_entry.py | 27 ++-- .../payroll_entry/test_payroll_entry.py | 67 ++++---- .../doctype/payroll_period/payroll_period.py | 55 ++++--- .../doctype/salary_slip/test_salary_slip.py | 67 +++++--- .../salary_structure/salary_structure.py | 36 +++-- .../salary_structure/test_salary_structure.py | 12 +- .../test_income_tax_computation.py | 17 ++- hrms/regional/india/utils.py | 29 ++-- 30 files changed, 660 insertions(+), 483 deletions(-) diff --git a/hrms/controllers/employee_boarding_controller.py b/hrms/controllers/employee_boarding_controller.py index 26b777eef5..f9578b2b47 100644 --- a/hrms/controllers/employee_boarding_controller.py +++ b/hrms/controllers/employee_boarding_controller.py @@ -75,20 +75,20 @@ def create_task_and_notify_user(self): users = [activity.user] if activity.user else [] if activity.role: - user_list = frappe.db.sql_list( - """ - SELECT - DISTINCT(has_role.parent) - FROM - `tabHas Role` has_role - LEFT JOIN `tabUser` user - ON has_role.parent = user.name - WHERE - has_role.parenttype = 'User' - AND user.enabled = 1 - AND has_role.role = %s - """, - activity.role, + has_role = frappe.qb.DocType("Has Role") + user = frappe.qb.DocType("User") + user_list = ( + frappe.qb.from_(has_role) + .left_join(user) + .on(has_role.parent == user.name) + .select(has_role.parent) + .distinct() + .where( + (has_role.parenttype == "User") + & (user.enabled == 1) + & (has_role.role == activity.role) + ) + .run(pluck="parent") ) users = unique(users + user_list) diff --git a/hrms/controllers/tests/test_employee_reminders.py b/hrms/controllers/tests/test_employee_reminders.py index 0efc09a047..40f5090808 100644 --- a/hrms/controllers/tests/test_employee_reminders.py +++ b/hrms/controllers/tests/test_employee_reminders.py @@ -63,8 +63,8 @@ def setUp(self): self.holiday_list_2 = test_holiday_list2 # Clear Email Queue - frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.sql("delete from `tabEmail Queue Recipient`") + frappe.qb.from_("Email Queue").delete().run() + frappe.qb.from_("Email Queue Recipient").delete().run() @classmethod def get_test_holiday_dates(cls): @@ -97,7 +97,9 @@ def test_is_holiday(self): self.assertTrue("test holiday1" in descriptions) def test_birthday_reminders(self): - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) + employee = frappe.get_doc( + "Employee", frappe.qb.from_("Employee").select("name").limit(1).run(pluck="name")[0] + ) employee.date_of_birth = "1992" + frappe.utils.nowdate()[4:] employee.company_email = "test@example.com" employee.company = "_Test Company" @@ -117,7 +119,7 @@ def test_birthday_reminders(self): send_birthday_reminders() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message) def test_work_anniversary_reminders(self): @@ -146,7 +148,7 @@ def test_work_anniversary_reminders(self): send_work_anniversary_reminders() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message) def test_work_anniversary_reminder_not_sent_for_0_years(self): @@ -179,7 +181,7 @@ def test_send_holidays_reminder_in_advance(self): send_holidays_reminder_in_advance(self.test_employee.get("name"), holidays) - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertEqual(len(email_queue), 1) self.assertTrue("Holidays this Week." in email_queue[0].message) @@ -196,7 +198,7 @@ def test_advance_holiday_reminders_monthly(self): ) send_reminders_in_advance_monthly() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertTrue(len(email_queue) > 0) # even though emp 2 has holiday, non-active employees should not be recipients @@ -223,7 +225,7 @@ def test_advance_holiday_reminders_weekly(self): ) send_reminders_in_advance_weekly() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertTrue(len(email_queue) > 0) # even though emp 2 has holiday, non-active employees should not be recipients @@ -249,7 +251,7 @@ def test_reminder_not_sent_if_no_holdays(self): raise_exception=False, ) send_holidays_reminder_in_advance(self.test_employee_2.get("name"), holidays) - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) + email_queue = frappe.qb.from_("Email Queue").select("*").run(as_dict=True) self.assertEqual(len(email_queue), 0) diff --git a/hrms/hr/doctype/attendance/attendance.py b/hrms/hr/doctype/attendance/attendance.py index 25d90bc694..454d7b242c 100644 --- a/hrms/hr/doctype/attendance/attendance.py +++ b/hrms/hr/doctype/attendance/attendance.py @@ -241,9 +241,12 @@ def check_leave_record(self): self.leave_application = None def validate_employee(self): - emp = frappe.db.sql( - "select name from `tabEmployee` where name = %s and status = 'Active'", self.employee - ) + Employee = frappe.qb.DocType("Employee") + emp = ( + frappe.qb.from_(Employee) + .select(Employee.name) + .where((Employee.name == self.employee) & (Employee.status == "Active")) + ).run() if not emp: frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee)) diff --git a/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py b/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py index 19a4772f7f..4520a6eb0f 100644 --- a/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py +++ b/hrms/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -60,11 +60,11 @@ def test_incoming(self): self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content) def setup_and_prepare_test(self, hour=None): - frappe.db.sql("delete from `tabDaily Work Summary`") - frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.sql("delete from `tabEmail Queue Recipient`") - frappe.db.sql("delete from `tabCommunication`") - frappe.db.sql("delete from `tabDaily Work Summary Group`") + frappe.qb.from_("Daily Work Summary").delete().run() + frappe.qb.from_("Email Queue").delete().run() + frappe.qb.from_("Email Queue Recipient").delete().run() + frappe.qb.from_("Communication").delete().run() + frappe.qb.from_("Daily Work Summary Group").delete().run() self.users = frappe.get_all("User", fields=["email"], filters=dict(email=("!=", "test@example.com"))) self.setup_groups(hour) @@ -75,11 +75,18 @@ def setup_and_prepare_test(self, hour=None): # check if emails are created - self.emails = frappe.db.sql( - """select r.recipient, q.message, q.message_id \ - from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \ - where q.name = r.parent""", - as_dict=1, + email_queue = frappe.qb.DocType("Email Queue") + email_queue_recipient = frappe.qb.DocType("Email Queue Recipient") + self.emails = ( + frappe.qb.from_(email_queue) + .join(email_queue_recipient) + .on(email_queue.name == email_queue_recipient.parent) + .select( + email_queue_recipient.recipient, + email_queue.message, + email_queue.message_id, + ) + .run(as_dict=True) ) def setup_groups(self, hour=None): diff --git a/hrms/hr/doctype/department_approver/department_approver.py b/hrms/hr/doctype/department_approver/department_approver.py index c2f09095a9..16918e72fb 100644 --- a/hrms/hr/doctype/department_approver/department_approver.py +++ b/hrms/hr/doctype/department_approver/department_approver.py @@ -5,6 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder import Order from frappe.utils import get_link_to_form @@ -48,14 +49,17 @@ def get_approvers(doctype: str, txt: str, searchfield: str, start: int, page_len "Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True ) if department_details: - department_list = frappe.db.sql( - """select name from `tabDepartment` where lft <= %s - and rgt >= %s - and disabled=0 - order by lft desc""", - (department_details.lft, department_details.rgt), - as_list=True, - ) + Department = frappe.qb.DocType("Department") + department_list = ( + frappe.qb.from_(Department) + .select(Department.name) + .where( + (Department.lft <= department_details.lft) + & (Department.rgt >= department_details.rgt) + & (Department.disabled == 0) + ) + .orderby(Department.lft, order=Order.desc) + ).run(as_list=True) if filters.get("doctype") == "Leave Application" and employee.leave_approver: approvers.append( @@ -82,17 +86,20 @@ def get_approvers(doctype: str, txt: str, searchfield: str, start: int, page_len parentfield = "shift_request_approver" field_name = "Shift Request Approver" if department_list: + User = frappe.qb.DocType("User") + Approver = frappe.qb.DocType("Department Approver") for d in department_list: - approvers += frappe.db.sql( - """select user.name, user.first_name, user.last_name from - tabUser user, `tabDepartment Approver` approver where - approver.parent = %s - and user.name like %s - and approver.parentfield = %s - and approver.approver=user.name""", - (d, "%" + txt + "%", parentfield), - as_list=True, - ) + approvers += ( + frappe.qb.from_(User) + .from_(Approver) + .select(User.name, User.first_name, User.last_name) + .where( + (Approver.parent == d) + & (User.name.like("%" + txt + "%")) + & (Approver.parentfield == parentfield) + & (Approver.approver == User.name) + ) + ).run(as_list=True) if len(approvers) == 0: error_msg = _("Please set {0} for the Employee: {1}").format( diff --git a/hrms/hr/doctype/expense_claim/test_expense_claim.py b/hrms/hr/doctype/expense_claim/test_expense_claim.py index 9268c948d9..ceac2e473a 100644 --- a/hrms/hr/doctype/expense_claim/test_expense_claim.py +++ b/hrms/hr/doctype/expense_claim/test_expense_claim.py @@ -427,13 +427,13 @@ def test_expense_claim_gl_entry(self): ) expense_claim.submit() - gl_entries = frappe.db.sql( - """select account, debit, credit - from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s - order by account asc""", - expense_claim.name, - as_dict=1, - ) + gle = frappe.qb.DocType("GL Entry") + gl_entries = ( + frappe.qb.from_(gle) + .select(gle.account, gle.debit, gle.credit) + .where((gle.voucher_type == "Expense Claim") & (gle.voucher_no == expense_claim.name)) + .orderby(gle.account, order=frappe.qb.asc) + ).run(as_dict=True) self.assertTrue(gl_entries) diff --git a/hrms/hr/doctype/interview/test_interview.py b/hrms/hr/doctype/interview/test_interview.py index ce307d425b..e02752ee44 100644 --- a/hrms/hr/doctype/interview/test_interview.py +++ b/hrms/hr/doctype/interview/test_interview.py @@ -41,7 +41,8 @@ def test_notification_on_rescheduling(self): ) previous_scheduled_date = interview.scheduled_on - frappe.db.sql("DELETE FROM `tabEmail Queue`") + email_queue = frappe.qb.DocType("Email Queue") + frappe.qb.from_(email_queue).delete().run() interview.reschedule_interview( add_days(getdate(previous_scheduled_date), 2), from_time="11:00:00", to_time="12:00:00" diff --git a/hrms/hr/doctype/job_offer/job_offer.py b/hrms/hr/doctype/job_offer/job_offer.py index 60574e6908..eadbb2fdf0 100644 --- a/hrms/hr/doctype/job_offer/job_offer.py +++ b/hrms/hr/doctype/job_offer/job_offer.py @@ -6,6 +6,7 @@ from frappe import _ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum from frappe.utils import cint, flt, get_link_to_form @@ -90,25 +91,30 @@ def update_job_applicant(status, job_applicant): def get_staffing_plan_detail(designation, company, offer_date): - detail = frappe.db.sql( - """ - SELECT DISTINCT spd.parent, - sp.from_date as from_date, - sp.to_date as to_date, + spd = frappe.qb.DocType("Staffing Plan Detail") + sp = frappe.qb.DocType("Staffing Plan") + + detail = ( + frappe.qb.from_(spd) + .inner_join(sp) + .on(spd.parent == sp.name) + .select( + spd.parent, + sp.from_date.as_("from_date"), + sp.to_date.as_("to_date"), sp.name, - sum(spd.vacancies) as vacancies, - spd.designation - FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp - WHERE - sp.docstatus=1 - AND spd.designation=%s - AND sp.company=%s - AND spd.parent = sp.name - AND %s between sp.from_date and sp.to_date - """, - (designation, company, offer_date), - as_dict=1, - ) + Sum(spd.vacancies).as_("vacancies"), + spd.designation, + ) + .distinct() + .where( + (sp.docstatus == 1) + & (spd.designation == designation) + & (sp.company == company) + & (sp.from_date <= offer_date) + & (offer_date <= sp.to_date) + ) + ).run(as_dict=1) return frappe._dict(detail[0]) if (detail and detail[0].parent) else None diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.py b/hrms/hr/doctype/leave_allocation/leave_allocation.py index 89f01cdf6c..8dbeaaa7f5 100755 --- a/hrms/hr/doctype/leave_allocation/leave_allocation.py +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.py @@ -248,13 +248,18 @@ def validate_allocation_overlap(self): ) def validate_back_dated_allocation(self): - future_allocation = frappe.db.sql( - """select name, from_date from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 and from_date > %s - and carry_forward=1""", - (self.employee, self.leave_type, self.to_date), - as_dict=1, - ) + LeaveAllocation = frappe.qb.DocType("Leave Allocation") + future_allocation = ( + frappe.qb.from_(LeaveAllocation) + .select(LeaveAllocation.name, LeaveAllocation.from_date) + .where( + (LeaveAllocation.employee == self.employee) + & (LeaveAllocation.leave_type == self.leave_type) + & (LeaveAllocation.docstatus == 1) + & (LeaveAllocation.from_date > self.to_date) + & (LeaveAllocation.carry_forward == 1) + ) + ).run(as_dict=True) if future_allocation: frappe.throw( diff --git a/hrms/hr/doctype/leave_application/leave_application.py b/hrms/hr/doctype/leave_application/leave_application.py index afa3bfc010..5d23f44a1c 100755 --- a/hrms/hr/doctype/leave_application/leave_application.py +++ b/hrms/hr/doctype/leave_application/leave_application.py @@ -6,7 +6,8 @@ import frappe from frappe import _ from frappe.model.workflow import get_workflow_name -from frappe.query_builder.functions import Max, Min, Sum +from frappe.query_builder import Order +from frappe.query_builder.functions import Count, Max, Min, Sum from frappe.utils import ( add_days, cint, @@ -266,13 +267,18 @@ def _get_leave_allocation_record(date): return allocation_based_on_from_date, allocation_based_on_to_date def validate_back_dated_application(self): - future_allocation = frappe.db.sql( - """select name, from_date from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 and from_date > %s - and carry_forward=1""", - (self.employee, self.leave_type, self.to_date), - as_dict=1, - ) + LeaveAllocation = frappe.qb.DocType("Leave Allocation") + future_allocation = ( + frappe.qb.from_(LeaveAllocation) + .select(LeaveAllocation.name, LeaveAllocation.from_date) + .where( + (LeaveAllocation.employee == self.employee) + & (LeaveAllocation.leave_type == self.leave_type) + & (LeaveAllocation.docstatus == 1) + & (LeaveAllocation.from_date > self.to_date) + & (LeaveAllocation.carry_forward == 1) + ) + ).run(as_dict=1) if future_allocation: frappe.throw( @@ -367,12 +373,17 @@ def create_or_update_attendance(self, attendance_name, date): def cancel_attendance(self): if self.docstatus == 2: - attendance = frappe.db.sql( - """select name from `tabAttendance` where employee = %s\ - and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""", - (self.employee, self.from_date, self.to_date), - as_dict=1, - ) + Attendance = frappe.qb.DocType("Attendance") + attendance = ( + frappe.qb.from_(Attendance) + .select(Attendance.name) + .where( + (Attendance.employee == self.employee) + & (Attendance.attendance_date.between(self.from_date, self.to_date)) + & (Attendance.docstatus < 2) + & (Attendance.status.isin(["On Leave", "Half Day"])) + ) + ).run(as_dict=1) for name in attendance: frappe.db.set_value("Attendance", name, "docstatus", 2) @@ -380,15 +391,21 @@ def validate_salary_processed_days(self): if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): return - last_processed_pay_slip = frappe.db.sql( - """ - select start_date, end_date from `tabSalary Slip` - where docstatus = 1 and employee = %s - and ((%s between start_date and end_date) or (%s between start_date and end_date)) - order by creation desc limit 1 - """, - (self.employee, self.to_date, self.from_date), - ) + SalarySlip = frappe.qb.DocType("Salary Slip") + last_processed_pay_slip = ( + frappe.qb.from_(SalarySlip) + .select(SalarySlip.start_date, SalarySlip.end_date) + .where( + (SalarySlip.docstatus == 1) + & (SalarySlip.employee == self.employee) + & ( + ((SalarySlip.start_date <= self.to_date) & (self.to_date <= SalarySlip.end_date)) + | ((SalarySlip.start_date <= self.from_date) & (self.from_date <= SalarySlip.end_date)) + ) + ) + .orderby(SalarySlip.creation, order=Order.desc) + .limit(1) + ).run() if last_processed_pay_slip: frappe.throw( @@ -487,22 +504,30 @@ def validate_leave_overlap(self): # hack! if name is null, it could cause problems with != self.name = "New Leave Application" - for d in frappe.db.sql( - """ - select - name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day, half_day_date - from `tabLeave Application` - where employee = %(employee)s and docstatus < 2 and status in ('Open', 'Approved') - and to_date >= %(from_date)s and from_date <= %(to_date)s - and name != %(name)s""", - { - "employee": self.employee, - "from_date": self.from_date, - "to_date": self.to_date, - "name": self.name, - }, - as_dict=1, - ): + LeaveApplication = frappe.qb.DocType("Leave Application") + overlapping_applications = ( + frappe.qb.from_(LeaveApplication) + .select( + LeaveApplication.name, + LeaveApplication.leave_type, + LeaveApplication.posting_date, + LeaveApplication.from_date, + LeaveApplication.to_date, + LeaveApplication.total_leave_days, + LeaveApplication.half_day, + LeaveApplication.half_day_date, + ) + .where( + (LeaveApplication.employee == self.employee) + & (LeaveApplication.docstatus < 2) + & (LeaveApplication.status.isin(["Open", "Approved"])) + & (LeaveApplication.to_date >= self.from_date) + & (LeaveApplication.from_date <= self.to_date) + & (LeaveApplication.name != self.name) + ) + ).run(as_dict=1) + + for d in overlapping_applications: if ( cint(self.half_day) == 1 and cint(d.half_day) == 1 @@ -527,16 +552,19 @@ def throw_overlap_error(self, d): frappe.throw(msg, OverlapError) def get_total_leaves_on_half_day(self): - leave_count_on_half_day_date = frappe.db.sql( - """select count(name) from `tabLeave Application` - where employee = %(employee)s - and docstatus < 2 - and status in ('Open', 'Approved') - and half_day = 1 - and half_day_date = %(half_day_date)s - and name != %(name)s""", - {"employee": self.employee, "half_day_date": self.half_day_date, "name": self.name}, - )[0][0] + LeaveApplication = frappe.qb.DocType("Leave Application") + leave_count_on_half_day_date = ( + frappe.qb.from_(LeaveApplication) + .select(Count(LeaveApplication.name)) + .where( + (LeaveApplication.employee == self.employee) + & (LeaveApplication.docstatus < 2) + & (LeaveApplication.status.isin(["Open", "Approved"])) + & (LeaveApplication.half_day == 1) + & (LeaveApplication.half_day_date == self.half_day_date) + & (LeaveApplication.name != self.name) + ) + ).run()[0][0] return leave_count_on_half_day_date * 0.5 @@ -1356,7 +1384,8 @@ def get_holidays(employee: str, from_date: str | datetime.date, to_date: str | d def is_lwp(leave_type): - lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type) + LeaveType = frappe.qb.DocType("Leave Type") + lwp = (frappe.qb.from_(LeaveType).select(LeaveType.is_lwp).where(LeaveType.name == leave_type)).run() return lwp and cint(lwp[0][0]) or 0 @@ -1463,12 +1492,14 @@ def add_holidays(events, start, end, employee, company): if not applicable_holiday_list: return - for holiday in frappe.db.sql( - """select name, holiday_date, description - from `tabHoliday` where parent=%s and holiday_date between %s and %s""", - (applicable_holiday_list, start, end), - as_dict=True, - ): + Holiday = frappe.qb.DocType("Holiday") + holidays = ( + frappe.qb.from_(Holiday) + .select(Holiday.name, Holiday.holiday_date, Holiday.description) + .where((Holiday.parent == applicable_holiday_list) & (Holiday.holiday_date.between(start, end))) + ).run(as_dict=True) + + for holiday in holidays: events.append( { "doctype": "Holiday", diff --git a/hrms/hr/doctype/leave_application/test_leave_application.py b/hrms/hr/doctype/leave_application/test_leave_application.py index 25884341a5..e50e6694cc 100644 --- a/hrms/hr/doctype/leave_application/test_leave_application.py +++ b/hrms/hr/doctype/leave_application/test_leave_application.py @@ -66,10 +66,10 @@ def setUp(self): ) def _clear_roles(self): - frappe.db.sql( - """delete from `tabHas Role` where parent in - ('test@example.com', 'test1@example.com', 'test2@example.com')""" - ) + has_role = frappe.qb.DocType("Has Role") + frappe.qb.from_(has_role).delete().where( + has_role.parent.isin(["test@example.com", "test1@example.com", "test2@example.com"]) + ).run() def get_application(self, doc): application = frappe.copy_doc(frappe.get_doc("Leave Application", doc)) diff --git a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 3f24022959..5be888aa55 100644 --- a/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -2,6 +2,8 @@ # For license information, please see license.txt import datetime +from pypika.terms import ExistsCriterion + import frappe from frappe import _ from frappe.model.document import Document @@ -113,14 +115,12 @@ def delete_ledger_entry(ledger): validate_leave_allocation_against_leave_application(ledger) expired_entry = get_previous_expiry_ledger_entry(ledger) - frappe.db.sql( - """DELETE - FROM `tabLeave Ledger Entry` - WHERE - `transaction_name`=%s - OR `name`=%s""", - (ledger.transaction_name, expired_entry), - ) + Ledger = frappe.qb.DocType("Leave Ledger Entry") + ( + frappe.qb.from_(Ledger) + .delete() + .where((Ledger.transaction_name == ledger.transaction_name) | (Ledger.name == expired_entry)) + ).run() def get_previous_expiry_ledger_entry(ledger): @@ -167,30 +167,43 @@ def process_expired_allocation(): leave_type = [record[0] for record in leave_type_records] or [""] # fetch non expired leave ledger entry of transaction_type allocation - expire_allocation = frappe.db.sql( - """ - SELECT - leaves, `to_date`, `from_date`, employee, leave_type, - is_carry_forward, transaction_name as name, transaction_type - FROM `tabLeave Ledger Entry` l - WHERE (NOT EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name<>l.name - AND docstatus = 1 - AND ( - is_carry_forward=l.is_carry_forward - OR (is_carry_forward = 0 AND leave_type not in %s) - ))) - AND transaction_type = 'Leave Allocation' - AND `to_date` < %s""", - (leave_type, today()), - as_dict=1, + Ledger = frappe.qb.DocType("Leave Ledger Entry").as_("l") + InnerLedger = frappe.qb.DocType("Leave Ledger Entry") + + inner_query = ( + frappe.qb.from_(InnerLedger) + .select(InnerLedger.name) + .where( + (InnerLedger.transaction_name == Ledger.transaction_name) + & (InnerLedger.transaction_type == "Leave Allocation") + & (InnerLedger.name != Ledger.name) + & (InnerLedger.docstatus == 1) + & ( + (InnerLedger.is_carry_forward == Ledger.is_carry_forward) + | ((InnerLedger.is_carry_forward == 0) & (InnerLedger.leave_type.notin(leave_type))) + ) + ) ) + expire_allocation = ( + frappe.qb.from_(Ledger) + .select( + Ledger.leaves, + Ledger.to_date, + Ledger.from_date, + Ledger.employee, + Ledger.leave_type, + Ledger.is_carry_forward, + Ledger.transaction_name.as_("name"), + Ledger.transaction_type, + ) + .where( + ExistsCriterion(inner_query).negate() + & (Ledger.transaction_type == "Leave Allocation") + & (Ledger.to_date < today()) + ) + ).run(as_dict=1) + if expire_allocation: create_expiry_ledger_entry(expire_allocation) diff --git a/hrms/hr/doctype/shift_request/shift_request.py b/hrms/hr/doctype/shift_request/shift_request.py index a7f7b6217f..28e337599a 100644 --- a/hrms/hr/doctype/shift_request/shift_request.py +++ b/hrms/hr/doctype/shift_request/shift_request.py @@ -106,9 +106,14 @@ def validate_default_shift(self): def validate_approver(self): department = frappe.get_value("Employee", self.employee, "department") shift_approver = frappe.get_value("Employee", self.employee, "shift_request_approver") - approvers = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), + dept_approver = frappe.qb.DocType("Department Approver") + approvers = ( + frappe.qb.from_(dept_approver) + .select(dept_approver.approver) + .where( + (dept_approver.parent == department) & (dept_approver.parentfield == "shift_request_approver") + ) + .run() ) approvers = [approver[0] for approver in approvers] approvers.append(shift_approver) diff --git a/hrms/hr/doctype/shift_request/test_shift_request.py b/hrms/hr/doctype/shift_request/test_shift_request.py index 363d1f0d78..a838981fc8 100644 --- a/hrms/hr/doctype/shift_request/test_shift_request.py +++ b/hrms/hr/doctype/shift_request/test_shift_request.py @@ -21,10 +21,13 @@ def test_make_shift_request(self): setup_shift_type(shift_type="Day Shift") department = frappe.get_value("Employee", "_T-Employee-00001", "department") set_shift_approver(department) - approver = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), - )[0][0] + DepartmentApprover = frappe.qb.DocType("Department Approver") + approver = ( + frappe.qb.from_(DepartmentApprover) + .select(DepartmentApprover.approver) + .where(DepartmentApprover.parent == department) + .where(DepartmentApprover.parentfield == "shift_request_approver") + ).run()[0][0] shift_request = make_shift_request(approver) @@ -64,10 +67,13 @@ def test_shift_request_approver_perms(self): shift_request.reload() department = frappe.get_value("Employee", "_T-Employee-00001", "department") set_shift_approver(department) - department_approver = frappe.db.sql( - """select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", - (department), - )[0][0] + DepartmentApprover = frappe.qb.DocType("Department Approver") + department_approver = ( + frappe.qb.from_(DepartmentApprover) + .select(DepartmentApprover.approver) + .where(DepartmentApprover.parent == department) + .where(DepartmentApprover.parentfield == "shift_request_approver") + ).run()[0][0] shift_request.approver = department_approver shift_request.save() self.assertTrue(shift_request.name not in frappe.share.get_shared("Shift Request", user)) diff --git a/hrms/hr/doctype/staffing_plan/staffing_plan.py b/hrms/hr/doctype/staffing_plan/staffing_plan.py index d7bc25edc9..66e762320e 100644 --- a/hrms/hr/doctype/staffing_plan/staffing_plan.py +++ b/hrms/hr/doctype/staffing_plan/staffing_plan.py @@ -6,6 +6,7 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder.functions import Sum from frappe.utils import cint, flt, getdate, nowdate from frappe.utils.nestedset import get_descendants_of @@ -78,14 +79,21 @@ def set_number_of_positions(self, detail): def validate_overlap(self, staffing_plan_detail): # Validate if any submitted Staffing Plan exist for any Designations in this plan # and spd.vacancies>0 ? - overlap = frappe.db.sql( - """select spd.parent - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), - ) + spd = frappe.qb.DocType("Staffing Plan Detail") + sp = frappe.qb.DocType("Staffing Plan") + overlap = ( + frappe.qb.from_(spd) + .join(sp) + .on(spd.parent == sp.name) + .select(spd.parent) + .where( + (spd.designation == staffing_plan_detail.designation) + & (sp.docstatus == 1) + & (sp.to_date >= self.from_date) + & (sp.from_date <= self.to_date) + & (sp.company == self.company) + ) + ).run() if overlap and overlap[0][0]: frappe.throw( _("Staffing Plan {0} already exist for designation {1}").format( @@ -125,16 +133,29 @@ def validate_with_parent_plan(self, staffing_plan_detail): # Get vacanices already planned for all companies down the hierarchy of Parent Company lft, rgt = frappe.get_cached_value("Company", parent_company, ["lft", "rgt"]) - all_sibling_details = frappe.db.sql( - """select sum(spd.vacancies) as vacancies, - sum(spd.total_estimated_cost) as total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <=%s - and sp.company in (select name from tabCompany where lft > %s and rgt < %s) - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), - as_dict=1, + spd = frappe.qb.DocType("Staffing Plan Detail") + sp = frappe.qb.DocType("Staffing Plan") + company = frappe.qb.DocType("Company") + all_sibling_details = ( + frappe.qb.from_(spd) + .join(sp) + .on(spd.parent == sp.name) + .select( + Sum(spd.vacancies).as_("vacancies"), + Sum(spd.total_estimated_cost).as_("total_estimated_cost"), + ) + .where( + (spd.designation == staffing_plan_detail.designation) + & (sp.docstatus == 1) + & (sp.to_date >= self.from_date) + & (sp.from_date <= self.to_date) + & sp.company.isin( + frappe.qb.from_(company) + .select(company.name) + .where((company.lft > lft) & (company.rgt < rgt)) + ) + ) + .run(as_dict=1) )[0] if ( @@ -160,16 +181,29 @@ def validate_with_parent_plan(self, staffing_plan_detail): def validate_with_subsidiary_plans(self, staffing_plan_detail): # Valdate this plan with all child company plan - children_details = frappe.db.sql( - """select sum(spd.vacancies) as vacancies, - sum(spd.total_estimated_cost) as total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where spd.designation=%s and sp.docstatus=1 - and sp.to_date >= %s and sp.from_date <=%s - and sp.company in (select name from tabCompany where parent_company = %s) - """, - (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), - as_dict=1, + spd = frappe.qb.DocType("Staffing Plan Detail") + sp = frappe.qb.DocType("Staffing Plan") + company = frappe.qb.DocType("Company") + children_details = ( + frappe.qb.from_(spd) + .join(sp) + .on(spd.parent == sp.name) + .select( + Sum(spd.vacancies).as_("vacancies"), + Sum(spd.total_estimated_cost).as_("total_estimated_cost"), + ) + .where( + (spd.designation == staffing_plan_detail.designation) + & (sp.docstatus == 1) + & (sp.to_date >= self.from_date) + & (sp.from_date <= self.to_date) + & sp.company.isin( + frappe.qb.from_(company) + .select(company.name) + .where(company.parent_company == self.company) + ) + ) + .run(as_dict=1) )[0] if ( @@ -251,14 +285,21 @@ def get_active_staffing_plan_details( if not company or not designation: frappe.throw(_("Please select Company and Designation")) - staffing_plan = frappe.db.sql( - """ - select sp.name, spd.vacancies, spd.total_estimated_cost - from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name - where company=%s and spd.designation=%s and sp.docstatus=1 - and to_date >= %s and from_date <= %s """, - (company, designation, from_date, to_date), - as_dict=1, + spd = frappe.qb.DocType("Staffing Plan Detail") + sp = frappe.qb.DocType("Staffing Plan") + staffing_plan = ( + frappe.qb.from_(spd) + .join(sp) + .on(spd.parent == sp.name) + .select(sp.name, spd.vacancies, spd.total_estimated_cost) + .where( + (sp.company == company) + & (spd.designation == designation) + & (sp.docstatus == 1) + & (sp.to_date >= from_date) + & (sp.from_date <= to_date) + ) + .run(as_dict=1) ) if not staffing_plan: diff --git a/hrms/hr/doctype/upload_attendance/upload_attendance.py b/hrms/hr/doctype/upload_attendance/upload_attendance.py index f3176a29c5..79247c732d 100644 --- a/hrms/hr/doctype/upload_attendance/upload_attendance.py +++ b/hrms/hr/doctype/upload_attendance/upload_attendance.py @@ -151,12 +151,20 @@ def get_active_employees(): def get_existing_attendance_records(args): - attendance = frappe.db.sql( - """select name, attendance_date, employee, status, leave_type, naming_series - from `tabAttendance` where attendance_date between %s and %s and docstatus < 2""", - (args["from_date"], args["to_date"]), - as_dict=1, - ) + Attendance = frappe.qb.DocType("Attendance") + attendance = ( + frappe.qb.from_(Attendance) + .select( + Attendance.name, + Attendance.attendance_date, + Attendance.employee, + Attendance.status, + Attendance.leave_type, + Attendance.naming_series, + ) + .where(Attendance.attendance_date[args["from_date"] : args["to_date"]]) + .where(Attendance.docstatus < 2) + ).run(as_dict=1) existing_attendance = {} for att in attendance: diff --git a/hrms/hr/doctype/vehicle_log/test_vehicle_log.py b/hrms/hr/doctype/vehicle_log/test_vehicle_log.py index 792efa3108..4f5d589c77 100644 --- a/hrms/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/hrms/hr/doctype/vehicle_log/test_vehicle_log.py @@ -19,7 +19,10 @@ class TestVehicleLog(HRMSTestSuite): def setUp(self): - employee_id = frappe.db.sql("""select name from `tabEmployee` where name='testdriver@example.com'""") + employee = frappe.qb.DocType("Employee") + employee_id = ( + frappe.qb.from_(employee).select(employee.name).where(employee.name == "testdriver@example.com") + ).run() self.employee_id = employee_id[0][0] if employee_id else None if not self.employee_id: diff --git a/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index e991571c02..7237a75b5f 100644 --- a/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/hrms/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -9,7 +9,10 @@ def execute(filters=None): - leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc") + LeaveType = frappe.qb.DocType("Leave Type") + leave_types = ( + frappe.qb.from_(LeaveType).select(LeaveType.name).orderby(LeaveType.name, order=frappe.qb.asc) + ).run(pluck="name") columns = get_columns(leave_types) data = get_data(filters, leave_types) diff --git a/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py b/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py index d4b94fc63c..b68a5b994f 100644 --- a/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py +++ b/hrms/hr/report/vehicle_expenses/test_vehicle_expenses.py @@ -16,7 +16,10 @@ class TestVehicleExpenses(HRMSTestSuite): def setUp(self): - employee_id = frappe.db.sql("""select name from `tabEmployee` where name='testdriver@example.com'""") + employee = frappe.qb.DocType("Employee") + employee_id = ( + frappe.qb.from_(employee).select(employee.name).where(employee.name == "testdriver@example.com") + ).run() self.employee_id = employee_id[0][0] if employee_id else None if not self.employee_id: self.employee_id = make_employee("testdriver@example.com", company="_Test Company") diff --git a/hrms/hr/report/vehicle_expenses/vehicle_expenses.py b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py index 8fec0140ab..cda1516f9a 100644 --- a/hrms/hr/report/vehicle_expenses/vehicle_expenses.py +++ b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Sum from frappe.utils import flt from erpnext.accounts.report.financial_statements import get_period_list @@ -61,29 +62,40 @@ def get_columns(): def get_vehicle_log_data(filters): start_date, end_date = get_period_dates(filters) - conditions, values = get_conditions(filters) - - # nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql - data = frappe.db.sql( - f""" - SELECT - vhcl.license_plate as vehicle, vhcl.make, vhcl.model, - vhcl.location, log.name as log_name, log.odometer, - log.date, log.employee, log.fuel_qty, - log.price as fuel_price, - log.fuel_qty * log.price as fuel_expense - FROM - `tabVehicle` vhcl,`tabVehicle Log` log - WHERE - vhcl.license_plate = log.license_plate - and log.docstatus = 1 - and date between %(start_date)s and %(end_date)s - {conditions} - ORDER BY date""", - values, - as_dict=1, + + vhcl = frappe.qb.DocType("Vehicle") + log = frappe.qb.DocType("Vehicle Log") + + query = ( + frappe.qb.from_(vhcl) + .from_(log) + .select( + vhcl.license_plate.as_("vehicle"), + vhcl.make, + vhcl.model, + vhcl.location, + log.name.as_("log_name"), + log.odometer, + log.date, + log.employee, + log.fuel_qty, + log.price.as_("fuel_price"), + (log.fuel_qty * log.price).as_("fuel_expense"), + ) + .where(vhcl.license_plate == log.license_plate) + .where(log.docstatus == 1) + .where(log.date[start_date:end_date]) + .orderby(log.date) ) + if filters.employee: + query = query.where(log.employee == filters.employee) + + if filters.vehicle: + query = query.where(vhcl.license_plate == filters.vehicle) + + data = query.run(as_dict=True) + for row in data: row["service_expense"] = get_service_expense(row.log_name) @@ -118,16 +130,16 @@ def get_period_dates(filters): def get_service_expense(logname): - expense_amount = frappe.db.sql( - """ - SELECT sum(expense_amount) - FROM - `tabVehicle Log` log, `tabVehicle Service` service - WHERE - service.parent=log.name and log.name=%s - """, - logname, - ) + log = frappe.qb.DocType("Vehicle Log") + service = frappe.qb.DocType("Vehicle Service") + + expense_amount = ( + frappe.qb.from_(log) + .from_(service) + .select(Sum(service.expense_amount)) + .where(service.parent == log.name) + .where(log.name == logname) + ).run() return flt(expense_amount[0][0]) if expense_amount else 0.0 diff --git a/hrms/hr/utils.py b/hrms/hr/utils.py index 1b78f4dcb6..11fe15869a 100644 --- a/hrms/hr/utils.py +++ b/hrms/hr/utils.py @@ -9,7 +9,7 @@ from frappe.model.document import Document from frappe.query_builder import Criterion from frappe.query_builder.custom import ConstantColumn -from frappe.query_builder.functions import Count +from frappe.query_builder.functions import Count, Sum from frappe.utils import ( add_days, add_months, @@ -202,28 +202,18 @@ def validate_dates(doc, from_date, to_date, restrict_future_dates=True): def validate_overlap(doc, from_date, to_date, company=None): - query = """ - select name - from `tab{0}` - where name != %(name)s - """ - query += get_doc_condition(doc.doctype) - if not doc.name: # hack! if name is null, it could cause problems with != doc.name = "New " + doc.doctype - overlap_doc = frappe.db.sql( - query.format(doc.doctype), - { - "employee": doc.get("employee"), - "from_date": from_date, - "to_date": to_date, - "name": doc.name, - "company": company, - }, - as_dict=1, - ) + table = frappe.qb.DocType(doc.doctype) + query = frappe.qb.from_(table).select(table.name).where(table.name != doc.name) + + condition = get_doc_condition(doc.doctype, table, doc.get("employee"), from_date, to_date, company) + if condition is not None: + query = query.where(condition) + + overlap_doc = query.run(as_dict=True) if overlap_doc: if doc.get("employee"): @@ -233,16 +223,23 @@ def validate_overlap(doc, from_date, to_date, company=None): throw_overlap_error(doc, exists_for, overlap_doc[0].name, from_date, to_date) -def get_doc_condition(doctype): +def get_doc_condition(doctype, table, employee, from_date, to_date, company): if doctype == "Compensatory Leave Request": - return "and employee = %(employee)s and docstatus < 2 \ - and (work_from_date between %(from_date)s and %(to_date)s \ - or work_end_date between %(from_date)s and %(to_date)s \ - or (work_from_date < %(from_date)s and work_end_date > %(to_date)s))" + return ( + (table.employee == employee) + & (table.docstatus < 2) + & ( + table.work_from_date.between(from_date, to_date) + | table.work_end_date.between(from_date, to_date) + | ((table.work_from_date < from_date) & (table.work_end_date > to_date)) + ) + ) elif doctype == "Leave Period": - return "and company = %(company)s and (`from_date` between %(from_date)s and %(to_date)s \ - or `to_date` between %(from_date)s and %(to_date)s \ - or (`from_date` < %(from_date)s and `to_date` > %(to_date)s))" + return (table.company == company) & ( + table.from_date.between(from_date, to_date) + | table.to_date.between(from_date, to_date) + | ((table.from_date < from_date) & (table.to_date > to_date)) + ) def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date): @@ -678,31 +675,34 @@ def get_salary_assignments(employee, payroll_period): def get_sal_slip_total_benefit_given(employee, payroll_period, component=False): total_given_benefit_amount = 0 - query = """ - select sum(sd.amount) as total_amount - from `tabSalary Slip` ss, `tabSalary Detail` sd - where ss.employee=%(employee)s - and ss.docstatus = 1 and ss.name = sd.parent - and sd.is_flexible_benefit = 1 and sd.parentfield = "earnings" - and sd.parenttype = "Salary Slip" - and (ss.start_date between %(start_date)s and %(end_date)s - or ss.end_date between %(start_date)s and %(end_date)s - or (ss.start_date < %(start_date)s and ss.end_date > %(end_date)s)) - """ + start_date = payroll_period.start_date + end_date = payroll_period.end_date + + ss = frappe.qb.DocType("Salary Slip") + sd = frappe.qb.DocType("Salary Detail") + query = ( + frappe.qb.from_(ss) + .from_(sd) + .select(Sum(sd.amount).as_("total_amount")) + .where( + (ss.employee == employee) + & (ss.docstatus == 1) + & (ss.name == sd.parent) + & (sd.is_flexible_benefit == 1) + & (sd.parentfield == "earnings") + & (sd.parenttype == "Salary Slip") + & ( + ss.start_date.between(start_date, end_date) + | ss.end_date.between(start_date, end_date) + | ((ss.start_date < start_date) & (ss.end_date > end_date)) + ) + ) + ) if component: - query += "and sd.salary_component = %(component)s" + query = query.where(sd.salary_component == component) - sum_of_given_benefit = frappe.db.sql( - query, - { - "employee": employee, - "start_date": payroll_period.start_date, - "end_date": payroll_period.end_date, - "component": component, - }, - as_dict=True, - ) + sum_of_given_benefit = query.run(as_dict=True) if sum_of_given_benefit and flt(sum_of_given_benefit[0].total_amount) > 0: total_given_benefit_amount = sum_of_given_benefit[0].total_amount @@ -768,28 +768,22 @@ def calculate_tax_with_marginal_relief(tax_slab, tax_amount, annual_taxable_earn def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, component=False): total_claimed_amount = 0 - query = """ - select sum(claimed_amount) as 'total_amount' - from `tabEmployee Benefit Claim` - where employee=%(employee)s - and docstatus = 1 - and (claim_date between %(start_date)s and %(end_date)s) - """ + ebc = frappe.qb.DocType("Employee Benefit Claim") + query = ( + frappe.qb.from_(ebc) + .select(Sum(ebc.claimed_amount).as_("total_amount")) + .where( + (ebc.employee == employee) + & (ebc.docstatus == 1) + & (ebc.claim_date.between(payroll_period.start_date, payroll_period.end_date)) + ) + ) if non_pro_rata: - query += "and pay_against_benefit_claim = 1" + query = query.where(ebc.pay_against_benefit_claim == 1) if component: - query += "and earning_component = %(component)s" + query = query.where(ebc.earning_component == component) - sum_of_claimed_amount = frappe.db.sql( - query, - { - "employee": employee, - "start_date": payroll_period.start_date, - "end_date": payroll_period.end_date, - "component": component, - }, - as_dict=True, - ) + sum_of_claimed_amount = query.run(as_dict=True) if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0: total_claimed_amount = sum_of_claimed_amount[0].total_amount return total_claimed_amount diff --git a/hrms/overrides/employee_master.py b/hrms/overrides/employee_master.py index 60c6533c43..5a2ea9f5f2 100644 --- a/hrms/overrides/employee_master.py +++ b/hrms/overrides/employee_master.py @@ -4,6 +4,8 @@ import frappe from frappe import _ from frappe.model.naming import set_name_by_naming_series +from frappe.query_builder import Interval +from frappe.query_builder.functions import Count, CurDate, UnixTimestamp from frappe.utils import add_years, cint, get_link_to_form, getdate from erpnext.setup.doctype.employee.employee import Employee @@ -130,16 +132,15 @@ def get_timeline_data(doctype: str, name: str) -> dict: open_count = get_open_count(doctype, name) out["count"] = open_count["count"] + Attendance = frappe.qb.DocType("Attendance") timeline_data = dict( - frappe.db.sql( - """ - select unix_timestamp(attendance_date), count(*) - from `tabAttendance` where employee=%s - and attendance_date > date_sub(curdate(), interval 1 year) - and status in ('Present', 'Half Day') - group by attendance_date""", - name, - ) + frappe.qb.from_(Attendance) + .select(UnixTimestamp(Attendance.attendance_date), Count("*")) + .where(Attendance.employee == name) + .where(Attendance.attendance_date > (CurDate() - Interval(years=1))) + .where(Attendance.status.isin(["Present", "Half Day"])) + .groupby(Attendance.attendance_date) + .run() ) out["timeline_data"] = timeline_data diff --git a/hrms/payroll/doctype/payroll_entry/payroll_entry.py b/hrms/payroll/doctype/payroll_entry/payroll_entry.py index b9a4b82d8e..da2863315b 100644 --- a/hrms/payroll/doctype/payroll_entry/payroll_entry.py +++ b/hrms/payroll/doctype/payroll_entry/payroll_entry.py @@ -1729,18 +1729,25 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr def get_payroll_entries_for_jv( doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict ) -> list: - # nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql - return frappe.db.sql( - f""" - select name from `tabPayroll Entry` - where `{searchfield}` LIKE %(txt)s - and name not in - (select reference_name from `tabJournal Entry Account` - where reference_type="Payroll Entry") - order by name limit %(start)s, %(page_len)s""", - {"txt": "%%%s%%" % txt, "start": start, "page_len": page_len}, + PayrollEntry = frappe.qb.DocType("Payroll Entry") + JournalEntryAccount = frappe.qb.DocType("Journal Entry Account") + + linked_entries = ( + frappe.qb.from_(JournalEntryAccount) + .select(JournalEntryAccount.reference_name) + .where(JournalEntryAccount.reference_type == "Payroll Entry") ) + return ( + frappe.qb.from_(PayrollEntry) + .select(PayrollEntry.name) + .where(PayrollEntry[searchfield].like("%%%s%%" % txt)) + .where(PayrollEntry.name.notin(linked_entries)) + .orderby(PayrollEntry.name) + .limit(page_len) + .offset(start) + ).run() + def get_employee_list( filters: frappe._dict, diff --git a/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py b/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py index 524d4cc22a..4e1e9c8d23 100644 --- a/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/hrms/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -4,6 +4,7 @@ from dateutil.relativedelta import relativedelta import frappe +from frappe.query_builder.functions import Coalesce, Sum from frappe.utils import add_days, add_months, cstr, date_diff, flt import erpnext @@ -110,19 +111,19 @@ def test_multi_currency_payroll_entry(self): self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit) self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit) - payment_entry = frappe.db.sql( - """ - select - ifnull(sum(je.total_debit),0) as total_debit, - ifnull(sum(je.total_credit),0) as total_credit - from `tabJournal Entry` je, `tabJournal Entry Account` jea - where je.name = jea.parent - and (je.voucher_type = 'Bank Entry' or je.voucher_type = 'Cash Entry') - and jea.reference_name = %s - """, - payroll_entry.name, - as_dict=1, - ) + je = frappe.qb.DocType("Journal Entry") + jea = frappe.qb.DocType("Journal Entry Account") + payment_entry = ( + frappe.qb.from_(je) + .from_(jea) + .select( + Coalesce(Sum(je.total_debit), 0).as_("total_debit"), + Coalesce(Sum(je.total_credit), 0).as_("total_credit"), + ) + .where(je.name == jea.parent) + .where((je.voucher_type == "Bank Entry") | (je.voucher_type == "Cash Entry")) + .where(jea.reference_name == payroll_entry.name) + ).run(as_dict=1) self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_debit) self.assertEqual(salary_slip.base_net_pay, payment_entry[0].total_credit) @@ -154,15 +155,14 @@ def test_payroll_entry_with_employee_cost_center(self): cost_center="Main - _TC", ) je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry") - je_entries = frappe.db.sql( - """ - select account, cost_center, debit, credit - from `tabJournal Entry Account` - where parent=%s - order by account, cost_center - """, - je, - ) + jea = frappe.qb.DocType("Journal Entry Account") + je_entries = ( + frappe.qb.from_(jea) + .select(jea.account, jea.cost_center, jea.debit, jea.credit) + .where(jea.parent == je) + .orderby(jea.account) + .orderby(jea.cost_center) + ).run() expected_je = ( ("_Test Payroll Payable - _TC", "Main - _TC", 0.0, 155600.0), ("Salary - _TC", "_Test Cost Center - _TC", 124800.0, 0.0), @@ -857,17 +857,18 @@ def run_test_for_loan_repayment_from_salary(self): payroll_entry.make_bank_entry() submit_bank_entry(payroll_entry.name) - bank_entry = frappe.db.sql( - """ - SELECT je.total_debit, je.total_credit - FROM `tabJournal Entry` je - INNER JOIN `tabJournal Entry Account` jea ON je.name = jea.parent - WHERE (je.voucher_type = 'Bank Entry' or je.voucher_type = 'Cash Entry') AND jea.reference_type = 'Payroll Entry' AND jea.reference_name = %s - LIMIT 1 - """, - payroll_entry.name, - as_dict=True, - ) + je = frappe.qb.DocType("Journal Entry") + jea = frappe.qb.DocType("Journal Entry Account") + bank_entry = ( + frappe.qb.from_(je) + .inner_join(jea) + .on(je.name == jea.parent) + .select(je.total_debit, je.total_credit) + .where((je.voucher_type == "Bank Entry") | (je.voucher_type == "Cash Entry")) + .where(jea.reference_type == "Payroll Entry") + .where(jea.reference_name == payroll_entry.name) + .limit(1) + ).run(as_dict=True) total_debit = bank_entry[0].get("total_debit", 0) total_credit = bank_entry[0].get("total_credit", 0) diff --git a/hrms/payroll/doctype/payroll_period/payroll_period.py b/hrms/payroll/doctype/payroll_period/payroll_period.py index 3e9bd20994..38ab4cf8f9 100644 --- a/hrms/payroll/doctype/payroll_period/payroll_period.py +++ b/hrms/payroll/doctype/payroll_period/payroll_period.py @@ -37,28 +37,24 @@ def clear_cache(self): return super().clear_cache() def validate_overlap(self): - query = """ - select name - from `tab{0}` - where name != %(name)s - and company = %(company)s and (start_date between %(start_date)s and %(end_date)s \ - or end_date between %(start_date)s and %(end_date)s \ - or (start_date < %(start_date)s and end_date > %(end_date)s)) - """ if not self.name: # hack! if name is null, it could cause problems with != self.name = "New " + self.doctype - overlap_doc = frappe.db.sql( - query.format(self.doctype), - { - "start_date": self.start_date, - "end_date": self.end_date, - "name": self.name, - "company": self.company, - }, - as_dict=1, - ) + DocType = frappe.qb.DocType(self.doctype) + overlap_doc = ( + frappe.qb.from_(DocType) + .select(DocType.name) + .where( + (DocType.name != self.name) + & (DocType.company == self.company) + & ( + DocType.start_date.between(self.start_date, self.end_date) + | DocType.end_date.between(self.start_date, self.end_date) + | ((DocType.start_date < self.start_date) & (DocType.end_date > self.end_date)) + ) + ) + ).run(as_dict=1) if overlap_doc: msg = ( @@ -74,17 +70,18 @@ def validate_overlap(self): def get_payroll_period_days(start_date, end_date, employee, company=None): if not company: company = frappe.db.get_value("Employee", employee, "company") - payroll_period = frappe.db.sql( - """ - select name, start_date, end_date - from `tabPayroll Period` - where - company=%(company)s - and %(start_date)s between start_date and end_date - and %(end_date)s between start_date and end_date - """, - {"company": company, "start_date": start_date, "end_date": end_date}, - ) + PayrollPeriod = frappe.qb.DocType("Payroll Period") + payroll_period = ( + frappe.qb.from_(PayrollPeriod) + .select(PayrollPeriod.name, PayrollPeriod.start_date, PayrollPeriod.end_date) + .where( + (PayrollPeriod.company == company) + & (PayrollPeriod.start_date <= start_date) + & (PayrollPeriod.end_date >= start_date) + & (PayrollPeriod.start_date <= end_date) + & (PayrollPeriod.end_date >= end_date) + ) + ).run() if len(payroll_period) > 0: actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 diff --git a/hrms/payroll/doctype/salary_slip/test_salary_slip.py b/hrms/payroll/doctype/salary_slip/test_salary_slip.py index 885b1ffac1..d59c24feb4 100644 --- a/hrms/payroll/doctype/salary_slip/test_salary_slip.py +++ b/hrms/payroll/doctype/salary_slip/test_salary_slip.py @@ -7,6 +7,7 @@ import frappe from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.model.document import Document +from frappe.query_builder.functions import Sum from frappe.utils import ( add_days, add_months, @@ -101,7 +102,8 @@ def test_employee_status_inactive(self): "Company", employee_doc.company, "default_holiday_list", "Salary Slip Test Holiday List" ) - frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""") + ss = frappe.qb.DocType("Salary Structure") + frappe.qb.from_(ss).delete().where(ss.name == "Test Inactive Employee Salary Slip").run() salary_structure = make_salary_structure( "Test Inactive Employee Salary Slip", "Monthly", @@ -950,7 +952,8 @@ def test_multi_currency_salary_slip(self): from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure applicant = make_employee("test_multi_currency_salary_slip@salary.com", company="_Test Company") - frappe.db.sql("""delete from `tabSalary Structure` where name='Test Multi Currency Salary Slip'""") + ss = frappe.qb.DocType("Salary Structure") + frappe.qb.from_(ss).delete().where(ss.name == "Test Multi Currency Salary Slip").run() salary_structure = make_salary_structure( "Test Multi Currency Salary Slip", "Monthly", @@ -990,7 +993,8 @@ def test_year_to_date_computation(self): ) # clear salary slip for this employee - frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'") + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee_name == "test_ytd@salary.com").run() create_salary_slips_for_payroll_period( applicant, salary_structure.name, payroll_period, deduct_random=False, num=6 @@ -1034,7 +1038,8 @@ def test_component_wise_year_to_date_computation(self): ) # clear salary slip for this employee - frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = '%s'" % employee_name) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee_name == employee_name).run() create_salary_slips_for_payroll_period( applicant, salary_structure.name, payroll_period, deduct_random=False, num=3 @@ -1092,7 +1097,8 @@ def test_tax_for_payroll_period(self): except AssertionError: print("\nSalary Slip - Annual tax calculation failed\n") raise - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee == employee).run() # create exemption declaration so the tax amount varies create_exemption_declaration(employee, payroll_period.name) @@ -1113,7 +1119,8 @@ def test_tax_for_payroll_period(self): # Submit proof for total 120000 data["proof"] = create_proof_submission(employee, payroll_period, 120000) - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee == employee).run() data["deducted_dates"] = create_salary_slips_for_payroll_period( employee, salary_structure.name, payroll_period ) @@ -1127,7 +1134,8 @@ def test_tax_for_payroll_period(self): raise # create additional salary of 150000 - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee == employee).run() data["additional-1"] = create_additional_salary(employee, payroll_period, 150000, "_Test Company") data["deducted_dates"] = create_salary_slips_for_payroll_period( employee, salary_structure.name, payroll_period @@ -1141,7 +1149,8 @@ def test_tax_for_payroll_period(self): except AssertionError: print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") raise - frappe.db.sql("""delete from `tabAdditional Salary` where employee=%s""", (employee)) + add_sal = frappe.qb.DocType("Additional Salary") + frappe.qb.from_(add_sal).delete().where(add_sal.employee == employee).run() # undelete fixture data frappe.db.rollback() @@ -1199,7 +1208,8 @@ def test_tax_for_recurring_additional_salary(self): "Salary Structure Assignment", ] for doc in delete_docs: - frappe.db.sql(f"DELETE FROM `tab{doc}` WHERE employee='{employee}'") + dt = frappe.qb.DocType(doc) + frappe.qb.from_(dt).delete().where(dt.employee == employee).run() from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure @@ -1222,7 +1232,8 @@ def test_tax_for_recurring_additional_salary(self): annual_tax = 23196.0 self.assertEqual(tax_paid, annual_tax) - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee == employee).run() # ------------------------------------ # Recurring additional salary @@ -1232,7 +1243,8 @@ def test_tax_for_recurring_additional_salary(self): employee, "Performance Bonus", 20000, start_date, end_date, "_Test Company" ) - frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee)) + ss = frappe.qb.DocType("Salary Slip") + frappe.qb.from_(ss).delete().where(ss.employee == employee).run() create_salary_slips_for_payroll_period( employee, salary_structure.name, payroll_period, deduct_random=False, num=4 @@ -1329,8 +1341,10 @@ def test_salary_slip_generation_against_opening_entries_in_ssa(self): from hrms.payroll.doctype.payroll_period.payroll_period import get_period_factor from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure - frappe.db.sql("DELETE FROM `tabPayroll Period` where company = '_Test Company'") - frappe.db.sql("DELETE FROM `tabIncome Tax Slab` where currency = 'INR'") + payroll_period_dt = frappe.qb.DocType("Payroll Period") + frappe.qb.from_(payroll_period_dt).delete().where(payroll_period_dt.company == "_Test Company").run() + income_tax_slab_dt = frappe.qb.DocType("Income Tax Slab") + frappe.qb.from_(income_tax_slab_dt).delete().where(income_tax_slab_dt.currency == "INR").run() payroll_period = create_payroll_period( name="_Test Payroll Period for Tax", @@ -1417,8 +1431,10 @@ def test_salary_slip_generation_against_opening_entries_in_ssa(self): if deduction.salary_component == "TDS": self.assertEqual(deduction.amount, 7691.0) - frappe.db.sql("DELETE FROM `tabPayroll Period` where company = '_Test Company'") - frappe.db.sql("DELETE FROM `tabIncome Tax Slab` where currency = 'INR'") + payroll_period_dt = frappe.qb.DocType("Payroll Period") + frappe.qb.from_(payroll_period_dt).delete().where(payroll_period_dt.company == "_Test Company").run() + income_tax_slab_dt = frappe.qb.DocType("Income Tax Slab") + frappe.qb.from_(income_tax_slab_dt).delete().where(income_tax_slab_dt.currency == "INR").run() def test_income_tax_breakup_fields(self): from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure @@ -1558,7 +1574,8 @@ def test_income_tax_unchanged_on_submit_with_exempted_additional_deduction(self) # Clean up any state left from prior runs of this test so the slip/payroll-entry # inserts below don't collide with the "already created for this period" check. for table in ("Salary Slip", "Additional Salary", "Salary Structure Assignment"): - frappe.db.sql(f"DELETE FROM `tab{table}` WHERE employee=%s", emp) + dt = frappe.qb.DocType(table) + frappe.qb.from_(dt).delete().where(dt.employee == emp).run() payroll_period = frappe.get_doc("Payroll Period", "_Test Payroll Period") @@ -1663,7 +1680,8 @@ def get_tds(doc): # Clean up any state left from prior runs of this test for emp2 too. for table in ("Salary Slip", "Additional Salary", "Salary Structure Assignment"): - frappe.db.sql(f"DELETE FROM `tab{table}` WHERE employee=%s", emp2) + dt = frappe.qb.DocType(table) + frappe.qb.from_(dt).delete().where(dt.employee == emp2).run() create_salary_structure_assignment( emp2, @@ -2540,12 +2558,15 @@ def make_employee_benefit_earning_components( def get_tax_paid_in_period(employee): - tax_paid_amount = frappe.db.sql( - """select sum(sd.amount) from `tabSalary Detail` - sd join `tabSalary Slip` ss where ss.name=sd.parent and ss.employee=%s - and ss.docstatus=1 and sd.salary_component='TDS'""", - (employee), - ) + sd = frappe.qb.DocType("Salary Detail") + ss = frappe.qb.DocType("Salary Slip") + tax_paid_amount = ( + frappe.qb.from_(sd) + .join(ss) + .on(ss.name == sd.parent) + .select(Sum(sd.amount)) + .where((ss.employee == employee) & (ss.docstatus == 1) & (sd.salary_component == "TDS")) + ).run() return tax_paid_amount[0][0] diff --git a/hrms/payroll/doctype/salary_structure/salary_structure.py b/hrms/payroll/doctype/salary_structure/salary_structure.py index d8715f9d92..ea52a2ef37 100644 --- a/hrms/payroll/doctype/salary_structure/salary_structure.py +++ b/hrms/payroll/doctype/salary_structure/salary_structure.py @@ -186,19 +186,13 @@ def reset_condition_and_formula_fields(self): self.db_update_all() def get_employees(self, **kwargs): - conditions, values = [], [] + Employee = frappe.qb.DocType("Employee") + query = frappe.qb.from_(Employee).select(Employee.name).where(Employee.status == "Active") for field, value in kwargs.items(): if value: - conditions.append(f"{field}=%s") - values.append(value) + query = query.where(Employee[field] == value) - condition_str = " and " + " and ".join(conditions) if conditions else "" - - # nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql - employees = frappe.db.sql_list( - f"select name from tabEmployee where status='Active' {condition_str}", - tuple(values), - ) + employees = query.run(pluck="name") return employees @@ -346,15 +340,19 @@ def create_salary_structure_assignment( def get_existing_assignments(employees, salary_structure, from_date): - # nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql - salary_structures_assignments = frappe.db.sql_list( - f""" - SELECT DISTINCT employee FROM `tabSalary Structure Assignment` - WHERE salary_structure=%s AND employee IN ({", ".join(["%s"] * len(employees))}) - AND from_date=%s AND company=%s AND docstatus=1 - """, - [salary_structure.name, *employees, from_date, salary_structure.company], - ) + ssa = frappe.qb.DocType("Salary Structure Assignment") + salary_structures_assignments = ( + frappe.qb.from_(ssa) + .select(ssa.employee) + .distinct() + .where( + (ssa.salary_structure == salary_structure.name) + & (ssa.employee.isin(employees)) + & (ssa.from_date == from_date) + & (ssa.company == salary_structure.company) + & (ssa.docstatus == 1) + ) + ).run(pluck="employee") if salary_structures_assignments: frappe.msgprint( _( diff --git a/hrms/payroll/doctype/salary_structure/test_salary_structure.py b/hrms/payroll/doctype/salary_structure/test_salary_structure.py index 4e68bd3998..798e47d0bd 100644 --- a/hrms/payroll/doctype/salary_structure/test_salary_structure.py +++ b/hrms/payroll/doctype/salary_structure/test_salary_structure.py @@ -92,10 +92,11 @@ def test_salary_structures_assignment(self): employee = "test_assign_structure@salary.com" employee_doc_name = make_employee(employee, company="_Test Company") # clear the already assigned structures - frappe.db.sql( - """delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s """, - ("test_assign_structure@salary.com", salary_structure.name), - ) + ssa = frappe.qb.DocType("Salary Structure Assignment") + frappe.qb.from_(ssa).delete().where( + (ssa.employee == "test_assign_structure@salary.com") + & (ssa.salary_structure == salary_structure.name) + ).run() # test structure_assignment salary_structure.assign_salary_structure( employee=employee_doc_name, from_date="2013-01-01", base=5000, variable=200 @@ -240,7 +241,8 @@ def create_salary_structure_assignment( currency = "INR" if not allow_duplicate and frappe.db.exists("Salary Structure Assignment", {"employee": employee}): - frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""", (employee)) + ssa = frappe.qb.DocType("Salary Structure Assignment") + frappe.qb.from_(ssa).delete().where(ssa.employee == employee).run() if not payroll_period: payroll_period = create_payroll_period(company="_Test Company") diff --git a/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py b/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py index f450d8d30f..4ffc82e231 100644 --- a/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py +++ b/hrms/payroll/report/income_tax_computation/test_income_tax_computation.py @@ -22,14 +22,15 @@ def setUp(self): self.create_records() def cleanup_records(self): - frappe.db.sql("delete from `tabEmployee Tax Exemption Declaration`") - frappe.db.sql("delete from `tabPayroll Period`") - frappe.db.sql("delete from `tabIncome Tax Slab`") - frappe.db.sql("delete from `tabSalary Component`") - frappe.db.sql("delete from `tabEmployee Benefit Application`") - frappe.db.sql("delete from `tabEmployee Benefit Claim`") - frappe.db.sql("delete from `tabEmployee` where company='_Test Company'") - frappe.db.sql("delete from `tabSalary Slip`") + frappe.qb.from_("Employee Tax Exemption Declaration").delete().run() + frappe.qb.from_("Payroll Period").delete().run() + frappe.qb.from_("Income Tax Slab").delete().run() + frappe.qb.from_("Salary Component").delete().run() + frappe.qb.from_("Employee Benefit Application").delete().run() + frappe.qb.from_("Employee Benefit Claim").delete().run() + employee = frappe.qb.DocType("Employee") + frappe.qb.from_(employee).delete().where(employee.company == "_Test Company").run() + frappe.qb.from_("Salary Slip").delete().run() def create_records(self): self.employee = make_employee( diff --git a/hrms/regional/india/utils.py b/hrms/regional/india/utils.py index 528fa88be7..5b4944b72b 100644 --- a/hrms/regional/india/utils.py +++ b/hrms/regional/india/utils.py @@ -163,21 +163,20 @@ def validate_house_rent_dates(doc): if date_diff(doc.rented_to_date, doc.rented_from_date) < 14: frappe.throw(_("House rented dates should be atleast 15 days apart")) - proofs = frappe.db.sql( - """ - select name - from `tabEmployee Tax Exemption Proof Submission` - where - docstatus=1 and employee=%(employee)s and payroll_period=%(payroll_period)s - and (rented_from_date between %(from_date)s and %(to_date)s or rented_to_date between %(from_date)s and %(to_date)s) - """, - { - "employee": doc.employee, - "payroll_period": doc.payroll_period, - "from_date": doc.rented_from_date, - "to_date": doc.rented_to_date, - }, - ) + proof = frappe.qb.DocType("Employee Tax Exemption Proof Submission") + proofs = ( + frappe.qb.from_(proof) + .select(proof.name) + .where( + (proof.docstatus == 1) + & (proof.employee == doc.employee) + & (proof.payroll_period == doc.payroll_period) + & ( + proof.rented_from_date.between(doc.rented_from_date, doc.rented_to_date) + | proof.rented_to_date.between(doc.rented_from_date, doc.rented_to_date) + ) + ) + ).run() if proofs: frappe.throw(_("House rent paid days overlapping with {0}").format(proofs[0][0])) From b3ce54401adece80ccd3d40174d40dc8f7b7e7bd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 22 Jul 2026 16:47:41 +0530 Subject: [PATCH 2/2] chore: remove dead code --- .../report/vehicle_expenses/vehicle_expenses.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/hrms/hr/report/vehicle_expenses/vehicle_expenses.py b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py index f064d3a167..412b2c6d89 100644 --- a/hrms/hr/report/vehicle_expenses/vehicle_expenses.py +++ b/hrms/hr/report/vehicle_expenses/vehicle_expenses.py @@ -102,23 +102,6 @@ def get_vehicle_log_data(filters): return data -def get_conditions(filters): - conditions = "" - - start_date, end_date = get_period_dates(filters) - values = {"start_date": start_date, "end_date": end_date} - - if filters.employee: - conditions += " and log.employee = %(employee)s" - values["employee"] = filters.employee - - if filters.vehicle: - conditions += " and vhcl.license_plate = %(vehicle)s" - values["vehicle"] = filters.vehicle - - return conditions, values - - def get_period_dates(filters): if filters.filter_based_on == "Fiscal Year" and filters.fiscal_year: fy = frappe.db.get_value(