@fleetbase/fleetops-engine 0.6.44 → 0.6.46
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/entity/form.hbs +24 -0
- package/addon/components/entity/form.js +30 -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/leaflet-draw-control.js +1 -1
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/geofence-event-listing.js +5 -2
- 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/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 +78 -18
- package/addon/components/order/form/service-rate.hbs +1 -1
- 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/service-rate/details.hbs +6 -6
- package/addon/components/service-rate/form.hbs +28 -15
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details/virtual.js +5 -0
- package/addon/controllers/operations/orders/index/details.js +113 -15
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/operations/service-rates/index/edit.js +1 -0
- package/addon/controllers/operations/service-rates/index/new.js +1 -0
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/f-to-int.js +6 -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 +9 -3
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details/virtual.js +1 -0
- package/addon/routes/operations/orders/index/details.js +47 -24
- 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/equipment-actions.js +4 -6
- 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 +21 -0
- package/addon/services/maintenance-actions.js +4 -6
- 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/part-actions.js +4 -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/services/service-rate-actions.js +8 -3
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details/virtual.hbs +7 -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 +34 -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/serialize-payload.js +1 -1
- 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/operations/orders/index/details/virtual.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/f-to-int.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 +3 -3
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +36 -8
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +11 -3
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +7 -2
- package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +6 -1
- 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 +61 -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/Requests/CreateServiceRateRequest.php +2 -2
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -2
- package/server/src/Http/Resources/v1/ServiceQuote.php +2 -2
- package/server/src/Models/Entity.php +7 -0
- package/server/src/Models/Order.php +81 -8
- 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 +125 -32
- package/server/src/Observers/PurchaseRateObserver.php +3 -5
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Rules/ComputableAlgo.php +1 -3
- package/server/src/Support/Algo.php +87 -1
- package/server/src/Support/Utils.php +69 -0
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -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) {
|
|
@@ -31,10 +31,10 @@ class CreateServiceRateRequest extends FleetbaseRequest
|
|
|
31
31
|
'service_type' => [Rule::requiredIf($this->isMethod('POST')), 'string'],
|
|
32
32
|
'service_area' => [Rule::exists('service_areas', 'public_id')->whereNull('deleted_at')],
|
|
33
33
|
'zone' => [Rule::exists('zones', 'public_id')->whereNull('deleted_at')],
|
|
34
|
-
'rate_calculation_method' => [Rule::requiredIf($this->isMethod('POST')), 'string', 'in:fixed_meter,fixed_rate,per_meter,per_drop,algo'],
|
|
34
|
+
'rate_calculation_method' => [Rule::requiredIf($this->isMethod('POST')), 'string', 'in:fixed_meter,fixed_rate,per_meter,per_drop,algo,parcel'],
|
|
35
35
|
'currency' => ['required', 'size:3'],
|
|
36
36
|
'base_fee' => ['numeric'],
|
|
37
|
-
'per_meter_unit' => ['required_if:rate_calculation_method,per_meter', 'string', 'in:km,m'],
|
|
37
|
+
'per_meter_unit' => ['required_if:rate_calculation_method,per_meter', 'string', 'in:km,m,ft,yd,mi'],
|
|
38
38
|
'per_meter_flat_rate_fee' => ['required_if:rate_calculation_method,per_meter', 'numeric'],
|
|
39
39
|
'meter_fees' => [Rule::requiredIf(function () {
|
|
40
40
|
return in_array($this->input('rate_calculation_method'), ['fixed_meter', 'fixed_rate']);
|
|
@@ -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,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Models\Contact as ContactModel;
|
|
5
6
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
7
|
use Fleetbase\Http\Resources\Comment;
|
|
7
8
|
use Fleetbase\Http\Resources\File;
|
|
@@ -45,7 +46,6 @@ class Order extends FleetbaseResource
|
|
|
45
46
|
'tracking_number_uuid' => $this->when($isInternal, $this->tracking_number_uuid),
|
|
46
47
|
'driver_assigned_uuid' => $this->when($isInternal, $this->driver_assigned_uuid),
|
|
47
48
|
'vehicle_assigned_uuid'=> $this->when($isInternal, $this->vehicle_assigned_uuid),
|
|
48
|
-
'service_quote_uuid' => $this->when($isInternal, $this->service_quote_uuid),
|
|
49
49
|
'has_driver_assigned' => $this->when($isInternal, $this->has_driver_assigned),
|
|
50
50
|
'is_scheduled' => $this->when($isInternal, $this->is_scheduled),
|
|
51
51
|
'order_config_uuid' => $this->when($isInternal, $this->order_config_uuid),
|
|
@@ -57,7 +57,7 @@ class Order extends FleetbaseResource
|
|
|
57
57
|
$orderConfigPublicId
|
|
58
58
|
),
|
|
59
59
|
'customer' => $this->whenLoaded('customer', function () {
|
|
60
|
-
return $this->setCustomerType($this->
|
|
60
|
+
return $this->setCustomerType($this->transformOrderCustomerResource($this->customer));
|
|
61
61
|
}),
|
|
62
62
|
'payload' => new Payload($this->payload),
|
|
63
63
|
'facilitator' => $this->whenLoaded('facilitator', function () {
|
|
@@ -170,6 +170,24 @@ class Order extends FleetbaseResource
|
|
|
170
170
|
return (new \Illuminate\Http\Resources\Json\JsonResource($model))->resolve();
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Transform the order customer resource without nesting the customer's user relationship.
|
|
175
|
+
*
|
|
176
|
+
* @param \Illuminate\Database\Eloquent\Model|null $model
|
|
177
|
+
*
|
|
178
|
+
* @return array|null
|
|
179
|
+
*/
|
|
180
|
+
protected function transformOrderCustomerResource($model)
|
|
181
|
+
{
|
|
182
|
+
$resolved = $this->transformMorphResource($model);
|
|
183
|
+
|
|
184
|
+
if ($model instanceof ContactModel && is_array($resolved)) {
|
|
185
|
+
unset($resolved['user']);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return $resolved;
|
|
189
|
+
}
|
|
190
|
+
|
|
173
191
|
/**
|
|
174
192
|
* Transform the resource into an webhook payload.
|
|
175
193
|
*
|
|
@@ -23,8 +23,8 @@ class ServiceQuote extends FleetbaseResource
|
|
|
23
23
|
'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
|
|
24
24
|
'service_rate_uuid' => $this->when(Http::isInternalRequest(), $this->service_rate_uuid),
|
|
25
25
|
'payload_uuid' => $this->when(Http::isInternalRequest(), $this->payload_uuid),
|
|
26
|
-
'service_rate_name' => data_get($this, 'serviceRate.
|
|
27
|
-
'service_name' => data_get($this, 'serviceRate.
|
|
26
|
+
'service_rate_name' => data_get($this, 'serviceRate.service_name'),
|
|
27
|
+
'service_name' => data_get($this, 'serviceRate.service_name'),
|
|
28
28
|
'service_rate' => $this->when(Http::isPublicRequest(), data_get($this, 'serviceRate.public_id')),
|
|
29
29
|
'facilitator' => $this->when(Http::isPublicRequest(), data_get($this, 'integratedVendor.public_id')),
|
|
30
30
|
'items' => ServiceQuoteItem::collection($this->items),
|
|
@@ -168,6 +168,13 @@ class Entity extends Model
|
|
|
168
168
|
])->render();
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
public function setTypeAttribute(?string $type = null): void
|
|
172
|
+
{
|
|
173
|
+
$type = is_string($type) ? trim($type) : $type;
|
|
174
|
+
|
|
175
|
+
$this->attributes['type'] = filled($type) ? Str::snake($type) : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
171
178
|
/**
|
|
172
179
|
* @var BelongsTo
|
|
173
180
|
*/
|
|
@@ -1055,9 +1055,7 @@ class Order extends Model
|
|
|
1055
1055
|
'meta' => $meta,
|
|
1056
1056
|
]);
|
|
1057
1057
|
|
|
1058
|
-
$this->
|
|
1059
|
-
|
|
1060
|
-
return $this->save();
|
|
1058
|
+
return $this->attachPurchaseRate($purchasedRate);
|
|
1061
1059
|
}
|
|
1062
1060
|
|
|
1063
1061
|
/**
|
|
@@ -1096,14 +1094,48 @@ class Order extends Model
|
|
|
1096
1094
|
'meta' => $meta,
|
|
1097
1095
|
]);
|
|
1098
1096
|
|
|
1099
|
-
return $this->
|
|
1100
|
-
'purchase_rate_uuid' => $purchasedRate->uuid,
|
|
1101
|
-
]);
|
|
1097
|
+
return $this->attachPurchaseRate($purchasedRate);
|
|
1102
1098
|
}
|
|
1103
1099
|
|
|
1104
1100
|
return false;
|
|
1105
1101
|
}
|
|
1106
1102
|
|
|
1103
|
+
/**
|
|
1104
|
+
* Attach a purchase rate to the order and void any superseded transaction.
|
|
1105
|
+
*/
|
|
1106
|
+
public function attachPurchaseRate(PurchaseRate $purchaseRate): bool
|
|
1107
|
+
{
|
|
1108
|
+
$oldTransaction = $this->transaction_uuid
|
|
1109
|
+
? Transaction::where('uuid', $this->transaction_uuid)->first()
|
|
1110
|
+
: null;
|
|
1111
|
+
|
|
1112
|
+
$attached = (bool) $this->updateQuietly([
|
|
1113
|
+
'purchase_rate_uuid' => $purchaseRate->uuid,
|
|
1114
|
+
'transaction_uuid' => $purchaseRate->transaction_uuid,
|
|
1115
|
+
]);
|
|
1116
|
+
|
|
1117
|
+
if ($attached) {
|
|
1118
|
+
$this->setRelation('purchaseRate', $purchaseRate);
|
|
1119
|
+
|
|
1120
|
+
if ($purchaseRate->transaction_uuid) {
|
|
1121
|
+
$this->setRelation('transaction', Transaction::where('uuid', $purchaseRate->transaction_uuid)->first());
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (
|
|
1125
|
+
$oldTransaction instanceof Transaction
|
|
1126
|
+
&& $oldTransaction->uuid !== $purchaseRate->transaction_uuid
|
|
1127
|
+
&& !$oldTransaction->trashed()
|
|
1128
|
+
) {
|
|
1129
|
+
$oldTransaction->update([
|
|
1130
|
+
'status' => Transaction::STATUS_VOIDED,
|
|
1131
|
+
'voided_at' => now(),
|
|
1132
|
+
]);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
return $attached;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1107
1139
|
/**
|
|
1108
1140
|
* Creates a transaction for the order without a service quote.
|
|
1109
1141
|
* This method is used when an order is made without selecting a specific service quote.
|
|
@@ -1122,7 +1154,7 @@ class Order extends Model
|
|
|
1122
1154
|
'gateway_transaction_id' => Transaction::generateNumber(),
|
|
1123
1155
|
'gateway' => 'internal',
|
|
1124
1156
|
'amount' => 0,
|
|
1125
|
-
'currency' =>
|
|
1157
|
+
'currency' => Utils::getCompanyTransactionCurrency($this->company ?? $this->company_uuid),
|
|
1126
1158
|
'description' => 'Dispatch order',
|
|
1127
1159
|
'type' => 'dispatch',
|
|
1128
1160
|
'status' => 'success',
|
|
@@ -1700,6 +1732,16 @@ class Order extends Model
|
|
|
1700
1732
|
|
|
1701
1733
|
if (Str::isUuid($this->order_config_uuid)) {
|
|
1702
1734
|
$orderConfig = OrderConfig::where('uuid', $this->order_config_uuid)->withTrashed()->first();
|
|
1735
|
+
if ($orderConfig instanceof OrderConfig) {
|
|
1736
|
+
$orderConfig->setOrderContext($this);
|
|
1737
|
+
|
|
1738
|
+
return $orderConfig;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
$company = $this->relationLoaded('company') ? $this->company : $this->company()->first();
|
|
1743
|
+
$orderConfig = OrderConfig::defaultOrCreate($company);
|
|
1744
|
+
if ($orderConfig instanceof OrderConfig) {
|
|
1703
1745
|
$orderConfig->setOrderContext($this);
|
|
1704
1746
|
|
|
1705
1747
|
return $orderConfig;
|
|
@@ -1708,6 +1750,37 @@ class Order extends Model
|
|
|
1708
1750
|
return null;
|
|
1709
1751
|
}
|
|
1710
1752
|
|
|
1753
|
+
/**
|
|
1754
|
+
* Ensure the order has a persisted order config and normalized type.
|
|
1755
|
+
*/
|
|
1756
|
+
public function ensureOrderConfig(): ?OrderConfig
|
|
1757
|
+
{
|
|
1758
|
+
$orderConfig = $this->config();
|
|
1759
|
+
|
|
1760
|
+
if (!$orderConfig) {
|
|
1761
|
+
return null;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
$updates = [];
|
|
1765
|
+
|
|
1766
|
+
if ($this->order_config_uuid !== $orderConfig->uuid) {
|
|
1767
|
+
$updates['order_config_uuid'] = $orderConfig->uuid;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
if (empty($this->type) || $this->type === 'default' || $this->type !== $orderConfig->key) {
|
|
1771
|
+
$updates['type'] = $orderConfig->key;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
if (!empty($updates)) {
|
|
1775
|
+
$this->fill($updates);
|
|
1776
|
+
$this->save();
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
$this->setRelation('orderConfig', $orderConfig);
|
|
1780
|
+
|
|
1781
|
+
return $orderConfig;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1711
1784
|
/**
|
|
1712
1785
|
* Retrieves the flow configuration from the order config.
|
|
1713
1786
|
*
|
|
@@ -1721,7 +1794,7 @@ class Order extends Model
|
|
|
1721
1794
|
public function getConfigFlow(): array
|
|
1722
1795
|
{
|
|
1723
1796
|
$orderConfig = $this->config();
|
|
1724
|
-
if (is_array($orderConfig->flow)) {
|
|
1797
|
+
if ($orderConfig && is_array($orderConfig->flow)) {
|
|
1725
1798
|
return $orderConfig->flow;
|
|
1726
1799
|
}
|
|
1727
1800
|
|
|
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Models;
|
|
|
5
5
|
use Fleetbase\Casts\Json;
|
|
6
6
|
use Fleetbase\FleetOps\Casts\OrderConfigEntities;
|
|
7
7
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
8
|
+
use Fleetbase\FleetOps\Support\FleetOps;
|
|
8
9
|
use Fleetbase\Models\Company;
|
|
9
10
|
use Fleetbase\Models\Model;
|
|
10
11
|
use Fleetbase\Support\Auth;
|
|
@@ -498,4 +499,18 @@ class OrderConfig extends Model
|
|
|
498
499
|
{
|
|
499
500
|
return static::where(['namespace' => 'system:order-config:transport', 'company_uuid' => $company ? $company->uuid : session('company')])->first();
|
|
500
501
|
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Get the default transport config, creating it for the company if needed.
|
|
505
|
+
*/
|
|
506
|
+
public static function defaultOrCreate(?Company $company = null): ?self
|
|
507
|
+
{
|
|
508
|
+
$company ??= Company::where('uuid', session('company'))->first();
|
|
509
|
+
|
|
510
|
+
if (!$company) {
|
|
511
|
+
return static::default($company);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return static::default($company) ?? FleetOps::createTransportConfig($company);
|
|
515
|
+
}
|
|
501
516
|
}
|
|
@@ -137,6 +137,26 @@ class Payload extends Model
|
|
|
137
137
|
return $this->hasMany(Waypoint::class)->with(['place']);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* The first waypoint marker for lightweight index fallback.
|
|
142
|
+
*/
|
|
143
|
+
public function firstWaypointMarker()
|
|
144
|
+
{
|
|
145
|
+
return $this->hasOne(Waypoint::class, 'payload_uuid', 'uuid')
|
|
146
|
+
->oldestOfMany('order')
|
|
147
|
+
->with(['place']);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The last waypoint marker for lightweight index fallback.
|
|
152
|
+
*/
|
|
153
|
+
public function lastWaypointMarker()
|
|
154
|
+
{
|
|
155
|
+
return $this->hasOne(Waypoint::class, 'payload_uuid', 'uuid')
|
|
156
|
+
->latestOfMany('order')
|
|
157
|
+
->with(['place']);
|
|
158
|
+
}
|
|
159
|
+
|
|
140
160
|
public function getTotalEntitiesAttribute()
|
|
141
161
|
{
|
|
142
162
|
return $this->entities()->count();
|
|
@@ -534,48 +554,104 @@ class Payload extends Model
|
|
|
534
554
|
return $this;
|
|
535
555
|
}
|
|
536
556
|
|
|
537
|
-
$
|
|
557
|
+
$this->loadMissing('waypointMarkers');
|
|
558
|
+
$keptWaypointIds = [];
|
|
559
|
+
$availableWaypointMarkers = $this->waypointMarkers()->get();
|
|
538
560
|
|
|
539
|
-
// collect all place ids to insert
|
|
540
561
|
foreach ($waypoints as $index => $attributes) {
|
|
562
|
+
$raw = $attributes;
|
|
563
|
+
$type = data_get($raw, 'type', 'dropoff');
|
|
564
|
+
$customerUuidIn = data_get($raw, 'customer_uuid');
|
|
565
|
+
$customerPubIdIn = data_get($raw, 'customer_id');
|
|
566
|
+
$customerType = data_get($raw, 'customer_type', 'fleetops:contact');
|
|
567
|
+
|
|
541
568
|
if (Utils::isset($attributes, 'place') && is_array(Utils::get($attributes, 'place'))) {
|
|
542
569
|
$attributes = Utils::get($attributes, 'place');
|
|
543
570
|
}
|
|
544
571
|
|
|
545
|
-
|
|
546
|
-
|
|
572
|
+
$placeUuid = null;
|
|
573
|
+
|
|
574
|
+
if (
|
|
575
|
+
is_array($attributes)
|
|
576
|
+
&& isset($attributes['place_uuid'])
|
|
577
|
+
&& Place::where('uuid', $attributes['place_uuid'])->exists()
|
|
578
|
+
) {
|
|
579
|
+
$placeUuid = $attributes['place_uuid'];
|
|
580
|
+
} elseif (
|
|
581
|
+
is_array($attributes)
|
|
582
|
+
&& isset($attributes['uuid'])
|
|
583
|
+
&& ($resolvedUuid = Place::where('uuid', $attributes['uuid'])->value('uuid'))
|
|
584
|
+
) {
|
|
585
|
+
$placeUuid = $resolvedUuid;
|
|
586
|
+
} elseif (
|
|
587
|
+
is_array($attributes)
|
|
588
|
+
&& isset($attributes['id'])
|
|
589
|
+
&& ($resolvedUuid = Place::where('public_id', $attributes['id'])->value('uuid'))
|
|
590
|
+
) {
|
|
591
|
+
$placeUuid = $resolvedUuid;
|
|
547
592
|
} else {
|
|
548
|
-
$
|
|
549
|
-
|
|
593
|
+
$place = Place::createFromMixed($attributes);
|
|
594
|
+
|
|
595
|
+
if ($place instanceof Place && isset($attributes['uuid']) && $place->uuid !== $attributes['uuid']) {
|
|
596
|
+
$place->updateMeta('search_uuid', $attributes['uuid']);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
$placeUuid = $place->uuid;
|
|
550
600
|
}
|
|
551
|
-
}
|
|
552
601
|
|
|
553
|
-
|
|
554
|
-
|
|
602
|
+
$customerUuid = null;
|
|
603
|
+
$customerTypeNamespace = null;
|
|
555
604
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
605
|
+
if ($customerType) {
|
|
606
|
+
$customerTypeNamespace = Utils::getMutationType($customerType);
|
|
607
|
+
$customerModel = app($customerTypeNamespace);
|
|
608
|
+
|
|
609
|
+
if ($customerUuidIn && $customerModel->where('uuid', $customerUuidIn)->exists()) {
|
|
610
|
+
$customerUuid = $customerUuidIn;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (!$customerUuid && $customerPubIdIn) {
|
|
614
|
+
$maybeUuid = $customerModel->where('public_id', $customerPubIdIn)->value('uuid');
|
|
615
|
+
if ($maybeUuid) {
|
|
616
|
+
$customerUuid = $maybeUuid;
|
|
617
|
+
} else {
|
|
618
|
+
$customerTypeNamespace = null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (!$customerUuid) {
|
|
623
|
+
$customerTypeNamespace = null;
|
|
624
|
+
}
|
|
560
625
|
}
|
|
561
626
|
|
|
562
|
-
|
|
563
|
-
|
|
627
|
+
$values = [
|
|
628
|
+
'payload_uuid' => $this->uuid,
|
|
629
|
+
'place_uuid' => $placeUuid,
|
|
630
|
+
'order' => $index,
|
|
631
|
+
'type' => $type,
|
|
632
|
+
'customer_uuid' => $customerUuid,
|
|
633
|
+
'customer_type' => $customerTypeNamespace,
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
$existingWaypointMarker = $availableWaypointMarkers
|
|
637
|
+
->first(fn ($waypointMarker) => $waypointMarker->place_uuid === $placeUuid && !in_array($waypointMarker->uuid, $keptWaypointIds));
|
|
638
|
+
|
|
639
|
+
if ($existingWaypointMarker) {
|
|
640
|
+
$existingWaypointMarker->update($values);
|
|
641
|
+
$keptWaypointIds[] = $existingWaypointMarker->uuid;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
564
644
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
Waypoint::updateOrCreate(
|
|
568
|
-
[
|
|
569
|
-
'payload_uuid' => $this->uuid,
|
|
570
|
-
'place_uuid' => $placeId,
|
|
571
|
-
],
|
|
572
|
-
[
|
|
573
|
-
'payload_uuid' => $this->uuid,
|
|
574
|
-
'place_uuid' => $placeId,
|
|
575
|
-
]
|
|
576
|
-
);
|
|
645
|
+
$waypointRecord = Waypoint::create($values);
|
|
646
|
+
$keptWaypointIds[] = $waypointRecord->uuid;
|
|
577
647
|
}
|
|
578
648
|
|
|
649
|
+
$this->waypointMarkers()
|
|
650
|
+
->whereNotIn('uuid', $keptWaypointIds)
|
|
651
|
+
->get()
|
|
652
|
+
->each
|
|
653
|
+
->delete();
|
|
654
|
+
|
|
579
655
|
return $this->refresh()->load(['waypoints']);
|
|
580
656
|
}
|
|
581
657
|
|
|
@@ -673,6 +749,34 @@ class Payload extends Model
|
|
|
673
749
|
return null;
|
|
674
750
|
}
|
|
675
751
|
|
|
752
|
+
/**
|
|
753
|
+
* Get the lightweight pickup fallback for index resources.
|
|
754
|
+
*/
|
|
755
|
+
public function getIndexPickupPlaceAttribute(): ?Place
|
|
756
|
+
{
|
|
757
|
+
if ($this->pickup instanceof Place) {
|
|
758
|
+
return $this->pickup;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
$this->loadMissing('firstWaypointMarker.place');
|
|
762
|
+
|
|
763
|
+
return data_get($this, 'firstWaypointMarker.place');
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Get the lightweight dropoff fallback for index resources.
|
|
768
|
+
*/
|
|
769
|
+
public function getIndexDropoffPlaceAttribute(): ?Place
|
|
770
|
+
{
|
|
771
|
+
if ($this->dropoff instanceof Place) {
|
|
772
|
+
return $this->dropoff;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
$this->loadMissing('lastWaypointMarker.place');
|
|
776
|
+
|
|
777
|
+
return data_get($this, 'lastWaypointMarker.place');
|
|
778
|
+
}
|
|
779
|
+
|
|
676
780
|
public function getPickupRegion(): string
|
|
677
781
|
{
|
|
678
782
|
$pickup = $this->getPickupOrCurrentWaypoint();
|