@fleetbase/fleetops-engine 0.6.63 → 0.6.65

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 (289) hide show
  1. package/RELEASE.md +7 -6
  2. package/addon/components/attachment-list.hbs +65 -0
  3. package/addon/components/attachment-list.js +8 -0
  4. package/addon/components/cell/attached-vehicle.hbs +1 -1
  5. package/addon/components/cell/attached-vehicle.js +3 -3
  6. package/addon/components/cell/driver-identity.js +4 -4
  7. package/addon/components/cell/driver-name.hbs +1 -1
  8. package/addon/components/cell/translated-value.hbs +9 -0
  9. package/addon/components/cell/translated-value.js +57 -0
  10. package/addon/components/contact/card.hbs +58 -0
  11. package/addon/components/contact/card.js +19 -0
  12. package/addon/components/contact/form.hbs +1 -1
  13. package/addon/components/contact/panel-header.hbs +1 -1
  14. package/addon/components/customer/form.hbs +2 -2
  15. package/addon/components/device/manager.hbs +26 -28
  16. package/addon/components/device/manager.js +28 -2
  17. package/addon/components/device/panel-tabs/vehicle.hbs +13 -13
  18. package/addon/components/device/panel-tabs/vehicle.js +8 -0
  19. package/addon/components/driver/card.hbs +24 -13
  20. package/addon/components/driver/card.js +20 -6
  21. package/addon/components/driver/form.hbs +3 -3
  22. package/addon/components/driver/panel-header.hbs +2 -2
  23. package/addon/components/driver/pill.hbs +2 -2
  24. package/addon/components/equipment/form.js +11 -4
  25. package/addon/components/equipment/pill.hbs +20 -0
  26. package/addon/components/equipment/pill.js +3 -0
  27. package/addon/components/filter/multi-model.hbs +19 -0
  28. package/addon/components/filter/multi-model.js +90 -0
  29. package/addon/components/fleet/driver-listing.hbs +1 -1
  30. package/addon/components/fleet/vehicle-listing.hbs +1 -1
  31. package/addon/components/fleet-panel/driver-listing.hbs +1 -1
  32. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -1
  33. package/addon/components/layout/fleet-ops-sidebar.js +9 -7
  34. package/addon/components/maintenance/form.js +12 -4
  35. package/addon/components/maintenance-schedule/form.js +13 -7
  36. package/addon/components/map/leaflet-live-map.hbs +52 -0
  37. package/addon/components/map/order-list-overlay/order.hbs +4 -4
  38. package/addon/components/modals/attach-equipment.hbs +22 -0
  39. package/addon/components/modals/attach-equipment.js +3 -0
  40. package/addon/components/modals/attach-telematic-device.hbs +13 -8
  41. package/addon/components/modals/attach-telematic-device.js +11 -1
  42. package/addon/components/modals/attach-trailer.hbs +30 -0
  43. package/addon/components/modals/attach-trailer.js +3 -0
  44. package/addon/components/modals/contact-form.hbs +2 -2
  45. package/addon/components/modals/convert-customer-to-vendor.hbs +1 -1
  46. package/addon/components/modals/driver-form.hbs +2 -2
  47. package/addon/components/modals/select-trailer.hbs +35 -0
  48. package/addon/components/modals/select-trailer.js +3 -0
  49. package/addon/components/modals/vehicle-form.hbs +2 -2
  50. package/addon/components/orchestrator/resource-panel.hbs +2 -2
  51. package/addon/components/order/customer-avatar-stack.hbs +2 -2
  52. package/addon/components/order/details/detail.hbs +4 -4
  53. package/addon/components/order/details/payload.hbs +2 -2
  54. package/addon/components/order/kanban-card.hbs +4 -4
  55. package/addon/components/order-list-overlay/order.hbs +4 -4
  56. package/addon/components/trailer/card.hbs +68 -0
  57. package/addon/components/trailer/card.js +22 -0
  58. package/addon/components/trailer/details/connections.hbs +57 -0
  59. package/addon/components/trailer/details/connections.js +59 -0
  60. package/addon/components/trailer/details/equipment.hbs +25 -0
  61. package/addon/components/trailer/details/equipment.js +52 -0
  62. package/addon/components/trailer/details/maintenance-history.hbs +47 -0
  63. package/addon/components/trailer/details/maintenance-history.js +40 -0
  64. package/addon/components/trailer/details/schedules.hbs +45 -0
  65. package/addon/components/trailer/details/schedules.js +40 -0
  66. package/addon/components/trailer/details/work-orders.hbs +47 -0
  67. package/addon/components/trailer/details/work-orders.js +40 -0
  68. package/addon/components/trailer/details.hbs +399 -0
  69. package/addon/components/trailer/details.js +118 -0
  70. package/addon/components/trailer/form.hbs +447 -0
  71. package/addon/components/trailer/form.js +165 -0
  72. package/addon/components/trailer/panel-header.hbs +45 -0
  73. package/addon/components/trailer/panel-header.js +3 -0
  74. package/addon/components/trailer/pill.hbs +21 -0
  75. package/addon/components/trailer/pill.js +3 -0
  76. package/addon/components/vehicle/card.hbs +42 -11
  77. package/addon/components/vehicle/card.js +31 -0
  78. package/addon/components/vehicle/details/equipment.hbs +25 -0
  79. package/addon/components/vehicle/details/equipment.js +55 -0
  80. package/addon/components/vehicle/details/trailers.hbs +35 -0
  81. package/addon/components/vehicle/details/trailers.js +55 -0
  82. package/addon/components/vehicle/form.hbs +2 -2
  83. package/addon/components/vehicle/panel-header.hbs +2 -2
  84. package/addon/components/vehicle/pill.hbs +2 -2
  85. package/addon/components/vendor/card.hbs +58 -0
  86. package/addon/components/vendor/card.js +6 -0
  87. package/addon/components/vendor/panel-header.hbs +2 -2
  88. package/addon/components/vendor/personnel-manager.hbs +2 -2
  89. package/addon/components/work-order/form.js +12 -4
  90. package/addon/controllers/connectivity/devices/index/details/vehicle.js +11 -0
  91. package/addon/controllers/connectivity/devices/index/details.js +3 -3
  92. package/addon/controllers/connectivity/devices/index.js +1 -0
  93. package/addon/controllers/connectivity/telematics/details/attachments.js +28 -47
  94. package/addon/controllers/connectivity/telematics/details/devices.js +1 -0
  95. package/addon/controllers/management/contacts/customers.js +4 -0
  96. package/addon/controllers/management/contacts/index.js +32 -0
  97. package/addon/controllers/management/trailers/index/details.js +119 -0
  98. package/addon/controllers/management/trailers/index/edit.js +67 -0
  99. package/addon/controllers/management/trailers/index/new.js +43 -0
  100. package/addon/controllers/management/trailers/index.js +599 -0
  101. package/addon/controllers/management/vehicles/index/details.js +16 -0
  102. package/addon/controllers/management/vehicles/index.js +78 -4
  103. package/addon/controllers/management/vendors/index.js +32 -0
  104. package/addon/controllers/operations/orders/index.js +3 -1
  105. package/addon/controllers/settings/custom-fields.js +8 -0
  106. package/addon/extension.js +7 -0
  107. package/addon/helpers/placeholder-image.js +6 -0
  108. package/addon/helpers/resource-image.js +10 -0
  109. package/addon/helpers/trailer-placeholder-image.js +6 -0
  110. package/addon/routes/management/trailers/index/details/connections.js +2 -0
  111. package/addon/routes/management/trailers/index/details/devices.js +2 -0
  112. package/addon/routes/management/trailers/index/details/equipment.js +2 -0
  113. package/addon/routes/management/trailers/index/details/index.js +2 -0
  114. package/addon/routes/management/trailers/index/details/maintenance-history.js +2 -0
  115. package/addon/routes/management/trailers/index/details/positions.js +2 -0
  116. package/addon/routes/management/trailers/index/details/schedules.js +2 -0
  117. package/addon/routes/management/trailers/index/details/work-orders.js +2 -0
  118. package/addon/routes/management/trailers/index/details.js +30 -0
  119. package/addon/routes/management/trailers/index/edit.js +23 -0
  120. package/addon/routes/management/trailers/index/new.js +14 -0
  121. package/addon/routes/management/trailers/index.js +42 -0
  122. package/addon/routes/management/vehicles/index/details/trailers.js +2 -0
  123. package/addon/routes/management/vehicles/index.js +7 -1
  124. package/addon/routes.js +17 -0
  125. package/addon/services/device-actions.js +26 -16
  126. package/addon/services/trailer-actions.js +394 -0
  127. package/addon/services/vehicle-actions.js +111 -0
  128. package/addon/styles/fleetops-engine.css +349 -0
  129. package/addon/templates/connectivity/devices/index/details/vehicle.hbs +19 -19
  130. package/addon/templates/connectivity/telematics/details/attachments.hbs +15 -15
  131. package/addon/templates/management/contacts/customers.hbs +48 -29
  132. package/addon/templates/management/contacts/index.hbs +52 -33
  133. package/addon/templates/management/trailers/index/details/connections.hbs +1 -0
  134. package/addon/templates/management/trailers/index/details/devices.hbs +1 -0
  135. package/addon/templates/management/trailers/index/details/equipment.hbs +1 -0
  136. package/addon/templates/management/trailers/index/details/index.hbs +1 -0
  137. package/addon/templates/management/trailers/index/details/maintenance-history.hbs +1 -0
  138. package/addon/templates/management/trailers/index/details/positions.hbs +1 -0
  139. package/addon/templates/management/trailers/index/details/schedules.hbs +1 -0
  140. package/addon/templates/management/trailers/index/details/work-orders.hbs +1 -0
  141. package/addon/templates/management/trailers/index/details.hbs +15 -0
  142. package/addon/templates/management/trailers/index/edit.hbs +12 -0
  143. package/addon/templates/management/trailers/index/new.hbs +11 -0
  144. package/addon/templates/management/trailers/index.hbs +67 -0
  145. package/addon/templates/management/vehicles/index/details/equipment.hbs +1 -1
  146. package/addon/templates/management/vehicles/index/details/trailers.hbs +1 -0
  147. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  148. package/addon/templates/management/vendors/index.hbs +66 -32
  149. package/addon/utils/device-table-columns.js +10 -6
  150. package/addon/utils/live-map-card-content.js +56 -0
  151. package/addon/utils/placeholder-images.js +71 -0
  152. package/addon/utils/trailer-placeholder-image.js +11 -0
  153. package/app/components/attachment-list.js +1 -0
  154. package/app/components/cell/translated-value.js +1 -0
  155. package/app/components/contact/card.js +1 -0
  156. package/app/components/equipment/pill.js +1 -0
  157. package/app/components/filter/multi-model.js +1 -0
  158. package/app/components/modals/attach-equipment.js +1 -0
  159. package/app/components/modals/attach-trailer.js +1 -0
  160. package/app/components/modals/select-trailer.js +1 -0
  161. package/app/components/trailer/card.js +1 -0
  162. package/app/components/trailer/details/connections.js +1 -0
  163. package/app/components/trailer/details/equipment.js +1 -0
  164. package/app/components/trailer/details/maintenance-history.js +1 -0
  165. package/app/components/trailer/details/schedules.js +1 -0
  166. package/app/components/trailer/details/work-orders.js +1 -0
  167. package/app/components/trailer/details.js +1 -0
  168. package/app/components/trailer/form.js +1 -0
  169. package/app/components/trailer/panel-header.js +1 -0
  170. package/app/components/trailer/pill.js +1 -0
  171. package/app/components/vehicle/details/equipment.js +1 -0
  172. package/app/components/vehicle/details/trailers.js +1 -0
  173. package/app/components/vendor/card.js +1 -0
  174. package/app/controllers/management/trailers/index/details.js +1 -0
  175. package/app/controllers/management/trailers/index/edit.js +1 -0
  176. package/app/controllers/management/trailers/index/new.js +1 -0
  177. package/app/controllers/management/trailers/index.js +1 -0
  178. package/app/helpers/placeholder-image.js +1 -0
  179. package/app/helpers/resource-image.js +1 -0
  180. package/app/helpers/trailer-placeholder-image.js +1 -0
  181. package/app/routes/management/trailers/index/details/connections.js +1 -0
  182. package/app/routes/management/trailers/index/details/devices.js +1 -0
  183. package/app/routes/management/trailers/index/details/equipment.js +1 -0
  184. package/app/routes/management/trailers/index/details/index.js +1 -0
  185. package/app/routes/management/trailers/index/details/maintenance-history.js +1 -0
  186. package/app/routes/management/trailers/index/details/positions.js +1 -0
  187. package/app/routes/management/trailers/index/details/schedules.js +1 -0
  188. package/app/routes/management/trailers/index/details/work-orders.js +1 -0
  189. package/app/routes/management/trailers/index/details.js +1 -0
  190. package/app/routes/management/trailers/index/edit.js +1 -0
  191. package/app/routes/management/trailers/index/new.js +1 -0
  192. package/app/routes/management/trailers/index.js +1 -0
  193. package/app/routes/management/vehicles/index/details/trailers.js +1 -0
  194. package/app/services/trailer-actions.js +1 -0
  195. package/app/templates/management/trailers/index/details/connections.js +1 -0
  196. package/app/templates/management/trailers/index/details/devices.js +1 -0
  197. package/app/templates/management/trailers/index/details/equipment.js +1 -0
  198. package/app/templates/management/trailers/index/details/index.js +1 -0
  199. package/app/templates/management/trailers/index/details/maintenance-history.js +1 -0
  200. package/app/templates/management/trailers/index/details/positions.js +1 -0
  201. package/app/templates/management/trailers/index/details/schedules.js +1 -0
  202. package/app/templates/management/trailers/index/details/work-orders.js +1 -0
  203. package/app/templates/management/trailers/index/details.js +1 -0
  204. package/app/templates/management/trailers/index/edit.js +1 -0
  205. package/app/templates/management/trailers/index/new.js +1 -0
  206. package/app/templates/management/trailers/index.js +1 -0
  207. package/app/templates/management/vehicles/index/details/trailers.js +1 -0
  208. package/composer.json +1 -1
  209. package/extension.json +1 -1
  210. package/package.json +1 -1
  211. package/server/migrations/2026_09_03_000001_add_first_class_trailer_columns_to_assets_table.php +58 -0
  212. package/server/migrations/2026_09_03_000002_create_asset_connections_table.php +43 -0
  213. package/server/migrations/2026_09_03_000003_create_device_installations_table.php +33 -0
  214. package/server/src/Auth/Schemas/FleetOps.php +7 -0
  215. package/server/src/Events/TrailerLocationChanged.php +70 -0
  216. package/server/src/Exceptions/DeviceAlreadyAttachedException.php +18 -0
  217. package/server/src/Exports/TrailerExport.php +31 -0
  218. package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +8 -2
  219. package/server/src/Http/Controllers/Api/v1/DeviceController.php +22 -15
  220. package/server/src/Http/Controllers/Api/v1/EquipmentController.php +47 -0
  221. package/server/src/Http/Controllers/Api/v1/TrailerController.php +362 -0
  222. package/server/src/Http/Controllers/Api/v1/VehicleController.php +1 -0
  223. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +3 -0
  224. package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
  225. package/server/src/Http/Controllers/Internal/v1/SearchController.php +16 -0
  226. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +360 -0
  227. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +74 -0
  228. package/server/src/Http/Filter/EquipmentFilter.php +51 -0
  229. package/server/src/Http/Filter/OrderFilter.php +15 -0
  230. package/server/src/Http/Filter/TrailerFilter.php +269 -0
  231. package/server/src/Http/Filter/VehicleFilter.php +70 -3
  232. package/server/src/Http/Requests/CreateDeviceRequest.php +1 -1
  233. package/server/src/Http/Requests/CreateEquipmentRequest.php +1 -1
  234. package/server/src/Http/Requests/CreateTrailerRequest.php +54 -0
  235. package/server/src/Http/Requests/UpdateTrailerRequest.php +7 -0
  236. package/server/src/Http/Resources/v1/AssetConnection.php +90 -0
  237. package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +29 -3
  238. package/server/src/Http/Resources/v1/Device.php +3 -1
  239. package/server/src/Http/Resources/v1/Equipment.php +3 -1
  240. package/server/src/Http/Resources/v1/Fleet.php +3 -1
  241. package/server/src/Http/Resources/v1/Index/Vehicle.php +10 -0
  242. package/server/src/Http/Resources/v1/Trailer.php +199 -0
  243. package/server/src/Http/Resources/v1/Vehicle.php +2 -0
  244. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +3 -0
  245. package/server/src/Imports/TrailerImport.php +33 -0
  246. package/server/src/Models/Asset.php +39 -10
  247. package/server/src/Models/AssetConnection.php +43 -0
  248. package/server/src/Models/Device.php +39 -8
  249. package/server/src/Models/DeviceInstallation.php +27 -0
  250. package/server/src/Models/Equipment.php +2 -0
  251. package/server/src/Models/Trailer.php +181 -0
  252. package/server/src/Models/Vehicle.php +13 -0
  253. package/server/src/Providers/FleetOpsServiceProvider.php +4 -2
  254. package/server/src/Support/Ai/FleetOpsAiQueryResources.php +22 -0
  255. package/server/src/Support/Reporting/FleetOpsReportSchema.php +38 -0
  256. package/server/src/Support/Telematics/TelematicService.php +35 -0
  257. package/server/src/Support/TrailerSummary.php +35 -0
  258. package/server/src/routes.php +29 -0
  259. package/server/tests/CompactResourceSerializationTest.php +36 -0
  260. package/server/tests/ControllerFilterContractsTest.php +29 -0
  261. package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +30 -13
  262. package/server/tests/Feature/Http/Api/TrailerControllerLifecycleTest.php +640 -0
  263. package/server/tests/Feature/Http/Api/VehiclePublicContractTest.php +23 -0
  264. package/server/tests/Feature/Http/Internal/DeviceControllerContractsTest.php +20 -5
  265. package/server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php +19 -0
  266. package/server/tests/ModelAccessorContractsTest.php +10 -0
  267. package/server/tests/OrderFilterExecutionTest.php +5 -0
  268. package/server/tests/PublicApiControllerInputContractsTest.php +11 -0
  269. package/server/tests/PublicFleetOpsRestEndpointsTest.php +15 -3
  270. package/server/tests/ReportSchemaContractsTest.php +4 -0
  271. package/server/tests/TelematicsHardeningTest.php +4 -2
  272. package/server/tests/TrailerSupportCoverageTest.php +524 -0
  273. package/server/tests/Unit/Http/Resources/FleetResourceTest.php +17 -0
  274. package/server/tests/Unit/Http/Resources/IndexVehicleResourceTest.php +35 -0
  275. package/server/tests/Unit/Models/EquipmentTest.php +22 -0
  276. package/server/tests/Unit/Models/ModelLookupFallbackSeamsTest.php +7 -3
  277. package/server/tests/Unit/Models/TrailerTest.php +204 -0
  278. package/server/tests/Unit/Support/Telematics/TelematicServiceTelemetryTest.php +93 -0
  279. package/server/tests/VehicleControllerHelperContractsTest.php +14 -0
  280. package/server/tests/VehicleResourceContractsTest.php +39 -0
  281. package/translations/ar-ae.yml +396 -0
  282. package/translations/bg-bg.yaml +424 -0
  283. package/translations/en-us.yaml +423 -0
  284. package/translations/es-pa.yaml +425 -0
  285. package/translations/fr-fr.yaml +424 -0
  286. package/translations/mn-mn.yaml +424 -0
  287. package/translations/pt-br.yaml +424 -0
  288. package/translations/ru-ru.yaml +424 -0
  289. package/translations/vi-vn.yaml +435 -0
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/trailer/form';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/trailer/panel-header';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/trailer/pill';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/vehicle/details/equipment';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/vehicle/details/trailers';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/vendor/card';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/controllers/management/trailers/index/details';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/controllers/management/trailers/index/edit';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/controllers/management/trailers/index/new';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/controllers/management/trailers/index';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/helpers/placeholder-image';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/helpers/resource-image';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/helpers/trailer-placeholder-image';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/connections';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/devices';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/equipment';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/index';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/maintenance-history';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/positions';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/schedules';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details/work-orders';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/details';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/edit';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index/new';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/trailers/index';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/management/vehicles/index/details/trailers';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/services/trailer-actions';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/connections';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/devices';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/equipment';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/index';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/maintenance-history';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/positions';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/schedules';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details/work-orders';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/details';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/edit';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index/new';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/trailers/index';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/management/vehicles/index/details/trailers';
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.6.63",
3
+ "version": "0.6.65",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.6.63",
3
+ "version": "0.6.65",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.6.63",
3
+ "version": "0.6.65",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -0,0 +1,58 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ public function up(): void
9
+ {
10
+ Schema::table('assets', function (Blueprint $table) {
11
+ // Existing Asset rows predate public identifiers. Keep this nullable so
12
+ // Trailer support does not rewrite unrelated historical Asset data.
13
+ $table->string('public_id')->nullable()->after('uuid')->unique();
14
+ $table->string('asset_class')->nullable()->after('type')->index();
15
+ $table->foreignUuid('photo_uuid')->nullable()->after('current_place_uuid')->constrained('files', 'uuid')->nullOnDelete();
16
+ $table->boolean('online')->nullable()->after('altitude')->index();
17
+ $table->timestamp('last_online_at')->nullable()->after('online')->index();
18
+ $table->json('telematics')->nullable()->after('attributes');
19
+ $table->date('purchased_at')->nullable()->after('lease_expires_at');
20
+ $table->decimal('length', 12, 3)->nullable();
21
+ $table->decimal('width', 12, 3)->nullable();
22
+ $table->decimal('height', 12, 3)->nullable();
23
+ $table->decimal('tare_weight', 12, 3)->nullable();
24
+ $table->decimal('gvwr', 12, 3)->nullable();
25
+ $table->decimal('payload_capacity', 12, 3)->nullable();
26
+ $table->decimal('cargo_volume', 12, 3)->nullable();
27
+ $table->unsignedSmallInteger('axle_count')->nullable();
28
+ $table->unsignedSmallInteger('tire_count')->nullable();
29
+ $table->unsignedSmallInteger('door_count')->nullable();
30
+ $table->string('body_type')->nullable()->index();
31
+ $table->string('coupling_type')->nullable();
32
+ $table->string('brake_type')->nullable();
33
+ $table->boolean('abs_equipped')->nullable();
34
+ $table->boolean('ebs_equipped')->nullable();
35
+ $table->boolean('refrigerated')->nullable()->index();
36
+ $table->decimal('temperature_min', 8, 3)->nullable();
37
+ $table->decimal('temperature_max', 8, 3)->nullable();
38
+ $table->decimal('reefer_engine_hours', 14, 2)->nullable();
39
+ $table->index(['company_uuid', 'asset_class', 'status'], 'assets_company_class_status_index');
40
+ });
41
+ }
42
+
43
+ public function down(): void
44
+ {
45
+ Schema::table('assets', function (Blueprint $table) {
46
+ $table->dropIndex('assets_company_class_status_index');
47
+ $table->dropForeign(['photo_uuid']);
48
+ $table->dropUnique(['public_id']);
49
+ $table->dropColumn([
50
+ 'public_id', 'asset_class', 'photo_uuid', 'online', 'last_online_at', 'telematics', 'purchased_at',
51
+ 'length', 'width', 'height', 'tare_weight', 'gvwr', 'payload_capacity', 'cargo_volume',
52
+ 'axle_count', 'tire_count', 'door_count', 'body_type', 'coupling_type', 'brake_type',
53
+ 'abs_equipped', 'ebs_equipped', 'refrigerated', 'temperature_min', 'temperature_max',
54
+ 'reefer_engine_hours',
55
+ ]);
56
+ });
57
+ }
58
+ };
@@ -0,0 +1,43 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ public function up(): void
9
+ {
10
+ Schema::create('asset_connections', function (Blueprint $table) {
11
+ $table->bigIncrements('id');
12
+ $table->uuid('uuid')->unique();
13
+ $table->string('public_id')->unique();
14
+ $table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
15
+ $table->string('connector_type');
16
+ $table->uuid('connector_uuid');
17
+ $table->string('connected_type');
18
+ $table->uuid('connected_uuid');
19
+ $table->uuid('active_connected_uuid')->nullable()->unique();
20
+ $table->string('active_connector_position')->nullable()->unique();
21
+ $table->string('relationship_type')->default('towing');
22
+ $table->unsignedSmallInteger('position')->default(1);
23
+ $table->timestamp('connected_at');
24
+ $table->timestamp('disconnected_at')->nullable();
25
+ $table->string('source')->default('manual');
26
+ $table->string('confidence')->nullable();
27
+ $table->text('notes')->nullable();
28
+ $table->json('meta')->nullable();
29
+ $table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
30
+ $table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
31
+ $table->timestamps();
32
+ $table->softDeletes();
33
+ $table->index(['company_uuid', 'connector_type', 'connector_uuid'], 'asset_connections_connector_index');
34
+ $table->index(['company_uuid', 'connected_type', 'connected_uuid'], 'asset_connections_connected_index');
35
+ $table->index(['company_uuid', 'disconnected_at'], 'asset_connections_active_index');
36
+ });
37
+ }
38
+
39
+ public function down(): void
40
+ {
41
+ Schema::dropIfExists('asset_connections');
42
+ }
43
+ };
@@ -0,0 +1,33 @@
1
+ <?php
2
+
3
+ use Illuminate\Database\Migrations\Migration;
4
+ use Illuminate\Database\Schema\Blueprint;
5
+ use Illuminate\Support\Facades\Schema;
6
+
7
+ return new class extends Migration {
8
+ public function up(): void
9
+ {
10
+ Schema::create('device_installations', function (Blueprint $table) {
11
+ $table->bigIncrements('id');
12
+ $table->uuid('uuid')->unique();
13
+ $table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
14
+ $table->foreignUuid('device_uuid')->constrained('devices', 'uuid')->cascadeOnDelete();
15
+ $table->string('attachable_type');
16
+ $table->uuid('attachable_uuid');
17
+ $table->uuid('active_device_uuid')->nullable()->unique();
18
+ $table->timestamp('installed_at');
19
+ $table->timestamp('removed_at')->nullable();
20
+ $table->string('source')->default('manual');
21
+ $table->json('metadata')->nullable();
22
+ $table->softDeletes();
23
+ $table->timestamps();
24
+ $table->index(['company_uuid', 'attachable_type', 'attachable_uuid'], 'device_installations_attachable_index');
25
+ $table->index(['device_uuid', 'installed_at'], 'device_installations_history_index');
26
+ });
27
+ }
28
+
29
+ public function down(): void
30
+ {
31
+ Schema::dropIfExists('device_installations');
32
+ }
33
+ };
@@ -61,6 +61,10 @@ class FleetOps
61
61
  'name' => 'vehicle',
