@fleetbase/fleetops-engine 0.6.49 → 0.6.51

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 (271) hide show
  1. package/README.md +185 -67
  2. package/addon/components/contact/panel-header.hbs +11 -48
  3. package/addon/components/customer/create-order-form.hbs +2 -2
  4. package/addon/components/customer/details.hbs +1 -2
  5. package/addon/components/customer/form.hbs +62 -50
  6. package/addon/components/customer/form.js +27 -1
  7. package/addon/components/customer/order-form.hbs +2 -2
  8. package/addon/components/customer/orders.hbs +50 -2
  9. package/addon/components/customer/orders.js +67 -5
  10. package/addon/components/display-place.hbs +2 -2
  11. package/addon/components/driver/form.hbs +1 -1
  12. package/addon/components/home/getting-started-guidance.hbs +113 -0
  13. package/addon/components/home/getting-started-guidance.js +107 -0
  14. package/addon/components/issue/details/comments.hbs +3 -0
  15. package/addon/components/issue/details/comments.js +3 -0
  16. package/addon/components/issue/details/correspondence.hbs +9 -0
  17. package/addon/components/issue/details/correspondence.js +83 -0
  18. package/addon/components/issue/details/documents.hbs +69 -0
  19. package/addon/components/issue/details/documents.js +85 -0
  20. package/addon/components/issue/details.hbs +220 -59
  21. package/addon/components/issue/details.js +245 -0
  22. package/addon/components/issue/form.hbs +19 -1
  23. package/addon/components/issue/panel-header.hbs +43 -0
  24. package/addon/components/issue/panel-header.js +22 -0
  25. package/addon/components/issue/timeline.hbs +40 -0
  26. package/addon/components/issue/timeline.js +45 -0
  27. package/addon/components/leaflet-tracking-marker.js +3 -0
  28. package/addon/components/map/drawer/position-listing.js +3 -0
  29. package/addon/components/map/google-live-map.js +6 -2
  30. package/addon/components/map/leaflet-live-map.hbs +48 -14
  31. package/addon/components/map/leaflet-live-map.js +201 -20
  32. package/addon/components/map/order-list-overlay.hbs +4 -4
  33. package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
  34. package/addon/components/modals/convert-customer-to-vendor.js +55 -0
  35. package/addon/components/modals/issue-assign.hbs +20 -0
  36. package/addon/components/modals/issue-assign.js +49 -0
  37. package/addon/components/modals/issue-close.hbs +30 -0
  38. package/addon/components/modals/issue-close.js +73 -0
  39. package/addon/components/modals/issue-status.hbs +20 -0
  40. package/addon/components/modals/issue-status.js +43 -0
  41. package/addon/components/modals/order-change-destination.hbs +46 -0
  42. package/addon/components/modals/order-change-destination.js +106 -0
  43. package/addon/components/modals/reset-customer-credentials.js +12 -6
  44. package/addon/components/modals/stop-activity.hbs +28 -0
  45. package/addon/components/modals/stop-activity.js +52 -0
  46. package/addon/components/modals/update-order-activity.hbs +78 -2
  47. package/addon/components/modals/update-order-activity.js +197 -5
  48. package/addon/components/orchestrator/resource-panel.hbs +0 -10
  49. package/addon/components/orchestrator/resource-panel.js +2 -4
  50. package/addon/components/order/activity-list.hbs +2 -2
  51. package/addon/components/order/activity-timeline.hbs +31 -29
  52. package/addon/components/order/details/activity.hbs +3 -3
  53. package/addon/components/order/details/activity.js +13 -2
  54. package/addon/components/order/details/documents.js +2 -0
  55. package/addon/components/order/details/proof.hbs +40 -38
  56. package/addon/components/order/details/proof.js +31 -4
  57. package/addon/components/order/details/purchase-rate.hbs +2 -2
  58. package/addon/components/order/details/route.hbs +11 -3
  59. package/addon/components/order/details/route.js +91 -14
  60. package/addon/components/order/details/tracking.hbs +101 -72
  61. package/addon/components/order/details/tracking.js +149 -2
  62. package/addon/components/order/details.hbs +5 -5
  63. package/addon/components/order/details.js +19 -1
  64. package/addon/components/order/form/details.hbs +3 -3
  65. package/addon/components/order/form/documents.js +11 -4
  66. package/addon/components/order/form/route.hbs +108 -104
  67. package/addon/components/order/form/route.js +37 -1
  68. package/addon/components/order/form/service-rate.hbs +2 -2
  69. package/addon/components/order/header.hbs +1 -1
  70. package/addon/components/order-config-manager/activity-flow.hbs +4 -1
  71. package/addon/components/order-config-manager/activity-flow.js +178 -12
  72. package/addon/components/place/address.js +1 -1
  73. package/addon/components/route-list.hbs +17 -29
  74. package/addon/components/route-list.js +42 -10
  75. package/addon/components/service-rate/details.hbs +42 -1
  76. package/addon/components/service-rate/form.hbs +119 -1
  77. package/addon/components/service-rate/form.js +59 -0
  78. package/addon/components/vehicle/form.js +1 -1
  79. package/addon/components/vehicle/pill.hbs +1 -1
  80. package/addon/components/vendor/personnel-manager.hbs +166 -0
  81. package/addon/components/vendor/personnel-manager.js +143 -0
  82. package/addon/components/widget/live-fleet.hbs +1 -0
  83. package/addon/components/widget/live-fleet.js +2 -7
  84. package/addon/components/widget/revenue-trend.hbs +1 -1
  85. package/addon/components/widget/revenue-trend.js +75 -2
  86. package/addon/controllers/management/contacts/customers/details.js +31 -13
  87. package/addon/controllers/management/contacts/customers.js +16 -5
  88. package/addon/controllers/management/contacts/index/details.js +31 -13
  89. package/addon/controllers/management/contacts/index.js +7 -0
  90. package/addon/controllers/management/issues/index/details.js +52 -6
  91. package/addon/controllers/management/vehicles/index/new.js +1 -1
  92. package/addon/controllers/management/vendors/index/details.js +4 -0
  93. package/addon/controllers/operations/orders/index/details.js +15 -7
  94. package/addon/controllers/operations/orders/index.js +15 -1
  95. package/addon/extension.js +19 -12
  96. package/addon/helpers/format-duration.js +1 -38
  97. package/addon/helpers/place-address-html.js +2 -2
  98. package/addon/initializers/load-leaflet-assets.js +5 -6
  99. package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
  100. package/addon/routes/management/issues/index/details.js +1 -1
  101. package/addon/routes/operations/service-rates/index.js +1 -1
  102. package/addon/routes.js +1 -0
  103. package/addon/services/contact-actions.js +231 -1
  104. package/addon/services/geofence.js +163 -19
  105. package/addon/services/getting-started.js +44 -0
  106. package/addon/services/issue-actions.js +71 -0
  107. package/addon/services/leaflet-contextmenu-manager.js +27 -0
  108. package/addon/services/leaflet-map-manager.js +2 -0
  109. package/addon/services/map-adapter/leaflet.js +19 -0
  110. package/addon/services/service-areas.js +3 -0
  111. package/addon/services/vehicle-actions.js +1 -1
  112. package/addon/styles/fleetops-engine.css +1127 -1
  113. package/addon/templates/management/contacts/customers/details.hbs +1 -1
  114. package/addon/templates/management/contacts/index/details.hbs +1 -1
  115. package/addon/templates/management/contacts.hbs +2 -2
  116. package/addon/templates/management/issues/index/details/index.hbs +3 -1
  117. package/addon/templates/management/issues/index/details.hbs +3 -2
  118. package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
  119. package/addon/templates/operations/orders/index/details/index.hbs +2 -2
  120. package/addon/templates/operations/orders/index.hbs +1 -1
  121. package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
  122. package/addon/templates/operations/service-rates/index/new.hbs +1 -0
  123. package/addon/templates/settings/payments/onboard.hbs +8 -8
  124. package/addon/utils/fleet-ops-options.js +2 -1
  125. package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
  126. package/addon/utils/leaflet-plugin-loader.js +219 -0
  127. package/addon/utils/normalize-order-config-flow.js +41 -0
  128. package/addon/utils/place-address-html.js +3 -2
  129. package/addon/utils/route-visualization.js +54 -7
  130. package/addon/utils/setup-customer-portal.js +4 -64
  131. package/app/components/home/getting-started-guidance.js +1 -0
  132. package/app/components/issue/details/comments.js +1 -0
  133. package/app/components/issue/details/correspondence.js +1 -0
  134. package/app/components/issue/details/documents.js +1 -0
  135. package/app/components/issue/panel-header.js +1 -0
  136. package/app/components/modals/convert-customer-to-vendor.js +1 -0
  137. package/app/components/modals/issue-assign.js +1 -0
  138. package/app/components/modals/issue-close.js +1 -0
  139. package/app/components/modals/issue-status.js +1 -0
  140. package/app/components/modals/order-change-destination.js +1 -0
  141. package/app/components/modals/stop-activity.js +1 -0
  142. package/app/components/vendor/personnel-manager.js +1 -0
  143. package/app/helpers/format-duration.js +1 -1
  144. package/app/services/getting-started.js +1 -0
  145. package/app/utils/leaflet-draw-namespace-guard.js +1 -0
  146. package/app/utils/leaflet-plugin-loader.js +1 -0
  147. package/composer.json +1 -1
  148. package/extension.json +1 -1
  149. package/package.json +5 -5
  150. package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
  151. package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
  152. package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
  153. package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
  154. package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
  155. package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
  156. package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
  157. package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
  158. package/server/resources/views/labels/default.php +57 -49
  159. package/server/resources/views/labels/waypoint-label.php +27 -20
  160. package/server/resources/views/mail/customer-credentials.blade.php +16 -13
  161. package/server/seeders/Testing/ConnectivitySeeder.php +1 -0
  162. package/server/seeders/Testing/FleetSeeder.php +22 -22
  163. package/server/seeders/Testing/OrdersSeeder.php +6 -22
  164. package/server/seeders/Testing/VroomOrchestrationSeeder.php +21 -21
  165. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
  166. package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
  167. package/server/src/Expansions/UserFilterExpansion.php +28 -0
  168. package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
  169. package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
  170. package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
  171. package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
  172. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
  173. package/server/src/Http/Controllers/Api/v1/OrderController.php +109 -80
  174. package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
  175. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
  176. package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
  177. package/server/src/Http/Controllers/Internal/v1/ContactController.php +154 -0
  178. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
  179. package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
  180. package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
  181. package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
  182. package/server/src/Http/Controllers/Internal/v1/OrderController.php +530 -52
  183. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
  184. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  185. package/server/src/Http/Controllers/Internal/v1/SettingController.php +2 -2
  186. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
  187. package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
  188. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
  189. package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
  190. package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
  191. package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
  192. package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
  193. package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
  194. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
  195. package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
  196. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
  197. package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
  198. package/server/src/Http/Resources/v1/Customer.php +79 -0
  199. package/server/src/Http/Resources/v1/Index/Order.php +12 -9
  200. package/server/src/Http/Resources/v1/Issue.php +25 -0
  201. package/server/src/Http/Resources/v1/Order.php +20 -18
  202. package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
  203. package/server/src/Http/Resources/v1/Payload.php +23 -20
  204. package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
  205. package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
  206. package/server/src/Listeners/HandleOrderDispatched.php +2 -2
  207. package/server/src/Mail/CustomerCredentialsMail.php +5 -6
  208. package/server/src/Models/Contact.php +122 -66
  209. package/server/src/Models/Customer.php +64 -0
  210. package/server/src/Models/Driver.php +12 -3
  211. package/server/src/Models/Entity.php +2 -2
  212. package/server/src/Models/Issue.php +47 -6
  213. package/server/src/Models/Order.php +62 -10
  214. package/server/src/Models/Payload.php +17 -1
  215. package/server/src/Models/ServiceArea.php +1 -1
  216. package/server/src/Models/ServiceRate.php +273 -1
  217. package/server/src/Models/ServiceRateFee.php +39 -9
  218. package/server/src/Models/TrackingNumber.php +8 -2
  219. package/server/src/Models/Vehicle.php +18 -2
  220. package/server/src/Models/VendorPersonnel.php +10 -1
  221. package/server/src/Models/Waypoint.php +2 -2
  222. package/server/src/Models/Zone.php +1 -1
  223. package/server/src/Notifications/LateDeparture.php +2 -2
  224. package/server/src/Notifications/ProlongedStoppage.php +2 -2
  225. package/server/src/Notifications/RouteDeviation.php +2 -2
  226. package/server/src/Observers/ContactObserver.php +2 -0
  227. package/server/src/Observers/PurchaseRateObserver.php +20 -0
  228. package/server/src/Observers/ServiceRateObserver.php +6 -6
  229. package/server/src/Observers/ZoneObserver.php +56 -0
  230. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +1 -1
  231. package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
  232. package/server/src/Support/Analytics/AbstractAnalytics.php +6 -6
  233. package/server/src/Support/Analytics/FuelEfficiency.php +2 -2
  234. package/server/src/Support/Analytics/OrdersByStatus.php +1 -1
  235. package/server/src/Support/Analytics/RevenueTrend.php +0 -1
  236. package/server/src/Support/Analytics/TopDrivers.php +10 -2
  237. package/server/src/Support/CustomerAuth.php +98 -0
  238. package/server/src/Support/GettingStarted.php +144 -0
  239. package/server/src/Support/LabelPdf.php +52 -0
  240. package/server/src/Support/Metrics/AbstractMetric.php +4 -4
  241. package/server/src/Support/PlaceSearch.php +282 -0
  242. package/server/src/Support/ResolvesOrderServiceStops.php +522 -0
  243. package/server/src/Tracking/TrackingContextBuilder.php +41 -38
  244. package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
  245. package/server/src/Tracking/TrackingStop.php +13 -11
  246. package/server/src/routes.php +76 -10
  247. package/server/tests/AnalyticsRoutesTest.php +8 -0
  248. package/server/tests/CapacityAllocationEngineTest.php +10 -10
  249. package/server/tests/CustomerContactIdentitySafetyTest.php +95 -0
  250. package/server/tests/CustomerEndpointTest.php +199 -0
  251. package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
  252. package/server/tests/GettingStartedStatusTest.php +21 -0
  253. package/server/tests/LabelPdfRenderingTest.php +23 -0
  254. package/server/tests/LiveControllerViewportTest.php +60 -0
  255. package/server/tests/MetricsRegistryTest.php +4 -8
  256. package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
  257. package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
  258. package/server/tests/OrderConfigEndpointTest.php +86 -0
  259. package/server/tests/OrderFileAttachmentTest.php +20 -0
  260. package/server/tests/ServiceAreaZoneApiShapeTest.php +78 -0
  261. package/server/tests/VehicleDriverAssignmentTest.php +18 -0
  262. package/translations/ar-ae.yml +9 -0
  263. package/translations/bg-bg.yaml +9 -0
  264. package/translations/en-us.yaml +12 -0
  265. package/translations/es-pa.yaml +9 -0
  266. package/translations/fr-fr.yaml +9 -0
  267. package/translations/mn-mn.yaml +9 -0
  268. package/translations/pt-br.yaml +9 -0
  269. package/translations/ru-ru.yaml +9 -0
  270. package/translations/vi-vn.yaml +9 -0
  271. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
