Geo-Fencing
Model
Each organization_unit may enable geo-fencing. When on:
unit.geo_lat, unit.geo_lon, unit.geo_radius_m
Every punch carries lat, lon, accuracy_m. The server computes haversine(unit.centre, punch) and compares to radius + min(accuracy, 50) — a 50-metre cap on accuracy inflation prevents trivial spoofing.
Policy
red_flag_policies for the GEO_VIOLATION code per unit:
allow_flag— the punch lands,GEO_VIOLATIONattached (critical severity).block—400 GEO_VIOLATION_BLOCKED, punch rejected.require_approval— punch lands, attendance row held inpending_approval.
Mobile UX
The Flutter client requests location permission at onboarding and verifies it on every check-in:
flowchart TD
A[Tap Check-in] --> B{Location permission?}
B -- denied --> C[Show 'enable location' banner, do nothing]
B -- granted --> D[Get high-accuracy fix, timeout 8s]
D -- fix --> E[POST /attendance/punch]
D -- no fix --> F[Save to offline buffer without lat/lon]
F --> G[Server flags LOCATION_MISSING]
Accuracy Caveats
- Indoor lat/lon can be noisy; the app falls back to
last knownwith a visible warning and flagsLOW_ACCURACY. - Mocked locations are detected via
Geolocator.isMockedand triggerDEVICE_MISMATCH+GEO_VIOLATION.
Operator Tools
- SystemAdmin can draw the fence on a map in Filament.
- A debug endpoint (
GET /units/{id}/geo/preview) returns recent punches plotted relative to the fence for troubleshooting.