Skip to content
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

chore: release v14 #1018

Merged
merged 5 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
30 changes: 23 additions & 7 deletions hrms/controllers/employee_reminders.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ def send_holidays_reminder_in_advance(employee, holidays):
employee_doc = frappe.get_doc("Employee", employee)
employee_email = get_employee_email(employee_doc)
frequency = frappe.db.get_single_value("HR Settings", "frequency")

sender_email = get_sender_email()
email_header = _("Holidays this Month.") if frequency == "Monthly" else _("Holidays this Week.")
frappe.sendmail(
sender=sender_email,
recipients=[employee_email],
subject=_("Upcoming Holidays Reminder"),
template="holiday_reminder",
Expand All @@ -85,10 +86,12 @@ def send_holidays_reminder_in_advance(employee, holidays):
# ------------------
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

sender = get_sender_email()
employees_born_today = get_employees_who_are_born_today()

for company, birthday_persons in employees_born_today.items():
Expand All @@ -97,15 +100,15 @@ def send_birthday_reminders():
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)
send_birthday_reminder(recipients, reminder_text, birthday_persons, message, sender)

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)
send_birthday_reminder(person_email, reminder_text, others, message, sender)


def get_birthday_reminder_text_and_message(birthday_persons):
Expand All @@ -124,8 +127,9 @@ def get_birthday_reminder_text_and_message(birthday_persons):
return reminder_text, message


def send_birthday_reminder(recipients, reminder_text, birthday_persons, message):
def send_birthday_reminder(recipients, reminder_text, birthday_persons, message, sender=None):
frappe.sendmail(
sender=sender,
recipients=recipients,
subject=_("Birthday Reminder"),
template="birthday_reminder",
Expand Down Expand Up @@ -206,6 +210,7 @@ def send_work_anniversary_reminders():
if not to_send:
return

sender = get_sender_email()
employees_joined_today = get_employees_having_an_event_today("work_anniversary")

message = _("A friendly reminder of an important date for our team.")
Expand All @@ -218,15 +223,15 @@ def send_work_anniversary_reminders():
recipients = list(set(employee_emails) - set(anniversary_person_emails))

reminder_text = get_work_anniversary_reminder_text(anniversary_persons)
send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message)
send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message, sender)

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 = get_work_anniversary_reminder_text(others)
send_work_anniversary_reminder(person_email, reminder_text, others, message)
send_work_anniversary_reminder(person_email, reminder_text, others, message, sender)


def get_work_anniversary_reminder_text(anniversary_persons: list) -> str:
Expand Down Expand Up @@ -261,8 +266,15 @@ def get_pluralized_years(years):
return f"{years} years"


def send_work_anniversary_reminder(recipients, reminder_text, anniversary_persons, message):
def send_work_anniversary_reminder(
recipients,
reminder_text,
anniversary_persons,
message,
sender=None,
):
frappe.sendmail(
sender=sender,
recipients=recipients,
subject=_("Work Anniversary Reminder"),
template="anniversary_reminder",
Expand All @@ -273,3 +285,7 @@ def send_work_anniversary_reminder(recipients, reminder_text, anniversary_person
),
header=_("Work Anniversary Reminder"),
)