@@ -1,5 +1,6 @@
1
1
  <?php
2
- use Fleetbase\Support\Utils;
2
+ use Fleetbase\Support\Utils;
3
+
3
4
  ?>
4
5
 
5
6
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -8,10 +9,16 @@ use Fleetbase\Support\Utils;
8
9
  <head>
9
10
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
10
11
  <meta name="viewport" content="width=device-width, initial-scale=1" />
11
- <title><?= $company->name ?? ($waypoint->internal_id ?? $waypoint->public_id) ?> Waypoint Label</title>
12
+ <title><?php echo e($company->name ?? ($waypoint->internal_id ?? $waypoint->public_id)); ?> Waypoint Label</title>
12
13
  </head>
13
14
 
14
15
  <style>
16
+ body {
17
+ font-family: "DejaVu Sans", "Noto Sans", "Noto Sans Arabic", "Noto Sans CJK SC", "Noto Sans CJK JP", sans-serif;
18
+ font-size: 12px;
19
+ line-height: 1.35;
20
+ }
21
+
15
22
  .group:after {
16
23
  content: "";
17
24
  display: table;
@@ -45,10 +52,10 @@ use Fleetbase\Support\Utils;
45
52
  <div style="width: 420px; border: 2px #414141 solid; margin: auto;">
46
53
  <div class="group" style="border-bottom: 1px #414141 solid; overflow: hidden;">
47
54
  <div style="float: left; width: 130px; height: 120px; border-right: 1px #414141 solid; text-align: center; padding: 10px; box-sizing: border-box;">
48
- <img src="data:image/png;base64,<?= Utils::notEmpty($trackingNumber) ? $trackingNumber->qr_code : $order->trackingNumber->qr_code ?>" style="width: 100%; height: 100%; object-fit: cover;">
55
+ <img src="data:image/png;base64,<?php echo Utils::notEmpty($trackingNumber) ? $trackingNumber->qr_code : $order->trackingNumber->qr_code; ?>" style="width: 100%; height: 100%; object-fit: cover;">
49
56
  </div>
50
57
  <div style="margin-top: 10px;">
51
- <span style="padding-left: 10px; font-size: 18px; font-weight: bold;"><?= strtoupper($company->name) ?></span>
58
+ <span style="padding-left: 10px; font-size: 18px; font-weight: bold;"><?php echo e($company->name); ?></span>
52
59
  </div>
53
60
  </div>
54
61
 
@@ -59,35 +66,35 @@ use Fleetbase\Support\Utils;
59
66
  ADDRESS:
60
67
  </div>
61
68
  <div class="places--display-address" style="float: left; display: inline-block">
62
- <div class="line"><strong><?= $dropoff->name ?></strong></div>
63
- <div class="line"><?= $dropoff->street1 ?></div>
69
+ <div class="line"><strong><?php echo e($dropoff->name); ?></strong></div>
70
+ <div class="line"><?php echo e($dropoff->street1); ?></div>
64
71
  <?php if (!empty($dropoff->street2)) { ?>
65
- <div class="line"><?= $dropoff->street2 ?></div>
72
+ <div class="line"><?php echo e($dropoff->street2); ?></div>
66
73
  <?php } ?>
67
74
  <div class="line">
68
75
  <?php if (!empty($dropoff->neighborhood)) { ?>
69
- <span class="segment"><?= $dropoff->neighborhood ?></span>
76
+ <span class="segment"><?php echo e($dropoff->neighborhood); ?></span>
70
77
  <?php } ?>
71
78
  <?php if (!empty($dropoff->district)) { ?>
72
- <span class="segment"><?= $dropoff->district ?></span>
79
+ <span class="segment"><?php echo e($dropoff->district); ?></span>
73
80
  <?php } ?>
74
81
  </div>
75
82
  <div class="line">
76
83
  <?php if (!empty($dropoff->postal_code)) { ?>
77
- <span class="segment"><?= $dropoff->postal_code ?></span>
84
+ <span class="segment"><?php echo e($dropoff->postal_code); ?></span>
78
85
  <?php } ?>
79
86
  <?php if (!empty($dropoff->city)) { ?>
80
- <span class="segment"><?= $dropoff->city ?></span>
87
+ <span class="segment"><?php echo e($dropoff->city); ?></span>
81
88
  <?php } ?>
82
89
  <?php if (!empty($dropoff->province)) { ?>
83
- <span class="segment"><?= $dropoff->province ?></span>
90
+ <span class="segment"><?php echo e($dropoff->province); ?></span>
84
91
  <?php } ?>
85
92
  <?php if (!empty($dropoff->country)) { ?>
86
- <span class="segment"><?= $dropoff->country ?></span>
93
+ <span class="segment"><?php echo e($dropoff->country); ?></span>
87
94
  <?php } ?>
88
95
  </div>
89
96
  <?php if (!empty($dropoff->phone_number)) { ?>
90
- <div class="line"><?= $dropoff->phone_number ?></div>
97
+ <div class="line"><?php echo e($dropoff->phone_number); ?></div>
91
98
  <?php } ?>
92
99
  </div>
93
100
  </div>
@@ -102,11 +109,11 @@ use Fleetbase\Support\Utils;
102
109
  <strong>
103
110
  <?php
104
111
  if (data_get($entity, 'name')) {
105
- echo strtoupper(data_get($entity, 'name') . ' - ' . data_get($entity, 'internal_id', ''));
112
+ echo e(data_get($entity, 'name') . ' - ' . data_get($entity, 'internal_id', ''));
106
113
  } else {
107
- echo strtoupper('ITEM - ' . data_get($entity, 'internal_id', ''));
114
+ echo e('ITEM - ' . data_get($entity, 'internal_id', ''));
108
115
  }
109
- ?>
116
+ ?>
110
117
  </strong>
111
118
  </div>
112
119
  <?php } ?>
