forked from frappe/hrms
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request frappe#1948 from krantheman/feat-geofencing
feat(Employee Checkin): geofencing
- Loading branch information
Showing
14 changed files
with
408 additions
and
73 deletions.
There are no files selected for viewing
Submodule frappe-ui
updated
53 files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,15 +16,14 @@ | |
) | ||
|
||
from erpnext.setup.doctype.employee.test_employee import make_employee | ||
from erpnext.setup.doctype.holiday_list.test_holiday_list import set_holiday_list | ||
|
||
from hrms.hr.doctype.employee_checkin.employee_checkin import ( | ||
CheckinRadiusExceededError, | ||
add_log_based_on_employee_field, | ||
bulk_fetch_shift, | ||
calculate_working_hours, | ||
mark_attendance_and_link_log, | ||
) | ||
from hrms.hr.doctype.leave_application.test_leave_application import get_first_sunday | ||
from hrms.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type | ||
from hrms.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list | ||
|
||
|
@@ -39,6 +38,8 @@ def setUp(self): | |
to_date = get_year_ending(getdate()) | ||
self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date) | ||
|
||
frappe.db.set_single_value("HR Settings", "allow_geolocation_tracking", 0) | ||
|
||
def test_geolocation_tracking(self): | ||
employee = make_employee("[email protected]") | ||
checkin = make_checkin(employee) | ||
|
@@ -49,9 +50,7 @@ def test_geolocation_tracking(self): | |
# geolocation tracking is disabled | ||
self.assertIsNone(checkin.geolocation) | ||
|
||
hr_settings = frappe.get_single("HR Settings") | ||
hr_settings.allow_geolocation_tracking = 1 | ||
hr_settings.save() | ||
frappe.db.set_single_value("HR Settings", "allow_geolocation_tracking", 1) | ||
|
||
checkin.save() | ||
self.assertEqual( | ||
|
@@ -491,6 +490,65 @@ def test_consecutive_shift_assignments_overlapping_within_grace_period(self): | |
log = make_checkin(employee, timestamp) | ||
self.assertEqual(log.shift, shift2.name) | ||
|
||
@change_settings("HR Settings", {"allow_multiple_shift_assignments": 1}) | ||
@change_settings("HR Settings", {"allow_geolocation_tracking": 1}) | ||
def test_geofencing(self): | ||
employee = make_employee("[email protected]", company="_Test Company") | ||
|
||
# 8 - 12 | ||
shift1 = setup_shift_type() | ||
# 15 - 19 | ||
shift2 = setup_shift_type(shift_type="Consecutive Shift", start_time="15:00:00", end_time="19:00:00") | ||
|
||
date = getdate() | ||
location1 = make_shift_location("Loc A", 24, 72) | ||
location2 = make_shift_location("Loc B", 25, 75, checkin_radius=2000) | ||
make_shift_assignment(shift1.name, employee, date, shift_location=location1.name) | ||
make_shift_assignment(shift2.name, employee, date, shift_location=location2.name) | ||
|
||
timestamp = datetime.combine(add_days(date, -1), get_time("11:00:00")) | ||
# allowed as it is before the shift start date | ||
make_checkin(employee, timestamp, 20, 65) | ||
|
||
timestamp = datetime.combine(date, get_time("06:00:00")) | ||
# allowed as it is before the shift start time | ||
make_checkin(employee, timestamp, 20, 65) | ||
|
||
timestamp = datetime.combine(date, get_time("10:00:00")) | ||
# allowed as distance (150m) is within checkin radius (500m) | ||
make_checkin(employee, timestamp, 24.001, 72.001) | ||
|
||
timestamp = datetime.combine(date, get_time("10:30:00")) | ||
log = frappe.get_doc( | ||
{ | ||
"doctype": "Employee Checkin", | ||
"employee": employee, | ||
"time": timestamp, | ||
"latitude": 24.01, | ||
"longitude": 72.01, | ||
} | ||
) | ||
# not allowed as distance (1506m) is not within checkin radius | ||
self.assertRaises(CheckinRadiusExceededError, log.insert) | ||
|
||
# to ensure that the correct shift assignment is considered | ||
timestamp = datetime.combine(date, get_time("16:00:00")) | ||
# allowed as distance (1506m) is within checkin radius (2000m) | ||
make_checkin(employee, timestamp, 25.01, 75.01) | ||
|
||
timestamp = datetime.combine(date, get_time("16:30:00")) | ||
log = frappe.get_doc( | ||
{ | ||
"doctype": "Employee Checkin", | ||
"employee": employee, | ||
"time": timestamp, | ||
"latitude": 25.1, | ||
"longitude": 75.1, | ||
} | ||
) | ||
# not allowed as distance (15004m) is not within checkin radius | ||
self.assertRaises(CheckinRadiusExceededError, log.insert) | ||
|
||
def test_bulk_fetch_shift(self): | ||
emp1 = make_employee("[email protected]", company="_Test Company") | ||
emp2 = make_employee("[email protected]", company="_Test Company") | ||
|
@@ -533,7 +591,7 @@ def make_n_checkins(employee, n, hours_to_reverse=1): | |
return logs | ||
|
||
|
||
def make_checkin(employee, time=None): | ||
def make_checkin(employee, time=None, latitude=None, longitude=None): | ||
if not time: | ||
time = now_datetime() | ||
|
||
|
@@ -544,6 +602,22 @@ def make_checkin(employee, time=None): | |
"time": time, | ||
"device_id": "device1", | ||
"log_type": "IN", | ||
"latitude": latitude, | ||
"longitude": longitude, | ||
} | ||
).insert() | ||
return log | ||
|
||
|
||
def make_shift_location(location_name, latitude, longitude, checkin_radius=500): | ||
shift_location = frappe.get_doc( | ||
{ | ||
"doctype": "Shift Location", | ||
"location_name": location_name, | ||
"latitude": latitude, | ||
"longitude": longitude, | ||
"checkin_radius": checkin_radius, | ||
} | ||
).insert() | ||
|
||
return shift_location |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors | ||
// For license information, please see license.txt | ||
|
||
frappe.ui.form.on("Shift Location", { | ||
refresh: async () => { | ||
const allow_geolocation_tracking = await frappe.db.get_single_value( | ||
"HR Settings", | ||
"allow_geolocation_tracking", | ||
); | ||
|
||
if (!allow_geolocation_tracking) | ||
hide_field([ | ||
"checkin_radius", | ||
"fetch_geolocation", | ||
"latitude", | ||
"longitude", | ||
"geolocation", | ||
]); | ||
}, | ||
|
||
fetch_geolocation: (frm) => { | ||
hrms.fetch_geolocation(frm); | ||
}, | ||
}); |
Oops, something went wrong.