kamau1 commited on
Commit
713fc8b
·
1 Parent(s): 756bc21

fix: use historical ticket counts in timesheets to track daily workload regardless of completion status

Browse files
docs/devlogs/browser/response.json CHANGED
@@ -8,55 +8,60 @@
8
  "ticket_type": "installation",
9
  "service_type": "ftth",
10
  "work_description": "Install 100 for Nnacy Wanjiru",
11
- "status": "open",
12
  "priority": "normal",
13
  "scheduled_date": "2026-01-01",
14
  "scheduled_time_slot": null,
15
  "due_date": "2025-11-28T13:03:13.104571Z",
16
  "sla_target_date": "2025-11-28T13:03:13.104571Z",
17
  "sla_violated": true,
18
- "started_at": null,
19
- "completed_at": null,
20
  "is_invoiced": false,
21
  "invoiced_at": null,
22
  "project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
23
- "work_location_latitude": null,
24
- "work_location_longitude": null,
25
- "work_location_verified": false,
26
- "completion_data": {},
27
- "completion_photos_verified": false,
28
- "completion_data_verified": false,
29
- "notes": "Ticket reopened by Project Manager on 2025-12-13 06:02.\n",
 
 
 
 
 
30
  "version": 1,
31
  "created_at": "2025-11-25T13:03:13.104571Z",
32
- "updated_at": "2025-12-13T06:02:11.216941Z",
33
  "project_title": null,
34
  "region_name": null,
35
  "customer_name": null,
36
- "is_open": true,
37
  "is_assigned": false,
38
  "is_in_progress": false,
39
- "is_completed": false,
40
  "is_cancelled": false,
41
- "is_active": true,
42
- "is_overdue": true,
43
  "has_region": true,
44
  "has_schedule": true,
45
- "can_be_assigned": true,
46
  "can_be_started": false,
47
  "can_be_completed": false,
48
- "can_be_cancelled": true
49
  },
50
  "available_actions": [],
51
  "current_assignment": {
52
- "id": "4fe9d28c-b657-4672-9138-78edabb9749b",
53
- "action": "dropped",
54
  "status": "CLOSED",
55
- "journey_started": false,
56
- "arrived": false,
57
  "ended": true,
58
  "can_drop": false,
59
- "has_location_trail": false
60
  },
61
  "team_info": {
62
  "required_size": 1,
@@ -64,7 +69,7 @@
64
  "is_full": false,
65
  "assigned_agents": []
66
  },
67
- "message": "You dropped this ticket - awaiting review",
68
  "source_data": {
69
  "type": "sales_order",
70
  "id": "b2a47ac2-e779-4479-846b-f350f041dd43",
@@ -87,6 +92,146 @@
87
  "location_longitude": null
88
  },
89
  "expenses": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  {
91
  "id": "7b697e88-c4c1-4366-b05e-c0689a90451c",
92
  "ticket_assignment_id": "4fe9d28c-b657-4672-9138-78edabb9749b",
@@ -207,10 +352,10 @@
207
  "receipt_document_id": null,
208
  "location_verified": false,
209
  "verification_notes": "Not verified: No GPS location found for 2025-12-10. Manual review required.",
210
- "is_approved": false,
211
- "approved_by_user_id": null,
212
- "approved_by_user_name": null,
213
- "approved_at": null,
214
  "rejection_reason": null,
215
  "is_paid": false,
216
  "paid_to_user_id": null,
@@ -225,7 +370,7 @@
225
  },
226
  "notes": "",
227
  "created_at": "2025-12-10T12:02:36.667890+00:00",
228
- "updated_at": "2025-12-10T12:02:36.667892+00:00"
229
  },
230
  {
231
  "id": "db836b45-5d6b-4e67-a12a-4619c0fb3f38",
@@ -298,9 +443,58 @@
298
  "updated_at": "2025-12-10T11:53:42.062179+00:00"
299
  }
300
  ],
301
- "images": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  "comments": [],
303
  "assignments": [
 
 
 
 
 
 
 
 
 
 
 
 
304
  {
305
  "id": "4fe9d28c-b657-4672-9138-78edabb9749b",
306
  "user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
@@ -315,6 +509,57 @@
315
  }
316
  ],
317
  "status_history": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  {
319
  "id": "aafc41f2-5543-4a6d-a8d8-10b6c3df3a79",
320
  "old_status": "pending_review",
@@ -367,4 +612,110 @@
367
  "additional_metadata": {}
368
  }
369
  ]
370
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  "ticket_type": "installation",
9
  "service_type": "ftth",
10
  "work_description": "Install 100 for Nnacy Wanjiru",
11
+ "status": "completed",
12
  "priority": "normal",
13
  "scheduled_date": "2026-01-01",
14
  "scheduled_time_slot": null,
15
  "due_date": "2025-11-28T13:03:13.104571Z",
16
  "sla_target_date": "2025-11-28T13:03:13.104571Z",
17
  "sla_violated": true,
18
+ "started_at": "2025-12-13T11:35:04.461432Z",
19
+ "completed_at": "2025-12-13T12:27:07.446205Z",
20
  "is_invoiced": false,
21
  "invoiced_at": null,
