diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.js b/hrms/hr/doctype/employee_checkin/employee_checkin.js index 648b61d2b7..891b21dbaf 100644 --- a/hrms/hr/doctype/employee_checkin/employee_checkin.js +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.js @@ -2,6 +2,31 @@ // For license information, please see license.txt frappe.ui.form.on("Employee Checkin", { - // setup: (frm) => { - // } + refresh: async (frm) => { + if (!frm.doc.__islocal) frm.trigger("add_fetch_shift_button"); + }, + + 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({ + message: __("Shift has been successfully updated to {0}.", [ + frm.doc.shift, + ]), + indicator: "green", + }); + }, + }); + }); + }, }); diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin.py b/hrms/hr/doctype/employee_checkin/employee_checkin.py index 8524b6ef6a..7ff6700a94 100644 --- a/hrms/hr/doctype/employee_checkin/employee_checkin.py +++ b/hrms/hr/doctype/employee_checkin/employee_checkin.py @@ -35,30 +35,33 @@ def validate_duplicate_log(self): _("This employee already has a log with the same timestamp.{0}").format("
" + doc_link) ) + @frappe.whitelist() def fetch_shift(self): - shift_actual_timings = get_actual_start_end_datetime_of_shift( - self.employee, get_datetime(self.time), True - ) - if shift_actual_timings: - if ( - shift_actual_timings.shift_type.determine_check_in_and_check_out - == "Strictly based on Log Type in Employee Checkin" - and not self.log_type - and not self.skip_auto_attendance - ): - frappe.throw( - _("Log Type is required for check-ins falling in the shift: {0}.").format( - shift_actual_timings.shift_type.name - ) - ) - if not self.attendance: - self.shift = shift_actual_timings.shift_type.name - self.shift_actual_start = shift_actual_timings.actual_start - self.shift_actual_end = shift_actual_timings.actual_end - self.shift_start = shift_actual_timings.start_datetime - self.shift_end = shift_actual_timings.end_datetime - else: + if not ( + shift_actual_timings := get_actual_start_end_datetime_of_shift( + self.employee, get_datetime(self.time), True + ) + ): self.shift = None + return + + if ( + shift_actual_timings.shift_type.determine_check_in_and_check_out + == "Strictly based on Log Type in Employee Checkin" + and not self.log_type + and not self.skip_auto_attendance + ): + frappe.throw( + _("Log Type is required for check-ins falling in the shift: {0}.").format( + shift_actual_timings.shift_type.name + ) + ) + if not self.attendance: + self.shift = shift_actual_timings.shift_type.name + self.shift_actual_start = shift_actual_timings.actual_start + self.shift_actual_end = shift_actual_timings.actual_end + self.shift_start = shift_actual_timings.start_datetime + self.shift_end = shift_actual_timings.end_datetime @frappe.whitelist() @@ -111,6 +114,17 @@ def add_log_based_on_employee_field( return doc +@frappe.whitelist() +def bulk_fetch_shift(checkins: list[str] | str) -> None: + if isinstance(checkins, str): + checkins = frappe.json.loads(checkins) + for d in checkins: + doc = frappe.get_doc("Employee Checkin", d) + doc.fetch_shift() + doc.flags.ignore_validate = True + doc.save() + + def mark_attendance_and_link_log( logs, attendance_status, diff --git a/hrms/hr/doctype/employee_checkin/employee_checkin_list.js b/hrms/hr/doctype/employee_checkin/employee_checkin_list.js new file mode 100644 index 0000000000..da89813f80 --- /dev/null +++ b/hrms/hr/doctype/employee_checkin/employee_checkin_list.js @@ -0,0 +1,14 @@ +frappe.listview_settings["Employee Checkin"] = { + onload: function (listview) { + listview.page.add_action_item(__("Fetch Shifts"), () => { + const checkins = listview.get_checked_items().map((checkin) => checkin.name); + frappe.call({ + method: "hrms.hr.doctype.employee_checkin.employee_checkin.bulk_fetch_shift", + freeze: true, + args: { + checkins, + }, + }); + }); + }, +}; diff --git a/hrms/hr/doctype/employee_checkin/test_employee_checkin.py b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py index aa37b1d88e..d16a2851da 100644 --- a/hrms/hr/doctype/employee_checkin/test_employee_checkin.py +++ b/hrms/hr/doctype/employee_checkin/test_employee_checkin.py @@ -20,6 +20,7 @@ from hrms.hr.doctype.employee_checkin.employee_checkin import ( add_log_based_on_employee_field, + bulk_fetch_shift, calculate_working_hours, mark_attendance_and_link_log, ) @@ -457,6 +458,40 @@ def test_consecutive_shift_assignments_overlapping_within_grace_period(self): log = make_checkin(employee, timestamp) self.assertEqual(log.shift, shift2.name) + def test_bulk_fetch_shift(self): + emp1 = make_employee("emp1@example.com", company="_Test Company") + emp2 = make_employee("emp2@example.com", company="_Test Company") + + # 8 - 12 + shift1 = setup_shift_type(shift_type="Shift 1") + # 12:30 - 16:30 + shift2 = setup_shift_type(shift_type="Shift 2", start_time="12:30:00", end_time="16:30:00") + + frappe.db.set_value("Employee", emp1, "default_shift", shift1.name) + frappe.db.set_value("Employee", emp2, "default_shift", shift1.name) + + date = getdate() + timestamp = datetime.combine(date, get_time("12:30:00")) + + log1 = make_checkin(emp1, timestamp) + self.assertEqual(log1.shift, shift1.name) + log2 = make_checkin(emp2, timestamp) + self.assertEqual(log2.shift, shift1.name) + + mark_attendance_and_link_log([log2], "Present", date) + + make_shift_assignment(shift2.name, emp1, date) + make_shift_assignment(shift2.name, emp2, date) + + bulk_fetch_shift([log1.name, log2.name]) + + log1.reload() + # shift changes according to the new assignment + self.assertEqual(log1.shift, shift2.name) + log2.reload() + # shift does not change since attendance is already marked + self.assertEqual(log2.shift, shift1.name) + def make_n_checkins(employee, n, hours_to_reverse=1): logs = [make_checkin(employee, now_datetime() - timedelta(hours=hours_to_reverse, minutes=n + 1))] diff --git a/hrms/hr/doctype/expense_claim/expense_claim.js b/hrms/hr/doctype/expense_claim/expense_claim.js index e13a5dad69..10b997b105 100644 --- a/hrms/hr/doctype/expense_claim/expense_claim.js +++ b/hrms/hr/doctype/expense_claim/expense_claim.js @@ -282,17 +282,18 @@ frappe.ui.form.on("Expense Claim", { } }); }, + get_taxes: function (frm) { - if (frm.doc.taxes) { - frappe.call({ - method: "calculate_taxes", - doc: frm.doc, - callback: () => { - refresh_field("taxes"); - frm.trigger("update_employee_advance_claimed_amount"); - }, - }); - } + if (!frm.doc.taxes.length) return; + + frappe.call({ + method: "calculate_taxes", + doc: frm.doc, + callback: () => { + refresh_field("taxes"); + frm.trigger("update_employee_advance_claimed_amount"); + }, + }); }, get_advances: function (frm) { diff --git a/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json b/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json index a41d644182..06649c4bbb 100644 --- a/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json +++ b/hrms/hr/workspace/employee_lifecycle/employee_lifecycle.json @@ -230,7 +230,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Daily Work Summary Replies", "link_count": 0, "link_to": "Daily Work Summary Replies", @@ -287,7 +287,7 @@ "type": "Link" } ], - "modified": "2022-09-16 11:35:33.462001", + "modified": "2024-08-11 11:35:33.462001", "modified_by": "Administrator", "module": "HR", "name": "Employee Lifecycle", @@ -350,4 +350,4 @@ } ], "title": "Employee Lifecycle" -} \ No newline at end of file +} diff --git a/hrms/hr/workspace/expense_claims/expense_claims.json b/hrms/hr/workspace/expense_claims/expense_claims.json index f3f24df5eb..8eba09c565 100644 --- a/hrms/hr/workspace/expense_claims/expense_claims.json +++ b/hrms/hr/workspace/expense_claims/expense_claims.json @@ -169,7 +169,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Accounts Receivable", "link_count": 0, "link_to": "Accounts Receivable", @@ -179,7 +179,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Accounts Payable", "link_count": 0, "link_to": "Accounts Payable", @@ -189,7 +189,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "General Ledger", "link_count": 0, "link_to": "General Ledger", @@ -256,7 +256,7 @@ "type": "Link" } ], - "modified": "2024-02-05 09:10:45.636550", + "modified": "2024-08-11 11:40:45.636550", "modified_by": "Administrator", "module": "HR", "name": "Expense Claims", @@ -295,4 +295,4 @@ } ], "title": "Expense Claims" -} \ No newline at end of file +} diff --git a/hrms/hr/workspace/hr/hr.json b/hrms/hr/workspace/hr/hr.json index 33df7a8cc1..dfb8f4664b 100644 --- a/hrms/hr/workspace/hr/hr.json +++ b/hrms/hr/workspace/hr/hr.json @@ -170,7 +170,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Employee Exits", "link_count": 0, "link_to": "Employee Exits", @@ -385,7 +385,7 @@ "type": "Link" } ], - "modified": "2022-12-06 16:55:59.080694", + "modified": "2024-10-10 16:55:59.080694", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -449,4 +449,4 @@ } ], "title": "HR" -} \ No newline at end of file +} diff --git a/hrms/payroll/workspace/salary_payout/salary_payout.json b/hrms/payroll/workspace/salary_payout/salary_payout.json index 38f656d618..4d44a5270e 100644 --- a/hrms/payroll/workspace/salary_payout/salary_payout.json +++ b/hrms/payroll/workspace/salary_payout/salary_payout.json @@ -318,7 +318,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "General Ledger", "link_count": 0, "link_to": "General Ledger", @@ -328,7 +328,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Accounts Payable", "link_count": 0, "link_to": "Accounts Payable", @@ -338,7 +338,7 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Accounts Receivable", "link_count": 0, "link_to": "Accounts Receivable", @@ -385,7 +385,7 @@ "type": "Link" } ], - "modified": "2022-09-21 12:27:38.631110", + "modified": "2024-08-11 11:45:37.351654", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Payout", @@ -423,4 +423,4 @@ } ], "title": "Salary Payout" -} \ No newline at end of file +}