Skip to content

Commit 9002c7f

Browse files
authored
Merge pull request #2638 from frappe/version-15-hotfix
2 parents f1a1321 + eb7c313 commit 9002c7f

File tree

19 files changed

+192
-66
lines changed

19 files changed

+192
-66
lines changed

frontend/src/components/CheckInPanel.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
</div>
7070
</template>
7171

72-
<Button variant="solid" class="w-full py-5 text-sm" @click="submitLog(nextAction.action)">
72+
<Button variant="solid" class="w-full py-5 text-sm" @click.once="submitLog(nextAction.action)">
7373
{{ __("Confirm {0}", [nextAction.label]) }}
7474
</Button>
7575
</div>

hrms/hr/doctype/compensatory_leave_request/compensatory_leave_request.py

+10-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
22
# For license information, please see license.txt
33

4+
import datetime
45

56
import frappe
67
from frappe import _
@@ -78,7 +79,7 @@ def on_submit(self):
7879
comp_leave_valid_from = add_days(self.work_end_date, 1)
7980
leave_period = get_leave_period(comp_leave_valid_from, comp_leave_valid_from, company)
8081
if leave_period:
81-
leave_allocation = self.get_existing_allocation_for_period(leave_period)
82+
leave_allocation = self.get_existing_allocation(comp_leave_valid_from)
8283
if leave_allocation:
8384
leave_allocation.new_leaves_allocated += date_difference
8485
leave_allocation.validate()
@@ -122,30 +123,21 @@ def on_cancel(self):
122123
leave_allocation, date_difference * -1, add_days(self.work_end_date, 1)
123124
)
124125

125-
def get_existing_allocation_for_period(self, leave_period):
126-
leave_allocation = frappe.db.sql(
127-
"""
128-
select name
129-
from `tabLeave Allocation`
130-
where employee=%(employee)s and leave_type=%(leave_type)s
131-
and docstatus=1
132-
and (from_date between %(from_date)s and %(to_date)s
133-
or to_date between %(from_date)s and %(to_date)s
134-
or (from_date < %(from_date)s and to_date > %(to_date)s))
135-
""",
136-
{
137-
"from_date": leave_period[0].from_date,
138-
"to_date": leave_period[0].to_date,
126+
def get_existing_allocation(self, comp_leave_valid_from: datetime.date) -> dict | None:
127+
leave_allocation = frappe.db.get_all(
128+
"Leave Allocation",
129+
filters={
139130
"employee": self.employee,
140131
"leave_type": self.leave_type,
132+
"from_date": ("<=", comp_leave_valid_from),
133+
"to_date": (">=", comp_leave_valid_from),
134+
"docstatus": 1,
141135
},
142-
as_dict=1,
136+
limit=1,
143137
)
144138

145139
if leave_allocation:
146140
return frappe.get_doc("Leave Allocation", leave_allocation[0].name)
147-
else:
148-
return False
149141

150142
def create_leave_allocation(self, leave_period, date_difference):
151143
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")

hrms/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py

+51-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33

44
import frappe
55
from frappe.tests.utils import FrappeTestCase
6-
from frappe.utils import add_days, add_months, today
6+
from frappe.utils import add_days, add_months, getdate, today
77

88
from hrms.hr.doctype.attendance_request.test_attendance_request import get_employee
9+
from hrms.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
910
from hrms.hr.doctype.leave_application.leave_application import get_leave_balance_on
1011
from hrms.hr.doctype.leave_period.test_leave_period import create_leave_period
1112
from hrms.tests.test_utils import add_date_to_holiday_list
1213

13-
test_dependencies = ["Employee"]
14-
1514

1615
class TestCompensatoryLeaveRequest(FrappeTestCase):
1716
def setUp(self):
@@ -42,7 +41,7 @@ def test_leave_balance_on_submit(self):
4241
before + 1,
4342
)
4443

45-
def test_leave_allocation_update_on_submit(self):
44+
def test_allocation_update_on_submit(self):
4645
employee = get_employee()
4746
mark_attendance(employee, date=add_days(today(), -1))
4847
compensatory_leave_request = get_compensatory_leave_request(
@@ -70,6 +69,54 @@ def test_leave_allocation_update_on_submit(self):
7069
)
7170
self.assertEqual(leaves_allocated, 2)
7271

72+
def test_allocation_update_on_submit_on_multiple_allocations(self):
73+
"""Tests whether the correct allocation is updated when there are multiple allocations in the same leave period"""
74+
employee = get_employee()
75+
today = getdate()
76+
77+
first_alloc_start = add_months(today, -3)
78+
first_alloc_end = add_days(today, -1)
79+
second_alloc_start = today
80+
second_alloc_end = add_months(today, 1)
81+
82+
add_date_to_holiday_list(first_alloc_start, employee.holiday_list)
83+
allocation_1 = create_leave_allocation(
84+
leave_type="Compensatory Off",
85+
employee=employee.name,
86+
from_date=first_alloc_start,
87+
to_date=first_alloc_end,
88+
)
89+
allocation_1.new_leaves_allocated = 0
90+
allocation_1.submit()
91+
92+
add_date_to_holiday_list(second_alloc_start, employee.holiday_list)
93+
allocation_2 = create_leave_allocation(
94+
leave_type="Compensatory Off",
95+
employee=employee.name,
96+
from_date=second_alloc_start,
97+
to_date=second_alloc_end,
98+
)
99+
allocation_2.new_leaves_allocated = 0
100+
allocation_2.submit()
101+
102+
# adds leave balance in first allocation
103+
mark_attendance(employee, date=first_alloc_start)
104+
compensatory_leave_request = get_compensatory_leave_request(
105+
employee.name, leave_date=first_alloc_start
106+
)
107+
compensatory_leave_request.submit()
108+
allocation_1.reload()
109+
self.assertEqual(allocation_1.total_leaves_allocated, 1)
110+
111+
# adds leave balance in second allocation
112+
mark_attendance(employee, date=second_alloc_start)
113+
compensatory_leave_request = get_compensatory_leave_request(
114+
employee.name, leave_date=second_alloc_start
115+
)
116+
compensatory_leave_request.submit()
117+
allocation_2.reload()
118+
self.assertEqual(allocation_2.total_leaves_allocated, 1)
119+
73120
def test_creation_of_leave_ledger_entry_on_submit(self):
74121
"""check creation of leave ledger entry on submission of leave request"""
75122
employee = get_employee()

hrms/hr/doctype/expense_claim/expense_claim.js

+8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ frappe.ui.form.on("Expense Claim", {
6464
query: "erpnext.controllers.queries.employee_query",
6565
};
6666
});
67+
68+
frm.set_query("department", function () {
69+
return {
70+
filters: {
71+
company: frm.doc.company,
72+
},
73+
};
74+
});
6775
},
6876