22
  "project_region_id": "24510a5a-13a6-4334-9055-b4d476aa9e0a",
23
+ "work_location_latitude": "-1.1004887",
24
+ "work_location_longitude": "37.0174947",
25
+ "work_location_verified": true,
26
+ "completion_data": {
27
+ "odu_serial": "23448609",
28
+ "ont_serial": "2345637",
29
+ "odu_imei_number": "agri67o324567943yt2",
30
+ "activated_number": "1234835976325"
31
+ },
32
+ "completion_photos_verified": true,
33
+ "completion_data_verified": true,
34
+ "notes": "Ticket reopened by Project Manager on 2025-12-13 06:02.\n\n\n[COMPLETION] work was done",
35
  "version": 1,
36
  "created_at": "2025-11-25T13:03:13.104571Z",
37
+ "updated_at": "2025-12-13T12:27:07.705372Z",
38
  "project_title": null,
39
  "region_name": null,
40
  "customer_name": null,
41
+ "is_open": false,
42
  "is_assigned": false,
43
  "is_in_progress": false,
44
+ "is_completed": true,
45
  "is_cancelled": false,
46
+ "is_active": false,
47
+ "is_overdue": false,
48
  "has_region": true,
49
  "has_schedule": true,
50
+ "can_be_assigned": false,
51
  "can_be_started": false,
52
  "can_be_completed": false,
53
+ "can_be_cancelled": false
54
  },
55
  "available_actions": [],
56
  "current_assignment": {
57
+ "id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
58
+ "action": "completed",
59
  "status": "CLOSED",
60
+ "journey_started": true,
61
+ "arrived": true,
62
  "ended": true,
63
  "can_drop": false,
64
+ "has_location_trail": true
65
  },
66
  "team_info": {
67
  "required_size": 1,
 
69
  "is_full": false,
70
  "assigned_agents": []
71
  },
72
+ "message": "Ticket TicketStatus.COMPLETED",
73
  "source_data": {
74
  "type": "sales_order",
75
  "id": "b2a47ac2-e779-4479-846b-f350f041dd43",
 
92
  "location_longitude": null
93
  },