62
62
  'actions' => ['assign-driver-for', 'export', 'import'],
63
63
  ],
64
+ [
65
+ 'name' => 'trailer',
66
+ 'actions' => ['export', 'import', 'attach-vehicle-for', 'detach-vehicle-for', 'attach-device-for', 'detach-device-for', 'attach-equipment-for', 'detach-equipment-for'],
67
+ ],
64
68
  [
65
69
  'name' => 'fleet',
66
70
  'actions' => ['assign-driver-for', 'assign-vehicle-for', 'remove-driver-for', 'remove-vehicle-for', 'export', 'import'],
@@ -217,6 +221,7 @@ class FleetOps
217
221
  '* fleet',
218
222
  '* driver',
219
223
  '* vehicle',
224
+ '* trailer',
220
225
  ],
221
226
  ],
222
227
  [
@@ -294,6 +299,7 @@ class FleetOps
294
299
  '* work-order',
295
300
  '* equipment',
296
301
  '* part',
302
+ '* trailer',
297
303
  'see vehicle',
298
304
  'list vehicle',
299
305
  'view vehicle',
@@ -318,6 +324,7 @@ class FleetOps
318
324
  '* work-order',
319
325
  '* equipment',
320
326
  '* part',
327
+ '* trailer',
321
328
  ],