6977
onload: function (frm) {

hrms/hr/doctype/expense_claim/expense_claim.py

+14
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class ExpenseApproverIdentityError(frappe.ValidationError):
2929
pass
3030

3131

32+
class MismatchError(frappe.ValidationError):
33+
pass
34+
35+
3236
class ExpenseClaim(AccountsController, PWANotificationsMixin):
3337
def onload(self):
3438
self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value(
@@ -47,6 +51,7 @@ def validate(self):
4751
self.set_expense_account(validate=True)
4852
self.calculate_taxes()
4953
self.set_status()
54+
self.validate_company_and_department()
5055
if self.task and not self.project:
5156
self.project = frappe.db.get_value("Task", self.task, "project")
5257

@@ -83,6 +88,15 @@ def set_status(self, update=False):
8388
else:
8489
self.status = status
8590

91+
def validate_company_and_department(self):
92+
if self.department:
93+
company = frappe.db.get_value("Department", self.department, "company")
94+
if company and self.company != company:
95+
frappe.throw(
96+
_("Department {0} does not belong to company: {1}").format(self.department, self.company),
97+
exc=MismatchError,
98+
)
99+
86100
def on_update(self):
87101
share_doc_with_approver(self, self.expense_approver)
88102
self.publish_update()

hrms/hr/doctype/expense_claim/test_expense_claim.py

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from erpnext.setup.doctype.employee.test_employee import make_employee
1111

1212
from hrms.hr.doctype.expense_claim.expense_claim import (
13+
MismatchError,
1314
get_outstanding_amount_for_claim,
1415
make_bank_entry,
1516
make_expense_claim_for_delivery_trip,
@@ -568,6 +569,13 @@ def test_repost(self):
568569
)
569570
self.assertEqual(ledger_balance, expected_data)
570571

572+
def test_company_department_validation(self):
573+
# validate company and department
574+
expense_claim = frappe.new_doc("Expense Claim")
575+
expense_claim.company = "_Test Company 3"
576+
expense_claim.department = "Accounts - _TC2"
577+
self.assertRaises(MismatchError, expense_claim.save)
578+
571579

572580
def get_payable_account(company):
573581
return frappe.get_cached_value("Company", company, "default_payable_account")

hrms/hr/doctype/full_and_final_statement/full_and_final_statement.js

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ frappe.ui.form.on("Full and Final Statement", {
3535
filters["is_group"] = 0;
3636
}
3737

38+
if (frappe.model.is_submittable(fnf_doc.reference_document_type)) {
39+
filters["docstatus"] = ["!=", 2];
40+
}
41+
3842
if (frappe.meta.has_field(fnf_doc.reference_document_type, "company")) {
3943
filters["company"] = frm.doc.company;
4044
}

hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def create_journal_entry(self):
205205
"debit_in_account_currency": flt(data.amount, precision),
206206
"user_remark": data.remark,
207207
}
208-
if data.reference_document_type == "Expense Claim":
208+
if data.reference_document_type in ["Expense Claim", "Gratuity"]:
209209
account_dict["party_type"] = "Employee"
210210
account_dict["party"] = self.employee
211211

@@ -248,6 +248,15 @@ def create_journal_entry(self):
248248
)
249249
return jv
250250

251+
def set_gratuity_status(self):
252+
for payable in self.payables:
253+
if payable.component != "Gratuity":
254+
continue
255+
gratuity = frappe.get_doc("Gratuity", payable.reference_document)
256+
amount = payable.amount if self.docstatus == 1 and self.status == "Paid" else 0
257+
gratuity.db_set("paid_amount", amount)
258+
gratuity.set_status(update=True)
259+
251260

252261
@frappe.whitelist()
253262
def get_account_and_amount(ref_doctype, ref_document):
@@ -310,3 +319,4 @@ def update_full_and_final_statement_status(doc, method=None):
310319
fnf = frappe.get_doc("Full and Final Statement", entry.reference_name)
311320
fnf.db_set("status", status)
312321
fnf.notify_update()
322+
fnf.set_gratuity_status()

hrms/hr/doctype/full_and_final_statement/test_full_and_final_statement.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_check_bootstraped_data_asset_movement_and_jv_creation(self):
3434
"Leave Encashment",
3535
]
3636

