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

feat(Employee Checkin): add Fetch Shift buttons in form and list view actions #2071

Merged
merged 4 commits into from
Aug 15, 2024
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
28 changes: 27 additions & 1 deletion hrms/hr/doctype/employee_checkin/employee_checkin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// For license information, please see license.txt

frappe.ui.form.on("Employee Checkin", {
refresh: async (_frm) => {
refresh: async (frm) => {
if (!frm.doc.__islocal) frm.trigger("add_fetch_shift_button");

const allow_geolocation_tracking = await frappe.db.get_single_value(
"HR Settings",
"allow_geolocation_tracking",
Expand All @@ -14,6 +16,30 @@ 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();
krantheman marked this conversation as resolved.
Show resolved Hide resolved
frappe.show_alert({
message: __("Shift has been successfully updated to {0}.", [
frm.doc.shift,
]),
indicator: "green",
});
},
});
});
},

fetch_geolocation: async (frm) => {
if (!navigator.geolocation) {
frappe.msgprint({
Expand Down
58 changes: 36 additions & 22 deletions hrms/hr/doctype/employee_checkin/employee_checkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,33 @@ def validate_duplicate_log(self):
_("This employee already has a log with the same timestamp.{0}").format("<Br>" + 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()
def set_geolocation_from_coordinates(self):
Expand Down Expand Up @@ -134,6 +137,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,
Expand Down
14 changes: 14 additions & 0 deletions hrms/hr/doctype/employee_checkin/employee_checkin_list.js
Original file line number Diff line number Diff line change
@@ -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,
},
});
});
},
};
35 changes: 35 additions & 0 deletions hrms/hr/doctype/employee_checkin/test_employee_checkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down Expand Up @@ -490,6 +491,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("[email protected]", company="_Test Company")
emp2 = make_employee("[email protected]", 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))]
Expand Down
Loading