322
329
  ],
323
330
  [
@@ -0,0 +1,70 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Events;
4
+
5
+ use Fleetbase\FleetOps\Models\Trailer;
6
+ use Illuminate\Broadcasting\Channel;
7
+ use Illuminate\Broadcasting\InteractsWithSockets;
8
+ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
9
+ use Illuminate\Foundation\Bus\Dispatchable;
10
+ use Illuminate\Queue\SerializesModels;
11
+
12
+ class TrailerLocationChanged implements ShouldBroadcast
13
+ {
14
+ use Dispatchable;
15
+ use InteractsWithSockets;
16
+ use SerializesModels;
17
+
18
+ public string $eventId;
19
+ public string $sentAt;
20
+ public string $companyUuid;
21
+ public string $trailerUuid;
22
+ public string $trailerId;
23
+ public mixed $location;
24
+ public mixed $altitude;
25
+ public mixed $heading;
26
+ public mixed $speed;
27
+ public array $additionalData;
28
+
29
+ public function __construct(Trailer $trailer, array $additionalData = [])
30
+ {
31
+ $this->eventId = uniqid('event_');
32
+ $this->sentAt = now()->toDateTimeString();
33
+ $this->companyUuid = $trailer->company_uuid;
34
+ $this->trailerUuid = $trailer->uuid;
35
+ $this->trailerId = $trailer->public_id;
36
+ $this->location = $trailer->location;
37
+ $this->altitude = $trailer->altitude;
38
+ $this->heading = $trailer->heading;
39
+ $this->speed = $trailer->speed;
40
+ $this->additionalData = $additionalData;
41
+ }
42
+
43
+ public function broadcastOn(): array
44
+ {
45
+ return [new Channel('company.' . $this->companyUuid), new Channel('trailer.' . $this->trailerId), new Channel('trailer.' . $this->trailerUuid)];
46
+ }
47
+
48
+ public function broadcastAs(): string
49
+ {
50
+ return 'trailer.location_changed';
51
+ }
52
+
53
+ public function broadcastWith(): array
54
+ {
55
+ return [
56
+ 'id' => $this->eventId,
57
+ 'api_version' => config('api.version'),
58
+ 'event' => $this->broadcastAs(),
59
+ 'created_at' => $this->sentAt,
60
+ 'data' => [
61
+ 'id' => $this->trailerId,
62
+ 'location' => $this->location,
63
+ 'altitude' => $this->altitude,
64
+ 'heading' => $this->heading,
65
+ 'speed' => $this->speed,
66
+ 'additionalData' => $this->additionalData,
67
+ ],
68
+ ];
69
+ }
70
+ }
@@ -0,0 +1,18 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Exceptions;
4
+
5
+ /**
6
+ * Thrown when a device that is already installed on one asset is attached to another
7
+ * without being detached first. Attaching silently used to re-home the device, which
8
+ * let an operator "steal" a tracker from a vehicle with no feedback.
9
+ */
10
+ class DeviceAlreadyAttachedException extends \DomainException
11
+ {
12
+ public static function for(?string $attachedToName): self
13
+ {
14
+ $target = $attachedToName ? ' to ' . $attachedToName : ' to another asset';
15
+
16
+ return new self('Device is already attached' . $target . '. Detach it before attaching it elsewhere.');
17
+ }
18
+ }
@@ -0,0 +1,31 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Exports;
4
+
5
+ use Fleetbase\FleetOps\Models\Trailer;
6
+ use Maatwebsite\Excel\Concerns\FromCollection;
7
+ use Maatwebsite\Excel\Concerns\ShouldAutoSize;
8
+ use Maatwebsite\Excel\Concerns\WithHeadings;
9
+ use Maatwebsite\Excel\Concerns\WithMapping;
10
+
11
+ class TrailerExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize
12
+ {
13
+ public function __construct(protected array $selections = [])
14
+ {
15
+ }
16
+
17
+ public function headings(): array
18
+ {
19
+ return ['ID', 'Name', 'Code', 'Type', 'Status', 'VIN', 'Plate Number', 'Serial Number', 'Make', 'Model', 'Year', 'Length', 'Width', 'Height', 'Tare Weight', 'GVWR', 'Payload Capacity', 'Cargo Volume', 'Axle Count', 'Tire Count', 'Coupling Type', 'Brake Type', 'Refrigerated', 'Measurement System', 'Odometer', 'Odometer Unit', 'Ownership Type', 'Purchased At', 'Lease Expires At', 'Notes', 'Date Created', 'Date Updated'];
20
+ }
21
+
22
+ public function map($trailer): array
23
+ {
24
+ return [$trailer->public_id, $trailer->name, $trailer->code, $trailer->type, $trailer->status, $trailer->vin, $trailer->plate_number, $trailer->serial_number, $trailer->make, $trailer->model, $trailer->year, $trailer->length, $trailer->width, $trailer->height, $trailer->tare_weight, $trailer->gvwr, $trailer->payload_capacity, $trailer->cargo_volume, $trailer->axle_count, $trailer->tire_count, $trailer->coupling_type, $trailer->brake_type, $trailer->refrigerated ? 'Yes' : 'No', $trailer->measurement_system, $trailer->odometer, $trailer->odometer_unit, $trailer->ownership_type, $trailer->purchased_at, $trailer->lease_expires_at, $trailer->notes, $trailer->created_at, $trailer->updated_at];
25
+ }
26
+
27
+ public function collection()
28
+ {
29
+ return Trailer::where('company_uuid', session('company'))->when($this->selections, fn ($q) => $q->whereIn('uuid', $this->selections))->get();
30
+ }
31
+ }
@@ -12,10 +12,10 @@ use Fleetbase\FleetOps\Models\FuelReport;
12
12
  use Fleetbase\FleetOps\Models\Order;
13
13
  use Fleetbase\FleetOps\Models\Part;
14
14
  use Fleetbase\FleetOps\Models\Telematic;
15
+ use Fleetbase\FleetOps\Models\Trailer;
15
16
  use Fleetbase\FleetOps\Models\Vehicle;
16
17
  use Fleetbase\FleetOps\Models\Vendor;
17
18
  use Fleetbase\FleetOps\Models\Warranty;
18
- use Fleetbase\FleetOps\Support\Utils;
19
19
  use Fleetbase\Models\File;
20
20
  use Illuminate\Database\Eloquent\Model;
21
21
  use Illuminate\Database\Eloquent\ModelNotFoundException;
@@ -72,7 +72,10 @@ trait ResolvesFleetOpsApiResources
72
72
  return [null, null];
73
73
  }