94
  "expenses": [
95
+ {
96
+ "id": "cd981aa0-c664-4128-8710-412e5005794c",
97
+ "ticket_assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
98
+ "incurred_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
99
+ "incurred_by_user_name": "Viyisa Sasa",
100
+ "category": "transport",
101
+ "description": "test timesheet 3",
102
+ "expense_date": "2025-12-13",
103
+ "quantity": 1.0,
104
+ "unit": "unit",
105
+ "unit_cost": 20.0,
106
+ "total_cost": 20.0,
107
+ "receipt_document_id": null,
108
+ "location_verified": false,
109
+ "verification_notes": "Not verified: No GPS location found for 2025-12-13. Manual review required.",
110
+ "is_approved": false,
111
+ "approved_by_user_id": null,
112
+ "approved_by_user_name": null,
113
+ "approved_at": null,
114
+ "rejection_reason": null,
115
+ "is_paid": false,
116
+ "paid_to_user_id": null,
117
+ "paid_to_user_name": null,
118
+ "paid_at": null,
119
+ "payment_reference": null,
120
+ "payment_recipient_type": "agent",
121
+ "payment_method": "send_money",
122
+ "payment_details": {
123
+ "phone_number": "+254799459782",
124
+ "recipient_name": "Viyisa"
125
+ },
126
+ "notes": "seems the managers approval actions dont trigger a recalculation",
127
+ "created_at": "2025-12-13T11:21:48.583475+00:00",
128
+ "updated_at": "2025-12-13T11:21:48.648548+00:00"
129
+ },
130
+ {
131
+ "id": "de87f0fc-68df-418e-9208-194c7a1d4f44",
132
+ "ticket_assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
133
+ "incurred_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
134
+ "incurred_by_user_name": "Viyisa Sasa",
135
+ "category": "materials",
136
+ "description": "test timesheets 2",
137
+ "expense_date": "2025-12-13",
138
+ "quantity": 1.0,
139
+ "unit": "unit",
140
+ "unit_cost": 10.0,
141
+ "total_cost": 10.0,
142
+ "receipt_document_id": null,
143
+ "location_verified": false,
144
+ "verification_notes": "Not verified: No GPS location found for 2025-12-13. Manual review required.",
145
+ "is_approved": true,
146
+ "approved_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
147
+ "approved_by_user_name": "Project Manager",
148
+ "approved_at": "2025-12-13T11:33:58.366027+00:00",
149
+ "rejection_reason": null,
150
+ "is_paid": false,
151
+ "paid_to_user_id": null,
152
+ "paid_to_user_name": null,
153
+ "paid_at": null,
154
+ "payment_reference": null,
155
+ "payment_recipient_type": "agent",
156
+ "payment_method": "send_money",
157
+ "payment_details": {
158
+ "phone_number": "+254799459782",
159
+ "recipient_name": "Viyisa"
160
+ },
161
+ "notes": "10 bob",
162
+ "created_at": "2025-12-13T11:19:07.521199+00:00",
163
+ "updated_at": "2025-12-13T11:33:58.649908+00:00"
164
+ },
165
+ {
166
+ "id": "29df452a-ceb5-4bcb-8d3f-e4b732ab46fd",
167
+ "ticket_assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
168
+ "incurred_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
169
+ "incurred_by_user_name": "Viyisa Sasa",
170
+ "category": "transport",
171
+ "description": "testing vendor payment",
172
+ "expense_date": "2025-12-13",
173
+ "quantity": 1.0,
174
+ "unit": "unit",
175
+ "unit_cost": 1000.0,
176
+ "total_cost": 1000.0,
177
+ "receipt_document_id": null,
178
+ "location_verified": false,
179
+ "verification_notes": "Not verified: No GPS location found for 2025-12-13. Manual review required.",
180
+ "is_approved": true,
181
+ "approved_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
182
+ "approved_by_user_name": "Project Manager",
183
+ "approved_at": "2025-12-13T11:20:38.092678+00:00",
184
+ "rejection_reason": null,
185
+ "is_paid": false,
186
+ "paid_to_user_id": null,
187
+ "paid_to_user_name": null,
188
+ "paid_at": null,
189
+ "payment_reference": null,
190
+ "payment_recipient_type": "vendor",
191
+ "payment_method": "till_number",
192
+ "payment_details": {
193
+ "till_number": "2342356",
194
+ "business_name": "Shelli"
195
+ },
196
+ "notes": "pay for fuel to shell",
197
+ "created_at": "2025-12-13T10:52:33.770683+00:00",
198
+ "updated_at": "2025-12-13T11:20:38.092684+00:00"
199
+ },
200
+ {
201
+ "id": "f41ef6b4-9b47-48cf-8ed0-b59aa20a0757",
202
+ "ticket_assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
203
+ "incurred_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
204
+ "incurred_by_user_name": "Viyisa Sasa",
205
+ "category": "transport",
206
+ "description": "boda boda",
207
+ "expense_date": "2025-12-13",
208
+ "quantity": 1.0,
209
+ "unit": "unit",
210
+ "unit_cost": 100.0,
211
+ "total_cost": 100.0,
212
+ "receipt_document_id": null,
213
+ "location_verified": false,
214
+ "verification_notes": "Not verified: No GPS location found for 2025-12-13. Manual review required.",
215
+ "is_approved": true,
216
+ "approved_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
217
+ "approved_by_user_name": "Project Manager",
218
+ "approved_at": "2025-12-13T09:54:53.641819+00:00",
219
+ "rejection_reason": null,
220
+ "is_paid": false,
221
+ "paid_to_user_id": null,
222
+ "paid_to_user_name": null,
223
+ "paid_at": null,
224
+ "payment_reference": null,
225
+ "payment_recipient_type": "agent",
226
+ "payment_method": "send_money",
227
+ "payment_details": {
228
+ "phone_number": "+254799459782",
229
+ "recipient_name": "Viyisa"
230
+ },
231
+ "notes": "",
232
+ "created_at": "2025-12-13T09:51:22.487710+00:00",
233
+ "updated_at": "2025-12-13T09:54:53.641825+00:00"
234
+ },
235
  {
236
  "id": "7b697e88-c4c1-4366-b05e-c0689a90451c",
237
  "ticket_assignment_id": "4fe9d28c-b657-4672-9138-78edabb9749b",
 
352
  "receipt_document_id": null,
353
  "location_verified": false,
354
  "verification_notes": "Not verified: No GPS location found for 2025-12-10. Manual review required.",
355
+ "is_approved": true,
356
+ "approved_by_user_id": "c5cf92be-4172-4fe2-af5c-f05d83b3a938",
357
+ "approved_by_user_name": "Project Manager",
358
+ "approved_at": "2025-12-13T10:40:29.510366+00:00",
359
  "rejection_reason": null,
360
  "is_paid": false,
361
  "paid_to_user_id": null,
 
370
  },
371
  "notes": "",
372
  "created_at": "2025-12-10T12:02:36.667890+00:00",
373
+ "updated_at": "2025-12-13T10:40:29.510372+00:00"
374
  },
375
  {
376
  "id": "db836b45-5d6b-4e67-a12a-4619c0fb3f38",
 
443
  "updated_at": "2025-12-10T11:53:42.062179+00:00"
444
  }
445
  ],
