feat(attendance): add geofence monitoring and policy controls

This commit is contained in:
zouantchaw
2026-03-16 15:31:13 +01:00
parent b455455a49
commit 5d8240ed51
22 changed files with 1667 additions and 162 deletions

View File

@@ -60,6 +60,11 @@ function createMobileHandlers() {
assignmentId: payload.assignmentId || 'assignment-1',
status: 'CLOCK_OUT',
}),
submitLocationStreamBatch: async (_actor, payload) => ({
assignmentId: payload.assignmentId || 'assignment-1',
pointCount: payload.points.length,
status: 'RECORDED',
}),
saveTaxFormDraft: async (_actor, payload) => ({
formType: payload.formType,
status: 'DRAFT',
@@ -129,6 +134,8 @@ test('POST /commands/client/hubs returns injected hub response', async () => {
latitude: 37.422,
longitude: -122.084,
geofenceRadiusMeters: 100,
clockInMode: 'GEO_REQUIRED',
allowClockInOverride: true,
costCenterId: '44444444-4444-4444-8444-444444444444',
});
@@ -176,6 +183,7 @@ test('POST /commands/staff/clock-in accepts shift-based payload', async () => {
sourceType: 'GEO',
latitude: 37.422,
longitude: -122.084,
overrideReason: 'GPS timed out near the hub',
});
assert.equal(res.status, 200);
@@ -197,6 +205,31 @@ test('POST /commands/staff/clock-out accepts assignment-based payload', async ()
assert.equal(res.body.status, 'CLOCK_OUT');
});
test('POST /commands/staff/location-streams accepts batched location payloads', async () => {
const app = createApp({ mobileCommandHandlers: createMobileHandlers() });
const res = await request(app)
.post('/commands/staff/location-streams')
.set('Authorization', 'Bearer test-token')
.set('Idempotency-Key', 'location-stream-1')
.send({
assignmentId: '99999999-9999-4999-8999-999999999999',
sourceType: 'GEO',
deviceId: 'iphone-15',
points: [
{
capturedAt: '2026-03-16T08:00:00.000Z',
latitude: 37.422,
longitude: -122.084,
accuracyMeters: 12,
},
],
});
assert.equal(res.status, 200);
assert.equal(res.body.status, 'RECORDED');
assert.equal(res.body.pointCount, 1);
});
test('PUT /commands/staff/profile/tax-forms/:formType uppercases form type', async () => {
const app = createApp({ mobileCommandHandlers: createMobileHandlers() });
const res = await request(app)