@fleetbase/fleetops-engine 0.6.57 → 0.6.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (693) hide show
  1. package/README.md +3 -0
  2. package/addon/components/customer/form.hbs +0 -49
  3. package/addon/components/entity/card.hbs +1 -0
  4. package/addon/components/modals/order-label.hbs +1 -1
  5. package/addon/components/place/form.hbs +1 -1
  6. package/addon/services/entity-actions.js +32 -0
  7. package/addon/styles/fleetops-engine.css +0 -1
  8. package/composer.json +15 -3
  9. package/extension.json +1 -1
  10. package/index.js +7 -1
  11. package/package.json +3 -2
  12. package/phpunit.xml.dist +4 -4
  13. package/pnpm-workspace.yaml +2 -1
  14. package/scripts/coverage-file-runner.php +240 -0
  15. package/scripts/coverage-runner.php +52 -0
  16. package/scripts/coverage-summary.php +149 -0
  17. package/scripts/pest-bootstrap.php +411 -0
  18. package/scripts/pest-file-runner.php +94 -0
  19. package/scripts/pest-runner.php +119 -0
  20. package/server/resources/views/labels/entity-label.php +4 -4
  21. package/server/src/Casts/MultiPolygon.php +8 -3
  22. package/server/src/Casts/OrderConfigEntities.php +10 -3
  23. package/server/src/Casts/Point.php +10 -6
  24. package/server/src/Casts/Polygon.php +5 -2
  25. package/server/src/Console/Commands/AssignCustomerRoles.php +28 -6
  26. package/server/src/Console/Commands/AssignDriverRoles.php +36 -6
  27. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +6 -1
  28. package/server/src/Console/Commands/DispatchAdhocOrders.php +12 -2
  29. package/server/src/Console/Commands/FixCustomerCompanies.php +46 -11
  30. package/server/src/Console/Commands/FixDriverCompanies.php +18 -3
  31. package/server/src/Console/Commands/FixLegacyOrderConfigs.php +31 -6
  32. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +94 -42
  33. package/server/src/Console/Commands/PurgeUnpurchasedServiceQuotes.php +42 -12
  34. package/server/src/Console/Commands/ReplayVehicleLocations.php +120 -55
  35. package/server/src/Console/Commands/SendDriverNotification.php +25 -4
  36. package/server/src/Console/Commands/SendMaintenanceReminders.php +43 -23
  37. package/server/src/Console/Commands/SimulateOrderRouteNavigation.php +36 -6
  38. package/server/src/Console/Commands/TrackOrderDistanceAndTime.php +6 -1
  39. package/server/src/Events/DriverLocationChanged.php +1 -1
  40. package/server/src/Events/DriverSimulatedLocationChanged.php +1 -1
  41. package/server/src/Events/EntityActivityChanged.php +1 -1
  42. package/server/src/Events/EntityCompleted.php +1 -1
  43. package/server/src/Events/EntityDriverAssigned.php +1 -1
  44. package/server/src/Events/FuelProviderTransactionImported.php +1 -1
  45. package/server/src/Events/FuelProviderTransactionMatched.php +1 -1
  46. package/server/src/Events/FuelProviderTransactionUnmatched.php +1 -1
  47. package/server/src/Events/FuelReportCreatedFromProvider.php +1 -1
  48. package/server/src/Events/GeofenceDwelled.php +1 -1
  49. package/server/src/Events/GeofenceEntered.php +1 -1
  50. package/server/src/Events/GeofenceExited.php +1 -1
  51. package/server/src/Events/VehicleLocationChanged.php +1 -1
  52. package/server/src/Events/WaypointActivityChanged.php +1 -1
  53. package/server/src/Events/WaypointCompleted.php +1 -1
  54. package/server/src/Exceptions/IntegratedVendorException.php +2 -2
  55. package/server/src/Exceptions/TelematicRateLimitExceededException.php +1 -1
  56. package/server/src/Http/Controllers/Api/v1/ContactController.php +89 -22
  57. package/server/src/Http/Controllers/Api/v1/CustomerController.php +296 -97
  58. package/server/src/Http/Controllers/Api/v1/DeviceController.php +48 -14
  59. package/server/src/Http/Controllers/Api/v1/DriverController.php +168 -63
  60. package/server/src/Http/Controllers/Api/v1/EntityController.php +112 -66
  61. package/server/src/Http/Controllers/Api/v1/EquipmentController.php +40 -10
  62. package/server/src/Http/Controllers/Api/v1/FleetController.php +54 -14
  63. package/server/src/Http/Controllers/Api/v1/FuelReportController.php +54 -14
  64. package/server/src/Http/Controllers/Api/v1/FuelTransactionController.php +36 -11
  65. package/server/src/Http/Controllers/Api/v1/GeofenceController.php +39 -21
  66. package/server/src/Http/Controllers/Api/v1/IssueController.php +54 -14
  67. package/server/src/Http/Controllers/Api/v1/LabelController.php +61 -22
  68. package/server/src/Http/Controllers/Api/v1/NavigatorController.php +21 -5
  69. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +35 -5
  70. package/server/src/Http/Controllers/Api/v1/OrderController.php +388 -203
  71. package/server/src/Http/Controllers/Api/v1/PartController.php +40 -10
  72. package/server/src/Http/Controllers/Api/v1/PayloadController.php +90 -30
  73. package/server/src/Http/Controllers/Api/v1/PlaceController.php +188 -91
  74. package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +100 -36
  75. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +96 -26
  76. package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +169 -81
  77. package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +104 -75
  78. package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +83 -14
  79. package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +66 -16
  80. package/server/src/Http/Controllers/Api/v1/VehicleController.php +121 -67
  81. package/server/src/Http/Controllers/Api/v1/VendorController.php +54 -14
  82. package/server/src/Http/Controllers/Api/v1/WorkOrderController.php +47 -12
  83. package/server/src/Http/Controllers/Api/v1/ZoneController.php +84 -24
  84. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +15 -5
  85. package/server/src/Http/Controllers/Internal/v1/ContactController.php +113 -28
  86. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +36 -11
  87. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +9 -4
  88. package/server/src/Http/Controllers/Internal/v1/DriverController.php +167 -69
  89. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +18 -3
  90. package/server/src/Http/Controllers/Internal/v1/FleetController.php +137 -60
  91. package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +24 -9
  92. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +18 -3
  93. package/server/src/Http/Controllers/Internal/v1/GeocoderController.php +26 -8
  94. package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +12 -2
  95. package/server/src/Http/Controllers/Internal/v1/IntegratedVendorController.php +27 -6
  96. package/server/src/Http/Controllers/Internal/v1/IssueController.php +82 -29
  97. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +34 -26
  98. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +141 -97
  99. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +28 -8
  100. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +21 -12
  101. package/server/src/Http/Controllers/Internal/v1/MorphController.php +90 -18
  102. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +88 -35
  103. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +149 -39
  104. package/server/src/Http/Controllers/Internal/v1/OrderConfigController.php +54 -18
  105. package/server/src/Http/Controllers/Internal/v1/OrderController.php +298 -86
  106. package/server/src/Http/Controllers/Internal/v1/PartController.php +18 -3
  107. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +46 -14
  108. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +31 -5
  109. package/server/src/Http/Controllers/Internal/v1/ProofController.php +88 -48
  110. package/server/src/Http/Controllers/Internal/v1/SearchController.php +1 -1
  111. package/server/src/Http/Controllers/Internal/v1/SensorController.php +6 -1
  112. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +6 -1
  113. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +255 -105
  114. package/server/src/Http/Controllers/Internal/v1/SettingController.php +88 -35
  115. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -4
  116. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +42 -14
  117. package/server/src/Http/Controllers/Internal/v1/VendorController.php +103 -33
  118. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +42 -12
  119. package/server/src/Http/Filter/DriverFilter.php +2 -2
  120. package/server/src/Http/Filter/OrderFilter.php +1 -1
  121. package/server/src/Http/Filter/VehicleFilter.php +21 -7
  122. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +12 -2
  123. package/server/src/Http/Requests/BulkDispatchRequest.php +3 -9
  124. package/server/src/Http/Requests/CancelOrderRequest.php +2 -6
  125. package/server/src/Http/Requests/CreateContactRequest.php +2 -6
  126. package/server/src/Http/Requests/CreateDeviceRequest.php +2 -2
  127. package/server/src/Http/Requests/CreateDriverRequest.php +3 -9
  128. package/server/src/Http/Requests/CreateEntityRequest.php +2 -6
  129. package/server/src/Http/Requests/CreateEquipmentRequest.php +2 -2
  130. package/server/src/Http/Requests/CreateFleetRequest.php +2 -6
  131. package/server/src/Http/Requests/CreateFuelReportRequest.php +2 -6
  132. package/server/src/Http/Requests/CreateFuelTransactionRequest.php +2 -2
  133. package/server/src/Http/Requests/CreateIssueRequest.php +2 -6
  134. package/server/src/Http/Requests/CreateOrderRequest.php +7 -8
  135. package/server/src/Http/Requests/CreatePartRequest.php +2 -2
  136. package/server/src/Http/Requests/CreatePayloadRequest.php +7 -9
  137. package/server/src/Http/Requests/CreatePlaceRequest.php +2 -6
  138. package/server/src/Http/Requests/CreatePurchaseRateRequest.php +2 -6
  139. package/server/src/Http/Requests/CreateSensorRequest.php +2 -2
  140. package/server/src/Http/Requests/CreateServiceAreaRequest.php +2 -6
  141. package/server/src/Http/Requests/CreateServiceQuoteRequest.php +2 -6
  142. package/server/src/Http/Requests/CreateServiceRateRequest.php +2 -6
  143. package/server/src/Http/Requests/CreateTrackingNumberRequest.php +2 -6
  144. package/server/src/Http/Requests/CreateTrackingStatusRequest.php +2 -6
  145. package/server/src/Http/Requests/CreateVehicleRequest.php +2 -6
  146. package/server/src/Http/Requests/CreateVendorRequest.php +2 -6
  147. package/server/src/Http/Requests/CreateWorkOrderRequest.php +2 -2
  148. package/server/src/Http/Requests/CreateZoneRequest.php +2 -6
  149. package/server/src/Http/Requests/DecodeTrackingNumberQR.php +2 -6
  150. package/server/src/Http/Requests/DriverSimulationRequest.php +2 -6
  151. package/server/src/Http/Requests/Internal/AssignOrderRequest.php +3 -7
  152. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +4 -12
  153. package/server/src/Http/Requests/Internal/CreateOrderConfigRequest.php +2 -6
  154. package/server/src/Http/Requests/Internal/CreateOrderRequest.php +7 -8
  155. package/server/src/Http/Requests/Internal/FleetActionRequest.php +18 -12
  156. package/server/src/Http/Requests/Internal/UpdateDriverRequest.php +6 -3
  157. package/server/src/Http/Requests/QueryServiceQuotesRequest.php +2 -6
  158. package/server/src/Http/Requests/ScheduleOrderRequest.php +2 -6
  159. package/server/src/Http/Requests/UpdateIssueRequest.php +1 -3
  160. package/server/src/Http/Resources/v1/Customer.php +2 -2
  161. package/server/src/Http/Resources/v1/Maintenance.php +3 -4
  162. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +3 -4
  163. package/server/src/Http/Resources/v1/Order.php +3 -7
  164. package/server/src/Http/Resources/v1/PurchaseRate.php +4 -6
  165. package/server/src/Http/Resources/v1/TrackingStatus.php +4 -6
  166. package/server/src/Http/Resources/v1/WorkOrder.php +3 -4
  167. package/server/src/Imports/ContactImport.php +10 -1
  168. package/server/src/Imports/DriverImport.php +10 -1
  169. package/server/src/Imports/EquipmentImport.php +6 -1
  170. package/server/src/Imports/FleetImport.php +10 -1
  171. package/server/src/Imports/FuelReportImport.php +10 -1
  172. package/server/src/Imports/IssueImport.php +10 -1
  173. package/server/src/Imports/MaintenanceImport.php +6 -1
  174. package/server/src/Imports/MaintenanceScheduleImport.php +6 -1
  175. package/server/src/Imports/PartImport.php +6 -1
  176. package/server/src/Imports/PlaceImport.php +10 -1
  177. package/server/src/Imports/VehicleImport.php +10 -1
  178. package/server/src/Imports/VendorImport.php +10 -1
  179. package/server/src/Imports/WorkOrderImport.php +6 -1
  180. package/server/src/Integrations/Lalamove/Lalamove.php +3 -7
  181. package/server/src/Integrations/Lalamove/LalamoveServiceType.php +5 -8
  182. package/server/src/Jobs/CheckGeofenceDwell.php +41 -16
  183. package/server/src/Jobs/FinalizeApiOrderCreation.php +17 -2
  184. package/server/src/Jobs/FinalizeInternalOrderCreation.php +11 -1
  185. package/server/src/Jobs/NotifyBulkAssignedDriver.php +29 -16
  186. package/server/src/Jobs/ProcessAllocationJob.php +59 -25
  187. package/server/src/Jobs/ReplayPositions.php +17 -10
  188. package/server/src/Jobs/SendPositionReplay.php +18 -7
  189. package/server/src/Jobs/SimulateDrivingRoute.php +14 -3
  190. package/server/src/Jobs/SyncFuelProviderTransactionsJob.php +12 -2
  191. package/server/src/Listeners/HandleDeliveryCompletion.php +11 -1
  192. package/server/src/Listeners/HandleGeofenceDwelled.php +6 -1
  193. package/server/src/Listeners/HandleGeofenceEntered.php +4 -2
  194. package/server/src/Listeners/HandleGeofenceExited.php +6 -1
  195. package/server/src/Listeners/HandleOrderCanceled.php +18 -3
  196. package/server/src/Listeners/HandleOrderDispatchFailed.php +12 -2
  197. package/server/src/Listeners/HandleOrderDispatched.php +61 -26
  198. package/server/src/Listeners/HandleOrderDriverAssigned.php +12 -2
  199. package/server/src/Listeners/HandleUserRemovedFromCompany.php +19 -6
  200. package/server/src/Listeners/NotifyDriverOnShiftChange.php +24 -4
  201. package/server/src/Listeners/NotifyOrderEvent.php +11 -6
  202. package/server/src/Models/Contact.php +6 -7
  203. package/server/src/Models/Device.php +26 -3
  204. package/server/src/Models/Driver.php +0 -6
  205. package/server/src/Models/Entity.php +4 -4
  206. package/server/src/Models/Equipment.php +1 -1
  207. package/server/src/Models/FuelReport.php +5 -1
  208. package/server/src/Models/IntegratedVendor.php +2 -2
  209. package/server/src/Models/Issue.php +5 -1
  210. package/server/src/Models/MaintenanceSchedule.php +25 -10
  211. package/server/src/Models/Order.php +18 -15
  212. package/server/src/Models/OrderConfig.php +1 -1
  213. package/server/src/Models/Payload.php +9 -16
  214. package/server/src/Models/Place.php +20 -13
  215. package/server/src/Models/ServiceArea.php +3 -6
  216. package/server/src/Models/ServiceQuote.php +5 -0
  217. package/server/src/Models/ServiceRate.php +13 -6
  218. package/server/src/Models/Telematic.php +2 -2
  219. package/server/src/Models/TrackingNumber.php +55 -10
  220. package/server/src/Models/TrackingStatus.php +24 -4
  221. package/server/src/Models/Waypoint.php +44 -10
  222. package/server/src/Models/Zone.php +3 -6
  223. package/server/src/Notifications/DriverArrivedAtGeofence.php +1 -1
  224. package/server/src/Notifications/OrderDispatched.php +3 -9
  225. package/server/src/Notifications/OrderPing.php +3 -9
  226. package/server/src/Observers/CategoryObserver.php +6 -1
  227. package/server/src/Observers/CompanyObserver.php +5 -0
  228. package/server/src/Observers/CompanyUserObserver.php +12 -2
  229. package/server/src/Observers/DriverObserver.php +19 -4
  230. package/server/src/Observers/FleetObserver.php +6 -1
  231. package/server/src/Observers/PurchaseRateObserver.php +66 -11
  232. package/server/src/Observers/ServiceAreaObserver.php +12 -2
  233. package/server/src/Observers/ServiceRateObserver.php +21 -6
  234. package/server/src/Observers/TrackingNumberObserver.php +19 -4
  235. package/server/src/Observers/UserObserver.php +6 -1
  236. package/server/src/Observers/VehicleObserver.php +27 -7
  237. package/server/src/Observers/WorkOrderObserver.php +27 -7
  238. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +8 -3
  239. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +5 -3
  240. package/server/src/Providers/FleetOpsServiceProvider.php +12 -0
  241. package/server/src/Providers/NotificationServiceProvider.php +6 -0
  242. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +36 -26
  243. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +18 -5
  244. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +6 -1
  245. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +26 -6
  246. package/server/src/Support/Analytics/FuelEfficiency.php +9 -3
  247. package/server/src/Support/Analytics/FuelProviderSummary.php +14 -5
  248. package/server/src/Support/Analytics/GeofenceViolations.php +40 -20
  249. package/server/src/Support/Analytics/IssuesInsights.php +38 -18
  250. package/server/src/Support/Analytics/MaintenanceOverview.php +51 -21
  251. package/server/src/Support/Analytics/OnTimeDelivery.php +3 -2
  252. package/server/src/Support/Analytics/OperationsPulse.php +59 -35
  253. package/server/src/Support/Analytics/OrdersByStatus.php +12 -7
  254. package/server/src/Support/Analytics/RevenueTrend.php +16 -6
  255. package/server/src/Support/Analytics/TopDrivers.php +3 -2
  256. package/server/src/Support/FleetOps.php +109 -104
  257. package/server/src/Support/Geocoding.php +52 -14
  258. package/server/src/Support/GeofenceIntersectionService.php +47 -27
  259. package/server/src/Support/LiveOrderQuery.php +6 -1
  260. package/server/src/Support/Metrics/ActiveRevenueQuery.php +3 -2
  261. package/server/src/Support/Metrics/FuelCostsMetric.php +6 -1
  262. package/server/src/Support/ResolvesOrderServiceStops.php +10 -0
  263. package/server/src/Support/Telematics/Providers/GeotabProvider.php +9 -4
  264. package/server/src/Support/Utils.php +122 -16
  265. package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +6 -1
  266. package/server/src/Tracking/Providers/OsrmTrackingProvider.php +11 -6
  267. package/server/src/Tracking/TrackingOptions.php +6 -1
  268. package/server/src/Tracking/TrackingProviderManager.php +2 -3
  269. package/server/src/Tracking/TrackingProviderRegistry.php +8 -4
  270. package/server/src/Traits/PayloadAccessors.php +6 -1
  271. package/server/src/routes.php +23 -27
  272. package/server/tests/AiOperationalQueryCapabilityTest.php +457 -1
  273. package/server/tests/AiPreviewCapabilityContractsTest.php +180 -0
  274. package/server/tests/AnalyticsRoutesTest.php +384 -3
  275. package/server/tests/AnalyticsWidgetContractsTest.php +740 -0
  276. package/server/tests/ApiContactControllerContractsTest.php +284 -0
  277. package/server/tests/ApiCustomerControllerContractsTest.php +962 -0
  278. package/server/tests/ApiDriverControllerContractsTest.php +571 -0
  279. package/server/tests/ApiEntityControllerContractsTest.php +329 -0
  280. package/server/tests/ApiEquipmentControllerContractsTest.php +239 -0
  281. package/server/tests/ApiFleetControllerContractsTest.php +176 -0
  282. package/server/tests/ApiFuelReportControllerContractsTest.php +217 -0
  283. package/server/tests/ApiIssueControllerContractsTest.php +217 -0
  284. package/server/tests/ApiOrderControllerContractsTest.php +1030 -0
  285. package/server/tests/ApiPartControllerContractsTest.php +251 -0
  286. package/server/tests/ApiPayloadControllerContractsTest.php +314 -0
  287. package/server/tests/ApiPlaceControllerContractsTest.php +541 -0
  288. package/server/tests/ApiPurchaseRateControllerContractsTest.php +283 -0
  289. package/server/tests/ApiServiceAreaZoneControllerContractsTest.php +442 -0
  290. package/server/tests/ApiServiceQuoteControllerContractsTest.php +322 -0
  291. package/server/tests/ApiTrackingNumberControllerContractsTest.php +225 -0
  292. package/server/tests/ApiTrackingStatusControllerContractsTest.php +254 -0
  293. package/server/tests/ApiVehicleControllerContractsTest.php +368 -0
  294. package/server/tests/ApiVendorControllerContractsTest.php +199 -0
  295. package/server/tests/AssetAndPlaceSearchContractsTest.php +220 -0
  296. package/server/tests/BackendSmallModelContractsTest.php +354 -0
  297. package/server/tests/CapacityAllocationEngineTest.php +56 -0
  298. package/server/tests/CommandContractsTest.php +2695 -0
  299. package/server/tests/CompactResourceSerializationTest.php +2745 -0
  300. package/server/tests/ControllerFilterContractsTest.php +1273 -0
  301. package/server/tests/ControllerHelperContractsTest.php +1740 -0
  302. package/server/tests/ControllerSerializationContractsTest.php +141 -0
  303. package/server/tests/CoverageSummaryScriptTest.php +93 -0
  304. package/server/tests/CustomerContactIdentitySafetyTest.php +1 -6
  305. package/server/tests/CustomerEndpointTest.php +166 -1
  306. package/server/tests/DriverFilterExecutionTest.php +263 -0
  307. package/server/tests/DriverGeofenceProcessingTest.php +153 -0
  308. package/server/tests/DriverResourceSerializationTest.php +245 -0
  309. package/server/tests/DriverSchedulingTraitContractsTest.php +259 -0
  310. package/server/tests/EventContractsTest.php +1259 -0
  311. package/server/tests/ExceptionContractsTest.php +81 -0
  312. package/server/tests/ExpansionContractsTest.php +242 -0
  313. package/server/tests/ExportCoverageTest.php +116 -0
  314. package/server/tests/Feature/Http/Api/ContactControllerPhotoAndDeleteTest.php +199 -0
  315. package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +831 -0
  316. package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +347 -0
  317. package/server/tests/Feature/Http/Api/DeviceControllerCrudTest.php +176 -0
  318. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +451 -0
  319. package/server/tests/Feature/Http/Api/DriverControllerHelpersTest.php +240 -0
  320. package/server/tests/Feature/Http/Api/DriverControllerLookupTest.php +186 -0
  321. package/server/tests/Feature/Http/Api/DriverControllerOrganizationsTest.php +112 -0
  322. package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +207 -0
  323. package/server/tests/Feature/Http/Api/DriverControllerSwitchOrgAndGeofenceTest.php +329 -0
  324. package/server/tests/Feature/Http/Api/DriverControllerTrackTest.php +383 -0
  325. package/server/tests/Feature/Http/Api/EntityControllerHelpersTest.php +150 -0
  326. package/server/tests/Feature/Http/Api/FuelTransactionControllerContractsTest.php +352 -0
  327. package/server/tests/Feature/Http/Api/GeofenceControllerContractsTest.php +364 -0
  328. package/server/tests/Feature/Http/Api/LabelControllerSubjectsTest.php +148 -0
  329. package/server/tests/Feature/Http/Api/OrderConfigControllerTest.php +112 -0
  330. package/server/tests/Feature/Http/Api/OrderControllerCreateTest.php +279 -0
  331. package/server/tests/Feature/Http/Api/OrderControllerIntegratedVendorTest.php +295 -0
  332. package/server/tests/Feature/Http/Api/OrderControllerLifecycleActionsTest.php +279 -0
  333. package/server/tests/Feature/Http/Api/OrderControllerLookupHelpersTest.php +207 -0
  334. package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +282 -0
  335. package/server/tests/Feature/Http/Api/OrderControllerPersistenceHelpersTest.php +216 -0
  336. package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +184 -0
  337. package/server/tests/Feature/Http/Api/OrderControllerStartActivityTest.php +663 -0
  338. package/server/tests/Feature/Http/Api/PayloadControllerHelpersTest.php +134 -0
  339. package/server/tests/Feature/Http/Api/PlaceAndServiceQuoteSeamsTest.php +479 -0
  340. package/server/tests/Feature/Http/Api/PurchaseRateControllerHelpersTest.php +176 -0
  341. package/server/tests/Feature/Http/Api/PurchaseRateControllerOrderCreationTest.php +268 -0
  342. package/server/tests/Feature/Http/Api/SensorControllerCrudTest.php +239 -0
  343. package/server/tests/Feature/Http/Api/ServiceAreaControllerBorderAndSeamsTest.php +187 -0
  344. package/server/tests/Feature/Http/Api/ServiceQuoteControllerQueryTest.php +405 -0
  345. package/server/tests/Feature/Http/Api/ServiceQuoteControllerVendorBridgeTest.php +212 -0
  346. package/server/tests/Feature/Http/Api/ServiceRateControllerContractsTest.php +263 -0
  347. package/server/tests/Feature/Http/Api/SmallApiControllerHelpersTest.php +272 -0
  348. package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +154 -0
  349. package/server/tests/Feature/Http/Api/TrackingStatusControllerHelpersTest.php +165 -0
  350. package/server/tests/Feature/Http/Api/VehicleControllerTrackingTest.php +483 -0
  351. package/server/tests/Feature/Http/Api/WorkOrderControllerContractsTest.php +326 -0
  352. package/server/tests/Feature/Http/Api/WorkOrderControllerHelpersTest.php +130 -0
  353. package/server/tests/Feature/Http/Api/ZoneControllerBordersAndSeamsTest.php +294 -0
  354. package/server/tests/Feature/Http/FleetOpsRouteRegistrationTest.php +189 -0
  355. package/server/tests/Feature/Http/Internal/AnalyticsControllerSeamsTest.php +53 -0
  356. package/server/tests/Feature/Http/Internal/ContactControllerContractsTest.php +240 -0
  357. package/server/tests/Feature/Http/Internal/ContactControllerCustomerGuardsTest.php +128 -0
  358. package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +294 -0
  359. package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +196 -0
  360. package/server/tests/Feature/Http/Internal/DeviceControllerContractsTest.php +370 -0
  361. package/server/tests/Feature/Http/Internal/DeviceControllerExportAndFiltersTest.php +151 -0
  362. package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +1109 -0
  363. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +233 -0
  364. package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +710 -0
  365. package/server/tests/Feature/Http/Internal/DriverControllerHelpersTest.php +218 -0
  366. package/server/tests/Feature/Http/Internal/FleetAssignmentsAndTriggersTest.php +198 -0
  367. package/server/tests/Feature/Http/Internal/FleetOpsLookupControllerDbTest.php +90 -0
  368. package/server/tests/Feature/Http/Internal/HubControllerTest.php +206 -0
  369. package/server/tests/Feature/Http/Internal/IssueControllerContractsTest.php +404 -0
  370. package/server/tests/Feature/Http/Internal/IssueControllerExportImportTest.php +118 -0
  371. package/server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php +246 -0
  372. package/server/tests/Feature/Http/Internal/LiveControllerTest.php +778 -0
  373. package/server/tests/Feature/Http/Internal/MaintenanceScheduleControllerContractsTest.php +263 -0
  374. package/server/tests/Feature/Http/Internal/MaintenanceScheduleControllerSurfacesTest.php +214 -0
  375. package/server/tests/Feature/Http/Internal/MetricsControllerContractsTest.php +249 -0
  376. package/server/tests/Feature/Http/Internal/MetricsControllerHelpersTest.php +66 -0
  377. package/server/tests/Feature/Http/Internal/MorphControllerHelpersTest.php +125 -0
  378. package/server/tests/Feature/Http/Internal/NavigatorControllerHelpersTest.php +201 -0
  379. package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +928 -0
  380. package/server/tests/Feature/Http/Internal/OrchestrationControllerHelpersTest.php +214 -0
  381. package/server/tests/Feature/Http/Internal/OrchestrationControllerImportOrdersTest.php +340 -0
  382. package/server/tests/Feature/Http/Internal/OrderConfigControllerContractsTest.php +314 -0
  383. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +663 -0
  384. package/server/tests/Feature/Http/Internal/OrderControllerCapturePhotoTest.php +507 -0
  385. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +1742 -0
  386. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +488 -0
  387. package/server/tests/Feature/Http/Internal/OrderControllerHelperSeamsTest.php +298 -0
  388. package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +267 -0
  389. package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +179 -0
  390. package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +80 -0
  391. package/server/tests/Feature/Http/Internal/PaymentControllerPaymentsTest.php +154 -0
  392. package/server/tests/Feature/Http/Internal/PlaceControllerTest.php +252 -0
  393. package/server/tests/Feature/Http/Internal/PositionControllerReplayMetricsTest.php +163 -0
  394. package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +219 -0
  395. package/server/tests/Feature/Http/Internal/SearchControllerEndpointTest.php +151 -0
  396. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerHelpersTest.php +208 -0
  397. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerQueryRecordTest.php +217 -0
  398. package/server/tests/Feature/Http/Internal/ServiceQuoteControllerVendorBridgeTest.php +239 -0
  399. package/server/tests/Feature/Http/Internal/ServiceQuotePreliminaryQueryTest.php +271 -0
  400. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +133 -0
  401. package/server/tests/Feature/Http/Internal/SmallInternalControllersTest.php +180 -0
  402. package/server/tests/Feature/Http/Internal/TelematicControllerEndpointsTest.php +417 -0
  403. package/server/tests/Feature/Http/Internal/VehicleControllerEndpointsTest.php +361 -0
  404. package/server/tests/Feature/Http/Internal/VendorControllerEndpointsTest.php +203 -0
  405. package/server/tests/Feature/Http/Internal/WorkOrderAndConfigSurfacesTest.php +289 -0
  406. package/server/tests/Feature/Http/Internal/WorkOrderControllerContractsTest.php +199 -0
  407. package/server/tests/Feature/Http/TelematicWebhookIngestTest.php +343 -0
  408. package/server/tests/Feature/Http/Telematics/TelematicWebhookControllerProcessingTest.php +254 -0
  409. package/server/tests/FleetOpsSupportContractsTest.php +187 -0
  410. package/server/tests/FlespiProviderContractsTest.php +244 -0
  411. package/server/tests/FlowResourceTest.php +295 -0
  412. package/server/tests/FuelProviderConnectionControllerContractsTest.php +303 -0
  413. package/server/tests/FuelProviderImplementationTest.php +753 -0
  414. package/server/tests/FuelProviderRegistryTest.php +7 -9
  415. package/server/tests/FuelProviderResourceContractsTest.php +171 -0
  416. package/server/tests/FuelProviderTransactionControllerContractsTest.php +181 -0
  417. package/server/tests/GeofenceIntersectionServiceContractsTest.php +164 -0
  418. package/server/tests/HOSConstraintContractsTest.php +128 -0
  419. package/server/tests/HubAndGettingStartedContractsTest.php +158 -0
  420. package/server/tests/ImportContractsTest.php +191 -0
  421. package/server/tests/InternalCustomerControllerContractsTest.php +238 -0
  422. package/server/tests/InternalFleetControllerContractsTest.php +389 -0
  423. package/server/tests/InternalImportExportControllerContractsTest.php +289 -0
  424. package/server/tests/InternalMetricsControllerContractsTest.php +69 -0
  425. package/server/tests/InternalPlaceWorkOrderControllerContractsTest.php +207 -0
  426. package/server/tests/InternalServiceQuoteCheckoutControllerContractsTest.php +470 -0
  427. package/server/tests/InternalVendorControllerContractsTest.php +331 -0
  428. package/server/tests/InventoryTelemetryModelContractsTest.php +516 -0
  429. package/server/tests/IssueTimelineContractsTest.php +191 -0
  430. package/server/tests/LabelPdfRenderingTest.php +57 -0
  431. package/server/tests/LalamoveIntegrationContractsTest.php +167 -0
  432. package/server/tests/LiveOrderQueryTest.php +140 -19
  433. package/server/tests/LookupAndGeocoderControllerContractsTest.php +357 -0
  434. package/server/tests/MaintainableTraitContractsTest.php +245 -0
  435. package/server/tests/MaintenanceScheduleControllerContractsTest.php +101 -0
  436. package/server/tests/MaintenanceWarrantyContractsTest.php +237 -0
  437. package/server/tests/ManifestControllerContractsTest.php +310 -0
  438. package/server/tests/MetricsRegistryTest.php +596 -10
  439. package/server/tests/MiddlewareContractsTest.php +150 -0
  440. package/server/tests/ModelAccessorContractsTest.php +3201 -0
  441. package/server/tests/MorphControllerContractsTest.php +410 -0
  442. package/server/tests/NavigatorControllerContractsTest.php +206 -0
  443. package/server/tests/NotificationAndMailContractsTest.php +677 -0
  444. package/server/tests/ObserverContractsTest.php +1414 -0
  445. package/server/tests/OperationalAlgorithmContractsTest.php +852 -0
  446. package/server/tests/OperationsMonitorEndpointTest.php +2 -1
  447. package/server/tests/OrchestrationPayloadBuilderTest.php +87 -0
  448. package/server/tests/OrderActivityFlowRegressionTest.php +354 -7
  449. package/server/tests/OrderDispatchListenerContractsTest.php +577 -0
  450. package/server/tests/OrderFilterExecutionTest.php +244 -0
  451. package/server/tests/OrderQueuedClosureRegressionTest.php +2 -1
  452. package/server/tests/PaymentControllerContractsTest.php +199 -0
  453. package/server/tests/PointResolutionTest.php +54 -0
  454. package/server/tests/ProcessAllocationJobContractsTest.php +224 -0
  455. package/server/tests/ProofAndSensorControllerContractsTest.php +44 -0
  456. package/server/tests/ProviderContractsTest.php +409 -0
  457. package/server/tests/PublicApiControllerInputContractsTest.php +294 -0
  458. package/server/tests/QueueJobContractsTest.php +764 -0
  459. package/server/tests/ReportSchemaContractsTest.php +271 -0
  460. package/server/tests/RequestContractsTest.php +966 -0
  461. package/server/tests/RouteRegistrationExecutionTest.php +178 -0
  462. package/server/tests/RouteSequencingEngineTest.php +179 -0
  463. package/server/tests/RulesAndCastsTest.php +246 -0
  464. package/server/tests/SamsaraProviderContractsTest.php +272 -0
  465. package/server/tests/SearchControllerContractsTest.php +311 -0
  466. package/server/tests/ServiceAreaZoneApiShapeTest.php +8 -9
  467. package/server/tests/ServiceRateAlgorithmWeightVariablesTest.php +1 -1
  468. package/server/tests/ServiceRateRouteValidationTest.php +76 -0
  469. package/server/tests/SettingControllerContractsTest.php +410 -0
  470. package/server/tests/SmallControllerContractsTest.php +456 -0
  471. package/server/tests/SupportAnalyticsCustomerAuthContractsTest.php +221 -0
  472. package/server/tests/SupportJobAndAiCoverageTest.php +812 -0
  473. package/server/tests/SupportServiceContractsTest.php +287 -0
  474. package/server/tests/SupportValueObjectsTest.php +155 -0
  475. package/server/tests/TelematicControllerContractsTest.php +185 -0
  476. package/server/tests/TelematicProviderRegistryTest.php +167 -0
  477. package/server/tests/TelematicServiceHelperContractsTest.php +315 -0
  478. package/server/tests/TelematicWebhookControllerContractsTest.php +253 -0
  479. package/server/tests/TelematicsHardeningTest.php +666 -105
  480. package/server/tests/TrackingIntelligenceTest.php +709 -26
  481. package/server/tests/TrackingNumberTraitContractsTest.php +106 -0
  482. package/server/tests/Unit/Casts/SpatialCastBranchesTest.php +75 -0
  483. package/server/tests/Unit/Console/AssignCustomerRolesCommandTest.php +102 -0
  484. package/server/tests/Unit/Console/AssignDriverRolesCommandTest.php +113 -0
  485. package/server/tests/Unit/Console/Commands/FixLegacyOrderConfigsHelpersTest.php +108 -0
  486. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +265 -0
  487. package/server/tests/Unit/Console/Commands/ReplayVehicleLocationsCommandTest.php +320 -0
  488. package/server/tests/Unit/Console/Commands/SimulateGeofenceEventsCommandTest.php +207 -0
  489. package/server/tests/Unit/Console/FixCommandsAndMaintenanceControllerTest.php +256 -0
  490. package/server/tests/Unit/Console/FleetOpsCommandsTest.php +198 -0
  491. package/server/tests/Unit/Console/ReplayVehicleLocationsCommandTest.php +252 -0
  492. package/server/tests/Unit/Console/SendMaintenanceRemindersTest.php +212 -0
  493. package/server/tests/Unit/Console/SimulateGeofenceEventsCommandTest.php +232 -0
  494. package/server/tests/Unit/Console/SyncTelematicsCommandTest.php +136 -0
  495. package/server/tests/Unit/Constraints/HOSConstraintTest.php +158 -0
  496. package/server/tests/Unit/Events/EventModelRecordTest.php +93 -0
  497. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +34 -0
  498. package/server/tests/Unit/Exports/ExportCollectionsTest.php +142 -0
  499. package/server/tests/Unit/Exports/OperationalExportMappingTest.php +651 -0
  500. package/server/tests/Unit/Exports/VehicleExportTest.php +284 -0
  501. package/server/tests/Unit/Flow/EventFiringTest.php +95 -0
  502. package/server/tests/Unit/Http/Api/QueryWithRequestSeamsTest.php +198 -0
  503. package/server/tests/Unit/Http/Api/SensorInputTest.php +95 -0
  504. package/server/tests/Unit/Http/Filter/DeviceFilterTest.php +199 -0
  505. package/server/tests/Unit/Http/Filter/EmptyValueGuardsTest.php +66 -0
  506. package/server/tests/Unit/Http/Filter/FilterDateRangesTest.php +72 -0
  507. package/server/tests/Unit/Http/Filter/FuelProviderTransactionFilterTest.php +172 -0
  508. package/server/tests/Unit/Http/Filter/InternalRelationUuidLookupTest.php +135 -0
  509. package/server/tests/Unit/Http/Filter/IssueFilterTest.php +129 -0
  510. package/server/tests/Unit/Http/Filter/PartFilterTest.php +173 -0
  511. package/server/tests/Unit/Http/Internal/ControllerDelegationSeamsTest.php +136 -0
  512. package/server/tests/Unit/Http/Internal/ControllerExcelSeamsTest.php +89 -0
  513. package/server/tests/Unit/Http/Middleware/SetupDriverSessionTest.php +32 -0
  514. package/server/tests/Unit/Http/Requests/CreateFuelReportRequestTest.php +47 -0
  515. package/server/tests/Unit/Http/Requests/FleetActionRequestTest.php +80 -0
  516. package/server/tests/Unit/Http/Requests/RequestAuthorizationTest.php +50 -0
  517. package/server/tests/Unit/Http/Resources/CustomerResourceTest.php +108 -0
  518. package/server/tests/Unit/Http/Resources/EntityCustomerTypeTest.php +35 -0
  519. package/server/tests/Unit/Http/Resources/FleetResourceTest.php +102 -0
  520. package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +210 -0
  521. package/server/tests/Unit/Http/Resources/IndexVehicleResourceTest.php +194 -0
  522. package/server/tests/Unit/Http/Resources/MaintenanceResourcesTest.php +182 -0
  523. package/server/tests/Unit/Http/Resources/OrderAndDriverResourceTest.php +121 -0
  524. package/server/tests/Unit/Http/Resources/OrderConfigResourceTest.php +47 -0
  525. package/server/tests/Unit/Http/Resources/PayloadResourceTest.php +111 -0
  526. package/server/tests/Unit/Http/Resources/RelationResourceFallbacksTest.php +32 -0
  527. package/server/tests/Unit/Http/Resources/ResourceRelationResolutionTest.php +55 -0
  528. package/server/tests/Unit/Http/Resources/TrackingNumberResourceTest.php +174 -0
  529. package/server/tests/Unit/Http/Resources/WaypointResourceTest.php +295 -0
  530. package/server/tests/Unit/Http/Resources/WorkOrderResourceTest.php +164 -0
  531. package/server/tests/Unit/Imports/ImportDelegationTest.php +151 -0
  532. package/server/tests/Unit/Integrations/Lalamove/LalamoveDeliveryStopTest.php +79 -0
  533. package/server/tests/Unit/Integrations/Lalamove/LalamoveLifecycleTest.php +211 -0
  534. package/server/tests/Unit/Integrations/Lalamove/LalamoveQuoteAndServiceOrderTest.php +381 -0
  535. package/server/tests/Unit/Integrations/Lalamove/LalamoveServiceTypeTest.php +51 -0
  536. package/server/tests/Unit/Integrations/LalamoveQuotationTest.php +194 -0
  537. package/server/tests/Unit/Integrations/LalamoveResponseHandlingTest.php +58 -0
  538. package/server/tests/Unit/Jobs/GeofenceDwellAndBulkNotifyTest.php +183 -0
  539. package/server/tests/Unit/Jobs/ReplayPositionsTest.php +57 -0
  540. package/server/tests/Unit/Jobs/SimulateWaypointReachedTest.php +92 -0
  541. package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +256 -0
  542. package/server/tests/Unit/Listeners/HandleOrderDispatchFailedListenerTest.php +101 -0
  543. package/server/tests/Unit/Listeners/HandleOrderDispatchedConcreteTest.php +203 -0
  544. package/server/tests/Unit/Listeners/HandleOrderDispatchedHelpersTest.php +186 -0
  545. package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +65 -0
  546. package/server/tests/Unit/Listeners/ListenerDelegationSeamsTest.php +83 -0
  547. package/server/tests/Unit/Models/AssetTest.php +469 -0
  548. package/server/tests/Unit/Models/ContactCustomerUserTest.php +370 -0
  549. package/server/tests/Unit/Models/ContactTest.php +577 -0
  550. package/server/tests/Unit/Models/DeviceEventTest.php +481 -0
  551. package/server/tests/Unit/Models/DeviceLastPositionDefaultTest.php +128 -0
  552. package/server/tests/Unit/Models/DriverModelHelpersTest.php +268 -0
  553. package/server/tests/Unit/Models/DriverTest.php +401 -0
  554. package/server/tests/Unit/Models/EntityTest.php +171 -0
  555. package/server/tests/Unit/Models/EquipmentTest.php +434 -0
  556. package/server/tests/Unit/Models/FleetTest.php +153 -0
  557. package/server/tests/Unit/Models/FuelProviderTransactionTest.php +82 -0
  558. package/server/tests/Unit/Models/FuelReportImportAndLabelsTest.php +193 -0
  559. package/server/tests/Unit/Models/ImportRowFallbackSeamsTest.php +100 -0
  560. package/server/tests/Unit/Models/IntegratedVendorLifecycleTest.php +203 -0
  561. package/server/tests/Unit/Models/IntegratedVendorTest.php +135 -0
  562. package/server/tests/Unit/Models/IssueTest.php +160 -0
  563. package/server/tests/Unit/Models/MaintenanceScheduleTest.php +319 -0
  564. package/server/tests/Unit/Models/MaintenanceTest.php +277 -0
  565. package/server/tests/Unit/Models/MaintenanceWorkOrderImportTest.php +161 -0
  566. package/server/tests/Unit/Models/ManifestStopTest.php +121 -0
  567. package/server/tests/Unit/Models/ManifestTest.php +163 -0
  568. package/server/tests/Unit/Models/ModelLookupFallbackSeamsTest.php +149 -0
  569. package/server/tests/Unit/Models/OrderConfigTest.php +341 -0
  570. package/server/tests/Unit/Models/OrderDynamicContractsTest.php +190 -0
  571. package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +426 -0
  572. package/server/tests/Unit/Models/OrderPayloadRouteTest.php +227 -0
  573. package/server/tests/Unit/Models/OrderPurchaseAndDriversTest.php +188 -0
  574. package/server/tests/Unit/Models/OrderTest.php +917 -0
  575. package/server/tests/Unit/Models/PartTest.php +399 -0
  576. package/server/tests/Unit/Models/PayloadTest.php +710 -0
  577. package/server/tests/Unit/Models/PayloadWaypointsAndEntitiesTest.php +317 -0
  578. package/server/tests/Unit/Models/PlaceCreationAndImportTest.php +384 -0
  579. package/server/tests/Unit/Models/PlaceGeocodingResultsTest.php +253 -0
  580. package/server/tests/Unit/Models/PlaceSharedAndGeocodingTest.php +271 -0
  581. package/server/tests/Unit/Models/PlaceTest.php +589 -0
  582. package/server/tests/Unit/Models/PositionTest.php +49 -0
  583. package/server/tests/Unit/Models/RouteTest.php +55 -0
  584. package/server/tests/Unit/Models/SensorReadingsTest.php +168 -0
  585. package/server/tests/Unit/Models/SensorTest.php +284 -0
  586. package/server/tests/Unit/Models/ServiceAreaGeometryTest.php +129 -0
  587. package/server/tests/Unit/Models/ServiceAreaTest.php +125 -0
  588. package/server/tests/Unit/Models/ServiceQuoteTest.php +617 -0
  589. package/server/tests/Unit/Models/ServiceRateFeeTest.php +73 -0
  590. package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +428 -0
  591. package/server/tests/Unit/Models/ServiceRateTest.php +533 -0
  592. package/server/tests/Unit/Models/SmallModelSurfacesTest.php +294 -0
  593. package/server/tests/Unit/Models/TrackingNumberTest.php +199 -0
  594. package/server/tests/Unit/Models/TrackingStatusTest.php +131 -0
  595. package/server/tests/Unit/Models/VehicleDeviceTest.php +115 -0
  596. package/server/tests/Unit/Models/VehiclePositionsAndImportTest.php +220 -0
  597. package/server/tests/Unit/Models/VehicleTest.php +266 -0
  598. package/server/tests/Unit/Models/VendorRelationsAndImportTest.php +107 -0
  599. package/server/tests/Unit/Models/WarrantyTest.php +187 -0
  600. package/server/tests/Unit/Models/WaypointTest.php +366 -0
  601. package/server/tests/Unit/Models/WorkOrderTest.php +272 -0
  602. package/server/tests/Unit/Models/ZoneTest.php +83 -0
  603. package/server/tests/Unit/Notifications/MonitoringNotificationsTest.php +136 -0
  604. package/server/tests/Unit/Notifications/OrderAssignedChannelsTest.php +53 -0
  605. package/server/tests/Unit/Notifications/OrderDispatchedChannelsTest.php +81 -0
  606. package/server/tests/Unit/Notifications/OrderFailedChannelsTest.php +73 -0
  607. package/server/tests/Unit/Notifications/OrderPingChannelsTest.php +69 -0
  608. package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +185 -0
  609. package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +133 -0
  610. package/server/tests/Unit/Observers/ObserverHelperExecutionTest.php +135 -0
  611. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +267 -0
  612. package/server/tests/Unit/Observers/PurchaseRateObserverAndOrderJobsTest.php +169 -0
  613. package/server/tests/Unit/Orchestration/Engines/DriverAssignmentEngineTest.php +393 -0
  614. package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +397 -0
  615. package/server/tests/Unit/Orchestration/OrchestrationEngineRegistryTest.php +45 -0
  616. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +253 -0
  617. package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +59 -0
  618. package/server/tests/Unit/Rules/ResolvableVehicleTest.php +85 -0
  619. package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +120 -0
  620. package/server/tests/Unit/Support/Ai/Capabilities/CreateOrderPreviewCapabilityTest.php +385 -0
  621. package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +226 -0
  622. package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +237 -0
  623. package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +124 -0
  624. package/server/tests/Unit/Support/AlgoEdgeBranchesTest.php +24 -0
  625. package/server/tests/Unit/Support/Analytics/GeofenceViolationsTest.php +109 -0
  626. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +205 -0
  627. package/server/tests/Unit/Support/Analytics/MaintenanceOverviewTest.php +236 -0
  628. package/server/tests/Unit/Support/Analytics/OnTimeDeliveryAndTopDriversTest.php +250 -0
  629. package/server/tests/Unit/Support/CreateOrderPreviewDraftingTest.php +235 -0
  630. package/server/tests/Unit/Support/CustomerAuthTest.php +132 -0
  631. package/server/tests/Unit/Support/FuelEfficiencyAndCanceledHandlersTest.php +164 -0
  632. package/server/tests/Unit/Support/FuelProviderRegistryResolutionTest.php +51 -0
  633. package/server/tests/Unit/Support/FuelProviderServiceMatchingTest.php +280 -0
  634. package/server/tests/Unit/Support/GeocodingInjectedClientTest.php +268 -0
  635. package/server/tests/Unit/Support/GeocodingLocateTest.php +32 -0
  636. package/server/tests/Unit/Support/GeofenceIntersectionServiceTest.php +188 -0
  637. package/server/tests/Unit/Support/GettingStartedTest.php +164 -0
  638. package/server/tests/Unit/Support/GuardReturnSeamsTest.php +172 -0
  639. package/server/tests/Unit/Support/IntegratedVendorsResolverTest.php +250 -0
  640. package/server/tests/Unit/Support/IssuesInsightsAndAllocationJobTest.php +156 -0
  641. package/server/tests/Unit/Support/MagicAccessorSeamsTest.php +54 -0
  642. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +215 -0
  643. package/server/tests/Unit/Support/Metrics/AvgOrderValueMetricTest.php +308 -0
  644. package/server/tests/Unit/Support/Metrics/CountMetricsTest.php +280 -0
  645. package/server/tests/Unit/Support/Metrics/FuelCostsMetricTest.php +102 -0
  646. package/server/tests/Unit/Support/Metrics/MetricQuerySeamsTest.php +129 -0
  647. package/server/tests/Unit/Support/Metrics/OrdersScheduledMetricTest.php +146 -0
  648. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +410 -0
  649. package/server/tests/Unit/Support/OperationalQueryDistributionTest.php +152 -0
  650. package/server/tests/Unit/Support/OperationsPulseTest.php +130 -0
  651. package/server/tests/Unit/Support/OptimizeOrderRouteCapabilityTest.php +147 -0
  652. package/server/tests/Unit/Support/OrchestrationPayloadBuilderTest.php +135 -0
  653. package/server/tests/Unit/Support/OrchestrationRouteAndVehicleTasksTest.php +254 -0
  654. package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +247 -0
  655. package/server/tests/Unit/Support/PetroAppFuelProviderTest.php +165 -0
  656. package/server/tests/Unit/Support/PlaceSearchRankingTest.php +191 -0
  657. package/server/tests/Unit/Support/QuerySeamsTest.php +309 -0
  658. package/server/tests/Unit/Support/ResolvesOrderServiceStopsDbTest.php +303 -0
  659. package/server/tests/Unit/Support/ResolvesOrderServiceStopsTest.php +382 -0
  660. package/server/tests/Unit/Support/SkipAndFallbackGuardsTest.php +70 -0
  661. package/server/tests/Unit/Support/TelematicCredentialResolutionTest.php +31 -0
  662. package/server/tests/Unit/Support/TelematicServiceTest.php +256 -0
  663. package/server/tests/Unit/Support/Telematics/Providers/AbstractProviderTest.php +154 -0
  664. package/server/tests/Unit/Support/Telematics/Providers/AfaqyProviderTest.php +191 -0
  665. package/server/tests/Unit/Support/Telematics/Providers/AfaqyProviderTransportTest.php +138 -0
  666. package/server/tests/Unit/Support/Telematics/Providers/GeotabProviderTest.php +329 -0
  667. package/server/tests/Unit/Support/Telematics/Providers/ProviderTransportSeamsTest.php +133 -0
  668. package/server/tests/Unit/Support/Telematics/Providers/SafeeProviderTest.php +487 -0
  669. package/server/tests/Unit/Support/Telematics/TelematicProviderRegistryTest.php +39 -0
  670. package/server/tests/Unit/Support/Telematics/TelematicServiceLifecycleTest.php +711 -0
  671. package/server/tests/Unit/Support/Telematics/TelematicServiceTelemetryTest.php +364 -0
  672. package/server/tests/Unit/Support/UtilsAdditionalTest.php +256 -0
  673. package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +382 -0
  674. package/server/tests/Unit/Tracking/TrackingContextTest.php +114 -0
  675. package/server/tests/Unit/Tracking/TrackingIntelligenceAndContextTest.php +477 -0
  676. package/server/tests/Unit/Tracking/TrackingProviderManagerFallbacksTest.php +208 -0
  677. package/server/tests/Unit/Traits/HasTrackingNumberTest.php +303 -0
  678. package/server/tests/Unit/Traits/PayloadAccessorsTest.php +150 -0
  679. package/server/tests/UtilsContractsTest.php +244 -0
  680. package/server/tests/VehicleControllerHelperContractsTest.php +488 -0
  681. package/server/tests/VehicleDataParsingTest.php +45 -0
  682. package/server/tests/VehicleDriverAssignmentTest.php +8 -9
  683. package/server/tests/VehicleResourceContractsTest.php +313 -0
  684. package/translations/bg-bg.yaml +2 -0
  685. package/translations/en-us.yaml +2 -0
  686. package/translations/es-pa.yaml +2 -0
  687. package/translations/fr-fr.yaml +2 -0
  688. package/translations/mn-mn.yaml +2 -0
  689. package/translations/pt-br.yaml +2 -0
  690. package/translations/ru-ru.yaml +2 -0
  691. package/server/src/Http/Controllers/Api/v1/OrganizationController.php +0 -45
  692. package/server/tests/DeviceFilterTest.php +0 -45
  693. package/server/tests/LiveControllerViewportTest.php +0 -60
