@fleetbase/fleetops-engine 0.6.71 → 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 +15 -18
- 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/form/route.hbs +1 -1
- package/addon/components/order/form/route.js +13 -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/operations/orders/index/details.js +19 -3
- package/addon/controllers/operations/scheduler/index.js +45 -14
- 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 +65 -1
- package/addon/helpers/parcel-box-image.js +16 -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/location.js +69 -21
- package/addon/services/map-settings.js +28 -0
- package/addon/services/order-allocation.js +1 -1
- package/addon/styles/fleetops-engine.css +1391 -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/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/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 +84 -24
- 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 +200 -9
- 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 +101 -2
- 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/OrderControllerContractsTest.php +110 -14
- 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/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/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 +287 -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
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Jobs\PublishTrackingUpdate;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
7
|
+
use Fleetbase\FleetOps\Models\Entity;
|
|
8
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
9
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
10
|
+
use Fleetbase\FleetOps\Models\Waypoint;
|
|
11
|
+
use Fleetbase\FleetOps\Tracking\TrackingContextBuilder;
|
|
12
|
+
use Fleetbase\FleetOps\Tracking\TrackingIntelligenceService;
|
|
13
|
+
use Fleetbase\FleetOps\Tracking\TrackingOptions;
|
|
14
|
+
use Fleetbase\FleetOps\Tracking\TrackingStop;
|
|
15
|
+
use Fleetbase\Support\SocketCluster\SocketClusterService;
|
|
16
|
+
use Fleetbase\Support\SocketCluster\SocketToken;
|
|
17
|
+
use Illuminate\Support\Collection;
|
|
18
|
+
use Illuminate\Support\Facades\Cache;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Publishes per-customer updates to public tracking channels.
|
|
22
|
+
*
|
|
23
|
+
* The request-path hooks (orderChanged, stopChanged, driverMoved, vehicleMoved) do almost
|
|
24
|
+
* nothing: they bail out when socket authentication is off, throttle location changes through
|
|
25
|
+
* the cache, and queue a PublishTrackingUpdate job. The job builds and publishes the updates.
|
|
26
|
+
*/
|
|
27
|
+
class TrackingPublisher
|
|
28
|
+
{
|
|
29
|
+
/**
|
|
30
|
+
* Seconds between position updates for one order (and between order lookups for one driver or vehicle).
|
|
31
|
+
*/
|
|
32
|
+
public const LOCATION_INTERVAL = 5;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Seconds between ETA refreshes for one order driven by position changes.
|
|
36
|
+
*/
|
|
37
|
+
public const ETA_INTERVAL = 60;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Most active orders a single driver or vehicle position change fans out to.
|
|
41
|
+
*/
|
|
42
|
+
public const MAX_ORDERS_PER_SUBJECT = 25;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Order attributes whose change is published, with the reason reported.
|
|
46
|
+
*/
|
|
47
|
+
public const ORDER_ATTRIBUTES = [
|
|
48
|
+
'status' => TrackingUpdate::REASON_STATUS,
|
|
49
|
+
'started' => TrackingUpdate::REASON_STATUS,
|
|
50
|
+
'dispatched' => TrackingUpdate::REASON_STATUS,
|
|
51
|
+
'driver_assigned_uuid' => TrackingUpdate::REASON_ASSIGNMENT,
|
|
52
|
+
'vehicle_assigned_uuid' => TrackingUpdate::REASON_ASSIGNMENT,
|
|
53
|
+
'time' => TrackingUpdate::REASON_ETA,
|
|
54
|
+
'distance' => TrackingUpdate::REASON_ETA,
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
public static function enabled(): bool
|
|
58
|
+
{
|
|
59
|
+
return SocketToken::enabled();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* An order was saved: queue an update when a published attribute changed.
|
|
64
|
+
*/
|
|
65
|
+
public function orderChanged(Order $order): bool
|
|
66
|
+
{
|
|
67
|
+
if (!static::enabled() || !$order->uuid) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
$changed = array_intersect_key(self::ORDER_ATTRIBUTES, $order->getChanges());
|
|
72
|
+
if ($changed === []) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$reasons = array_values($changed);
|
|
77
|
+
$reason = in_array(TrackingUpdate::REASON_STATUS, $reasons, true) ? TrackingUpdate::REASON_STATUS : $reasons[0];
|
|
78
|
+
|
|
79
|
+
return $this->queue($order->uuid, $reason, $order->getConnectionName());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* An order's stops were edited: queue an update so customers see the new route and ETAs.
|
|
84
|
+
*/
|
|
85
|
+
public function routeChanged(Order $order): bool
|
|
86
|
+
{
|
|
87
|
+
if (!static::enabled() || !$order->uuid) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return $this->queue($order->uuid, TrackingUpdate::REASON_ROUTE, $order->getConnectionName());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A waypoint or entity activity changed: queue an update for its order.
|
|
96
|
+
*/
|
|
97
|
+
public function stopChanged(Waypoint|Entity|null $subject): bool
|
|
98
|
+
{
|
|
99
|
+
if (!static::enabled() || !$subject || !$subject->payload_uuid) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
$connection = $subject->getConnectionName();
|
|
104
|
+
$orderUuid = Order::on($connection)->where('payload_uuid', $subject->payload_uuid)->value('uuid');
|
|
105
|
+
|
|
106
|
+
return $orderUuid ? $this->queue($orderUuid, TrackingUpdate::REASON_STOP, $connection) : false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A driver reported a position: queue a location update for each of their en-route orders.
|
|
111
|
+
*/
|
|
112
|
+
public function driverMoved(Driver $driver): int
|
|
113
|
+
{
|
|
114
|
+
if (!static::enabled() || !$driver->uuid || !Cache::add('fleetops:tracking:driver:' . $driver->uuid, 1, self::LOCATION_INTERVAL)) {
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
$connection = $driver->getConnectionName();
|
|
119
|
+
|
|
120
|
+
return $this->queueLocations($this->enRouteOrders($connection)->where('driver_assigned_uuid', $driver->uuid)->limit(self::MAX_ORDERS_PER_SUBJECT)->pluck('uuid'), $connection);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A vehicle reported a position: queue a location update for each en-route order it serves.
|
|
125
|
+
*/
|
|
126
|
+
public function vehicleMoved(Vehicle $vehicle): int
|
|
127
|
+
{
|
|
128
|
+
if (!static::enabled() || !$vehicle->uuid || !Cache::add('fleetops:tracking:vehicle:' . $vehicle->uuid, 1, self::LOCATION_INTERVAL)) {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
$connection = $vehicle->getConnectionName();
|
|
133
|
+
$drivers = Driver::on($connection)->where('vehicle_uuid', $vehicle->uuid)->select('uuid')->toBase();
|
|
134
|
+
$orders = $this->enRouteOrders($connection)
|
|
135
|
+
->where(function ($query) use ($vehicle, $drivers) {
|
|
136
|
+
$query->where('vehicle_assigned_uuid', $vehicle->uuid)->orWhereIn('driver_assigned_uuid', $drivers);
|
|
137
|
+
})
|
|
138
|
+
->limit(self::MAX_ORDERS_PER_SUBJECT)
|
|
139
|
+
->pluck('uuid');
|
|
140
|
+
|
|
141
|
+
return $this->queueLocations($orders, $connection);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Publish the full update to every customer of the order. Returns the channels published to.
|
|
146
|
+
*/
|
|
147
|
+
public function publishOrder(Order $order, string $reason = TrackingUpdate::REASON_STATUS): int
|
|
148
|
+
{
|
|
149
|
+
if (!static::enabled()) {
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
$stops = $this->stopsFor($order);
|
|
154
|
+
$tracker = $this->trackerFor($order);
|
|
155
|
+
$sent = 0;
|
|
156
|
+
|
|
157
|
+
foreach (TrackingScope::allForOrder($order, $this->waypointsOf($stops)) as $scope) {
|
|
158
|
+
$update = TrackingUpdate::for($order, $scope, $stops, $tracker);
|
|
159
|
+
$sent += (int) $this->send(TrackingChannel::name($scope), $update->toArray($reason));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return $sent;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Publish the position to every customer of the order who may see it. Returns the channels published to.
|
|
167
|
+
*/
|
|
168
|
+
public function publishLocation(Order $order): int
|
|
169
|
+
{
|
|
170
|
+
if (!static::enabled()) {
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
$stops = $this->stopsFor($order);
|
|
175
|
+
$sent = 0;
|
|
176
|
+
|
|
177
|
+
foreach (TrackingScope::allForOrder($order, $this->waypointsOf($stops)) as $scope) {
|
|
178
|
+
$payload = (new TrackingUpdate($order, $scope, $stops))->locationPayload();
|
|
179
|
+
if ($payload !== null) {
|
|
180
|
+
$sent += (int) $this->send(TrackingChannel::name($scope), $payload);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return $sent;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Every stop of the order in route order, with completion state.
|
|
189
|
+
*
|
|
190
|
+
* @return Collection<int, TrackingStop>
|
|
191
|
+
*/
|
|
192
|
+
protected function stopsFor(Order $order): Collection
|
|
193
|
+
{
|
|
194
|
+
return app(TrackingContextBuilder::class)->build($order, TrackingOptions::fromArray([], $order->company_uuid))->stops;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The tracking intelligence result for the order's ETAs; empty when it cannot be computed.
|
|
199
|
+
*/
|
|
200
|
+
protected function trackerFor(Order $order): array
|
|
201
|
+
{
|
|
202
|
+
if (in_array(strtolower((string) $order->status), TrackingUpdate::TERMINAL_STATUSES, true)) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
return app(TrackingIntelligenceService::class)->track($order);
|
|
208
|
+
} catch (\Throwable) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
protected function send(string $channel, array $payload): bool
|
|
214
|
+
{
|
|
215
|
+
return SocketClusterService::publish($channel, $payload);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected function queue(string $orderUuid, string $reason, ?string $connection = null): bool
|
|
219
|
+
{
|
|
220
|
+
PublishTrackingUpdate::dispatch($orderUuid, $reason, $connection);
|
|
221
|
+
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Queue a location update for each order not updated within the location interval.
|
|
227
|
+
*/
|
|
228
|
+
protected function queueLocations(iterable $orderUuids, ?string $connection): int
|
|
229
|
+
{
|
|
230
|
+
$queued = 0;
|
|
231
|
+
foreach ($orderUuids as $orderUuid) {
|
|
232
|
+
if (Cache::add('fleetops:tracking:location:' . $orderUuid, 1, self::LOCATION_INTERVAL)) {
|
|
233
|
+
$queued += (int) $this->queue($orderUuid, TrackingUpdate::REASON_LOCATION, $connection);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return $queued;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Orders that have started and are not yet completed or canceled.
|
|
242
|
+
*/
|
|
243
|
+
protected function enRouteOrders(?string $connection)
|
|
244
|
+
{
|
|
245
|
+
return Order::on($connection)
|
|
246
|
+
->whereNotIn('status', TrackingUpdate::TERMINAL_STATUSES)
|
|
247
|
+
->where(function ($query) {
|
|
248
|
+
$query->where('started', 1)->orWhereNotNull('started_at')->orWhere('status', 'started');
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param Collection<int, TrackingStop> $stops
|
|
254
|
+
*
|
|
255
|
+
* @return array<int, Waypoint>
|
|
256
|
+
*/
|
|
257
|
+
protected function waypointsOf(Collection $stops): array
|
|
258
|
+
{
|
|
259
|
+
return $stops->map(fn (TrackingStop $stop) => $stop->waypoint)->filter()->values()->all();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Waypoint;
|
|
7
|
+
use Illuminate\Database\Eloquent\Model;
|
|
8
|
+
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* One customer's view of one order, the unit a public tracking channel is scoped to.
|
|
12
|
+
*
|
|
13
|
+
* A stop's customer is the waypoint's `customer` morph (a Contact or a Vendor); stops without
|
|
14
|
+
* one belong to the order's customer. Every customer of an order therefore gets their own
|
|
15
|
+
* scope, their own `tracking.{opaque}` channel and their own sanitized updates.
|
|
16
|
+
*
|
|
17
|
+
* Identity is (order_uuid, customer_type, customer_uuid). customer_type is the morph class as
|
|
18
|
+
* stored on the order or waypoint (a fully qualified model class), normalized without a
|
|
19
|
+
* leading backslash so that every caller derives the same channel.
|
|
20
|
+
*/
|
|
21
|
+
final class TrackingScope implements \JsonSerializable
|
|
22
|
+
{
|
|
23
|
+
public readonly string $order_uuid;
|
|
24
|
+
|
|
25
|
+
public readonly string $customer_type;
|
|
26
|
+
|
|
27
|
+
public readonly string $customer_uuid;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The order's company, when known. Not part of the scope's identity.
|
|
31
|
+
*/
|
|
32
|
+
public readonly ?string $company_uuid;
|
|
33
|
+
|
|
34
|
+
private ?Order $order;
|
|
35
|
+
|
|
36
|
+
public function __construct(string $orderUuid, string $customerType, string $customerUuid, ?Order $order = null)
|
|
37
|
+
{
|
|
38
|
+
$customerType = static::normalizeType($customerType);
|
|
39
|
+
|
|
40
|
+
if ($orderUuid === '' || $customerType === '' || $customerUuid === '') {
|
|
41
|
+
throw new \InvalidArgumentException('A tracking scope requires an order, a customer type and a customer.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
$this->order_uuid = $orderUuid;
|
|
45
|
+
$this->customer_type = $customerType;
|
|
46
|
+
$this->customer_uuid = $customerUuid;
|
|
47
|
+
$this->order = $order && $order->uuid === $orderUuid ? $order : null;
|
|
48
|
+
$this->company_uuid = $this->order?->company_uuid ?: null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The scope of the order's own customer, or null when the order has none.
|
|
53
|
+
*/
|
|
54
|
+
public static function forOrder(Order $order): ?self
|
|
55
|
+
{
|
|
56
|
+
return static::make($order, $order->customer_type, $order->customer_uuid);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The scope a stop belongs to: the waypoint's customer, else the order's customer.
|
|
61
|
+
*/
|
|
62
|
+
public static function forStop(Order $order, ?Waypoint $waypoint = null): ?self
|
|
63
|
+
{
|
|
64
|
+
return static::make($order, $waypoint?->customer_type, $waypoint?->customer_uuid) ?? static::forOrder($order);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The scope of an explicit customer on an order.
|
|
69
|
+
*/
|
|
70
|
+
public static function forCustomer(Order $order, Model $customer): self
|
|
71
|
+
{
|
|
72
|
+
return new self((string) $order->uuid, $customer->getMorphClass(), (string) $customer->uuid, $order);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Every customer scope of an order: the order's customer first, then each distinct waypoint customer.
|
|
77
|
+
*
|
|
78
|
+
* @param iterable<Waypoint>|null $waypoints the order's waypoints; loaded from the payload when null
|
|
79
|
+
*
|
|
80
|
+
* @return array<int, self>
|
|
81
|
+
*/
|
|
82
|
+
public static function allForOrder(Order $order, ?iterable $waypoints = null): array
|
|
83
|
+
{
|
|
84
|
+
if ($waypoints === null) {
|
|
85
|
+
$waypoints = $order->payload_uuid ? Waypoint::on($order->getConnectionName())->where('payload_uuid', $order->payload_uuid)->without(['place'])->get(['uuid', 'payload_uuid', 'customer_uuid', 'customer_type']) : [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
$scopes = [];
|
|
89
|
+
if ($scope = static::forOrder($order)) {
|
|
90
|
+
$scopes[$scope->key()] = $scope;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
foreach ($waypoints as $waypoint) {
|
|
94
|
+
$scope = $waypoint instanceof Waypoint ? static::make($order, $waypoint->customer_type, $waypoint->customer_uuid) : null;
|
|
95
|
+
if ($scope && !isset($scopes[$scope->key()])) {
|
|
96
|
+
$scopes[$scope->key()] = $scope;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return array_values($scopes);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The message the channel id is derived from: "{order_uuid}:{customer_type}:{customer_uuid}".
|
|
105
|
+
*/
|
|
106
|
+
public function key(): string
|
|
107
|
+
{
|
|
108
|
+
return $this->order_uuid . ':' . $this->customer_type . ':' . $this->customer_uuid;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether this scope is the given customer (type and uuid).
|
|
113
|
+
*/
|
|
114
|
+
public function is(?string $customerType, ?string $customerUuid): bool
|
|
115
|
+
{
|
|
116
|
+
return $customerUuid !== null
|
|
117
|
+
&& $customerUuid !== ''
|
|
118
|
+
&& $customerUuid === $this->customer_uuid
|
|
119
|
+
&& static::normalizeType((string) $customerType) === $this->customer_type;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether this scope is the order's own customer.
|
|
124
|
+
*/
|
|
125
|
+
public function isOrderCustomer(Order $order): bool
|
|
126
|
+
{
|
|
127
|
+
return $order->uuid === $this->order_uuid && $this->is($order->customer_type, $order->customer_uuid);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether a stop belongs to this scope: its waypoint's customer, else the order's customer.
|
|
132
|
+
*/
|
|
133
|
+
public function ownsStop(Order $order, ?Waypoint $waypoint = null): bool
|
|
134
|
+
{
|
|
135
|
+
if ($waypoint && $waypoint->customer_uuid && $waypoint->customer_type) {
|
|
136
|
+
return $this->is($waypoint->customer_type, $waypoint->customer_uuid);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return $this->isOrderCustomer($order);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The order this scope is about, loaded once when not given.
|
|
144
|
+
*/
|
|
145
|
+
public function order(): ?Order
|
|
146
|
+
{
|
|
147
|
+
return $this->order ??= Order::where('uuid', $this->order_uuid)->first();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public function toArray(): array
|
|
151
|
+
{
|
|
152
|
+
return [
|
|
153
|
+
'order_uuid' => $this->order_uuid,
|
|
154
|
+
'customer_type' => $this->customer_type,
|
|
155
|
+
'customer_uuid' => $this->customer_uuid,
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public function jsonSerialize(): array
|
|
160
|
+
{
|
|
161
|
+
return $this->toArray();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A morph type as a model class without a leading backslash; morph map aliases resolve to their class.
|
|
166
|
+
*/
|
|
167
|
+
public static function normalizeType(string $type): string
|
|
168
|
+
{
|
|
169
|
+
$type = ltrim(trim($type), '\\');
|
|
170
|
+
|
|
171
|
+
return ltrim((string) (Relation::getMorphedModel($type) ?? $type), '\\');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private static function make(Order $order, ?string $customerType, ?string $customerUuid): ?self
|
|
175
|
+
{
|
|
176
|
+
if (!$order->uuid || !$customerType || !$customerUuid) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return new self((string) $order->uuid, $customerType, $customerUuid, $order);
|
|
181
|
+
}
|
|
182
|
+
}
|