@@ -116,11 +123,11 @@ use Fleetbase\Support\Utils;
116
123
  <?php } ?>
117
124
  <div class="group">
118
125
  <div style="text-align: center; padding: 15px 0px; border-top: 1px #414141 solid;">
119
- <img src="data:image/png;base64,<?= isset($trackingNumber) ? $trackingNumber->barcode : $order->trackingNumber->barcode ?>" style="height: 90px; margin-top: 10px;">
120
- <div><?= strtoupper(isset($trackingNumber) ? $trackingNumber->tracking_number : $order->trackingNumber->tracking_number) ?></div>
126
+ <img src="data:image/png;base64,<?php echo isset($trackingNumber) ? $trackingNumber->barcode : $order->trackingNumber->barcode; ?>" style="height: 90px; margin-top: 10px;">
127
+ <div><?php echo e(strtoupper(isset($trackingNumber) ? $trackingNumber->tracking_number : $order->trackingNumber->tracking_number)); ?></div>
121
128
  </div>
122
129
  </div>
123
130
  </div>
124
131
  </body>
125
132
 
126
- </html>
133
+ </html>
@@ -1,23 +1,26 @@
1
1
  <x-mail-layout>
2
2
  <h2 style="font-size: 18px; font-weight: 600;">
3
- @if($currentHour < 12)
4
- Good Morning, {{ $customer->name }}!
5
- @elseif($currentHour < 18)
6
- Good Afternoon, {{ $customer->name }}!
7
- @else
8
- Good Evening, {{ $customer->name }}!
9
- @endif
3
+ Your customer portal access is ready
10
4
  </h2>