@@ -59,30 +59,23 @@ class OrderController extends Controller
59
59
  set_time_limit(180);
60
60
 
61
61
  // get request input
62
- $input = $request->only([
63
- 'internal_id', 'payload', 'service_quote', 'purchase_rate',
64
- 'adhoc', 'adhoc_distance', 'pod_method', 'pod_required',
65
- 'scheduled_at', 'status', 'meta', 'notes',
66
- // Orchestrator constraints
67
- 'time_window_start', 'time_window_end',
68
- 'required_skills', 'orchestrator_priority',
69
- ]);
62
+ $input = $this->orderCreateInputFromRequest($request);
70
63
 
71
64
  // Get order config
72
- $orderConfig = OrderConfig::resolveFromIdentifier($request->only(['type', 'order_config']));
65
+ $orderConfig = $this->resolveOrderConfig($request->only(['type', 'order_config']));
73
66
  if (!$orderConfig) {
74
- return response()->apiError('Invalid order `type` or `order_config` provided.');
67
+ return $this->apiError('Invalid order `type` or `order_config` provided.');
75
68
  }
76
69
 
77
70
  // Set order config to input
78
71
  $input['order_config_uuid'] = $orderConfig->uuid;
79
- $input['type'] = $orderConfig->key;
72
+ $input['type'] = $orderConfig->key ?? 'transport';
80
73
 