74
74
 
75
- $modelClass = Utils::getMutationType($type);
75
+ $modelClass = $this->allowedMorphTypes()[$type] ?? null;
76
+ if (!$modelClass) {
77
+ throw ValidationException::withMessages(['type' => ['Unsupported resource type.']]);
78
+ }
76
79
  $model = $this->resolveModel($modelClass, $id);
77
80
 
78
81
  return [$modelClass, $model->uuid];
@@ -166,6 +169,9 @@ trait ResolvesFleetOpsApiResources
166
169
  'fleet-ops:vehicle' => Vehicle::class,
167
170
  'vehicle' => Vehicle::class,
168
171
  Vehicle::class => Vehicle::class,
172
+ 'fleet-ops:trailer' => Trailer::class,
173
+ 'trailer' => Trailer::class,
174
+ Trailer::class => Trailer::class,
169
175
  'fleet-ops:driver' => Driver::class,
170
176
  'driver' => Driver::class,
171
177
  Driver::class => Driver::class,
@@ -2,6 +2,7 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
4
 
5
+ use Fleetbase\FleetOps\Exceptions\DeviceAlreadyAttachedException;
5
6
  use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
6
7
  use Fleetbase\FleetOps\Http\Requests\CreateDeviceRequest;
7
8
  use Fleetbase\FleetOps\Http\Requests\UpdateDeviceRequest;