11
5
 
12
- Your login credentials:
6
+ Hi {{ $customer->name }},
13
7
  <br />
14
8
  <br />
15
- Your Email: {{ $customer->user->email }}
9
+ {{ $customer->company->name }} has created customer portal access for you. You can use the portal to view orders, invoices, support requests, and account details when available.
16
10
  <br />
17
- Your Password: {{ $plaintextPassword }}
18
- @if($customerPortalUrl)
19
11
  <br />
20
- Customer Portal URL: {{ $customerPortalUrl}}
21
- @endif
12
+ <a href="{{ $customerPortalUrl }}" style="display:inline-block; background:#2563eb; color:#ffffff; text-decoration:none; border-radius:6px; padding:12px 18px; font-size:14px; line-height:20px; font-weight:700;">Sign in to customer portal</a>
13
+ <br />
14
+ <br />
15
+ <strong>Your sign-in details</strong>
16
+ <br />
17
+ Email: {{ $customer->user->email }}
18
+ <br />
19
+ Temporary password: {{ $plaintextPassword }}
20
+ <br />
21
+ Portal URL: {{ $customerPortalUrl }}
22
+ <br />
23
+ <br />
24
+ You can change your password after signing in.
22
25
 
23
26
  </x-mail-layout>
@@ -142,6 +142,7 @@ class ConnectivitySeeder extends Seeder
142
142
  'min_threshold' => $min,
143
143
  'max_threshold' => $max,
144
144
  'threshold_inclusive' => true,
145
+ 'last_position' => $device?->last_position ?? $this->point(0, 0),
145
146
  'last_reading_at' => $this->timestamp(2),
146
147
  'last_value' => $lastValue,
147
148
  'sensorable_type' => Device::class,
@@ -85,30 +85,30 @@ class FleetSeeder extends Seeder
85
85
  protected function seedVehicles(Company $company): array