446
+ "images": [
447
+ {
448
+ "id": "bff7ba68-c130-40b7-822a-ee6a60ce8afe",
449
+ "image_url": "https://res.cloudinary.com/dnhajmziu/image/upload/v1765628806/ticket_1e622599_ticket_photo_jcc_jcc_20251213_122646_mitchell-luo-xrezotwzdrq-unspl.webp",
450
+ "image_type": "completion",
451
+ "description": "[JCC] Completion photo for ticket",
452
+ "captured_at": "2025-12-13T12:26:46.680907+00:00",
453
+ "uploaded_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
454
+ "created_at": "2025-12-13T12:26:46.681819+00:00"
455
+ },
456
+ {
457
+ "id": "362a3d38-d474-4287-b244-e98e77d4f8b3",
458
+ "image_url": "https://res.cloudinary.com/dnhajmziu/image/upload/v1765628805/ticket_1e622599_ticket_photo_airtel_network_airtel_network_20251213_122645_edd47bc048acb0cc2a4894bc38edfd.webp",
459
+ "image_type": "completion",
460
+ "description": "[Airtel network] Completion photo for ticket",
461
+ "captured_at": "2025-12-13T12:26:45.985738+00:00",
462
+ "uploaded_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
463
+ "created_at": "2025-12-13T12:26:45.986471+00:00"
464
+ },
465
+ {
466
+ "id": "b7f89ca8-42b4-46d2-a4ab-17b1f9b0bd0a",
467
+ "image_url": "https://res.cloudinary.com/dnhajmziu/image/upload/v1765628800/ticket_1e622599_ticket_photo_odu_outdoor_image_odu_outdoor_image_20251213_122640_cosinewall.webp",
468
+ "image_type": "completion",
469
+ "description": "[ODU outdoor image] Completion photo for ticket",
470
+ "captured_at": "2025-12-13T12:26:41.169208+00:00",
471
+ "uploaded_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
472
+ "created_at": "2025-12-13T12:26:41.169955+00:00"
473
+ },
474
+ {
475
+ "id": "121b707d-14eb-415d-ac25-4bc33c46d5c2",
476
+ "image_url": "https://res.cloudinary.com/dnhajmziu/image/upload/v1765627093/ticket_1e622599_ticket_photo_speedtest_speedtest_20251213_115813_6dc8524b572ff4739383212fdca9f0.webp",
477
+ "image_type": "completion",
478
+ "description": "[Speedtest] Completion photo for ticket",
479
+ "captured_at": "2025-12-13T11:58:13.454327+00:00",
480
+ "uploaded_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
481
+ "created_at": "2025-12-13T11:58:13.488897+00:00"
482
+ }
483
+ ],
484
  "comments": [],
485
  "assignments": [
486
+ {
487
+ "id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
488
+ "user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
489
+ "user_name": "Viyisa Sasa",
490
+ "action": "completed",
491
+ "status": "CLOSED",
492
+ "assigned_at": "2025-12-13T07:17:52.093114+00:00",
493
+ "responded_at": "2025-12-13T07:17:52.093117+00:00",
494
+ "journey_started_at": "2025-12-13T11:35:04.461382+00:00",
495
+ "arrived_at": "2025-12-13T11:54:22.341317+00:00",
496
+ "ended_at": "2025-12-13T12:27:07.462199+00:00"
497
+ },
498
  {
499
  "id": "4fe9d28c-b657-4672-9138-78edabb9749b",
500
  "user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
 
509
  }
510
  ],
511
  "status_history": [
512
+ {
513
+ "id": "412d5cdc-2da4-4002-9408-db4bf858a556",
514
+ "old_status": "in_progress",
515
+ "new_status": "completed",
516
+ "changed_at": "2025-12-13T12:27:07.468055+00:00",
517
+ "changed_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
518
+ "changed_by_user_name": "viya",
519
+ "assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
520
+ "change_reason": "Ticket completed with all requirements satisfied",
521
+ "notes": "work was done",
522
+ "location_latitude": null,
523
+ "location_longitude": null,
524
+ "location_accuracy": null,
525
+ "location_verified": false,
526
+ "communication_method": "app",
527
+ "additional_metadata": {}
528
+ },
529
+ {
530
+ "id": "d3665227-ee7d-4208-a961-3735e3db6a8a",
531
+ "old_status": "assigned",
532
+ "new_status": "in_progress",
533
+ "changed_at": "2025-12-13T11:35:04.461449+00:00",
534
+ "changed_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
535
+ "changed_by_user_name": "viya",
536
+ "assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
537
+ "change_reason": "Agent started journey to site",
538
+ "notes": null,
539
+ "location_latitude": -1.1004173,
540
+ "location_longitude": 37.0174369,
541
+ "location_accuracy": null,
542
+ "location_verified": false,
543
+ "communication_method": "app",
544
+ "additional_metadata": {}
545
+ },
546
+ {
547
+ "id": "d6559a5d-29df-486b-a3f4-f490cc4e3f71",
548
+ "old_status": "open",
549
+ "new_status": "assigned",
550
+ "changed_at": "2025-12-13T07:17:52.155290+00:00",
551
+ "changed_by_user_id": "43b778b0-2062-4724-abbb-916a4835a9b0",
552
+ "changed_by_user_name": "viya",
553
+ "assignment_id": "e2096a93-8f55-4063-92c5-2bc7122b3228",
554
+ "change_reason": "Self-assigned by agent",
555
+ "notes": null,
556
+ "location_latitude": null,
557
+ "location_longitude": null,
558
+ "location_accuracy": null,
559
+ "location_verified": false,
560
+ "communication_method": "app",
561
+ "additional_metadata": {}
562
+ },
563
  {
564
  "id": "aafc41f2-5543-4a6d-a8d8-10b6c3df3a79",
565
  "old_status": "pending_review",
 
612
  "additional_metadata": {}
613
  }
614
  ]
