# Full Field Service Backend

This package contains the complete Express.js backend foundation for the React Native field-service application.

## Modules

1. Multi-company
2. JWT login
3. RBAC
4. Users and roles
5. Jobs
6. Job assignment
7. Job status workflow
8. Job notes
9. Materials and material requests
10. Material order creation
11. GPS tracking
12. Live Socket.IO GPS events
13. Circle geofencing
14. Geofence-enforced check-in/check-out
15. Before/During/After photos
16. Photo GPS metadata
17. Audit logging
18. Dashboard report
19. MySQL schema and seed scripts

## Installation

```bash
npm install
```

Copy `.env.example` to `.env` and configure MySQL.

Import SQL in this order:

```text
sql/001_schema.sql
sql/002_rbac_seed.sql
sql/003_demo_company.sql
```

Create a password hash:

```http
POST /api/auth/hash-password
Content-Type: application/json

{"password":"ChangeThisPassword123!"}
```

Put the returned bcrypt hash into `003_demo_company.sql`, uncomment the user/role statements, and run them.

Remove `/api/auth/hash-password` before production.

## Start

```bash
npm run dev
```

Production:

```bash
npm start
```

Health:

```http
GET /api/health
```

## JWT

Login:

```http
POST /api/auth/login
Content-Type: application/json

{"email":"admin@demo.com","password":"ChangeThisPassword123!"}
```

For protected APIs:

```text
Authorization: Bearer <token>
```

## Job APIs

```text
GET    /api/jobs
GET    /api/jobs/:id
POST   /api/jobs
POST   /api/jobs/:id/assign
GET    /api/jobs/:id/assignments
PATCH  /api/jobs/:id/status
POST   /api/jobs/:id/notes
GET    /api/jobs/:id/notes
```

Example status:

```json
{"status":"IN_PROGRESS","reason":"Technician started work"}
```

## GPS

```text
POST /api/location
GET  /api/location/history/:userId
```

Mobile payload:

```json
{
  "job_id": 10,
  "device_id": "android-001",
  "latitude": 12.971598,
  "longitude": 77.594566,
  "accuracy": 8.4,
  "speed": 2.1,
  "heading": 180,
  "battery_level": 77
}
```

Socket.IO clients can join:

```text
join_company(companyId)
```

and receive:

```text
location_update
```

## Geofence

Create:

```http
POST /api/geofences
```

```json
{
  "name":"Customer Site A",
  "latitude":12.971598,
  "longitude":77.594566,
  "radius":150
}
```

Check:

```http
POST /api/geofences/check
```

```json
{
  "geofence_id":1,
  "latitude":12.972000,
  "longitude":77.594700
}
```

The response returns distance and `inside`.

## Check-in/check-out

```text
POST /api/checkin/:jobId/check-in
POST /api/checkin/:jobId/check-out
```

The backend checks the job's `required_geofence_id`.

Normal request:

```json
{
  "latitude":12.971600,
  "longitude":77.594570,
  "accuracy":8
}
```

If outside the geofence, check-in is rejected.

An authorized workflow can send:

```json
{
  "latitude":12.971600,
  "longitude":77.594570,
  "accuracy":8,
  "manual_override":true,
  "override_reason":"GPS signal unavailable at site"
}
```

The override is recorded for audit.

## Photos

Multipart:

```text
POST /api/media/job/:jobId
```

Fields:

```text
photo
media_type = BEFORE | DURING | AFTER
latitude
longitude
gps_accuracy
captured_at
device_id
description
```

Example React Native:

```js
const form = new FormData();

form.append("photo", {
  uri: photo.uri,
  name: "before.jpg",
  type: "image/jpeg"
});

form.append("media_type", "BEFORE");
form.append("latitude", String(latitude));
form.append("longitude", String(longitude));
form.append("gps_accuracy", String(accuracy));

await axios.post(`${API_URL}/api/media/job/${jobId}`, form, {
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "multipart/form-data"
  }
});
```

List:

```text
GET /api/media/job/:jobId
```

## Materials

```text
GET  /api/materials
POST /api/materials/request
POST /api/materials/orders
GET  /api/materials/orders
```

Material orders store order/request information; this backend does not automatically create inventory entries.

## Admin

```text
GET  /api/admin/users
POST /api/admin/users
GET  /api/admin/roles
GET  /api/admin/permissions
```

## Dashboard

```text
GET /api/reports/dashboard
```

## Important production architecture

Recommended deployment:

```text
Internet
   |
HTTPS / Nginx or Apache
   |
Express.js + Socket.IO
   |
MySQL
   |
Object storage / protected file storage
```

Do not expose MySQL publicly.

For React Native background GPS, the mobile application must request the appropriate Android/iOS location permissions and send location updates to `/api/location`. The server cannot make a phone provide GPS by itself.

For high-volume GPS data, partition/archive `location_logs`.

For production photos, consider S3-compatible/object storage rather than storing many files directly on the application server.

The SQL schema deliberately keeps company_id on operational tables so one company cannot accidentally read another company's records when controllers use the company from the JWT.