86
86
  {
87
87
  $vehicles = [
88
- 'van_central' => ['CEN-01', 'Toyota', 'HiAce', 'GBB-1001', 1.2819, 103.8507, true, 'active', 250, 8, 2, 42],
89
- 'van_east' => ['EAS-01', 'Nissan', 'NV200', 'GBB-1002', 1.3520, 103.9449, true, 'active', 160, 5, 1, 28],
88
+ 'van_central' => ['CEN-01', 'Toyota', 'HiAce', 'GBB-1001', 1.2819, 103.8507, true, 'available', 250, 8, 2, 42],
89
+ 'van_east' => ['EAS-01', 'Nissan', 'NV200', 'GBB-1002', 1.3520, 103.9449, true, 'available', 160, 5, 1, 28],
90
90
  'truck_maintenance' => ['WRK-03', 'Isuzu', 'NPR', 'GBB-1003', 1.3347, 103.7423, false, 'maintenance', 900, 24, 8, 160],
91
91
  'bike_unassigned' => ['RCH-04', 'Honda', 'PCX', 'GBB-1004', 1.3039, 103.8520, false, 'inactive', 25, 1, 0, 6],
92
- 'van_airport' => ['CGO-05', 'Mercedes-Benz', 'Vito', 'GBB-1005', 1.3644, 103.9915, true, 'active', 220, 7, 2, 36],
93
- 'van_cold_chain' => ['CLD-06', 'Hyundai', 'H350', 'GBB-1006', 1.3525, 103.9447, true, 'active', 300, 10, 3, 48],
94
- 'truck_west' => ['JUR-07', 'Hino', '300 Series', 'GBB-1007', 1.3345, 103.7420, true, 'active', 1200, 30, 10, 180],
95
- 'van_linehaul' => ['LNH-08', 'Ford', 'Transit', 'GBB-1008', 1.3215, 103.7055, false, 'active', 450, 14, 4, 72],
96
- 'van_returns' => ['RTN-09', 'Peugeot', 'Expert', 'GBB-1009', 1.3150, 103.7649, true, 'active', 180, 6, 1, 32],
97
- 'van_central_2' => ['CEN-02', 'Toyota', 'HiAce', 'GBB-1010', 1.2871, 103.8550, true, 'active', 250, 8, 2, 42],
98
- 'van_central_3' => ['CEN-03', 'Nissan', 'NV350', 'GBB-1011', 1.2950, 103.8468, true, 'active', 280, 9, 2, 45],
99
- 'bike_rochor_2' => ['RCH-11', 'Yamaha', 'NMAX', 'GBB-1012', 1.3042, 103.8528, true, 'active', 25, 1, 0, 6],
100
- 'bike_rochor_3' => ['RCH-12', 'Honda', 'ADV160', 'GBB-1013', 1.3017, 103.8509, true, 'active', 30, 1, 0, 7],
101
- 'truck_alexandra_1' => ['ALX-14', 'Mitsubishi Fuso', 'Canter', 'GBB-1014', 1.2868, 103.8054, true, 'active', 850, 20, 6, 120],
102
- 'van_alexandra_2' => ['ALX-15', 'Ford', 'Transit Custom', 'GBB-1015', 1.2881, 103.8079, false, 'active', 400, 12, 3, 64],
103
- 'van_changi_2' => ['CGO-16', 'Mercedes-Benz', 'Sprinter', 'GBB-1016', 1.3658, 103.9871, true, 'active', 500, 16, 4, 80],
104
- 'truck_changi_3' => ['CGO-17', 'Isuzu', 'NPR', 'GBB-1017', 1.3610, 103.9954, true, 'active', 1000, 26, 8, 150],
105
- 'van_tampines_cold_2' => ['CLD-18', 'Hyundai', 'H350', 'GBB-1018', 1.3529, 103.9460, true, 'active', 320, 10, 3, 50],
92
+ 'van_airport' => ['CGO-05', 'Mercedes-Benz', 'Vito', 'GBB-1005', 1.3644, 103.9915, true, 'available', 220, 7, 2, 36],
93
+ 'van_cold_chain' => ['CLD-06', 'Hyundai', 'H350', 'GBB-1006', 1.3525, 103.9447, true, 'available', 300, 10, 3, 48],
94
+ 'truck_west' => ['JUR-07', 'Hino', '300 Series', 'GBB-1007', 1.3345, 103.7420, true, 'available', 1200, 30, 10, 180],
95
+ 'van_linehaul' => ['LNH-08', 'Ford', 'Transit', 'GBB-1008', 1.3215, 103.7055, false, 'available', 450, 14, 4, 72],
96
+ 'van_returns' => ['RTN-09', 'Peugeot', 'Expert', 'GBB-1009', 1.3150, 103.7649, true, 'available', 180, 6, 1, 32],
97
+ 'van_central_2' => ['CEN-02', 'Toyota', 'HiAce', 'GBB-1010', 1.2871, 103.8550, true, 'available', 250, 8, 2, 42],
98
+ 'van_central_3' => ['CEN-03', 'Nissan', 'NV350', 'GBB-1011', 1.2950, 103.8468, true, 'available', 280, 9, 2, 45],
99
+ 'bike_rochor_2' => ['RCH-11', 'Yamaha', 'NMAX', 'GBB-1012', 1.3042, 103.8528, true, 'available', 25, 1, 0, 6],
100
+ 'bike_rochor_3' => ['RCH-12', 'Honda', 'ADV160', 'GBB-1013', 1.3017, 103.8509, true, 'available', 30, 1, 0, 7],
101
+ 'truck_alexandra_1' => ['ALX-14', 'Mitsubishi Fuso', 'Canter', 'GBB-1014', 1.2868, 103.8054, true, 'available', 850, 20, 6, 120],
102
+ 'van_alexandra_2' => ['ALX-15', 'Ford', 'Transit Custom', 'GBB-1015', 1.2881, 103.8079, false, 'available', 400, 12, 3, 64],
103
+ 'van_changi_2' => ['CGO-16', 'Mercedes-Benz', 'Sprinter', 'GBB-1016', 1.3658, 103.9871, true, 'available', 500, 16, 4, 80],
104
+ 'truck_changi_3' => ['CGO-17', 'Isuzu', 'NPR', 'GBB-1017', 1.3610, 103.9954, true, 'available', 1000, 26, 8, 150],
105
+ 'van_tampines_cold_2' => ['CLD-18', 'Hyundai', 'H350', 'GBB-1018', 1.3529, 103.9460, true, 'available', 320, 10, 3, 50],
106
106
  'van_tampines_cold_3' => ['CLD-19', 'Toyota', 'HiAce Refrigerated', 'GBB-1019', 1.3498, 103.9425, false, 'maintenance', 260, 8, 2, 40],
107
- 'truck_jurong_2' => ['JUR-20', 'Hino', '500 Series', 'GBB-1020', 1.3340, 103.7398, true, 'active', 1400, 34, 12, 220],
108
- 'van_jurong_3' => ['JUR-21', 'Nissan', 'Urvan', 'GBB-1021', 1.3362, 103.7451, true, 'active', 300, 9, 2, 52],
109
- 'truck_linehaul_2' => ['LNH-22', 'Volvo', 'FL', 'GBB-1022', 1.3210, 103.7032, true, 'active', 1800, 42, 14, 260],
110
- 'van_linehaul_3' => ['LNH-23', 'Ford', 'Transit', 'GBB-1023', 1.3234, 103.7088, true, 'active', 470, 14, 4, 72],
111
- 'van_returns_2' => ['RTN-24', 'Peugeot', 'Expert', 'GBB-1024', 1.3168, 103.7660, true, 'active', 180, 6, 1, 32],
107
+ 'truck_jurong_2' => ['JUR-20', 'Hino', '500 Series', 'GBB-1020', 1.3340, 103.7398, true, 'available', 1400, 34, 12, 220],
108
+ 'van_jurong_3' => ['JUR-21', 'Nissan', 'Urvan', 'GBB-1021', 1.3362, 103.7451, true, 'available', 300, 9, 2, 52],
109
+ 'truck_linehaul_2' => ['LNH-22', 'Volvo', 'FL', 'GBB-1022', 1.3210, 103.7032, true, 'available', 1800, 42, 14, 260],
110
+ 'van_linehaul_3' => ['LNH-23', 'Ford', 'Transit', 'GBB-1023', 1.3234, 103.7088, true, 'available', 470, 14, 4, 72],
111
+ 'van_returns_2' => ['RTN-24', 'Peugeot', 'Expert', 'GBB-1024', 1.3168, 103.7660, true, 'available', 180, 6, 1, 32],
112
112
  'bike_returns_3' => ['RTN-25', 'Honda', 'PCX', 'GBB-1025', 1.3136, 103.7629, false, 'inactive', 25, 1, 0, 6],
113
113
  ];
114
114
 
@@ -167,7 +167,7 @@ class FleetSeeder extends Seeder
167
167
  'city' => 'Singapore',
168
168
  'online' => $online,
169
169
  'current_status' => $status,
170
- 'status' => 'active',
170
+ 'status' => 'available',
171
171
  'skills' => $skills,
172
172
  'max_travel_time' => 3600,
173
173
  'max_distance' => 60000,
@@ -82,7 +82,6 @@ class OrdersSeeder extends Seeder
82
82
  'order_started' => ['started', 'driver_ken', 'van_east', 'customer_ben', 'airport_hub', 'tampines_store', [], 8, 1, null, true, true],
83
83
  'order_completed' => ['completed', 'driver_ava', 'van_central', 'customer_alice', 'central_depot', 'orchard_store', [], -4, 1, null, true, true],
84
84
  'order_canceled' => ['canceled', null, null, 'customer_ben', 'central_depot', 'rochor_store', [], -2, 1, null, false, false],
85
- 'order_failed' => ['failed', 'driver_ken', 'van_east', 'customer_ben', 'airport_hub', 'tampines_store', [], -1, 1, null, true, true],
86
85
  'order_vehicle_assigned' => ['created', null, 'van_central', 'customer_alice', 'west_depot', 'airport_hub', ['orchard_store', 'rochor_store'], 10, 3, null, false, false],
87
86
  ];
