Skip to content

Commit

Permalink
Merge pull request #2204 from frappe/version-14-hotfix
Browse files Browse the repository at this point in the history
chore: release v14
  • Loading branch information
krantheman committed Sep 18, 2024
2 parents ac71345 + fa91264 commit 2d20943
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 27 deletions.
2 changes: 1 addition & 1 deletion hrms/hr/doctype/leave_allocation/leave_allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ frappe.ui.form.on("Leave Allocation", {
},

refresh: function (frm) {
if (frm.doc.docstatus === 1 && frm.doc.expired) {
if (frm.doc.docstatus === 1 && !frm.doc.expired) {
var valid_expiry = moment(frappe.datetime.get_today()).isBetween(
frm.doc.from_date,
frm.doc.to_date,
Expand Down
7 changes: 6 additions & 1 deletion hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt


import frappe
from frappe import _
from frappe.model.document import Document
Expand Down Expand Up @@ -185,6 +184,12 @@ def get_remaining_leaves(allocation):
@frappe.whitelist()
def expire_allocation(allocation, expiry_date=None):
"""expires non-carry forwarded allocation"""
import json

if isinstance(allocation, str):
allocation = json.loads(allocation)
allocation = frappe.get_doc("Leave Allocation", allocation["name"])

leaves = get_remaining_leaves(allocation)
expiry_date = expiry_date if expiry_date else allocation.to_date

Expand Down
39 changes: 37 additions & 2 deletions hrms/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt

# import frappe
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils.data import add_to_date, today

from erpnext.setup.doctype.employee.test_employee import make_employee

from hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation


class TestLeaveLedgerEntry(FrappeTestCase):
pass
def setUp(self):
emp_id = make_employee("[email protected]", company="_Test Company")
self.employee = frappe.get_doc("Employee", emp_id)

def test_expire_allocation(self):
import json

allocation = {
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": self.employee.name,
"employee_name": self.employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": today(),
"to_date": add_to_date(today(), days=30),
"new_leaves_allocated": 5,
"docstatus": 1,
}

allocation = frappe.get_doc(allocation).save()

expire_allocation(json.dumps(allocation.as_dict()))
allocation.reload()

self.assertEqual(allocation.expired, 1)

def tearDown(self):
frappe.db.rollback()


test_dependencies = ["Employee", "Leave Type"]
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ frappe.query_reports["Employee Advance Summary"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
width: "80",
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ frappe.query_reports["Employee Leave Balance"] = {
label: __("From Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.defaults.get_default("year_start_date"),
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.defaults.get_default("year_end_date"),
},
{
label: __("Company"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@ frappe.query_reports["Monthly Attendance Sheet"] = {
fieldtype: "Select",
options: ["", "Branch", "Grade", "Department", "Designation"],
},
{
fieldname: "include_company_descendants",
label: __("Include Company Descendants"),
fieldtype: "Check",
default: 1,
},
{
fieldname: "summarized_view",
label: __("Summarized View"),
fieldtype: "Check",
Default: 0,
default: 0,
},
],
onload: function () {
Expand Down
35 changes: 21 additions & 14 deletions hrms/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from frappe import _
from frappe.query_builder.functions import Count, Extract, Sum
from frappe.utils import cint, cstr, getdate
from frappe.utils.nestedset import get_descendants_of

Filters = frappe._dict

Expand All @@ -31,6 +32,11 @@ def execute(filters: Filters | None = None) -> tuple:
if not (filters.month and filters.year):
frappe.throw(_("Please select month and year."))

if filters.company:
filters.companies = [filters.company]
if filters.include_company_descendants:
filters.companies.extend(get_descendants_of("Company", filters.company))

attendance_map = get_attendance_map(filters)
if not attendance_map:
frappe.msgprint(_("No attendance records found."), alert=True, indicator="orange")
Expand Down Expand Up @@ -223,7 +229,7 @@ def get_attendance_map(filters: Filters) -> dict:

for d in attendance_list:
if d.status == "On Leave":
leave_map.setdefault(d.employee, []).append(d.day_of_month)
leave_map.setdefault(d.employee, {}).setdefault(d.shift, []).append(d.day_of_month)
continue

if d.shift is None:
Expand All @@ -234,13 +240,14 @@ def get_attendance_map(filters: Filters) -> dict:

# leave is applicable for the entire day so all shifts should show the leave entry
for employee, leave_days in leave_map.items():
# no attendance records exist except leaves
if employee not in attendance_map:
attendance_map.setdefault(employee, {}).setdefault(None, {})
for assigned_shift, days in leave_days.items():
# no attendance records exist except leaves
if employee not in attendance_map:
attendance_map.setdefault(employee, {}).setdefault(assigned_shift, {})

for day in leave_days:
for shift in attendance_map[employee].keys():
attendance_map[employee][shift][day] = "On Leave"
for day in days:
for shift in attendance_map[employee].keys():
attendance_map[employee][shift][day] = "On Leave"

return attendance_map

Expand All @@ -257,7 +264,7 @@ def get_attendance_records(filters: Filters) -> list[dict]:
)
.where(
(Attendance.docstatus == 1)
& (Attendance.company == filters.company)
& (Attendance.company.isin(filters.companies))
& (Extract("month", Attendance.attendance_date) == filters.month)
& (Extract("year", Attendance.attendance_date) == filters.year)
)
Expand Down Expand Up @@ -288,7 +295,7 @@ def get_employee_related_details(filters: Filters) -> tuple[dict, list]:
Employee.company,
Employee.holiday_list,
)
.where(Employee.company == filters.company)
.where(Employee.company.isin(filters.companies))
)

if filters.employee:
Expand All @@ -305,7 +312,7 @@ def get_employee_related_details(filters: Filters) -> tuple[dict, list]:
emp_map = {}

if group_by:
group_key = lambda d: d[group_by] # noqa
group_key = lambda d: "" if d[group_by] is None else d[group_by] # noqa
for parameter, employees in groupby(sorted(employee_details, key=group_key), key=group_key):
group_by_param_values.append(parameter)
emp_map.setdefault(parameter, frappe._dict())
Expand Down Expand Up @@ -466,7 +473,7 @@ def get_attendance_summary_and_days(employee: str, filters: Filters) -> tuple[di
.where(
(Attendance.docstatus == 1)
& (Attendance.employee == employee)
& (Attendance.company == filters.company)
& (Attendance.company.isin(filters.companies))
& (Extract("month", Attendance.attendance_date) == filters.month)
& (Extract("year", Attendance.attendance_date) == filters.year)
)
Expand All @@ -479,7 +486,7 @@ def get_attendance_summary_and_days(employee: str, filters: Filters) -> tuple[di
.where(
(Attendance.docstatus == 1)
& (Attendance.employee == employee)
& (Attendance.company == filters.company)
& (Attendance.company.isin(filters.companies))
& (Extract("month", Attendance.attendance_date) == filters.month)
& (Extract("year", Attendance.attendance_date) == filters.year)
)
Expand Down Expand Up @@ -543,7 +550,7 @@ def get_leave_summary(employee: str, filters: Filters) -> dict[str, float]:
.where(
(Attendance.employee == employee)
& (Attendance.docstatus == 1)
& (Attendance.company == filters.company)
& (Attendance.company.isin(filters.companies))
& ((Attendance.leave_type.isnotnull()) | (Attendance.leave_type != ""))
& (Extract("month", Attendance.attendance_date) == filters.month)
& (Extract("year", Attendance.attendance_date) == filters.year)
Expand Down Expand Up @@ -577,7 +584,7 @@ def get_entry_exits_summary(employee: str, filters: Filters) -> dict[str, float]
.where(
(Attendance.docstatus == 1)
& (Attendance.employee == employee)
& (Attendance.company == filters.company)
& (Attendance.company.isin(filters.companies))
& (Extract("month", Attendance.attendance_date) == filters.month)
& (Extract("year", Attendance.attendance_date) == filters.year)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
make_holiday_list,
make_leave_application,
)
from hrms.tests.test_utils import get_first_day_for_prev_month
from hrms.tests.test_utils import create_company, get_first_day_for_prev_month


class TestMonthlyAttendanceSheet(FrappeTestCase):
Expand All @@ -41,6 +41,9 @@ def test_monthly_attendance_sheet_report(self):
mark_attendance(self.employee, previous_month_first + relativedelta(days=1), "Present")
mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")

employee_on_leave_with_shift = make_employee("[email protected]", company=self.company)
mark_attendance(employee_on_leave_with_shift, previous_month_first, "On Leave", "Day Shift")

filters = frappe._dict(
{
"month": previous_month_first.month,
Expand All @@ -50,14 +53,14 @@ def test_monthly_attendance_sheet_report(self):
)
report = execute(filters=filters)

record = report[1][0]
datasets = report[3]["data"]["datasets"]
absent = datasets[0]["values"]
present = datasets[1]["values"]
leaves = datasets[2]["values"]

# ensure correct attendance is reflected on the report
self.assertEqual(self.employee, record.get("employee"))
self.assertEqual(self.employee, report[1][0].get("employee"))
self.assertEqual("Day Shift", report[1][1].get("shift"))
self.assertEqual(absent[0], 1)
self.assertEqual(present[1], 1)
self.assertEqual(leaves[2], 1)
Expand Down Expand Up @@ -205,6 +208,11 @@ def test_attendance_with_group_by_filter(self):
mark_attendance(self.employee, previous_month_first + relativedelta(days=2), "On Leave")
mark_attendance(self.employee, previous_month_first + relativedelta(days=3), "Present")

departmentless_employee = make_employee(
"[email protected]", company=self.company, department=None
)
mark_attendance(departmentless_employee, previous_month_first + relativedelta(days=3), "Present")

filters = frappe._dict(
{
"month": previous_month_first.month,
Expand Down Expand Up @@ -276,6 +284,35 @@ def test_attendance_with_employee_filter(self):
self.assertEqual(present[1], 1)
self.assertEqual(leaves[2], 1)

def test_attendance_with_company_filter(self):
create_company("Test Parent Company", is_group=1)
create_company("Test Child Company", is_group=1, parent_company="Test Parent Company")
create_company("Test Grandchild Company", parent_company="Test Child Company")

employee1 = make_employee("[email protected]", company="Test Parent Company")
employee2 = make_employee("[email protected]", company="Test Child Company")
employee3 = make_employee("[email protected]", company="Test Grandchild Company")

previous_month_first = get_first_day_for_prev_month()
mark_attendance(employee1, previous_month_first, "Present")
mark_attendance(employee2, previous_month_first, "Present")
mark_attendance(employee3, previous_month_first, "Present")

filters = frappe._dict(
{
"month": previous_month_first.month,
"year": previous_month_first.year,
"company": "Test Parent Company",
"include_company_descendants": 1,
}
)
report = execute(filters=filters)
self.assertEqual(len(report[1]), 3)

filters.include_company_descendants = 0
report = execute(filters=filters)
self.assertEqual(len(report[1]), 1)

def test_attendance_with_employee_filter_and_summarized_view(self):
previous_month_first = get_first_day_for_prev_month()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def get_data_for_eval(self, emp: str, emp_details: dict) -> tuple:
salary_slip = frappe.new_doc("Salary Slip")
salary_slip.employee = emp
salary_slip.salary_structure = emp_details.salary_structure
salary_slip.start_date = self.payroll_period_start_date
salary_slip.start_date = max(self.payroll_period_start_date, emp_details.date_of_joining)
salary_slip.payroll_frequency = frappe.db.get_value(
"Salary Structure", emp_details.salary_structure, "payroll_frequency"
)
Expand Down
4 changes: 3 additions & 1 deletion hrms/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def add_date_to_holiday_list(date: str, holiday_list: str) -> None:
holiday_list.save()


def create_company(name: str = "_Test Company"):
def create_company(name: str = "_Test Company", is_group: 0 | 1 = 0, parent_company: str | None = None):
if frappe.db.exists("Company", name):
return frappe.get_doc("Company", name)

Expand All @@ -98,6 +98,8 @@ def create_company(name: str = "_Test Company"):
"company_name": name,
"default_currency": "INR",
"country": "India",
"is_group": is_group,
"parent_company": parent_company,
}
).insert()

Expand Down

0 comments on commit 2d20943

Please sign in to comment.