Spaces:
Sleeping
Sleeping
feat: tickets detail view
Browse files- docs/agent/frontend/TICKET_DETAIL_API.md +204 -0
- docs/devlogs/browser/response.json +201 -691
- src/app/api/v1/tickets.py +234 -0
- src/app/services/project_service.py +41 -4
docs/agent/frontend/TICKET_DETAIL_API.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ticket Detail API
|
| 2 |
+
|
| 3 |
+
## Endpoint
|
| 4 |
+
`GET /api/v1/tickets/{ticket_id}/detail`
|
| 5 |
+
|
| 6 |
+
## Response
|
| 7 |
+
```json
|
| 8 |
+
{
|
| 9 |
+
"ticket": {
|
| 10 |
+
"id": "uuid",
|
| 11 |
+
"ticket_name": "Peter Otieno",
|
| 12 |
+
"ticket_type": "installation",
|
| 13 |
+
"service_type": "ftth",
|
| 14 |
+
"work_description": "Install Standard 20Mbps for Peter Otieno",
|
| 15 |
+
"status": "open",
|
| 16 |
+
"priority": "normal",
|
| 17 |
+
"scheduled_date": "2025-12-10",
|
| 18 |
+
"scheduled_time_slot": "10:00-14:00",
|
| 19 |
+
"notes": "Needs router configuration assistance",
|
| 20 |
+
...
|
| 21 |
+
},
|
| 22 |
+
"source_data": {
|
| 23 |
+
"type": "sales_order",
|
| 24 |
+
"id": "uuid",
|
| 25 |
+
"order_number": "SO-2024-001",
|
| 26 |
+
"customer_preferred_package": "Standard 20Mbps",
|
| 27 |
+
"installation_address": "123 Main St, Nairobi",
|
| 28 |
+
"preferred_visit_date": "2025-12-10",
|
| 29 |
+
"status": "pending",
|
| 30 |
+
"total_amount": 5000.00
|
| 31 |
+
},
|
| 32 |
+
"customer": {
|
| 33 |
+
"id": "uuid",
|
| 34 |
+
"name": "Peter Otieno",
|
| 35 |
+
"phone": "+254712345678",
|
| 36 |
+
"email": "peter@example.com",
|
| 37 |
+
"address": "123 Main St, Nairobi",
|
| 38 |
+
"location_latitude": -1.2921,
|
| 39 |
+
"location_longitude": 36.8219
|
| 40 |
+
},
|
| 41 |
+
"expenses": [
|
| 42 |
+
{
|
| 43 |
+
"id": "uuid",
|
| 44 |
+
"expense_type": "transport",
|
| 45 |
+
"description": "Fuel to customer site",
|
| 46 |
+
"quantity": 1,
|
| 47 |
+
"unit_cost": 500.00,
|
| 48 |
+
"total_cost": 500.00,
|
| 49 |
+
"is_approved": false,
|
| 50 |
+
"approved_at": null,
|
| 51 |
+
"created_at": "2025-11-27T10:00:00Z"
|
| 52 |
+
}
|
| 53 |
+
],
|
| 54 |
+
"images": [
|
| 55 |
+
{
|
| 56 |
+
"id": "uuid",
|
| 57 |
+
"image_url": "https://...",
|
| 58 |
+
"image_type": "before",
|
| 59 |
+
"caption": "Site before installation",
|
| 60 |
+
"uploaded_by_user_id": "uuid",
|
| 61 |
+
"created_at": "2025-11-27T10:00:00Z"
|
| 62 |
+
}
|
| 63 |
+
],
|
| 64 |
+
"comments": [
|
| 65 |
+
{
|
| 66 |
+
"id": "uuid",
|
| 67 |
+
"comment_text": "Customer confirmed availability",
|
| 68 |
+
"user_id": "uuid",
|
| 69 |
+
"user_name": "John Doe",
|
| 70 |
+
"created_at": "2025-11-27T10:00:00Z"
|
| 71 |
+
}
|
| 72 |
+
],
|
| 73 |
+
"assignments": [
|
| 74 |
+
{
|
| 75 |
+
"id": "uuid",
|
| 76 |
+
"user_id": "uuid",
|
| 77 |
+
"user_name": "Field Agent Name",
|
| 78 |
+
"action": "assigned",
|
| 79 |
+
"status": "PENDING",
|
| 80 |
+
"assigned_at": "2025-11-27T08:00:00Z",
|
| 81 |
+
"responded_at": null,
|
| 82 |
+
"journey_started_at": null,
|
| 83 |
+
"arrived_at": null,
|
| 84 |
+
"ended_at": null
|
| 85 |
+
}
|
| 86 |
+
]
|
| 87 |
+
}
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## Usage
|
| 91 |
+
|
| 92 |
+
```typescript
|
| 93 |
+
const response = await fetch(
|
| 94 |
+
`/api/v1/tickets/${ticketId}/detail`,
|
| 95 |
+
{ headers: { 'Authorization': `Bearer ${token}` } }
|
| 96 |
+
);
|
| 97 |
+
const data = await response.json();
|
| 98 |
+
|
| 99 |
+
// Display ticket info
|
| 100 |
+
<TicketHeader ticket={data.ticket} />
|
| 101 |
+
|
| 102 |
+
// Display customer info
|
| 103 |
+
{data.customer && (
|
| 104 |
+
<CustomerCard customer={data.customer} />
|
| 105 |
+
)}
|
| 106 |
+
|
| 107 |
+
// Display source data (sales order, task, incident, or subscription)
|
| 108 |
+
{data.source_data && (
|
| 109 |
+
<>
|
| 110 |
+
{data.source_data.type === 'sales_order' && (
|
| 111 |
+
<SalesOrderCard order={data.source_data} />
|
| 112 |
+
)}
|
| 113 |
+
{data.source_data.type === 'task' && (
|
| 114 |
+
<TaskCard task={data.source_data} />
|
| 115 |
+
)}
|
| 116 |
+
{data.source_data.type === 'incident' && (
|
| 117 |
+
<IncidentCard incident={data.source_data} />
|
| 118 |
+
)}
|
| 119 |
+
{data.source_data.type === 'subscription' && (
|
| 120 |
+
<SubscriptionCard subscription={data.source_data} />
|
| 121 |
+
)}
|
| 122 |
+
</>
|
| 123 |
+
)}
|
| 124 |
+
|
| 125 |
+
// Display expenses
|
| 126 |
+
<ExpensesList expenses={data.expenses} />
|
| 127 |
+
|
| 128 |
+
// Display images
|
| 129 |
+
<ImageGallery images={data.images} />
|
| 130 |
+
|
| 131 |
+
// Display comments
|
| 132 |
+
<CommentsList comments={data.comments} />
|
| 133 |
+
|
| 134 |
+
// Display assignments
|
| 135 |
+
<AssignmentTimeline assignments={data.assignments} />
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
## Source Data Types
|
| 139 |
+
|
| 140 |
+
### Sales Order (type: "sales_order")
|
| 141 |
+
```json
|
| 142 |
+
{
|
| 143 |
+
"type": "sales_order",
|
| 144 |
+
"id": "uuid",
|
| 145 |
+
"order_number": "SO-2024-001",
|
| 146 |
+
"customer_preferred_package": "Standard 20Mbps",
|
| 147 |
+
"installation_address": "123 Main St",
|
| 148 |
+
"preferred_visit_date": "2025-12-10",
|
| 149 |
+
"status": "pending",
|
| 150 |
+
"total_amount": 5000.00
|
| 151 |
+
}
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
### Task (type: "task")
|
| 155 |
+
```json
|
| 156 |
+
{
|
| 157 |
+
"type": "task",
|
| 158 |
+
"id": "uuid",
|
| 159 |
+
"task_title": "Install fiber backbone",
|
| 160 |
+
"task_description": "Install 10km fiber cable",
|
| 161 |
+
"task_type": "infrastructure",
|
| 162 |
+
"priority": "high",
|
| 163 |
+
"status": "in_progress",
|
| 164 |
+
"scheduled_date": "2025-12-10",
|
| 165 |
+
"due_date": "2025-12-15"
|
| 166 |
+
}
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
### Incident (type: "incident")
|
| 170 |
+
```json
|
| 171 |
+
{
|
| 172 |
+
"type": "incident",
|
| 173 |
+
"id": "uuid",
|
| 174 |
+
"incident_number": "INC-2024-001",
|
| 175 |
+
"incident_type": "service_outage",
|
| 176 |
+
"issue_description": "No internet connection",
|
| 177 |
+
"priority": "urgent",
|
| 178 |
+
"status": "open",
|
| 179 |
+
"reported_at": "2025-11-27T10:00:00Z"
|
| 180 |
+
}
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
### Subscription (type: "subscription")
|
| 184 |
+
```json
|
| 185 |
+
{
|
| 186 |
+
"type": "subscription",
|
| 187 |
+
"id": "uuid",
|
| 188 |
+
"subscription_number": "SUB-2024-001",
|
| 189 |
+
"service_type": "ftth",
|
| 190 |
+
"package_name": "Premium 100Mbps",
|
| 191 |
+
"status": "active",
|
| 192 |
+
"start_date": "2025-01-01",
|
| 193 |
+
"next_billing_date": "2025-12-01"
|
| 194 |
+
}
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## Notes
|
| 198 |
+
|
| 199 |
+
- `source_data` is null if no source linked
|
| 200 |
+
- `source_data.type` indicates the source type (sales_order, task, incident, subscription)
|
| 201 |
+
- `customer` is null if no customer linked (tasks don't have customers)
|
| 202 |
+
- Arrays are empty if no data exists
|
| 203 |
+
- All dates in ISO 8601 format
|
| 204 |
+
- Authorization: User must have access to ticket's project
|
docs/devlogs/browser/response.json
CHANGED
|
@@ -1,723 +1,233 @@
|
|
| 1 |
{
|
| 2 |
-
"
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
"agent_name": "Alex",
|
| 7 |
-
"agent_number": "+254783993562",
|
| 8 |
-
"customer_preferred_package": "100",
|
| 9 |
-
"package_price": "2000.00",
|
| 10 |
-
"installation_address_line1": "Kwa kina Joel",
|
| 11 |
-
"installation_address_line2": null,
|
| 12 |
-
"installation_maps_link": null,
|
| 13 |
-
"installation_latitude": null,
|
| 14 |
-
"installation_longitude": null,
|
| 15 |
-
"is_installation_required": true,
|
| 16 |
-
"preferred_visit_date": "2026-01-01",
|
| 17 |
-
"preferred_visit_time": "afternoon",
|
| 18 |
-
"received_date": null,
|
| 19 |
-
"received_time": null,
|
| 20 |
-
"notes": null,
|
| 21 |
-
"id": "b2a47ac2-e779-4479-846b-f350f041dd43",
|
| 22 |
-
"customer_id": "d43f2634-d381-47a1-a206-c6151e36668f",
|
| 23 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 24 |
-
"sales_agent_id": null,
|
| 25 |
-
"project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
|
| 26 |
-
"status": "processed",
|
| 27 |
-
"is_processed": true,
|
| 28 |
-
"is_ticket_created": true,
|
| 29 |
-
"processed_at": "2025-11-25T13:03:14.165168Z",
|
| 30 |
-
"processed_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 31 |
-
"cancellation_reason": null,
|
| 32 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 33 |
-
"submitted_at": "2025-11-25T12:34:30.459762Z",
|
| 34 |
-
"created_at": "2025-11-25T12:34:30.459762Z",
|
| 35 |
-
"updated_at": "2025-11-25T13:03:14.165168Z",
|
| 36 |
-
"customer_name": "Nnacy Wanjiru",
|
| 37 |
-
"customer_phone": "+254707622683",
|
| 38 |
-
"customer_email": "nancy@example.com",
|
| 39 |
-
"project_title": "Atomio Fttx",
|
| 40 |
-
"region_name": "Kimbo",
|
| 41 |
-
"sales_agent_name": "Alex",
|
| 42 |
-
"is_pending": false,
|
| 43 |
-
"is_cancelled": false,
|
| 44 |
-
"has_ticket": true,
|
| 45 |
-
"has_location": false,
|
| 46 |
-
"has_region_assignment": true,
|
| 47 |
-
"can_promote_to_ticket": false,
|
| 48 |
-
"can_cancel": false
|
| 49 |
-
},
|
| 50 |
-
{
|
| 51 |
-
"order_number": "ORD-2025-023",
|
| 52 |
-
"service_request_number": null,
|
| 53 |
-
"agent_name": "Sarah Mwangi",
|
| 54 |
-
"agent_number": "+254723456789",
|
| 55 |
-
"customer_preferred_package": "Premium Fiber 100Mbps",
|
| 56 |
-
"package_price": "8000.00",
|
| 57 |
-
"installation_address_line1": "1111 Kasarani Road",
|
| 58 |
-
"installation_address_line2": null,
|
| 59 |
-
"installation_maps_link": null,
|
| 60 |
-
"installation_latitude": null,
|
| 61 |
-
"installation_longitude": null,
|
| 62 |
-
"is_installation_required": true,
|
| 63 |
-
"preferred_visit_date": "2025-12-29",
|
| 64 |
-
"preferred_visit_time": "09:00",
|
| 65 |
-
"received_date": "2025-11-25",
|
| 66 |
-
"received_time": "15:00:00",
|
| 67 |
-
"notes": "Duplicate customer - second location",
|
| 68 |
-
"id": "99dab688-6f3c-4f41-82b7-5b5508e60453",
|
| 69 |
-
"customer_id": "40bee251-a186-4194-9d01-e74597dc8326",
|
| 70 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 71 |
-
"sales_agent_id": null,
|
| 72 |
-
"project_region_id": null,
|
| 73 |
-
"status": "pending",
|
| 74 |
-
"is_processed": false,
|
| 75 |
-
"is_ticket_created": false,
|
| 76 |
-
"processed_at": null,
|
| 77 |
-
"processed_by_user_id": null,
|
| 78 |
-
"cancellation_reason": null,
|
| 79 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 80 |
-
"submitted_at": "2025-11-25T10:20:39.340044Z",
|
| 81 |
-
"created_at": "2025-11-25T10:20:39.340044Z",
|
| 82 |
-
"updated_at": "2025-11-25T10:20:39.340044Z",
|
| 83 |
-
"customer_name": "Catherine Njoki",
|
| 84 |
-
"customer_phone": "+254734567891",
|
| 85 |
-
"customer_email": "catherine.njoki@email.com",
|
| 86 |
-
"project_title": "Atomio Fttx",
|
| 87 |
-
"region_name": null,
|
| 88 |
-
"sales_agent_name": "Sarah Mwangi",
|
| 89 |
-
"is_pending": true,
|
| 90 |
-
"is_cancelled": false,
|
| 91 |
-
"has_ticket": false,
|
| 92 |
-
"has_location": false,
|
| 93 |
-
"has_region_assignment": false,
|
| 94 |
-
"can_promote_to_ticket": true,
|
| 95 |
-
"can_cancel": true
|
| 96 |
-
},
|
| 97 |
-
{
|
| 98 |
-
"order_number": "ORD-2025-022",
|
| 99 |
-
"service_request_number": null,
|
| 100 |
-
"agent_name": "Mike Ochieng",
|
| 101 |
-
"agent_number": "+254712345678",
|
| 102 |
-
"customer_preferred_package": "Premium Fiber 50Mbps",
|
| 103 |
-
"package_price": "5000.00",
|
| 104 |
-
"installation_address_line1": "456 Westlands Avenue",
|
| 105 |
-
"installation_address_line2": null,
|
| 106 |
-
"installation_maps_link": null,
|
| 107 |
-
"installation_latitude": null,
|
| 108 |
-
"installation_longitude": null,
|
| 109 |
-
"is_installation_required": true,
|
| 110 |
-
"preferred_visit_date": "2025-12-27",
|
| 111 |
-
"preferred_visit_time": "11:00",
|
| 112 |
-
"received_date": "2025-11-25",
|
| 113 |
-
"received_time": "14:00:00",
|
| 114 |
-
"notes": "Duplicate customer - upgrade request",
|
| 115 |
-
"id": "cebea221-402f-49a3-8418-bbcf39697488",
|
| 116 |
-
"customer_id": "88745284-05f7-4a2a-acaf-6a39df45d36b",
|
| 117 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 118 |
-
"sales_agent_id": null,
|
| 119 |
-
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 120 |
-
"status": "processed",
|
| 121 |
-
"is_processed": true,
|
| 122 |
-
"is_ticket_created": true,
|
| 123 |
-
"processed_at": "2025-11-25T13:05:40.888237Z",
|
| 124 |
-
"processed_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 125 |
-
"cancellation_reason": null,
|
| 126 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 127 |
-
"submitted_at": "2025-11-25T10:20:38.505319Z",
|
| 128 |
-
"created_at": "2025-11-25T10:20:38.505319Z",
|
| 129 |
-
"updated_at": "2025-11-25T13:05:40.888261Z",
|
| 130 |
-
"customer_name": "Jane Wanjiru",
|
| 131 |
-
"customer_phone": "+254723456789",
|
| 132 |
-
"customer_email": "jane.wanjiru@email.com",
|
| 133 |
-
"project_title": "Atomio Fttx",
|
| 134 |
-
"region_name": "Test",
|
| 135 |
-
"sales_agent_name": "Mike Ochieng",
|
| 136 |
-
"is_pending": false,
|
| 137 |
-
"is_cancelled": false,
|
| 138 |
-
"has_ticket": true,
|
| 139 |
-
"has_location": false,
|
| 140 |
-
"has_region_assignment": true,
|
| 141 |
-
"can_promote_to_ticket": false,
|
| 142 |
-
"can_cancel": false
|
| 143 |
-
},
|
| 144 |
{
|
| 145 |
-
"
|
| 146 |
-
"service_request_number": null,
|
| 147 |
-
"agent_name": "Mike Ochieng",
|
| 148 |
-
"agent_number": "+254712345678",
|
| 149 |
-
"customer_preferred_package": "Premium Fiber 100Mbps",
|
| 150 |
-
"package_price": "8000.00",
|
| 151 |
-
"installation_address_line1": "1717 Runda Estate",
|
| 152 |
-
"installation_address_line2": "Villa 20",
|
| 153 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.210,36.800",
|
| 154 |
-
"installation_latitude": -1.21,
|
| 155 |
-
"installation_longitude": 36.8,
|
| 156 |
-
"is_installation_required": true,
|
| 157 |
-
"preferred_visit_date": "2025-12-23",
|
| 158 |
-
"preferred_visit_time": "14:00",
|
| 159 |
-
"received_date": "2025-11-25",
|
| 160 |
-
"received_time": "12:00:00",
|
| 161 |
-
"notes": "High-end residential area",
|
| 162 |
-
"id": "dfd0b969-ca77-4433-9791-9339a79a1807",
|
| 163 |
-
"customer_id": "e3b4dd99-0888-4b52-8edc-24d8fb51646a",
|
| 164 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 165 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 167 |
-
"
|
| 168 |
-
"
|
| 169 |
-
"
|
| 170 |
-
"
|
| 171 |
-
"
|
| 172 |
-
"
|
| 173 |
-
"
|
| 174 |
-
"
|
| 175 |
-
"
|
| 176 |
-
"
|
| 177 |
-
"
|
| 178 |
-
"
|
| 179 |
-
"
|
| 180 |
-
"
|
| 181 |
-
"region_name": "Test",
|
| 182 |
-
"sales_agent_name": "Mike Ochieng",
|
| 183 |
-
"is_pending": false,
|
| 184 |
"is_cancelled": false,
|
| 185 |
-
"
|
| 186 |
-
"
|
| 187 |
-
"
|
| 188 |
-
"
|
| 189 |
-
"
|
|
|
|
|
|
|
|
|
|
| 190 |
},
|
| 191 |
{
|
| 192 |
-
"
|
| 193 |
-
"service_request_number": null,
|
| 194 |
-
"agent_name": "Mike Ochieng",
|
| 195 |
-
"agent_number": "+254712345678",
|
| 196 |
-
"customer_preferred_package": "Standard 20Mbps",
|
| 197 |
-
"package_price": "3500.00",
|
| 198 |
-
"installation_address_line1": "1515 Karen Close",
|
| 199 |
-
"installation_address_line2": "House 8",
|
| 200 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.320,36.710",
|
| 201 |
-
"installation_latitude": -1.32,
|
| 202 |
-
"installation_longitude": 36.71,
|
| 203 |
-
"is_installation_required": true,
|
| 204 |
-
"preferred_visit_date": "2025-12-19",
|
| 205 |
-
"preferred_visit_time": "15:00",
|
| 206 |
-
"received_date": "2025-11-25",
|
| 207 |
-
"received_time": "10:30:00",
|
| 208 |
-
"notes": "Referral from existing customer",
|
| 209 |
-
"id": "29417f9e-918f-4efb-8a02-0702c5ca14ff",
|
| 210 |
-
"customer_id": "7d146d24-d866-4655-bcc7-e8b98968a85a",
|
| 211 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 212 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
"project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
|
| 214 |
-
"
|
| 215 |
-
"
|
| 216 |
-
"
|
| 217 |
-
"processed_at": null,
|
| 218 |
-
"processed_by_user_id": null,
|
| 219 |
-
"cancellation_reason": null,
|
| 220 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 221 |
-
"submitted_at": "2025-11-25T10:20:36.041956Z",
|
| 222 |
-
"created_at": "2025-11-25T10:20:36.041956Z",
|
| 223 |
-
"updated_at": "2025-11-25T10:20:36.041956Z",
|
| 224 |
-
"customer_name": "Sarah Wangari",
|
| 225 |
-
"customer_phone": "+254712345679",
|
| 226 |
-
"customer_email": "sarah.wangari@email.com",
|
| 227 |
-
"project_title": "Atomio Fttx",
|
| 228 |
-
"region_name": "Kimbo",
|
| 229 |
-
"sales_agent_name": "Mike Ochieng",
|
| 230 |
-
"is_pending": true,
|
| 231 |
-
"is_cancelled": false,
|
| 232 |
-
"has_ticket": false,
|
| 233 |
-
"has_location": true,
|
| 234 |
-
"has_region_assignment": true,
|
| 235 |
-
"can_promote_to_ticket": true,
|
| 236 |
-
"can_cancel": true
|
| 237 |
-
},
|
| 238 |
-
{
|
| 239 |
-
"order_number": "ORD-2025-016",
|
| 240 |
-
"service_request_number": null,
|
| 241 |
-
"agent_name": "Mike Ochieng",
|
| 242 |
-
"agent_number": "+254712345678",
|
| 243 |
-
"customer_preferred_package": "Premium Fiber 100Mbps",
|
| 244 |
-
"package_price": "8000.00",
|
| 245 |
-
"installation_address_line1": "1313 Ridgeways Close",
|
| 246 |
-
"installation_address_line2": "Gate 5",
|
| 247 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.215,36.815",
|
| 248 |
-
"installation_latitude": -1.215,
|
| 249 |
-
"installation_longitude": 36.815,
|
| 250 |
-
"is_installation_required": true,
|
| 251 |
-
"preferred_visit_date": "2025-12-11",
|
| 252 |
-
"preferred_visit_time": null,
|
| 253 |
-
"received_date": "2025-11-24",
|
| 254 |
-
"received_time": "15:30:00",
|
| 255 |
-
"notes": "Premium customer - white glove service required",
|
| 256 |
-
"id": "c93b28c0-d7bf-4f57-b750-d0c6864543b0",
|
| 257 |
-
"customer_id": "a9bccac9-1513-412c-be40-71ea7be24e9a",
|
| 258 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 259 |
-
"sales_agent_id": null,
|
| 260 |
-
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 261 |
-
"status": "pending",
|
| 262 |
-
"is_processed": false,
|
| 263 |
-
"is_ticket_created": false,
|
| 264 |
-
"processed_at": null,
|
| 265 |
-
"processed_by_user_id": null,
|
| 266 |
-
"cancellation_reason": null,
|
| 267 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 268 |
-
"submitted_at": "2025-11-25T10:20:34.652101Z",
|
| 269 |
-
"created_at": "2025-11-25T10:20:34.653103Z",
|
| 270 |
-
"updated_at": "2025-11-25T10:20:34.653103Z",
|
| 271 |
-
"customer_name": "Elizabeth Muthoni",
|
| 272 |
-
"customer_phone": "+254756789013",
|
| 273 |
-
"customer_email": "elizabeth.muthoni@email.com",
|
| 274 |
-
"project_title": "Atomio Fttx",
|
| 275 |
-
"region_name": "Test",
|
| 276 |
-
"sales_agent_name": "Mike Ochieng",
|
| 277 |
-
"is_pending": true,
|
| 278 |
-
"is_cancelled": false,
|
| 279 |
-
"has_ticket": false,
|
| 280 |
-
"has_location": true,
|
| 281 |
-
"has_region_assignment": true,
|
| 282 |
-
"can_promote_to_ticket": true,
|
| 283 |
-
"can_cancel": true
|
| 284 |
-
},
|
| 285 |
-
{
|
| 286 |
-
"order_number": "ORD-2025-014",
|
| 287 |
-
"service_request_number": null,
|
| 288 |
-
"agent_name": "Mike Ochieng",
|
| 289 |
-
"agent_number": "+254712345678",
|
| 290 |
-
"customer_preferred_package": "Basic 10Mbps",
|
| 291 |
-
"package_price": "2500.00",
|
| 292 |
-
"installation_address_line1": "1111 Kasarani Road",
|
| 293 |
-
"installation_address_line2": null,
|
| 294 |
-
"installation_maps_link": null,
|
| 295 |
-
"installation_latitude": null,
|
| 296 |
-
"installation_longitude": null,
|
| 297 |
-
"is_installation_required": true,
|
| 298 |
-
"preferred_visit_date": "2025-12-28",
|
| 299 |
-
"preferred_visit_time": "13:00",
|
| 300 |
-
"received_date": "2025-11-24",
|
| 301 |
-
"received_time": "14:00:00",
|
| 302 |
"notes": null,
|
| 303 |
-
"
|
| 304 |
-
"
|
| 305 |
-
"
|
| 306 |
-
"
|
| 307 |
-
"
|
| 308 |
-
"
|
| 309 |
-
"
|
| 310 |
-
"
|
| 311 |
-
"
|
| 312 |
-
"
|
| 313 |
-
"cancellation_reason": null,
|
| 314 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 315 |
-
"submitted_at": "2025-11-25T10:20:33.433223Z",
|
| 316 |
-
"created_at": "2025-11-25T10:20:33.433223Z",
|
| 317 |
-
"updated_at": "2025-11-25T10:20:33.433223Z",
|
| 318 |
-
"customer_name": "Catherine Njoki",
|
| 319 |
-
"customer_phone": "+254734567891",
|
| 320 |
-
"customer_email": "catherine.njoki@email.com",
|
| 321 |
-
"project_title": "Atomio Fttx",
|
| 322 |
-
"region_name": "Kimbo",
|
| 323 |
-
"sales_agent_name": "Mike Ochieng",
|
| 324 |
-
"is_pending": true,
|
| 325 |
-
"is_cancelled": false,
|
| 326 |
-
"has_ticket": false,
|
| 327 |
-
"has_location": false,
|
| 328 |
-
"has_region_assignment": true,
|
| 329 |
-
"can_promote_to_ticket": true,
|
| 330 |
-
"can_cancel": true
|
| 331 |
-
},
|
| 332 |
-
{
|
| 333 |
-
"order_number": "ORD-2025-015",
|
| 334 |
-
"service_request_number": null,
|
| 335 |
-
"agent_name": "Mike Ochieng",
|
| 336 |
-
"agent_number": "+254712345678",
|
| 337 |
-
"customer_preferred_package": "Premium Fiber 100Mbps",
|
| 338 |
-
"package_price": "8000.00",
|
| 339 |
-
"installation_address_line1": "1313 Ridgeways Close",
|
| 340 |
-
"installation_address_line2": "Gate 5",
|
| 341 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.215,36.815",
|
| 342 |
-
"installation_latitude": -1.215,
|
| 343 |
-
"installation_longitude": 36.815,
|
| 344 |
-
"is_installation_required": true,
|
| 345 |
-
"preferred_visit_date": "2025-12-11",
|
| 346 |
-
"preferred_visit_time": "Anytime",
|
| 347 |
-
"received_date": "2025-11-24",
|
| 348 |
-
"received_time": null,
|
| 349 |
-
"notes": "Premium customer - white glove service required",
|
| 350 |
-
"id": "a1d04741-b1b5-4a9e-baf6-d1e186886122",
|
| 351 |
-
"customer_id": "a9bccac9-1513-412c-be40-71ea7be24e9a",
|
| 352 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 353 |
-
"sales_agent_id": null,
|
| 354 |
-
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 355 |
-
"status": "pending",
|
| 356 |
-
"is_processed": false,
|
| 357 |
-
"is_ticket_created": false,
|
| 358 |
-
"processed_at": null,
|
| 359 |
-
"processed_by_user_id": null,
|
| 360 |
-
"cancellation_reason": null,
|
| 361 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 362 |
-
"submitted_at": "2025-11-25T09:13:26.574163Z",
|
| 363 |
-
"created_at": "2025-11-25T09:13:26.575164Z",
|
| 364 |
-
"updated_at": "2025-11-25T09:13:26.575164Z",
|
| 365 |
-
"customer_name": "Elizabeth Muthoni",
|
| 366 |
-
"customer_phone": "+254756789013",
|
| 367 |
-
"customer_email": "elizabeth.muthoni@email.com",
|
| 368 |
-
"project_title": "Atomio Fttx",
|
| 369 |
-
"region_name": "Test",
|
| 370 |
-
"sales_agent_name": "Mike Ochieng",
|
| 371 |
-
"is_pending": true,
|
| 372 |
-
"is_cancelled": false,
|
| 373 |
-
"has_ticket": false,
|
| 374 |
-
"has_location": true,
|
| 375 |
-
"has_region_assignment": true,
|
| 376 |
-
"can_promote_to_ticket": true,
|
| 377 |
-
"can_cancel": true
|
| 378 |
-
},
|
| 379 |
-
{
|
| 380 |
-
"order_number": "ORD-2025-012",
|
| 381 |
-
"service_request_number": null,
|
| 382 |
-
"agent_name": "Sarah Mwangi",
|
| 383 |
-
"agent_number": "+254723456789",
|
| 384 |
-
"customer_preferred_package": "Premium Fiber 50Mbps",
|
| 385 |
-
"package_price": "5000.00",
|
| 386 |
-
"installation_address_line1": "1010 Spring Valley Lane",
|
| 387 |
-
"installation_address_line2": "House 22",
|
| 388 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.265,36.795",
|
| 389 |
-
"installation_latitude": -1.265,
|
| 390 |
-
"installation_longitude": 36.795,
|
| 391 |
-
"is_installation_required": true,
|
| 392 |
-
"preferred_visit_date": "2025-12-22",
|
| 393 |
-
"preferred_visit_time": "Morning",
|
| 394 |
-
"received_date": "2025-11-24",
|
| 395 |
-
"received_time": null,
|
| 396 |
-
"notes": "New construction - check for electricity",
|
| 397 |
-
"id": "4bbe8e66-b1c2-476c-af4a-ed19d3281e3a",
|
| 398 |
-
"customer_id": "3f9a7a22-b683-4abf-ac5d-4bf368fab250",
|
| 399 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 400 |
-
"sales_agent_id": null,
|
| 401 |
-
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 402 |
-
"status": "pending",
|
| 403 |
-
"is_processed": false,
|
| 404 |
-
"is_ticket_created": false,
|
| 405 |
-
"processed_at": null,
|
| 406 |
-
"processed_by_user_id": null,
|
| 407 |
-
"cancellation_reason": null,
|
| 408 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 409 |
-
"submitted_at": "2025-11-25T09:13:24.338526Z",
|
| 410 |
-
"created_at": "2025-11-25T09:13:24.338526Z",
|
| 411 |
-
"updated_at": "2025-11-25T09:13:24.338526Z",
|
| 412 |
-
"customer_name": "Robert Kimani",
|
| 413 |
-
"customer_phone": "+254723456780",
|
| 414 |
-
"customer_email": "robert.kimani@email.com",
|
| 415 |
-
"project_title": "Atomio Fttx",
|
| 416 |
-
"region_name": "Test",
|
| 417 |
-
"sales_agent_name": "Sarah Mwangi",
|
| 418 |
-
"is_pending": true,
|
| 419 |
"is_cancelled": false,
|
| 420 |
-
"
|
| 421 |
-
"
|
| 422 |
-
"
|
| 423 |
-
"
|
| 424 |
-
"
|
|
|
|
|
|
|
|
|
|
| 425 |
},
|
| 426 |
{
|
| 427 |
-
"
|
| 428 |
-
"service_request_number": null,
|
| 429 |
-
"agent_name": "Mike Ochieng",
|
| 430 |
-
"agent_number": "+254712345678",
|
| 431 |
-
"customer_preferred_package": "Premium Fiber 100Mbps",
|
| 432 |
-
"package_price": "8000.00",
|
| 433 |
-
"installation_address_line1": "707 Runda Gardens",
|
| 434 |
-
"installation_address_line2": "Villa 15",
|
| 435 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.235,36.805",
|
| 436 |
-
"installation_latitude": -1.235,
|
| 437 |
-
"installation_longitude": 36.805,
|
| 438 |
-
"is_installation_required": true,
|
| 439 |
-
"preferred_visit_date": "2025-12-25",
|
| 440 |
-
"preferred_visit_time": "Anytime",
|
| 441 |
-
"received_date": "2025-11-24",
|
| 442 |
-
"received_time": null,
|
| 443 |
-
"notes": "Holiday season installation",
|
| 444 |
-
"id": "0091e819-ec49-4966-8632-52f965f3e24d",
|
| 445 |
-
"customer_id": "128a3846-d5b3-46a8-9f1e-657ffbcbb0e3",
|
| 446 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 447 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 449 |
-
"
|
| 450 |
-
"
|
| 451 |
-
"
|
| 452 |
-
"processed_at": null,
|
| 453 |
-
"processed_by_user_id": null,
|
| 454 |
-
"cancellation_reason": null,
|
| 455 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 456 |
-
"submitted_at": "2025-11-25T09:13:22.938543Z",
|
| 457 |
-
"created_at": "2025-11-25T09:13:22.938543Z",
|
| 458 |
-
"updated_at": "2025-11-25T09:13:22.938543Z",
|
| 459 |
-
"customer_name": "Nancy Wairimu",
|
| 460 |
-
"customer_phone": "+254790123456",
|
| 461 |
-
"customer_email": "nancy.wairimu@email.com",
|
| 462 |
-
"project_title": "Atomio Fttx",
|
| 463 |
-
"region_name": "Test",
|
| 464 |
-
"sales_agent_name": "Mike Ochieng",
|
| 465 |
-
"is_pending": true,
|
| 466 |
-
"is_cancelled": false,
|
| 467 |
-
"has_ticket": false,
|
| 468 |
-
"has_location": true,
|
| 469 |
-
"has_region_assignment": true,
|
| 470 |
-
"can_promote_to_ticket": true,
|
| 471 |
-
"can_cancel": true
|
| 472 |
-
},
|
| 473 |
-
{
|
| 474 |
-
"order_number": "ORD-2025-007",
|
| 475 |
-
"service_request_number": null,
|
| 476 |
-
"agent_name": "Sarah Mwangi",
|
| 477 |
-
"agent_number": "+254723456789",
|
| 478 |
-
"customer_preferred_package": "Premium Fiber 50Mbps",
|
| 479 |
-
"package_price": "5000.00",
|
| 480 |
-
"installation_address_line1": "404 Upperhill Close",
|
| 481 |
-
"installation_address_line2": null,
|
| 482 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.287,36.818",
|
| 483 |
-
"installation_latitude": -1.287,
|
| 484 |
-
"installation_longitude": 36.818,
|
| 485 |
-
"is_installation_required": true,
|
| 486 |
-
"preferred_visit_date": "2025-12-12",
|
| 487 |
-
"preferred_visit_time": "Morning",
|
| 488 |
-
"received_date": "2025-11-24",
|
| 489 |
-
"received_time": null,
|
| 490 |
"notes": null,
|
| 491 |
-
"
|
| 492 |
-
"
|
| 493 |
-
"
|
| 494 |
-
"
|
| 495 |
-
"
|
| 496 |
-
"
|
| 497 |
-
"
|
| 498 |
-
"
|
| 499 |
-
"
|
| 500 |
-
"
|
| 501 |
-
"cancellation_reason": null,
|
| 502 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 503 |
-
"submitted_at": "2025-11-25T09:13:21.172174Z",
|
| 504 |
-
"created_at": "2025-11-25T09:13:21.172174Z",
|
| 505 |
-
"updated_at": "2025-11-25T09:13:21.172174Z",
|
| 506 |
-
"customer_name": "James Kipchoge",
|
| 507 |
-
"customer_phone": "+254767890123",
|
| 508 |
-
"customer_email": "james.kipchoge@email.com",
|
| 509 |
-
"project_title": "Atomio Fttx",
|
| 510 |
-
"region_name": "Test",
|
| 511 |
-
"sales_agent_name": "Sarah Mwangi",
|
| 512 |
-
"is_pending": true,
|
| 513 |
"is_cancelled": false,
|
| 514 |
-
"
|
| 515 |
-
"
|
| 516 |
-
"
|
| 517 |
-
"
|
| 518 |
-
"
|
|
|
|
|
|
|
|
|
|
| 519 |
},
|
| 520 |
{
|
| 521 |
-
"
|
| 522 |
-
"service_request_number": null,
|
| 523 |
-
"agent_name": "Sarah Mwangi",
|
| 524 |
-
"agent_number": "+254723456789",
|
| 525 |
-
"customer_preferred_package": "Standard 20Mbps",
|
| 526 |
-
"package_price": "3500.00",
|
| 527 |
-
"installation_address_line1": "789 Ngong Road",
|
| 528 |
-
"installation_address_line2": null,
|
| 529 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.292066,36.782421",
|
| 530 |
-
"installation_latitude": -1.292066,
|
| 531 |
-
"installation_longitude": 36.782421,
|
| 532 |
-
"is_installation_required": true,
|
| 533 |
-
"preferred_visit_date": "2025-12-10",
|
| 534 |
-
"preferred_visit_time": "10:00-14:00",
|
| 535 |
-
"received_date": "2025-11-24",
|
| 536 |
-
"received_time": null,
|
| 537 |
-
"notes": "Needs router configuration assistance",
|
| 538 |
-
"id": "7ad1e823-9c68-448c-8d7c-7fdefbf640cb",
|
| 539 |
-
"customer_id": "be628c26-7bb5-404d-9fc1-c58ffe91c9a2",
|
| 540 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 541 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 543 |
-
"
|
| 544 |
-
"
|
| 545 |
-
"
|
| 546 |
-
"
|
| 547 |
-
"
|
| 548 |
-
"
|
| 549 |
-
"
|
| 550 |
-
"
|
| 551 |
-
"
|
| 552 |
-
"
|
| 553 |
-
"
|
| 554 |
-
"
|
| 555 |
-
"
|
| 556 |
-
"
|
| 557 |
-
"region_name": "Test",
|
| 558 |
-
"sales_agent_name": "Sarah Mwangi",
|
| 559 |
-
"is_pending": true,
|
| 560 |
"is_cancelled": false,
|
| 561 |
-
"
|
| 562 |
-
"
|
| 563 |
-
"
|
| 564 |
-
"
|
| 565 |
-
"
|
|
|
|
|
|
|
|
|
|
| 566 |
},
|
| 567 |
{
|
| 568 |
-
"
|
| 569 |
-
"service_request_number": null,
|
| 570 |
-
"agent_name": "Sarah Mwangi",
|
| 571 |
-
"agent_number": "+254723456789",
|
| 572 |
-
"customer_preferred_package": "Premium Fiber 50Mbps",
|
| 573 |
-
"package_price": "5000.00",
|
| 574 |
-
"installation_address_line1": "123 Kilimani Road",
|
| 575 |
-
"installation_address_line2": "Apt 4B",
|
| 576 |
-
"installation_maps_link": "https://maps.google.com/?q=-1.286389,36.817223",
|
| 577 |
-
"installation_latitude": -1.286389,
|
| 578 |
-
"installation_longitude": 36.817223,
|
| 579 |
-
"is_installation_required": true,
|
| 580 |
-
"preferred_visit_date": "2025-12-01",
|
| 581 |
-
"preferred_visit_time": "Morning 9AM-12PM",
|
| 582 |
-
"received_date": "2025-11-24",
|
| 583 |
-
"received_time": null,
|
| 584 |
-
"notes": "Customer prefers early morning installation",
|
| 585 |
-
"id": "f40b7d15-01f3-4dbc-9fd5-3f9114db5439",
|
| 586 |
-
"customer_id": "5daadd72-7e1c-454b-8a7a-50e50a28e7ba",
|
| 587 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 588 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 590 |
-
"
|
| 591 |
-
"
|
| 592 |
-
"
|
| 593 |
-
"processed_at": null,
|
| 594 |
-
"processed_by_user_id": null,
|
| 595 |
-
"cancellation_reason": null,
|
| 596 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 597 |
-
"submitted_at": "2025-11-25T09:13:17.409653Z",
|
| 598 |
-
"created_at": "2025-11-25T09:13:17.409653Z",
|
| 599 |
-
"updated_at": "2025-11-25T09:13:17.409653Z",
|
| 600 |
-
"customer_name": "John Kamau",
|
| 601 |
-
"customer_phone": "+254712345678",
|
| 602 |
-
"customer_email": "john.kamau@email.com",
|
| 603 |
-
"project_title": "Atomio Fttx",
|
| 604 |
-
"region_name": "Test",
|
| 605 |
-
"sales_agent_name": "Sarah Mwangi",
|
| 606 |
-
"is_pending": true,
|
| 607 |
-
"is_cancelled": false,
|
| 608 |
-
"has_ticket": false,
|
| 609 |
-
"has_location": true,
|
| 610 |
-
"has_region_assignment": true,
|
| 611 |
-
"can_promote_to_ticket": true,
|
| 612 |
-
"can_cancel": true
|
| 613 |
-
},
|
| 614 |
-
{
|
| 615 |
-
"order_number": "ORD-2025-013",
|
| 616 |
-
"service_request_number": null,
|
| 617 |
-
"agent_name": "Mike Ochieng",
|
| 618 |
-
"agent_number": "+254712345678",
|
| 619 |
-
"customer_preferred_package": "Basic 10Mbps",
|
| 620 |
-
"package_price": "2500.00",
|
| 621 |
-
"installation_address_line1": "1111 Kasarani Road",
|
| 622 |
-
"installation_address_line2": null,
|
| 623 |
-
"installation_maps_link": null,
|
| 624 |
-
"installation_latitude": null,
|
| 625 |
-
"installation_longitude": null,
|
| 626 |
-
"is_installation_required": true,
|
| 627 |
-
"preferred_visit_date": "2025-12-28",
|
| 628 |
-
"preferred_visit_time": "Afternoon 1PM-4PM",
|
| 629 |
-
"received_date": "2025-11-24",
|
| 630 |
-
"received_time": null,
|
| 631 |
"notes": null,
|
| 632 |
-
"
|
| 633 |
-
"
|
| 634 |
-
"
|
| 635 |
-
"
|
| 636 |
-
"project_region_id": null,
|
| 637 |
-
"status": "pending",
|
| 638 |
-
"is_processed": false,
|
| 639 |
-
"is_ticket_created": false,
|
| 640 |
-
"processed_at": null,
|
| 641 |
-
"processed_by_user_id": null,
|
| 642 |
-
"cancellation_reason": null,
|
| 643 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 644 |
-
"submitted_at": "2025-11-25T08:17:52.191260Z",
|
| 645 |
-
"created_at": "2025-11-25T08:17:52.191260Z",
|
| 646 |
-
"updated_at": "2025-11-25T08:17:52.191260Z",
|
| 647 |
-
"customer_name": "Catherine Njoki",
|
| 648 |
-
"customer_phone": "+254734567891",
|
| 649 |
-
"customer_email": "catherine.njoki@email.com",
|
| 650 |
-
"project_title": "Atomio Fttx",
|
| 651 |
"region_name": null,
|
| 652 |
-
"
|
| 653 |
-
"
|
|
|
|
|
|
|
|
|
|
| 654 |
"is_cancelled": false,
|
| 655 |
-
"
|
| 656 |
-
"
|
| 657 |
-
"
|
| 658 |
-
"
|
| 659 |
-
"
|
|
|
|
|
|
|
|
|
|
| 660 |
},
|
| 661 |
-
{
|
| 662 |
-
"order_number": "ORD-2025-002",
|
| 663 |
-
"service_request_number": null,
|
| 664 |
-
"agent_name": "Mike Ochieng",
|
| 665 |
-
"agent_number": "+254712345678",
|
| 666 |
-
"customer_preferred_package": "Basic 10Mbps",
|
| 667 |
-
"package_price": "2500.00",
|
| 668 |
-
"installation_address_line1": "456 Westlands Avenue",
|
| 669 |
-
"installation_address_line2": null,
|
| 670 |
-
"installation_maps_link": null,
|
| 671 |
-
"installation_latitude": null,
|
| 672 |
-
"installation_longitude": null,
|
| 673 |
-
"is_installation_required": true,
|
| 674 |
-
"preferred_visit_date": "2025-12-05",
|
| 675 |
-
"preferred_visit_time": "Afternoon",
|
| 676 |
-
"received_date": "2025-11-24",
|
| 677 |
-
"received_time": null,
|
| 678 |
-
"notes": null,
|
| 679 |
-
"id": "eefc45f6-c101-41cd-be8a-63e919c5288d",
|
| 680 |
-
"customer_id": "88745284-05f7-4a2a-acaf-6a39df45d36b",
|
| 681 |
-
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 682 |
-
"sales_agent_id": null,
|
| 683 |
-
"project_region_id": null,
|
| 684 |
-
"status": "pending",
|
| 685 |
-
"is_processed": false,
|
| 686 |
-
"is_ticket_created": false,
|
| 687 |
-
"processed_at": null,
|
| 688 |
-
"processed_by_user_id": null,
|
| 689 |
-
"cancellation_reason": null,
|
| 690 |
-
"submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
|
| 691 |
-
"submitted_at": "2025-11-25T08:17:50.772198Z",
|
| 692 |
-
"created_at": "2025-11-25T08:17:50.772198Z",
|
| 693 |
-
"updated_at": "2025-11-25T08:17:50.772198Z",
|
| 694 |
-
"customer_name": "Jane Wanjiru",
|
| 695 |
-
"customer_phone": "+254723456789",
|
| 696 |
-
"customer_email": "jane.wanjiru@email.com",
|
| 697 |
-
"project_title": "Atomio Fttx",
|
| 698 |
-
"region_name": null,
|
| 699 |
-
"sales_agent_name": "Mike Ochieng",
|
| 700 |
-
"is_pending": true,
|
| 701 |
-
"is_cancelled": false,
|
| 702 |
-
"has_ticket": false,
|
| 703 |
-
"has_location": false,
|
| 704 |
-
"has_region_assignment": false,
|
| 705 |
-
"can_promote_to_ticket": true,
|
| 706 |
-
"can_cancel": true
|
| 707 |
-
}
|
| 708 |
-
],
|
| 709 |
-
"total": 15,
|
| 710 |
-
"skip": 0,
|
| 711 |
-
"limit": 50
|
| 712 |
-
}
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
{
|
| 717 |
-
"total": 4,
|
| 718 |
-
"skip": 0,
|
| 719 |
-
"limit": 50,
|
| 720 |
-
"tickets": [
|
| 721 |
{
|
| 722 |
"id": "b2c279c7-861a-414f-839d-f4cedddf5aef",
|
| 723 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
|
@@ -899,4 +409,4 @@
|
|
| 899 |
"can_be_cancelled": true
|
| 900 |
}
|
| 901 |
]
|
| 902 |
-
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"total": 9,
|
| 3 |
+
"skip": 0,
|
| 4 |
+
"limit": 50,
|
| 5 |
+
"tickets": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
{
|
| 7 |
+
"id": "1f807cf8-f139-421b-86e3-38c2f8bc7070",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 9 |
+
"source": "sales_order",
|
| 10 |
+
"source_id": "7ad1e823-9c68-448c-8d7c-7fdefbf640cb",
|
| 11 |
+
"ticket_name": "Peter Otieno",
|
| 12 |
+
"ticket_type": "installation",
|
| 13 |
+
"service_type": "ftth",
|
| 14 |
+
"work_description": "Install Standard 20Mbps for Peter Otieno",
|
| 15 |
+
"status": "open",
|
| 16 |
+
"priority": "normal",
|
| 17 |
+
"scheduled_date": "2025-12-10",
|
| 18 |
+
"scheduled_time_slot": "10:00-14:00",
|
| 19 |
+
"due_date": "2025-11-30T11:41:47.990551Z",
|
| 20 |
+
"sla_target_date": "2025-11-30T11:41:47.990551Z",
|
| 21 |
+
"sla_violated": false,
|
| 22 |
+
"started_at": null,
|
| 23 |
+
"completed_at": null,
|
| 24 |
+
"is_invoiced": false,
|
| 25 |
+
"invoiced_at": null,
|
| 26 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 27 |
+
"work_location_latitude": null,
|
| 28 |
+
"work_location_longitude": null,
|
| 29 |
+
"work_location_verified": false,
|
| 30 |
+
"notes": "Needs router configuration assistance",
|
| 31 |
+
"version": 1,
|
| 32 |
+
"created_at": "2025-11-27T11:41:47.990551Z",
|
| 33 |
+
"updated_at": "2025-11-27T11:41:47.990555Z",
|
| 34 |
+
"project_title": null,
|
| 35 |
+
"region_name": null,
|
| 36 |
+
"customer_name": null,
|
| 37 |
+
"is_open": true,
|
| 38 |
+
"is_assigned": false,
|
| 39 |
+
"is_in_progress": false,
|
| 40 |
+
"is_completed": false,
|
|
|
|
|
|
|
|
|
|
| 41 |
"is_cancelled": false,
|
| 42 |
+
"is_active": true,
|
| 43 |
+
"is_overdue": false,
|
| 44 |
+
"has_region": true,
|
| 45 |
+
"has_schedule": true,
|
| 46 |
+
"can_be_assigned": true,
|
| 47 |
+
"can_be_started": false,
|
| 48 |
+
"can_be_completed": false,
|
| 49 |
+
"can_be_cancelled": true
|
| 50 |
},
|
| 51 |
{
|
| 52 |
+
"id": "74ecc2e2-0526-43ec-a454-8aa8668139a1",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 54 |
+
"source": "sales_order",
|
| 55 |
+
"source_id": "29417f9e-918f-4efb-8a02-0702c5ca14ff",
|
| 56 |
+
"ticket_name": "Sarah Wangari",
|
| 57 |
+
"ticket_type": "installation",
|
| 58 |
+
"service_type": "ftth",
|
| 59 |
+
"work_description": "Install Standard 20Mbps for Sarah Wangari",
|
| 60 |
+
"status": "open",
|
| 61 |
+
"priority": "normal",
|
| 62 |
+
"scheduled_date": "2025-12-19",
|
| 63 |
+
"scheduled_time_slot": "15:00",
|
| 64 |
+
"due_date": "2025-11-29T18:08:27.148533Z",
|
| 65 |
+
"sla_target_date": "2025-11-29T18:08:27.148533Z",
|
| 66 |
+
"sla_violated": false,
|
| 67 |
+
"started_at": null,
|
| 68 |
+
"completed_at": null,
|
| 69 |
+
"is_invoiced": false,
|
| 70 |
+
"invoiced_at": null,
|
| 71 |
"project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
|
| 72 |
+
"work_location_latitude": null,
|
| 73 |
+
"work_location_longitude": null,
|
| 74 |
+
"work_location_verified": false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
"notes": null,
|
| 76 |
+
"version": 1,
|
| 77 |
+
"created_at": "2025-11-26T18:08:27.148533Z",
|
| 78 |
+
"updated_at": "2025-11-26T18:08:27.148538Z",
|
| 79 |
+
"project_title": null,
|
| 80 |
+
"region_name": null,
|
| 81 |
+
"customer_name": null,
|
| 82 |
+
"is_open": true,
|
| 83 |
+
"is_assigned": false,
|
| 84 |
+
"is_in_progress": false,
|
| 85 |
+
"is_completed": false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
"is_cancelled": false,
|
| 87 |
+
"is_active": true,
|
| 88 |
+
"is_overdue": false,
|
| 89 |
+
"has_region": true,
|
| 90 |
+
"has_schedule": true,
|
| 91 |
+
"can_be_assigned": true,
|
| 92 |
+
"can_be_started": false,
|
| 93 |
+
"can_be_completed": false,
|
| 94 |
+
"can_be_cancelled": true
|
| 95 |
},
|
| 96 |
{
|
| 97 |
+
"id": "f59b29fc-d0b9-4618-b0d1-889e340da612",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 99 |
+
"source": "sales_order",
|
| 100 |
+
"source_id": "c93b28c0-d7bf-4f57-b750-d0c6864543b0",
|
| 101 |
+
"ticket_name": "Elizabeth Muthoni",
|
| 102 |
+
"ticket_type": "installation",
|
| 103 |
+
"service_type": "ftth",
|
| 104 |
+
"work_description": "Install Premium Fiber 100Mbps for Elizabeth Muthoni",
|
| 105 |
+
"status": "open",
|
| 106 |
+
"priority": "normal",
|
| 107 |
+
"scheduled_date": "2025-12-11",
|
| 108 |
+
"scheduled_time_slot": null,
|
| 109 |
+
"due_date": "2025-11-29T13:24:19.212882Z",
|
| 110 |
+
"sla_target_date": "2025-11-29T13:24:19.212882Z",
|
| 111 |
+
"sla_violated": false,
|
| 112 |
+
"started_at": null,
|
| 113 |
+
"completed_at": null,
|
| 114 |
+
"is_invoiced": false,
|
| 115 |
+
"invoiced_at": null,
|
| 116 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 117 |
+
"work_location_latitude": null,
|
| 118 |
+
"work_location_longitude": null,
|
| 119 |
+
"work_location_verified": false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
"notes": null,
|
| 121 |
+
"version": 1,
|
| 122 |
+
"created_at": "2025-11-26T13:24:19.212882Z",
|
| 123 |
+
"updated_at": "2025-11-26T13:24:19.212885Z",
|
| 124 |
+
"project_title": null,
|
| 125 |
+
"region_name": null,
|
| 126 |
+
"customer_name": null,
|
| 127 |
+
"is_open": true,
|
| 128 |
+
"is_assigned": false,
|
| 129 |
+
"is_in_progress": false,
|
| 130 |
+
"is_completed": false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
"is_cancelled": false,
|
| 132 |
+
"is_active": true,
|
| 133 |
+
"is_overdue": false,
|
| 134 |
+
"has_region": true,
|
| 135 |
+
"has_schedule": true,
|
| 136 |
+
"can_be_assigned": true,
|
| 137 |
+
"can_be_started": false,
|
| 138 |
+
"can_be_completed": false,
|
| 139 |
+
"can_be_cancelled": true
|
| 140 |
},
|
| 141 |
{
|
| 142 |
+
"id": "70090c47-e9c1-4b0a-add4-69bec53d92f9",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 144 |
+
"source": "sales_order",
|
| 145 |
+
"source_id": "4bbe8e66-b1c2-476c-af4a-ed19d3281e3a",
|
| 146 |
+
"ticket_name": "Robert Kimani",
|
| 147 |
+
"ticket_type": "installation",
|
| 148 |
+
"service_type": "ftth",
|
| 149 |
+
"work_description": "Install Premium Fiber 50Mbps for Robert Kimani",
|
| 150 |
+
"status": "open",
|
| 151 |
+
"priority": "normal",
|
| 152 |
+
"scheduled_date": "2025-12-22",
|
| 153 |
+
"scheduled_time_slot": "Morning",
|
| 154 |
+
"due_date": "2025-11-29T09:33:10.330653Z",
|
| 155 |
+
"sla_target_date": "2025-11-29T09:33:10.330653Z",
|
| 156 |
+
"sla_violated": false,
|
| 157 |
+
"started_at": null,
|
| 158 |
+
"completed_at": null,
|
| 159 |
+
"is_invoiced": false,
|
| 160 |
+
"invoiced_at": null,
|
| 161 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 162 |
+
"work_location_latitude": null,
|
| 163 |
+
"work_location_longitude": null,
|
| 164 |
+
"work_location_verified": false,
|
| 165 |
+
"notes": null,
|
| 166 |
+
"version": 1,
|
| 167 |
+
"created_at": "2025-11-26T09:33:10.330653Z",
|
| 168 |
+
"updated_at": "2025-11-26T09:33:10.330656Z",
|
| 169 |
+
"project_title": null,
|
| 170 |
+
"region_name": null,
|
| 171 |
+
"customer_name": null,
|
| 172 |
+
"is_open": true,
|
| 173 |
+
"is_assigned": false,
|
| 174 |
+
"is_in_progress": false,
|
| 175 |
+
"is_completed": false,
|
|
|
|
|
|
|
|
|
|
| 176 |
"is_cancelled": false,
|
| 177 |
+
"is_active": true,
|
| 178 |
+
"is_overdue": false,
|
| 179 |
+
"has_region": true,
|
| 180 |
+
"has_schedule": true,
|
| 181 |
+
"can_be_assigned": true,
|
| 182 |
+
"can_be_started": false,
|
| 183 |
+
"can_be_completed": false,
|
| 184 |
+
"can_be_cancelled": true
|
| 185 |
},
|
| 186 |
{
|
| 187 |
+
"id": "2de41ce7-dff1-4151-9710-87958d18b5c4",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
| 189 |
+
"source": "sales_order",
|
| 190 |
+
"source_id": "a1d04741-b1b5-4a9e-baf6-d1e186886122",
|
| 191 |
+
"ticket_name": "Elizabeth Muthoni",
|
| 192 |
+
"ticket_type": "installation",
|
| 193 |
+
"service_type": "ftth",
|
| 194 |
+
"work_description": "Install Premium Fiber 100Mbps for Elizabeth Muthoni",
|
| 195 |
+
"status": "open",
|
| 196 |
+
"priority": "normal",
|
| 197 |
+
"scheduled_date": "2025-12-11",
|
| 198 |
+
"scheduled_time_slot": "Anytime",
|
| 199 |
+
"due_date": "2025-11-29T09:33:10.216625Z",
|
| 200 |
+
"sla_target_date": "2025-11-29T09:33:10.216625Z",
|
| 201 |
+
"sla_violated": false,
|
| 202 |
+
"started_at": null,
|
| 203 |
+
"completed_at": null,
|
| 204 |
+
"is_invoiced": false,
|
| 205 |
+
"invoiced_at": null,
|
| 206 |
"project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
|
| 207 |
+
"work_location_latitude": null,
|
| 208 |
+
"work_location_longitude": null,
|
| 209 |
+
"work_location_verified": false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
"notes": null,
|
| 211 |
+
"version": 1,
|
| 212 |
+
"created_at": "2025-11-26T09:33:10.216625Z",
|
| 213 |
+
"updated_at": "2025-11-26T09:33:10.216627Z",
|
| 214 |
+
"project_title": null,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
"region_name": null,
|
| 216 |
+
"customer_name": null,
|
| 217 |
+
"is_open": true,
|
| 218 |
+
"is_assigned": false,
|
| 219 |
+
"is_in_progress": false,
|
| 220 |
+
"is_completed": false,
|
| 221 |
"is_cancelled": false,
|
| 222 |
+
"is_active": true,
|
| 223 |
+
"is_overdue": false,
|
| 224 |
+
"has_region": true,
|
| 225 |
+
"has_schedule": true,
|
| 226 |
+
"can_be_assigned": true,
|
| 227 |
+
"can_be_started": false,
|
| 228 |
+
"can_be_completed": false,
|
| 229 |
+
"can_be_cancelled": true
|
| 230 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
{
|
| 232 |
"id": "b2c279c7-861a-414f-839d-f4cedddf5aef",
|
| 233 |
"project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
|
|
|
|
| 409 |
"can_be_cancelled": true
|
| 410 |
}
|
| 411 |
]
|
| 412 |
+
}
|
src/app/api/v1/tickets.py
CHANGED
|
@@ -436,6 +436,240 @@ def get_ticket(
|
|
| 436 |
return TicketResponse.from_orm(ticket)
|
| 437 |
|
| 438 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
# ============================================
|
| 440 |
# UPDATE TICKETS
|
| 441 |
# ============================================
|
|
|
|
| 436 |
return TicketResponse.from_orm(ticket)
|
| 437 |
|
| 438 |
|
| 439 |
+
@router.get(
|
| 440 |
+
"/{ticket_id}/detail",
|
| 441 |
+
summary="Get comprehensive ticket details"
|
| 442 |
+
)
|
| 443 |
+
def get_ticket_detail(
|
| 444 |
+
ticket_id: UUID,
|
| 445 |
+
db: Session = Depends(get_db),
|
| 446 |
+
current_user: User = Depends(get_current_user)
|
| 447 |
+
):
|
| 448 |
+
"""
|
| 449 |
+
Get comprehensive ticket details with all related data.
|
| 450 |
+
|
| 451 |
+
**Returns:**
|
| 452 |
+
- Ticket data
|
| 453 |
+
- Sales order (if source is sales_order)
|
| 454 |
+
- Customer details
|
| 455 |
+
- Expenses list
|
| 456 |
+
- Images list
|
| 457 |
+
- Comments list
|
| 458 |
+
- Assignments list
|
| 459 |
+
- Status history (if available)
|
| 460 |
+
|
| 461 |
+
**Authorization:** Must have access to ticket's project
|
| 462 |
+
|
| 463 |
+
**Perfect for:** Ticket detail view in frontend
|
| 464 |
+
"""
|
| 465 |
+
from app.models.ticket import Ticket
|
| 466 |
+
from app.models.sales_order import SalesOrder
|
| 467 |
+
from app.models.customer import Customer
|
| 468 |
+
from app.models.ticket_expense import TicketExpense
|
| 469 |
+
from app.models.ticket_image import TicketImage
|
| 470 |
+
from app.models.ticket_comment import TicketComment
|
| 471 |
+
from app.models.ticket_assignment import TicketAssignment
|
| 472 |
+
from sqlalchemy.orm import joinedload
|
| 473 |
+
|
| 474 |
+
# Get ticket with authorization
|
| 475 |
+
ticket = TicketService.get_ticket_by_id(db, ticket_id, current_user)
|
| 476 |
+
|
| 477 |
+
# Build comprehensive response
|
| 478 |
+
response = {
|
| 479 |
+
"ticket": TicketResponse.from_orm(ticket),
|
| 480 |
+
"source_data": None, # sales_order, task, incident, or subscription
|
| 481 |
+
"customer": None,
|
| 482 |
+
"expenses": [],
|
| 483 |
+
"images": [],
|
| 484 |
+
"comments": [],
|
| 485 |
+
"assignments": []
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
# Get source-specific data based on ticket source
|
| 489 |
+
if ticket.source_id:
|
| 490 |
+
if ticket.source == "sales_order":
|
| 491 |
+
from app.models.sales_order import SalesOrder
|
| 492 |
+
sales_order = db.query(SalesOrder).options(
|
| 493 |
+
joinedload(SalesOrder.customer)
|
| 494 |
+
).filter(SalesOrder.id == ticket.source_id).first()
|
| 495 |
+
|
| 496 |
+
if sales_order:
|
| 497 |
+
response["source_data"] = {
|
| 498 |
+
"type": "sales_order",
|
| 499 |
+
"id": str(sales_order.id),
|
| 500 |
+
"order_number": sales_order.order_number,
|
| 501 |
+
"customer_preferred_package": sales_order.customer_preferred_package,
|
| 502 |
+
"installation_address": sales_order.installation_address,
|
| 503 |
+
"preferred_visit_date": sales_order.preferred_visit_date.isoformat() if sales_order.preferred_visit_date else None,
|
| 504 |
+
"status": sales_order.status,
|
| 505 |
+
"total_amount": float(sales_order.total_amount) if sales_order.total_amount else None
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
# Get customer from sales order
|
| 509 |
+
if sales_order.customer:
|
| 510 |
+
customer = sales_order.customer
|
| 511 |
+
response["customer"] = {
|
| 512 |
+
"id": str(customer.id),
|
| 513 |
+
"name": customer.name,
|
| 514 |
+
"phone": customer.phone,
|
| 515 |
+
"email": customer.email,
|
| 516 |
+
"address": customer.address,
|
| 517 |
+
"location_latitude": float(customer.location_latitude) if customer.location_latitude else None,
|
| 518 |
+
"location_longitude": float(customer.location_longitude) if customer.location_longitude else None
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
elif ticket.source == "task":
|
| 522 |
+
from app.models.task import Task
|
| 523 |
+
task = db.query(Task).filter(Task.id == ticket.source_id).first()
|
| 524 |
+
|
| 525 |
+
if task:
|
| 526 |
+
response["source_data"] = {
|
| 527 |
+
"type": "task",
|
| 528 |
+
"id": str(task.id),
|
| 529 |
+
"task_title": task.task_title,
|
| 530 |
+
"task_description": task.task_description,
|
| 531 |
+
"task_type": task.task_type,
|
| 532 |
+
"priority": task.priority,
|
| 533 |
+
"status": task.status,
|
| 534 |
+
"scheduled_date": task.scheduled_date.isoformat() if task.scheduled_date else None,
|
| 535 |
+
"due_date": task.due_date.isoformat() if task.due_date else None
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
elif ticket.source == "incident":
|
| 539 |
+
from app.models.incident import Incident
|
| 540 |
+
incident = db.query(Incident).options(
|
| 541 |
+
joinedload(Incident.customer)
|
| 542 |
+
).filter(Incident.id == ticket.source_id).first()
|
| 543 |
+
|
| 544 |
+
if incident:
|
| 545 |
+
response["source_data"] = {
|
| 546 |
+
"type": "incident",
|
| 547 |
+
"id": str(incident.id),
|
| 548 |
+
"incident_number": incident.incident_number,
|
| 549 |
+
"incident_type": incident.incident_type,
|
| 550 |
+
"issue_description": incident.issue_description,
|
| 551 |
+
"priority": incident.priority,
|
| 552 |
+
"status": incident.status,
|
| 553 |
+
"reported_at": incident.reported_at.isoformat() if incident.reported_at else None
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
# Get customer from incident
|
| 557 |
+
if incident.customer:
|
| 558 |
+
customer = incident.customer
|
| 559 |
+
response["customer"] = {
|
| 560 |
+
"id": str(customer.id),
|
| 561 |
+
"name": customer.name,
|
| 562 |
+
"phone": customer.phone,
|
| 563 |
+
"email": customer.email,
|
| 564 |
+
"address": customer.address,
|
| 565 |
+
"location_latitude": float(customer.location_latitude) if customer.location_latitude else None,
|
| 566 |
+
"location_longitude": float(customer.location_longitude) if customer.location_longitude else None
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
elif ticket.source == "subscription":
|
| 570 |
+
from app.models.subscription import Subscription
|
| 571 |
+
subscription = db.query(Subscription).options(
|
| 572 |
+
joinedload(Subscription.customer)
|
| 573 |
+
).filter(Subscription.id == ticket.source_id).first()
|
| 574 |
+
|
| 575 |
+
if subscription:
|
| 576 |
+
response["source_data"] = {
|
| 577 |
+
"type": "subscription",
|
| 578 |
+
"id": str(subscription.id),
|
| 579 |
+
"subscription_number": subscription.subscription_number,
|
| 580 |
+
"service_type": subscription.service_type,
|
| 581 |
+
"package_name": subscription.package_name,
|
| 582 |
+
"status": subscription.status,
|
| 583 |
+
"start_date": subscription.start_date.isoformat() if subscription.start_date else None,
|
| 584 |
+
"next_billing_date": subscription.next_billing_date.isoformat() if subscription.next_billing_date else None
|
| 585 |
+
}
|
| 586 |
+
|
| 587 |
+
# Get customer from subscription
|
| 588 |
+
if subscription.customer:
|
| 589 |
+
customer = subscription.customer
|
| 590 |
+
response["customer"] = {
|
| 591 |
+
"id": str(customer.id),
|
| 592 |
+
"name": customer.name,
|
| 593 |
+
"phone": customer.phone,
|
| 594 |
+
"email": customer.email,
|
| 595 |
+
"address": customer.address,
|
| 596 |
+
"location_latitude": float(customer.location_latitude) if customer.location_latitude else None,
|
| 597 |
+
"location_longitude": float(customer.location_longitude) if customer.location_longitude else None
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
# Get expenses
|
| 601 |
+
expenses = db.query(TicketExpense).filter(
|
| 602 |
+
TicketExpense.ticket_id == ticket_id,
|
| 603 |
+
TicketExpense.deleted_at.is_(None)
|
| 604 |
+
).all()
|
| 605 |
+
|
| 606 |
+
response["expenses"] = [{
|
| 607 |
+
"id": str(exp.id),
|
| 608 |
+
"expense_type": exp.expense_type,
|
| 609 |
+
"description": exp.description,
|
| 610 |
+
"quantity": float(exp.quantity) if exp.quantity else None,
|
| 611 |
+
"unit_cost": float(exp.unit_cost) if exp.unit_cost else None,
|
| 612 |
+
"total_cost": float(exp.total_cost) if exp.total_cost else None,
|
| 613 |
+
"is_approved": exp.is_approved,
|
| 614 |
+
"approved_at": exp.approved_at.isoformat() if exp.approved_at else None,
|
| 615 |
+
"created_at": exp.created_at.isoformat() if exp.created_at else None
|
| 616 |
+
} for exp in expenses]
|
| 617 |
+
|
| 618 |
+
# Get images
|
| 619 |
+
images = db.query(TicketImage).filter(
|
| 620 |
+
TicketImage.ticket_id == ticket_id,
|
| 621 |
+
TicketImage.deleted_at.is_(None)
|
| 622 |
+
).order_by(TicketImage.created_at.desc()).all()
|
| 623 |
+
|
| 624 |
+
response["images"] = [{
|
| 625 |
+
"id": str(img.id),
|
| 626 |
+
"image_url": img.image_url,
|
| 627 |
+
"image_type": img.image_type,
|
| 628 |
+
"caption": img.caption,
|
| 629 |
+
"uploaded_by_user_id": str(img.uploaded_by_user_id) if img.uploaded_by_user_id else None,
|
| 630 |
+
"created_at": img.created_at.isoformat() if img.created_at else None
|
| 631 |
+
} for img in images]
|
| 632 |
+
|
| 633 |
+
# Get comments
|
| 634 |
+
comments = db.query(TicketComment).options(
|
| 635 |
+
joinedload(TicketComment.user)
|
| 636 |
+
).filter(
|
| 637 |
+
TicketComment.ticket_id == ticket_id,
|
| 638 |
+
TicketComment.deleted_at.is_(None)
|
| 639 |
+
).order_by(TicketComment.created_at.desc()).all()
|
| 640 |
+
|
| 641 |
+
response["comments"] = [{
|
| 642 |
+
"id": str(comment.id),
|
| 643 |
+
"comment_text": comment.comment_text,
|
| 644 |
+
"user_id": str(comment.user_id),
|
| 645 |
+
"user_name": comment.user.name if comment.user else None,
|
| 646 |
+
"created_at": comment.created_at.isoformat() if comment.created_at else None
|
| 647 |
+
} for comment in comments]
|
| 648 |
+
|
| 649 |
+
# Get assignments
|
| 650 |
+
assignments = db.query(TicketAssignment).options(
|
| 651 |
+
joinedload(TicketAssignment.user)
|
| 652 |
+
).filter(
|
| 653 |
+
TicketAssignment.ticket_id == ticket_id,
|
| 654 |
+
TicketAssignment.deleted_at.is_(None)
|
| 655 |
+
).order_by(TicketAssignment.assigned_at.desc()).all()
|
| 656 |
+
|
| 657 |
+
response["assignments"] = [{
|
| 658 |
+
"id": str(assignment.id),
|
| 659 |
+
"user_id": str(assignment.user_id),
|
| 660 |
+
"user_name": assignment.user.name if assignment.user else None,
|
| 661 |
+
"action": assignment.action,
|
| 662 |
+
"status": assignment.status,
|
| 663 |
+
"assigned_at": assignment.assigned_at.isoformat() if assignment.assigned_at else None,
|
| 664 |
+
"responded_at": assignment.responded_at.isoformat() if assignment.responded_at else None,
|
| 665 |
+
"journey_started_at": assignment.journey_started_at.isoformat() if assignment.journey_started_at else None,
|
| 666 |
+
"arrived_at": assignment.arrived_at.isoformat() if assignment.arrived_at else None,
|
| 667 |
+
"ended_at": assignment.ended_at.isoformat() if assignment.ended_at else None
|
| 668 |
+
} for assignment in assignments]
|
| 669 |
+
|
| 670 |
+
return response
|
| 671 |
+
|
| 672 |
+
|
| 673 |
# ============================================
|
| 674 |
# UPDATE TICKETS
|
| 675 |
# ============================================
|
src/app/services/project_service.py
CHANGED
|
@@ -1641,7 +1641,10 @@ class ProjectService:
|
|
| 1641 |
Authorization:
|
| 1642 |
- platform_admin: Full access
|
| 1643 |
- project_manager: Only their projects (primary_manager_id)
|
| 1644 |
-
-
|
|
|
|
|
|
|
|
|
|
| 1645 |
"""
|
| 1646 |
project = db.query(Project).filter(
|
| 1647 |
Project.id == project_id,
|
|
@@ -1655,11 +1658,45 @@ class ProjectService:
|
|
| 1655 |
)
|
| 1656 |
|
| 1657 |
# Authorization check
|
| 1658 |
-
if current_user.role
|
| 1659 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1660 |
raise HTTPException(
|
| 1661 |
status_code=status.HTTP_403_FORBIDDEN,
|
| 1662 |
-
detail="You don't have permission to
|
| 1663 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1664 |
|
| 1665 |
return project
|
|
|
|
| 1641 |
Authorization:
|
| 1642 |
- platform_admin: Full access
|
| 1643 |
- project_manager: Only their projects (primary_manager_id)
|
| 1644 |
+
- client_admin: Their client's projects
|
| 1645 |
+
- contractor_admin: Their contractor's projects
|
| 1646 |
+
- dispatcher: Their contractor's projects
|
| 1647 |
+
- field_agent/sales_agent: Projects they're team members of
|
| 1648 |
"""
|
| 1649 |
project = db.query(Project).filter(
|
| 1650 |
Project.id == project_id,
|
|
|
|
| 1658 |
)
|
| 1659 |
|
| 1660 |
# Authorization check
|
| 1661 |
+
if current_user.role == 'platform_admin':
|
| 1662 |
+
return project
|
| 1663 |
+
elif current_user.role == 'project_manager':
|
| 1664 |
+
if str(project.primary_manager_id) != str(current_user.id):
|
| 1665 |
+
raise HTTPException(
|
| 1666 |
+
status_code=status.HTTP_403_FORBIDDEN,
|
| 1667 |
+
detail="You don't have permission to access this project"
|
| 1668 |
+
)
|
| 1669 |
+
elif current_user.role == 'client_admin':
|
| 1670 |
+
if str(project.client_id) != str(current_user.client_id):
|
| 1671 |
+
raise HTTPException(
|
| 1672 |
+
status_code=status.HTTP_403_FORBIDDEN,
|
| 1673 |
+
detail="You don't have permission to access this project"
|
| 1674 |
+
)
|
| 1675 |
+
elif current_user.role in ['contractor_admin', 'dispatcher']:
|
| 1676 |
+
if str(project.contractor_id) != str(current_user.contractor_id):
|
| 1677 |
raise HTTPException(
|
| 1678 |
status_code=status.HTTP_403_FORBIDDEN,
|
| 1679 |
+
detail="You don't have permission to access this project"
|
| 1680 |
)
|
| 1681 |
+
elif current_user.role in ['field_agent', 'sales_agent']:
|
| 1682 |
+
# Check if they're a team member
|
| 1683 |
+
from app.models.project_team import ProjectTeam
|
| 1684 |
+
team_member = db.query(ProjectTeam).filter(
|
| 1685 |
+
ProjectTeam.project_id == project_id,
|
| 1686 |
+
ProjectTeam.user_id == current_user.id,
|
| 1687 |
+
ProjectTeam.deleted_at.is_(None),
|
| 1688 |
+
ProjectTeam.removed_at.is_(None)
|
| 1689 |
+
).first()
|
| 1690 |
+
|
| 1691 |
+
if not team_member:
|
| 1692 |
+
raise HTTPException(
|
| 1693 |
+
status_code=status.HTTP_403_FORBIDDEN,
|
| 1694 |
+
detail="You don't have permission to access this project"
|
| 1695 |
+
)
|
| 1696 |
+
else:
|
| 1697 |
+
raise HTTPException(
|
| 1698 |
+
status_code=status.HTTP_403_FORBIDDEN,
|
| 1699 |
+
detail="You don't have permission to access this project"
|
| 1700 |
+
)
|
| 1701 |
|
| 1702 |
return project
|