88
87
 
@@ -111,15 +110,15 @@ class OrdersSeeder extends Seeder
111
110
  'status' => $status,
112
111
  'notes' => 'FleetOps testing fixture order: ' . $seedId,
113
112
  'pod_method' => 'scan',
114
- 'pod_required' => in_array($status, ['completed', 'failed'], true),
115
- 'orchestrator_priority' => $status === 'failed' ? 90 : 50,
113
+ 'pod_required' => $status === 'completed',
114
+ 'orchestrator_priority' => 50,
116
115
  'required_skills' => $vehicleSeedId === 'van_east' ? ['cold_chain'] : [],
117
116
  'meta' => $this->meta($seedId),
118
117
  'created_at' => $this->timestamp($hourOffset),
119
118
  'updated_at' => $this->timestamp($hourOffset + 1),
120
119
  ]);
121
120
 
122
- $trackingNumber = $this->createTrackingNumber($company, $order, $seedId, $status);
121
+ $trackingNumber = $this->createTrackingNumber($company, $order, $seedId);
123
122
  $order->forceFill(['tracking_number_uuid' => $trackingNumber->uuid])->save();
124
123
  $this->seedEntities($company, $payload, $trackingNumber, $seedId, $entityCount, $places[$dropoffSeedId]);
125
124
  }
@@ -154,7 +153,7 @@ class OrdersSeeder extends Seeder
154
153
  return $payload;
155
154
  }
156
155
 
157
- protected function createTrackingNumber(Company $company, Order $order, string $seedId, string $status): TrackingNumber
156
+ protected function createTrackingNumber(Company $company, Order $order, string $seedId): TrackingNumber
158
157
  {
159
158
  /** @var TrackingNumber $trackingNumber */
160
159
  $trackingNumber = $this->createRecord(TrackingNumber::class, [
@@ -166,22 +165,6 @@ class OrdersSeeder extends Seeder
166
165
  'region' => 'SG',
167
166
  ]);
168
167
 
169
- $trackingStatus = $this->createRecord(TrackingStatus::class, [
170
- '_key' => $this->fixtureKey($seedId . '_tracking_status'),
171
- 'company_uuid' => $company->uuid,
172
- 'tracking_number_uuid' => $trackingNumber->uuid,
173
- 'status' => str_replace('_', ' ', $status),
174
- 'details' => 'FleetOps testing tracking status for ' . $seedId,
175
- 'code' => $status,
176
- 'complete' => $status === 'completed',
177
- 'city' => 'Singapore',
178
- 'country' => 'SG',
179
- 'location' => $this->point(1.3048, 103.8318),
180
- 'meta' => $this->meta($seedId . '_tracking_status'),
181
- ]);
182
-
183
- $trackingNumber->forceFill(['status_uuid' => $trackingStatus->uuid])->save();
184
-
185
168
  return $trackingNumber;
186
169
  }