615
+ }
616
+
617
+
618
+
619
+ next i need you to notice that image names are being contructed wrong, when giving the response for all images we should include image name and this should be contructed well, look into the
620
+
621
+
622
+
623
+ Request URL
624
+
625
+ https://kamau1-swiftops-backend.hf.space/api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/detail
626
+
627
+ Request Method
628
+
629
+ GET
630
+
631
+ Status Code
632
+
633
+ 200 OK
634
+
635
+ Remote Address
636
+
637
+ 3.226.56.242:443
638
+
639
+ Referrer Policy
640
+
641
+ strict-origin-when-cross-origin
642
+
643
+
644
+
645
+
646
+
647
+ i think the image name should be something like
648
+
649
+ Completion Speedtest
650
+
651
+
652
+
653
+ in the project table photo requirements row we are storing the requirements as
654
+
655
+
656
+
657
+ [
658
+
659
+   {
660
+
661
+     "type": "Speedtest",
662
+
663
+     "required": true,
664
+
665
+     "max_photos": 1,
666
+
667
+     "min_photos": 1,
668
+
669
+     "description": "speed test collected "
670
+
671
+   },
672
+
673
+   {
674
+
675
+     "type": "Airtel network",
676
+
677
+     "required": true,
678
+
679
+     "max_photos": 1,
680
+
681
+     "min_photos": 1,
682
+
683
+     "description": "strength metrics"
684
+
685
+   },
686
+
687
+   {
688
+
689
+     "type": "ODU outdoor image",
690
+
691
+     "required": true,
692
+
693
+     "max_photos": 1,
694
+
695
+     "min_photos": 1,
696
+
697
+     "description": "take a photo of the odu antenna"
698
+
699
+   },
700
+
701
+   {
702
+
703
+     "type": "JCC",
704
+
705
+     "required": true,
706
+
707
+     "max_photos": 1,
708
+
709
+     "min_photos": 1,
710
+
711
+     "description": "Job Completion Form"
712
+
713
+   }
714
+
715
+ ]
716
+
717
+
718
+
719
+
720
+
721
+ this structure is very okay
docs/devlogs/db/logs.sql CHANGED
@@ -1 +1 @@
1
- INSERT INTO "public"."timesheets" ("id", "user_id", "project_id", "work_date", "status", "check_in_time", "check_out_time", "hours_worked", "leave_reason", "leave_approved_by_user_id", "notes", "additional_metadata", "created_at", "updated_at", "deleted_at", "tickets_assigned", "tickets_completed", "tickets_rescheduled", "tickets_cancelled", "tickets_rejected", "is_payroll_generated", "payroll_id", "total_expenses", "approved_expenses", "pending_expenses", "rejected_expenses", "expense_claims_count", "inventory_issued_count", "inventory_issued_value", "inventory_installed_count", "inventory_consumed_count", "inventory_returned_count", "inventory_returned_value", "inventory_lost_count", "inventory_damaged_count", "inventory_loss_value", "inventory_on_hand_count", "inventory_on_hand_value", "inventory_details", "version") VALUES ('39d65205-7d0a-4081-9eb2-e581c767992f', '43b778b0-2062-4724-abbb-916a4835a9b0', '0ade6bd1-e492-4e25-b681-59f42058d29a', '2025-12-11', 'on_leave', null, null, null, 'please', null, null, '{}', '2025-12-10 08:12:03.015561+00', '2025-12-10 08:12:03.015564+00', null, '0', '0', '0', '0', '0', 'false', null, '0.00', '0.00', '0.00', '0.00', '0', '0', '0.00', '0', '0', '0', '0.00', '0', '0', '0.00', '0', '0.00', '[]', '1'), ('7d9fa491-c16d-42b2-8115-b02d98873b64', '43b778b0-2062-4724-abbb-916a4835a9b0', '0ade6bd1-e492-4e25-b681-59f42058d29a', '2025-12-13', 'present', '2025-12-13 11:35:04.461382+00', null, null, null, null, null, '{}', '2025-12-13 07:17:52.420939+00', '2025-12-13 11:54:22.418767+00', null, '1', '0', '0', '0', '0', 'false', null, '1130.00', '1110.00', '20.00', '0.00', '4', '0', '0.00', '0', '0', '0', '0.00', '0', '0', '0.00', '0', '0.00', '[]', '7');
 
