Spaces:
Sleeping
Sleeping
| # 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. | |