187
170
 
@@ -191,6 +174,7 @@ class OrdersSeeder extends Seeder
191
174
  $this->createRecord(Entity::class, [
192
175
  '_key' => $this->fixtureKey($seedId . '_entity_' . $i),
193
176
  '_import_id' => $this->fixtureKey($seedId . ':entity:' . $i),
177
+ 'public_id' => Entity::generatePublicId(),
194
178
  'company_uuid' => $company->uuid,
195
179
  'payload_uuid' => $payload->uuid,
196
180
  'tracking_number_uuid' => $trackingNumber->uuid,
@@ -208,7 +192,7 @@ class OrdersSeeder extends Seeder
208
192
  'currency' => 'SGD',
209
193
  'declared_value' => 1000 + ($i * 100),
210
194
  'meta' => $this->meta($seedId . '_entity_' . $i),
211
- ]);
195
+ ], true);
212
196
  }
213
197
  }
214
198
  }
@@ -257,7 +257,7 @@ class VroomOrchestrationSeeder extends Seeder
257
257
  'model' => $vehicle['model'],
258
258
  'year' => '2024',
259
259
  'type' => 'van',
260
- 'status' => 'active',
260
+ 'status' => 'available',
261
261
  'online' => true,
262
262
  'location' => isset($vehicle['lat'], $vehicle['lng']) ? new Point($vehicle['lat'], $vehicle['lng']) : null,
263
263
  'skills' => $vehicle['skills'] ?? [],