1
+ INSERT INTO "public"."timesheets" ("id", "user_id", "project_id", "work_date", "status", "check_in_time", "check_out_time", "hours_worked", "leave_reason", "leave_approved_by_user_id", "notes", "additional_metadata", "created_at", "updated_at", "deleted_at", "tickets_assigned", "tickets_completed", "tickets_rescheduled", "tickets_cancelled", "tickets_rejected", "is_payroll_generated", "payroll_id", "total_expenses", "approved_expenses", "pending_expenses", "rejected_expenses", "expense_claims_count", "inventory_issued_count", "inventory_issued_value", "inventory_installed_count", "inventory_consumed_count", "inventory_returned_count", "inventory_returned_value", "inventory_lost_count", "inventory_damaged_count", "inventory_loss_value", "inventory_on_hand_count", "inventory_on_hand_value", "inventory_details", "version") VALUES ('39d65205-7d0a-4081-9eb2-e581c767992f', '43b778b0-2062-4724-abbb-916a4835a9b0', '0ade6bd1-e492-4e25-b681-59f42058d29a', '2025-12-11', 'on_leave', null, null, null, 'please', null, null, '{}', '2025-12-10 08:12:03.015561+00', '2025-12-10 08:12:03.015564+00', null, '0', '0', '0', '0', '0', 'false', null, '0.00', '0.00', '0.00', '0.00', '0', '0', '0.00', '0', '0', '0', '0.00', '0', '0', '0.00', '0', '0.00', '[]', '1'), ('7d9fa491-c16d-42b2-8115-b02d98873b64', '43b778b0-2062-4724-abbb-916a4835a9b0', '0ade6bd1-e492-4e25-b681-59f42058d29a', '2025-12-13', 'present', '2025-12-13 11:35:04.461382+00', '2025-12-13 12:27:07.462199+00', '0.87', null, null, null, '{}', '2025-12-13 07:17:52.420939+00', '2025-12-13 12:27:07.66546+00', null, '0', '1', '0', '0', '0', 'false', null, '1130.00', '1110.00', '20.00', '0.00', '4', '0', '0.00', '0', '0', '0', '0.00', '0', '0', '0.00', '0', '0.00', '[]', '8');
docs/devlogs/server/runtimeerror.txt CHANGED
@@ -65,4 +65,86 @@ INFO: 2025-12-13T12:00:58 - app.services.ticket_completion_service: Completion d
65
  INFO: 10.16.13.79:7379 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-data HTTP/1.1" 200 OK
66
  INFO: 2025-12-13T12:01:47 - app.services.ticket_completion_service: Completion data replaced for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
67
  INFO: 10.16.13.79:38355 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-data HTTP/1.1" 200 OK
68
- INFO: 10.16.37.13:40215 - "GET /health HTTP/1.1" 200 OK
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  INFO: 10.16.13.79:7379 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-data HTTP/1.1" 200 OK
66
  INFO: 2025-12-13T12:01:47 - app.services.ticket_completion_service: Completion data replaced for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
67
  INFO: 10.16.13.79:38355 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-data HTTP/1.1" 200 OK
