Skip to content

feat: Credit present on holiday against absence #3425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions hrms/payroll/doctype/payroll_settings/payroll_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@
},
{
"default": "0",
"depends_on": "include_holidays_in_total_working_days",
"description": "If enabled, deducts payment days for absent attendance on holidays. By default, holidays are considered as paid",
"description": "Depends on whether holidays are included in total working days. If holidays are included: Deducts pay for 'Absent' attendance on a holiday. If holidays are not included: Credits 'Present' attendance on a holiday against an absent day.",
"fieldname": "consider_marked_attendance_on_holidays",
"fieldtype": "Check",
"label": "Consider Marked Attendance on Holidays"
Expand Down Expand Up @@ -198,4 +197,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}
46 changes: 29 additions & 17 deletions hrms/payroll/doctype/salary_slip/salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,16 @@ def get_working_days_details(self, lwp=None, for_preview=0):
as_dict=1,
)

consider_marked_attendance_on_holidays = (
consider_absent_on_holidays = (
payroll_settings.include_holidays_in_total_working_days
and payroll_settings.consider_marked_attendance_on_holidays
)

credit_present_on_holidays = (
not payroll_settings.include_holidays_in_total_working_days
and payroll_settings.consider_marked_attendance_on_holidays
)

daily_wages_fraction_for_half_day = flt(payroll_settings.daily_wages_fraction_for_half_day) or 0.5

working_days = date_diff(self.end_date, self.start_date) + 1
Expand All @@ -456,7 +461,7 @@ def get_working_days_details(self, lwp=None, for_preview=0):

if payroll_settings.payroll_based_on == "Attendance":
actual_lwp, absent = self.calculate_lwp_ppl_and_absent_days_based_on_attendance(
holidays, daily_wages_fraction_for_half_day, consider_marked_attendance_on_holidays
holidays, daily_wages_fraction_for_half_day, consider_absent_on_holidays, credit_present_on_holidays
)
self.absent_days = absent
else:
Expand Down Expand Up @@ -491,11 +496,11 @@ def get_working_days_details(self, lwp=None, for_preview=0):
unmarked_days = self.get_unmarked_days(
payroll_settings.include_holidays_in_total_working_days, holidays
)
self.absent_days += unmarked_days # will be treated as absent
self.absent_days += unmarked_days
self.payment_days -= unmarked_days
half_absent_days = self.get_half_absent_days(
consider_marked_attendance_on_holidays,
holidays,
consider_absent_on_holidays,
holidays
)
self.absent_days += half_absent_days * daily_wages_fraction_for_half_day
self.payment_days -= half_absent_days * daily_wages_fraction_for_half_day
Expand All @@ -517,7 +522,7 @@ def get_unmarked_days(

return unmarked_days

def get_half_absent_days(self, consider_marked_attendance_on_holidays, holidays):
def get_half_absent_days(self, consider_absent_on_holidays, holidays):
"""Calculates the number of half absent days for an employee within a date range"""
Attendance = frappe.qb.DocType("Attendance")
query = (
Expand All @@ -531,7 +536,7 @@ def get_half_absent_days(self, consider_marked_attendance_on_holidays, holidays)
& (Attendance.half_day_status == "Absent")
)
)
if (not consider_marked_attendance_on_holidays) and holidays:
if (not consider_absent_on_holidays) and holidays:
query = query.where(Attendance.attendance_date.notin(holidays))
return query.run()[0][0]

Expand Down Expand Up @@ -688,8 +693,7 @@ def get_employee_attendance(self, start_date, end_date):
attendance.half_day_status,
)
.where(
(attendance.status.isin(["Absent", "Half Day", "On Leave"]))
& (attendance.employee == self.employee)
(attendance.employee == self.employee)
& (attendance.docstatus == 1)
& (attendance.attendance_date.between(start_date, end_date))
)
Expand All @@ -698,7 +702,7 @@ def get_employee_attendance(self, start_date, end_date):
return attendance_details

def calculate_lwp_ppl_and_absent_days_based_on_attendance(
self, holidays, daily_wages_fraction_for_half_day, consider_marked_attendance_on_holidays
self, holidays, daily_wages_fraction_for_half_day, consider_absent_on_holidays, credit_present_on_holidays
):
lwp = 0
absent = 0
Expand All @@ -716,15 +720,23 @@ def calculate_lwp_ppl_and_absent_days_based_on_attendance(
):
continue

# skip counting absent on holidays
if not consider_marked_attendance_on_holidays and getdate(d.attendance_date) in holidays:
if d.status in ["Absent", "Half Day"] or (
d.leave_type
and d.leave_type in leave_type_map.keys()
and not leave_type_map[d.leave_type]["include_holiday"]
):
# Check if attendance date is a holiday
if getdate(d.attendance_date) in holidays:
if credit_present_on_holidays:
if d.status == "Present":
absent -= 1
elif d.status == "Half Day":
absent -= (1 - daily_wages_fraction_for_half_day)
continue

elif not consider_absent_on_holidays:
if d.status in ["Absent", "Half Day"] or (
d.leave_type
and d.leave_type in leave_type_map.keys()
and not leave_type_map[d.leave_type]["include_holiday"]
):
continue

if d.leave_type:
fraction_of_daily_salary_per_leave = leave_type_map[d.leave_type][
"fraction_of_daily_salary_per_leave"
Expand Down
Loading