@@ -287,22 +287,22 @@ class VroomOrchestrationSeeder extends Seeder
287
287
  $orders = [
288
288
  'endpoint_only' => [
289
289
  'payload_seed_id' => 'endpoint_only',
290
- 'name' => 'VROOM Test Order - Pickup Dropoff',
291
- 'pickup' => $places['depot'],
292
- 'dropoff' => $places['orchard'],
293
- 'waypoints' => [],
294
- 'entities' => [
290
+ 'name' => 'VROOM Test Order - Pickup Dropoff',
291
+ 'pickup' => $places['depot'],
292
+ 'dropoff' => $places['orchard'],
293
+ 'waypoints' => [],
294
+ 'entities' => [
295
295
  ['name' => 'Small carton', 'type' => 'parcel', 'weight' => 12, 'weight_unit' => 'kg', 'length' => 60, 'width' => 40, 'height' => 35, 'dimensions_unit' => 'cm'],
296
296
  ['name' => 'Fragile tote', 'type' => 'parcel', 'weight' => 8, 'weight_unit' => 'kg', 'length' => 50, 'width' => 35, 'height' => 30, 'dimensions_unit' => 'cm'],
297
297
  ],
298
298
  ],
299
299
  'waypoint_only' => [
300
300
  'payload_seed_id' => 'waypoint_only',
301
- 'name' => 'VROOM Test Order - Waypoints Only',
302
- 'pickup' => null,
303
- 'dropoff' => null,
304
- 'waypoints' => [$places['rochor'], $places['novena'], $places['serangoon']],
305
- 'entities' => [
301
+ 'name' => 'VROOM Test Order - Waypoints Only',
302
+ 'pickup' => null,
303
+ 'dropoff' => null,
304
+ 'waypoints' => [$places['rochor'], $places['novena'], $places['serangoon']],
305
+ 'entities' => [
306
306
  ['name' => 'Multi-stop parcel 1', 'type' => 'parcel', 'weight' => 25, 'weight_unit' => 'kg', 'length' => 80, 'width' => 50, 'height' => 40, 'dimensions_unit' => 'cm'],
307
307
  ['name' => 'Multi-stop parcel 2', 'type' => 'parcel', 'weight' => 18, 'weight_unit' => 'kg', 'length' => 70, 'width' => 45, 'height' => 35, 'dimensions_unit' => 'cm'],
308
308
  ['name' => 'Multi-stop parcel 3', 'type' => 'parcel', 'weight' => 15, 'weight_unit' => 'kg', 'length' => 65, 'width' => 40, 'height' => 35, 'dimensions_unit' => 'cm'],
@@ -310,22 +310,22 @@ class VroomOrchestrationSeeder extends Seeder
310
310
  ],
311
311
  'mixed' => [
312
312
  'payload_seed_id' => 'mixed',
313
- 'name' => 'VROOM Test Order - Mixed Stops',
314
- 'pickup' => $places['depot'],
315
- 'dropoff' => $places['changi'],
316
- 'waypoints' => [$places['paya_lebar'], $places['tampines']],
317
- 'entities' => [
313
+ 'name' => 'VROOM Test Order - Mixed Stops',
314
+ 'pickup' => $places['depot'],
315
+ 'dropoff' => $places['changi'],
316
+ 'waypoints' => [$places['paya_lebar'], $places['tampines']],
317
+ 'entities' => [
318
318
  ['name' => 'Heavy equipment crate', 'type' => 'freight', 'weight' => 180, 'weight_unit' => 'kg', 'length' => 120, 'width' => 80, 'height' => 90, 'dimensions_unit' => 'cm'],
319
319
  ['name' => 'Accessory box', 'type' => 'parcel', 'weight' => 22, 'weight_unit' => 'kg', 'length' => 75, 'width' => 55, 'height' => 45, 'dimensions_unit' => 'cm'],
320
320
  ],
321
321
  ],
322
322
  'single_waypoint' => [
323
323
  'payload_seed_id' => 'single_waypoint',
324
- 'name' => 'VROOM Test Order - Single Waypoint',
325
- 'pickup' => null,
326
- 'dropoff' => null,
327
- 'waypoints' => [$places['woodlands']],
328
- 'entities' => [
324
+ 'name' => 'VROOM Test Order - Single Waypoint',
325
+ 'pickup' => null,
326
+ 'dropoff' => null,
327
+ 'waypoints' => [$places['woodlands']],
328
+ 'entities' => [
329
329
  ['name' => 'Oversized sample case', 'type' => 'parcel', 'weight' => 60, 'weight_unit' => 'kg', 'length' => 100, 'width' => 60, 'height' => 60, 'dimensions_unit' => 'cm'],
330
330
  ],
331
331
  ],
@@ -0,0 +1,75 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Console\Commands;
4
+
5
+ use Fleetbase\FleetOps\Models\Contact;
6
+ use Illuminate\Console\Command;
7
+
8
+ class AuditCustomerUserConflicts extends Command
9
+ {
10
+ protected $signature = 'fleetops:audit-customer-user-conflicts {--company= : Limit audit to a company UUID} {--json : Output JSON instead of a table}';
11
+
12
+ protected $description = 'Reports customer contacts linked to users that may have been incorrectly promoted from staff/admin accounts.';
13
+
14
+ public function handle(): int
15
+ {
16
+ $query = Contact::where('type', 'customer')->whereNotNull('user_uuid')->with(['anyUser.companyUsers.roles', 'company']);
17
+
18
+ if ($company = $this->option('company')) {
19
+ $query->where('company_uuid', $company);
20
+ }
21
+
22
+ $rows = $query->get()->map(function (Contact $contact) {
23
+ $user = $contact->anyUser;
24
+ $companyUser = $user?->companyUsers?->firstWhere('company_uuid', $contact->company_uuid);
25
+ $roles = $companyUser?->roles?->pluck('name')->filter()->values()->all() ?? [];
26
+ $unexpected = array_values(array_filter($roles, fn ($role) => $role !== 'Fleet-Ops Customer'));
27
+ $reasons = [];
28
+
29
+ if (!$user) {
30
+ $reasons[] = 'missing linked user';
31
+ } elseif ($user->type !== 'customer') {
32
+ $reasons[] = 'linked user type is ' . $user->type;
33
+ }
34
+
35
+ if (!empty($unexpected)) {
36
+ $reasons[] = 'linked user has non-customer roles: ' . implode(', ', $unexpected);
37
+ }
38
+
39
+ if (empty($reasons)) {
40
+ return null;
41
+ }
42
+
43
+ return [
44
+ 'contact_id' => $contact->public_id,
45
+ 'contact_uuid' => $contact->uuid,
46
+ 'contact_name' => $contact->name,
47
+ 'contact_email' => $contact->email,
48
+ 'user_uuid' => $user?->uuid,
49
+ 'user_email' => $user?->email,
50
+ 'user_type' => $user?->type,
51
+ 'roles' => implode(', ', $roles),
52
+ 'company_uuid' => $contact->company_uuid,
53
+ 'company_name' => $contact->company?->name,
54
+ 'updated_at' => optional($contact->updated_at)->toDateTimeString(),
55
+ 'reason' => implode('; ', $reasons),
56
+ ];
57
+ })->filter()->values();
58
+
59
+ if ($this->option('json')) {
60
+ $this->line($rows->toJson(JSON_PRETTY_PRINT));
61
+
62
+ return self::SUCCESS;
63
+ }
64
+
65
+ if ($rows->isEmpty()) {
66
+ $this->info('No suspicious customer user conflicts found.');
67
+
68
+ return self::SUCCESS;
69
+ }
70
+
71
+ $this->table(array_keys($rows->first()), $rows->all());
72
+
73
+ return self::SUCCESS;
74
+ }
75
+ }
@@ -0,0 +1,13 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Exceptions;
4
+
5
+ use Fleetbase\Models\User;
6
+
7
+ class CustomerUserConflictException extends UserAlreadyExistsException
8
+ {
9
+ public function __construct(string $message = '', ?User $user = null, int $code = 0, ?\Throwable $previous = null)
10
+ {
11
+ parent::__construct($message, $user, $code, $previous);
12
+ }
13
+ }
@@ -47,6 +47,21 @@ class UserFilterExpansion implements Expansion
47
47
  };
48
48
  }
49
49
 
50
+ /**
51
+ * Filter where a user is not a customer.
52
+ *
53
+ * @return void
54
+ */
55
+ public static function isNotCustomer()
56
+ {
57
+ return function () {
58
+ /** @var \Fleetbase\Http\Filter\UserFilter|\Fleetbase\Http\Filter\Filter $this */
59
+ $this->builder->where(function ($query) {
60
+ $query->whereNull('type')->orWhere('type', '!=', 'customer');
61
+ });
62
+ };
63
+ }
64
+
50
65
  /**
51
66
  * Filter where doesnt have driver within the CURRENT organizations.
52
67
  *
@@ -62,6 +77,19 @@ class UserFilterExpansion implements Expansion
62
77
  };
63
78
  }
64
79
 
80
+ /**
81
+ * Filter where users can be driver.
82
+ *
83
+ * @return void
84
+ */
85
+ public static function canBeDriver()
86
+ {
87
+ return function () {
88
+ /** @var \Fleetbase\Http\Filter\UserFilter|\Fleetbase\Http\Filter\Filter $this */
89
+ $this->builder->whereIn('type', ['user', 'admin']);
90
+ };
91
+ }
92
+
65
93
  /**
66
94
  * @return void
67
95
  */
@@ -39,6 +39,10 @@ class ContactController extends Controller
39
39
  }
40
40
 
41
41
  try {
42
+ $contactCandidate = new Contact($input);
43
+ $contactCandidate->company_uuid = session('company');
44
+ $contactCandidate->assertCustomerIdentityIsAvailable();
45
+
42
46
  // create the contact
43
47
  $contact = Contact::updateOrCreate(
44
48
  [
@@ -106,8 +110,19 @@ class ContactController extends Controller
106
110
  }
107
111
  }
108
112
 
109
- // update the contact
110
- $contact->update($input);
113
+ try {
114
+ // update the contact
115
+ $contactCandidate = $contact->replicate();
116
+ $contactCandidate->forceFill($contact->getAttributes());
117
+ $contactCandidate->exists = $contact->exists;
118
+ $contactCandidate->forceFill($input);
119
+ $contactCandidate->assertCustomerIdentityIsAvailable();
120
+
121
+ $contact->update($input);
122
+ } catch (\Exception $e) {
123
+ return response()->apiError($e->getMessage());
124
+ }
125
+
111
126
  $contact->flushAttributesCache();
112
127
 
113
128
  // response the contact resource