81
74
  // make sure company is set
82
- $input['company_uuid'] = session('company');
75
+ $input['company_uuid'] = $this->sessionCompany();
83
76
 
84
77
  // resolve service quote if applicable
85
- $serviceQuote = ServiceQuote::resolveFromRequest($request);
78
+ $serviceQuote = $this->resolveServiceQuote($request);
86
79
  $integratedVendorOrder = null;
87
80
 
88
81
  // if service quote is applied, resolve it
@@ -97,18 +90,14 @@ class OrderController extends Controller
97
90
 
98
91
  // create payload
99
92
  if ($request->has('payload') && $request->isArray('payload')) {
100
- $payload = new Payload();
101
- $payloadInput = $request->input('payload');
102
- $entities = data_get($payloadInput, 'entities', []);
103
- $waypoints = data_get($payloadInput, 'waypoints', []);
104
- $pickup = data_get($payloadInput, 'pickup');
105
- $dropoff = data_get($payloadInput, 'dropoff');
106
- $return = data_get($payloadInput, 'return');
107
- $hasPickupField = array_key_exists('pickup', $payloadInput);
108
- $hasDropoffField = array_key_exists('dropoff', $payloadInput);
109
- $hasReturnField = array_key_exists('return', $payloadInput);
110
- $hasWaypointsField = array_key_exists('waypoints', $payloadInput);
111
- $hasRouteEndpointFields = $hasPickupField || $hasDropoffField || $hasReturnField;
93
+ $payload = $this->newPayload();
94
+ [
95
+ 'entities' => $entities,
96
+ 'waypoints' => $waypoints,
97
+ 'pickup' => $pickup,
98
+ 'dropoff' => $dropoff,
99
+ 'return' => $return,
100
+ ] = $this->payloadShapeFromArray($request->input('payload'));
112
101
 
113
102
  if ($pickup) {
114
103
  $payload->setPickup($pickup, [
@@ -140,22 +129,23 @@ class OrderController extends Controller
140
129
 
141
130
  $input['payload_uuid'] = $payload->uuid;
142
131
  } elseif ($request->isString('payload')) {
143
- $input['payload_uuid'] = Utils::getUuid('payloads', [
132
+ $input['payload_uuid'] = $this->getUuid('payloads', [
144
133
  'public_id' => $request->input('payload'),
145
- 'company_uuid' => session('company'),
134
+ 'company_uuid' => $this->sessionCompany(),
146
135
  ]);
147
136
  unset($input['payload']);
148
137
  }
149
138
 
150
139
  // create a payload if missing payload[] but has pickup/dropoff/etc
151
140
  if ($request->missing('payload')) {
152
- $payload = new Payload();
153
- $payloadInput = $request->only(['pickup', 'dropoff', 'return', 'waypoints', 'entities']);
154
- $entities = data_get($payloadInput, 'entities', []);
155
- $waypoints = data_get($payloadInput, 'waypoints', []);
156
- $pickup = data_get($payloadInput, 'pickup');
157
- $dropoff = data_get($payloadInput, 'dropoff');
158
- $return = data_get($payloadInput, 'return');
141
+ $payload = $this->newPayload();
142
+ [
143
+ 'entities' => $entities,
144
+ 'waypoints' => $waypoints,
145
+ 'pickup' => $pickup,
146
+ 'dropoff' => $dropoff,
147
+ 'return' => $return,
148
+ ] = $this->payloadShapeFromRequest($request);
159
149
 
160
150
  if ($pickup) {
161
151
  $payload->setPickup($pickup, [
@@ -190,7 +180,7 @@ class OrderController extends Controller
190
180
 
191
181
  // driver assignment
192
182
  if ($request->has('driver') && $integratedVendorOrder === null) {
193
- $driver = Driver::where(['public_id' => $request->input('driver'), 'company_uuid' => session('company')])->first();
183
+ $driver = $this->findDriverByPublicId($request->input('driver'));
194
184
  if ($driver) {
195
185
  $input['driver_assigned_uuid'] = $driver->uuid;
196
186
  // set vehicle assignmend from driver
@@ -202,19 +192,19 @@ class OrderController extends Controller
202
192
 
203
193
  // driver assignment
204
194
  if ($request->has('vehicle') && $integratedVendorOrder === null) {
205
- $input['vehicle_assigned_uuid'] = Utils::getUuid('vehicles', [
195
+ $input['vehicle_assigned_uuid'] = $this->getUuid('vehicles', [
206
196
  'public_id' => $request->input('vehicle'),
207
- 'company_uuid' => session('company'),
197
+ 'company_uuid' => $this->sessionCompany(),
208
198
  ]);
209
199
  }
210
200
 
211
201
  // facilitator assignment
212
202
  if ($request->has('facilitator') && $integratedVendorOrder === null) {
213
- $facilitator = Utils::getUuid(
203
+ $facilitator = $this->getUuid(
214
204
  ['contacts', 'vendors', 'integrated_vendors'],
215
205
  [
216
206
  'public_id' => $request->input('facilitator'),
217
- 'company_uuid' => session('company'),
207
+ 'company_uuid' => $this->sessionCompany(),
218
208
  ],
219
209
  [
220
210
  'with_table' => true,
@@ -223,11 +213,11 @@ class OrderController extends Controller
223
213
 
224
214
  if (is_array($facilitator)) {
225
215
  $input['facilitator_uuid'] = Utils::get($facilitator, 'uuid');
226
- $input['facilitator_type'] = Utils::getModelClassName(Utils::get($facilitator, 'table'));
216
+ $input['facilitator_type'] = $this->getModelClassName(Utils::get($facilitator, 'table'));
227
217
  }
228
218
  } elseif ($integratedVendorOrder) {
229
219
  $input['facilitator_uuid'] = $serviceQuote->integratedVendor->uuid;
230
- $input['facilitator_type'] = Utils::getModelClassName('integrated_vendors');
220
+ $input['facilitator_type'] = $this->getModelClassName('integrated_vendors');
231
221
  }
232
222
 
233
223
  // customer assignment
@@ -235,11 +225,11 @@ class OrderController extends Controller
235
225
  $customer = $request->input('customer');
236
226
 
237
227
  if (is_string($customer)) {
238
- $customer = Utils::getUuid(
228
+ $customer = $this->getUuid(
239
229
  ['contacts', 'vendors'],
240
230
  [
241
231
  'public_id' => $customer,
242
- 'company_uuid' => session('company'),
232
+ 'company_uuid' => $this->sessionCompany(),
243
233
  ],
244
234
  [
245
235
  'with_table' => true,
@@ -248,29 +238,29 @@ class OrderController extends Controller
248
238
 
249
239
  if (is_array($customer)) {
250
240
  $input['customer_uuid'] = Utils::get($customer, 'uuid');
251
- $input['customer_type'] = Utils::getModelClassName(Utils::get($customer, 'table'));
241
+ $input['customer_type'] = $this->getModelClassName(Utils::get($customer, 'table'));
252
242
  }
253
243
  } elseif (is_array($customer)) {
254
244
  // create customer from input
255
245
  $customer = Arr::only($customer, ['internal_id', 'name', 'title', 'email', 'phone', 'meta']);
256
246
 
257
247
  try {
258
- $customerCandidate = new Contact([
248
+ $customerCandidate = $this->newCustomerContact([
259
249
  ...$customer,
260
- 'company_uuid' => session('company'),
250
+ 'company_uuid' => $this->sessionCompany(),
261
251
  'type' => 'customer',
262
252
  ]);
263
253
  $customerCandidate->assertCustomerIdentityIsAvailable();
264
254
 
265
- $customer = Contact::firstOrCreate(
255
+ $customer = $this->firstOrCreateCustomerContact(
266
256
  [
267
- 'company_uuid' => session('company'),
257
+ 'company_uuid' => $this->sessionCompany(),
268
258
  'email' => $customer['email'],
269
259
  'type' => 'customer',
270
260
  ],
271
261
  [
272
262
  ...$customer,
273
- 'company_uuid' => session('company'),
263
+ 'company_uuid' => $this->sessionCompany(),
274
264
  'type' => 'customer',
275
265
  ]
276
266
  );
@@ -282,22 +272,13 @@ class OrderController extends Controller
282
272
  return response()->apiError('Failed to find or create customer for order.');
283
273
  }
284
274
 
285
- if (Str::isUuid($customer)) {
286
- $customer = Contact::where('uuid', $customer)->first();
287
- }
288
-
289
275
  if ($customer instanceof Contact) {
290
276
  $input['customer_uuid'] = $customer->uuid;
291
- $input['customer_type'] = Utils::getModelClassName($customer);
277
+ $input['customer_type'] = $this->getModelClassName($customer);
292
278
  }
293
279
  }
294
280
  }
295
281
 
296
- // if no type is set its default to transport
297
- if (!isset($input['type'])) {
298
- $input['type'] = 'transport';
299
- }
300
-
301
282
  // if no status is set its default to `created`
302
283
  if (!isset($input['status'])) {
303
284
  $input['status'] = 'created';
@@ -319,7 +300,7 @@ class OrderController extends Controller
319
300
  }
320
301
 
321
302
  // create the order
322
- $order = Order::create($input);
303
+ $order = $this->createOrder($input);
323
304
 
324
305
  // if it's integrated vendor order apply to meta
325
306
  if ($integratedVendorOrder) {
@@ -335,14 +316,14 @@ class OrderController extends Controller
335
316
  // Determine if order should be dispatched on creation
336
317
  $shouldDispatch = $request->boolean('dispatch') && $integratedVendorOrder === null;
337
318
 
338
- FinalizeApiOrderCreation::dispatch(
319
+ $this->dispatchFinalizeApiOrderCreation(
339
320
  $order->uuid,
340
321
  $serviceQuote instanceof ServiceQuote ? $serviceQuote->uuid : null,
341
322
  $shouldDispatch
342
- )->afterCommit();
323
+ );
343
324
 
344
325
  // response the driver resource
345
- return new OrderResource($order);
326
+ return $this->orderResource($order);
346
327
  }
347
328
 
348
329
  /**
@@ -358,7 +339,7 @@ class OrderController extends Controller
358
339
 
359
340
  // find for the order
360
341
  try {
361
- $order = Order::findRecordOrFail($id, ['trackingNumber', 'driverAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
342
+ $order = $this->findOrder($id, ['trackingNumber', 'driverAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
362
343
  } catch (ModelNotFoundException $exception) {
363
344
  return response()->json(
364
345
  [
@@ -369,23 +350,20 @@ class OrderController extends Controller
369
350
  }
370
351
 
371
352
  // get request input
372
- $input = $request->only([
373
- 'internal_id', 'payload', 'adhoc', 'adhoc_distance',
374
- 'pod_method', 'pod_required', 'scheduled_at', 'meta', 'type', 'status', 'notes',
375
- // Orchestrator constraints
376
- 'time_window_start', 'time_window_end',
377
- 'required_skills', 'orchestrator_priority',
378
- ]);
353
+ $input = $this->orderUpdateInputFromRequest($request);
379
354
 
380
355
  // update payload if new input or change payload by id
381
356
  if ($request->isArray('payload')) {
382
- $payload = data_get($order, 'payload', new Payload());
383
- $payloadInput = $request->input('payload');
384
- $entities = data_get($payloadInput, 'entities', []);
385
- $waypoints = data_get($payloadInput, 'waypoints', []);
386
- $pickup = data_get($payloadInput, 'pickup');
387
- $dropoff = data_get($payloadInput, 'dropoff');
388
- $return = data_get($payloadInput, 'return');
357
+ $payload = data_get($order, 'payload', $this->newPayload());
358
+ [
359
+ 'entities' => $entities,
360
+ 'waypoints' => $waypoints,
361
+ 'pickup' => $pickup,
362
+ 'dropoff' => $dropoff,
363
+ 'return' => $return,
364
+ 'has_waypoints_field' => $hasWaypointsField,
365
+ 'has_route_endpoint_fields' => $hasRouteEndpointFields,
366
+ ] = $this->payloadShapeFromArray($request->input('payload'));
389
367
 
390
368
  // if no pickup and dropoff extract from waypoints
391
369
  if (empty($pickup) && empty($dropoff) && count($waypoints)) {
@@ -425,27 +403,25 @@ class OrderController extends Controller
425
403
 
426
404
  $input['payload_uuid'] = $payload->uuid;
427
405
  } elseif ($request->has('payload')) {
428
- $input['payload_uuid'] = Utils::getUuid('payloads', [
406
+ $input['payload_uuid'] = $this->getUuid('payloads', [
429
407
  'public_id' => $request->input('payload'),
430
- 'company_uuid' => session('company'),
408
+ 'company_uuid' => $this->sessionCompany(),
431
409
  ]);
432
410
  unset($input['payload']);
433
411
  }
434
412
 
435
413
  // create a payload if missing payload[] but has pickup/dropoff/etc
436
414
  if ($request->missing('payload')) {
437
- $payload = data_get($order, 'payload', new Payload());
438
- $payloadInput = $request->only(['pickup', 'dropoff', 'return', 'waypoints', 'entities']);
439
- $entities = data_get($payloadInput, 'entities', []);
440
- $waypoints = data_get($payloadInput, 'waypoints', []);
441
- $pickup = data_get($payloadInput, 'pickup');
442
- $dropoff = data_get($payloadInput, 'dropoff');
443
- $return = data_get($payloadInput, 'return');
444
- $hasPickupField = $request->exists('pickup');
445
- $hasDropoffField = $request->exists('dropoff');
446
- $hasReturnField = $request->exists('return');
447
- $hasWaypointsField = $request->exists('waypoints');
448
- $hasRouteEndpointFields = $hasPickupField || $hasDropoffField || $hasReturnField;
415
+ $payload = data_get($order, 'payload', $this->newPayload());
416
+ [
417
+ 'entities' => $entities,
418
+ 'waypoints' => $waypoints,
419
+ 'pickup' => $pickup,
420
+ 'dropoff' => $dropoff,
421
+ 'return' => $return,
422
+ 'has_waypoints_field' => $hasWaypointsField,
423
+ 'has_route_endpoint_fields' => $hasRouteEndpointFields,
424
+ ] = $this->payloadShapeFromRequest($request);
449
425
 
450
426
  // if no pickup and dropoff extract from waypoints
451
427
  if (empty($pickup) && empty($dropoff) && count($waypoints)) {
@@ -488,27 +464,27 @@ class OrderController extends Controller
488
464
 
489
465
  // driver assignment
490
466
  if ($request->has('driver')) {
491
- $input['driver_assigned_uuid'] = Utils::getUuid('drivers', [
467
+ $input['driver_assigned_uuid'] = $this->getUuid('drivers', [
492
468
  'public_id' => $request->input('driver'),
493
- 'company_uuid' => session('company'),
469
+ 'company_uuid' => $this->sessionCompany(),
494
470
  ]);
495
471
  }
496
472
 
497
473
  // vehicle assignment
498
474
  if ($request->has('vehicle')) {
499
- $input['vehicle_assigned_uuid'] = Utils::getUuid('vehicles', [
475
+ $input['vehicle_assigned_uuid'] = $this->getUuid('vehicles', [
500
476
  'public_id' => $request->input('vehicle'),
501
- 'company_uuid' => session('company'),
477
+ 'company_uuid' => $this->sessionCompany(),
502
478
  ]);
503
479
  }
504
480
 
505
481
  // facilitator assignment
506
482
  if ($request->has('facilitator')) {
507
- $facilitator = Utils::getUuid(
483
+ $facilitator = $this->getUuid(
508
484
  ['contacts', 'vendors'],
509
485
  [
510
486
  'public_id' => $request->input('facilitator'),
511
- 'company_uuid' => session('company'),
487
+ 'company_uuid' => $this->sessionCompany(),
512
488
  ],
513
489
  [
514
490
  'with_table' => true,
@@ -517,17 +493,17 @@ class OrderController extends Controller
517
493
 
518
494
  if (is_array($facilitator)) {
519
495
  $input['facilitator_uuid'] = Utils::get($facilitator, 'uuid');
520
- $input['facilitator_type'] = Utils::getModelClassName(Utils::get($facilitator, 'table'));
496
+ $input['facilitator_type'] = $this->getModelClassName(Utils::get($facilitator, 'table'));
521
497
  }
522
498
  }
523
499
 
524
500
  // customer assignment
525
501
  if ($request->has('customer')) {
526
- $customer = Utils::getUuid(
502
+ $customer = $this->getUuid(
527
503
  ['contacts', 'vendors'],
528
504
  [
529
505
  'public_id' => $request->input('customer'),
530
- 'company_uuid' => session('company'),
506
+ 'company_uuid' => $this->sessionCompany(),
531
507
  ],
532
508
  [
533
509
  'with_table' => true,
@@ -536,12 +512,12 @@ class OrderController extends Controller
536
512
 
537
513
  if (is_array($customer)) {
538
514
  $input['customer_uuid'] = Utils::get($customer, 'uuid');
539
- $input['customer_type'] = Utils::getModelClassName(Utils::get($customer, 'table'));
515
+ $input['customer_type'] = $this->getModelClassName(Utils::get($customer, 'table'));
540
516
  }
541
517
  }
542
518
 
543
519
  // If adding a service quote for a purchase
544
- $serviceQuote = ServiceQuote::resolveFromRequest($request);
520
+ $serviceQuote = $this->resolveServiceQuote($request);
545
521
  if ($serviceQuote) {
546
522
  $order->purchaseServiceQuote($serviceQuote);
547
523
  }
@@ -565,7 +541,56 @@ class OrderController extends Controller
565
541
  $order->load(['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
566
542
 
567
543
  // response the order resource
568
- return new OrderResource($order);
544
+ return $this->orderResource($order);
545
+ }
546
+
547
+ protected function orderCreateInputFromRequest(Request $request): array
548
+ {
549
+ return $request->only([
550
+ 'internal_id', 'payload', 'service_quote', 'purchase_rate',
551
+ 'adhoc', 'adhoc_distance', 'pod_method', 'pod_required',
552
+ 'scheduled_at', 'status', 'meta', 'notes',
553
+ // Orchestrator constraints
554
+ 'time_window_start', 'time_window_end',
555
+ 'required_skills', 'orchestrator_priority',
556
+ ]);
557
+ }
558
+
559
+ protected function orderUpdateInputFromRequest(Request $request): array
560
+ {
561
+ return $request->only([
562
+ 'internal_id', 'payload', 'adhoc', 'adhoc_distance',
563
+ 'pod_method', 'pod_required', 'scheduled_at', 'meta', 'type', 'status', 'notes',
564
+ // Orchestrator constraints
565
+ 'time_window_start', 'time_window_end',
566
+ 'required_skills', 'orchestrator_priority',
567
+ ]);
568
+ }
569
+
570
+ protected function payloadShapeFromRequest(Request $request): array
571
+ {
572
+ return $this->payloadShapeFromArray($request->only(['pickup', 'dropoff', 'return', 'waypoints', 'entities']));
573
+ }
574
+
575
+ protected function payloadShapeFromArray(array $payloadInput): array
576
+ {
577
+ $hasPickupField = array_key_exists('pickup', $payloadInput);
578
+ $hasDropoffField = array_key_exists('dropoff', $payloadInput);
579
+ $hasReturnField = array_key_exists('return', $payloadInput);
580
+ $hasWaypointsField = array_key_exists('waypoints', $payloadInput);
581
+
582
+ return [
583
+ 'entities' => data_get($payloadInput, 'entities', []),
584
+ 'waypoints' => data_get($payloadInput, 'waypoints', []),
585
+ 'pickup' => data_get($payloadInput, 'pickup'),
586
+ 'dropoff' => data_get($payloadInput, 'dropoff'),
587
+ 'return' => data_get($payloadInput, 'return'),
588
+ 'has_pickup_field' => $hasPickupField,
589
+ 'has_dropoff_field' => $hasDropoffField,
590
+ 'has_return_field' => $hasReturnField,
591
+ 'has_waypoints_field' => $hasWaypointsField,
592
+ 'has_route_endpoint_fields' => $hasPickupField || $hasDropoffField || $hasReturnField,
593
+ ];
569
594
  }
570
595
 
571
596
  /**
@@ -699,6 +724,15 @@ class OrderController extends Controller
699
724
  }
700
725
 
701
726
  // request wants to find orders nearby a driver ?
727
+ //
728
+ // Unreachable in full: `Utils::isCoordinates()` resolves a
729
+ // `driver_*` public id to that driver's location, so the
730
+ // coordinates branch above consumes every driver that has one.
731
+ // What reaches here is a driver with no stored location, and the
732
+ // distance query below then raises on the null point rather than
733
+ // completing — see the "without a stored location" case in
734
+ // OrderControllerNearbyFiltersTest.
735
+ // @codeCoverageIgnoreStart
702
736
  if ($addedNearbyQuery === false && is_string($nearby) && Str::startsWith($nearby, 'driver_')) {
703
737
  $driver = Driver::where('public_id', $nearby)->first();
704
738
 
@@ -727,6 +761,7 @@ class OrderController extends Controller
727
761
  $addedNearbyQuery = true;
728
762
  }
729
763
  }
764
+ // @codeCoverageIgnoreEnd
730
765
 
731
766
  // if is a string like address string
732
767
  if ($addedNearbyQuery === false && is_string($nearby)) {
@@ -772,9 +807,9 @@ class OrderController extends Controller
772
807
  {
773
808
  // find for the order
774
809
  try {
775
- $order = Order::findRecordOrFail($id, ['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
810
+ $order = $this->findOrder($id, ['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
776
811
  } catch (ModelNotFoundException $exception) {
777
- return response()->json(
812
+ return $this->jsonResponse(
778
813
  [
779
814
  'error' => 'Order resource not found.',
780
815
  ],
@@ -783,7 +818,7 @@ class OrderController extends Controller
783
818
  }
784
819
 
785
820
  // response the order resource
786
- return new OrderResource($order);
821
+ return $this->orderResource($order);
787
822
  }
788
823
 
789
824
  /**
@@ -795,9 +830,9 @@ class OrderController extends Controller
795
830
  {
796
831
  // find for the driver
797
832
  try {
798
- $order = Order::findRecordOrFail($id);
833
+ $order = $this->findOrder($id);
799
834
  } catch (ModelNotFoundException $exception) {
800
- return response()->json(
835
+ return $this->jsonResponse(
801
836
  [
802
837
  'error' => 'Order resource not found.',
803
838
  ],
@@ -809,7 +844,7 @@ class OrderController extends Controller
809
844
  $order->delete();
810
845
 
811
846
  // response the order resource
812
- return new DeletedResource($order);
847
+ return $this->deletedOrderResource($order);
813
848
  }
814
849
 
815
850
  /**
@@ -821,9 +856,9 @@ class OrderController extends Controller
821
856
  {
822
857
  // find the order
823
858
  try {
824
- $order = Order::findRecordOrFail($id);
859
+ $order = $this->findOrder($id);
825
860
  } catch (ModelNotFoundException $exception) {
826
- return response()->json(
861
+ return $this->jsonResponse(
827
862
  [
828
863
  'error' => 'Order resource not found.',
829
864
  ],
@@ -836,12 +871,12 @@ class OrderController extends Controller
836
871
  $origin = $order->payload->pickup ?? $order->payload->waypoints->first();
837
872
  $destination = $order->payload->dropoff ?? $order->payload->waypoints->firstWhere('current_waypoint_uuid', $order->current_waypoint_uuid);
838
873
 
839
- $matrix = Utils::getDrivingDistanceAndTime($origin, $destination);
874
+ $matrix = $this->drivingDistanceAndTime($origin, $destination);
840
875
 
841
876
  $order->update(['distance' => $matrix->distance, 'time' => $matrix->time]);
842
877
 
843
878
  // response distance and time matrix
844
- return response()->json($matrix);
879
+ return $this->jsonResponse($matrix);
845
880
  }
846
881
 
847
882
  /**
@@ -852,9 +887,9 @@ class OrderController extends Controller
852
887
  public function dispatchOrder(string $id)
853
888
  {
854
889
  try {
855
- $order = Order::findRecordOrFail($id, ['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
890
+ $order = $this->findOrder($id, ['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
856
891
  } catch (ModelNotFoundException $exception) {
857
- return response()->json(
892
+ return $this->jsonResponse(
858
893
  [
859
894
  'error' => 'Order resource not found.',
860
895
  ],
@@ -863,17 +898,17 @@ class OrderController extends Controller
863
898
  }
864
899
 
865
900
  if (!$order->hasDriverAssigned && !$order->adhoc) {
866
- return response()->apiError('No driver assigned to dispatch!');
901
+ return $this->apiError('No driver assigned to dispatch!');
867
902
  }
868
903
 
869
904
  if ($order->dispatched) {
870
- return response()->apiError('Order has already been dispatched!');
905
+ return $this->apiError('Order has already been dispatched!');
871
906
  }
872
907
 
873
908
  $order->dispatch();
874
909
  $order->insertDispatchActivity();
875
910
 
876
- return new OrderResource($order);
911
+ return $this->orderResource($order);
877
912
  }
878
913
 
879
914
  /**
@@ -889,14 +924,13 @@ class OrderController extends Controller
889
924
  $timeInput = $request->input('time');
890
925
 
891
926
  // get the default tz
892
- $company = Auth::getCompany();
893
- $defaultTz = data_get($company, 'timezone', config('app.timezone'));
927
+ $defaultTz = $this->defaultCompanyTimezone();
894
928
  $timezoneInput = $request->input('timezone', $defaultTz);
895
929
 
896
930
  try {
897
- $order = Order::findRecordOrFail($id);
931
+ $order = $this->findOrder($id);
898
932
  } catch (ModelNotFoundException $exception) {
899
- return response()->json(
933
+ return $this->jsonResponse(
900
934
  [
901
935
  'error' => 'Order resource not found.',
902
936
  ],
@@ -919,7 +953,7 @@ class OrderController extends Controller
919
953
  $order->scheduled_at = $date;
920
954
  $order->save();
921
955
 
922
- return new OrderResource($order);
956
+ return $this->orderResource($order);
923
957
  }
924
958
 
925
959
  /**
@@ -934,7 +968,7 @@ class OrderController extends Controller
934
968
  $assignAdhocDriver = $request->input('assign');
935
969
 
936
970
  try {
937
- $order = Order::findRecordOrFail($id, ['payload.waypoints', 'driverAssigned'], []);
971
+ $order = $this->findOrder($id, ['payload.waypoints', 'driverAssigned']);
938
972
  } catch (ModelNotFoundException $exception) {
939
973
  return response()->json(
940
974
  [
@@ -954,10 +988,10 @@ class OrderController extends Controller
954
988
  }
955
989
 
956
990
  /** @var \Fleetbase\Models\Driver */
957
- $driver = Driver::where('uuid', $order->driver_assigned_uuid)->withoutGlobalScopes()->first();
991
+ $driver = $this->findDriverByUuid($order->driver_assigned_uuid);
958
992
 
959
993
  /** @var \Fleetbase\Models\Payload */
960
- $payload = Payload::where('uuid', $order->payload_uuid)->withoutGlobalScopes()->with(['waypoints', 'waypointMarkers', 'entities'])->first();
994
+ $payload = $this->findPayloadByUuid($order->payload_uuid);
961
995
 
962
996
  if ($order->adhoc && !$driver) {
963
997
  return response()->apiError('You must send driver to accept adhoc order.');
@@ -1031,7 +1065,7 @@ class OrderController extends Controller
1031
1065
  // if string $id
1032
1066
  if (!$order) {
1033
1067
  try {
1034
- $order = Order::findRecordOrFail($id, [
1068
+ $order = $this->findOrder($id, [
1035
1069
  'driverAssigned',
1036
1070
  'payload.entities',
1037
1071
  'payload.pickup',
@@ -1048,9 +1082,16 @@ class OrderController extends Controller
1048
1082
  }
1049
1083
 
1050
1084
  // if no order found
1085
+ //
1086
+ // Unreachable: findOrder() above is typed `: Order`, so it either returns
1087
+ // an order or throws — it never yields null. This is independent of the
1088
+ // upstream findByIdOrFail defect and stays unreachable after that is
1089
+ // fixed; the catch above is what becomes live then, not this guard.
1090
+ // @codeCoverageIgnoreStart
1051
1091
  if (!$order) {
1052
1092
  return response()->apiError('Order resource not found.', 404);
1053
1093
  }
1094
+ // @codeCoverageIgnoreEnd
1054
1095
 
1055
1096
  // if order is still status of `created` trigger started flag
1056
1097
  if ($order->status === 'created') {
@@ -1086,7 +1127,7 @@ class OrderController extends Controller
1086
1127
 
1087
1128
  $order->dispatch();
1088
1129
 
1089
- return new OrderResource($order);
1130
+ return $this->orderResource($order);
1090
1131
  }
1091
1132
 
1092
1133
  /** @var \Fleetbase\LaravelMysqlSpatial\Types\Point */
@@ -1116,7 +1157,7 @@ class OrderController extends Controller
1116
1157
  $order->complete($this->resolveProof($proof));
1117
1158
  }
1118
1159
 
1119
- return new OrderResource($order->refresh());
1160
+ return $this->orderResource($order->refresh());
1120
1161
  }
1121
1162
 
1122
1163
  if (!$order->started && in_array($order->status, ['created', 'dispatched'], true)) {
@@ -1132,7 +1173,7 @@ class OrderController extends Controller
1132
1173
  $order->driverAssigned?->unassignCurrentJob();
1133
1174
  $order->cancel();
1134
1175
 
1135
- return new OrderResource($order->refresh());
1176
+ return $this->orderResource($order->refresh());
1136
1177
  }
1137
1178
 
1138
1179
  if (Utils::isActivity($activity) && $activity->completesOrder()) {
@@ -1144,7 +1185,7 @@ class OrderController extends Controller
1144
1185
  }
1145
1186
  }
1146
1187
 
1147
- return new OrderResource($order->refresh());
1188
+ return $this->orderResource($order->refresh());
1148
1189
  }
1149
1190
 
1150
1191
  /**
@@ -1157,7 +1198,7 @@ class OrderController extends Controller
1157
1198
  $waypointId = $request->input('waypoint');
1158
1199
 
1159
1200
  try {
1160
- $order = Order::findRecordOrFail($id, [
1201
+ $order = $this->findOrder($id, [
1161
1202
  'payload.pickup',
1162
1203
  'payload.dropoff',
1163
1204
  'payload.return',
@@ -1215,9 +1256,9 @@ class OrderController extends Controller
1215
1256
  public function completeOrder(string $id)
1216
1257
  {
1217
1258
  try {
1218
- $order = Order::findRecordOrFail($id);
1259
+ $order = $this->findOrder($id);
1219
1260
  } catch (ModelNotFoundException $exception) {
1220
- return response()->json(
1261
+ return $this->jsonResponse(
1221
1262
  [
1222
1263
  'error' => 'Order resource not found.',
1223
1264
  ],
@@ -1247,7 +1288,7 @@ class OrderController extends Controller
1247
1288
  $order->insertActivity($activity, $location);
1248
1289
  $order->notifyCompleted();
1249
1290
 
1250
- return new OrderResource($order);
1291
+ return $this->orderResource($order);
1251
1292
  }
1252
1293
 
1253
1294
  /**
@@ -1258,9 +1299,9 @@ class OrderController extends Controller
1258
1299
  public function cancelOrder(string $id)
1259
1300
  {
1260
1301
  try {
1261
- $order = Order::findRecordOrFail($id);
1302
+ $order = $this->findOrder($id);
1262
1303
  } catch (ModelNotFoundException $exception) {
1263
- return response()->json(
1304
+ return $this->jsonResponse(
1264
1305
  [
1265
1306
  'error' => 'Order resource not found.',
1266
1307
  ],
@@ -1270,7 +1311,7 @@ class OrderController extends Controller
1270
1311
 
1271
1312
  $order->cancel();
1272
1313
 
1273
- return new OrderResource($order);
1314
+ return $this->orderResource($order);
1274
1315
  }
1275
1316
 
1276
1317
  /**
@@ -1282,7 +1323,7 @@ class OrderController extends Controller
1282
1323
  public function setDestination(string $id, string $placeId)
1283
1324
  {
1284
1325
  try {
1285
- $order = Order::findRecordOrFail($id, [
1326
+ $order = $this->findOrder($id, [
1286
1327
  'payload.pickup',
1287
1328
  'payload.dropoff',
1288
1329
  'payload.return',
@@ -1309,7 +1350,7 @@ class OrderController extends Controller
1309
1350
  // Persist destination choice
1310
1351
  $this->setPayloadCurrentServiceStop($payload, $stop);
1311
1352
 
1312
- return new OrderResource($order->refresh());
1353
+ return $this->orderResource($order->refresh());
1313
1354
  }
1314
1355
 
1315
1356
  /**
@@ -1320,14 +1361,14 @@ class OrderController extends Controller
1320
1361
  public function optimize(string $id)
1321
1362
  {
1322
1363
  try {
1323
- $order = Order::findRecordOrFail($id);
1364
+ $order = $this->findOrder($id);
1324
1365
  } catch (ModelNotFoundException $e) {
1325
- return response()->apiError('Order resource not found.', 404);
1366
+ return $this->apiError('Order resource not found.', 404);
1326
1367
  }
1327
1368
 
1328
1369
  // do this code
1329
1370
 
1330
- return new OrderResource($order);
1371
+ return $this->orderResource($order);
1331
1372
  }
1332
1373
 
1333
1374
  /**
@@ -1340,17 +1381,17 @@ class OrderController extends Controller
1340
1381
  set_time_limit(280);
1341
1382
 
1342
1383
  try {
1343
- $order = Order::findRecordOrFail($id);
1384
+ $order = $this->findOrder($id);
1344
1385
  $data = $order->tracker()->toArray($request->only(['provider', 'fallbacks', 'traffic_enabled']));
1345
1386
 
1346
- return response()->json($data);
1387
+ return $this->jsonResponse($data);
1347
1388
  } catch (ModelNotFoundException $e) {
1348
- return response()->apiError('Order resource not found.', 404);
1389
+ return $this->apiError('Order resource not found.', 404);
1349
1390
  } catch (\Throwable $e) {
1350
- return response()->apiError('An error occured trying to track order.', 404);
1391
+ return $this->apiError('An error occured trying to track order.', 404);
1351
1392
  }
1352
1393
 
1353
- return response()->apiError('An error occured trying to track order.', 404);
1394
+ return $this->apiError('An error occured trying to track order.', 404);
1354
1395
  }
1355
1396
 
1356
1397
  /**
@@ -1361,17 +1402,17 @@ class OrderController extends Controller
1361
1402
  public function etaData(Request $request, string $id)
1362
1403
  {
1363
1404
  try {
1364
- $order = Order::findRecordOrFail($id);
1405
+ $order = $this->findOrder($id);
1365
1406
  $data = $order->tracker()->eta($request->only(['provider', 'fallbacks', 'traffic_enabled']));
1366
1407
 
1367
- return response()->json($data);
1408
+ return $this->jsonResponse($data);
1368
1409
  } catch (ModelNotFoundException $e) {
1369
- return response()->apiError('Order resource not found.', 404);
1410
+ return $this->apiError('Order resource not found.', 404);
1370
1411
  } catch (\Throwable $e) {
1371
- return response()->apiError('An error occured trying to track order.', 404);
1412
+ return $this->apiError('An error occured trying to track order.', 404);
1372
1413
  }
1373
1414
 
1374
- return response()->apiError('An error occured trying to track order.', 404);
1415
+ return $this->apiError('An error occured trying to track order.', 404);
1375
1416
  }
1376
1417
 
1377
1418
  /**
@@ -1387,26 +1428,26 @@ class OrderController extends Controller
1387
1428
  $type = $subjectId ? strtok($subjectId, '_') : null;
1388
1429
 
1389
1430
  if (!$code) {
1390
- return response()->apiError('No QR code data to capture.');
1431
+ return $this->apiError('No QR code data to capture.');
1391
1432
  }
1392
1433
 
1393
1434
  // Load Order
1394
1435
  try {
1395
- $order = Order::findRecordOrFail($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1436
+ $order = $this->findOrder($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1396
1437
  } catch (ModelNotFoundException $e) {
1397
- return response()->apiError('Order resource not found.', 404);
1438
+ return $this->apiError('Order resource not found.', 404);
1398
1439
  }
1399
1440
 
1400
1441
  // Resolve subject
1401
1442
  $subject = $this->resolveSubject($order, $type, $subjectId);
1402
1443
  if (!$subject) {
1403
- return response()->apiError('Unable to capture QR code data.');
1444
+ return $this->apiError('Unable to capture QR code data.');
1404
1445
  }
1405
1446
 
1406
1447
  // validate
1407
1448
  if ($subject && $code === $subject->uuid) {
1408
1449
  // create verification proof
1409
- $proof = Proof::create([
1450
+ $proof = $this->createProof([
1410
1451
  'company_uuid' => session('company'),
1411
1452
  'order_uuid' => $order->uuid,
1412
1453
  'subject_uuid' => $subject->uuid,
@@ -1416,10 +1457,10 @@ class OrderController extends Controller
1416
1457
  'data' => $data,
1417
1458
  ]);
1418
1459
 
1419
- return new ProofResource($proof);
1460
+ return $this->proofResource($proof);
1420
1461
  }
1421
1462
 
1422
- return response()->apiError('Unable to validate QR code data.');
1463
+ return $this->apiError('Unable to validate QR code data.');
1423
1464
  }
1424
1465
 
1425
1466
  /**
@@ -1437,24 +1478,24 @@ class OrderController extends Controller
1437
1478
  $type = $subjectId ? strtok($subjectId, '_') : null;
1438
1479
 
1439
1480
  if (!$signature) {
1440
- return response()->apiError('No signature data to capture.');
1481
+ return $this->apiError('No signature data to capture.');
1441
1482
  }
1442
1483
 
1443
1484
  // Load Order
1444
1485
  try {
1445
- $order = Order::findRecordOrFail($id);
1486
+ $order = $this->findOrder($id);
1446
1487
  } catch (ModelNotFoundException $e) {
1447
- return response()->apiError('Order resource not found.', 404);
1488
+ return $this->apiError('Order resource not found.', 404);
1448
1489
  }
1449
1490
 
1450
1491
  // Resolve subject
1451
1492
  $subject = $this->resolveSubject($order, $type, $subjectId);
1452
1493
  if (!$subject) {
1453
- return response()->apiError('Unable to capture signature data.');
1494
+ return $this->apiError('Unable to capture signature data.');
1454
1495
  }
1455
1496
 
1456
1497
  // create proof instance
1457
- $proof = Proof::create([
1498
+ $proof = $this->createProof([
1458
1499
  'company_uuid' => session('company'),
1459
1500
  'order_uuid' => $order->uuid,
1460
1501
  'subject_uuid' => $subject->uuid,
@@ -1468,10 +1509,10 @@ class OrderController extends Controller
1468
1509
  $path = 'uploads/' . session('company') . '/signatures/' . $proof->public_id . '.png';
1469
1510
 
1470
1511
  // upload signature
1471
- Storage::disk($disk)->put($path, base64_decode(str_replace('data:image/png;base64,', '', $signature)));
1512
+ $this->putStorage($disk, $path, base64_decode(str_replace('data:image/png;base64,', '', $signature)));
1472
1513
 
1473
1514
  // create file record for upload
1474
- $file = File::create([
1515
+ $file = $this->createFile([
1475
1516
  'company_uuid' => session('company'),
1476
1517
  'uploader_uuid' => session('user'),
1477
1518
  'name' => basename($path),
@@ -1488,7 +1529,7 @@ class OrderController extends Controller
1488
1529
  $proof->file_uuid = $file->uuid;
1489
1530
  $proof->save();
1490
1531
 
1491
- return new ProofResource($proof);
1532
+ return $this->proofResource($proof);
1492
1533
  }
1493
1534
 
1494
1535
  /**
@@ -1552,7 +1593,7 @@ class OrderController extends Controller
1552
1593
  } catch (ValidationException $e) {
1553
1594
  $errorMessage = collect($e->errors())->flatten()->first();
1554
1595
 
1555
- return response()->apiError($errorMessage, 422);
1596
+ return $this->apiError($errorMessage, 422);
1556
1597
  }
1557
1598
 
1558
1599
  // Determine storage disk & bucket
@@ -1581,26 +1622,32 @@ class OrderController extends Controller
1581
1622
  // Normalize into one array
1582
1623
  $incoming = array_merge($rawInputs, $base64Inputs);
1583
1624
 
1625
+ // Defensive only: the validate() above already requires `photos` to be a
1626
+ // non-empty array whose every element is an upload or a decodable base64
1627
+ // string, and each of those survives the filter, so this cannot be empty.
1628
+ // Kept in case those rules are relaxed.
1629
+ // @codeCoverageIgnoreStart
1584
1630
  if (empty($incoming)) {
1585
- return response()->apiError('No photo data to capture.');
1631
+ return $this->apiError('No photo data to capture.');
1586
1632
  }
1633
+ // @codeCoverageIgnoreEnd
1587
1634
 
1588
1635
  // Load Order
1589
1636
  try {
1590
- $order = Order::findRecordOrFail($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1637
+ $order = $this->findOrder($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1591
1638
  } catch (ModelNotFoundException $e) {
1592
- return response()->apiError('Order resource not found.', 404);
1639
+ return $this->apiError('Order resource not found.', 404);
1593
1640
  }
1594
1641
 
1595
1642
  // Resolve subject
1596
1643
  $subject = $this->resolveSubject($order, $type, $subjectId);
1597
1644
  if (!$subject) {
1598
- return response()->apiError('Unable to capture photo as proof.');
1645
+ return $this->apiError('Unable to capture photo as proof.');
1599
1646
  }
1600
1647
 
1601
1648
  // 5) Loop through each item, create Proof + File
1602
1649
  foreach ($incoming as $item) {
1603
- $proof = Proof::create([
1650
+ $proof = $this->createProof([
1604
1651
  'company_uuid' => session('company'),
1605
1652
  'order_uuid' => $order->uuid,
1606
1653
  'subject_uuid' => $subject->uuid,
@@ -1621,7 +1668,7 @@ class OrderController extends Controller
1621
1668
  }
1622
1669
 
1623
1670
  // Return the last Proof resource created
1624
- return new ProofResource($proof);
1671
+ return $this->proofResource($proof);
1625
1672
  }
1626
1673
 
1627
1674
  /**
@@ -1649,9 +1696,9 @@ class OrderController extends Controller
1649
1696
  $company = session('company');
1650
1697
  $path = "uploads/{$company}/photos/{$proof->public_id}.{$extension}";
1651
1698
 
1652
- Storage::disk($disk)->put($path, $contents);
1699
+ $this->putStorage($disk, $path, $contents);
1653
1700
 
1654
- return File::create([
1701
+ return $this->createFile([
1655
1702
  'company_uuid' => $company,
1656
1703
  'uploader_uuid' => session('user'),
1657
1704
  'name' => basename($path),
@@ -1726,9 +1773,9 @@ class OrderController extends Controller
1726
1773
  public function proofs(Request $request, string $id, ?string $subjectId = null)
1727
1774
  {
1728
1775
  try {
1729
- $order = Order::findRecordOrFail($id);
1776
+ $order = $this->findOrder($id);
1730
1777
  } catch (ModelNotFoundException $e) {
1731
- return response()->apiError('Order resource not found.', 404);
1778
+ return $this->apiError('Order resource not found.', 404);
1732
1779
  }
1733
1780
 
1734
1781
  $subject = $order;
@@ -1738,23 +1785,12 @@ class OrderController extends Controller
1738
1785
  }
1739
1786
 
1740
1787
  if (!$subject) {
1741
- return response()->apiError('Unable to retrieve proof of delivery for subject.');
1788
+ return $this->apiError('Unable to retrieve proof of delivery for subject.');
1742
1789
  }
1743
1790
 
1744
- $proofsQuery = Proof::where([
1745
- 'company_uuid' => session('company'),
1746
- 'order_uuid' => $order->uuid,
1747
- ]);
1791
+ $proofs = $this->proofsForSubject($order, $subject);
1748
1792
 
1749
- // if subject is not the order then filter by subject
1750
- if ($order->uuid !== $subject->uuid) {
1751
- $proofsQuery->where('subject_uuid', $subject->uuid);
1752
- }
1753
-
1754
- // get proofs
1755
- $proofs = $proofsQuery->get();
1756
-
1757
- return ProofResource::collection($proofs);
1793
+ return $this->proofResourceCollection($proofs);
1758
1794
  }
1759
1795
 
1760
1796
  /**
@@ -1776,9 +1812,9 @@ class OrderController extends Controller
1776
1812
  public function getEditableEntityFields(string $id, Request $request)
1777
1813
  {
1778
1814
  try {
1779
- $order = Order::findRecordOrFail($id);
1815
+ $order = $this->findOrder($id);
1780
1816
  } catch (ModelNotFoundException $e) {
1781
- return response()->apiError('Order resource not found.', 404);
1817
+ return $this->apiError('Order resource not found.', 404);
1782
1818
  }
1783
1819
 
1784
1820
  // Define settings as array
@@ -1788,12 +1824,12 @@ class OrderController extends Controller
1788
1824
  $orderConfigId = data_get($order, 'order_config_uuid');
1789
1825
 
1790
1826
  // Get entity editing settings
1791
- $savedEntityEditingSettings = Setting::where('key', 'fleet-ops.entity-editing-settings')->value('value');
1827
+ $savedEntityEditingSettings = $this->entityEditingSettings();
1792
1828
  if ($orderConfigId && $savedEntityEditingSettings) {
1793
1829
  $entityEditingSettings = data_get($savedEntityEditingSettings, $orderConfigId, []);
1794
1830
  }
1795
1831
 
1796
- return response()->json($entityEditingSettings);
1832
+ return $this->jsonResponse($entityEditingSettings);
1797
1833
  }
1798
1834
 
1799
1835
  /**
@@ -1804,16 +1840,165 @@ class OrderController extends Controller
1804
1840
  public function orderComments(string $id)
1805
1841
  {
1806
1842
  try {
1807
- $order = Order::findRecordOrFail($id);
1843
+ $order = $this->findOrder($id);
1808
1844
  $order->loadMissing('comments');
1809
1845
 
1810
- return CommentResource::collection($order->comments);
1846
+ return $this->commentResourceCollection($order->comments);
1811
1847
  } catch (ModelNotFoundException $e) {
1812
- return response()->apiError('Order resource not found.', 404);
1848
+ return $this->apiError('Order resource not found.', 404);
1813
1849
  } catch (\Throwable $e) {
1814
- return response()->apiError('An error occured trying to get order comments.', 404);
1850
+ return $this->apiError('An error occured trying to get order comments.', 404);
1851
+ }
1852
+
1853
+ return $this->apiError('An error occured trying to get order comments.', 404);
1854
+ }
1855
+
1856
+ protected function findOrder(string $id, array $with = [], array $withCount = []): Order
1857
+ {
1858
+ return Order::findRecordOrFail($id, $with, $withCount);
1859
+ }
1860
+
1861
+ protected function resolveOrderConfig(array $input): ?OrderConfig
1862
+ {
1863
+ return OrderConfig::resolveFromIdentifier($input);
1864
+ }
1865
+
1866
+ protected function resolveServiceQuote(Request $request): ?ServiceQuote
1867
+ {
1868
+ return ServiceQuote::resolveFromRequest($request);
1869
+ }
1870
+
1871
+ protected function newPayload(): Payload
1872
+ {
1873
+ return new Payload();
1874
+ }
1875
+
1876
+ protected function findDriverByPublicId(string $publicId): ?Driver
1877
+ {
1878
+ return Driver::where(['public_id' => $publicId, 'company_uuid' => session('company')])->first();
1879
+ }
1880
+
1881
+ protected function findDriverByUuid(?string $uuid): ?Driver
1882
+ {
1883
+ return Driver::where('uuid', $uuid)->withoutGlobalScopes()->first();
1884
+ }
1885
+
1886
+ protected function findPayloadByUuid(?string $uuid): ?Payload
1887
+ {
1888
+ return Payload::where('uuid', $uuid)->withoutGlobalScopes()->with(['waypoints', 'waypointMarkers', 'entities'])->first();
1889
+ }
1890
+
1891
+ protected function sessionCompany(): ?string
1892
+ {
1893
+ return session('company');
1894
+ }
1895
+
1896
+ protected function getUuid(array|string $table, array $where, array $options = []): mixed
1897
+ {
1898
+ return Utils::getUuid($table, $where, $options);
1899
+ }
1900
+
1901
+ protected function getModelClassName(mixed $tableOrModel): ?string
1902
+ {
1903
+ return Utils::getModelClassName($tableOrModel);
1904
+ }
1905
+
1906
+ protected function newCustomerContact(array $attributes): Contact
1907
+ {
1908
+ return new Contact($attributes);
1909
+ }
1910
+
1911
+ protected function firstOrCreateCustomerContact(array $attributes, array $values): Contact
1912
+ {
1913
+ return Contact::firstOrCreate($attributes, $values);
1914
+ }
1915
+
1916
+ protected function defaultCompanyTimezone(): string
1917
+ {
1918
+ return data_get(Auth::getCompany(), 'timezone', config('app.timezone'));
1919
+ }
1920
+
1921
+ protected function createOrder(array $input): Order
1922
+ {
1923
+ return Order::create($input);
1924
+ }
1925
+
1926
+ protected function dispatchFinalizeApiOrderCreation(string $orderUuid, ?string $serviceQuoteUuid, bool $shouldDispatch): void
1927
+ {
1928
+ FinalizeApiOrderCreation::dispatch($orderUuid, $serviceQuoteUuid, $shouldDispatch)->afterCommit();
1929
+ }
1930
+
1931
+ protected function drivingDistanceAndTime(mixed $origin, mixed $destination): mixed
1932
+ {
1933
+ return Utils::getDrivingDistanceAndTime($origin, $destination);
1934
+ }
1935
+
1936
+ protected function createProof(array $input): Proof
1937
+ {
1938
+ return Proof::create($input);
1939
+ }
1940
+
1941
+ protected function createFile(array $input): File
1942
+ {
1943
+ return File::create($input);
1944
+ }
1945
+
1946
+ protected function putStorage(string $disk, string $path, string $contents): void
1947
+ {
1948
+ Storage::disk($disk)->put($path, $contents);
1949
+ }
1950
+
1951
+ protected function proofsForSubject(Order $order, mixed $subject)
1952
+ {
1953
+ $proofsQuery = Proof::where([
1954
+ 'company_uuid' => session('company'),
1955
+ 'order_uuid' => $order->uuid,
1956
+ ]);
1957
+
1958
+ if ($order->uuid !== $subject->uuid) {
1959
+ $proofsQuery->where('subject_uuid', $subject->uuid);
1815
1960
  }
1816
1961
 
1817
- return response()->apiError('An error occured trying to get order comments.', 404);
1962
+ return $proofsQuery->get();
1963
+ }
1964
+
1965
+ protected function entityEditingSettings(): mixed
1966
+ {
1967
+ return Setting::where('key', 'fleet-ops.entity-editing-settings')->value('value');
1968
+ }
1969
+
1970
+ protected function orderResource(Order $order)
1971
+ {
1972
+ return new OrderResource($order);
1973
+ }
1974
+
1975
+ protected function deletedOrderResource(Order $order)
1976
+ {
1977
+ return new DeletedResource($order);
1978
+ }
1979
+
1980
+ protected function proofResource(Proof $proof)
1981
+ {
1982
+ return new ProofResource($proof);
1983
+ }
1984
+
1985
+ protected function proofResourceCollection($proofs)
1986
+ {
1987
+ return ProofResource::collection($proofs);
1988
+ }
1989
+
1990
+ protected function commentResourceCollection($comments)
1991
+ {
1992
+ return CommentResource::collection($comments);
1993
+ }
1994
+
1995
+ protected function jsonResponse(mixed $payload, int $status = 200)
1996
+ {
1997
+ return response()->json($payload, $status);
1998
+ }
1999
+
2000
+ protected function apiError(string $message, int $status = 400)
2001
+ {
2002
+ return response()->apiError($message, $status);
1818
2003
  }
1819
2004
  }