@@ -9,7 +10,6 @@ use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
9
10
  use Fleetbase\FleetOps\Http\Resources\v1\Device as DeviceResource;
10
11
  use Fleetbase\FleetOps\Models\Device;
11
12
  use Fleetbase\FleetOps\Models\Telematic;
12
- use Fleetbase\FleetOps\Models\Vehicle;
13
13
  use Fleetbase\FleetOps\Models\Warranty;
14
14
  use Fleetbase\FleetOps\Support\Utils;
15
15
  use Fleetbase\Http\Controllers\Controller;
@@ -90,28 +90,34 @@ class DeviceController extends Controller
90
90
  $this->rejectUuidIdentifiers($request);
91
91
 
92
92
  $request->validate([
93
- 'vehicle' => 'required_without:attachable|string|nullable',
93
+ 'vehicle' => 'required_without:attachable|string|nullable',
94
+ 'attachable' => 'required_without:vehicle|string|nullable',
95
+ 'attachable_type' => 'nullable|in:fleet-ops:vehicle,fleet-ops:trailer,vehicle,trailer',
94
96
  ]);
95
97
 
96
- $deviceId = $id;
97
- $vehicleId = $request->input('vehicle') ?? $request->input('attachable');
98
+ $deviceId = $id;
99
+ $attachableId = $request->input('vehicle') ?? $request->input('attachable');
100
+ $attachableType = $request->filled('vehicle') ? 'fleet-ops:vehicle' : $request->input('attachable_type', 'fleet-ops:vehicle');
98
101
 
