@fleetbase/fleetops-engine 0.6.48 → 0.6.50
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/customer/create-order-form.hbs +2 -2
- package/addon/components/customer/form.hbs +2 -2
- package/addon/components/customer/order-form.hbs +2 -2
- package/addon/components/customer/orders.hbs +2 -2
- package/addon/components/display-place.hbs +2 -2
- package/addon/components/driver/details.hbs +5 -1
- package/addon/components/driver/form.hbs +11 -1
- package/addon/components/fleet-listing-panel.hbs +2 -2
- package/addon/components/fleet-listing-panel.js +10 -0
- package/addon/components/map/google-live-map.js +18 -75
- package/addon/components/map/leaflet-live-map.hbs +187 -37
- package/addon/components/map/leaflet-live-map.js +184 -20
- package/addon/components/modals/order-change-destination.hbs +46 -0
- package/addon/components/modals/order-change-destination.js +106 -0
- package/addon/components/modals/stop-activity.hbs +28 -0
- package/addon/components/modals/stop-activity.js +52 -0
- package/addon/components/modals/update-order-activity.hbs +45 -3
- package/addon/components/modals/update-order-activity.js +143 -5
- package/addon/components/orchestrator/phase-builder.hbs +21 -0
- package/addon/components/orchestrator/phase-builder.js +13 -0
- package/addon/components/orchestrator/resource-panel.hbs +0 -10
- package/addon/components/orchestrator/resource-panel.js +2 -4
- package/addon/components/orchestrator-workbench.js +1 -0
- package/addon/components/order/activity-list.hbs +2 -2
- package/addon/components/order/activity-timeline.hbs +31 -29
- package/addon/components/order/details/activity.hbs +3 -3
- package/addon/components/order/details/activity.js +11 -0
- package/addon/components/order/details/documents.js +2 -0
- package/addon/components/order/details/purchase-rate.hbs +2 -2
- package/addon/components/order/details/route.hbs +11 -3
- package/addon/components/order/details/route.js +91 -14
- package/addon/components/order/details/tracking.hbs +101 -72
- package/addon/components/order/details/tracking.js +149 -2
- package/addon/components/order/form/details.hbs +3 -3
- package/addon/components/order/form/documents.js +11 -4
- package/addon/components/order/form/route.hbs +108 -104
- package/addon/components/order/form/route.js +37 -1
- package/addon/components/order/form/service-rate.hbs +2 -2
- package/addon/components/order/form.hbs +2 -2
- package/addon/components/order-config-manager/activity-flow.hbs +4 -1
- package/addon/components/order-config-manager/activity-flow.js +178 -12
- package/addon/components/place/address.js +1 -1
- package/addon/components/route-list.hbs +17 -17
- package/addon/components/route-list.js +42 -10
- package/addon/components/service-rate/details.hbs +41 -0
- package/addon/components/service-rate/form.hbs +118 -0
- package/addon/components/service-rate/form.js +55 -0
- package/addon/components/vehicle/form.js +1 -1
- package/addon/components/vehicle/pill.hbs +1 -1
- package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
- package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
- package/addon/components/widget/fuel-efficiency.hbs +26 -0
- package/addon/components/widget/fuel-efficiency.js +43 -0
- package/addon/components/widget/geofence-violations.hbs +44 -0
- package/addon/components/widget/geofence-violations.js +55 -0
- package/addon/components/widget/issues-insights.hbs +43 -0
- package/addon/components/widget/issues-insights.js +62 -0
- package/addon/components/widget/kpi-active-orders.hbs +10 -0
- package/addon/components/widget/kpi-active-orders.js +5 -0
- package/addon/components/widget/kpi-aov.hbs +10 -0
- package/addon/components/widget/kpi-aov.js +5 -0
- package/addon/components/widget/kpi-distance.hbs +10 -0
- package/addon/components/widget/kpi-distance.js +5 -0
- package/addon/components/widget/kpi-drivers-online.hbs +10 -0
- package/addon/components/widget/kpi-drivers-online.js +5 -0
- package/addon/components/widget/kpi-earnings.hbs +10 -0
- package/addon/components/widget/kpi-earnings.js +5 -0
- package/addon/components/widget/kpi-open-issues.hbs +11 -0
- package/addon/components/widget/kpi-open-issues.js +5 -0
- package/addon/components/widget/kpi-tile.hbs +59 -0
- package/addon/components/widget/kpi-tile.js +168 -0
- package/addon/components/widget/live-fleet.hbs +122 -0
- package/addon/components/widget/live-fleet.js +365 -0
- package/addon/components/widget/maintenance-overview.hbs +45 -0
- package/addon/components/widget/maintenance-overview.js +40 -0
- package/addon/components/widget/on-time-delivery.hbs +27 -0
- package/addon/components/widget/on-time-delivery.js +73 -0
- package/addon/components/widget/operations-pulse.hbs +51 -0
- package/addon/components/widget/operations-pulse.js +173 -0
- package/addon/components/widget/orders-by-status.hbs +26 -0
- package/addon/components/widget/orders-by-status.js +54 -0
- package/addon/components/widget/revenue-trend.hbs +34 -0
- package/addon/components/widget/revenue-trend.js +67 -0
- package/addon/components/widget/top-drivers.hbs +44 -0
- package/addon/components/widget/top-drivers.js +54 -0
- package/addon/controllers/management/vehicles/index/new.js +1 -1
- package/addon/controllers/operations/orders/index/details.js +4 -4
- package/addon/controllers/operations/orders/index.js +14 -0
- package/addon/controllers/settings/routing.js +26 -0
- package/addon/extension.js +172 -3
- package/addon/helpers/place-address-html.js +2 -2
- package/addon/routes/operations/service-rates/index.js +1 -1
- package/addon/services/geofence.js +163 -19
- package/addon/services/leaflet-contextmenu-manager.js +27 -0
- package/addon/services/map-adapter/google.js +13 -2
- package/addon/services/vehicle-actions.js +1 -1
- package/addon/styles/fleetops-engine.css +205 -88
- package/addon/templates/maintenance/equipment/index.hbs +1 -1
- package/addon/templates/maintenance/parts/index.hbs +1 -1
- package/addon/templates/maintenance/schedules/index.hbs +1 -1
- package/addon/templates/management/vehicles/index.hbs +1 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/custom-fields.hbs +1 -1
- package/addon/templates/settings/map.hbs +1 -1
- package/addon/templates/settings/navigator-app.hbs +1 -1
- package/addon/templates/settings/notifications.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +1 -1
- package/addon/templates/settings/payments/index.hbs +1 -1
- package/addon/templates/settings/payments/onboard.hbs +1 -1
- package/addon/templates/settings/routing.hbs +22 -2
- package/addon/templates/settings/scheduling.hbs +1 -1
- package/addon/utils/fleet-ops-options.js +2 -1
- package/addon/utils/live-map-card-content.js +171 -0
- package/addon/utils/normalize-order-config-flow.js +41 -0
- package/addon/utils/place-address-html.js +3 -2
- package/addon/utils/route-visualization.js +54 -7
- package/app/components/modals/order-change-destination.js +1 -0
- package/app/components/modals/stop-activity.js +1 -0
- package/app/components/widget/fuel-efficiency.js +1 -0
- package/app/components/widget/geofence-violations.js +1 -0
- package/app/components/widget/issues-insights.js +1 -0
- package/app/components/widget/kpi-active-orders.js +1 -0
- package/app/components/widget/kpi-aov.js +1 -0
- package/app/components/widget/kpi-distance.js +1 -0
- package/app/components/widget/kpi-drivers-online.js +1 -0
- package/app/components/widget/kpi-earnings.js +1 -0
- package/app/components/widget/kpi-open-issues.js +1 -0
- package/app/components/widget/kpi-tile.js +1 -0
- package/app/components/widget/live-fleet.js +1 -0
- package/app/components/widget/maintenance-overview.js +1 -0
- package/app/components/widget/on-time-delivery.js +1 -0
- package/app/components/widget/operations-pulse.js +1 -0
- package/app/components/widget/orders-by-status.js +1 -0
- package/app/components/widget/revenue-trend.js +1 -0
- package/app/components/widget/top-drivers.js +1 -0
- package/app/utils/live-map-card-content.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +1 -0
- package/server/config/telematics.php +111 -1
- package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
- package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
- package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
- package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
- package/server/resources/views/labels/default.php +57 -49
- package/server/resources/views/labels/waypoint-label.php +27 -20
- package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
- package/server/seeders/Testing/ConnectivitySeeder.php +213 -0
- package/server/seeders/Testing/FleetSeeder.php +341 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
- package/server/seeders/Testing/NetworkSeeder.php +250 -0
- package/server/seeders/Testing/OrdersSeeder.php +198 -0
- package/server/seeders/Testing/TestingSeeder.php +40 -0
- package/server/seeders/{VroomOrchestrationSeeder.php → Testing/VroomOrchestrationSeeder.php} +29 -22
- package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
- package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
- package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +98 -70
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
- package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +19 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +441 -32
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +46 -1
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
- package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
- package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
- package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
- package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
- package/server/src/Http/Resources/v1/Customer.php +79 -0
- package/server/src/Http/Resources/v1/Driver.php +2 -0
- package/server/src/Http/Resources/v1/Fleet.php +4 -0
- package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
- package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
- package/server/src/Http/Resources/v1/Payload.php +23 -20
- package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
- package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
- package/server/src/Listeners/HandleOrderDispatched.php +2 -2
- package/server/src/Models/Contact.php +92 -15
- package/server/src/Models/Customer.php +64 -0
- package/server/src/Models/Driver.php +14 -3
- package/server/src/Models/Entity.php +2 -2
- package/server/src/Models/Order.php +29 -4
- package/server/src/Models/OrderConfig.php +14 -11
- package/server/src/Models/Payload.php +17 -1
- package/server/src/Models/ServiceRate.php +273 -1
- package/server/src/Models/ServiceRateFee.php +39 -9
- package/server/src/Models/TrackingNumber.php +8 -2
- package/server/src/Models/Vehicle.php +18 -2
- package/server/src/Models/Waypoint.php +2 -2
- package/server/src/Notifications/LateDeparture.php +48 -0
- package/server/src/Notifications/ProlongedStoppage.php +48 -0
- package/server/src/Notifications/RouteDeviation.php +48 -0
- package/server/src/Observers/ContactObserver.php +2 -0
- package/server/src/Observers/ServiceRateObserver.php +6 -6
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +29 -2
- package/server/src/Providers/FleetOpsServiceProvider.php +6 -0
- package/server/src/Providers/NotificationServiceProvider.php +3 -0
- package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
- package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
- package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
- package/server/src/Support/Analytics/IssuesInsights.php +67 -0
- package/server/src/Support/Analytics/LiveFleet.php +127 -0
- package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
- package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
- package/server/src/Support/Analytics/OperationsPulse.php +86 -0
- package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
- package/server/src/Support/Analytics/RevenueTrend.php +78 -0
- package/server/src/Support/Analytics/TopDrivers.php +92 -0
- package/server/src/Support/CustomerAuth.php +98 -0
- package/server/src/Support/LabelPdf.php +52 -0
- package/server/src/Support/Metrics/AbstractMetric.php +170 -0
- package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
- package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
- package/server/src/Support/Metrics/EarningsMetric.php +30 -0
- package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
- package/server/src/Support/Metrics/MoneyMetric.php +30 -0
- package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
- package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
- package/server/src/Support/Metrics/Registry.php +46 -0
- package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
- package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
- package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
- package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
- package/server/src/Support/Metrics.php +46 -249
- package/server/src/Support/ResolvesOrderServiceStops.php +512 -0
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
- package/server/src/Tracking/TrackingContextBuilder.php +41 -38
- package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
- package/server/src/Tracking/TrackingStop.php +13 -11
- package/server/src/routes.php +50 -0
- package/server/tests/AnalyticsRoutesTest.php +41 -0
- package/server/tests/CapacityAllocationEngineTest.php +10 -10
- package/server/tests/CustomerContactIdentitySafetyTest.php +54 -0
- package/server/tests/CustomerEndpointTest.php +199 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
- package/server/tests/LabelPdfRenderingTest.php +23 -0
- package/server/tests/LiveControllerViewportTest.php +60 -0
- package/server/tests/MetricsRegistryTest.php +84 -0
- package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
- package/server/tests/OrderConfigEndpointTest.php +86 -0
- package/server/tests/OrderFileAttachmentTest.php +20 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +34 -0
- package/server/tests/VehicleDriverAssignmentTest.php +18 -0
- package/server/tests/VroomOrchestrationEngineTest.php +91 -0
- package/translations/en-us.yaml +38 -0
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Exceptions\FleetbaseRequestValidationException;
|
|
6
|
+
use Fleetbase\FleetOps\Events\EntityActivityChanged;
|
|
7
|
+
use Fleetbase\FleetOps\Events\EntityCompleted;
|
|
6
8
|
use Fleetbase\FleetOps\Events\OrderDispatchFailed;
|
|
7
9
|
use Fleetbase\FleetOps\Events\OrderReady;
|
|
8
10
|
use Fleetbase\FleetOps\Events\OrderStarted;
|
|
11
|
+
use Fleetbase\FleetOps\Events\WaypointActivityChanged;
|
|
12
|
+
use Fleetbase\FleetOps\Events\WaypointCompleted;
|
|
9
13
|
use Fleetbase\FleetOps\Exports\OrderExport;
|
|
10
14
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
11
15
|
use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
|
|
@@ -27,6 +31,7 @@ use Fleetbase\FleetOps\Models\ServiceQuote;
|
|
|
27
31
|
use Fleetbase\FleetOps\Models\TrackingStatus;
|
|
28
32
|
use Fleetbase\FleetOps\Models\Waypoint;
|
|
29
33
|
use Fleetbase\FleetOps\Notifications\OrderPing;
|
|
34
|
+
use Fleetbase\FleetOps\Support\ResolvesOrderServiceStops;
|
|
30
35
|
use Fleetbase\FleetOps\Support\Utils;
|
|
31
36
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
32
37
|
use Fleetbase\Http\Requests\Internal\BulkActionRequest;
|
|
@@ -37,13 +42,18 @@ use Fleetbase\Support\TemplateString;
|
|
|
37
42
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
38
43
|
use Illuminate\Database\QueryException;
|
|
39
44
|
use Illuminate\Http\Request;
|
|
45
|
+
use Illuminate\Http\UploadedFile;
|
|
40
46
|
use Illuminate\Support\Facades\DB;
|
|
47
|
+
use Illuminate\Support\Facades\Storage;
|
|
41
48
|
use Illuminate\Support\Facades\Validator;
|
|
42
49
|
use Illuminate\Support\Str;
|
|
50
|
+
use Illuminate\Validation\ValidationException;
|
|
43
51
|
use Maatwebsite\Excel\Facades\Excel;
|
|
44
52
|
|
|
45
53
|
class OrderController extends FleetOpsController
|
|
46
54
|
{
|
|
55
|
+
use ResolvesOrderServiceStops;
|
|
56
|
+
|
|
47
57
|
/**
|
|
48
58
|
* The resource to query.
|
|
49
59
|
*
|
|
@@ -63,6 +73,11 @@ class OrderController extends FleetOpsController
|
|
|
63
73
|
*/
|
|
64
74
|
public function onAfterUpdate($request, $order)
|
|
65
75
|
{
|
|
76
|
+
$uploads = $request->array('order.files');
|
|
77
|
+
if ($uploads) {
|
|
78
|
+
$order->attachFiles($uploads);
|
|
79
|
+
}
|
|
80
|
+
|
|
66
81
|
$waypoints = $request->array('order.payload.waypoints');
|
|
67
82
|
if ($waypoints) {
|
|
68
83
|
$order->loadMissing('payload');
|
|
@@ -112,13 +127,19 @@ class OrderController extends FleetOpsController
|
|
|
112
127
|
$input['integrated_vendor_order'] = $integratedVendorOrder;
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
// Normalize order config + type
|
|
116
|
-
//
|
|
117
|
-
$
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
// Normalize order config + type. Invalid explicit configs must
|
|
131
|
+
// not silently fall back to the default transport config.
|
|
132
|
+
$hasExplicitOrderConfig = !empty($input['order_config_uuid']);
|
|
133
|
+
$hasExplicitOrderType = !empty($input['type']);
|
|
134
|
+
$resolvedOrderConfig = OrderConfig::resolveFromIdentifier([$input['order_config_uuid'] ?? null, $input['type'] ?? null]);
|
|
135
|
+
|
|
136
|
+
if (!$resolvedOrderConfig && $hasExplicitOrderConfig) {
|
|
137
|
+
throw new FleetbaseRequestValidationException(['order_config_uuid' => 'The selected order config is invalid.']);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!$resolvedOrderConfig && !$hasExplicitOrderType) {
|
|
141
|
+
$resolvedOrderConfig = OrderConfig::defaultOrCreate();
|
|
120
142
|
}
|
|
121
|
-
$resolvedOrderConfig ??= OrderConfig::defaultOrCreate();
|
|
122
143
|
|
|
123
144
|
if ($resolvedOrderConfig) {
|
|
124
145
|
$input['order_config_uuid'] = $resolvedOrderConfig->uuid;
|
|
@@ -159,14 +180,7 @@ class OrderController extends FleetOpsController
|
|
|
159
180
|
->insertEntities($entities);
|
|
160
181
|
|
|
161
182
|
// If order creation includes files assosciate each to this order
|
|
162
|
-
|
|
163
|
-
$ids = collect($uploads)->pluck('uuid');
|
|
164
|
-
$files = File::whereIn('uuid', $ids)->get();
|
|
165
|
-
|
|
166
|
-
foreach ($files as $file) {
|
|
167
|
-
$file->setKey($order);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
183
|
+
$order->attachFiles($uploads);
|
|
170
184
|
|
|
171
185
|
// save custom field values
|
|
172
186
|
if (is_array($customFieldValues)) {
|
|
@@ -700,11 +714,22 @@ class OrderController extends FleetOpsController
|
|
|
700
714
|
*/
|
|
701
715
|
public function updateActivity(string $id, Request $request)
|
|
702
716
|
{
|
|
703
|
-
$order = Order::findById($id, [
|
|
717
|
+
$order = Order::findById($id, [
|
|
718
|
+
'driverAssigned',
|
|
719
|
+
'payload.entities',
|
|
720
|
+
'payload.pickup',
|
|
721
|
+
'payload.dropoff',
|
|
722
|
+
'payload.return',
|
|
723
|
+
'payload.currentWaypoint',
|
|
724
|
+
'payload.waypoints',
|
|
725
|
+
'payload.waypointMarkers.place',
|
|
726
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
727
|
+
]);
|
|
704
728
|
if (!$order) {
|
|
705
729
|
return response()->error('No order found.');
|
|
706
730
|
}
|
|
707
731
|
|
|
732
|
+
$proof = $request->input('proof');
|
|
708
733
|
$activity = $request->array('activity');
|
|
709
734
|
$activity = new Activity($activity, $order->getConfigFlow());
|
|
710
735
|
|
|
@@ -725,29 +750,77 @@ class OrderController extends FleetOpsController
|
|
|
725
750
|
/**
|
|
726
751
|
* @var \Fleetbase\LaravelMysqlSpatial\Types\Point
|
|
727
752
|
*/
|
|
728
|
-
$location
|
|
729
|
-
$
|
|
730
|
-
|
|
753
|
+
$location = $order->getLastLocation();
|
|
754
|
+
$isMultipleWaypointOrder = $this->payloadHasWaypoints($order->payload);
|
|
755
|
+
|
|
756
|
+
if (!$isMultipleWaypointOrder) {
|
|
757
|
+
$order->updateActivity($activity, $proof);
|
|
731
758
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
if (!$order->payload->isMultipleDropOrder) {
|
|
759
|
+
// also update for each order entities if not multiple drop order
|
|
760
|
+
// all entities will share the same activity status as is one drop order
|
|
735
761
|
foreach ($order->payload->entities as $entity) {
|
|
736
|
-
$entity->insertActivity($activity, $location);
|
|
762
|
+
$entity->insertActivity($activity, $location, $proof);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Handle order completed
|
|
766
|
+
if (Utils::isActivity($activity) && $activity->completesOrder() && $order->driverAssigned) {
|
|
767
|
+
// unset from driver current job
|
|
768
|
+
$order->driverAssigned->unassignCurrentOrder();
|
|
769
|
+
$order->complete($this->resolveProof($proof));
|
|
737
770
|
}
|
|
771
|
+
|
|
772
|
+
return new OrderResource($order->refresh());
|
|
738
773
|
}
|
|
739
774
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
$order->
|
|
744
|
-
$order->complete();
|
|
775
|
+
if ($activity->is('dispatched')) {
|
|
776
|
+
$order->updateActivity($activity, $proof);
|
|
777
|
+
|
|
778
|
+
return new OrderResource($order->refresh());
|
|
745
779
|
}
|
|
746
780
|
|
|
747
|
-
|
|
748
|
-
|
|
781
|
+
if ($activity->is('started')) {
|
|
782
|
+
$order->updateActivity($activity, $proof);
|
|
783
|
+
|
|
784
|
+
$this->ensurePayloadCurrentServiceStop($order->payload);
|
|
785
|
+
if (!$this->payloadHasCurrentServiceStopActivity($order->payload, $activity)) {
|
|
786
|
+
$this->updateCurrentServiceStopActivity($order, $activity, $location, $proof);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return new OrderResource($order->refresh());
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (!$order->started && in_array($order->status, ['created', 'dispatched'])) {
|
|
793
|
+
return response()->error('Order must be started before waypoint activity can be updated.', 422);
|
|
794
|
+
}
|
|
749
795
|
|
|
750
|
-
|
|
796
|
+
$currentStop = $this->ensurePayloadCurrentServiceStop($order->payload);
|
|
797
|
+
$isCompletingCurrentStop = Utils::isActivity($activity) && $activity->complete();
|
|
798
|
+
|
|
799
|
+
$this->updateCurrentServiceStopActivity($order, $activity, $location, $proof);
|
|
800
|
+
|
|
801
|
+
if (Utils::isActivity($activity) && $activity->is('canceled')) {
|
|
802
|
+
if ($order->driverAssigned) {
|
|
803
|
+
$order->driverAssigned->unassignCurrentOrder();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
$order->cancel();
|
|
807
|
+
|
|
808
|
+
return new OrderResource($order->refresh());
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (Utils::isActivity($activity) && $activity->completesOrder()) {
|
|
812
|
+
$nextStop = $isCompletingCurrentStop ? $this->advanceCurrentServiceStopDestination($order, $order->payload) : null;
|
|
813
|
+
|
|
814
|
+
if (!$nextStop || !$currentStop) {
|
|
815
|
+
if ($order->driverAssigned) {
|
|
816
|
+
$order->driverAssigned->unassignCurrentOrder();
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
$order->complete($this->resolveProof($proof));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return new OrderResource($order->refresh());
|
|
751
824
|
}
|
|
752
825
|
|
|
753
826
|
/**
|
|
@@ -763,12 +836,24 @@ class OrderController extends FleetOpsController
|
|
|
763
836
|
return response()->error('No order found.');
|
|
764
837
|
}
|
|
765
838
|
|
|
766
|
-
$
|
|
839
|
+
$canUpdateWaypointActivity = $order->started || $order->started_at || !in_array($order->status, ['created', 'dispatched']);
|
|
840
|
+
$stop = null;
|
|
841
|
+
if ($request->filled('waypoint') && $canUpdateWaypointActivity) {
|
|
842
|
+
$order->loadMissing([
|
|
843
|
+
'payload.pickup',
|
|
844
|
+
'payload.dropoff',
|
|
845
|
+
'payload.return',
|
|
846
|
+
'payload.waypoints',
|
|
847
|
+
'payload.waypointMarkers.place',
|
|
848
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
849
|
+
]);
|
|
850
|
+
$stop = $this->resolveServiceStopFromKey($order->payload, $request->input('waypoint'));
|
|
851
|
+
}
|
|
767
852
|
$orderConfig = $order->ensureOrderConfig();
|
|
768
853
|
if (!$orderConfig) {
|
|
769
854
|
return response()->error('No order config found for order.');
|
|
770
855
|
}
|
|
771
|
-
$activities = $orderConfig->nextActivity(
|
|
856
|
+
$activities = $stop ? $this->nextActivitiesForServiceStop($order, $order->payload, $stop) : $orderConfig->nextActivity();
|
|
772
857
|
|
|
773
858
|
// If activity is to complete order add proof of delivery properties if required
|
|
774
859
|
// This is a temporary fix until activity is updated to handle POD on it's own
|
|
@@ -788,6 +873,330 @@ class OrderController extends FleetOpsController
|
|
|
788
873
|
return response()->json($activities);
|
|
789
874
|
}
|
|
790
875
|
|
|
876
|
+
/**
|
|
877
|
+
* Mark a waypoint as the current destination for a multi-waypoint order.
|
|
878
|
+
*
|
|
879
|
+
* @return \Fleetbase\Http\Resources\v1\Order
|
|
880
|
+
*/
|
|
881
|
+
public function setDestination(string $id, string $placeId)
|
|
882
|
+
{
|
|
883
|
+
$order = Order::findById($id, [
|
|
884
|
+
'payload.pickup',
|
|
885
|
+
'payload.dropoff',
|
|
886
|
+
'payload.return',
|
|
887
|
+
'payload.waypoints',
|
|
888
|
+
'payload.waypointMarkers.place',
|
|
889
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
890
|
+
'driverAssigned',
|
|
891
|
+
'vehicleAssigned',
|
|
892
|
+
'customer',
|
|
893
|
+
'facilitator',
|
|
894
|
+
]);
|
|
895
|
+
if (!$order) {
|
|
896
|
+
return response()->error('No order found.');
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (!$this->payloadHasWaypoints($order->payload)) {
|
|
900
|
+
return response()->error('Destination can only be changed for multi-waypoint orders.', 422);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
$stop = $this->resolveServiceStopFromKey($order->payload, $placeId);
|
|
904
|
+
if (!$stop) {
|
|
905
|
+
return response()->error('Place resource is not a valid route destination.', 422);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
$this->setPayloadCurrentServiceStop($order->payload, $stop);
|
|
909
|
+
|
|
910
|
+
return new OrderResource($order->refresh());
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Capture one or more photos for order/waypoint proof of delivery.
|
|
915
|
+
*
|
|
916
|
+
* @return ProofResource|\Illuminate\Http\Response
|
|
917
|
+
*/
|
|
918
|
+
public function capturePhoto(Request $request, string $id, ?string $subjectId = null)
|
|
919
|
+
{
|
|
920
|
+
try {
|
|
921
|
+
$request->validate([
|
|
922
|
+
'photos' => 'required|array|min:1',
|
|
923
|
+
'photos.*' => [
|
|
924
|
+
function ($attribute, $value, $fail) {
|
|
925
|
+
if ($value instanceof UploadedFile) {
|
|
926
|
+
if (
|
|
927
|
+
!$value->isValid()
|
|
928
|
+
|| !in_array($value->extension(), ['jpg', 'jpeg', 'png', 'gif', 'webp'])
|
|
929
|
+
|| $value->getSize() > 10 * 1024 * 1024
|
|
930
|
+
) {
|
|
931
|
+
$fail("{$attribute} must be a valid image file <= 10 MB.");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (is_string($value) && base64_decode($value, true) !== false) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
$fail("{$attribute} must be an image file or a valid Base64 string.");
|
|
942
|
+
},
|
|
943
|
+
],
|
|
944
|
+
'remarks' => 'sometimes|string|max:255',
|
|
945
|
+
'data' => 'sometimes|array',
|
|
946
|
+
]);
|
|
947
|
+
} catch (ValidationException $e) {
|
|
948
|
+
$errorMessage = collect($e->errors())->flatten()->first();
|
|
949
|
+
|
|
950
|
+
return response()->error($errorMessage, 422);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
$order = Order::findById($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
|
|
954
|
+
if (!$order) {
|
|
955
|
+
return response()->error('No order found.');
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
$subject = $this->resolveProofSubject($order, $subjectId);
|
|
959
|
+
if (!$subject) {
|
|
960
|
+
return response()->error('Unable to capture photo as proof.', 422);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
$disk = $request->input('disk', config('filesystems.default'));
|
|
964
|
+
$bucket = $request->input("filesystems.disks.{$disk}.bucket", config('filesystems.disks.s3.bucket'));
|
|
965
|
+
$rawInputs = $request->file('photos', []);
|
|
966
|
+
$base64Inputs = array_filter($request->input('photos', []), function ($value) {
|
|
967
|
+
return is_string($value) && base64_decode($value, true) !== false;
|
|
968
|
+
});
|
|
969
|
+
$incoming = array_merge($rawInputs, $base64Inputs);
|
|
970
|
+
$remarks = $request->input('remarks', 'Verified by Photo');
|
|
971
|
+
$data = $request->input('data', []);
|
|
972
|
+
|
|
973
|
+
if (empty($incoming)) {
|
|
974
|
+
return response()->error('No photo data to capture.', 422);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
foreach ($incoming as $item) {
|
|
978
|
+
$proof = Proof::create([
|
|
979
|
+
'company_uuid' => session('company'),
|
|
980
|
+
'order_uuid' => $order->uuid,
|
|
981
|
+
'subject_uuid' => $subject->uuid,
|
|
982
|
+
'subject_type' => Utils::getModelClassName($subject),
|
|
983
|
+
'remarks' => $remarks,
|
|
984
|
+
'raw_data' => $item instanceof UploadedFile ? null : $item,
|
|
985
|
+
'data' => $data,
|
|
986
|
+
]);
|
|
987
|
+
|
|
988
|
+
$file = $this->storeProofPhoto($proof, $item, $disk, $bucket);
|
|
989
|
+
$proof->update(['file_uuid' => $file->uuid]);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
return new ProofResource($proof);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Store a proof photo and create its file record.
|
|
997
|
+
*/
|
|
998
|
+
protected function storeProofPhoto(Proof $proof, UploadedFile|string $photo, string $disk, string $bucket): File
|
|
999
|
+
{
|
|
1000
|
+
$isFile = $photo instanceof UploadedFile;
|
|
1001
|
+
$contents = $isFile ? file_get_contents($photo->getRealPath()) : base64_decode($photo);
|
|
1002
|
+
$extension = $isFile ? $photo->getClientOriginalExtension() : 'png';
|
|
1003
|
+
$contentType = $isFile ? $photo->getClientMimeType() : 'image/png';
|
|
1004
|
+
$company = session('company');
|
|
1005
|
+
$path = "uploads/{$company}/photos/{$proof->public_id}.{$extension}";
|
|
1006
|
+
|
|
1007
|
+
Storage::disk($disk)->put($path, $contents);
|
|
1008
|
+
|
|
1009
|
+
return File::create([
|
|
1010
|
+
'company_uuid' => $company,
|
|
1011
|
+
'uploader_uuid' => session('user'),
|
|
1012
|
+
'name' => basename($path),
|
|
1013
|
+
'original_filename' => basename($path),
|
|
1014
|
+
'extension' => $extension,
|
|
1015
|
+
'content_type' => $contentType,
|
|
1016
|
+
'path' => $path,
|
|
1017
|
+
'bucket' => $bucket,
|
|
1018
|
+
'type' => 'photo',
|
|
1019
|
+
'size' => strlen($contents),
|
|
1020
|
+
])->setKey($proof);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Resolve the subject that proof belongs to.
|
|
1025
|
+
*/
|
|
1026
|
+
protected function resolveProofSubject(Order $order, ?string $subjectId = null): Order|Waypoint|Place|null
|
|
1027
|
+
{
|
|
1028
|
+
if (!$subjectId) {
|
|
1029
|
+
return $order;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
$stop = $this->resolveServiceStopFromKey($order->payload, $subjectId);
|
|
1033
|
+
if ($stop) {
|
|
1034
|
+
return ($stop['waypoint'] ?? null) instanceof Waypoint ? $stop['waypoint'] : ($stop['place'] ?? $order);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
return Waypoint::withoutGlobalScopes()
|
|
1038
|
+
->where('payload_uuid', $order->payload_uuid)
|
|
1039
|
+
->where(function ($query) use ($subjectId) {
|
|
1040
|
+
$query
|
|
1041
|
+
->where('public_id', $subjectId)
|
|
1042
|
+
->orWhere('uuid', $subjectId)
|
|
1043
|
+
->orWhereHas('place', function ($query) use ($subjectId) {
|
|
1044
|
+
$query->where('public_id', $subjectId)->orWhere('uuid', $subjectId);
|
|
1045
|
+
});
|
|
1046
|
+
})
|
|
1047
|
+
->first();
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Resolve proof to the model expected by order completion.
|
|
1052
|
+
*/
|
|
1053
|
+
protected function resolveProof($proof): ?Proof
|
|
1054
|
+
{
|
|
1055
|
+
if ($proof instanceof Proof) {
|
|
1056
|
+
return $proof;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (is_string($proof)) {
|
|
1060
|
+
return Proof::where('public_id', $proof)->orWhere('uuid', $proof)->first();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Determine whether the payload should use waypoint-scoped activity.
|
|
1068
|
+
*/
|
|
1069
|
+
protected function payloadHasWaypoints(?Payload $payload): bool
|
|
1070
|
+
{
|
|
1071
|
+
if (!$payload) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
$payload->loadMissing('waypoints');
|
|
1076
|
+
|
|
1077
|
+
return $payload->waypoints->isNotEmpty();
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Insert an activity on the payload's current waypoint and its destination entities.
|
|
1082
|
+
*/
|
|
1083
|
+
protected function updateCurrentWaypointActivity(?Payload $payload, Activity $activity, $location = null, $proof = null): ?Waypoint
|
|
1084
|
+
{
|
|
1085
|
+
if (!$payload || !Utils::isActivity($activity) || !$location) {
|
|
1086
|
+
return null;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
$payload->loadMissing(['order', 'waypointMarkers', 'entities']);
|
|
1090
|
+
$currentWaypoint = $payload->waypointMarkers->firstWhere('place_uuid', $payload->current_waypoint_uuid);
|
|
1091
|
+
if (!$currentWaypoint) {
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
$currentWaypoint->insertActivity($activity, $location, $proof);
|
|
1096
|
+
$activity->fireEvents($payload->order, $currentWaypoint);
|
|
1097
|
+
|
|
1098
|
+
$entities = $payload->entities->where('destination_uuid', $payload->current_waypoint_uuid);
|
|
1099
|
+
foreach ($entities as $entity) {
|
|
1100
|
+
$entity->insertActivity($activity, $location, $proof);
|
|
1101
|
+
if ($activity->complete()) {
|
|
1102
|
+
event(new EntityCompleted($entity, $activity));
|
|
1103
|
+
} else {
|
|
1104
|
+
event(new EntityActivityChanged($entity, $activity));
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if ($activity->complete()) {
|
|
1109
|
+
event(new WaypointCompleted($currentWaypoint, $activity));
|
|
1110
|
+
} else {
|
|
1111
|
+
event(new WaypointActivityChanged($currentWaypoint, $activity));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
return $currentWaypoint;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Reload waypoint marker status and decide whether every stop is complete.
|
|
1119
|
+
*/
|
|
1120
|
+
protected function allWaypointMarkersComplete(?Payload $payload): bool
|
|
1121
|
+
{
|
|
1122
|
+
if (!$payload) {
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
$waypointMarkers = $this->freshWaypointMarkers($payload);
|
|
1127
|
+
|
|
1128
|
+
return $waypointMarkers->isNotEmpty() && $waypointMarkers->every(fn (Waypoint $waypoint) => $this->waypointMarkerIsComplete($waypoint));
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Move the current destination to the next incomplete waypoint.
|
|
1133
|
+
*/
|
|
1134
|
+
protected function advanceCurrentWaypointDestination(?Payload $payload): ?Waypoint
|
|
1135
|
+
{
|
|
1136
|
+
if (!$payload) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
$nextWaypoint = $this->freshWaypointMarkers($payload)
|
|
1141
|
+
->sortBy('order')
|
|
1142
|
+
->first(function (Waypoint $waypoint) use ($payload) {
|
|
1143
|
+
return !$this->waypointMarkerIsComplete($waypoint) && $waypoint->place_uuid !== $payload->current_waypoint_uuid;
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
if (!$nextWaypoint) {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
$payload->setCurrentWaypoint($nextWaypoint);
|
|
1151
|
+
$payload->setRelation('currentWaypoint', $nextWaypoint->getPlace());
|
|
1152
|
+
$payload->setRelation('currentWaypointMarker', $nextWaypoint);
|
|
1153
|
+
|
|
1154
|
+
return $nextWaypoint;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Reload waypoint markers with current tracking status.
|
|
1159
|
+
*/
|
|
1160
|
+
protected function freshWaypointMarkers(Payload $payload)
|
|
1161
|
+
{
|
|
1162
|
+
$payload->unsetRelation('waypointMarkers');
|
|
1163
|
+
$payload->load(['waypointMarkers.trackingNumber.status']);
|
|
1164
|
+
|
|
1165
|
+
return $payload->waypointMarkers;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Determine whether a waypoint marker's latest status is complete.
|
|
1170
|
+
*/
|
|
1171
|
+
protected function waypointMarkerIsComplete(Waypoint $waypoint): bool
|
|
1172
|
+
{
|
|
1173
|
+
if (!$waypoint->tracking_number_uuid) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
return (bool) data_get($this->trackingNumberStatus($waypoint->tracking_number_uuid), 'complete', false);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Determine whether the payload's current waypoint already has an activity row.
|
|
1182
|
+
*/
|
|
1183
|
+
protected function payloadHasCurrentWaypointActivity(?Payload $payload, Activity $activity): bool
|
|
1184
|
+
{
|
|
1185
|
+
if (!$payload || !$payload->current_waypoint_uuid) {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
$payload->loadMissing('waypointMarkers');
|
|
1190
|
+
$currentWaypoint = $payload->waypointMarkers->firstWhere('place_uuid', $payload->current_waypoint_uuid);
|
|
1191
|
+
if (!$currentWaypoint || !$currentWaypoint->tracking_number_uuid) {
|
|
1192
|
+
return false;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return TrackingStatus::where('tracking_number_uuid', $currentWaypoint->tracking_number_uuid)
|
|
1196
|
+
->where('code', TrackingStatus::prepareCode($activity->code))
|
|
1197
|
+
->exists();
|
|
1198
|
+
}
|
|
1199
|
+
|
|
791
1200
|
/**
|
|
792
1201
|
* Finds and responds with the orders next activity update based on the orders configuration.
|
|
793
1202
|
*
|
|
@@ -188,7 +188,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
188
188
|
$service = $request->input('service', 'all'); // the specific service rate to query - defaults to `all`
|
|
189
189
|
$serviceType = $request->input('service_type'); // the specific type of service rate to query
|
|
190
190
|
$isCashOnDelivery = $request->has('cod');
|
|
191
|
-
$currency = $request->
|
|
191
|
+
$currency = $request->input('currency');
|
|
192
192
|
$totalDistance = $request->input('distance');
|
|
193
193
|
$totalTime = $request->input('time');
|
|
194
194
|
$pickup = $request->or(['payload.pickup', 'payload.pickup_uuid', 'pickup']);
|
|
@@ -251,6 +251,7 @@ class SettingController extends Controller
|
|
|
251
251
|
'route_cache_ttl_seconds' => (int) $request->input('route_cache_ttl_seconds', data_get($config, 'route_cache_ttl_seconds', 600)),
|
|
252
252
|
'stale_location_threshold_seconds' => (int) $request->input('stale_location_threshold_seconds', data_get($config, 'stale_location_threshold_seconds', 300)),
|
|
253
253
|
'default_vehicle_speed_kph' => (float) $request->input('default_vehicle_speed_kph', data_get($config, 'default_vehicle_speed_kph', 35)),
|
|
254
|
+
'alerts' => $this->normalizeTrackingAlertSettings((array) $request->input('alerts', data_get($config, 'alerts', []))),
|
|
254
255
|
]);
|
|
255
256
|
|
|
256
257
|
return response()->json([
|
|
@@ -275,7 +276,9 @@ class SettingController extends Controller
|
|
|
275
276
|
'route_cache_ttl_seconds' => data_get($config, 'route_cache_ttl_seconds', 600),
|
|
276
277
|
'stale_location_threshold_seconds' => data_get($config, 'stale_location_threshold_seconds', 300),
|
|
277
278
|
'default_vehicle_speed_kph' => data_get($config, 'default_vehicle_speed_kph', 35),
|
|
279
|
+
'alerts' => data_get($config, 'alerts', $this->trackingAlertDefaults()),
|
|
278
280
|
]);
|
|
281
|
+
$trackingSettings['alerts'] = $this->normalizeTrackingAlertSettings(data_get($trackingSettings, 'alerts', []));
|
|
279
282
|
$trackingSettings['providers'] = $this->trackingProviderOptions();
|
|
280
283
|
|
|
281
284
|
return response()->json($trackingSettings);
|
|
@@ -304,6 +307,7 @@ class SettingController extends Controller
|
|
|
304
307
|
'route_cache_ttl_seconds' => (int) $request->input('route_cache_ttl_seconds', data_get($config, 'route_cache_ttl_seconds', 600)),
|
|
305
308
|
'stale_location_threshold_seconds' => (int) $request->input('stale_location_threshold_seconds', data_get($config, 'stale_location_threshold_seconds', 300)),
|
|
306
309
|
'default_vehicle_speed_kph' => (float) $request->input('default_vehicle_speed_kph', data_get($config, 'default_vehicle_speed_kph', 35)),
|
|
310
|
+
'alerts' => $this->normalizeTrackingAlertSettings((array) $request->input('alerts', data_get($config, 'alerts', []))),
|
|
307
311
|
]);
|
|
308
312
|
|
|
309
313
|
return response()->json($this->getAdminTrackingSettings()->getData(true));
|
|
@@ -519,7 +523,48 @@ class SettingController extends Controller
|
|
|
519
523
|
$config = config('fleetops.tracking', []);
|
|
520
524
|
$systemSettings = Setting::lookup('fleet-ops.tracking-settings', []);
|
|
521
525
|
|
|
522
|
-
|
|
526
|
+
$defaults = array_merge($config, is_array($systemSettings) ? $systemSettings : []);
|
|
527
|
+
$defaults['alerts'] = $this->normalizeTrackingAlertSettings(data_get($defaults, 'alerts', []));
|
|
528
|
+
|
|
529
|
+
return $defaults;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
protected function trackingAlertDefaults(): array
|
|
533
|
+
{
|
|
534
|
+
return [
|
|
535
|
+
'late_departures' => [
|
|
536
|
+
'enabled' => true,
|
|
537
|
+
'grace_period_minutes' => 15,
|
|
538
|
+
],
|
|
539
|
+
'route_deviations' => [
|
|
540
|
+
'enabled' => true,
|
|
541
|
+
'distance_threshold_meters' => 500,
|
|
542
|
+
],
|
|
543
|
+
'prolonged_stoppages' => [
|
|
544
|
+
'enabled' => true,
|
|
545
|
+
'duration_threshold_minutes' => 30,
|
|
546
|
+
],
|
|
547
|
+
];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
protected function normalizeTrackingAlertSettings(array $alerts): array
|
|
551
|
+
{
|
|
552
|
+
$defaults = $this->trackingAlertDefaults();
|
|
553
|
+
|
|
554
|
+
return [
|
|
555
|
+
'late_departures' => [
|
|
556
|
+
'enabled' => (bool) data_get($alerts, 'late_departures.enabled', data_get($defaults, 'late_departures.enabled')),
|
|
557
|
+
'grace_period_minutes' => max(0, (int) data_get($alerts, 'late_departures.grace_period_minutes', data_get($defaults, 'late_departures.grace_period_minutes'))),
|
|
558
|
+
],
|
|
559
|
+
'route_deviations' => [
|
|
560
|
+
'enabled' => (bool) data_get($alerts, 'route_deviations.enabled', data_get($defaults, 'route_deviations.enabled')),
|
|
561
|
+
'distance_threshold_meters' => max(0, (int) data_get($alerts, 'route_deviations.distance_threshold_meters', data_get($defaults, 'route_deviations.distance_threshold_meters'))),
|
|
562
|
+
],
|
|
563
|
+
'prolonged_stoppages' => [
|
|
564
|
+
'enabled' => (bool) data_get($alerts, 'prolonged_stoppages.enabled', data_get($defaults, 'prolonged_stoppages.enabled')),
|
|
565
|
+
'duration_threshold_minutes' => max(0, (int) data_get($alerts, 'prolonged_stoppages.duration_threshold_minutes', data_get($defaults, 'prolonged_stoppages.duration_threshold_minutes'))),
|
|
566
|
+
],
|
|
567
|
+
];
|
|
523
568
|
}
|
|
524
569
|
|
|
525
570
|
protected function trackingProviderOptions(): array
|