def get_sender_email() -> str | None:
return frappe.db.get_single_value("HR Settings", "sender_email")
1 change: 1 addition & 0 deletions hrms/hr/doctype/attendance/attendance_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ frappe.listview_settings["Attendance"] = {
fieldtype: "MultiCheck",
options: [],
columns: 2,
select_all: true,
},
],
primary_action(data) {
Expand Down
9 changes: 9 additions & 0 deletions hrms/hr/doctype/hr_settings/hr_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
// For license information, please see license.txt

frappe.ui.form.on('HR Settings', {
refresh: function (frm) {
frm.set_query("sender", () => {
return {
filters: {
enable_outgoing: 1,
},
};
});
}
});

frappe.tour['HR Settings'] = [
Expand Down
31 changes: 24 additions & 7 deletions hrms/hr/doctype/hr_settings/hr_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
"column_break_9",
"retirement_age",
"reminders_section",
"send_birthday_reminders",
"column_break_11",
"send_work_anniversary_reminders",
"column_break_18",
"send_birthday_reminders",
"send_holiday_reminders",
"frequency",
"column_break_hyec",
"sender",
"sender_email",
"leave_and_expense_claim_settings",
"send_leave_notification",
"leave_approval_notification_template",
Expand Down Expand Up @@ -71,10 +73,6 @@
"fieldtype": "Check",
"label": "Expense Approver Mandatory In Expense Claim"
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
},
{
"default": "1",
"fieldname": "leave_approver_mandatory_in_leave_application",
Expand Down Expand Up @@ -251,13 +249,31 @@
{
"fieldname": "column_break_34",
"fieldtype": "Column Break"
},
{
"fieldname": "sender",
"fieldtype": "Link",
"label": "Sender",
"options": "Email Account"
},
{
"depends_on": "eval:doc.sender",
"fetch_from": "sender.email_id",
"fieldname": "sender_email",
"fieldtype": "Data",
"label": "Sender Email",
"read_only": 1
},
{
"fieldname": "column_break_hyec",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
"links": [],
"modified": "2022-10-04 17:29:45.926918",
"modified": "2023-11-01 11:06:31.329718",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
Expand All @@ -275,5 +291,6 @@
],
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
10 changes: 10 additions & 0 deletions hrms/payroll/doctype/payroll_settings/payroll_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
// For license information, please see license.txt

frappe.ui.form.on('Payroll Settings', {
refresh: function (frm) {
frm.set_query("sender", () => {
return {
filters: {
enable_outgoing: 1,
},
};
});
},

encrypt_salary_slips_in_emails: function(frm) {
let encrypt_state = frm.doc.encrypt_salary_slips_in_emails;
frm.set_df_property('password_policy', 'reqd', encrypt_state);
Expand Down
19 changes: 18 additions & 1 deletion hrms/payroll/doctype/payroll_settings/payroll_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"show_leave_balances_in_salary_slip",
"email_section",
"email_salary_slip_to_employee",
"sender",
"sender_email",
"column_break_iewr",
"encrypt_salary_slips_in_emails",
"password_policy",
Expand Down Expand Up @@ -155,13 +157,28 @@
"fieldname": "consider_marked_attendance_on_holidays",
"fieldtype": "Check",
"label": "Consider Marked Attendance on Holidays"
},
{
"depends_on": "eval:doc.email_salary_slip_to_employee",
"fieldname": "sender",
"fieldtype": "Link",
"label": "Sender",
"options": "Email Account"
},
{
"depends_on": "eval:doc.sender",
"fetch_from": "sender.email_id",
"fieldname": "sender_email",
"fieldtype": "Data",
"label": "Sender Email",
"read_only": 1
}
],
"icon": "fa fa-cog",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-09-25 14:03:59.215240",
"modified": "2023-11-01 13:51:04.225492",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Settings",
Expand Down
2 changes: 2 additions & 0 deletions hrms/payroll/doctype/salary_slip/salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,7 @@ def get_loan_details(self):
"docstatus": 1,
"repay_from_salary": 1,
"company": self.company,
"status": ("!=", "Closed"),
},
)

Expand Down Expand Up @@ -1860,6 +1861,7 @@ def email_salary_slip(self):

if receiver:
email_args = {
"sender": payroll_settings.sender_email,
"recipients": [receiver],
"message": _(message),
"subject": "Salary Slip - from {0} to {1}".format(self.start_date, self.end_date),
Expand Down
89 changes: 89 additions & 0 deletions hrms/payroll/doctype/salary_slip/test_salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ def test_loan_repayment_salary_slip(self):
"Test Loan Repayment Salary Structure",
"Monthly",
employee=applicant,
company="_Test Company",
currency="INR",
payroll_period=payroll_period,
)
Expand Down Expand Up @@ -689,6 +690,94 @@ def test_payroll_frequency(self):
elif payroll_frequency == "Daily":
self.assertEqual(ss.end_date, nowdate())

def test_loan_write_off_salary_slip(self):
from erpnext.loan_management.doctype.loan.loan import make_loan_write_off
from erpnext.loan_management.doctype.loan.test_loan import (
create_loan,
create_loan_accounts,
create_loan_type,
create_repayment_entry,
make_loan_disbursement_entry,
)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
process_loan_interest_accrual_for_term_loans,
)

from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure

applicant = make_employee("test_loan_repayment_salary_slip@salary.com", company="_Test Company")

create_loan_accounts()

create_loan_type(
"Personal Loan",
12000,
0,
is_term_loan=1,
mode_of_payment="Cash",
disbursement_account="Disbursement Account - _TC",
payment_account="Payment Account - _TC",
loan_account="Loan Account - _TC",
interest_income_account="Interest Income Account - _TC",
penalty_income_account="Penalty Income Account - _TC",
repayment_schedule_type="Monthly as per repayment start date",
)

payroll_period = create_payroll_period(name="_Test Payroll Period", company="_Test Company")

make_salary_structure(
"Test Loan Repayment Salary Structure",
"Monthly",
employee=applicant,
company="_Test Company",
currency="INR",
payroll_period=payroll_period,
)

frappe.db.sql(
"delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'"
)
loan = create_loan(
applicant,
"Personal Loan",
12000,
"Repay Over Number of Periods",
12,
posting_date=payroll_period.start_date,
)
loan.repay_from_salary = 1
loan.submit()

make_loan_disbursement_entry(
loan.name, loan.loan_amount, disbursement_date=payroll_period.start_date
)

process_loan_interest_accrual_for_term_loans(
posting_date=add_months(payroll_period.start_date, 12)
)

repayment_entry = create_repayment_entry(
loan.name, applicant, add_months(payroll_period.start_date, 7), 7000
)
repayment_entry.submit()

we = make_loan_write_off(
loan.name, posting_date=add_months(payroll_period.start_date, 8), amount=5000
)
we.submit()

self.assertEqual(frappe.db.get_value("Loan", loan.name, "status"), "Closed")

ss = make_employee_salary_slip(
applicant,
"Monthly",
"Test Loan Repayment Salary Structure",
posting_date=add_months(payroll_period.start_date, 8),
)
ss.submit()

self.assertEqual(ss.total_loan_repayment, 0)

def test_multi_currency_salary_slip(self):
from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure

Expand Down