def send_advance_holiday_reminders(frequency): """Send Holiday Reminders in Advance to Employees `frequency` (str): 'Weekly' or 'Monthly' """ if frequency == "Weekly": start_date = getdate() end_date = add_days(getdate(), 7) elif frequency == "Monthly": # Sent on 1st of every month start_date = getdate() end_date = add_months(getdate(), 1) else: return employees = frappe.db.get_all("Employee", filters={"status": "Active"}, pluck="name") for employee in employees: holidays = get_holidays_for_employee(employee, start_date, end_date, only_non_weekly=True, raise_exception=False) send_holidays_reminder_in_advance(employee, holidays)
def get_payroll_period_days(start_date, end_date, employee): 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 }) if len(payroll_period) > 0: actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 working_days = actual_no_of_days if not cint( frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")): holidays = get_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2])) working_days -= len(holidays) return payroll_period[0][0], working_days, actual_no_of_days return False, False, False
def validate_holidays(self): holidays = get_holidays_for_employee(self.employee, self.work_from_date, self.work_end_date) if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1: frappe.throw( _("Compensatory leave request days not in valid holidays"))
def get_holidays_for_employees(employees, from_date, to_date): holidays = {} for employee in employees: holiday_list = get_holiday_list_for_employee(employee) holiday = get_holidays_for_employee(employee, getdate(from_date), getdate(to_date)) if holiday_list not in holidays: holidays[holiday_list] = holiday return holidays
def validate_holidays(self): holidays = get_holidays_for_employee(self.employee, self.work_from_date, self.work_end_date) if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1: if date_diff(self.work_end_date, self.work_from_date): msg = _("The days between {0} to {1} are not valid holidays." ).format(frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date))) else: msg = _("{0} is not a holiday.").format( frappe.bold(format_date(self.work_from_date))) frappe.throw(msg)
def test_reminder_not_sent_if_no_holdays(self): setup_hr_settings("Monthly") # reminder not sent if there are no holidays holidays = get_holidays_for_employee( self.test_employee_2.get("name"), getdate(), getdate() + timedelta(days=3), only_non_weekly=True, 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) self.assertEqual(len(email_queue), 0)
def get_max_benefits_remaining(employee, on_date, payroll_period): max_benefits = get_max_benefits(employee, on_date) if max_benefits and max_benefits > 0: have_depends_on_payment_days = False per_day_amount_total = 0 payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0] payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) # Get all salary slip flexi amount in the payroll period prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given( employee, payroll_period_obj) if prev_sal_slip_flexi_total > 0: # Check salary structure hold depends_on_payment_days component # If yes then find the amount per day of each component and find the sum sal_struct_name = get_assigned_salary_structure(employee, on_date) if sal_struct_name: sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) for sal_struct_row in sal_struct.get("earnings"): salary_component = frappe.get_doc( "Salary Component", sal_struct_row.salary_component) if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1: have_depends_on_payment_days = True benefit_amount = get_benefit_pro_rata_ratio_amount( sal_struct, salary_component.max_benefit_amount) amount_per_day = benefit_amount / payroll_period_days per_day_amount_total += amount_per_day # Then the sum multiply with the no of lwp in that period # Include that amount to the prev_sal_slip_flexi_total to get the actual if have_depends_on_payment_days and per_day_amount_total > 0: holidays = get_holidays_for_employee( employee, payroll_period_obj.start_date, on_date) working_days = date_diff(on_date, payroll_period_obj.start_date) + 1 leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days) leave_days_amount = leave_days * per_day_amount_total prev_sal_slip_flexi_total += leave_days_amount return max_benefits - prev_sal_slip_flexi_total return max_benefits
def test_send_holidays_reminder_in_advance(self): setup_hr_settings("Weekly") holidays = get_holidays_for_employee( self.test_employee.get("name"), getdate(), getdate() + timedelta(days=3), only_non_weekly=True, raise_exception=False, ) send_holidays_reminder_in_advance(self.test_employee.get("name"), holidays) email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) self.assertEqual(len(email_queue), 1) self.assertTrue("Holidays this Week." in email_queue[0].message)
def test_send_holidays_reminder_in_advance(self): from erpnext.hr.doctype.employee.employee_reminders import send_holidays_reminder_in_advance from erpnext.hr.utils import get_holidays_for_employee # Get HR settings and enable advance holiday reminders hr_settings = frappe.get_doc("HR Settings", "HR Settings") hr_settings.send_holiday_reminders = 1 set_proceed_with_frequency_change() hr_settings.frequency = 'Weekly' hr_settings.save() holidays = get_holidays_for_employee(self.test_employee.get('name'), getdate(), getdate() + timedelta(days=3), only_non_weekly=True, raise_exception=False) send_holidays_reminder_in_advance(self.test_employee.get('name'), holidays) email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) self.assertEqual(len(email_queue), 1)
def get_max_benefits_remaining(employee, on_date, payroll_period): max_benefits = get_max_benefits(employee, on_date) if max_benefits and max_benefits > 0: have_depends_on_payment_days = False per_day_amount_total = 0 payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0] payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) # Get all salary slip flexi amount in the payroll period prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj) if prev_sal_slip_flexi_total > 0: # Check salary structure hold depends_on_payment_days component # If yes then find the amount per day of each component and find the sum sal_struct_name = get_assigned_salary_structure(employee, on_date) if sal_struct_name: sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) for sal_struct_row in sal_struct.get("earnings"): salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component) if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1: have_depends_on_payment_days = True benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount) amount_per_day = benefit_amount / payroll_period_days per_day_amount_total += amount_per_day # Then the sum multiply with the no of lwp in that period # Include that amount to the prev_sal_slip_flexi_total to get the actual if have_depends_on_payment_days and per_day_amount_total > 0: holidays = get_holidays_for_employee(employee, payroll_period_obj.start_date, on_date) working_days = date_diff(on_date, payroll_period_obj.start_date) + 1 leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days) leave_days_amount = leave_days * per_day_amount_total prev_sal_slip_flexi_total += leave_days_amount return max_benefits - prev_sal_slip_flexi_total return max_benefits