68
+ INFO: 10.16.37.13:40215 - "GET /health HTTP/1.1" 200 OK
69
+ INFO: 10.16.13.79:12569 - "GET /health HTTP/1.1" 200 OK
70
+ INFO: 10.16.13.79:65340 - "GET /health HTTP/1.1" 200 OK
71
+ INFO: 10.16.37.13:43113 - "GET /health HTTP/1.1" 200 OK
72
+ INFO: 10.16.37.13:22852 - "GET /health HTTP/1.1" 200 OK
73
+ INFO: 10.16.13.79:60321 - "GET /health HTTP/1.1" 200 OK
74
+ INFO: 10.16.37.13:1805 - "GET /health HTTP/1.1" 200 OK
75
+ INFO: 10.16.37.13:36073 - "GET /health HTTP/1.1" 200 OK
76
+ INFO: 10.16.37.13:33817 - "GET /health HTTP/1.1" 200 OK
77
+ INFO: 10.16.13.79:3410 - "GET /api/v1/projects/0ade6bd1-e492-4e25-b681-59f42058d29a/regions/24510a5a-13a6-4334-9055-b4d476aa9e0a HTTP/1.1" 200 OK
78
+ INFO: 10.16.13.79:3749 - "GET /health HTTP/1.1" 200 OK
79
+ INFO: 2025-12-13T12:13:30 - app.core.supabase_auth: Session refreshed successfully
80
+ INFO: 2025-12-13T12:13:30 - app.api.v1.auth: ✅ Token refreshed successfully for: viyisa8151@feralrex.com
81
+ INFO: 10.16.13.79:1170 - "POST /api/v1/auth/refresh-token HTTP/1.1" 200 OK
82
+ INFO: 10.16.13.79:53898 - "GET /health HTTP/1.1" 200 OK
83
+ INFO: 2025-12-13T12:15:21 - app.core.supabase_auth: Session refreshed successfully
84
+ INFO: 2025-12-13T12:15:21 - app.api.v1.auth: ✅ Token refreshed successfully for: nadina73@nembors.com
85
+ INFO: 10.16.13.79:42393 - "POST /api/v1/auth/refresh-token HTTP/1.1" 200 OK
86
+ INFO: 10.16.13.79:57294 - "GET /health HTTP/1.1" 200 OK
87
+ INFO: 10.16.37.13:42214 - "GET /health HTTP/1.1" 200 OK
88
+ INFO: 10.16.37.13:41635 - "GET /health HTTP/1.1" 200 OK
89
+ INFO: 10.16.37.13:51146 - "GET /health HTTP/1.1" 200 OK
90
+ INFO: 10.16.13.79:47342 - "GET /health HTTP/1.1" 200 OK
91
+ INFO: 10.16.13.79:19339 - "GET /health HTTP/1.1" 200 OK
92
+ INFO: 10.16.37.13:27317 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-checklist HTTP/1.1" 200 OK
93
+ INFO: 10.16.37.13:12938 - "GET /health HTTP/1.1" 200 OK
94
+ INFO: 10.16.13.79:4932 - "GET /health HTTP/1.1" 200 OK
95
+ INFO: 10.16.13.79:59672 - "GET /health HTTP/1.1" 200 OK
96
+ INFO: 10.16.13.79:56468 - "GET /health HTTP/1.1" 200 OK
97
+ INFO: 10.16.13.79:8562 - "GET /health HTTP/1.1" 200 OK
98
+ INFO: 10.16.37.13:62050 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-checklist HTTP/1.1" 200 OK
99
+ INFO: 10.16.13.79:1292 - "GET /health HTTP/1.1" 200 OK
100
+ INFO: 10.16.13.79:1292 - "GET /health HTTP/1.1" 200 OK
101
+ INFO: 2025-12-13T12:21:18 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
102
+ INFO: 2025-12-13T12:21:18 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
103
+ INFO: 10.16.13.79:1292 - "GET /api/v1/auth/me HTTP/1.1" 200 OK
104
+ INFO: 2025-12-13T12:21:21 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
105
+ INFO: 2025-12-13T12:21:21 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
106
+ INFO: 10.16.37.13:4404 - "GET /api/v1/auth/me/preferences HTTP/1.1" 200 OK
107
+ INFO: 2025-12-13T12:21:22 - app.api.deps: Checking active user: 43b778b0-2062-4724-abbb-916a4835a9b0, is_active: True, type: <class 'bool'>
108
+ INFO: 2025-12-13T12:21:22 - app.api.deps: User 43b778b0-2062-4724-abbb-916a4835a9b0 is active - proceeding
109
+ INFO: 10.16.37.13:4404 - "GET /api/v1/auth/me/preferences/available-apps HTTP/1.1" 200 OK
110
+ INFO: 10.16.13.79:45992 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/detail HTTP/1.1" 200 OK
111
+ INFO: 10.16.13.79:60551 - "GET /api/v1/projects/0ade6bd1-e492-4e25-b681-59f42058d29a/regions/24510a5a-13a6-4334-9055-b4d476aa9e0a HTTP/1.1" 200 OK
112
+ INFO: 10.16.37.13:31190 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-checklist HTTP/1.1" 200 OK
113
+ INFO: 10.16.13.79:26917 - "GET /health HTTP/1.1" 200 OK
114
+ INFO: 10.16.13.79:50629 - "GET /health HTTP/1.1" 200 OK
115
+ INFO: 10.16.13.79:44565 - "GET /health HTTP/1.1" 200 OK
116
+ INFO: 10.16.37.13:56961 - "GET /health HTTP/1.1" 200 OK
117
+ INFO: 2025-12-13T12:26:37 - app.services.ticket_completion_service: Completion data replaced for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
118
+ INFO: 10.16.37.13:60562 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-data HTTP/1.1" 200 OK
119
+ INFO: 2025-12-13T12:26:40 - app.services.media_service: Using forced provider: cloudinary
120
+ INFO: 2025-12-13T12:26:40 - app.services.media_service: Uploading cosinewall.jpg as ticket_1e622599_ticket_photo_odu_outdoor_image_odu_outdoor_image_20251213_122640_cosinewall.jpg to cloudinary
121
+ INFO: 2025-12-13T12:26:41 - app.integrations.cloudinary: Uploaded file to Cloudinary: https://res.cloudinary.com/dnhajmziu/image/upload/v1765628800/ticket_1e622599_ticket_photo_odu_outdoor_image_odu_outdoor_image_20251213_122640_cosinewall.webp
122
+ INFO: 2025-12-13T12:26:41 - app.services.media_service: Document created: c9585dbe-7cfe-484e-bc34-618fa5c246d4 (version 1)
123
+ INFO: 2025-12-13T12:26:41 - app.services.ticket_completion_service: Photos updated for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e: ['ODU outdoor image']
124
+ INFO: 10.16.13.79:16972 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/upload-photos HTTP/1.1" 200 OK
125
+ INFO: 2025-12-13T12:26:45 - app.services.media_service: Using forced provider: cloudinary
126
+ INFO: 2025-12-13T12:26:45 - app.services.media_service: Uploading edd47bc048acb0cc2a4894bc38edfd45.jpg as ticket_1e622599_ticket_photo_airtel_network_airtel_network_20251213_122645_edd47bc048acb0cc2a4894bc38edfd.jpg to cloudinary
127
+ INFO: 2025-12-13T12:26:45 - app.integrations.cloudinary: Uploaded file to Cloudinary: https://res.cloudinary.com/dnhajmziu/image/upload/v1765628805/ticket_1e622599_ticket_photo_airtel_network_airtel_network_20251213_122645_edd47bc048acb0cc2a4894bc38edfd.webp
128
+ INFO: 2025-12-13T12:26:45 - app.services.media_service: Document created: ce3f8249-3342-4e2d-b6f2-0e70bafe9119 (version 1)
129
+ INFO: 2025-12-13T12:26:46 - app.services.ticket_completion_service: Photos updated for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e: ['Airtel network']
130
+ INFO: 10.16.13.79:31310 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/upload-photos HTTP/1.1" 200 OK
131
+ INFO: 2025-12-13T12:26:46 - app.services.media_service: Using forced provider: cloudinary
132
+ INFO: 2025-12-13T12:26:46 - app.services.media_service: Uploading mitchell-luo-xREzOtWzDrQ-unsplash.jpg as ticket_1e622599_ticket_photo_jcc_jcc_20251213_122646_mitchell-luo-xrezotwzdrq-unspl.jpg to cloudinary
133
+ INFO: 2025-12-13T12:26:46 - app.integrations.cloudinary: Uploaded file to Cloudinary: https://res.cloudinary.com/dnhajmziu/image/upload/v1765628806/ticket_1e622599_ticket_photo_jcc_jcc_20251213_122646_mitchell-luo-xrezotwzdrq-unspl.webp
134
+ INFO: 2025-12-13T12:26:46 - app.services.media_service: Document created: 38671308-25b8-44e6-8d92-7fc1c0df617a (version 1)
135
+ INFO: 2025-12-13T12:26:46 - app.services.ticket_completion_service: Photos updated for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e: ['JCC']
136
+ INFO: 10.16.13.79:25745 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/upload-photos HTTP/1.1" 200 OK
137
+ INFO: 10.16.37.13:42000 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/completion-checklist HTTP/1.1" 200 OK
138
+ INFO: 10.16.37.13:1308 - "GET /health HTTP/1.1" 200 OK
139
+ INFO: 10.16.13.79:61029 - "GET /health HTTP/1.1" 200 OK
140
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_location_service: Derived work location for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e from assignment e2096a93-8f55-4063-92c5-2bc7122b3228: (-1.1004887, 37.0174947)
141
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Work location update for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e: action=derived, success=True, message=Work location derived from arrival coordinates
142
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Closed assignment e2096a93-8f55-4063-92c5-2bc7122b3228 for completed ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
143
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Status history created: TicketStatus.IN_PROGRESS → completed for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
144
+ WARNING: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Serial 2345637 reported in ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e but not found in inventory assignments
145
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Equipment tracking complete for ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e: 0 items marked as installed
146
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Creating subscription from ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e
147
+ INFO: 2025-12-13T12:27:07 - app.services.timesheet_realtime_service: Timesheet 7d9fa491-c16d-42b2-8115-b02d98873b64 updated for ticket_completed. Entity: ticket:1e622599-1909-49b9-9d8b-4c5cb483b29e
148
+ INFO: 2025-12-13T12:27:07 - app.services.ticket_completion_service: Ticket 1e622599-1909-49b9-9d8b-4c5cb483b29e completed successfully
149
+ INFO: 10.16.37.13:17817 - "POST /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/complete HTTP/1.1" 200 OK
150
+ INFO: 10.16.37.13:17817 - "GET /api/v1/tickets/1e622599-1909-49b9-9d8b-4c5cb483b29e/detail HTTP/1.1" 200 OK
src/app/services/timesheet_realtime_service.py CHANGED
@@ -127,21 +127,24 @@ class TimesheetRealtimeService:
127
  try:
