diff --git a/hrms/api/roster.py b/hrms/api/roster.py index bd80a2ad6e..04e222db6e 100644 --- a/hrms/api/roster.py +++ b/hrms/api/roster.py @@ -273,27 +273,26 @@ def get_leaves(month_start: str, month_end: str, employee_filters: dict[str, str LeaveApplication = frappe.qb.DocType("Leave Application") Employee = frappe.qb.DocType("Employee") - frappe.has_permission("Leave Application", "read", throw=True) - - query = ( - frappe.qb.select( + query = frappe.qb.get_query( + "Leave Application", + fields=[ LeaveApplication.name.as_("leave"), LeaveApplication.employee, LeaveApplication.leave_type, LeaveApplication.from_date, LeaveApplication.to_date, - ) - .from_(LeaveApplication) - .left_join(Employee) - .on(LeaveApplication.employee == Employee.name) - .where( - (LeaveApplication.docstatus == 1) - & (LeaveApplication.status == "Approved") - & (LeaveApplication.from_date <= month_end) - & (LeaveApplication.to_date >= month_start) - ) + ], + filters={ + "docstatus": 1, + "status": "Approved", + "from_date": ("<=", month_end), + "to_date": (">=", month_start), + }, + ignore_permissions=False, ) + query = query.left_join(Employee).on(LeaveApplication.employee == Employee.name) + for filter in employee_filters: query = query.where(Employee[filter] == employee_filters[filter]) @@ -309,8 +308,9 @@ def get_shifts( ShiftType = frappe.qb.DocType("Shift Type") Employee = frappe.qb.DocType("Employee") - query = ( - frappe.qb.select( + query = frappe.qb.get_query( + "Shift Assignment", + fields=[ ShiftAssignment.name, ShiftAssignment.employee, ShiftAssignment.shift_type, @@ -322,17 +322,23 @@ def get_shifts( ShiftType.start_time, ShiftType.end_time, ShiftType.color, - ) - .from_(ShiftAssignment) - .left_join(ShiftType) + ], + filters={ + "docstatus": 1, + "start_date": ("<=", month_end), + }, + ignore_permissions=False, + ) + + # end_date is open-ended (None for shifts with no defined end) — must be + # expressed as an OR, which the filters dict can't represent cleanly. + query = query.where((ShiftAssignment.end_date >= month_start) | (ShiftAssignment.end_date.isnull())) + + query = ( + query.left_join(ShiftType) .on(ShiftAssignment.shift_type == ShiftType.name) .left_join(Employee) .on(ShiftAssignment.employee == Employee.name) - .where( - (ShiftAssignment.docstatus == 1) - & (ShiftAssignment.start_date <= month_end) - & ((ShiftAssignment.end_date >= month_start) | (ShiftAssignment.end_date.isnull())) - ) ) for filter in employee_filters: diff --git a/hrms/hr/doctype/goal/goal.py b/hrms/hr/doctype/goal/goal.py index cad78df7fb..61cc4a3c7d 100644 --- a/hrms/hr/doctype/goal/goal.py +++ b/hrms/hr/doctype/goal/goal.py @@ -163,48 +163,50 @@ def update_goal_progress_in_appraisal(self): @frappe.whitelist() def get_children(doctype: str, parent: str, is_root: bool = False, **filters) -> list[dict]: - Goal = frappe.qb.DocType(doctype) - - query = ( - frappe.qb.from_(Goal) - .select( - Goal.name.as_("value"), - Goal.goal_name.as_("title"), - Goal.is_group.as_("expandable"), - Goal.status, - Goal.employee, - Goal.employee_name, - Goal.appraisal_cycle, - Goal.progress, - Goal.kra, - ) - .where(Goal.status != "Archived") - ) + query_filters = [["status", "!=", "Archived"]] + query_or_filters = [] if filters.get("employee"): - query = query.where(Goal.employee == filters.get("employee")) + query_filters.append(["employee", "=", filters.get("employee")]) if filters.get("appraisal_cycle"): - query = query.where(Goal.appraisal_cycle == filters.get("appraisal_cycle")) + query_filters.append(["appraisal_cycle", "=", filters.get("appraisal_cycle")]) if filters.get("goal"): - query = query.where(Goal.parent_goal == filters.get("goal")) + query_filters.append(["parent_goal", "=", filters.get("goal")]) + elif parent and not is_root: - # via expand child - query = query.where(Goal.parent_goal == parent) + query_filters.append(["parent_goal", "=", parent]) + else: - ifnull = CustomFunction("IFNULL", ["value", "default"]) - query = query.where(ifnull(Goal.parent_goal, "") == "") + query_filters.append(["parent_goal", "in", ["", None]]) if filters.get("date_range"): - date_range = frappe.parse_json(filters.get("date_range")) - - query = query.where( - (Goal.start_date.between(date_range[0], date_range[1])) - & ((Goal.end_date.isnull()) | (Goal.end_date.between(date_range[0], date_range[1]))) - ) + from_date, to_date = frappe.parse_json(filters.get("date_range")) + query_filters.append(["start_date", "between", [from_date, to_date]]) + + # or_filters + query_or_filters.append(["end_date", "is", "not set"]) + query_or_filters.append(["end_date", "between", [from_date, to_date]]) + + goals = frappe.get_list( + doctype, + fields=[ + "name as value", + "goal_name as title", + "is_group as expandable", + "status", + "employee", + "employee_name", + "appraisal_cycle", + "progress", + "kra", + ], + filters=query_filters, + or_filters=query_or_filters if query_or_filters else None, + order_by="employee, kra", + ) - goals = query.orderby(Goal.employee, Goal.kra).run(as_dict=True) _update_goal_completion_status(goals) return goals diff --git a/hrms/locale/fa.po b/hrms/locale/fa.po index de17166981..a311d63b2a 100644 --- a/hrms/locale/fa.po +++ b/hrms/locale/fa.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: contact@frappe.io\n" "POT-Creation-Date: 2026-05-31 10:16+0000\n" -"PO-Revision-Date: 2026-06-01 14:00\n" +"PO-Revision-Date: 2026-06-07 15:12\n" "Last-Translator: contact@frappe.io\n" "Language-Team: Persian\n" "MIME-Version: 1.0\n" @@ -297,7 +297,7 @@ msgstr "" #: hrms/hr/doctype/job_requisition/job_requisition.js:101 msgid "A Job Requisition already exists for {0} requested by {1} for the same department.
Do you want to continue?" -msgstr "" +msgstr "یک درخواست شغل برای {0} که توسط {1} برای همان بخش درخواست شده است، از قبل وجود دارد.
آیا می‌خواهید ادامه دهید؟" #: hrms/controllers/employee_reminders.py:123 #: hrms/controllers/employee_reminders.py:216 @@ -5439,7 +5439,7 @@ msgstr "زمان‌های شیفت نامعتبر است" #: hrms/api/roster.py:31 msgid "Invalid employee filter: {0}" -msgstr "" +msgstr "فیلتر کارمند نامعتبر: {0}" #: hrms/hr/doctype/expense_claim/expense_claim.py:879 msgid "Invalid parameters provided. Please pass the required arguments." @@ -5447,7 +5447,7 @@ msgstr "پارامترهای نامعتبر ارائه شده است. لطفا #: hrms/api/roster.py:40 msgid "Invalid shift filter: {0}" -msgstr "" +msgstr "فیلتر شیفت نامعتبر: {0}" #. Option for the 'Status' (Select) field in DocType 'Employee Grievance' #: hrms/hr/doctype/employee_grievance/employee_grievance.json diff --git a/hrms/locale/main.pot b/hrms/locale/main.pot index 57041041f2..342b01302e 100644 --- a/hrms/locale/main.pot +++ b/hrms/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Frappe HR VERSION\n" "Report-Msgid-Bugs-To: contact@frappe.io\n" -"POT-Creation-Date: 2026-05-31 10:16+0000\n" -"PO-Revision-Date: 2026-05-31 10:16+0000\n" +"POT-Creation-Date: 2026-06-07 10:18+0000\n" +"PO-Revision-Date: 2026-06-07 10:18+0000\n" "Last-Translator: contact@frappe.io\n" "Language-Team: contact@frappe.io\n" "MIME-Version: 1.0\n" @@ -1383,8 +1383,8 @@ msgstr "" msgid "Attendance To Date" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:187 -#: hrms/hr/doctype/attendance_request/attendance_request.py:206 +#: hrms/hr/doctype/attendance_request/attendance_request.py:189 +#: hrms/hr/doctype/attendance_request/attendance_request.py:208 msgid "Attendance Updated" msgstr "" @@ -1436,11 +1436,11 @@ msgstr "" msgid "Attendance marked successfully." msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:225 +#: hrms/hr/doctype/attendance_request/attendance_request.py:227 msgid "Attendance not submitted for {0} as it is a Holiday." msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:234 +#: hrms/hr/doctype/attendance_request/attendance_request.py:236 msgid "Attendance not submitted for {0} as {1} is on leave." msgstr "" @@ -1535,11 +1535,11 @@ msgstr "" msgid "Avg Feedback Score" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:218 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:220 msgid "Avg Utilization" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:224 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:226 msgid "Avg Utilization (Billed Only)" msgstr "" @@ -1637,7 +1637,7 @@ msgstr "" msgid "Bill Amount" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:249 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:251 msgid "Billed Hours" msgstr "" @@ -1750,7 +1750,7 @@ msgstr "" msgid "CTC" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:61 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:62 msgid "CTC Missing for Employee" msgstr "" @@ -1877,15 +1877,15 @@ msgstr "" msgid "Cause of Grievance" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:192 +#: hrms/hr/doctype/attendance_request/attendance_request.py:194 msgid "Changed the Status for Other Half from {0} to {1} via Attendance Request as the status is Half Day" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:171 +#: hrms/hr/doctype/attendance_request/attendance_request.py:173 msgid "Changed the status from {0} to {1} and Status for Other Half to {2} via Attendance Request" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:175 +#: hrms/hr/doctype/attendance_request/attendance_request.py:177 msgid "Changed the status from {0} to {1} via Attendance Request" msgstr "" @@ -2035,7 +2035,7 @@ msgstr "" msgid "Closing Notes" msgstr "" -#: frontend/src/views/Profile.vue:177 +#: frontend/src/views/Profile.vue:178 msgid "Company Information" msgstr "" @@ -2326,6 +2326,10 @@ msgstr "" msgid "Current Openings" msgstr "" +#: frontend/src/views/ChangePassword.vue:22 +msgid "Current Password" +msgstr "" + #. Label of the current_payroll_period (Link) field in DocType 'Salary Slip' #: hrms/payroll/doctype/salary_slip/salary_slip.json msgid "Current Payroll Period" @@ -2635,7 +2639,7 @@ msgstr "" msgid "Disable {0} or {1} to proceed." msgstr "" -#: frontend/src/views/AppSettings.vue:40 +#: frontend/src/views/AppSettings.vue:65 msgid "Disabling Push Notifications..." msgstr "" @@ -2989,7 +2993,7 @@ msgstr "" #. Other Income' #. Label of the section_break_24 (Section Break) field in DocType 'Payroll #. Entry' -#: frontend/src/views/Profile.vue:165 +#: frontend/src/views/Profile.vue:166 #: hrms/hr/doctype/employee_onboarding/employee_onboarding.json #: hrms/hr/doctype/employee_performance_feedback/employee_performance_feedback.json #: hrms/hr/doctype/exit_interview/exit_interview.json @@ -3390,11 +3394,11 @@ msgstr "" msgid "Employee was marked Absent for other half due to missing Employee Checkins." msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:480 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:479 msgid "Employee {0} : {1}" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:133 +#: hrms/hr/doctype/attendance_request/attendance_request.py:135 msgid "Employee {0} already has an Attendance Request {1} that overlaps with this period" msgstr "" @@ -3535,7 +3539,7 @@ msgstr "" msgid "Enable Late Entry Marking" msgstr "" -#: frontend/src/views/AppSettings.vue:25 +#: frontend/src/views/AppSettings.vue:50 msgid "Enable Push Notifications" msgstr "" @@ -3557,7 +3561,7 @@ msgstr "" msgid "Enabled only for Employee Benefit components from Salary Structure Assignment" msgstr "" -#: frontend/src/views/AppSettings.vue:40 +#: frontend/src/views/AppSettings.vue:65 msgid "Enabling Push Notifications..." msgstr "" @@ -3631,6 +3635,10 @@ msgstr "" msgid "Enter yearly benefit amounts" msgstr "" +#: frontend/src/views/ForgotPassword.vue:22 +msgid "Enter your email address and we'll send you a link to reset your password." +msgstr "" + #: frontend/src/components/FormField.vue:40 #: frontend/src/components/FormField.vue:52 msgid "Enter {0}" @@ -4071,10 +4079,14 @@ msgstr "" msgid "Failed to delete defaults for country {0}." msgstr "" -#: hrms/api/__init__.py:784 +#: hrms/api/__init__.py:796 msgid "Failed to download PDF: {0}" msgstr "" +#: frontend/src/views/ForgotPassword.vue:83 +msgid "Failed to send reset link" +msgstr "" + #: hrms/hr/doctype/interview/interview.py:146 msgid "Failed to send the Interview Reschedule notification. Please configure your email account." msgstr "" @@ -4087,6 +4099,10 @@ msgstr "" msgid "Failed to submit some leave policy assignments:" msgstr "" +#: frontend/src/views/ChangePassword.vue:94 +msgid "Failed to update password" +msgstr "" + #: hrms/hr/doctype/interview/interview.py:239 msgid "Failed to update the Job Applicant status" msgstr "" @@ -4341,7 +4357,7 @@ msgstr "" msgid "Formula" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:227 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:229 msgid "Formula/Amount" msgstr "" @@ -4539,7 +4555,7 @@ msgstr "" msgid "Go to Login" msgstr "" -#: frontend/src/views/Login.vue:72 +#: frontend/src/views/Login.vue:29 msgid "Go to Reset Password page" msgstr "" @@ -4877,15 +4893,15 @@ msgstr "" msgid "Hourly Rate" msgstr "" -#: hrms/regional/india/utils.py:184 +#: hrms/regional/india/utils.py:183 msgid "House rent paid days overlapping with {0}" msgstr "" -#: hrms/regional/india/utils.py:162 +#: hrms/regional/india/utils.py:161 msgid "House rented dates required for exemption calculation" msgstr "" -#: hrms/regional/india/utils.py:165 +#: hrms/regional/india/utils.py:164 msgid "House rented dates should be atleast 15 days apart" msgstr "" @@ -6394,7 +6410,7 @@ msgstr "" msgid "Lodging Required" msgstr "" -#: frontend/src/views/Profile.vue:107 +#: frontend/src/views/Profile.vue:106 msgid "Log Out" msgstr "" @@ -6412,7 +6428,7 @@ msgstr "" msgid "Login Failed" msgstr "" -#: frontend/src/views/Login.vue:8 +#: frontend/src/views/Login.vue:38 msgid "Login to Frappe HR" msgstr "" @@ -6449,6 +6465,10 @@ msgstr "" msgid "Mandatory fields required for this action:" msgstr "" +#: hrms/payroll/doctype/payroll_entry/payroll_entry.js:213 +msgid "Mandatory fields required in {0}" +msgstr "" + #. Option for the 'KRA Evaluation Method' (Select) field in DocType 'Appraisal #. Cycle' #: hrms/hr/doctype/appraisal_cycle/appraisal_cycle.json @@ -6717,7 +6737,7 @@ msgstr "" msgid "Missing Tax Slab" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:311 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:313 msgid "Missing value for filters" msgstr "" @@ -6865,6 +6885,10 @@ msgstr "" msgid "New Leaves Allocated (In Days)" msgstr "" +#: frontend/src/views/ChangePassword.vue:112 +msgid "New passwords do not match" +msgstr "" + #. Description of the 'Create Shifts After' (Date) field in DocType 'Shift #. Schedule Assignment' #: hrms/hr/doctype/shift_schedule_assignment/shift_schedule_assignment.json @@ -7040,7 +7064,7 @@ msgstr "" msgid "No leaves have been allocated." msgstr "" -#: frontend/src/views/Login.vue:53 +#: frontend/src/views/Login.vue:91 msgid "No login methods are available. Please contact your administrator." msgstr "" @@ -7096,7 +7120,7 @@ msgstr "" msgid "Non Taxable Earnings" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:250 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:252 msgid "Non-Billed Hours" msgstr "" @@ -7183,11 +7207,11 @@ msgstr "" msgid "Number of leaves eligible for encashment based on leave type settings" msgstr "" -#: frontend/src/views/Login.vue:88 +#: frontend/src/views/Login.vue:105 msgid "OTP Code" msgstr "" -#: frontend/src/views/Login.vue:79 +#: frontend/src/views/Login.vue:96 msgid "OTP Verification" msgstr "" @@ -7379,7 +7403,7 @@ msgstr "" msgid "Overall Average Rating" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:138 +#: hrms/hr/doctype/attendance_request/attendance_request.py:140 msgid "Overlapping Attendance Request" msgstr "" @@ -7445,11 +7469,11 @@ msgstr "" msgid "Overtime Slip" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:481 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:480 msgid "Overtime Slip Creation Error for {0}" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:492 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:491 msgid "Overtime Slip Creation Failed" msgstr "" @@ -7458,19 +7482,19 @@ msgstr "" msgid "Overtime Slip Step" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:515 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:514 msgid "Overtime Slip Submission Error for {0}" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:526 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:525 msgid "Overtime Slip Submission Failed" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:521 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:520 msgid "Overtime Slip Submitted" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:485 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:484 msgid "Overtime Slip created for {0} employee(s)" msgstr "" @@ -7486,11 +7510,11 @@ msgstr "" msgid "Overtime Slip:{0} has been created between {1} and {2}" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:487 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:486 msgid "Overtime Slips Created" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:519 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:518 msgid "Overtime Slips submitted for {0} employee(s)" msgstr "" @@ -7591,6 +7615,10 @@ msgstr "" msgid "Password policy for Salary Slips is not set" msgstr "" +#: frontend/src/views/ForgotPassword.vue:74 +msgid "Password reset link has been sent to your email." +msgstr "" + #. Label of the pay_details_tab (Tab Break) field in DocType 'Job Opening' #. Label of the pay_details_section (Section Break) field in DocType 'Job #. Opening Template' @@ -7869,7 +7897,7 @@ msgstr "" msgid "Percent Deduction" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:250 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:252 msgid "Percent of CTC (%)" msgstr "" @@ -7927,10 +7955,22 @@ msgstr "" msgid "Please enable default incoming account before creating Daily Work Summary Group" msgstr "" +#: frontend/src/views/ForgotPassword.vue:109 +msgid "Please enter a valid email address" +msgstr "" + #: hrms/hr/doctype/staffing_plan/staffing_plan.js:97 msgid "Please enter the designation" msgstr "" +#: frontend/src/views/ForgotPassword.vue:104 +msgid "Please enter your email address" +msgstr "" + +#: frontend/src/views/ChangePassword.vue:107 +msgid "Please fill all fields" +msgstr "" + #: hrms/hr/doctype/overtime_slip/overtime_slip.js:11 msgid "Please fill in Employee, Posting Date, and Company before fetching overtime details." msgstr "" @@ -8083,7 +8123,7 @@ msgstr "" msgid "Please set account in Salary Component {0}" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:53 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:54 msgid "Please set cost to company(CTC) for employee {0} in the {1}" msgstr "" @@ -8140,7 +8180,7 @@ msgstr "" msgid "Please set {0} for the Employee: {1}" msgstr "" -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:310 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.py:312 msgid "Please set {0} to get CTC report" msgstr "" @@ -8385,15 +8425,15 @@ msgstr "" msgid "Purpose of Travel" msgstr "" -#: frontend/src/views/AppSettings.vue:128 +#: frontend/src/views/AppSettings.vue:152 msgid "Push Notification permission denied" msgstr "" -#: frontend/src/views/AppSettings.vue:96 +#: frontend/src/views/AppSettings.vue:121 msgid "Push notifications disabled" msgstr "" -#: frontend/src/views/AppSettings.vue:80 +#: frontend/src/views/AppSettings.vue:106 msgid "Push notifications have been disabled on your site" msgstr "" @@ -8991,7 +9031,7 @@ msgstr "" msgid "Salary Expectation" msgstr "" -#: frontend/src/views/Profile.vue:200 +#: frontend/src/views/Profile.vue:201 msgid "Salary Information" msgstr "" @@ -9165,7 +9205,7 @@ msgstr "" #. Label of a Workspace Sidebar Item #: hrms/payroll/doctype/income_tax_slab/income_tax_slab.js:8 #: hrms/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json -#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.js:34 +#: hrms/payroll/report/employee_ctc_break_up/employee_ctc_break_up.js:51 #: hrms/payroll/workspace/payroll/payroll.json #: hrms/workspace_sidebar/payroll.json msgid "Salary Structure Assignment" @@ -9479,6 +9519,10 @@ msgstr "" msgid "Send Leave Notification" msgstr "" +#: frontend/src/views/ForgotPassword.vue:45 +msgid "Send Reset Link" +msgstr "" + #. Label of the sender_copy (Link) field in DocType 'Payroll Settings' #: hrms/payroll/doctype/payroll_settings/payroll_settings.json msgid "Sender Copy" @@ -10726,7 +10770,7 @@ msgstr "" msgid "Total Net Pay" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:228 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:230 msgid "Total Non-Billed Hours" msgstr "" @@ -11132,7 +11176,7 @@ msgstr "" msgid "Unsubmitted Appraisals" msgstr "" -#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:251 +#: hrms/hr/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py:253 msgid "Untracked Hours" msgstr "" @@ -11185,11 +11229,11 @@ msgstr "" msgid "Update Tax" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:198 +#: hrms/hr/doctype/attendance_request/attendance_request.py:200 msgid "Updated Status for Other Half from {0} to {1} for date {2} in the attendance record {3}" msgstr "" -#: hrms/hr/doctype/attendance_request/attendance_request.py:181 +#: hrms/hr/doctype/attendance_request/attendance_request.py:183 msgid "Updated status from {0} to {1} for date {2} in the attendance record {3}" msgstr "" @@ -11568,7 +11612,7 @@ msgstr "" msgid "You can only submit Leave Encashment for a valid encashment amount" msgstr "" -#: hrms/api/__init__.py:741 +#: hrms/api/__init__.py:751 msgid "You can only upload JPG, PNG, PDF, TXT or Microsoft documents." msgstr "" @@ -11625,7 +11669,11 @@ msgstr "" msgid "Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}" msgstr "" -#: frontend/src/views/Login.vue:63 +#: frontend/src/views/ChangePassword.vue:85 +msgid "Your password has been updated." +msgstr "" + +#: frontend/src/views/Login.vue:22 msgid "Your password has expired. Please reset your password to continue" msgstr "" @@ -11661,7 +11709,7 @@ msgstr "" msgid "here" msgstr "" -#: frontend/src/views/Login.vue:16 +#: frontend/src/views/Login.vue:46 msgid "johndoe@mail.com" msgstr "" @@ -11724,7 +11772,7 @@ msgstr "" msgid "{0} & {1} more" msgstr "" -#: hrms/hr/doctype/overtime_slip/overtime_slip.py:513 +#: hrms/hr/doctype/overtime_slip/overtime_slip.py:512 msgid "{0} : {1}" msgstr "" diff --git a/hrms/locale/sv.po b/hrms/locale/sv.po index 75768d57f3..f261c72e85 100644 --- a/hrms/locale/sv.po +++ b/hrms/locale/sv.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: contact@frappe.io\n" "POT-Creation-Date: 2026-05-31 10:16+0000\n" -"PO-Revision-Date: 2026-06-01 14:00\n" +"PO-Revision-Date: 2026-06-05 14:58\n" "Last-Translator: contact@frappe.io\n" "Language-Team: Swedish\n" "MIME-Version: 1.0\n" @@ -2267,7 +2267,7 @@ msgstr "Skapa Utvärderingar" #: hrms/payroll/doctype/payroll_entry/payroll_entry.js:458 msgid "Creating Payment Entries......" -msgstr "......Skapar Betalning Poster......" +msgstr "Skapar Kontering Poster......" #: hrms/payroll/doctype/payroll_entry/payroll_entry.py:1611 msgid "Creating Salary Slips..." diff --git a/hrms/payroll/doctype/employee_incentive/employee_incentive.js b/hrms/payroll/doctype/employee_incentive/employee_incentive.js index 0f73d99b55..49de854752 100644 --- a/hrms/payroll/doctype/employee_incentive/employee_incentive.js +++ b/hrms/payroll/doctype/employee_incentive/employee_incentive.js @@ -47,7 +47,7 @@ frappe.ui.form.on("Employee Incentive", { if (!frm.doc.company) return; frm.set_query("salary_component", function () { return { - filters: { type: "earning", company: frm.doc.company }, + filters: { type: "earning" }, }; }); }, diff --git a/hrms/payroll/doctype/salary_slip/salary_slip.py b/hrms/payroll/doctype/salary_slip/salary_slip.py index 1fe9d09e02..4a7fd24a46 100644 --- a/hrms/payroll/doctype/salary_slip/salary_slip.py +++ b/hrms/payroll/doctype/salary_slip/salary_slip.py @@ -2543,6 +2543,7 @@ def get_salary_component_data(component): "is_flexible_benefit", "variable_based_on_taxable_salary", "accrual_component", + "exempted_from_income_tax", ), as_dict=1, cache=True, diff --git a/hrms/payroll/doctype/salary_slip/test_salary_slip.py b/hrms/payroll/doctype/salary_slip/test_salary_slip.py index 2342dfce84..bb5dd0f53f 100644 --- a/hrms/payroll/doctype/salary_slip/test_salary_slip.py +++ b/hrms/payroll/doctype/salary_slip/test_salary_slip.py @@ -1533,6 +1533,178 @@ def test_income_tax_breakup_when_tax_added_via_additional_salary(self): self.assertEqual(flt(salary_slip.future_income_tax_deductions, 2), 124843.25) # as 136843.25 - 12000 self.assertEqual(flt(salary_slip.total_income_tax, 2), 136843.25) + def test_income_tax_unchanged_on_submit_with_exempted_additional_deduction(self): + from hrms.payroll.doctype.payroll_entry.test_payroll_entry import make_payroll_entry + from hrms.payroll.doctype.salary_structure.test_salary_structure import ( + create_salary_structure_assignment, + make_salary_structure, + ) + from hrms.tests.test_utils import create_department + + frappe.db.delete("Income Tax Slab", {"currency": "INR"}) + # Two dedicated departments so the Payroll Entry below can filter to only emp2. + emp1_department = create_department("Issue 70232 Direct Submission Department") + emp2_department = create_department("Issue 70232 Payroll Entry Department") + + emp = make_employee( + "test_employee_ss_exempted_addnl_deduction@salary.com", + company="_Test Company", + department=emp1_department, + date_of_joining="2021-01-01", + ) + # Force the department even if the employee existed from a prior test run. + frappe.db.set_value("Employee", emp, "department", emp1_department) + + # 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) + + payroll_period = frappe.get_doc("Payroll Period", "_Test Payroll Period") + + create_tax_slab( + payroll_period, + effective_date=payroll_period.start_date, + allow_tax_exemption=True, + currency="INR", + ) + + salary_structure_name = "Test Salary Structure - Exempted Additional Deduction" + if frappe.db.exists("Salary Structure", salary_structure_name): + frappe.db.delete("Salary Structure", salary_structure_name) + + salary_structure_doc = make_salary_structure( + salary_structure_name, + "Monthly", + company="_Test Company", + employee=emp, + from_date=payroll_period.start_date, + payroll_period=payroll_period, + test_tax=True, + base=200000, # high base so TDS is non-zero under the default slabs + ) + + # Deduction component marked as exempted from income tax (e.g., Car Lease Deduction). + exempted_component = "Car Lease Deduction" + make_salary_component( + [ + { + "salary_component": exempted_component, + "abbr": "CLD", + "type": "Deduction", + "exempted_from_income_tax": 1, + "depends_on_payment_days": 0, + } + ], + False, + company_list=["_Test Company"], + ) + + def create_exempted_additional_salary(employee): + frappe.get_doc( + { + "doctype": "Additional Salary", + "employee": employee, + "company": "_Test Company", + "salary_component": exempted_component, + "amount": 25000, + "type": "Deduction", + "payroll_date": payroll_period.start_date, + "currency": "INR", + } + ).submit() + + create_exempted_additional_salary(emp) + + salary_slip = make_salary_slip( + salary_structure_doc.name, employee=emp, posting_date=payroll_period.start_date + ) + + def get_tds(doc): + for d in doc.deductions: + if d.salary_component == "TDS": + return flt(d.amount, 2) + return 0.0 + + salary_slip.submit() + salary_slip.reload() + + tds_after_save = get_tds(salary_slip) + current_month_tax_after = flt(salary_slip.current_month_income_tax, 2) + total_income_tax_after = flt(salary_slip.total_income_tax, 2) + annual_taxable_after = flt(salary_slip.annual_taxable_amount, 2) + deductions_before_tax_after = flt(salary_slip.deductions_before_tax_calculation, 2) + + # Reproduce the same scenario via Payroll Entry for a second employee + # and verify both submitted slips produce identical income tax values. + default_payable = frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") + if not default_payable: + create_account( + account_name="_Test Payroll Payable", + company="_Test Company", + parent_account="Current Liabilities - _TC", + account_type="Payable", + ) + default_payable = "_Test Payroll Payable - _TC" + frappe.db.set_value( + "Company", "_Test Company", "default_payroll_payable_account", default_payable + ) + if frappe.db.get_value("Account", default_payable, "account_type") != "Payable": + frappe.db.set_value("Account", default_payable, "account_type", "Payable") + + emp2 = make_employee( + "test_employee2_ss_exempted_addnl_deduction@salary.com", + company="_Test Company", + department=emp2_department, + date_of_joining="2021-01-01", + ) + # Force the department even if the employee existed from a prior test run. + frappe.db.set_value("Employee", emp2, "department", emp2_department) + + # 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) + + create_salary_structure_assignment( + emp2, + salary_structure_doc.name, + from_date=payroll_period.start_date, + company="_Test Company", + currency="INR", + payroll_period=payroll_period, + base=200000, + ) + create_exempted_additional_salary(emp2) + + payroll_entry = make_payroll_entry( + start_date=payroll_period.start_date, + end_date=get_last_day(payroll_period.start_date), + payable_account=default_payable, + currency="INR", + company="_Test Company", + department=emp2_department, + cost_center="Main - _TC", + ) + + emp2_slip_name = frappe.db.get_value( + "Salary Slip", {"payroll_entry": payroll_entry.name, "employee": emp2} + ) + self.assertTrue( + emp2_slip_name, + "Payroll Entry did not create a Salary Slip for the second employee", + ) + emp2_slip = frappe.get_doc("Salary Slip", emp2_slip_name) + + self.assertEqual( + get_tds(emp2_slip), + tds_after_save, + "TDS on Payroll-Entry-submitted slip does not match the directly-submitted slip", + ) + self.assertEqual(flt(emp2_slip.current_month_income_tax, 2), current_month_tax_after) + self.assertEqual(flt(emp2_slip.total_income_tax, 2), total_income_tax_after) + self.assertEqual(flt(emp2_slip.annual_taxable_amount, 2), annual_taxable_after) + self.assertEqual(flt(emp2_slip.deductions_before_tax_calculation, 2), deductions_before_tax_after) + def test_consistent_future_earnings_irrespective_of_payment_days(self): """ For CTC calculation, verifies that future non taxable earnings remain
{0}{1}