Skip to main content

Field Service

Overview

The Field Service handles worker clock in/out, timesheet management, and GPS validation for field operations.

Base URL

http://localhost:5003/api

Authentication

Cookie: sAccessToken=...; sRefreshToken=...

Clock In/Out

Clock In Worker

POST /api/timesheets/clock-in
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"workerId": "uuid",
"projectId": "uuid",
"scopeId": "uuid",
"pin": "1234",
"gpsLat": 29.4241,
"gpsLong": -98.4936
}

Roles: FOREMAN, ADMIN

Validations:

  • ✅ PIN must match worker's PIN hash
  • ✅ GPS coordinates must be within project geofence (if enabled)
  • ✅ Worker cannot have active timesheet already

Clock Out Worker

POST /api/timesheets/clock-out
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"timesheetId": "uuid",
"gpsLat": 29.4241,
"gpsLong": -98.4936
}

Roles: FOREMAN, ADMIN

Auto-Calculations:

  • Total hours
  • Regular hours
  • Overtime hours (if applicable)

Get Active Timesheets

GET /api/timesheets/active
Cookie: sAccessToken=...; sRefreshToken=...

Roles: FOREMAN, ADMIN

Timesheet Management

List Timesheets

GET /api/timesheets?projectId=uuid&status=APPROVED
Cookie: sAccessToken=...; sRefreshToken=...

Roles: FOREMAN, PM, ADMIN

Query Parameters:

  • projectId - Filter by project
  • workerId - Filter by worker
  • status - Filter by status (ACTIVE, PENDING_APPROVAL, APPROVED, REJECTED)
  • startDate - Filter by date range start
  • endDate - Filter by date range end

Get Timesheet

GET /api/timesheets/:id
Cookie: sAccessToken=...; sRefreshToken=...

Roles: FOREMAN, PM, ADMIN


Update Timesheet

PUT /api/timesheets/:id
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"scopeId": "new-scope-uuid",
"notes": "Moved to different scope"
}

Roles: FOREMAN, ADMIN


Delete Timesheet

DELETE /api/timesheets/:id
Cookie: sAccessToken=...; sRefreshToken=...

Roles: ADMIN


Approve Timesheet

POST /api/timesheets/:id/approve
Cookie: sAccessToken=...; sRefreshToken=...

Roles: PM, ADMIN


Reject Timesheet

POST /api/timesheets/:id/reject
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"reason": "Incorrect hours logged"
}

Roles: PM, ADMIN

Workers

List Workers

GET /api/workers
Cookie: sAccessToken=...; sRefreshToken=...

Roles: FOREMAN, PM, ADMIN

info

PIN hashes are NEVER returned in API responses for security.


Get Worker

GET /api/workers/:id
Cookie: sAccessToken=...; sRefreshToken=...

Roles: FOREMAN, PM, ADMIN


Create Worker

POST /api/workers
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"name": "Jane Doe",
"employeeNumber": "EMP-002",
"pin": "5678",
"status": "ACTIVE",
"slClStatus": "CL"
}

Roles: ADMIN

Required Fields:

  • name (string)
  • employeeNumber (string, unique)
  • pin (string, 4 digits) - Hashed on save
  • status (enum: ACTIVE, INACTIVE)
  • slClStatus (enum: SL, CL, BOTH)

Update Worker

PUT /api/workers/:id
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"name": "Jane D. Doe",
"status": "INACTIVE"
}

Roles: ADMIN


Delete Worker

DELETE /api/workers/:id
Cookie: sAccessToken=...; sRefreshToken=...

Roles: ADMIN


Update Worker PIN

PUT /api/workers/:id/pin
Cookie: sAccessToken=...; sRefreshToken=...
Content-Type: application/json

{
"pin": "9999"
}

Roles: ADMIN

warning

PINs are hashed and never stored or returned in plain text.

GPS Validation

Geofence Check

Projects can enable geofence validation with a radius (meters):

const distance = calculateDistance(
worker.gpsLat, worker.gpsLong,
project.locationLat, project.locationLong
)

if (distance > project.geofenceRadius) {
throw new Error("GPS location outside project geofence")
}

Bypass: ADMIN role can bypass geofence validation.

GPS Fields

FieldTypeDescription
gpsLatnumberLatitude (required)
gpsLongnumberLongitude (required)
gpsAccuracynumberAccuracy in meters (optional)

Data Models

interface Timesheet {
id: string
timesheetId: string // TS-YYYY-###
workerId: string
projectId: string
scopeId: string | null
clockInTime: DateTime
clockOutTime: DateTime | null
clockInGPS: {
lat: number
long: number
accuracy: number | null
}
clockOutGPS: {
lat: number
long: number
accuracy: number | null
} | null
totalHours: number | null
regularHours: number | null
overtimeHours: number | null
status: TimesheetStatus
notes: string | null
createdAt: DateTime
updatedAt: DateTime
}

interface Worker {
id: string
name: string
employeeNumber: string // Unique
pinHash: string // NEVER returned in API
status: WorkerStatus
slClStatus: SLCLStatus
createdAt: DateTime
updatedAt: DateTime
}

enum TimesheetStatus {
ACTIVE
PENDING_APPROVAL
APPROVED
REJECTED
}

enum WorkerStatus {
ACTIVE
INACTIVE
}

enum SLCLStatus {
SL // Skilled Labor
CL // Common Labor
BOTH // Both
}

Role-Based Access

RolePermissions
FOREMANClock in/out workers, view timesheets
PMApprove/reject timesheets, view all
ADMINFull access, manage workers, bypass GPS