kamau1 commited on
Commit
7ed6785
·
1 Parent(s): 65410e6

feat: tickets detail view

Browse files
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
- "items": [
3
- {
4
- "order_number": null,
5
- "service_request_number": null,
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
- "order_number": "ORD-2025-020",
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
- "sales_agent_id": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  "project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
167
- "status": "processed",
168
- "is_processed": true,
169
- "is_ticket_created": true,
170
- "processed_at": "2025-11-26T08:33:01.404539Z",
171
- "processed_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
172
- "cancellation_reason": null,
173
- "submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
174
- "submitted_at": "2025-11-25T10:20:37.693575Z",
175
- "created_at": "2025-11-25T10:20:37.693575Z",
176
- "updated_at": "2025-11-26T08:33:01.404544Z",
177
- "customer_name": "Anne Njeri",
178
- "customer_phone": "+254734567893",
179
- "customer_email": "anne.njeri@email.com",
180
- "project_title": "Atomio Fttx",
181
- "region_name": "Test",
182
- "sales_agent_name": "Mike Ochieng",
183
- "is_pending": false,
184
  "is_cancelled": false,
185
- "has_ticket": true,
186
- "has_location": true,
187
- "has_region_assignment": true,
188
- "can_promote_to_ticket": false,
189
- "can_cancel": false
 
 
 
190
  },
191
  {
192
- "order_number": "ORD-2025-018",
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
- "sales_agent_id": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  "project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
214
- "status": "pending",
215
- "is_processed": false,
216
- "is_ticket_created": false,
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
- "id": "fea49301-74c1-4f2a-8012-a36c801cc8ed",
304
- "customer_id": "40bee251-a186-4194-9d01-e74597dc8326",
305
- "project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
306
- "sales_agent_id": null,
307
- "project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
308
- "status": "pending",
309
- "is_processed": false,
310
- "is_ticket_created": false,
311
- "processed_at": null,
312
- "processed_by_user_id": null,
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
- "has_ticket": false,
421
- "has_location": true,
422
- "has_region_assignment": true,
423
- "can_promote_to_ticket": true,
424
- "can_cancel": true
 
 
 
425
  },
426
  {
427
- "order_number": "ORD-2025-010",
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
- "sales_agent_id": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  "project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
449
- "status": "pending",
450
- "is_processed": false,
451
- "is_ticket_created": false,
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
- "id": "16155120-da0c-42c8-99b2-fc2362d7969f",
492
- "customer_id": "36f644c2-7a79-49d1-819f-7473807336ed",
493
- "project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
494
- "sales_agent_id": null,
495
- "project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
496
- "status": "pending",
497
- "is_processed": false,
498
- "is_ticket_created": false,
499
- "processed_at": null,
500
- "processed_by_user_id": null,
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
- "has_ticket": false,
515
- "has_location": true,
516
- "has_region_assignment": true,
517
- "can_promote_to_ticket": true,
518
- "can_cancel": true
 
 
 
519
  },
520
  {
521
- "order_number": "ORD-2025-003",
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
- "sales_agent_id": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  "project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
543
- "status": "pending",
544
- "is_processed": false,
545
- "is_ticket_created": false,
546
- "processed_at": null,
547
- "processed_by_user_id": null,
548
- "cancellation_reason": null,
549
- "submitted_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
550
- "submitted_at": "2025-11-25T09:13:19.748453Z",
551
- "created_at": "2025-11-25T09:13:19.748970Z",
552
- "updated_at": "2025-11-25T09:13:19.748970Z",
553
- "customer_name": "Peter Otieno",
554
- "customer_phone": "+254734567890",
555
- "customer_email": "peter.otieno@email.com",
556
- "project_title": "Atomio Fttx",
557
- "region_name": "Test",
558
- "sales_agent_name": "Sarah Mwangi",
559
- "is_pending": true,
560
  "is_cancelled": false,
561
- "has_ticket": false,
562
- "has_location": true,
563
- "has_region_assignment": true,
564
- "can_promote_to_ticket": true,
565
- "can_cancel": true
 
 
 
566
  },
567
  {
568
- "order_number": "ORD-2025-001",
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
- "sales_agent_id": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  "project_region_id": "4cd27765-5720-4cc0-872e-bf0da3cd1898",
590
- "status": "pending",
591
- "is_processed": false,
592
- "is_ticket_created": false,
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
- "id": "5cb76cc5-3d9a-4ce0-87a8-43f912896c1f",
633
- "customer_id": "40bee251-a186-4194-9d01-e74597dc8326",
634
- "project_id": "0ade6bd1-e492-4e25-b681-59f42058d29a",
635
- "sales_agent_id": null,
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
- "sales_agent_name": "Mike Ochieng",
653
- "is_pending": true,
 
 
 
654
  "is_cancelled": false,
655
- "has_ticket": false,
656
- "has_location": false,
657
- "has_region_assignment": false,
658
- "can_promote_to_ticket": true,
659
- "can_cancel": true
 
 
 
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
- - Other roles: No access
 
 
 
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 != 'platform_admin':
1659
- if current_user.role != 'project_manager' or str(project.primary_manager_id) != str(current_user.id):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1660
  raise HTTPException(
1661
  status_code=status.HTTP_403_FORBIDDEN,
1662
- detail="You don't have permission to manage this project"
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