@fleetbase/fleetops-engine 0.6.70 → 0.6.72
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/RELEASE.md +18 -47
- package/addon/components/admin/tracking-page-settings.hbs +56 -0
- package/addon/components/admin/tracking-page-settings.js +67 -0
- package/addon/components/device/form.hbs +2 -2
- package/addon/components/driver/details.hbs +1 -1
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/equipment/form.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +11 -3
- package/addon/components/maintenance/form.hbs +3 -3
- package/addon/components/map/google-live-map.js +2 -2
- package/addon/components/map/leaflet-live-map.js +45 -32
- package/addon/components/orchestrator/phase-builder.js +2 -1
- package/addon/components/orchestrator-workbench.hbs +7 -1
- package/addon/components/orchestrator-workbench.js +16 -2
- package/addon/components/order/details/activity.hbs +8 -6
- package/addon/components/order/details/activity.js +9 -0
- package/addon/components/order/details.hbs +29 -0
- package/addon/components/order/details.js +26 -0
- package/addon/components/order/form/details.hbs +30 -4
- package/addon/components/order/form/details.js +46 -0
- package/addon/components/order/form/route.hbs +5 -3
- package/addon/components/order/form/route.js +24 -1
- package/addon/components/order/form.hbs +12 -0
- package/addon/components/order/form.js +21 -1
- package/addon/components/order/kanban.js +72 -0
- package/addon/components/order-config-manager/activity-flow.js +45 -10
- package/addon/components/order-config-manager/lifecycle.hbs +250 -0
- package/addon/components/order-config-manager/lifecycle.js +358 -0
- package/addon/components/order-config-manager.js +5 -1
- package/addon/components/service-rate/details.hbs +1 -1
- package/addon/components/service-rate/form.hbs +1 -1
- package/addon/components/tracking-page/full-view.hbs +276 -0
- package/addon/components/tracking-page/full-view.js +474 -0
- package/addon/components/tracking-page/locale-selector.js +30 -0
- package/addon/components/tracking-page/map.hbs +44 -0
- package/addon/components/tracking-page/map.js +328 -0
- package/addon/components/tracking-page/order-list.hbs +35 -0
- package/addon/components/tracking-page/public-view.hbs +60 -0
- package/addon/components/tracking-page/settings-preview.hbs +21 -0
- package/addon/components/tracking-page/settings-preview.js +23 -0
- package/addon/components/tracking-page/verify-panel.hbs +109 -0
- package/addon/components/tracking-page.hbs +142 -0
- package/addon/components/tracking-page.js +564 -0
- package/addon/components/trailer/form.hbs +2 -2
- package/addon/components/vehicle/form.hbs +3 -3
- package/addon/components/warranty/form.hbs +2 -2
- package/addon/components/widget/live-fleet.js +54 -6
- package/addon/components/widget/proof-of-delivery.hbs +49 -0
- package/addon/components/widget/proof-of-delivery.js +62 -0
- package/addon/components/work-order/form.hbs +3 -3
- package/addon/controllers/analytics/reports/index.js +10 -1
- package/addon/controllers/connectivity/devices/index/details.js +1 -0
- package/addon/controllers/connectivity/devices/index.js +8 -2
- package/addon/controllers/connectivity/events/index.js +31 -1
- package/addon/controllers/connectivity/sensors/index/details.js +1 -1
- package/addon/controllers/connectivity/sensors/index.js +37 -1
- package/addon/controllers/maintenance/equipment/index/details.js +2 -2
- package/addon/controllers/maintenance/equipment/index.js +28 -9
- package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
- package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
- package/addon/controllers/maintenance/inspection-submissions/index.js +49 -9
- package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
- package/addon/controllers/maintenance/maintenances/index.js +20 -1
- package/addon/controllers/maintenance/parts/index/details.js +2 -2
- package/addon/controllers/maintenance/parts/index.js +28 -8
- package/addon/controllers/maintenance/schedules/index.js +33 -4
- package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
- package/addon/controllers/maintenance/work-orders/index.js +39 -8
- package/addon/controllers/management/contacts/customers.js +13 -0
- package/addon/controllers/management/contacts/index.js +43 -1
- package/addon/controllers/management/drivers/index/details.js +1 -0
- package/addon/controllers/management/drivers/index.js +32 -2
- package/addon/controllers/management/fleets/index/details.js +1 -0
- package/addon/controllers/management/fleets/index.js +39 -1
- package/addon/controllers/management/fuel-reports/index.js +22 -2
- package/addon/controllers/management/fuel-transactions/index.js +43 -7
- package/addon/controllers/management/issues/index/details.js +1 -0
- package/addon/controllers/management/issues/index.js +22 -2
- package/addon/controllers/management/places/index.js +36 -1
- package/addon/controllers/management/trailers/index/details.js +1 -0
- package/addon/controllers/management/trailers/index.js +50 -2
- package/addon/controllers/management/vehicles/index/details.js +1 -0
- package/addon/controllers/management/vehicles/index.js +33 -2
- package/addon/controllers/management/vendors/index.js +26 -2
- package/addon/controllers/operations/orders/index/details.js +50 -7
- package/addon/controllers/operations/orders/index.js +59 -4
- package/addon/controllers/operations/scheduler/index.js +45 -14
- package/addon/controllers/operations/service-rates/index.js +15 -1
- package/addon/controllers/settings/index.js +6 -0
- package/addon/controllers/settings/map.js +75 -0
- package/addon/controllers/settings/orchestrator.js +8 -6
- package/addon/controllers/settings/tracking-page.js +212 -0
- package/addon/extension.js +97 -1
- package/addon/helpers/parcel-box-image.js +16 -0
- package/addon/routes/operations/orders/index/details.js +1 -0
- package/addon/routes/operations/orders/index/new.js +5 -4
- package/addon/routes/operations/scheduler/index.js +1 -1
- package/addon/routes/settings/tracking-page.js +31 -0
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +1 -0
- package/addon/services/customer-actions.js +2 -1
- package/addon/services/device-actions.js +1 -0
- package/addon/services/device-event-actions.js +1 -0
- package/addon/services/driver-actions.js +4 -13
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/fleet-actions.js +1 -0
- package/addon/services/fuel-report-actions.js +1 -0
- package/addon/services/inspection-form-actions.js +1 -0
- package/addon/services/inspection-submission-actions.js +1 -0
- package/addon/services/integrated-vendor-actions.js +1 -0
- package/addon/services/issue-actions.js +1 -0
- package/addon/services/location.js +69 -21
- package/addon/services/maintenance-actions.js +1 -0
- package/addon/services/maintenance-schedule-actions.js +2 -1
- package/addon/services/map-settings.js +28 -0
- package/addon/services/order-actions.js +7 -0
- package/addon/services/order-allocation.js +1 -1
- package/addon/services/order-presentation.js +129 -0
- package/addon/services/part-actions.js +1 -0
- package/addon/services/place-actions.js +1 -0
- package/addon/services/sensor-actions.js +1 -0
- package/addon/services/service-area-actions.js +1 -0
- package/addon/services/service-rate-actions.js +1 -0
- package/addon/services/telematic-actions.js +1 -0
- package/addon/services/trailer-actions.js +4 -14
- package/addon/services/vehicle-actions.js +4 -13
- package/addon/services/vendor-actions.js +1 -0
- package/addon/services/work-order-actions.js +1 -0
- package/addon/services/zone-actions.js +1 -0
- package/addon/styles/fleetops-engine.css +1391 -0
- package/addon/templates/analytics/reports/index/details.hbs +1 -0
- package/addon/templates/analytics/reports/index.hbs +1 -0
- package/addon/templates/connectivity/devices/index/details.hbs +1 -0
- package/addon/templates/connectivity/devices/index.hbs +1 -0
- package/addon/templates/connectivity/events/index.hbs +1 -0
- package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
- package/addon/templates/connectivity/sensors/index.hbs +1 -0
- package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
- package/addon/templates/maintenance/equipment/index.hbs +2 -0
- package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index.hbs +1 -0
- package/addon/templates/maintenance/parts/index/details.hbs +1 -0
- package/addon/templates/maintenance/parts/index.hbs +2 -0
- package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
- package/addon/templates/maintenance/schedules/index.hbs +2 -0
- package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
- package/addon/templates/maintenance/work-orders/index.hbs +1 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -0
- package/addon/templates/management/contacts/customers.hbs +1 -0
- package/addon/templates/management/contacts/index/details.hbs +1 -0
- package/addon/templates/management/contacts/index.hbs +1 -0
- package/addon/templates/management/contacts.hbs +1 -0
- package/addon/templates/management/drivers/index/details.hbs +1 -0
- package/addon/templates/management/drivers/index.hbs +2 -0
- package/addon/templates/management/fleets/index/details.hbs +1 -0
- package/addon/templates/management/fleets/index.hbs +1 -0
- package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
- package/addon/templates/management/fuel-reports/index.hbs +1 -0
- package/addon/templates/management/fuel-transactions/index.hbs +1 -0
- package/addon/templates/management/issues/index/details.hbs +1 -0
- package/addon/templates/management/issues/index.hbs +1 -0
- package/addon/templates/management/places/index/details.hbs +1 -0
- package/addon/templates/management/places/index.hbs +1 -0
- package/addon/templates/management/trailers/index/details.hbs +1 -0
- package/addon/templates/management/trailers/index.hbs +2 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -0
- package/addon/templates/management/vehicles/index.hbs +2 -0
- package/addon/templates/management/vendors/index/details.hbs +1 -0
- package/addon/templates/management/vendors/index.hbs +2 -0
- package/addon/templates/management/vendors/integrated.hbs +1 -0
- package/addon/templates/operations/orders/index/details.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +1 -0
- package/addon/templates/operations/service-rates/index/details.hbs +1 -0
- package/addon/templates/operations/service-rates/index.hbs +1 -0
- package/addon/templates/settings/map.hbs +46 -0
- package/addon/templates/settings/tracking-page.hbs +318 -0
- package/addon/utils/identity-cell-resource.js +2 -56
- package/addon/utils/map-default-view.js +132 -0
- package/addon/utils/normalize-order-config-flow.js +6 -1
- package/addon/utils/placeholder-images.js +4 -69
- package/addon/utils/resource-descriptors/assets.js +2 -57
- package/addon/utils/resource-descriptors/helpers.js +23 -122
- package/addon/utils/resource-descriptors/operations.js +2 -61
- package/addon/utils/resource-descriptors/people.js +3 -142
- package/addon/utils/resource-descriptors/shared.js +18 -0
- package/addon/utils/tracking-live-source.js +123 -0
- package/addon/utils/tracking-page-theme.js +123 -0
- package/app/components/admin/tracking-page-settings.js +1 -0
- package/app/components/tracking-page/full-view.js +1 -0
- package/app/components/tracking-page/locale-selector.js +1 -0
- package/app/components/tracking-page/map.js +1 -0
- package/app/components/tracking-page/order-list.js +1 -0
- package/app/components/tracking-page/public-view.js +1 -0
- package/app/components/tracking-page/settings-preview.js +1 -0
- package/app/components/tracking-page/verify-panel.js +1 -0
- package/app/components/tracking-page.js +1 -0
- package/app/components/widget/proof-of-delivery.js +1 -0
- package/app/controllers/settings/tracking-page.js +1 -0
- package/app/helpers/parcel-box-image.js +1 -0
- package/app/routes/settings/tracking-page.js +1 -0
- package/app/services/order-presentation.js +1 -0
- package/app/templates/settings/tracking-page.js +1 -0
- package/app/utils/map-default-view.js +1 -0
- package/app/utils/tracking-live-source.js +1 -0
- package/app/utils/tracking-page-theme.js +1 -0
- package/composer.json +2 -2
- package/extension.json +1 -1
- package/package.json +4 -4
- package/scripts/pest-bootstrap.php +4 -0
- package/server/config/fleetops.php +4 -0
- package/server/migrations/2026_10_08_000001_create_tracking_sessions_tables.php +51 -0
- package/server/migrations/2026_10_10_000001_add_trashed_index_to_device_events.php +40 -0
- package/server/migrations/2026_10_10_000002_add_trashed_index_to_positions.php +40 -0
- package/server/migrations/2026_10_12_000001_add_deleted_at_to_tracking_sessions_tables.php +32 -0
- package/server/resources/views/labels/default.php +2 -2
- package/server/resources/views/labels/entity-label.php +2 -2
- package/server/resources/views/labels/waypoint-label.php +2 -2
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +1 -0
- package/server/src/Console/Commands/PruneTelematicsData.php +101 -26
- package/server/src/Events/OrderChanged.php +257 -0
- package/server/src/Events/OrderDriverUnassigned.php +68 -0
- package/server/src/Exceptions/OrderEditLockedException.php +32 -0
- package/server/src/Expansions/CompanyExpansion.php +3 -2
- package/server/src/Expansions/UserExpansion.php +12 -10
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +108 -18
- package/server/src/Http/Controllers/Api/v1/DriverController.php +95 -10
- package/server/src/Http/Controllers/Api/v1/EntityController.php +66 -0
- package/server/src/Http/Controllers/Api/v1/FleetController.php +10 -4
- package/server/src/Http/Controllers/Api/v1/OrderController.php +139 -36
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +76 -8
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +3 -3
- package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +6 -32
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +5 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +3 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +10 -0
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +16 -2
- package/server/src/Http/Controllers/Internal/v1/InspectionSubmissionController.php +13 -8
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +4 -2
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +93 -11
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +118 -28
- package/server/src/Http/Controllers/Internal/v1/PositionController.php +22 -0
- package/server/src/Http/Controllers/Internal/v1/ProofController.php +55 -4
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +303 -10
- package/server/src/Http/Controllers/Internal/v1/TrailerController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +1 -0
- package/server/src/Http/Controllers/Public/TrackingController.php +518 -0
- package/server/src/Http/Filter/ContactFilter.php +1 -1
- package/server/src/Http/Filter/InspectionFormFilter.php +23 -0
- package/server/src/Http/Filter/InspectionSubmissionFilter.php +23 -0
- package/server/src/Http/Filter/MaintenanceFilter.php +23 -0
- package/server/src/Http/Filter/OrderFilter.php +39 -4
- package/server/src/Http/Filter/ProofFilter.php +18 -0
- package/server/src/Http/Filter/RouteFilter.php +18 -0
- package/server/src/Http/Filter/TelematicFilter.php +23 -0
- package/server/src/Http/Filter/WarrantyFilter.php +23 -0
- package/server/src/Http/Requests/CancelOrderRequest.php +2 -1
- package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +14 -1
- package/server/src/Http/Resources/v1/Index/Customer.php +18 -0
- package/server/src/Http/Resources/v1/Index/Order.php +11 -2
- package/server/src/Http/Resources/v1/PublicOrderTracking.php +153 -0
- package/server/src/Http/Resources/v1/TrackingNumber.php +8 -34
- package/server/src/Http/Resources/v1/Zone.php +31 -23
- package/server/src/Jobs/FinalizeApiOrderCreation.php +3 -1
- package/server/src/Jobs/ProcessAllocationJob.php +16 -6
- package/server/src/Jobs/PublishTrackingUpdate.php +50 -0
- package/server/src/Listeners/HandleGeofenceEntered.php +31 -15
- package/server/src/Listeners/HandleOrderCanceled.php +9 -1
- package/server/src/Listeners/HandleUserRemovedFromCompany.php +2 -1
- package/server/src/Listeners/PublishTrackingUpdates.php +27 -0
- package/server/src/Mail/CustomerCredentialsMail.php +4 -2
- package/server/src/Models/AssetConnection.php +3 -0
- package/server/src/Models/Contact.php +3 -0
- package/server/src/Models/Device.php +28 -0
- package/server/src/Models/DeviceEvent.php +7 -0
- package/server/src/Models/Driver.php +20 -7
- package/server/src/Models/Entity.php +12 -4
- package/server/src/Models/Fleet.php +11 -32
- package/server/src/Models/FleetDriver.php +3 -0
- package/server/src/Models/FleetVehicle.php +3 -0
- package/server/src/Models/FuelReport.php +5 -13
- package/server/src/Models/Issue.php +4 -16
- package/server/src/Models/Order.php +234 -11
- package/server/src/Models/OrderConfig.php +129 -0
- package/server/src/Models/Payload.php +227 -35
- package/server/src/Models/Place.php +7 -1
- package/server/src/Models/ServiceArea.php +7 -0
- package/server/src/Models/ServiceRate.php +7 -17
- package/server/src/Models/TrackingNumber.php +109 -6
- package/server/src/Models/TrackingSession.php +33 -0
- package/server/src/Models/TrackingSessionGrant.php +22 -0
- package/server/src/Models/TrackingStatus.php +6 -0
- package/server/src/Models/Vehicle.php +21 -2
- package/server/src/Models/Vendor.php +7 -0
- package/server/src/Models/Waypoint.php +13 -4
- package/server/src/Notifications/OrderAssigned.php +2 -1
- package/server/src/Notifications/OrderCanceled.php +7 -1
- package/server/src/Notifications/OrderChanged.php +87 -0
- package/server/src/Notifications/OrderCompleted.php +2 -1
- package/server/src/Notifications/OrderDispatchFailed.php +2 -1
- package/server/src/Notifications/OrderDispatched.php +2 -1
- package/server/src/Notifications/OrderFailed.php +2 -1
- package/server/src/Notifications/OrderUnassigned.php +66 -0
- package/server/src/Notifications/WaypointCompleted.php +2 -1
- package/server/src/Observers/CompanyUserObserver.php +8 -7
- package/server/src/Observers/DriverObserver.php +6 -1
- package/server/src/Observers/OrderObserver.php +141 -1
- package/server/src/Observers/TrackingNumberObserver.php +18 -6
- package/server/src/Observers/UserObserver.php +2 -1
- package/server/src/Observers/VehicleObserver.php +8 -3
- package/server/src/Orchestration/Engines/RouteSequencingEngine.php +21 -3
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +38 -8
- package/server/src/Orchestration/OrchestrationEngineRegistry.php +61 -1
- package/server/src/Orchestration/Support/OrchestratorSettings.php +61 -0
- package/server/src/Orchestration/Support/RouteMetrics.php +165 -0
- package/server/src/Providers/EventServiceProvider.php +11 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +13 -0
- package/server/src/Support/ApiCacheThrottle.php +76 -0
- package/server/src/Support/LiveOrderQuery.php +3 -1
- package/server/src/Support/OrderEdit.php +365 -0
- package/server/src/Support/OrderEditPolicy.php +292 -0
- package/server/src/Support/ProfileAccountManager.php +54 -1
- package/server/src/Support/ResolvesOrderServiceStops.php +5 -0
- package/server/src/Support/SocketChannels.php +346 -0
- package/server/src/Support/Telematics/TelematicService.php +7 -2
- package/server/src/Support/Telematics/Telemetry/Inbox.php +8 -1
- package/server/src/Support/TrackingChannel.php +55 -0
- package/server/src/Support/TrackingCode.php +242 -0
- package/server/src/Support/TrackingPage/TrackingAccess.php +356 -0
- package/server/src/Support/TrackingPage/TrackingPageConfig.php +264 -0
- package/server/src/Support/TrackingPage/TrackingRecipient.php +111 -0
- package/server/src/Support/TrackingPage/TrackingResolver.php +201 -0
- package/server/src/Support/TrackingPage/TrackingTarget.php +40 -0
- package/server/src/Support/TrackingPage/TrackingView.php +543 -0
- package/server/src/Support/TrackingPublisher.php +261 -0
- package/server/src/Support/TrackingScope.php +182 -0
- package/server/src/Support/TrackingUpdate.php +402 -0
- package/server/src/Support/Utils.php +15 -3
- package/server/src/Traits/HasTrackingNumber.php +2 -0
- package/server/src/routes.php +39 -7
- package/server/tests/ApiCustomerControllerContractsTest.php +54 -7
- package/server/tests/ApiDriverPasswordContractsTest.php +25 -21
- package/server/tests/ApiEntityControllerContractsTest.php +84 -0
- package/server/tests/ApiOrderControllerContractsTest.php +245 -0
- package/server/tests/ApiPayloadControllerContractsTest.php +121 -0
- package/server/tests/ApiPlaceControllerContractsTest.php +1 -1
- package/server/tests/ApiTrackingNumberControllerContractsTest.php +6 -9
- package/server/tests/CompactResourceSerializationTest.php +23 -59
- package/server/tests/ControllerFilterContractsTest.php +1 -1
- package/server/tests/CustomerContactIdentitySafetyTest.php +1 -1
- package/server/tests/EventContractsTest.php +26 -60
- package/server/tests/ExpansionContractsTest.php +15 -2
- package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +29 -3
- package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +23 -0
- package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +16 -16
- package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +41 -14
- package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +57 -22
- package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +23 -9
- package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +171 -1
- package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
- package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +110 -14
- package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
- package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +7 -5
- package/server/tests/Feature/Http/Internal/PositionControllerReplayChannelTest.php +135 -0
- package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +3 -3
- package/server/tests/Feature/Http/Internal/ProofControllerRecentTest.php +170 -0
- package/server/tests/Feature/Http/Public/TrackingControllerTest.php +495 -0
- package/server/tests/IndexCustomerPhotoTest.php +151 -0
- package/server/tests/ModelAccessorContractsTest.php +4 -3
- package/server/tests/NotificationAndMailContractsTest.php +5 -8
- package/server/tests/ObserverContractsTest.php +72 -10
- package/server/tests/OrderActivityFlowRegressionTest.php +3 -2
- package/server/tests/OrderConfigLifecycleTest.php +120 -0
- package/server/tests/OrderFilterExecutionTest.php +167 -2
- package/server/tests/ProcessAllocationJobContractsTest.php +20 -0
- package/server/tests/PublicOrderTrackingResourceTest.php +221 -0
- package/server/tests/QueueJobContractsTest.php +2 -1
- package/server/tests/RequestContractsTest.php +1 -1
- package/server/tests/RouteRegistrationExecutionTest.php +26 -0
- package/server/tests/RouteSequencingEngineTest.php +17 -0
- package/server/tests/SettingControllerContractsTest.php +110 -0
- package/server/tests/Support/TrackingPageDatabase.php +200 -0
- package/server/tests/TenantScopingTest.php +206 -0
- package/server/tests/TrackingPageSettingsTest.php +343 -0
- package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +51 -0
- package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
- package/server/tests/Unit/Http/Filter/ContactFilterAddressTest.php +61 -0
- package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +30 -0
- package/server/tests/Unit/Jobs/FinalizeApiOrderCreationDispatchGuardTest.php +266 -0
- package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +182 -22
- package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +5 -0
- package/server/tests/Unit/Models/DriverAssignVehicleTest.php +81 -0
- package/server/tests/Unit/Models/FleetTest.php +4 -3
- package/server/tests/Unit/Models/OrderCancelReasonTest.php +143 -0
- package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +60 -0
- package/server/tests/Unit/Models/OrderPayloadRouteTest.php +4 -2
- package/server/tests/Unit/Models/PayloadTest.php +18 -4
- package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +2 -2
- package/server/tests/Unit/Models/TrackingNumberTest.php +67 -6
- package/server/tests/Unit/Models/VehicleTest.php +16 -2
- package/server/tests/Unit/Models/WaypointTest.php +4 -3
- package/server/tests/Unit/Notifications/OrderChangeNotificationsTest.php +59 -0
- package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +3 -3
- package/server/tests/Unit/Observers/DriverDeletionCleanupTest.php +158 -0
- package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +20 -0
- package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +3 -3
- package/server/tests/Unit/Observers/OrderAssignmentObserverTest.php +100 -0
- package/server/tests/Unit/Observers/OrderDeleteCascadeTest.php +213 -0
- package/server/tests/Unit/Observers/VehicleObserverDeleteTest.php +67 -0
- package/server/tests/Unit/Orchestration/EngineSelectionAndRouteMetricsTest.php +336 -0
- package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +8 -1
- package/server/tests/Unit/Support/ApiCacheDependenciesTest.php +347 -0
- package/server/tests/Unit/Support/OrderEditPolicyTest.php +156 -0
- package/server/tests/Unit/Support/OrderOperationsTest.php +514 -0
- package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +4 -3
- package/server/tests/Unit/Support/ProfileAccountManagerTest.php +141 -0
- package/server/tests/Unit/Support/QuerySeamsTest.php +5 -1
- package/server/tests/Unit/Support/SocketChannelsTest.php +396 -0
- package/server/tests/Unit/Support/TrackingChannelTest.php +232 -0
- package/server/tests/Unit/Support/TrackingCodeTest.php +186 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingAccessTest.php +200 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingRecipientTest.php +51 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingResolverTest.php +181 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingViewTest.php +479 -0
- package/server/tests/Unit/Support/TrackingPublisherTest.php +425 -0
- package/server/tests/Unit/Support/TrackingUpdateTest.php +363 -0
- package/server/tests/Unit/Support/UtilsAdditionalTest.php +9 -0
- package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +25 -0
- package/server/tests/VehicleResourceContractsTest.php +46 -0
- package/server/tests/VroomOrchestrationEngineTest.php +34 -1
- package/server/tests/ZoneGeofenceSettingsResourceTest.php +121 -0
- package/translations/ar-ae.yml +274 -0
- package/translations/bg-bg.yaml +274 -0
- package/translations/en-us.yaml +347 -0
- package/translations/es-pa.yaml +274 -0
- package/translations/fr-fr.yaml +274 -0
- package/translations/mn-mn.yaml +274 -0
- package/translations/pt-br.yaml +274 -0
- package/translations/ru-ru.yaml +276 -2
- package/translations/uk-ua.yaml +275 -1
- package/translations/vi-vn.yaml +274 -0
- package/addon/components/cell/contact-identity.hbs +0 -1
- package/addon/components/cell/customer-identity.hbs +0 -1
- package/addon/components/cell/driver-identity.hbs +0 -1
- package/addon/components/cell/fleet-identity.hbs +0 -1
- package/addon/components/cell/order-identity.hbs +0 -1
- package/addon/components/cell/place-identity.hbs +0 -1
- package/addon/components/cell/vehicle-identity.hbs +0 -1
- package/addon/components/cell/vendor-identity.hbs +0 -1
- package/addon/components/contact/pill.hbs +0 -24
- package/addon/components/contact/summary.hbs +0 -1
- package/addon/components/customer/pill.hbs +0 -24
- package/addon/components/customer/summary.hbs +0 -1
- package/addon/components/driver/pill.hbs +0 -24
- package/addon/components/driver/summary.hbs +0 -1
- package/addon/components/fleet/pill.hbs +0 -24
- package/addon/components/fleet/summary.hbs +0 -1
- package/addon/components/order/pill.hbs +0 -24
- package/addon/components/order/summary.hbs +0 -1
- package/addon/components/place/pill.hbs +0 -24
- package/addon/components/place/summary.hbs +0 -1
- package/addon/components/select-option/contact.hbs +0 -12
- package/addon/components/select-option/customer.hbs +0 -12
- package/addon/components/select-option/driver.hbs +0 -12
- package/addon/components/select-option/fleet.hbs +0 -12
- package/addon/components/select-option/order.hbs +0 -12
- package/addon/components/select-option/place.hbs +0 -12
- package/addon/components/select-option/vehicle.hbs +0 -12
- package/addon/components/select-option/vendor.hbs +0 -12
- package/addon/components/vehicle/pill.hbs +0 -24
- package/addon/components/vehicle/summary.hbs +0 -1
- package/addon/components/vendor/pill.hbs +0 -24
- package/addon/components/vendor/summary.hbs +0 -1
- package/app/components/cell/contact-identity.js +0 -1
- package/app/components/cell/customer-identity.js +0 -1
- package/app/components/cell/driver-identity.js +0 -1
- package/app/components/cell/fleet-identity.js +0 -1
- package/app/components/cell/order-identity.js +0 -1
- package/app/components/cell/place-identity.js +0 -1
- package/app/components/cell/vehicle-identity.js +0 -1
- package/app/components/cell/vendor-identity.js +0 -1
- package/app/components/contact/pill.js +0 -1
- package/app/components/contact/summary.js +0 -1
- package/app/components/customer/pill.js +0 -1
- package/app/components/customer/summary.js +0 -1
- package/app/components/driver/pill.js +0 -1
- package/app/components/driver/summary.js +0 -1
- package/app/components/fleet/pill.js +0 -1
- package/app/components/fleet/summary.js +0 -1
- package/app/components/order/pill.js +0 -1
- package/app/components/order/summary.js +0 -1
- package/app/components/place/pill.js +0 -1
- package/app/components/place/summary.js +0 -1
- package/app/components/select-option/contact.js +0 -1
- package/app/components/select-option/customer.js +0 -1
- package/app/components/select-option/driver.js +0 -1
- package/app/components/select-option/fleet.js +0 -1
- package/app/components/select-option/order.js +0 -1
- package/app/components/select-option/place.js +0 -1
- package/app/components/select-option/vehicle.js +0 -1
- package/app/components/select-option/vendor.js +0 -1
- package/app/components/vehicle/pill.js +0 -1
- package/app/components/vehicle/summary.js +0 -1
- package/app/components/vendor/pill.js +0 -1
- package/app/components/vendor/summary.js +0 -1
|
@@ -8,8 +8,11 @@ use Fleetbase\FleetOps\Events\OrderCanceled;
|
|
|
8
8
|
use Fleetbase\FleetOps\Events\OrderCompleted;
|
|
9
9
|
use Fleetbase\FleetOps\Events\OrderDispatched;
|
|
10
10
|
use Fleetbase\FleetOps\Events\OrderDriverAssigned;
|
|
11
|
+
use Fleetbase\FleetOps\Events\OrderDriverUnassigned;
|
|
11
12
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
13
|
+
use Fleetbase\FleetOps\Notifications\OrderUnassigned;
|
|
12
14
|
use Fleetbase\FleetOps\Support\LabelPdf;
|
|
15
|
+
use Fleetbase\FleetOps\Support\OrderEditPolicy;
|
|
13
16
|
use Fleetbase\FleetOps\Support\OrderTracker;
|
|
14
17
|
use Fleetbase\FleetOps\Support\Utils;
|
|
15
18
|
use Fleetbase\FleetOps\Traits\HasTrackingNumber;
|
|
@@ -35,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
|
35
38
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
36
39
|
use Illuminate\Support\Carbon;
|
|
37
40
|
use Illuminate\Support\Collection;
|
|
41
|
+
use Illuminate\Support\Facades\Log;
|
|
38
42
|
use Illuminate\Support\Str;
|
|
39
43
|
use Spatie\Activitylog\LogOptions;
|
|
40
44
|
use Spatie\Activitylog\Traits\LogsActivity;
|
|
@@ -196,6 +200,38 @@ class Order extends Model
|
|
|
196
200
|
*/
|
|
197
201
|
protected $with = [];
|
|
198
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Relation paths and tables the cached order lists embed, so a write to any
|
|
205
|
+
* of them invalidates those lists (fleetbase/core-api#301).
|
|
206
|
+
*
|
|
207
|
+
* They mirror OrderFilter::queryForInternal()'s eager loads and whereHas
|
|
208
|
+
* membership, the payload relation's own `with`, Payload's `$with` and the
|
|
209
|
+
* public query's loads. customer and facilitator are morphTo relations, so
|
|
210
|
+
* their tables (contacts, vendors, integrated_vendors) are named instead;
|
|
211
|
+
* a vendor loads its place, which payload.pickup already covers. files
|
|
212
|
+
* (customer photo, vendor logo) is left out: every upload writes it, and a
|
|
213
|
+
* new photo is set by writing the contact's or vendor's own row.
|
|
214
|
+
*
|
|
215
|
+
* @var string[]
|
|
216
|
+
*/
|
|
217
|
+
protected $apiCacheDependencies = [
|
|
218
|
+
'payload',
|
|
219
|
+
'payload.pickup',
|
|
220
|
+
'payload.dropoff',
|
|
221
|
+
'payload.return',
|
|
222
|
+
'payload.entities',
|
|
223
|
+
'payload.waypoints',
|
|
224
|
+
'payload.firstWaypointMarker.place',
|
|
225
|
+
'payload.lastWaypointMarker.place',
|
|
226
|
+
'trackingNumber',
|
|
227
|
+
'trackingStatuses',
|
|
228
|
+
'driverAssigned',
|
|
229
|
+
'vehicleAssigned',
|
|
230
|
+
'contacts',
|
|
231
|
+
'vendors',
|
|
232
|
+
'integrated_vendors',
|
|
233
|
+
];
|
|
234
|
+
|
|
199
235
|
/**
|
|
200
236
|
* The attributes that should be cast to native types.
|
|
201
237
|
*
|
|
@@ -244,6 +280,16 @@ class Order extends Model
|
|
|
244
280
|
'_key',
|
|
245
281
|
];
|
|
246
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Set for one save: the driver assignment being saved sends no assignment notification.
|
|
285
|
+
*/
|
|
286
|
+
public bool $silentDriverAssignment = false;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Set for one save: the driver taken off the order is not sent an unassigned push (they declined it themselves).
|
|
290
|
+
*/
|
|
291
|
+
public bool $quietDriverUnassignment = false;
|
|
292
|
+
|
|
247
293
|
/**
|
|
248
294
|
* Enforce a morph map.
|
|
249
295
|
*/
|
|
@@ -263,6 +309,31 @@ class Order extends Model
|
|
|
263
309
|
);
|
|
264
310
|
}
|
|
265
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Bulk removal deletes the matched orders one at a time instead of with a single
|
|
314
|
+
* query, so the "deleted" event fires for each and OrderObserver cascades the soft
|
|
315
|
+
* delete to the order's payload, entities and tracking data. Matching, the tenant
|
|
316
|
+
* constraint (GHSA-3wj9-hh56-7fw7) and the returned count follow core-api's version.
|
|
317
|
+
*/
|
|
318
|
+
public function bulkRemove($ids = [])
|
|
319
|
+
{
|
|
320
|
+
$orders = static::where(function ($query) use ($ids) {
|
|
321
|
+
$query->whereIn($this->getQualifiedKeyName(), $ids)->orWhereIn($this->getQualifiedPublicId(), $ids);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Tenant constraint: never delete another company's orders.
|
|
325
|
+
$companyUuid = session('company');
|
|
326
|
+
if ($companyUuid) {
|
|
327
|
+
$orders->where($this->qualifyColumn('company_uuid'), $companyUuid);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
$orders = $orders->get();
|
|
331
|
+
$orders->each->delete();
|
|
332
|
+
$this->invalidateApiCacheOnChange();
|
|
333
|
+
|
|
334
|
+
return $orders->count();
|
|
335
|
+
}
|
|
336
|
+
|
|
266
337
|
/**
|
|
267
338
|
* Get the activity log options for the model.
|
|
268
339
|
*/
|
|
@@ -1034,6 +1105,7 @@ class Order extends Model
|
|
|
1034
1105
|
$route->save();
|
|
1035
1106
|
|
|
1036
1107
|
$this->updateQuietly(['route_uuid' => $route->uuid]);
|
|
1108
|
+
$this->invalidateApiCache();
|
|
1037
1109
|
|
|
1038
1110
|
return $this;
|
|
1039
1111
|
}
|
|
@@ -1169,6 +1241,7 @@ class Order extends Model
|
|
|
1169
1241
|
]);
|
|
1170
1242
|
|
|
1171
1243
|
if ($attached) {
|
|
1244
|
+
$this->invalidateApiCache();
|
|
1172
1245
|
$this->setRelation('purchaseRate', $purchaseRate);
|
|
1173
1246
|
|
|
1174
1247
|
if ($purchaseRate->transaction_uuid) {
|
|
@@ -1231,6 +1304,7 @@ class Order extends Model
|
|
|
1231
1304
|
]);
|
|
1232
1305
|
|
|
1233
1306
|
$this->updateQuietly(['transaction_uuid' => $transaction->uuid]);
|
|
1307
|
+
$this->invalidateApiCache();
|
|
1234
1308
|
} catch (\Throwable $e) {
|
|
1235
1309
|
}
|
|
1236
1310
|
|
|
@@ -1270,6 +1344,7 @@ class Order extends Model
|
|
|
1270
1344
|
if ($save) {
|
|
1271
1345
|
$this->saveQuietly();
|
|
1272
1346
|
$this->flushAttributesCache();
|
|
1347
|
+
$this->invalidateApiCache();
|
|
1273
1348
|
}
|
|
1274
1349
|
|
|
1275
1350
|
dispatch(fn () => event(new OrderDispatched($this)))->afterCommit();
|
|
@@ -1308,6 +1383,29 @@ class Order extends Model
|
|
|
1308
1383
|
return $this;
|
|
1309
1384
|
}
|
|
1310
1385
|
|
|
1386
|
+
/**
|
|
1387
|
+
* Determine whether the order is still waiting to be dispatched.
|
|
1388
|
+
*
|
|
1389
|
+
* A deferred dispatch, such as the queued finalize job for orders created with
|
|
1390
|
+
* `dispatch: true`, can run after the order was already dispatched or started
|
|
1391
|
+
* through another path. Dispatching it then would move the status back to
|
|
1392
|
+
* "dispatched", so only an order that still sits at its initial status with no
|
|
1393
|
+
* dispatch or start recorded qualifies.
|
|
1394
|
+
*/
|
|
1395
|
+
public function isAwaitingDispatch(): bool
|
|
1396
|
+
{
|
|
1397
|
+
if ($this->dispatched || $this->dispatched_at || $this->started || $this->started_at) {
|
|
1398
|
+
return false;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
$status = $this->status ?? 'created';
|
|
1402
|
+
if ($status !== 'created' && $status !== $this->config()?->getInitialStatusCode()) {
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
return !$this->hasDispatchedStatus();
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1311
1409
|
/**
|
|
1312
1410
|
* Dispatches the order only if it hasn't been dispatched yet.
|
|
1313
1411
|
* A check is performed before dispatching the order.
|
|
@@ -1346,10 +1444,33 @@ class Order extends Model
|
|
|
1346
1444
|
*/
|
|
1347
1445
|
public function cancel()
|
|
1348
1446
|
{
|
|
1349
|
-
$this->
|
|
1447
|
+
return $this->cancelWithReason(null);
|
|
1448
|
+
}
|
|
1350
1449
|
|
|
1450
|
+
/**
|
|
1451
|
+
* Cancels the order, recording why.
|
|
1452
|
+
*
|
|
1453
|
+
* The reason becomes the details of the canceled tracking status and is carried on
|
|
1454
|
+
* the OrderCanceled event's activity, which the canceled notifications read.
|
|
1455
|
+
*
|
|
1456
|
+
* @return mixed the result of the OrderCanceled event
|
|
1457
|
+
*/
|
|
1458
|
+
public function cancelWithReason(?string $reason = null)
|
|
1459
|
+
{
|
|
1460
|
+
// The canceled activity comes from the order config, which may define its own code.
|
|
1351
1461
|
$this->loadMissing('orderConfig');
|
|
1352
|
-
$
|
|
1462
|
+
$orderConfig = $this->orderConfig ?? $this->config();
|
|
1463
|
+
$canceledActivity = $orderConfig->getCanceledActivity();
|
|
1464
|
+
|
|
1465
|
+
// A reason given for the cancellation becomes the canceled status details, which
|
|
1466
|
+
// the canceled notifications read. The config's activity itself is left untouched.
|
|
1467
|
+
$reason = is_string($reason) ? trim($reason) : null;
|
|
1468
|
+
$reasonActivity = null;
|
|
1469
|
+
if ($reason !== null && $reason !== '' && $canceledActivity instanceof Activity) {
|
|
1470
|
+
$canceledActivity = $reasonActivity = (clone $canceledActivity)->set('details', $reason);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
$this->status = $canceledActivity->code ?? 'canceled';
|
|
1353
1474
|
$this->updateActivity($canceledActivity);
|
|
1354
1475
|
|
|
1355
1476
|
if ($this->isIntegratedVendorOrder()) {
|
|
@@ -1360,7 +1481,13 @@ class Order extends Model
|
|
|
1360
1481
|
}
|
|
1361
1482
|
}
|
|
1362
1483
|
|
|
1363
|
-
|
|
1484
|
+
// The event carries the activity when a reason was given, so the canceled
|
|
1485
|
+
// notifications (which read the activity details) include it.
|
|
1486
|
+
return dispatch(function () use ($reasonActivity) {
|
|
1487
|
+
$event = new OrderCanceled($this);
|
|
1488
|
+
$event->activity = $reasonActivity;
|
|
1489
|
+
event($event);
|
|
1490
|
+
})->afterCommit();
|
|
1364
1491
|
}
|
|
1365
1492
|
|
|
1366
1493
|
/**
|
|
@@ -1376,6 +1503,52 @@ class Order extends Model
|
|
|
1376
1503
|
}
|
|
1377
1504
|
}
|
|
1378
1505
|
|
|
1506
|
+
/**
|
|
1507
|
+
* Tell a driver the order was taken off them: broadcasts `order.driver_unassigned`
|
|
1508
|
+
* on their channels and pushes `{ type: "order_unassigned", id }` to their devices.
|
|
1509
|
+
* The order is cleared as the driver's current job.
|
|
1510
|
+
*
|
|
1511
|
+
* @param Driver|string|null $driver the driver (or driver uuid) removed from the order
|
|
1512
|
+
*/
|
|
1513
|
+
public function notifyDriverUnassigned($driver): bool
|
|
1514
|
+
{
|
|
1515
|
+
if (is_string($driver)) {
|
|
1516
|
+
$driver = Driver::where('uuid', $driver)->withoutGlobalScopes()->first();
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
if (!$driver instanceof Driver) {
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
if ($driver->current_job_uuid && $driver->current_job_uuid === $this->uuid) {
|
|
1524
|
+
$driver->forceFill(['current_job_uuid' => null])->saveQuietly();
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
try {
|
|
1528
|
+
event(new OrderDriverUnassigned($this, $driver));
|
|
1529
|
+
} catch (\Throwable $exception) {
|
|
1530
|
+
Log::warning('Unable to broadcast order.driver_unassigned', ['order' => $this->public_id, 'error' => $exception->getMessage()]);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (!$this->quietDriverUnassignment) {
|
|
1534
|
+
try {
|
|
1535
|
+
$driver->notify(new OrderUnassigned($this));
|
|
1536
|
+
} catch (\Throwable $exception) {
|
|
1537
|
+
Log::warning('Unable to notify driver of unassignment', ['order' => $this->public_id, 'error' => $exception->getMessage()]);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
return true;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* The edit policy for the order at its current status.
|
|
1546
|
+
*/
|
|
1547
|
+
public function editPolicy(): OrderEditPolicy
|
|
1548
|
+
{
|
|
1549
|
+
return OrderEditPolicy::for($this);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1379
1552
|
/**
|
|
1380
1553
|
* Updates the activity of the order.
|
|
1381
1554
|
* Updates the order status and inserts a new activity based on the provided activity details.
|
|
@@ -1633,7 +1806,7 @@ class Order extends Model
|
|
|
1633
1806
|
return $this;
|
|
1634
1807
|
}
|
|
1635
1808
|
|
|
1636
|
-
return $this->assignDriver($driver);
|
|
1809
|
+
return $this->assignDriver($driver, $silent);
|
|
1637
1810
|
}
|
|
1638
1811
|
|
|
1639
1812
|
throw new \Exception('Invalid driver provided for assignment!');
|
|
@@ -1646,12 +1819,16 @@ class Order extends Model
|
|
|
1646
1819
|
$this->setRelation('driverAssigned', $driver);
|
|
1647
1820
|
}
|
|
1648
1821
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1822
|
+
// The order observer sends OrderDriverAssigned once the assignment is saved
|
|
1823
|
+
// (and tells a replaced driver they were unassigned), so it is not fired here
|
|
1824
|
+
// as well. A silent assignment is flagged for that one save.
|
|
1825
|
+
$this->silentDriverAssignment = (bool) $silent;
|
|
1826
|
+
try {
|
|
1827
|
+
$this->save();
|
|
1828
|
+
} finally {
|
|
1829
|
+
$this->silentDriverAssignment = false;
|
|
1651
1830
|
}
|
|
1652
1831
|
|
|
1653
|
-
$this->save();
|
|
1654
|
-
|
|
1655
1832
|
return $this;
|
|
1656
1833
|
}
|
|
1657
1834
|
|
|
@@ -1671,7 +1848,10 @@ class Order extends Model
|
|
|
1671
1848
|
$driverAssigned = Driver::where('uuid', $this->driver_assigned_uuid)->first();
|
|
1672
1849
|
}
|
|
1673
1850
|
|
|
1674
|
-
|
|
1851
|
+
// A driver that has never reported a position still carries the
|
|
1852
|
+
// POINT(0 0) placeholder it was created with. Measuring from there
|
|
1853
|
+
// puts the origin at null island, so fall back to the pickup.
|
|
1854
|
+
if ($driverAssigned instanceof Driver && static::isKnownPosition($driverAssigned->location)) {
|
|
1675
1855
|
return $driverAssigned->location;
|
|
1676
1856
|
}
|
|
1677
1857
|
}
|
|
@@ -1702,6 +1882,16 @@ class Order extends Model
|
|
|
1702
1882
|
return $destination ? $destination->location : null;
|
|
1703
1883
|
}
|
|
1704
1884
|
|
|
1885
|
+
/**
|
|
1886
|
+
* Determines whether a position is a real, resolved location rather than
|
|
1887
|
+
* missing or the POINT(0 0) placeholder stored for drivers that have not
|
|
1888
|
+
* reported a position yet and for places that failed to geocode.
|
|
1889
|
+
*/
|
|
1890
|
+
public static function isKnownPosition($position): bool
|
|
1891
|
+
{
|
|
1892
|
+
return $position instanceof Point && ($position->getLat() != 0.0 || $position->getLng() != 0.0);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1705
1895
|
/**
|
|
1706
1896
|
* Sets the preliminary distance and time for the order.
|
|
1707
1897
|
* Uses a utility method to calculate the distance and time between the current origin and destination positions.
|
|
@@ -1713,13 +1903,14 @@ class Order extends Model
|
|
|
1713
1903
|
$origin = $this->getCurrentOriginPosition();
|
|
1714
1904
|
$destination = $this->getDestinationPosition();
|
|
1715
1905
|
|
|
1716
|
-
if ($origin
|
|
1906
|
+
if (!static::isKnownPosition($origin) || !static::isKnownPosition($destination)) {
|
|
1717
1907
|
return $this;
|
|
1718
1908
|
}
|
|
1719
1909
|
|
|
1720
1910
|
$matrix = Utils::getPreliminaryDistanceMatrix($origin, $destination);
|
|
1721
1911
|
|
|
1722
1912
|
$this->updateQuietly(['distance' => $matrix->distance, 'time' => $matrix->time]);
|
|
1913
|
+
$this->invalidateApiCache();
|
|
1723
1914
|
|
|
1724
1915
|
return $this;
|
|
1725
1916
|
}
|
|
@@ -1735,7 +1926,7 @@ class Order extends Model
|
|
|
1735
1926
|
$origin = $this->getCurrentOriginPosition();
|
|
1736
1927
|
$destination = $this->getDestinationPosition();
|
|
1737
1928
|
|
|
1738
|
-
if (
|
|
1929
|
+
if (!static::isKnownPosition($origin) || !static::isKnownPosition($destination)) {
|
|
1739
1930
|
return $this;
|
|
1740
1931
|
}
|
|
1741
1932
|
|
|
@@ -1757,6 +1948,7 @@ class Order extends Model
|
|
|
1757
1948
|
'distance' => $matrix->distance,
|
|
1758
1949
|
'time' => $matrix->time,
|
|
1759
1950
|
]);
|
|
1951
|
+
$this->invalidateApiCache();
|
|
1760
1952
|
}
|
|
1761
1953
|
|
|
1762
1954
|
return $this;
|
|
@@ -1783,6 +1975,37 @@ class Order extends Model
|
|
|
1783
1975
|
return (int) Utils::get($this, 'adhoc_distance', Utils::get($this, 'company.options.fleetops.adhoc_distance', 6000));
|
|
1784
1976
|
}
|
|
1785
1977
|
|
|
1978
|
+
/**
|
|
1979
|
+
* Returns the initial tracking status for a newly created order.
|
|
1980
|
+
*
|
|
1981
|
+
* Used by the tracking number on creation. Orders whose config defines its own
|
|
1982
|
+
* lifecycle start at the configured initial activity instead of `created`.
|
|
1983
|
+
*
|
|
1984
|
+
* @return array|null ['code', 'status', 'details'] or null to use the default created status
|
|
1985
|
+
*/
|
|
1986
|
+
public function getInitialTrackingStatus(): ?array
|
|
1987
|
+
{
|
|
1988
|
+
if (!$this->order_config_uuid) {
|
|
1989
|
+
return null;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
$orderConfig = OrderConfig::where('uuid', $this->order_config_uuid)->first();
|
|
1993
|
+
if (!$orderConfig || !$orderConfig->hasConfiguredLifecycle()) {
|
|
1994
|
+
return null;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
$activity = $orderConfig->getInitialActivity();
|
|
1998
|
+
if (!$activity) {
|
|
1999
|
+
return null;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
return [
|
|
2003
|
+
'code' => $activity->code,
|
|
2004
|
+
'status' => $this->resolveActivityTemplateString((string) $activity->get('status', '')),
|
|
2005
|
+
'details' => $this->resolveActivityTemplateString((string) $activity->get('details', '')),
|
|
2006
|
+
];
|
|
2007
|
+
}
|
|
2008
|
+
|
|
1786
2009
|
/**
|
|
1787
2010
|
* Retrieves the OrderConfig associated with this order.
|
|
1788
2011
|
*
|
|
@@ -380,6 +380,125 @@ class OrderConfig extends Model
|
|
|
380
380
|
return $this->activities()->firstWhere('code', $code);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Returns the configured lifecycle semantics stored under `meta.lifecycle`.
|
|
385
|
+
*
|
|
386
|
+
* A configured lifecycle is opt-in. When absent the default dispatch lifecycle
|
|
387
|
+
* (created -> dispatched -> ... -> completed / canceled) applies unchanged.
|
|
388
|
+
*
|
|
389
|
+
* Supported keys:
|
|
390
|
+
* - `initial` (string) activity code new orders start at
|
|
391
|
+
* - `completed` (string) activity code that completes an order
|
|
392
|
+
* - `canceled` (string) activity code used when an order is canceled
|
|
393
|
+
* - `terminal` (string[]) activity codes after which no transition is allowed
|
|
394
|
+
* - `dispatch` (bool) false disables dispatch for orders using this config
|
|
395
|
+
* - `strict_transitions` (bool) true only allows moving to a configured child activity
|
|
396
|
+
*/
|
|
397
|
+
public function lifecycle(): array
|
|
398
|
+
{
|
|
399
|
+
$lifecycle = data_get($this->meta, 'lifecycle');
|
|
400
|
+
|
|
401
|
+
return is_array($lifecycle) ? $lifecycle : [];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Determines if this config defines its own lifecycle semantics.
|
|
406
|
+
*/
|
|
407
|
+
public function hasConfiguredLifecycle(): bool
|
|
408
|
+
{
|
|
409
|
+
return !empty($this->lifecycle());
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Returns the activity new orders using this config start at.
|
|
414
|
+
*
|
|
415
|
+
* Without a configured lifecycle this is the `created` activity, if present in the flow.
|
|
416
|
+
*/
|
|
417
|
+
public function getInitialActivity(): ?Activity
|
|
418
|
+
{
|
|
419
|
+
$initialCode = data_get($this->lifecycle(), 'initial');
|
|
420
|
+
if ($initialCode) {
|
|
421
|
+
return $this->getActivityByCode($initialCode);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return $this->getCreatedActivity();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Returns the status code new orders using this config start at.
|
|
429
|
+
*/
|
|
430
|
+
public function getInitialStatusCode(): string
|
|
431
|
+
{
|
|
432
|
+
return data_get($this->lifecycle(), 'initial') ?: 'created';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Determines if orders using this config may be dispatched.
|
|
437
|
+
*/
|
|
438
|
+
public function allowsDispatch(): bool
|
|
439
|
+
{
|
|
440
|
+
$dispatch = data_get($this->lifecycle(), 'dispatch', true);
|
|
441
|
+
|
|
442
|
+
return !in_array($dispatch, [false, 'false', 0, '0'], true);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Determines if activity updates must follow the configured flow graph.
|
|
447
|
+
*/
|
|
448
|
+
public function hasStrictTransitions(): bool
|
|
449
|
+
{
|
|
450
|
+
return (bool) data_get($this->lifecycle(), 'strict_transitions', false);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Returns the terminal activity codes of this config.
|
|
455
|
+
*/
|
|
456
|
+
public function getTerminalActivityCodes(): array
|
|
457
|
+
{
|
|
458
|
+
if (!$this->hasConfiguredLifecycle()) {
|
|
459
|
+
return ['completed', 'canceled'];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
$terminal = data_get($this->lifecycle(), 'terminal', []);
|
|
463
|
+
$terminal = is_array($terminal) ? $terminal : [];
|
|
464
|
+
foreach (['completed', 'canceled'] as $semantic) {
|
|
465
|
+
$code = data_get($this->lifecycle(), $semantic);
|
|
466
|
+
if ($code) {
|
|
467
|
+
$terminal[] = $code;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return array_values(array_unique($terminal));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Determines if the given activity code is terminal for this config.
|
|
476
|
+
*/
|
|
477
|
+
public function isTerminalActivityCode(?string $code): bool
|
|
478
|
+
{
|
|
479
|
+
return $code !== null && in_array($code, $this->getTerminalActivityCodes(), true);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Determines if the configured flow permits moving from one activity code to another.
|
|
484
|
+
*
|
|
485
|
+
* Only the configured graph is consulted: the target must be listed as a child of the
|
|
486
|
+
* source activity, and terminal activities permit no further transition.
|
|
487
|
+
*/
|
|
488
|
+
public function canTransition(?string $fromCode, string $toCode): bool
|
|
489
|
+
{
|
|
490
|
+
if ($fromCode === null || $this->isTerminalActivityCode($fromCode)) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
$from = $this->getActivityByCode($fromCode);
|
|
495
|
+
if (!$from) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return $from->children()->contains(fn ($activity) => $activity->code === $toCode);
|
|
500
|
+
}
|
|
501
|
+
|
|
383
502
|
/**
|
|
384
503
|
* Creates an Activity instance representing a canceled order.
|
|
385
504
|
*
|
|
@@ -391,6 +510,11 @@ class OrderConfig extends Model
|
|
|
391
510
|
*/
|
|
392
511
|
public function getCanceledActivity()
|
|
393
512
|
{
|
|
513
|
+
$configuredCode = data_get($this->lifecycle(), 'canceled');
|
|
514
|
+
if ($configuredCode && ($configuredActivity = $this->getActivityByCode($configuredCode))) {
|
|
515
|
+
return $configuredActivity;
|
|
516
|
+
}
|
|
517
|
+
|
|
394
518
|
$canceledActivity = $this->activities()->firstWhere('code', 'canceled');
|
|
395
519
|
if ($canceledActivity) {
|
|
396
520
|
return $canceledActivity;
|
|
@@ -416,6 +540,11 @@ class OrderConfig extends Model
|
|
|
416
540
|
*/
|
|
417
541
|
public function getCompletedActivity()
|
|
418
542
|
{
|
|
543
|
+
$configuredCode = data_get($this->lifecycle(), 'completed');
|
|
544
|
+
if ($configuredCode && ($configuredActivity = $this->getActivityByCode($configuredCode))) {
|
|
545
|
+
return $configuredActivity;
|
|
546
|
+
}
|
|
547
|
+
|
|
419
548
|
$completedActivity = $this->activities()->firstWhere('code', 'completed');
|
|
420
549
|
if ($completedActivity) {
|
|
421
550
|
return $completedActivity;
|