From 85b18e23d786001bbc4865ccfc9135914bf1b241 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 10 Jun 2022 16:26:07 +0530 Subject: [PATCH] fix: remove employee reminders, move to HRMS app - change `erpnext.hr.EmployeeController` to `erpnext.setup.EmployeeController` --- erpnext/hooks.py | 8 +- erpnext/setup/doctype/employee/employee.js | 6 +- .../doctype/employee/employee_reminders.py | 274 ------------------ .../employee/test_employee_reminders.py | 268 ----------------- 4 files changed, 6 insertions(+), 550 deletions(-) delete mode 100644 erpnext/setup/doctype/employee/employee_reminders.py delete mode 100644 erpnext/setup/doctype/employee/test_employee_reminders.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index bec9fefc42..5fca061f6b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -255,7 +255,9 @@ sounds = [ {"name": "call-disconnect", "src": "/assets/erpnext/sounds/call-disconnect.mp3", "volume": 0.2}, ] -has_upload_permission = {"Employee": "employee.setup.doctype.employee.employee.has_upload_permission"} +has_upload_permission = { + "Employee": "employee.setup.doctype.employee.employee.has_upload_permission" +} has_website_permission = { "Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission", @@ -419,8 +421,6 @@ scheduler_events = { "erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity", "erpnext.controllers.accounts_controller.update_invoice_status", "erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year", - "employee.setup.doctype.employee.employee_reminders.send_work_anniversary_reminders", - "employee.setup.doctype.employee.employee_reminders.send_birthday_reminders", "erpnext.projects.doctype.task.task.set_tasks_as_overdue", "erpnext.assets.doctype.asset.depreciation.post_depreciation_entries", "erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary", @@ -451,8 +451,6 @@ scheduler_events = { "erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans", "erpnext.crm.doctype.lead.lead.daily_open_lead", ], - "weekly": ["employee.setup.doctype.employee.employee_reminders.send_reminders_in_advance_weekly"], - "monthly": ["employee.setup.doctype.employee.employee_reminders.send_reminders_in_advance_monthly"], "monthly_long": [ "erpnext.accounts.deferred_revenue.process_deferred_accounting", "erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans", diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 72173a9615..4697d68b9d 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.provide("erpnext.hr"); -erpnext.hr.EmployeeController = class EmployeeController extends frappe.ui.form.Controller { +frappe.provide("erpnext.setup"); +erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.form.Controller { setup() { this.frm.fields_dict.user_id.get_query = function(doc, cdt, cdn) { return { @@ -115,7 +115,7 @@ frappe.ui.form.on('Employee', { } }); -cur_frm.cscript = new erpnext.hr.EmployeeController({ +cur_frm.cscript = new erpnext.setup.EmployeeController({ frm: cur_frm }); diff --git a/erpnext/setup/doctype/employee/employee_reminders.py b/erpnext/setup/doctype/employee/employee_reminders.py deleted file mode 100644 index 098b4f8483..0000000000 --- a/erpnext/setup/doctype/employee/employee_reminders.py +++ /dev/null @@ -1,274 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from frappe import _ -from frappe.utils import add_days, add_months, comma_sep, getdate, today - -from employee.setup.doctype.employee.employee import get_all_employee_emails, get_employee_email -from erpnext.hr.utils import get_holidays_for_employee - - -# ----------------- -# HOLIDAY REMINDERS -# ----------------- -def send_reminders_in_advance_weekly(): - to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders")) - frequency = frappe.db.get_single_value("HR Settings", "frequency") - if not (to_send_in_advance and frequency == "Weekly"): - return - - send_advance_holiday_reminders("Weekly") - - -def send_reminders_in_advance_monthly(): - to_send_in_advance = int(frappe.db.get_single_value("HR Settings", "send_holiday_reminders")) - frequency = frappe.db.get_single_value("HR Settings", "frequency") - if not (to_send_in_advance and frequency == "Monthly"): - return - - send_advance_holiday_reminders("Monthly") - - -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 send_holidays_reminder_in_advance(employee, holidays): - if not holidays: - return - - employee_doc = frappe.get_doc("Employee", employee) - employee_email = get_employee_email(employee_doc) - frequency = frappe.db.get_single_value("HR Settings", "frequency") - - email_header = _("Holidays this Month.") if frequency == "Monthly" else _("Holidays this Week.") - frappe.sendmail( - recipients=[employee_email], - subject=_("Upcoming Holidays Reminder"), - template="holiday_reminder", - args=dict( - reminder_text=_("Hey {}! This email is to remind you about the upcoming holidays.").format( - employee_doc.get("first_name") - ), - message=_("Below is the list of upcoming holidays for you:"), - advance_holiday_reminder=True, - holidays=holidays, - frequency=frequency[:-2], - ), - header=email_header, - ) - - -# ------------------ -# BIRTHDAY REMINDERS -# ------------------ -def send_birthday_reminders(): - """Send Employee birthday reminders if no 'Stop Birthday Reminders' is not set.""" - to_send = int(frappe.db.get_single_value("HR Settings", "send_birthday_reminders")) - if not to_send: - return - - employees_born_today = get_employees_who_are_born_today() - - for company, birthday_persons in employees_born_today.items(): - employee_emails = get_all_employee_emails(company) - birthday_person_emails = [get_employee_email(doc) for doc in birthday_persons] - recipients = list(set(employee_emails) - set(birthday_person_emails)) - - reminder_text, message = get_birthday_reminder_text_and_message(birthday_persons) - send_birthday_reminder(recipients, reminder_text, birthday_persons, message) - - if len(birthday_persons) > 1: - # special email for people sharing birthdays - for person in birthday_persons: - person_email = person["user_id"] or person["personal_email"] or person["company_email"] - others = [d for d in birthday_persons if d != person] - reminder_text, message = get_birthday_reminder_text_and_message(others) - send_birthday_reminder(person_email, reminder_text, others, message) - - -def get_birthday_reminder_text_and_message(birthday_persons): - if len(birthday_persons) == 1: - birthday_person_text = birthday_persons[0]["name"] - else: - # converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim - person_names = [d["name"] for d in birthday_persons] - birthday_person_text = comma_sep(person_names, frappe._("{0} & {1}"), False) - - reminder_text = _("Today is {0}'s birthday ๐ŸŽ‰").format(birthday_person_text) - message = _("A friendly reminder of an important date for our team.") - message += "
" - message += _("Everyone, letโ€™s congratulate {0} on their birthday.").format(birthday_person_text) - - return reminder_text, message - - -def send_birthday_reminder(recipients, reminder_text, birthday_persons, message): - frappe.sendmail( - recipients=recipients, - subject=_("Birthday Reminder"), - template="birthday_reminder", - args=dict( - reminder_text=reminder_text, - birthday_persons=birthday_persons, - message=message, - ), - header=_("Birthday Reminder ๐ŸŽ‚"), - ) - - -def get_employees_who_are_born_today(): - """Get all employee born today & group them based on their company""" - return get_employees_having_an_event_today("birthday") - - -def get_employees_having_an_event_today(event_type): - """Get all employee who have `event_type` today - & group them based on their company. `event_type` - can be `birthday` or `work_anniversary`""" - - from collections import defaultdict - - # Set column based on event type - if event_type == "birthday": - condition_column = "date_of_birth" - elif event_type == "work_anniversary": - condition_column = "date_of_joining" - else: - return - - employees_born_today = frappe.db.multisql( - { - "mariadb": f""" - SELECT `personal_email`, `company`, `company_email`, `user_id`, `employee_name` AS 'name', `image`, `date_of_joining` - FROM `tabEmployee` - WHERE - DAY({condition_column}) = DAY(%(today)s) - AND - MONTH({condition_column}) = MONTH(%(today)s) - AND - YEAR({condition_column}) < YEAR(%(today)s) - AND - `status` = 'Active' - """, - "postgres": f""" - SELECT "personal_email", "company", "company_email", "user_id", "employee_name" AS 'name', "image" - FROM "tabEmployee" - WHERE - DATE_PART('day', {condition_column}) = date_part('day', %(today)s) - AND - DATE_PART('month', {condition_column}) = date_part('month', %(today)s) - AND - DATE_PART('year', {condition_column}) < date_part('year', %(today)s) - AND - "status" = 'Active' - """, - }, - dict(today=today(), condition_column=condition_column), - as_dict=1, - ) - - grouped_employees = defaultdict(lambda: []) - - for employee_doc in employees_born_today: - grouped_employees[employee_doc.get("company")].append(employee_doc) - - return grouped_employees - - -# -------------------------- -# WORK ANNIVERSARY REMINDERS -# -------------------------- -def send_work_anniversary_reminders(): - """Send Employee Work Anniversary Reminders if 'Send Work Anniversary Reminders' is checked""" - to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders")) - if not to_send: - return - - employees_joined_today = get_employees_having_an_event_today("work_anniversary") - - for company, anniversary_persons in employees_joined_today.items(): - employee_emails = get_all_employee_emails(company) - anniversary_person_emails = [get_employee_email(doc) for doc in anniversary_persons] - recipients = list(set(employee_emails) - set(anniversary_person_emails)) - - reminder_text, message = get_work_anniversary_reminder_text_and_message(anniversary_persons) - send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message) - - if len(anniversary_persons) > 1: - # email for people sharing work anniversaries - for person in anniversary_persons: - person_email = person["user_id"] or person["personal_email"] or person["company_email"] - others = [d for d in anniversary_persons if d != person] - reminder_text, message = get_work_anniversary_reminder_text_and_message(others) - send_work_anniversary_reminder(person_email, reminder_text, others, message) - - -def get_work_anniversary_reminder_text_and_message(anniversary_persons): - if len(anniversary_persons) == 1: - anniversary_person = anniversary_persons[0]["name"] - persons_name = anniversary_person - # Number of years completed at the company - completed_years = getdate().year - anniversary_persons[0]["date_of_joining"].year - anniversary_person += f" completed {get_pluralized_years(completed_years)}" - else: - person_names_with_years = [] - names = [] - for person in anniversary_persons: - person_text = person["name"] - names.append(person_text) - # Number of years completed at the company - completed_years = getdate().year - person["date_of_joining"].year - person_text += f" completed {get_pluralized_years(completed_years)}" - person_names_with_years.append(person_text) - - # converts ["Jim", "Rim", "Dim"] to Jim, Rim & Dim - anniversary_person = comma_sep(person_names_with_years, frappe._("{0} & {1}"), False) - persons_name = comma_sep(names, frappe._("{0} & {1}"), False) - - reminder_text = _("Today {0} at our Company! ๐ŸŽ‰").format(anniversary_person) - message = _("A friendly reminder of an important date for our team.") - message += "
" - message += _("Everyone, letโ€™s congratulate {0} on their work anniversary!").format(persons_name) - - return reminder_text, message - - -def get_pluralized_years(years): - if years == 1: - return "1 year" - return f"{years} years" - - -def send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message): - frappe.sendmail( - recipients=recipients, - subject=_("Work Anniversary Reminder"), - template="anniversary_reminder", - args=dict( - reminder_text=reminder_text, - anniversary_persons=anniversary_persons, - message=message, - ), - header=_("Work Anniversary Reminder"), - ) diff --git a/erpnext/setup/doctype/employee/test_employee_reminders.py b/erpnext/setup/doctype/employee/test_employee_reminders.py deleted file mode 100644 index 2fcc2f9373..0000000000 --- a/erpnext/setup/doctype/employee/test_employee_reminders.py +++ /dev/null @@ -1,268 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import unittest -from datetime import timedelta - -import frappe -from frappe.utils import add_months, getdate - -from employee.setup.doctype.employee.employee_reminders import send_holidays_reminder_in_advance -from employee.setup.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.hr_settings.hr_settings import set_proceed_with_frequency_change -from erpnext.hr.utils import get_holidays_for_employee - - -class TestEmployeeReminders(unittest.TestCase): - @classmethod - def setUpClass(cls): - from employee.setup.doctype.holiday_list.test_holiday_list import make_holiday_list - - # Create a test holiday list - test_holiday_dates = cls.get_test_holiday_dates() - test_holiday_list = make_holiday_list( - "TestHolidayRemindersList", - holiday_dates=[ - {"holiday_date": test_holiday_dates[0], "description": "test holiday1"}, - {"holiday_date": test_holiday_dates[1], "description": "test holiday2"}, - {"holiday_date": test_holiday_dates[2], "description": "test holiday3", "weekly_off": 1}, - {"holiday_date": test_holiday_dates[3], "description": "test holiday4"}, - {"holiday_date": test_holiday_dates[4], "description": "test holiday5"}, - {"holiday_date": test_holiday_dates[5], "description": "test holiday6"}, - ], - from_date=getdate() - timedelta(days=10), - to_date=getdate() + timedelta(weeks=5), - ) - - # Create a test employee - test_employee = frappe.get_doc( - "Employee", make_employee("test@gopher.io", company="_Test Company") - ) - - # Attach the holiday list to employee - test_employee.holiday_list = test_holiday_list.name - test_employee.save() - - # Attach to class - cls.test_employee = test_employee - cls.test_holiday_dates = test_holiday_dates - - # Employee without holidays in this month/week - test_employee_2 = make_employee("test@empwithoutholiday.io", company="_Test Company") - test_employee_2 = frappe.get_doc("Employee", test_employee_2) - - test_holiday_list = make_holiday_list( - "TestHolidayRemindersList2", - holiday_dates=[ - {"holiday_date": add_months(getdate(), 1), "description": "test holiday1"}, - ], - from_date=add_months(getdate(), -2), - to_date=add_months(getdate(), 2), - ) - test_employee_2.holiday_list = test_holiday_list.name - test_employee_2.save() - - cls.test_employee_2 = test_employee_2 - cls.holiday_list_2 = test_holiday_list - - @classmethod - def get_test_holiday_dates(cls): - today_date = getdate() - return [ - today_date, - today_date - timedelta(days=4), - today_date - timedelta(days=3), - today_date + timedelta(days=1), - today_date + timedelta(days=3), - today_date + timedelta(weeks=3), - ] - - def setUp(self): - # Clear Email Queue - frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.sql("delete from `tabEmail Queue Recipient`") - - def test_is_holiday(self): - from employee.setup.doctype.employee.employee import is_holiday - - self.assertTrue(is_holiday(self.test_employee.name)) - self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[1])) - self.assertFalse(is_holiday(self.test_employee.name, date=getdate() - timedelta(days=1))) - - # Test weekly_off holidays - self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[2])) - self.assertFalse( - is_holiday(self.test_employee.name, date=self.test_holiday_dates[2], only_non_weekly=True) - ) - - # Test with descriptions - has_holiday, descriptions = is_holiday(self.test_employee.name, with_description=True) - self.assertTrue(has_holiday) - 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.date_of_birth = "1992" + frappe.utils.nowdate()[4:] - employee.company_email = "test@example.com" - employee.company = "_Test Company" - employee.save() - - from employee.setup.doctype.employee.employee_reminders import ( - get_employees_who_are_born_today, - send_birthday_reminders, - ) - - employees_born_today = get_employees_who_are_born_today() - self.assertTrue(employees_born_today.get("_Test Company")) - - hr_settings = frappe.get_doc("HR Settings", "HR Settings") - hr_settings.send_birthday_reminders = 1 - hr_settings.save() - - send_birthday_reminders() - - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) - self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message) - - def test_work_anniversary_reminders(self): - make_employee( - "test_work_anniversary@gmail.com", - date_of_joining="1998" + frappe.utils.nowdate()[4:], - company="_Test Company", - ) - - from employee.setup.doctype.employee.employee_reminders import ( - get_employees_having_an_event_today, - send_work_anniversary_reminders, - ) - - employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary") - employees = employees_having_work_anniversary.get("_Test Company") or [] - user_ids = [] - for entry in employees: - user_ids.append(entry.user_id) - - self.assertTrue("test_work_anniversary@gmail.com" in user_ids) - - hr_settings = frappe.get_doc("HR Settings", "HR Settings") - hr_settings.send_work_anniversary_reminders = 1 - hr_settings.save() - - send_work_anniversary_reminders() - - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", 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): - make_employee( - "test_work_anniversary_2@gmail.com", - date_of_joining=getdate(), - company="_Test Company", - ) - - from employee.setup.doctype.employee.employee_reminders import get_employees_having_an_event_today - - employees_having_work_anniversary = get_employees_having_an_event_today("work_anniversary") - employees = employees_having_work_anniversary.get("_Test Company") or [] - user_ids = [] - for entry in employees: - user_ids.append(entry.user_id) - - self.assertTrue("test_work_anniversary_2@gmail.com" not in user_ids) - - 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_advance_holiday_reminders_monthly(self): - from employee.setup.doctype.employee.employee_reminders import send_reminders_in_advance_monthly - - setup_hr_settings("Monthly") - - # disable emp 2, set same holiday list - frappe.db.set_value( - "Employee", - self.test_employee_2.name, - {"status": "Left", "holiday_list": self.test_employee.holiday_list}, - ) - - send_reminders_in_advance_monthly() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) - self.assertTrue(len(email_queue) > 0) - - # even though emp 2 has holiday, non-active employees should not be recipients - recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient") - self.assertTrue(self.test_employee_2.user_id not in recipients) - - # teardown: enable emp 2 - frappe.db.set_value( - "Employee", - self.test_employee_2.name, - {"status": "Active", "holiday_list": self.holiday_list_2.name}, - ) - - def test_advance_holiday_reminders_weekly(self): - from employee.setup.doctype.employee.employee_reminders import send_reminders_in_advance_weekly - - setup_hr_settings("Weekly") - - # disable emp 2, set same holiday list - frappe.db.set_value( - "Employee", - self.test_employee_2.name, - {"status": "Left", "holiday_list": self.test_employee.holiday_list}, - ) - - send_reminders_in_advance_weekly() - email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) - self.assertTrue(len(email_queue) > 0) - - # even though emp 2 has holiday, non-active employees should not be recipients - recipients = frappe.db.get_all("Email Queue Recipient", pluck="recipient") - self.assertTrue(self.test_employee_2.user_id not in recipients) - - # teardown: enable emp 2 - frappe.db.set_value( - "Employee", - self.test_employee_2.name, - {"status": "Active", "holiday_list": self.holiday_list_2.name}, - ) - - 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 setup_hr_settings(frequency=None): - # 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 = frequency or "Weekly" - hr_settings.save()