99
102
  try {
100
- $device = $this->resolveModel(Device::class, $deviceId);
101
- $vehicle = $this->resolveModel(Vehicle::class, $vehicleId);
103
+ $device = $this->resolveModel(Device::class, $deviceId);
104
+ [$modelClass] = $this->resolveMorph($attachableType, $attachableId);
105
+ $attachable = $this->resolveModel($modelClass, $attachableId);
102
106
  } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
103
- $this->logDeviceAttachmentLookupFailure('attach', $deviceId, $vehicleId);
107
+ $this->logDeviceAttachmentLookupFailure('attach', $deviceId, $attachableId);
104
108
 
105
- return response()->json(['error' => 'Device or vehicle resource not found.'], 404);
109
+ return response()->json(['error' => 'Device or attachable resource not found.'], 404);
106
110
  }
107
111
 
108
112
  try {
109
- $device->attachTo($vehicle);
113
+ $device->attachTo($attachable);
110
114
  $this->loadDeviceRelations($device);
115
+ } catch (DeviceAlreadyAttachedException $e) {
116
+ return response()->json(['error' => $e->getMessage()], 409);
111
117
  } catch (\Throwable $e) {
112
- $this->logDeviceAttachmentFailure('attach', $device, $vehicle, $e);
118
+ $this->logDeviceAttachmentFailure('attach', $device, $attachable, $e);
113
119
 
114
- return response()->json(['error' => 'Unable to attach device to vehicle.'], 500);
120
+ return response()->json(['error' => 'Unable to attach device to resource.'], 500);
115
121
  }