128
  # Query assignments for this day
129
  result = self.db.query(
130
- # Count by action type
131
- # tickets_assigned: count 'assigned' and 'accepted' (self-assignments are auto-accepted)
132
- func.count(
133
- case((TicketAssignment.action.in_(['assigned', 'accepted']), 1))
134
- ).label('tickets_assigned'),
 
135
  func.count(
136
  case((TicketAssignment.action == 'completed', 1))
137
  ).label('tickets_completed'),
 
 
138
  func.count(
139
  case((TicketAssignment.action == 'rejected', 1))
140
  ).label('tickets_rejected'),
 
 
141
  # NOTE: 'dropped' assignments are counted as 'cancelled' here
142
  # Semantic: Agent drops → PENDING_REVIEW (not truly cancelled)
143
- # True cancellation is done by managers, but we don't track that separately yet
144
- # TODO: Consider adding tickets_dropped field to distinguish agent drops from manager cancellations
145
  func.count(
146
  case((TicketAssignment.action == 'dropped', 1))
147
  ).label('tickets_cancelled'),
 
127
  try:
128
  # Query assignments for this day
129
  result = self.db.query(
130
+ # Count by action type - HISTORICAL COUNTS (what happened today)
131
+ # tickets_assigned: count ALL assignments created today (regardless of current state)
132
+ # This gives a true picture of workload assigned, even if later completed/rejected
133
+ func.count(TicketAssignment.id).label('tickets_assigned'),
134
+
135
+ # tickets_completed: count assignments that reached 'completed' state
136
  func.count(
137
  case((TicketAssignment.action == 'completed', 1))
138
  ).label('tickets_completed'),
139
+
140
+ # tickets_rejected: count assignments that were rejected
141
  func.count(
142
  case((TicketAssignment.action == 'rejected', 1))
143
  ).label('tickets_rejected'),
144
+
145
+ # tickets_cancelled: count assignments that were dropped
146
  # NOTE: 'dropped' assignments are counted as 'cancelled' here
147
  # Semantic: Agent drops → PENDING_REVIEW (not truly cancelled)
 
 
148
  func.count(
149
  case((TicketAssignment.action == 'dropped', 1))
150
  ).label('tickets_cancelled'),