37-
receivable_bootstraped_component = ["Employee Advance", "Loan"]
37+
receivable_bootstraped_component = self.fnf.get_receivable_component()
3838

3939
# checking payables and receivables bootstraped value
4040
self.assertEqual([payable.component for payable in self.fnf.payables], payables_bootstraped_component)

hrms/hr/doctype/leave_control_panel/leave_control_panel.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
"idx": 1,
187187
"issingle": 1,
188188
"links": [],
189-
"modified": "2024-03-20 15:05:39.635388",
189+
"modified": "2025-01-13 13:47:55.262534",
190190
"modified_by": "Administrator",
191191
"module": "HR",
192192
"name": "Leave Control Panel",
@@ -199,8 +199,7 @@
199199
"write": 1
200200
}
201201
],
202-
"read_only": 1,
203-
"sort_field": "modified",
202+
"sort_field": "creation",
204203
"sort_order": "DESC",
205204
"states": []
206205
}

hrms/hr/doctype/leave_encashment/leave_encashment.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ def create_leave_ledger_entry(self, submit=True):
204204
return
205205

206206
to_date = leave_allocation.get("to_date")
207-
if to_date < getdate():
207+
208+
can_expire = not frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
209+
if to_date < getdate() and can_expire:
208210
args = frappe._dict(
209211
leaves=self.encashment_days, from_date=to_date, to_date=to_date, is_carry_forward=0
210212
)

hrms/hr/doctype/leave_ledger_entry/leave_ledger_entry.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44
import frappe
55
from frappe import _
66
from frappe.model.document import Document
7-
from frappe.utils import DATE_FORMAT, flt, get_link_to_form, getdate, today
7+
from frappe.utils import DATE_FORMAT, flt, formatdate, get_link_to_form, getdate, today
8+
9+
10+
class InvalidLeaveLedgerEntry(frappe.ValidationError):
11+
pass
812

913

1014
class LeaveLedgerEntry(Document):
1115
def validate(self):
1216
if getdate(self.from_date) > getdate(self.to_date):
13-
frappe.throw(_("To date needs to be before from date"))
17+
frappe.throw(
18+
_(
19+
"Leave Ledger Entry's To date needs to be after From date. Currently, From Date is {0} and To Date is {1}"
20+
).format(
21+
frappe.bold(formatdate(self.from_date)),
22+
frappe.bold(formatdate(self.to_date)),
23+
),
24+
exc=InvalidLeaveLedgerEntry,
25+
title=_("Invalid Leave Ledger Entry"),
26+
)
1427

1528
def on_cancel(self):
1629
# allow cancellation of expiry leaves

hrms/hr/doctype/shift_assignment_tool/shift_assignment_tool.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@
209209
"hide_toolbar": 1,
210210
"issingle": 1,
211211
"links": [],
212-
"modified": "2024-12-13 17:38:45.675004",
212+
"modified": "2025-01-13 13:48:33.710186",
213213
"modified_by": "Administrator",
214214
"module": "HR",
215215
"name": "Shift Assignment Tool",
@@ -225,7 +225,6 @@
225225
"write": 1
226226
}
227227
],
228-
"read_only": 1,
229228
"sort_field": "modified",
230229
"sort_order": "DESC",
231230
"states": []

hrms/payroll/doctype/bulk_salary_structure_assignment/bulk_salary_structure_assignment.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
"hide_toolbar": 1,
148148
"issingle": 1,
149149
"links": [],
150-
"modified": "2024-07-09 19:33:40.135057",
150+
"modified": "2025-01-13 13:48:46.095481",
151151
"modified_by": "Administrator",
152152
"module": "Payroll",
153153
"name": "Bulk Salary Structure Assignment",
@@ -163,8 +163,7 @@
163163
"write": 1
164164
}
165165
],
166-
"read_only": 1,
167-
"sort_field": "modified",
166+
"sort_field": "creation",
168167
"sort_order": "DESC",
169168
"states": []
170169
}

hrms/payroll/doctype/gratuity/gratuity.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def set_status(self, update=False):
4444
else:
4545
status = "Unpaid"
4646

47-
if update:
47+
if update and self.status != status:
4848
self.db_set("status", status)
4949
else:
5050
self.status = status

0 commit comments

Comments
 (0)