116
122
 
117
123
  return response()->json([
@@ -136,7 +142,7 @@ class DeviceController extends Controller
136
142
  } catch (\Throwable $e) {
137
143
  $this->logDeviceAttachmentFailure('detach', $device, null, $e);
138
144
 
139
- return response()->json(['error' => 'Unable to detach device from vehicle.'], 500);
145
+ return response()->json(['error' => 'Unable to detach device from resource.'], 500);
140
146
  }
141
147
 
142
148
  return response()->json([
@@ -242,14 +248,15 @@ class DeviceController extends Controller
242
248
  ]);
243
249
  }
244
250
 
245
- protected function logDeviceAttachmentFailure(string $action, Device $device, ?Vehicle $vehicle, \Throwable $exception): void
251
+ protected function logDeviceAttachmentFailure(string $action, Device $device, ?\Illuminate\Database\Eloquent\Model $attachable, \Throwable $exception): void
246
252
  {
247
253
  Log::error('Public API device attachment failed', [
248
254
  'action' => $action,
249
255
  'device_uuid' => $device->uuid,
250
256
  'device_id' => $device->public_id,
251
- 'vehicle_uuid' => $vehicle?->uuid,
252
- 'vehicle_id' => $vehicle?->public_id,
257
+ 'attachable_type' => $attachable ? get_class($attachable) : null,
258
+ 'attachable_uuid' => $attachable?->uuid,
259
+ 'attachable_id' => $attachable?->public_id,
253
260
  'company_uuid' => session('company'),
254
261
  'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
255
262
  'exception_class' => get_class($exception),