From 2029a8b1edadee0bc06be8f8130fc1a624e49615 Mon Sep 17 00:00:00 2001 From: Asmita Hase Date: Mon, 27 Jan 2025 15:26:42 +0530 Subject: [PATCH 1/9] fix: fetch shift even if previous shift is not the same as current shift (cherry picked from commit 38719473e5cdb56921687c21b02aadfd9dbe64a8) --- hrms/hr/doctype/employee_checkin/employee_checkin.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.js b/hrms/hr/doctype/employee_checkin/employee_checkin.js index 2d86442b1f..fb1f7ac805 100644 --- a/hrms/hr/doctype/employee_checkin/employee_checkin.js +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.js @@ -23,14 +23,12 @@ frappe.ui.form.on("Employee Checkin", { add_fetch_shift_button(frm) { if (frm.doc.attendace) return; frm.add_custom_button(__("Fetch Shift"), function () { - const previous_shift = frm.doc.shift; frappe.call({ method: "fetch_shift", doc: frm.doc, freeze: true, freeze_message: __("Fetching Shift"), callback: function () { - if (previous_shift === frm.doc.shift) return; frm.dirty(); frm.save(); frappe.show_alert({ From 41e49630929853d901d239900e2415637fbee572 Mon Sep 17 00:00:00 2001 From: Asmita Hase Date: Tue, 28 Jan 2025 00:43:40 +0530 Subject: [PATCH 2/9] chore: test bulk fetch shift if shift settings change for the same shift (cherry picked from commit 21066361525951a72d2a7a5075e307c6ddbcb5aa) --- .../employee_checkin/test_employee_checkin.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hrms/hr/doctype/employee_checkin/test_employee_checkin.py b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py index 98f7388a6d..79371a7583 100644 --- a/hrms/hr/doctype/employee_checkin/test_employee_checkin.py +++ b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py @@ -583,6 +583,34 @@ def test_bulk_fetch_shift(self): # shift does not change since attendance is already marked self.assertEqual(log2.shift, shift1.name) + def test_bulk_fetch_shift_if_shift_settings_change_for_the_same_shift(self): + emp1 = make_employee("bulkemp1@example.com", company="_Test Company") + emp2 = make_employee("bulkemp2@example.com", company="_Test Company") + + # 8 - 12, + shift = setup_shift_type(shift_type="Test Bulk Shift") + date = getdate() + make_shift_assignment(shift.name, emp1, date) + make_shift_assignment(shift.name, emp2, date) + + timestamp = datetime.combine(date, get_time("08:00:00")) + # shift actual start is `current date 07:00:00` + log1 = make_checkin(emp1, timestamp) + self.assertEqual(log1.shift_actual_start, datetime.combine(date, get_time("07:00:00"))) + log2 = make_checkin(emp2, timestamp) + self.assertEqual(log2.shift_actual_start, datetime.combine(date, get_time("07:00:00"))) + + # change shift settings like check in buffer from 60 minutes to 120 minutes + # so now shift actual start is `current date 06:00:00` + shift.begin_check_in_before_shift_start_time = 120 + shift.save() + bulk_fetch_shift([log1.name, log2.name]) + # shift changes according to the new assignment + log1.reload() + self.assertEqual(log1.shift_actual_start, datetime.combine(date, get_time("06:00:00"))) + log2.reload() + self.assertEqual(log2.shift_actual_start, datetime.combine(date, get_time("06:00:00"))) + def make_n_checkins(employee, n, hours_to_reverse=1): logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n + 1))] From a5c603deddccd06ff2bd7477d857590c372a46aa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:38:43 +0530 Subject: [PATCH 3/9] chore: bump actions/cache to v4 (backport #2706) (#2708) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cc6649f62..52dfd03c45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml') }} @@ -79,7 +79,7 @@ jobs: ${{ runner.os }}- - name: Cache node modules - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-node-modules with: @@ -94,7 +94,7 @@ jobs: id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} From 337cc8db07f3949923b3bb3166a55afc01837e91 Mon Sep 17 00:00:00 2001 From: Asmita Hase Date: Tue, 28 Jan 2025 11:13:23 +0530 Subject: [PATCH 4/9] fix: alert on fetch shift if no shift is found (cherry picked from commit f38b61a278af760146d675b4845d89cca455f332) --- .../employee_checkin/employee_checkin.js | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.js b/hrms/hr/doctype/employee_checkin/employee_checkin.js index fb1f7ac805..9b9a2a98c3 100644 --- a/hrms/hr/doctype/employee_checkin/employee_checkin.js +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.js @@ -29,14 +29,21 @@ frappe.ui.form.on("Employee Checkin", { freeze: true, freeze_message: __("Fetching Shift"), callback: function () { - frm.dirty(); - frm.save(); - frappe.show_alert({ - message: __("Shift has been successfully updated to {0}.", [ - frm.doc.shift, - ]), - indicator: "green", - }); + if (frm.doc.shift) { + frappe.show_alert({ + message: __("Shift has been successfully updated to {0}.", [ + frm.doc.shift, + ]), + indicator: "green", + }); + frm.dirty(); + frm.save(); + } else { + frappe.show_alert({ + message: __("No valid shift found for log time"), + indicator: "orange", + }); + } }, }); }); From 8c8019a5027656fa6ea687b2b721670f5c9b0821 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Jan 2025 14:59:27 +0530 Subject: [PATCH 5/9] fix: Lending app uninstall error (cherry picked from commit 3bcb7b8810ab20ceb313d39d32f3cea409437f93) --- hrms/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hrms/setup.py b/hrms/setup.py index 383b5118c0..ced82e2da9 100644 --- a/hrms/setup.py +++ b/hrms/setup.py @@ -595,6 +595,7 @@ def remove_lending_docperms_from_ess(): if row.document_type in loan_docperms: doc.user_doctypes.remove(row) + doc.flags.ignore_links = True doc.save(ignore_permissions=True) From 2f70cf0f44ac45a3c882c55b0398c14ddcb9ffb7 Mon Sep 17 00:00:00 2001 From: Aysha Date: Wed, 15 Jan 2025 16:19:04 +0530 Subject: [PATCH 6/9] fix: handle loan repayment on fnf submission (cherry picked from commit a0b56e1657363496851a7f9239a52a92f4bcb626) --- .../full_and_final_statement.py | 5 ++ .../full_and_final_statement_loan_utils.py | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py index dd1a4ba2e6..82a2deeff8 100644 --- a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py @@ -6,6 +6,8 @@ from frappe.model.document import Document from frappe.utils import flt, get_link_to_form, today +from hrms.hr.doctype.full_and_final_statement.full_and_final_statement_loan_utils import process_loan_accrual + class FullandFinalStatement(Document): def before_insert(self): @@ -22,6 +24,9 @@ def before_submit(self): self.validate_settlement("receivables") self.validate_assets() + def on_submit(self): + process_loan_accrual(self) + def on_cancel(self): self.ignore_linked_doctypes = ("GL Entry",) diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py new file mode 100644 index 0000000000..50603e5449 --- /dev/null +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py @@ -0,0 +1,78 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from typing import TYPE_CHECKING + +import frappe +from frappe import _ + +from hrms.payroll.doctype.salary_slip.salary_slip_loan_utils import if_lending_app_installed + +if TYPE_CHECKING: + from hrms.payroll.doctype.full_and_final_statement.full_and_final_statement import FullandFinalStatement + + +@if_lending_app_installed +def process_loan_accrual(doc: "FullandFinalStatement"): + from lending.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import ( + make_loan_interest_accrual_entry, + ) + from lending.loan_management.doctype.loan_repayment.loan_repayment import ( + calculate_amounts, + create_repayment_entry, + get_pending_principal_amount, + ) + + loan_receivables = [] + for receivable in doc.receivables: + if receivable.component != "Loan": + continue + + loan_receivables.append(receivable.reference_document) + + loan_receivables = list(set(loan_receivables)) + + for loan in loan_receivables: + loan_doc = frappe.get_doc("Loan", loan) + loan_repayment_schedule = frappe.get_doc("Loan Repayment Schedule", {"loan": loan, "docstatus": 1}) + if loan_repayment_schedule.repayment_schedule: + amounts = [] + for repayment_schedule in loan_repayment_schedule.repayment_schedule: + amounts = calculate_amounts(loan, doc.transaction_date, "Normal Repayment") + pending_principal_amount = get_pending_principal_amount(loan_doc) + if not repayment_schedule.is_accrued: + args = frappe._dict( + { + "loan": loan, + "applicant_type": loan_doc.applicant_type, + "applicant": loan_doc.applicant, + "interest_income_account": loan_doc.interest_income_account, + "loan_account": loan_doc.loan_account, + "pending_principal_amount": amounts["pending_principal_amount"], + "payable_principal": repayment_schedule.principal_amount, + "interest_amount": repayment_schedule.interest_amount, + "total_pending_interest_amount": pending_principal_amount, + "penalty_amount": amounts["penalty_amount"], + "posting_date": doc.transaction_date, + "repayment_schedule_name": repayment_schedule.name, + "accrual_type": "Regular", + "due_date": doc.transaction_date, + } + ) + make_loan_interest_accrual_entry(args) + frappe.db.set_value("Repayment Schedule", repayment_schedule.name, "is_accrued", 1) + + repayment_entry = create_repayment_entry( + loan, + doc.employee, + doc.company, + doc.transaction_date, + loan_doc.loan_product, + "Normal Repayment", + amounts["interest_amount"], + amounts["pending_principal_amount"], + receivable.amount, + ) + + repayment_entry.save() + repayment_entry.submit() From 0c62a3b81235d3b099842a130486724c6a0206ed Mon Sep 17 00:00:00 2001 From: Aysha Date: Wed, 15 Jan 2025 16:54:38 +0530 Subject: [PATCH 7/9] fix: cancell loan repayment on fnf cancellation (cherry picked from commit a77be530109da5d32ae81027e0957952d481b1d5) --- .../full_and_final_statement.py | 8 ++++-- .../full_and_final_statement_loan_utils.py | 28 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py index 82a2deeff8..83f607db17 100644 --- a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py @@ -6,7 +6,10 @@ from frappe.model.document import Document from frappe.utils import flt, get_link_to_form, today -from hrms.hr.doctype.full_and_final_statement.full_and_final_statement_loan_utils import process_loan_accrual +from hrms.hr.doctype.full_and_final_statement.full_and_final_statement_loan_utils import ( + cancel_loan_repayment, + process_loan_accrual, +) class FullandFinalStatement(Document): @@ -28,7 +31,8 @@ def on_submit(self): process_loan_accrual(self) def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry",) + self.ignore_linked_doctypes = "GL Entry" + cancel_loan_repayment(self) def validate_relieving_date(self): if not self.relieving_date: diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py index 50603e5449..e20332ca69 100644 --- a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py @@ -30,8 +30,6 @@ def process_loan_accrual(doc: "FullandFinalStatement"): loan_receivables.append(receivable.reference_document) - loan_receivables = list(set(loan_receivables)) - for loan in loan_receivables: loan_doc = frappe.get_doc("Loan", loan) loan_repayment_schedule = frappe.get_doc("Loan Repayment Schedule", {"loan": loan, "docstatus": 1}) @@ -76,3 +74,29 @@ def process_loan_accrual(doc: "FullandFinalStatement"): repayment_entry.save() repayment_entry.submit() + + +@if_lending_app_installed +def cancel_loan_repayment(doc: "FullandFinalStatement"): + loan_receivables = [] + for receivable in doc.receivables: + if receivable.component != "Loan": + continue + + loan_receivables.append(receivable.reference_document) + + # loan_receivables = list(set(loan_receivables)) + for loan in loan_receivables: + posting_date = frappe.utils.getdate(doc.transaction_date) + loan_repayment = frappe.get_doc( + "Loan Repayment", {"against_loan": loan, "docstatus": 1, "posting_date": posting_date} + ) + + if loan_repayment: + loan_repayment.cancel() + + loan_interest_accruals = frappe.get_all( + "Loan Interest Accrual", filters={"loan": loan, "docstatus": 1, "posting_date": posting_date} + ) + for accrual in loan_interest_accruals: + frappe.get_doc("Loan Interest Accrual", accrual.name).cancel() From 148d73da4816c741de3aecb4d2a41d5bde946e59 Mon Sep 17 00:00:00 2001 From: Aysha Date: Wed, 15 Jan 2025 16:57:51 +0530 Subject: [PATCH 8/9] fix: undo change (cherry picked from commit 054d0219aac33d838f9e549c314f306bca135ab9) --- .../full_and_final_statement/full_and_final_statement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py index 83f607db17..4d334c6ecd 100644 --- a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement.py @@ -31,7 +31,7 @@ def on_submit(self): process_loan_accrual(self) def on_cancel(self): - self.ignore_linked_doctypes = "GL Entry" + self.ignore_linked_doctypes = ("GL Entry",) cancel_loan_repayment(self) def validate_relieving_date(self): From b420fe800988d207b70807044b8b915c7b8673a8 Mon Sep 17 00:00:00 2001 From: Aysha Date: Wed, 15 Jan 2025 17:00:10 +0530 Subject: [PATCH 9/9] chore: remove commented code (cherry picked from commit 2f8014dcd1dfb95a17fc21f48ae9b59d2e3c18f4) --- .../full_and_final_statement_loan_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py index e20332ca69..530e6ece2b 100644 --- a/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py +++ b/hrms/hr/doctype/full_and_final_statement/full_and_final_statement_loan_utils.py @@ -85,7 +85,6 @@ def cancel_loan_repayment(doc: "FullandFinalStatement"): loan_receivables.append(receivable.reference_document) - # loan_receivables = list(set(loan_receivables)) for loan in loan_receivables: posting_date = frappe.utils.getdate(doc.transaction_date) loan_repayment = frappe.get_doc(