swiftops-backend / docs /agent /frontend /TICKET_COMPLETION_API.md
kamau1's picture
Fix dashboard and global work queue by correcting ticket_name usage, and ensuring overview endpoint returns active assignments.
99ef9a1
# Ticket Completion API Guide
## Overview
Dynamic completion forms generated from project requirements. Each project defines its own photo and activation requirements.
## Workflow
```
1. Agent arrives β†’ Record arrival
2. Get completion checklist β†’ See what's required
3. Upload photos β†’ Multiple times, different types
4. Submit activation data β†’ Equipment serials, etc.
5. Complete ticket β†’ PM reviews and invoices
```
---
## 1. Get Completion Checklist
**Endpoint:** `GET /api/v1/tickets/{ticket_id}/completion-checklist`
**Response:**
```json
{
"ticket_id": "uuid",
"project_id": "uuid",
"completion_percentage": 45.5,
"is_photos_complete": false,
"is_activation_complete": true,
"is_complete": false,
"photo_items": [
{
"id": "photo_Speedtest",
"type": "photo",
"photo_type": "Speedtest",
"label": "Speed test collected",
"required": true,
"min_photos": 1,
"max_photos": 1,
"uploaded_count": 0,
"uploaded_photos": [],
"status": "pending"
}
],
"field_items": [
{
"id": "field_ont_serial",
"type": "field",
"field_name": "ont_serial",
"label": "ONT Serial Number",
"data_type": "text",
"required": true,
"value": "HW12345678",
"status": "complete"
}
]
}
```
---
## 2. Upload Photos
**Endpoint:** `POST /api/v1/tickets/{ticket_id}/upload-photos`
**Content-Type:** `multipart/form-data`
**Form Data:**
- `photo_type`: string (matches photo_items[].photo_type from checklist)
- `files`: file[] (image files)
**Example:**
```javascript
const formData = new FormData();
formData.append('photo_type', 'Speedtest');
formData.append('files', file1);
formData.append('files', file2); // If multiple allowed
await api.post(`/tickets/${ticketId}/upload-photos`, formData);
```
**Response:**
```json
{
"success": true,
"message": "Photos updated successfully. Activation data still required.",
"ticket_id": "uuid",
"checklist": { /* updated checklist */ }
}
```
**Notes:**
- Can call multiple times for different photo types
- Photos stored with `image_type="completion"`
- Description includes photo type: `[Speedtest] Completion photo for ticket`
---
## 3. Submit Activation Data
**Endpoint:** `POST /api/v1/tickets/{ticket_id}/activation-data`
**Payload:**
```json
{
"ont_serial": "HW12345678",
"router_serial": "RT98765432",
"installation_notes": "Installed on second floor"
}
```
**Field names must match `field_items[].field_name` from checklist.**
**Response:**
```json
{
"success": true,
"message": "Activation data updated successfully",
"ticket_id": "uuid",
"checklist": { /* updated checklist */ }
}
```
**Notes:**
- Data stored in `ticket.completion_data` (JSONB)
- Used to create subscription on invoicing
- Equipment serials tracked in inventory
---
## 4. Complete Ticket
**Endpoint:** `POST /api/v1/tickets/{ticket_id}/complete`
**Payload:**
```json
{
"work_notes": "Installation completed successfully. Customer trained on WiFi setup.",
"force_complete": false
}
```
**Response:**
```json
{
"success": true,
"message": "Ticket completed successfully",
"ticket_id": "uuid",
"subscription_id": "uuid", // If created
"equipment_installed": ["HW12345678", "RT98765432"]
}
```
**Validation:**
- All required photos uploaded
- All required fields filled
- Agent must have arrived at location
- Set `force_complete: true` to bypass (admin only)
**Notes:**
- Ticket status β†’ `completed`
- Subscription NOT auto-created (PM does this on invoicing)
- Equipment marked as installed in inventory
---
## Error Responses
**400 Bad Request:**
```json
{
"detail": {
"message": "Photo validation failed",
"errors": [
"Speedtest: Requires 1 photo, got 0",
"ODU outdoor image: Requires 1 photo, got 0"
]
}
}
```
**404 Not Found:**
```json
{
"detail": "Ticket not found"
}
```
---
## Progressive Completion
Agent can complete in any order:
1. Upload some photos β†’ Save
2. Upload more photos later β†’ Save
3. Fill activation data β†’ Save
4. Upload remaining photos β†’ Save
5. Complete ticket β†’ Final
Each step updates `completion_percentage` in checklist.
---
## Example Flow
```javascript
// 1. Get checklist
const checklist = await api.get(`/tickets/${ticketId}/completion-checklist`);
// 2. Render form from checklist
checklist.photo_items.forEach(item => {
renderPhotoUpload(item.photo_type, item.label, item.required);
});
checklist.field_items.forEach(item => {
renderField(item.field_name, item.label, item.data_type, item.required);
});
// 3. Upload photos as agent takes them
async function uploadPhoto(photoType, file) {
const formData = new FormData();
formData.append('photo_type', photoType);
formData.append('files', file);
await api.post(`/tickets/${ticketId}/upload-photos`, formData);
// Refresh checklist to show progress
const updated = await api.get(`/tickets/${ticketId}/completion-checklist`);
updateProgress(updated.completion_percentage);
}
// 4. Submit activation data
async function submitActivationData(data) {
await api.post(`/tickets/${ticketId}/activation-data`, data);
// Refresh checklist
const updated = await api.get(`/tickets/${ticketId}/completion-checklist`);
updateProgress(updated.completion_percentage);
}
// 5. Complete ticket
async function completeTicket(workNotes) {
const result = await api.post(`/tickets/${ticketId}/complete`, {
work_notes: workNotes
});
showSuccess('Ticket completed!');
}
```
---
## Project Requirements Format
Projects define requirements in JSONB fields:
**photo_requirements:**
```json
[
{
"type": "Speedtest",
"required": true,
"min_photos": 1,
"max_photos": 1,
"description": "Speed test collected"
}
]
```
**activation_requirements:**
```json
[
{
"field": "ont_serial",
"label": "ONT Serial Number",
"type": "text",
"required": true,
"placeholder": "HW12345678"
}
]
```
Different projects = different requirements. Forms adapt automatically.