@fleetbase/fleetops-engine 0.6.45 → 0.6.47
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.
- package/addon/components/admin/map-settings.hbs +26 -0
- package/addon/components/admin/map-settings.js +46 -0
- package/addon/components/admin/routing-settings.hbs +11 -0
- package/addon/components/cell/order-route-type.hbs +46 -0
- package/addon/components/cell/order-route-type.js +90 -0
- package/addon/components/cell/place-address.hbs +1 -0
- package/addon/components/display-place.hbs +45 -56
- package/addon/components/display-place.js +21 -0
- package/addon/components/layout/fleet-ops-sidebar/driver-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar/fleet-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/place-listing.js +5 -5
- package/addon/components/map/drawer/position-listing.js +102 -21
- package/addon/components/map/drawer/vehicle-listing.js +5 -5
- package/addon/components/map/google-live-map.hbs +9 -0
- package/addon/components/map/google-live-map.js +445 -0
- package/addon/components/map/leaflet-live-map.hbs +186 -142
- package/addon/components/map/leaflet-live-map.js +121 -41
- package/addon/components/map/toolbar/visibility-control-panel.js +2 -2
- package/addon/components/map/toolbar/zones-panel.hbs +9 -9
- package/addon/components/map/toolbar.hbs +3 -3
- package/addon/components/map/toolbar.js +9 -1
- package/addon/components/modals/map-layer-form.hbs +132 -63
- package/addon/components/modals/orchestrator-import.hbs +4 -11
- package/addon/components/modals/orchestrator-import.js +17 -1
- package/addon/components/modals/update-order-activity.hbs +1 -1
- package/addon/components/orchestrator/order-pool.js +3 -2
- package/addon/components/orchestrator-workbench.js +29 -41
- package/addon/components/order/details/detail.js +2 -2
- package/addon/components/order/details/payload.hbs +5 -5
- package/addon/components/order/details/payload.js +18 -1
- package/addon/components/order/details/route.hbs +2 -2
- package/addon/components/order/details/route.js +76 -3
- package/addon/components/order/form/details.hbs +1 -2
- package/addon/components/order/form/details.js +6 -0
- package/addon/components/order/form/payload.hbs +33 -0
- package/addon/components/order/form/payload.js +1 -0
- package/addon/components/order/form/route.hbs +8 -8
- package/addon/components/order/form/route.js +86 -18
- package/addon/components/order/form.js +2 -2
- package/addon/components/order/route-editor.hbs +209 -147
- package/addon/components/order/route-editor.js +143 -102
- package/addon/components/place/address.hbs +1 -0
- package/addon/components/place/address.js +12 -0
- package/addon/components/route-list.hbs +43 -55
- package/addon/components/route-list.js +47 -0
- package/addon/components/route-optimization-wizard-panel.js +3 -4
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details.js +110 -37
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/payments/index.js +1 -1
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/place-address-html.js +6 -0
- package/addon/instance-initializers/register-google-routes.js +17 -0
- package/addon/instance-initializers/register-osrm.js +5 -0
- package/addon/routes/application.js +6 -0
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details.js +29 -22
- package/addon/routes/operations/orders/index/new.js +9 -0
- package/addon/routes/operations/orders/index.js +0 -1
- package/addon/routes/settings/map.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/entity-actions.js +1 -1
- package/addon/services/geofence-event-bus.js +2 -1
- package/addon/services/geofence.js +204 -168
- package/addon/services/google-routes.js +300 -0
- package/addon/services/leaflet-draw-restriction.js +2 -1
- package/addon/services/leaflet-map-manager.js +17 -0
- package/addon/services/map-adapter/google.js +2205 -0
- package/addon/services/map-adapter/leaflet.js +1042 -0
- package/addon/services/map-adapter-interface.js +551 -0
- package/addon/services/map-manager.js +768 -0
- package/addon/services/map-settings.js +75 -0
- package/addon/services/movement-tracker.js +83 -32
- package/addon/services/order-actions.js +11 -1
- package/addon/services/osrm.js +79 -6
- package/addon/services/position-playback.js +135 -62
- package/addon/services/route-engine.js +83 -0
- package/addon/services/service-areas.js +8 -1
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details.hbs +0 -1
- package/addon/templates/settings/map.hbs +53 -0
- package/addon/templates/settings/routing.hbs +15 -4
- package/addon/utils/fleet-ops-options.js +1 -0
- package/addon/utils/has-valid-coordinates.js +6 -0
- package/addon/utils/map-provider-options.js +6 -0
- package/addon/utils/order-route-editing.js +86 -0
- package/addon/utils/order-route-summary.js +20 -0
- package/addon/utils/place-address-html.js +41 -0
- package/addon/utils/route-colors.js +43 -12
- package/addon/utils/route-visualization.js +93 -0
- package/addon/utils/vendor-integration.js +2 -1
- package/app/components/admin/map-settings.js +1 -0
- package/app/components/admin/routing-settings.js +1 -0
- package/app/components/cell/order-route-type.js +1 -0
- package/app/components/cell/place-address.js +1 -0
- package/app/components/map/google-live-map.js +1 -0
- package/app/components/place/address.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/place-address-html.js +1 -0
- package/app/routes/settings/map.js +1 -0
- package/app/services/map-adapter/google.js +1 -0
- package/app/services/map-adapter/leaflet.js +1 -0
- package/app/services/map-adapter-interface.js +1 -0
- package/app/services/map-manager.js +1 -0
- package/app/services/map-settings.js +1 -0
- package/app/templates/settings/map.js +1 -0
- package/app/utils/has-valid-coordinates.js +1 -0
- package/app/utils/order-route-editing.js +2 -0
- package/app/utils/order-route-summary.js +1 -0
- package/app/utils/place-address-html.js +1 -0
- package/app/utils/route-colors.js +1 -1
- package/app/utils/route-visualization.js +1 -0
- package/assets/images/drawing-controls-spritesheet.svg +156 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/pnpm-workspace.yaml +7 -0
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +31 -3
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +2 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +7 -4
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +90 -34
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +106 -3
- package/server/src/Http/Filter/OrderFilter.php +2 -1
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -1
- package/server/src/Models/Order.php +42 -1
- package/server/src/Models/OrderConfig.php +15 -0
- package/server/src/Models/Payload.php +131 -27
- package/server/src/Models/Place.php +182 -48
- package/server/src/Models/ServiceRate.php +4 -3
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Support/Utils.php +36 -2
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -100,6 +100,11 @@ class OrderController extends Controller
|
|
|
100
100
|
$pickup = data_get($payloadInput, 'pickup');
|
|
101
101
|
$dropoff = data_get($payloadInput, 'dropoff');
|
|
102
102
|
$return = data_get($payloadInput, 'return');
|
|
103
|
+
$hasPickupField = array_key_exists('pickup', $payloadInput);
|
|
104
|
+
$hasDropoffField = array_key_exists('dropoff', $payloadInput);
|
|
105
|
+
$hasReturnField = array_key_exists('return', $payloadInput);
|
|
106
|
+
$hasWaypointsField = array_key_exists('waypoints', $payloadInput);
|
|
107
|
+
$hasRouteEndpointFields = $hasPickupField || $hasDropoffField || $hasReturnField;
|
|
103
108
|
|
|
104
109
|
if ($pickup) {
|
|
105
110
|
$payload->setPickup($pickup, [
|
|
@@ -414,14 +419,21 @@ class OrderController extends Controller
|
|
|
414
419
|
$payload->save();
|
|
415
420
|
|
|
416
421
|
// set waypoints and entities after payload is saved
|
|
417
|
-
if ($waypoints) {
|
|
422
|
+
if ($hasWaypointsField && is_array($waypoints) && count($waypoints)) {
|
|
418
423
|
$payload->setWaypoints($waypoints);
|
|
424
|
+
} elseif ($hasWaypointsField || $hasRouteEndpointFields) {
|
|
425
|
+
$payload->removeWaypoints();
|
|
419
426
|
}
|
|
420
427
|
|
|
421
428
|
if ($entities) {
|
|
422
429
|
$payload->setEntities($entities);
|
|
423
430
|
}
|
|
424
431
|
|
|
432
|
+
$firstWaypoint = $payload->getPickupOrFirstWaypoint();
|
|
433
|
+
if ($firstWaypoint instanceof Place) {
|
|
434
|
+
$payload->setCurrentWaypoint($firstWaypoint);
|
|
435
|
+
}
|
|
436
|
+
|
|
425
437
|
$input['payload_uuid'] = $payload->uuid;
|
|
426
438
|
} elseif ($request->has('payload')) {
|
|
427
439
|
$input['payload_uuid'] = Utils::getUuid('payloads', [
|
|
@@ -440,6 +452,11 @@ class OrderController extends Controller
|
|
|
440
452
|
$pickup = data_get($payloadInput, 'pickup');
|
|
441
453
|
$dropoff = data_get($payloadInput, 'dropoff');
|
|
442
454
|
$return = data_get($payloadInput, 'return');
|
|
455
|
+
$hasPickupField = $request->exists('pickup');
|
|
456
|
+
$hasDropoffField = $request->exists('dropoff');
|
|
457
|
+
$hasReturnField = $request->exists('return');
|
|
458
|
+
$hasWaypointsField = $request->exists('waypoints');
|
|
459
|
+
$hasRouteEndpointFields = $hasPickupField || $hasDropoffField || $hasReturnField;
|
|
443
460
|
|
|
444
461
|
// if no pickup and dropoff extract from waypoints
|
|
445
462
|
if (empty($pickup) && empty($dropoff) && count($waypoints)) {
|
|
@@ -462,14 +479,21 @@ class OrderController extends Controller
|
|
|
462
479
|
$payload->save();
|
|
463
480
|
|
|
464
481
|
// set waypoints and entities after payload is saved
|
|
465
|
-
if ($waypoints) {
|
|
482
|
+
if ($hasWaypointsField && is_array($waypoints) && count($waypoints)) {
|
|
466
483
|
$payload->setWaypoints($waypoints);
|
|
484
|
+
} elseif ($hasWaypointsField || $hasRouteEndpointFields) {
|
|
485
|
+
$payload->removeWaypoints();
|
|
467
486
|
}
|
|
468
487
|
|
|
469
488
|
if ($entities) {
|
|
470
489
|
$payload->setEntities($entities);
|
|
471
490
|
}
|
|
472
491
|
|
|
492
|
+
$firstWaypoint = $payload->getPickupOrFirstWaypoint();
|
|
493
|
+
if ($firstWaypoint instanceof Place) {
|
|
494
|
+
$payload->setCurrentWaypoint($firstWaypoint);
|
|
495
|
+
}
|
|
496
|
+
|
|
473
497
|
$input['payload_uuid'] = $payload->uuid;
|
|
474
498
|
}
|
|
475
499
|
|
|
@@ -1159,7 +1183,11 @@ class OrderController extends Controller
|
|
|
1159
1183
|
})->first();
|
|
1160
1184
|
}
|
|
1161
1185
|
|
|
1162
|
-
$
|
|
1186
|
+
$orderConfig = $order->ensureOrderConfig();
|
|
1187
|
+
if (!$orderConfig) {
|
|
1188
|
+
return response()->apiError('No order config found for order.');
|
|
1189
|
+
}
|
|
1190
|
+
$activities = $orderConfig->nextActivity($waypoint);
|
|
1163
1191
|
|
|
1164
1192
|
// If activity is to complete order add proof of delivery properties if required
|
|
1165
1193
|
// This is a temporary fix until activity is updated to handle POD on it's own
|
|
@@ -31,6 +31,11 @@ class PayloadController extends Controller
|
|
|
31
31
|
$pickup = data_get($input, 'pickup');
|
|
32
32
|
$dropoff = data_get($input, 'dropoff');
|
|
33
33
|
$return = data_get($input, 'return');
|
|
34
|
+
$hasPickupField = array_key_exists('pickup', $input);
|
|
35
|
+
$hasDropoffField = array_key_exists('dropoff', $input);
|
|
36
|
+
$hasReturnField = array_key_exists('return', $input);
|
|
37
|
+
$hasWaypointsField = array_key_exists('waypoints', $input);
|
|
38
|
+
$hasRouteEndpointFields = $hasPickupField || $hasDropoffField || $hasReturnField;
|
|
34
39
|
|
|
35
40
|
// make sure company is set
|
|
36
41
|
$input['company_uuid'] = session('company');
|
|
@@ -116,8 +121,10 @@ class PayloadController extends Controller
|
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
// set waypoints
|
|
119
|
-
if ($waypoints) {
|
|
124
|
+
if ($hasWaypointsField && is_array($waypoints) && count($waypoints)) {
|
|
120
125
|
$payload->setWaypoints($waypoints);
|
|
126
|
+
} elseif ($hasWaypointsField || $hasRouteEndpointFields) {
|
|
127
|
+
$payload->removeWaypoints();
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
// set entities
|
|
@@ -131,6 +138,12 @@ class PayloadController extends Controller
|
|
|
131
138
|
// save the payload
|
|
132
139
|
$payload->save();
|
|
133
140
|
|
|
141
|
+
// set the first / current waypoint from the effective route shape
|
|
142
|
+
$firstWaypoint = $payload->getPickupOrFirstWaypoint();
|
|
143
|
+
if ($firstWaypoint instanceof Place) {
|
|
144
|
+
$payload->setCurrentWaypoint($firstWaypoint);
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
// make sure entities and waypoints is loaded
|
|
135
148
|
$payload->load(['entities', 'waypoints', 'pickup', 'dropoff', 'return']);
|
|
136
149
|
|
|
@@ -65,6 +65,7 @@ class ServiceAreaController extends Controller
|
|
|
65
65
|
// create the serviceArea
|
|
66
66
|
try {
|
|
67
67
|
$serviceArea = ServiceArea::create($input);
|
|
68
|
+
$serviceArea->refresh();
|
|
68
69
|
} catch (\Throwable $e) {
|
|
69
70
|
logger()->error('Unable to create service area.', ['error' => $e->getMessage()]);
|
|
70
71
|
|
|
@@ -135,6 +136,7 @@ class ServiceAreaController extends Controller
|
|
|
135
136
|
|
|
136
137
|
// update the serviceArea
|
|
137
138
|
$serviceArea->update($input);
|
|
139
|
+
$serviceArea->refresh();
|
|
138
140
|
|
|
139
141
|
// response the serviceArea resource
|
|
140
142
|
return new ServiceAreaResource($serviceArea);
|
|
@@ -232,6 +232,7 @@ class ServiceQuoteController extends Controller
|
|
|
232
232
|
|
|
233
233
|
// should all be Place like
|
|
234
234
|
$waypoints = collect([$pickup, ...$waypoints, $dropoff])->filter();
|
|
235
|
+
$endpointCount = (int) ($pickup instanceof Place) + (int) ($dropoff instanceof Place);
|
|
235
236
|
|
|
236
237
|
// if facilitator is an integrated partner resolve service quotes from bridge
|
|
237
238
|
if ($facilitator && Utils::isIntegratedVendorId($facilitator)) {
|
|
@@ -281,7 +282,7 @@ class ServiceQuoteController extends Controller
|
|
|
281
282
|
$serviceQuotes = collect();
|
|
282
283
|
|
|
283
284
|
if ($serviceRate) {
|
|
284
|
-
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery);
|
|
285
|
+
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery, $endpointCount);
|
|
285
286
|
|
|
286
287
|
$quote = ServiceQuote::create([
|
|
287
288
|
'request_id' => $requestId,
|
|
@@ -329,7 +330,7 @@ class ServiceQuoteController extends Controller
|
|
|
329
330
|
|
|
330
331
|
// calculate quotes
|
|
331
332
|
foreach ($serviceRates as $serviceRate) {
|
|
332
|
-
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery);
|
|
333
|
+
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery, $endpointCount);
|
|
333
334
|
|
|
334
335
|
$quote = ServiceQuote::create([
|
|
335
336
|
'request_id' => $requestId,
|
|
@@ -69,6 +69,7 @@ class ZoneController extends Controller
|
|
|
69
69
|
|
|
70
70
|
// create the zone
|
|
71
71
|
$zone = Zone::create($input);
|
|
72
|
+
$zone->refresh();
|
|
72
73
|
|
|
73
74
|
// response the zone resource
|
|
74
75
|
return new ZoneResource($zone);
|
|
@@ -134,6 +135,7 @@ class ZoneController extends Controller
|
|
|
134
135
|
|
|
135
136
|
// update the zone
|
|
136
137
|
$zone->update($input);
|
|
138
|
+
$zone->refresh();
|
|
137
139
|
|
|
138
140
|
// response the zone resource
|
|
139
141
|
return new ZoneResource($zone);
|
|
@@ -128,10 +128,11 @@ class LiveController extends Controller
|
|
|
128
128
|
->applyDirectivesForPermissions('fleet-ops list order')
|
|
129
129
|
->with([
|
|
130
130
|
'payload.entities',
|
|
131
|
-
'payload.waypoints',
|
|
132
131
|
'payload.dropoff',
|
|
133
132
|
'payload.pickup',
|
|
134
133
|
'payload.return',
|
|
134
|
+
'payload.firstWaypointMarker.place',
|
|
135
|
+
'payload.lastWaypointMarker.place',
|
|
135
136
|
'trackingNumber',
|
|
136
137
|
'trackingStatuses',
|
|
137
138
|
'driverAssigned' => function ($query) {
|
|
@@ -574,12 +574,15 @@ class OrchestrationController extends Controller
|
|
|
574
574
|
|
|
575
575
|
// ── Resolve OrderConfig ───────────────────────────────────────
|
|
576
576
|
$orderConfigUuid = null;
|
|
577
|
+
$resolvedOrderConfig = null;
|
|
577
578
|
if (!empty($firstRow['type'])) {
|
|
578
|
-
$
|
|
579
|
-
if ($
|
|
580
|
-
$orderConfigUuid = $
|
|
579
|
+
$resolvedOrderConfig = OrderConfig::resolveFromIdentifier($firstRow['type']);
|
|
580
|
+
if ($resolvedOrderConfig) {
|
|
581
|
+
$orderConfigUuid = $resolvedOrderConfig->uuid;
|
|
581
582
|
}
|
|
582
583
|
}
|
|
584
|
+
$resolvedOrderConfig ??= OrderConfig::defaultOrCreate();
|
|
585
|
+
$orderConfigUuid = $resolvedOrderConfig?->uuid;
|
|
583
586
|
|
|
584
587
|
// ── Resolve Customer ─────────────────────────────────────────
|
|
585
588
|
$customerUuid = null;
|
|
@@ -660,7 +663,7 @@ class OrchestrationController extends Controller
|
|
|
660
663
|
'driver_assigned_uuid' => $driverUuid,
|
|
661
664
|
'internal_id' => $firstRow['internal_id'] ?? null,
|
|
662
665
|
'status' => $firstRow['status'] ?? 'created',
|
|
663
|
-
'type' => $
|
|
666
|
+
'type' => $resolvedOrderConfig?->key ?? 'transport',
|
|
664
667
|
'notes' => $firstRow['notes'] ?? null,
|
|
665
668
|
'scheduled_at' => !empty($firstRow['scheduled_at'])
|
|
666
669
|
? Carbon::parse($firstRow['scheduled_at'])
|
|
@@ -97,6 +97,8 @@ class OrderController extends FleetOpsController
|
|
|
97
97
|
function ($request, &$input) {
|
|
98
98
|
$serviceQuote = ServiceQuote::resolveFromRequest($request);
|
|
99
99
|
|
|
100
|
+
$this->normalizeCustomerType($input);
|
|
101
|
+
|
|
100
102
|
// if service quote is applied, resolve it
|
|
101
103
|
if ($serviceQuote instanceof ServiceQuote && $serviceQuote->fromIntegratedVendor()) {
|
|
102
104
|
// create order with integrated vendor, then resume fleetbase order creation
|
|
@@ -109,9 +111,19 @@ class OrderController extends FleetOpsController
|
|
|
109
111
|
$input['integrated_vendor_order'] = $integratedVendorOrder;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
// Normalize order config + type onto the company transport
|
|
115
|
+
// config when an explicit config was not provided.
|
|
116
|
+
$resolvedOrderConfig = null;
|
|
117
|
+
if (!empty($input['order_config_uuid']) || !empty($input['type'])) {
|
|
118
|
+
$resolvedOrderConfig = OrderConfig::resolveFromIdentifier([$input['order_config_uuid'] ?? null, $input['type'] ?? null]);
|
|
119
|
+
}
|
|
120
|
+
$resolvedOrderConfig ??= OrderConfig::defaultOrCreate();
|
|
121
|
+
|
|
122
|
+
if ($resolvedOrderConfig) {
|
|
123
|
+
$input['order_config_uuid'] = $resolvedOrderConfig->uuid;
|
|
124
|
+
$input['type'] = $resolvedOrderConfig->key;
|
|
125
|
+
} elseif (!isset($input['type'])) {
|
|
126
|
+
$input['type'] = 'transport';
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
// if no status is set its default to `created`
|
|
@@ -119,14 +131,6 @@ class OrderController extends FleetOpsController
|
|
|
119
131
|
$input['status'] = 'created';
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
// Set order config
|
|
123
|
-
if (!isset($input['order_config_uuid'])) {
|
|
124
|
-
$defaultOrderConfig = OrderConfig::default();
|
|
125
|
-
if ($defaultOrderConfig) {
|
|
126
|
-
$input['order_config_uuid'] = $defaultOrderConfig->uuid;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
134
|
// Ensure orchestrator_priority is never null — the column is NOT NULL
|
|
131
135
|
// and the DB default is bypassed when Eloquent receives an explicit null.
|
|
132
136
|
if (!isset($input['orchestrator_priority']) || !is_numeric($input['orchestrator_priority'])) {
|
|
@@ -216,6 +220,33 @@ class OrderController extends FleetOpsController
|
|
|
216
220
|
}
|
|
217
221
|
}
|
|
218
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Resolve the concrete polymorphic customer type from the submitted UUID.
|
|
225
|
+
*/
|
|
226
|
+
protected function normalizeCustomerType(array &$input): void
|
|
227
|
+
{
|
|
228
|
+
$customerUuid = data_get($input, 'customer_uuid') ?? data_get($input, 'customer.uuid');
|
|
229
|
+
if (!$customerUuid) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
$customer = Utils::getUuid(
|
|
234
|
+
['contacts', 'vendors'],
|
|
235
|
+
[
|
|
236
|
+
'uuid' => $customerUuid,
|
|
237
|
+
'company_uuid' => session('company'),
|
|
238
|
+
],
|
|
239
|
+
[
|
|
240
|
+
'with_table' => true,
|
|
241
|
+
]
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
if (is_array($customer)) {
|
|
245
|
+
$input['customer_uuid'] = Utils::get($customer, 'uuid');
|
|
246
|
+
$input['customer_type'] = Utils::getModelClassName(Utils::get($customer, 'table'));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
219
250
|
/**
|
|
220
251
|
* Route which enables editing of an order route.
|
|
221
252
|
*
|
|
@@ -225,10 +256,14 @@ class OrderController extends FleetOpsController
|
|
|
225
256
|
*/
|
|
226
257
|
public function editOrderRoute(string $id, Request $request)
|
|
227
258
|
{
|
|
228
|
-
$pickup
|
|
229
|
-
$dropoff
|
|
230
|
-
$return
|
|
231
|
-
$waypoints
|
|
259
|
+
$pickup = $request->input('pickup');
|
|
260
|
+
$dropoff = $request->input('dropoff');
|
|
261
|
+
$return = $request->input('return');
|
|
262
|
+
$waypoints = $request->array('waypoints', []);
|
|
263
|
+
$hasPickupInput = $request->exists('pickup');
|
|
264
|
+
$hasDropoffInput = $request->exists('dropoff');
|
|
265
|
+
$hasReturnInput = $request->exists('return');
|
|
266
|
+
$hasWaypointsInput = $request->exists('waypoints');
|
|
232
267
|
|
|
233
268
|
// Get the order
|
|
234
269
|
$order = Order::where('uuid', $id)->with(['payload'])->first();
|
|
@@ -236,30 +271,51 @@ class OrderController extends FleetOpsController
|
|
|
236
271
|
return response()->error('Unable to find order to update route for.');
|
|
237
272
|
}
|
|
238
273
|
|
|
239
|
-
|
|
240
|
-
if ($waypoints) {
|
|
241
|
-
$order->payload->updateWaypoints($waypoints);
|
|
242
|
-
$order->payload->removePlace(['pickup', 'dropoff', 'return'], ['save' => true]);
|
|
243
|
-
} else {
|
|
244
|
-
// Update pickup
|
|
274
|
+
if ($hasPickupInput) {
|
|
245
275
|
if ($pickup) {
|
|
246
276
|
$order->payload->setPickup($pickup, ['save' => true]);
|
|
277
|
+
} else {
|
|
278
|
+
$order->payload->removePlace('pickup', ['save' => true]);
|
|
247
279
|
}
|
|
280
|
+
}
|
|
248
281
|
|
|
249
|
-
|
|
282
|
+
if ($hasDropoffInput) {
|
|
250
283
|
if ($dropoff) {
|
|
251
284
|
$order->payload->setDropoff($dropoff, ['save' => true]);
|
|
285
|
+
} else {
|
|
286
|
+
$order->payload->removePlace('dropoff', ['save' => true]);
|
|
252
287
|
}
|
|
288
|
+
}
|
|
253
289
|
|
|
254
|
-
|
|
290
|
+
if ($hasReturnInput) {
|
|
255
291
|
if ($return) {
|
|
256
|
-
$order->payload->
|
|
292
|
+
$order->payload->setReturn($return, ['save' => true]);
|
|
293
|
+
} else {
|
|
294
|
+
$order->payload->removePlace('return', ['save' => true]);
|
|
257
295
|
}
|
|
296
|
+
}
|
|
258
297
|
|
|
259
|
-
|
|
298
|
+
if ($hasWaypointsInput) {
|
|
299
|
+
if (!empty($waypoints)) {
|
|
300
|
+
$order->payload->updateWaypoints($waypoints);
|
|
301
|
+
} else {
|
|
302
|
+
$order->payload->removeWaypoints();
|
|
303
|
+
}
|
|
304
|
+
} elseif ($hasPickupInput || $hasDropoffInput || $hasReturnInput) {
|
|
260
305
|
$order->payload->removeWaypoints();
|
|
261
306
|
}
|
|
262
307
|
|
|
308
|
+
$startingDestination = $order->payload->getPickupOrFirstWaypoint();
|
|
309
|
+
if (!$startingDestination) {
|
|
310
|
+
$startingDestination = $order->payload->getDropoffOrLastWaypoint();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if ($startingDestination instanceof Place || $startingDestination instanceof Waypoint) {
|
|
314
|
+
$order->payload->setCurrentWaypoint($startingDestination);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
$order->load(['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints']);
|
|
318
|
+
|
|
263
319
|
return ['order' => new $this->resource($order)];
|
|
264
320
|
}
|
|
265
321
|
|
|
@@ -531,14 +587,10 @@ class OrderController extends FleetOpsController
|
|
|
531
587
|
return response()->error('No order found to dispatch.');
|
|
532
588
|
}
|
|
533
589
|
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
if (!$order->
|
|
537
|
-
|
|
538
|
-
if ($defaultOrderConfig) {
|
|
539
|
-
$order->update(['order_config_uuid' => $defaultOrderConfig->uuid]);
|
|
540
|
-
$order->loadMissing('orderConfig');
|
|
541
|
-
}
|
|
590
|
+
// Ensure the order is normalized onto a real order config before
|
|
591
|
+
// dispatching activities.
|
|
592
|
+
if (!$order->ensureOrderConfig()) {
|
|
593
|
+
return response()->error('No order config found for dispatch.');
|
|
542
594
|
}
|
|
543
595
|
|
|
544
596
|
if (!$order->hasDriverAssigned && !$order->adhoc) {
|
|
@@ -711,7 +763,11 @@ class OrderController extends FleetOpsController
|
|
|
711
763
|
}
|
|
712
764
|
|
|
713
765
|
$waypoint = $request->filled('waypoint') ? Waypoint::findByPlace($request->input('waypoint'), $order) : null;
|
|
714
|
-
$
|
|
766
|
+
$orderConfig = $order->ensureOrderConfig();
|
|
767
|
+
if (!$orderConfig) {
|
|
768
|
+
return response()->error('No order config found for order.');
|
|
769
|
+
}
|
|
770
|
+
$activities = $orderConfig->nextActivity($waypoint);
|
|
715
771
|
|
|
716
772
|
// If activity is to complete order add proof of delivery properties if required
|
|
717
773
|
// This is a temporary fix until activity is updated to handle POD on it's own
|
|
@@ -227,6 +227,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
227
227
|
|
|
228
228
|
// should all be Place like
|
|
229
229
|
$waypoints = collect([$pickup, ...$waypoints, $dropoff])->filter();
|
|
230
|
+
$endpointCount = (int) ($pickup instanceof Place) + (int) ($dropoff instanceof Place);
|
|
230
231
|
|
|
231
232
|
// if facilitator is an integrated partner resolve service quotes from bridge
|
|
232
233
|
if ($facilitator && Str::startsWith($facilitator, 'integrated_vendor')) {
|
|
@@ -274,7 +275,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
274
275
|
$serviceQuotes = collect();
|
|
275
276
|
|
|
276
277
|
if ($serviceRate) {
|
|
277
|
-
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery);
|
|
278
|
+
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery, $endpointCount);
|
|
278
279
|
|
|
279
280
|
$quote = ServiceQuote::create([
|
|
280
281
|
'request_id' => $requestId,
|
|
@@ -322,7 +323,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
322
323
|
|
|
323
324
|
// calculate quotes
|
|
324
325
|
foreach ($serviceRates as $serviceRate) {
|
|
325
|
-
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery);
|
|
326
|
+
[$subTotal, $lines] = $serviceRate->quoteFromPreliminaryData($entities, $waypoints, $totalDistance, $totalTime, $isCashOnDelivery, $endpointCount);
|
|
326
327
|
|
|
327
328
|
$quote = ServiceQuote::create([
|
|
328
329
|
'request_id' => $requestId,
|
|
@@ -184,9 +184,17 @@ class SettingController extends Controller
|
|
|
184
184
|
*/
|
|
185
185
|
public function saveRoutingSettings(Request $request)
|
|
186
186
|
{
|
|
187
|
-
$
|
|
188
|
-
$
|
|
189
|
-
|
|
187
|
+
$displayEngine = $request->input('display_engine', $request->input('router', 'osrm'));
|
|
188
|
+
$optimizationEngine = $request->input('optimization_engine', $displayEngine);
|
|
189
|
+
$unit = $request->input('unit', 'km');
|
|
190
|
+
Setting::configureCompany('routing', [
|
|
191
|
+
'router' => $displayEngine,
|
|
192
|
+
'display_engine' => $displayEngine,
|
|
193
|
+
'optimization_engine' => $optimizationEngine,
|
|
194
|
+
'routing_display_engine' => $displayEngine,
|
|
195
|
+
'routing_optimization_engine' => $optimizationEngine,
|
|
196
|
+
'unit' => $unit,
|
|
197
|
+
]);
|
|
190
198
|
|
|
191
199
|
return response()->json([
|
|
192
200
|
'status' => 'ok',
|
|
@@ -203,6 +211,14 @@ class SettingController extends Controller
|
|
|
203
211
|
{
|
|
204
212
|
$routingSettings = Setting::lookupCompany('routing', ['router' => 'osrm', 'unit' => 'km']);
|
|
205
213
|
|
|
214
|
+
$displayEngine = data_get($routingSettings, 'display_engine', data_get($routingSettings, 'routing_display_engine', data_get($routingSettings, 'router', 'osrm')));
|
|
215
|
+
$optimizationEngine = data_get($routingSettings, 'optimization_engine', data_get($routingSettings, 'routing_optimization_engine', $displayEngine));
|
|
216
|
+
$routingSettings['router'] = $displayEngine;
|
|
217
|
+
$routingSettings['display_engine'] = $displayEngine;
|
|
218
|
+
$routingSettings['optimization_engine'] = $optimizationEngine;
|
|
219
|
+
$routingSettings['routing_display_engine'] = $displayEngine;
|
|
220
|
+
$routingSettings['routing_optimization_engine'] = $optimizationEngine;
|
|
221
|
+
|
|
206
222
|
// always default to km if no unit is set
|
|
207
223
|
if (!isset($routingSettings['unit'])) {
|
|
208
224
|
$routingSettings['unit'] = 'km';
|
|
@@ -211,6 +227,93 @@ class SettingController extends Controller
|
|
|
211
227
|
return response()->json($routingSettings);
|
|
212
228
|
}
|
|
213
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Retrieve and return the map provider settings for the current company.
|
|
232
|
+
*
|
|
233
|
+
* The Google Maps API key is sourced exclusively from the system-level
|
|
234
|
+
* services configuration managed by the core-api admin settings panel
|
|
235
|
+
* (services.google_maps.api_key). FleetOps never stores or manages the
|
|
236
|
+
* key independently — it simply reads it from the shared system config
|
|
237
|
+
* and passes it to the frontend so the Google Maps adapter can initialise.
|
|
238
|
+
*
|
|
239
|
+
* @return \Illuminate\Http\JsonResponse
|
|
240
|
+
*/
|
|
241
|
+
public function getMapSettings()
|
|
242
|
+
{
|
|
243
|
+
$defaults = [
|
|
244
|
+
'mapProvider' => 'leaflet',
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
$systemMapSettings = Setting::lookup('fleet-ops.map-settings', []);
|
|
248
|
+
$mapSettings = Setting::lookupFromCompany('fleet-ops.map-settings', $defaults);
|
|
249
|
+
$mapSettings['mapProvider'] = data_get($mapSettings, 'mapProvider') ?: data_get($systemMapSettings, 'mapProvider', 'leaflet');
|
|
250
|
+
|
|
251
|
+
// Source the Google Maps API key from the system-level services config
|
|
252
|
+
// that is managed by the core-api admin settings panel. This ensures a
|
|
253
|
+
// single source of truth and avoids duplicating key management.
|
|
254
|
+
$mapSettings['googleMapsApiKey'] = config('services.google_maps.api_key', env('GOOGLE_MAPS_API_KEY', ''));
|
|
255
|
+
$mapSettings['googleMapsMapId'] = data_get($systemMapSettings, 'googleMapsMapId', '');
|
|
256
|
+
|
|
257
|
+
return response()->json($mapSettings);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Persist the map provider settings for the current company.
|
|
262
|
+
*
|
|
263
|
+
* The Google Maps API key is managed entirely through the core-api admin
|
|
264
|
+
* settings panel (Settings → Services → Google Maps) and is therefore
|
|
265
|
+
* never accepted or stored by this endpoint. Only the provider selection
|
|
266
|
+
* and display preferences are persisted here.
|
|
267
|
+
*
|
|
268
|
+
* @return \Illuminate\Http\JsonResponse
|
|
269
|
+
*/
|
|
270
|
+
public function saveMapSettings(Request $request)
|
|
271
|
+
{
|
|
272
|
+
$settings = $request->input('settings', []);
|
|
273
|
+
|
|
274
|
+
// The API key is managed at the system level via core-api — strip it
|
|
275
|
+
// from the payload in case a client accidentally sends it.
|
|
276
|
+
unset($settings['googleMapsApiKey']);
|
|
277
|
+
|
|
278
|
+
// Validate provider value
|
|
279
|
+
$allowedProviders = ['leaflet', 'google'];
|
|
280
|
+
if (isset($settings['mapProvider']) && !in_array($settings['mapProvider'], $allowedProviders)) {
|
|
281
|
+
$settings['mapProvider'] = 'leaflet';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Setting::configureCompany('fleet-ops.map-settings', $settings);
|
|
285
|
+
|
|
286
|
+
return response()->json($this->getMapSettings()->getData(true));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
public function getAdminMapSettings()
|
|
290
|
+
{
|
|
291
|
+
$defaults = [
|
|
292
|
+
'mapProvider' => 'leaflet',
|
|
293
|
+
'googleMapsMapId' => '',
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
return response()->json(Setting::lookup('fleet-ops.map-settings', $defaults));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public function saveAdminMapSettings(Request $request)
|
|
300
|
+
{
|
|
301
|
+
$allowedProviders = ['leaflet', 'google'];
|
|
302
|
+
$mapProvider = $request->input('mapProvider', 'leaflet');
|
|
303
|
+
if (!in_array($mapProvider, $allowedProviders)) {
|
|
304
|
+
$mapProvider = 'leaflet';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
$settings = [
|
|
308
|
+
'mapProvider' => $mapProvider,
|
|
309
|
+
'googleMapsMapId' => (string) $request->input('googleMapsMapId', ''),
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
Setting::configure('fleet-ops.map-settings', $settings);
|
|
313
|
+
|
|
314
|
+
return response()->json($this->getAdminMapSettings()->getData(true));
|
|
315
|
+
}
|
|
316
|
+
|
|
214
317
|
/**
|
|
215
318
|
* Retrieve driver scheduling settings for the current company.
|
|
216
319
|
*
|
|
@@ -44,10 +44,11 @@ class OrderFilter extends Filter
|
|
|
44
44
|
// eager load main relationships to reduce N+1 overhead
|
|
45
45
|
$this->builder->with([
|
|
46
46
|
'payload.entities',
|
|
47
|
-
'payload.waypoints',
|
|
48
47
|
'payload.pickup',
|
|
49
48
|
'payload.dropoff',
|
|
50
49
|
'payload.return',
|
|
50
|
+
'payload.firstWaypointMarker.place',
|
|
51
|
+
'payload.lastWaypointMarker.place',
|
|
51
52
|
'trackingNumber',
|
|
52
53
|
'trackingStatuses',
|
|
53
54
|
'driverAssigned' => function ($query) {
|
|
@@ -19,6 +19,8 @@ class Payload extends FleetbaseResource
|
|
|
19
19
|
public function toArray($request): array
|
|
20
20
|
{
|
|
21
21
|
$isInternal = Http::isInternalRequest();
|
|
22
|
+
$pickup = $this->index_pickup_place;
|
|
23
|
+
$dropoff = $this->index_dropoff_place;
|
|
22
24
|
|
|
23
25
|
return [
|
|
24
26
|
'id' => $this->when($isInternal, $this->id, $this->public_id),
|
|
@@ -30,24 +32,24 @@ class Payload extends FleetbaseResource
|
|
|
30
32
|
'return_uuid' => $this->when($isInternal, $this->return_uuid),
|
|
31
33
|
|
|
32
34
|
// Minimal pickup - only what's displayed in the table
|
|
33
|
-
'pickup' => $this->
|
|
34
|
-
return new Place($
|
|
35
|
+
'pickup' => $this->when($pickup, function () use ($pickup) {
|
|
36
|
+
return new Place($pickup);
|
|
35
37
|
}),
|
|
36
38
|
|
|
37
39
|
// Minimal dropoff - only what's displayed in the table
|
|
38
|
-
'dropoff' => $this->
|
|
39
|
-
return new Place($
|
|
40
|
+
'dropoff' => $this->when($dropoff, function () use ($dropoff) {
|
|
41
|
+
return new Place($dropoff);
|
|
40
42
|
}),
|
|
41
43
|
|
|
42
44
|
// Entity count instead of full entities
|
|
43
45
|
'entities_count' => $this->whenLoaded('entities', function () {
|
|
44
46
|
return $this->entities->count();
|
|
45
|
-
}),
|
|
47
|
+
}, $this->entities()->count()),
|
|
46
48
|
|
|
47
|
-
// Waypoint count
|
|
49
|
+
// Waypoint count for lightweight route-shape summaries.
|
|
48
50
|
'waypoints_count' => $this->whenLoaded('waypoints', function () {
|
|
49
51
|
return $this->waypoints->count();
|
|
50
|
-
}),
|
|
52
|
+
}, $this->waypoints()->count()),
|
|
51
53
|
|
|
52
54
|
'type' => $this->type,
|
|
53
55
|
'created_at' => $this->created_at,
|