@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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\CustomerController;
|
|
4
|
+
use Fleetbase\FleetOps\Http\Middleware\AuthenticateCustomerToken;
|
|
5
|
+
use Fleetbase\FleetOps\Http\Requests\CreateCustomerOrderRequest;
|
|
6
|
+
use Fleetbase\FleetOps\Http\Requests\CreateCustomerRequest;
|
|
7
|
+
use Fleetbase\FleetOps\Http\Requests\VerifyCreateCustomerRequest;
|
|
8
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Customer as CustomerResource;
|
|
9
|
+
use Fleetbase\FleetOps\Models\Customer as CustomerModel;
|
|
10
|
+
use Fleetbase\FleetOps\Support\CustomerAuth;
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
|--------------------------------------------------------------------------
|
|
14
|
+
| Customer API surface — static shape checks.
|
|
15
|
+
|
|
|
16
|
+
| These mirror the lightweight static checks used elsewhere in this package
|
|
17
|
+
| (see PingDriverEndpointTest.php). They verify that the customer endpoints,
|
|
18
|
+
| controller methods, middleware, and supporting classes exist and are wired
|
|
19
|
+
| through `Api/v1/CustomerController` without the Storefront layer.
|
|
20
|
+
|
|
|
21
|
+
| End-to-end HTTP tests against a running app belong in the parent `api/`
|
|
22
|
+
| project's test harness; this file ensures the package surface is correct.
|
|
23
|
+
|--------------------------------------------------------------------------
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
test('customers route group is registered inside the consumable v1 namespace', function () {
|
|
27
|
+
$routes = file_get_contents(dirname(__DIR__) . '/src/routes.php');
|
|
28
|
+
|
|
29
|
+
expect($routes)
|
|
30
|
+
->toContain("\$router->group(['prefix' => 'customers', 'middleware' => []]")
|
|
31
|
+
->and($routes)->toContain('CustomerController@requestCreationCode')
|
|
32
|
+
->and($routes)->toContain('CustomerController@create')
|
|
33
|
+
->and($routes)->toContain('CustomerController@login')
|
|
34
|
+
->and($routes)->toContain('CustomerController@loginWithPhone')
|
|
35
|
+
->and($routes)->toContain('CustomerController@verifyCode')
|
|
36
|
+
->and($routes)->toContain('CustomerController@forgotPassword')
|
|
37
|
+
->and($routes)->toContain('CustomerController@resetPassword');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('authenticated customer routes are gated by AuthenticateCustomerToken middleware', function () {
|
|
41
|
+
$routes = file_get_contents(dirname(__DIR__) . '/src/routes.php');
|
|
42
|
+
|
|
43
|
+
expect($routes)
|
|
44
|
+
->toContain('AuthenticateCustomerToken::class')
|
|
45
|
+
->and($routes)->toContain('CustomerController@me')
|
|
46
|
+
->and($routes)->toContain('CustomerController@updateMe')
|
|
47
|
+
->and($routes)->toContain('CustomerController@logout')
|
|
48
|
+
->and($routes)->toContain('CustomerController@logoutAll')
|
|
49
|
+
->and($routes)->toContain('CustomerController@orders')
|
|
50
|
+
->and($routes)->toContain('CustomerController@createOrder')
|
|
51
|
+
->and($routes)->toContain('CustomerController@findOrder')
|
|
52
|
+
->and($routes)->toContain('CustomerController@places')
|
|
53
|
+
->and($routes)->toContain('CustomerController@registerDevice');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('customer controller exposes the documented method surface', function () {
|
|
57
|
+
$expected = [
|
|
58
|
+
'requestCreationCode', 'create',
|
|
59
|
+
'login', 'loginWithPhone', 'verifyCode',
|
|
60
|
+
'forgotPassword', 'resetPassword',
|
|
61
|
+
'me', 'updateMe', 'logout', 'logoutAll',
|
|
62
|
+
'orders', 'createOrder', 'findOrder',
|
|
63
|
+
'places', 'registerDevice',
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
foreach ($expected as $method) {
|
|
67
|
+
expect(method_exists(CustomerController::class, $method))->toBeTrue("CustomerController::{$method} missing");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('customer controller does not reference Storefront concerns', function () {
|
|
72
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/CustomerController.php');
|
|
73
|
+
|
|
74
|
+
expect($source)
|
|
75
|
+
->not->toContain('Storefront::about')
|
|
76
|
+
->not->toContain("session('storefront_key')")
|
|
77
|
+
->not->toContain("session('storefront_store')")
|
|
78
|
+
->not->toContain("session('storefront_network')")
|
|
79
|
+
->not->toContain('storefront_id')
|
|
80
|
+
->not->toContain('createStripeCustomerForContact');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('customer controller does not introduce client-portal field aliases', function () {
|
|
84
|
+
// Public Fleetbase APIs must accept canonical model field names only. No
|
|
85
|
+
// aliasing like line1/state/zip → street1/province/postal_code, no portal-
|
|
86
|
+
// specific tagging like meta.origin, no top-level item/weight/value/mode
|
|
87
|
+
// aliases on order create — clients conform to the API, not vice versa.
|
|
88
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/CustomerController.php');
|
|
89
|
+
|
|
90
|
+
expect($source)
|
|
91
|
+
->not->toContain("'line1'")
|
|
92
|
+
->not->toContain("'line2'")
|
|
93
|
+
->not->toContain("'state'")
|
|
94
|
+
->not->toContain("'zip'")
|
|
95
|
+
->not->toContain("'origin' => 'fleetops_customer_portal'")
|
|
96
|
+
->not->toContain('fleetops_customer_portal')
|
|
97
|
+
->not->toContain("\$request->input('item'")
|
|
98
|
+
->not->toContain("\$request->input('value'")
|
|
99
|
+
->not->toContain("\$request->input('delivery'")
|
|
100
|
+
->not->toContain("\$request->input('category'");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('createOrder mirrors the canonical Fleet-Ops order shape', function () {
|
|
104
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/CustomerController.php');
|
|
105
|
+
|
|
106
|
+
expect($source)
|
|
107
|
+
->toContain('OrderConfig::resolveFromIdentifier')
|
|
108
|
+
->toContain('buildPayloadFromInput')
|
|
109
|
+
->toContain('Payload')
|
|
110
|
+
->toContain('setPickup')
|
|
111
|
+
->toContain('setDropoff')
|
|
112
|
+
->toContain('setEntities')
|
|
113
|
+
->toContain("'customer_uuid'");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('verification code slugs are FleetOps-namespaced, not Storefront', function () {
|
|
117
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/CustomerController.php');
|
|
118
|
+
|
|
119
|
+
expect($source)
|
|
120
|
+
->toContain("'fleetops_create_customer'")
|
|
121
|
+
->toContain("'fleetops_customer_login'")
|
|
122
|
+
->toContain("'fleetops_customer_password_reset'")
|
|
123
|
+
->not->toContain("'storefront_create_customer'")
|
|
124
|
+
->not->toContain("'storefront_login'");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('AuthenticateCustomerToken enforces a customer-token + company cross-check', function () {
|
|
128
|
+
expect(class_exists(AuthenticateCustomerToken::class))->toBeTrue();
|
|
129
|
+
|
|
130
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Middleware/AuthenticateCustomerToken.php');
|
|
131
|
+
expect($source)
|
|
132
|
+
->toContain('Customer token is missing or invalid')
|
|
133
|
+
->toContain('Customer does not belong to this company')
|
|
134
|
+
->toContain('CustomerAuth::resolveFromHeader')
|
|
135
|
+
->toContain('CustomerAuth::setCurrent');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('CustomerAuth resolves tokens by contact UUID with company-preferred fallback', function () {
|
|
139
|
+
expect(class_exists(CustomerAuth::class))->toBeTrue()
|
|
140
|
+
->and(method_exists(CustomerAuth::class, 'resolveFromHeader'))->toBeTrue()
|
|
141
|
+
->and(method_exists(CustomerAuth::class, 'current'))->toBeTrue()
|
|
142
|
+
->and(method_exists(CustomerAuth::class, 'setCurrent'))->toBeTrue()
|
|
143
|
+
->and(CustomerAuth::HEADER)->toBe('Customer-Token');
|
|
144
|
+
|
|
145
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Support/CustomerAuth.php');
|
|
146
|
+
expect($source)
|
|
147
|
+
->toContain('PersonalAccessToken::findToken')
|
|
148
|
+
->toContain("->where('type', 'customer')")
|
|
149
|
+
->toContain("->where('company_uuid'");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('Customer model extends Contact with a type=customer global scope', function () {
|
|
153
|
+
expect(is_subclass_of(CustomerModel::class, Fleetbase\FleetOps\Models\Contact::class))->toBeTrue();
|
|
154
|
+
|
|
155
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Models/Customer.php');
|
|
156
|
+
expect($source)
|
|
157
|
+
->toContain("\$model->type = 'customer'")
|
|
158
|
+
->toContain("->where('type', 'customer')");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('Customer API resource exposes token and orders_count for the consumable shape', function () {
|
|
162
|
+
expect(class_exists(CustomerResource::class))->toBeTrue();
|
|
163
|
+
|
|
164
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Customer.php');
|
|
165
|
+
expect($source)
|
|
166
|
+
->toContain("'token'")
|
|
167
|
+
->toContain("'orders_count'")
|
|
168
|
+
->toContain("Str::replaceFirst('contact', 'customer'");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('Customer API resource includes a company sub-object resolved from the API credential', function () {
|
|
172
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Customer.php');
|
|
173
|
+
|
|
174
|
+
expect($source)
|
|
175
|
+
// The sub-object lives on `company`, keyed off the customer's company_uuid.
|
|
176
|
+
->toContain("'company'")
|
|
177
|
+
->toContain('buildCompanyPayload')
|
|
178
|
+
// Currency must be resolved through the existing canonical helper —
|
|
179
|
+
// never hardcoded, never aliased.
|
|
180
|
+
->toContain('Utils::getCompanyTransactionCurrency')
|
|
181
|
+
// Public-safe projection only: id, name, currency, country, phone.
|
|
182
|
+
->toContain("'currency'")
|
|
183
|
+
->toContain("'country'")
|
|
184
|
+
->toContain("'phone'");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('FormRequest validators are present and authorize via api credential', function () {
|
|
188
|
+
expect(class_exists(CreateCustomerRequest::class))->toBeTrue()
|
|
189
|
+
->and(class_exists(VerifyCreateCustomerRequest::class))->toBeTrue()
|
|
190
|
+
->and(class_exists(CreateCustomerOrderRequest::class))->toBeTrue();
|
|
191
|
+
|
|
192
|
+
$create = file_get_contents(dirname(__DIR__) . '/src/Http/Requests/CreateCustomerRequest.php');
|
|
193
|
+
$verify = file_get_contents(dirname(__DIR__) . '/src/Http/Requests/VerifyCreateCustomerRequest.php');
|
|
194
|
+
|
|
195
|
+
expect($create)
|
|
196
|
+
->toContain("'code' => 'required|exists:verification_codes,code'")
|
|
197
|
+
->toContain("'password' => 'required|string|min:8'")
|
|
198
|
+
->and($verify)->toContain("'mode' => 'required|in:email,sms'");
|
|
199
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
4
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
5
|
+
|
|
6
|
+
test('driver and vehicle statuses default to available', function () {
|
|
7
|
+
expect((new Driver())->status)
|
|
8
|
+
->toBe('available')
|
|
9
|
+
->and((new Vehicle())->status)
|
|
10
|
+
->toBe('available');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('legacy active and null driver and vehicle statuses normalize to available', function () {
|
|
14
|
+
$driver = new Driver();
|
|
15
|
+
$driver->status = 'active';
|
|
16
|
+
expect($driver->status)->toBe('available');
|
|
17
|
+
|
|
18
|
+
$driver->status = null;
|
|
19
|
+
expect($driver->status)->toBe('available');
|
|
20
|
+
|
|
21
|
+
$vehicle = new Vehicle();
|
|
22
|
+
$vehicle->status = 'active';
|
|
23
|
+
expect($vehicle->status)->toBe('available');
|
|
24
|
+
|
|
25
|
+
$vehicle->status = null;
|
|
26
|
+
expect($vehicle->status)->toBe('available');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('dispatch driver availability queries use available status', function () {
|
|
30
|
+
$listener = file_get_contents(dirname(__DIR__) . '/src/Listeners/HandleOrderDispatched.php');
|
|
31
|
+
$order = file_get_contents(dirname(__DIR__) . '/src/Models/Order.php');
|
|
32
|
+
|
|
33
|
+
expect($listener)
|
|
34
|
+
->toContain("'status' => 'available'")
|
|
35
|
+
->not->toContain("Driver::where(['status' => 'active'")
|
|
36
|
+
->and($order)
|
|
37
|
+
->toContain("'status' => 'available'")
|
|
38
|
+
->not->toContain("Driver::where(['status' => 'active'");
|
|
39
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
test('labels use the shared utf8 pdf renderer instead of arabic-only html rewriting', function () {
|
|
4
|
+
$labelPdf = file_get_contents(dirname(__DIR__) . '/src/Support/LabelPdf.php');
|
|
5
|
+
$order = file_get_contents(dirname(__DIR__) . '/src/Models/Order.php');
|
|
6
|
+
$waypoint = file_get_contents(dirname(__DIR__) . '/src/Models/Waypoint.php');
|
|
7
|
+
$entity = file_get_contents(dirname(__DIR__) . '/src/Models/Entity.php');
|
|
8
|
+
$default = file_get_contents(dirname(__DIR__) . '/resources/views/labels/default.php');
|
|
9
|
+
$stop = file_get_contents(dirname(__DIR__) . '/resources/views/labels/waypoint-label.php');
|
|
10
|
+
$utils = file_get_contents(dirname(__DIR__) . '/src/Support/Utils.php');
|
|
11
|
+
|
|
12
|
+
expect($labelPdf)
|
|
13
|
+
->toContain("Pdf::loadHTML(\$html, 'UTF-8')")
|
|
14
|
+
->toContain("'defaultFont' => 'DejaVu Sans'")
|
|
15
|
+
->and($order)->toContain('LabelPdf::fromHtml($this->label())')
|
|
16
|
+
->and($waypoint)->toContain('LabelPdf::fromHtml($this->label())')
|
|
17
|
+
->and($entity)->toContain('LabelPdf::fromHtml($this->label())')
|
|
18
|
+
->and($default)->toContain('Noto Sans Arabic')
|
|
19
|
+
->not->toContain('strtoupper($company->name)')
|
|
20
|
+
->and($stop)->toContain('Noto Sans CJK JP')
|
|
21
|
+
->not->toContain('strtoupper($company->name)')
|
|
22
|
+
->and($utils)->not->toContain('fixArabicInHTML');
|
|
23
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Http\Controllers\Internal\v1\LiveController;
|
|
4
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
5
|
+
use Illuminate\Http\Request;
|
|
6
|
+
|
|
7
|
+
function callLiveControllerMethod(string $method, array $arguments = [])
|
|
8
|
+
{
|
|
9
|
+
$reflection = new ReflectionMethod(LiveController::class, $method);
|
|
10
|
+
$reflection->setAccessible(true);
|
|
11
|
+
|
|
12
|
+
return $reflection->invokeArgs(new LiveController(), $arguments);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test('live viewport bounds are normalized for stable cache keys', function () {
|
|
16
|
+
$request = new Request([
|
|
17
|
+
'bounds' => ['1.234567', '103.876543', '1.345678', '103.987654'],
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
expect(callLiveControllerMethod('normalizeLiveBounds', [$request]))
|
|
21
|
+
->toBe([1.2346, 103.8765, 1.3457, 103.9877]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('invalid live viewport bounds fall back to unbounded queries', function ($bounds) {
|
|
25
|
+
$request = new Request(['bounds' => $bounds]);
|
|
26
|
+
|
|
27
|
+
expect(callLiveControllerMethod('normalizeLiveBounds', [$request]))->toBeNull();
|
|
28
|
+
})->with([
|
|
29
|
+
'missing coordinate' => [[1, 2, 3]],
|
|
30
|
+
'non numeric' => [[1, 'west', 3, 4]],
|
|
31
|
+
'invalid latitude' => [[-91, 103, 1, 104]],
|
|
32
|
+
'invalid longitude' => [[1, -181, 2, 104]],
|
|
33
|
+
'inverted latitude' => [[2, 103, 1, 104]],
|
|
34
|
+
'inverted longitude' => [[1, 104, 2, 103]],
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
test('live viewport limit defaults and clamps', function () {
|
|
38
|
+
expect(callLiveControllerMethod('normalizeLiveLimit', [new Request()]))->toBe(500)
|
|
39
|
+
->and(callLiveControllerMethod('normalizeLiveLimit', [new Request(['limit' => 25])]))->toBe(25)
|
|
40
|
+
->and(callLiveControllerMethod('normalizeLiveLimit', [new Request(['limit' => 0])]))->toBe(500)
|
|
41
|
+
->and(callLiveControllerMethod('normalizeLiveLimit', [new Request(['limit' => 5000])]))->toBe(1000);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('live viewport query avoids spatial constructors with fixed srids', function () {
|
|
45
|
+
$query = Driver::query();
|
|
46
|
+
|
|
47
|
+
callLiveControllerMethod('applyLiveLocationGuards', [$query]);
|
|
48
|
+
callLiveControllerMethod('applyLiveViewportBounds', [$query, [1.2, 103.8, 1.4, 104.0]]);
|
|
49
|
+
$query->orderByDesc('updated_at')->orderByDesc('id')->limit(25);
|
|
50
|
+
|
|
51
|
+
$sql = $query->toSql();
|
|
52
|
+
|
|
53
|
+
expect($sql)->toContain('ST_Y(location) BETWEEN ? AND ?')
|
|
54
|
+
->and($sql)->toContain('ST_X(location) BETWEEN ? AND ?')
|
|
55
|
+
->and($sql)->toContain('limit 25')
|
|
56
|
+
->and($sql)->not->toContain('ST_MakeEnvelope')
|
|
57
|
+
->and($sql)->not->toContain('ST_GeomFromText');
|
|
58
|
+
|
|
59
|
+
expect($query->getBindings())->toContain(1.2, 1.4, 103.8, 104.0);
|
|
60
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Support\Metrics\AbstractMetric;
|
|
4
|
+
use Fleetbase\FleetOps\Support\Metrics\OrdersInProgressMetric;
|
|
5
|
+
use Fleetbase\FleetOps\Support\Metrics\Registry;
|
|
6
|
+
use Fleetbase\FleetOps\Support\Metrics\TotalTimeTraveledMetric;
|
|
7
|
+
|
|
8
|
+
test('registry exposes every known metric slug', function () {
|
|
9
|
+
$slugs = Registry::slugs();
|
|
10
|
+
|
|
11
|
+
expect($slugs)->toContain('earnings');
|
|
12
|
+
expect($slugs)->toContain('fuel_costs');
|
|
13
|
+
expect($slugs)->toContain('total_distance_traveled');
|
|
14
|
+
expect($slugs)->toContain('total_time_traveled');
|
|
15
|
+
expect($slugs)->toContain('orders_completed');
|
|
16
|
+
expect($slugs)->toContain('orders_canceled');
|
|
17
|
+
expect($slugs)->toContain('orders_in_progress');
|
|
18
|
+
expect($slugs)->toContain('orders_scheduled');
|
|
19
|
+
expect($slugs)->toContain('active_live_orders');
|
|
20
|
+
expect($slugs)->toContain('drivers_online');
|
|
21
|
+
expect($slugs)->toContain('total_drivers');
|
|
22
|
+
expect($slugs)->toContain('total_customers');
|
|
23
|
+
expect($slugs)->toContain('open_issues');
|
|
24
|
+
expect($slugs)->toContain('resolved_issues');
|
|
25
|
+
expect($slugs)->toContain('avg_order_value');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('registry resolves unknown slugs to null', function () {
|
|
29
|
+
expect(Registry::resolve('does_not_exist'))->toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('every registered metric extends the abstract base', function () {
|
|
33
|
+
foreach (Registry::all() as $slug => $class) {
|
|
34
|
+
expect(is_subclass_of($class, AbstractMetric::class))
|
|
35
|
+
->toBeTrue("Metric class for slug '{$slug}' must extend AbstractMetric");
|
|
36
|
+
expect($class::slug())->toBe($slug);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('open and resolved issue metrics scope to the constructor company (not session)', function () {
|
|
41
|
+
$sourceOpen = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/OpenIssuesMetric.php');
|
|
42
|
+
$sourceResolved = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/ResolvedIssuesMetric.php');
|
|
43
|
+
|
|
44
|
+
expect($sourceOpen)->not->toContain("session('company')");
|
|
45
|
+
expect($sourceOpen)->toContain('$this->company->uuid');
|
|
46
|
+
expect($sourceResolved)->not->toContain("session('company')");
|
|
47
|
+
expect($sourceResolved)->toContain('$this->company->uuid');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('total_time_traveled metric sums the time column, not distance', function () {
|
|
51
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/TotalTimeTraveledMetric.php');
|
|
52
|
+
|
|
53
|
+
expect($source)->toContain("\$query->sum('time')");
|
|
54
|
+
expect($source)->not->toContain("\$query->sum('distance')");
|
|
55
|
+
expect(TotalTimeTraveledMetric::slug())->toBe('total_time_traveled');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('money metrics return float values to preserve cents', function () {
|
|
59
|
+
$earnings = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/EarningsMetric.php');
|
|
60
|
+
$fuelCosts = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/FuelCostsMetric.php');
|
|
61
|
+
|
|
62
|
+
expect($earnings)->toContain('(float) $query->sum');
|
|
63
|
+
expect($earnings)->not->toMatch('/\(int\)\s*\$query->sum/');
|
|
64
|
+
expect($fuelCosts)->toContain('(float) $query->sum');
|
|
65
|
+
expect($fuelCosts)->not->toMatch('/\(int\)\s*\$query->sum/');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('money metrics filter by currency to avoid mixed-currency sums', function () {
|
|
69
|
+
$earnings = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/EarningsMetric.php');
|
|
70
|
+
$fuelCosts = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/FuelCostsMetric.php');
|
|
71
|
+
$aov = file_get_contents(dirname(__DIR__) . '/src/Support/Metrics/AvgOrderValueMetric.php');
|
|
72
|
+
|
|
73
|
+
expect($earnings)->toContain("->where('currency'");
|
|
74
|
+
expect($fuelCosts)->toContain("->where('currency'");
|
|
75
|
+
expect($aov)->toContain("->where('currency'");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('ordersInProgress uses an explicit allowlist rather than an exclusion list', function () {
|
|
79
|
+
$statuses = OrdersInProgressMetric::IN_PROGRESS_STATUSES;
|
|
80
|
+
expect($statuses)->toBeArray();
|
|
81
|
+
expect($statuses)->not->toBeEmpty();
|
|
82
|
+
expect($statuses)->toContain('dispatched');
|
|
83
|
+
expect($statuses)->toContain('started');
|
|
84
|
+
});
|
|
@@ -39,7 +39,7 @@ function orchestrationTestOrder($pickup = null, $dropoff = null, array $waypoint
|
|
|
39
39
|
public function __construct(public $pickup, public $dropoff, array $waypoints)
|
|
40
40
|
{
|
|
41
41
|
$this->entities = collect();
|
|
42
|
-
$this->waypointMarkers
|
|
42
|
+
$this->waypointMarkers = collect($waypoints)->map(function ($place, $index) {
|
|
43
43
|
return new class($place, $index) {
|
|
44
44
|
public string $public_id;
|
|
45
45
|
public string $uuid;
|
|
@@ -61,7 +61,7 @@ function orchestrationTestOrder($pickup = null, $dropoff = null, array $waypoint
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
$order
|
|
64
|
+
$order = new Order();
|
|
65
65
|
$order->public_id = 'order_test';
|
|
66
66
|
$order->setRelation('payload', $payload);
|
|
67
67
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\OrderConfigController;
|
|
4
|
+
use Fleetbase\FleetOps\Http\Resources\Internal\v1\OrderConfig as InternalOrderConfigResource;
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\OrderConfig as OrderConfigResource;
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|--------------------------------------------------------------------------
|
|
9
|
+
| OrderConfig API surface — static shape checks.
|
|
10
|
+
|
|
|
11
|
+
| The OrderConfig public API surface gives consumers (customer portals,
|
|
12
|
+
| third-party integrations) a read-only projection of the OrderConfig
|
|
13
|
+
| `flow` JSON so they can drive status filters, activity labels, and
|
|
14
|
+
| similar UI from the canonical config rather than hardcoding their own.
|
|
15
|
+
|
|
|
16
|
+
| These mirror the lightweight static checks used elsewhere in this
|
|
17
|
+
| package and verify the routes, controller methods, and resource shape
|
|
18
|
+
| exist without booting Laravel.
|
|
19
|
+
|--------------------------------------------------------------------------
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
test('order-configs route group is registered in the consumable v1 namespace', function () {
|
|
23
|
+
$routes = file_get_contents(dirname(__DIR__) . '/src/routes.php');
|
|
24
|
+
|
|
25
|
+
expect($routes)
|
|
26
|
+
->toContain("\$router->group(['prefix' => 'order-configs']")
|
|
27
|
+
->and($routes)->toContain('OrderConfigController@query')
|
|
28
|
+
->and($routes)->toContain('OrderConfigController@find');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('OrderConfigController exposes only read-only methods', function () {
|
|
32
|
+
foreach (['query', 'find'] as $method) {
|
|
33
|
+
expect(method_exists(OrderConfigController::class, $method))->toBeTrue(
|
|
34
|
+
"OrderConfigController::{$method} missing",
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// No write methods are exposed — OrderConfigs are owned by operator UIs.
|
|
39
|
+
foreach (['create', 'update', 'delete', 'store', 'destroy'] as $method) {
|
|
40
|
+
expect(method_exists(OrderConfigController::class, $method))->toBeFalse(
|
|
41
|
+
"OrderConfigController::{$method} should NOT exist on the public surface",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('find() resolves by uuid, public_id, namespace, or key', function () {
|
|
47
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/OrderConfigController.php');
|
|
48
|
+
|
|
49
|
+
// The controller defers to the canonical resolver so /transport,
|
|
50
|
+
// /system:order-config:transport, the public_id, and the uuid all work.
|
|
51
|
+
expect($source)
|
|
52
|
+
->toContain('OrderConfig::resolveFromIdentifier')
|
|
53
|
+
->toContain('findRecordOrFail');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('OrderConfig resource projects only public-safe fields', function () {
|
|
57
|
+
expect(class_exists(OrderConfigResource::class))->toBeTrue();
|
|
58
|
+
|
|
59
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/OrderConfig.php');
|
|
60
|
+
|
|
61
|
+
expect($source)
|
|
62
|
+
// Public contract: id/key/name/namespace + the flow projection.
|
|
63
|
+
->toContain("'key'")
|
|
64
|
+
->toContain("'name'")
|
|
65
|
+
->toContain("'namespace'")
|
|
66
|
+
->toContain("'flow'")
|
|
67
|
+
->toContain('projectFlow')
|
|
68
|
+
// Each flow row keeps the canonical activity keys.
|
|
69
|
+
->toContain("'code'")
|
|
70
|
+
->toContain("'status'")
|
|
71
|
+
->toContain("'complete'")
|
|
72
|
+
// Internal config payload must not be exposed on the public surface.
|
|
73
|
+
->not->toContain("'entities'");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('internal OrderConfig resource preserves editable flow graph fields', function () {
|
|
77
|
+
expect(class_exists(InternalOrderConfigResource::class))->toBeTrue();
|
|
78
|
+
|
|
79
|
+
$source = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/Internal/v1/OrderConfig.php');
|
|
80
|
+
|
|
81
|
+
expect($source)
|
|
82
|
+
->toContain("'flow'")
|
|
83
|
+
->toContain('$this->flow')
|
|
84
|
+
->toContain("'entities'")
|
|
85
|
+
->toContain("'core_service'");
|
|
86
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
test('order file updates attach files to the order like creation does', function () {
|
|
4
|
+
$controller = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Internal/v1/OrderController.php');
|
|
5
|
+
$orderModel = file_get_contents(dirname(__DIR__) . '/src/Models/Order.php');
|
|
6
|
+
$detailsJs = file_get_contents(dirname(dirname(__DIR__)) . '/addon/components/order/details/documents.js');
|
|
7
|
+
$formJs = file_get_contents(dirname(dirname(__DIR__)) . '/addon/components/order/form/documents.js');
|
|
8
|
+
|
|
9
|
+
expect($controller)
|
|
10
|
+
->toContain("\$uploads = \$request->array('order.files')")
|
|
11
|
+
->toContain('$order->attachFiles($uploads)')
|
|
12
|
+
->and($orderModel)
|
|
13
|
+
->toContain('public function attachFiles($uploads): self')
|
|
14
|
+
->toContain('$file->setKey($this)')
|
|
15
|
+
->and($detailsJs)
|
|
16
|
+
->toContain("subject_type: 'fleet-ops:order'")
|
|
17
|
+
->and($formJs)
|
|
18
|
+
->toContain('if (!this.args.resource.isNew)')
|
|
19
|
+
->toContain('subject_uuid = this.args.resource.id');
|
|
20
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
test('service area create and update accept persisted map geometry and style fields', function () {
|
|
4
|
+
$controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Api/v1/ServiceAreaController.php');
|
|
5
|
+
|
|
6
|
+
expect($controller)
|
|
7
|
+
->toContain("'border'")
|
|
8
|
+
->toContain("'color'")
|
|
9
|
+
->toContain("'stroke_color'");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('service area resource returns map geometry and style fields', function () {
|
|
13
|
+
$resource = file_get_contents(__DIR__ . '/../src/Http/Resources/v1/ServiceArea.php');
|
|
14
|
+
|
|
15
|
+
expect($resource)
|
|
16
|
+
->toContain("'border'")
|
|
17
|
+
->toContain("'color'")
|
|
18
|
+
->toContain("'stroke_color'")
|
|
19
|
+
->toContain("'trigger_on_entry'")
|
|
20
|
+
->toContain("'trigger_on_exit'");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('zone create accepts only service area public ids on the consumable api', function () {
|
|
24
|
+
$request = file_get_contents(__DIR__ . '/../src/Http/Requests/CreateZoneRequest.php');
|
|
25
|
+
$controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Api/v1/ZoneController.php');
|
|
26
|
+
|
|
27
|
+
expect($request)
|
|
28
|
+
->toContain("'service_area'")
|
|
29
|
+
->not->toContain("'service_area_uuid'");
|
|
30
|
+
|
|
31
|
+
expect($controller)
|
|
32
|
+
->toContain("'public_id' => \$request->input('service_area')")
|
|
33
|
+
->not->toContain("->orWhere('uuid'");
|
|
34
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
test('vehicle updates preserve driver assignment unless driver input is explicit', function () {
|
|
4
|
+
$internalController = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Internal/v1/VehicleController.php');
|
|
5
|
+
$publicController = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Api/v1/VehicleController.php');
|
|
6
|
+
$vehicleModel = file_get_contents(dirname(__DIR__) . '/src/Models/Vehicle.php');
|
|
7
|
+
|
|
8
|
+
expect($internalController)
|
|
9
|
+
->toContain('hasDriverInput($request)')
|
|
10
|
+
->toContain('syncDriverAssignment($vehicle, $this->driverIdentifierFromRequest($request))')
|
|
11
|
+
->toContain("Arr::has(\$payload, 'vehicle.driver_uuid')")
|
|
12
|
+
->and($publicController)
|
|
13
|
+
->toContain("\$request->exists('driver')")
|
|
14
|
+
->toContain('$vehicle->unassignDriver()')
|
|
15
|
+
->and($vehicleModel)
|
|
16
|
+
->toContain('public function unassignDriver(): self')
|
|
17
|
+
->toContain("Driver::where('vehicle_uuid', \$this->uuid)->update(['vehicle_uuid' => null])");
|
|
18
|
+
});
|