@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
|
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
|
5
5
|
use Fleetbase\FleetOps\Events\OrderDispatchFailed;
|
|
6
6
|
use Fleetbase\FleetOps\Events\OrderStarted;
|
|
7
7
|
use Fleetbase\FleetOps\Exceptions\CustomerUserConflictException;
|
|
8
|
+
use Fleetbase\FleetOps\Exceptions\OrderEditLockedException;
|
|
8
9
|
use Fleetbase\FleetOps\Exceptions\UserAlreadyExistsException;
|
|
9
10
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
10
11
|
use Fleetbase\FleetOps\Http\Requests\CreateOrderRequest;
|
|
@@ -24,7 +25,9 @@ use Fleetbase\FleetOps\Models\Place;
|
|
|
24
25
|
use Fleetbase\FleetOps\Models\Proof;
|
|
25
26
|
use Fleetbase\FleetOps\Models\ServiceQuote;
|
|
26
27
|
use Fleetbase\FleetOps\Models\Waypoint;
|
|
28
|
+
use Fleetbase\FleetOps\Support\OrderEdit;
|
|
27
29
|
use Fleetbase\FleetOps\Support\ResolvesOrderServiceStops;
|
|
30
|
+
use Fleetbase\FleetOps\Support\TrackingCode;
|
|
28
31
|
use Fleetbase\FleetOps\Support\Utils;
|
|
29
32
|
use Fleetbase\Http\Controllers\Controller;
|
|
30
33
|
use Fleetbase\Http\Resources\Comment as CommentResource;
|
|
@@ -352,6 +355,19 @@ class OrderController extends Controller
|
|
|
352
355
|
// get request input
|
|
353
356
|
$input = $this->orderUpdateInputFromRequest($request);
|
|
354
357
|
|
|
358
|
+
// `entities_mode=replace` makes the entities sent the payload's full list
|
|
359
|
+
// (update by id, create new, delete missing); by default they are appended
|
|
360
|
+
$entitiesMode = OrderEdit::entitiesMode($request->input('entities_mode', $request->input('payload.entities_mode')));
|
|
361
|
+
$codAmount = $this->codAmountFromRequest($request);
|
|
362
|
+
|
|
363
|
+
// refuse changes the order's status locks before anything is written
|
|
364
|
+
$edit = $this->beginOrderEdit($order);
|
|
365
|
+
try {
|
|
366
|
+
$edit->guard($edit->requestedChanges($this->orderEditInputFromRequest($request, $codAmount), $entitiesMode));
|
|
367
|
+
} catch (OrderEditLockedException $exception) {
|
|
368
|
+
return $this->lockedEditResponse($exception);
|
|
369
|
+
}
|
|
370
|
+
|
|
355
371
|
// update payload if new input or change payload by id
|
|
356
372
|
if ($request->isArray('payload')) {
|
|
357
373
|
$payload = data_get($order, 'payload', $this->newPayload());
|
|
@@ -383,6 +399,10 @@ class OrderController extends Controller
|
|
|
383
399
|
$payload->setReturn($return);
|
|
384
400
|
}
|
|
385
401
|
|
|
402
|
+
if ($codAmount !== null) {
|
|
403
|
+
$payload->cod_amount = $codAmount;
|
|
404
|
+
}
|
|
405
|
+
|
|
386
406
|
$payload->save();
|
|
387
407
|
|
|
388
408
|
// set waypoints and entities after payload is saved
|
|
@@ -392,14 +412,10 @@ class OrderController extends Controller
|
|
|
392
412
|
$payload->removeWaypoints();
|
|
393
413
|
}
|
|
394
414
|
|
|
395
|
-
|
|
396
|
-
$payload->setEntities($entities);
|
|
397
|
-
}
|
|
415
|
+
$this->applyPayloadEntities($payload, $entities, $entitiesMode);
|
|
398
416
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
$payload->setCurrentWaypoint($firstWaypoint);
|
|
402
|
-
}
|
|
417
|
+
// the current waypoint is reconciled once the edit is committed: it is only
|
|
418
|
+
// moved when the route changed, and an order under way keeps its stop
|
|
403
419
|
|
|
404
420
|
$input['payload_uuid'] = $payload->uuid;
|
|
405
421
|
} elseif ($request->has('payload')) {
|
|
@@ -408,6 +424,11 @@ class OrderController extends Controller
|
|
|
408
424
|
'company_uuid' => $this->sessionCompany(),
|
|
409
425
|
]);
|
|
410
426
|
unset($input['payload']);
|
|
427
|
+
|
|
428
|
+
if ($codAmount !== null && is_string($input['payload_uuid']) && ($switchedPayload = $this->findPayloadByUuid($input['payload_uuid']))) {
|
|
429
|
+
$switchedPayload->cod_amount = $codAmount;
|
|
430
|
+
$switchedPayload->save();
|
|
431
|
+
}
|
|
411
432
|
}
|
|
412
433
|
|
|
413
434
|
// create a payload if missing payload[] but has pickup/dropoff/etc
|
|
@@ -441,6 +462,10 @@ class OrderController extends Controller
|
|
|
441
462
|
$payload->setReturn($return);
|
|
442
463
|
}
|
|
443
464
|
|
|
465
|
+
if ($codAmount !== null) {
|
|
466
|
+
$payload->cod_amount = $codAmount;
|
|
467
|
+
}
|
|
468
|
+
|
|
444
469
|
$payload->save();
|
|
445
470
|
|
|
446
471
|
// set waypoints and entities after payload is saved
|
|
@@ -450,14 +475,10 @@ class OrderController extends Controller
|
|
|
450
475
|
$payload->removeWaypoints();
|
|
451
476
|
}
|
|
452
477
|
|
|
453
|
-
|
|
454
|
-
$payload->setEntities($entities);
|
|
455
|
-
}
|
|
478
|
+
$this->applyPayloadEntities($payload, $entities, $entitiesMode);
|
|
456
479
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
$payload->setCurrentWaypoint($firstWaypoint);
|
|
460
|
-
}
|
|
480
|
+
// the current waypoint is reconciled once the edit is committed: it is only
|
|
481
|
+
// moved when the route changed, and an order under way keeps its stop
|
|
461
482
|
|
|
462
483
|
$input['payload_uuid'] = $payload->uuid;
|
|
463
484
|
}
|
|
@@ -537,6 +558,10 @@ class OrderController extends Controller
|
|
|
537
558
|
$order->update($input);
|
|
538
559
|
$order->flushAttributesCache();
|
|
539
560
|
|
|
561
|
+
// reroute, recompute distance and time, republish tracking and announce
|
|
562
|
+
// `order.changed` for whatever this update changed
|
|
563
|
+
$this->commitOrderEdit($edit);
|
|
564
|
+
|
|
540
565
|
// load required relations
|
|
541
566
|
$order->load(['trackingNumber', 'trackingStatuses', 'driverAssigned', 'vehicleAssigned', 'purchaseRate.serviceQuote.items', 'customer', 'facilitator']);
|
|
542
567
|
|
|
@@ -544,6 +569,79 @@ class OrderController extends Controller
|
|
|
544
569
|
return $this->orderResource($order);
|
|
545
570
|
}
|
|
546
571
|
|
|
572
|
+
/**
|
|
573
|
+
* The cash to collect sent with an order update, as `payload.cod_amount` or a
|
|
574
|
+
* top-level `cod_amount`. Zero is a valid amount; anything not numeric is ignored.
|
|
575
|
+
*/
|
|
576
|
+
protected function codAmountFromRequest(Request $request): int|float|string|null
|
|
577
|
+
{
|
|
578
|
+
$value = null;
|
|
579
|
+
if ($request->isArray('payload') && Arr::has((array) $request->input('payload'), 'cod_amount')) {
|
|
580
|
+
$value = data_get($request->input('payload'), 'cod_amount');
|
|
581
|
+
} elseif ($request->has('cod_amount')) {
|
|
582
|
+
$value = $request->input('cod_amount');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return is_numeric($value) ? $value : null;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* The parts of an order update the edit policy looks at.
|
|
590
|
+
*/
|
|
591
|
+
protected function orderEditInputFromRequest(Request $request, mixed $codAmount = null): array
|
|
592
|
+
{
|
|
593
|
+
$routeKeys = ['pickup', 'dropoff', 'return', 'waypoints', 'entities'];
|
|
594
|
+
|
|
595
|
+
if ($request->isArray('payload')) {
|
|
596
|
+
$edit = Arr::only((array) $request->input('payload'), $routeKeys);
|
|
597
|
+
} elseif ($request->has('payload')) {
|
|
598
|
+
// switching to another payload replaces the whole route
|
|
599
|
+
$edit = ['pickup' => true, 'dropoff' => true, 'waypoints' => true];
|
|
600
|
+
} else {
|
|
601
|
+
$edit = $request->only($routeKeys);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
foreach (['scheduled_at', 'notes', 'customer'] as $key) {
|
|
605
|
+
if ($request->has($key)) {
|
|
606
|
+
$edit[$key] = $request->input($key);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if ($codAmount !== null) {
|
|
611
|
+
$edit['cod_amount'] = $codAmount;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return $edit;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
protected function applyPayloadEntities(Payload $payload, mixed $entities, string $mode): void
|
|
618
|
+
{
|
|
619
|
+
if ($mode === OrderEdit::ENTITIES_REPLACE && is_array($entities)) {
|
|
620
|
+
$payload->syncEntities($entities);
|
|
621
|
+
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if ($entities) {
|
|
626
|
+
$payload->setEntities($entities);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
protected function beginOrderEdit(Order $order): OrderEdit
|
|
631
|
+
{
|
|
632
|
+
return OrderEdit::begin($order);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
protected function commitOrderEdit(OrderEdit $edit): array
|
|
636
|
+
{
|
|
637
|
+
return $edit->commit();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
protected function lockedEditResponse(OrderEditLockedException $exception)
|
|
641
|
+
{
|
|
642
|
+
return response()->apiError($exception->getMessage(), 422, $exception->toResponseData());
|
|
643
|
+
}
|
|
644
|
+
|
|
547
645
|
protected function orderCreateInputFromRequest(Request $request): array
|
|
548
646
|
{
|
|
549
647
|
return $request->only([
|
|
@@ -631,24 +729,6 @@ class OrderController extends Controller
|
|
|
631
729
|
});
|
|
632
730
|
}
|
|
633
731
|
|
|
634
|
-
if ($request->has('facilitator')) {
|
|
635
|
-
$query->whereHas('facilitator', function ($q) use ($request) {
|
|
636
|
-
$q->where(function ($q) use ($request) {
|
|
637
|
-
$q->where('public_id', $request->input('facilitator'));
|
|
638
|
-
$q->orWhere('internal_id', $request->input('facilitator'));
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
if ($request->has('customer')) {
|
|
644
|
-
$query->whereHas('customer', function ($q) use ($request) {
|
|
645
|
-
$q->where(function ($q) use ($request) {
|
|
646
|
-
$q->where('public_id', $request->input('customer'));
|
|
647
|
-
$q->orWhere('internal_id', $request->input('customer'));
|
|
648
|
-
});
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
|
|
652
732
|
if ($request->has('entity')) {
|
|
653
733
|
$query->whereHas('payload.entities', function ($q) use ($request) {
|
|
654
734
|
$q->where(function ($q) use ($request) {
|
|
@@ -1296,8 +1376,12 @@ class OrderController extends Controller
|
|
|
1296
1376
|
*
|
|
1297
1377
|
* @return \Fleetbase\Http\Resources\v1\Order
|
|
1298
1378
|
*/
|
|
1299
|
-
public function cancelOrder(string $id)
|
|
1379
|
+
public function cancelOrder(string $id, ?Request $request = null)
|
|
1300
1380
|
{
|
|
1381
|
+
// Laravel does not inject a class-typed parameter that has a default, so the
|
|
1382
|
+
// current request is read here; the default keeps cancelOrder($id) callable.
|
|
1383
|
+
$request = $request ?? request();
|
|
1384
|
+
|
|
1301
1385
|
try {
|
|
1302
1386
|
$order = $this->findOrder($id);
|
|
1303
1387
|
} catch (ModelNotFoundException $exception) {
|
|
@@ -1309,11 +1393,30 @@ class OrderController extends Controller
|
|
|
1309
1393
|
);
|
|
1310
1394
|
}
|
|
1311
1395
|
|
|
1312
|
-
|
|
1396
|
+
// the reason becomes the canceled status details and is sent with the canceled notifications
|
|
1397
|
+
$reason = $this->reasonFromRequest($request);
|
|
1398
|
+
if ($reason !== null) {
|
|
1399
|
+
$order->cancelWithReason($reason);
|
|
1400
|
+
} else {
|
|
1401
|
+
$order->cancel();
|
|
1402
|
+
}
|
|
1313
1403
|
|
|
1314
1404
|
return $this->orderResource($order);
|
|
1315
1405
|
}
|
|
1316
1406
|
|
|
1407
|
+
/**
|
|
1408
|
+
* The free-text `reason` sent with a cancel, trimmed to 1000 characters.
|
|
1409
|
+
*/
|
|
1410
|
+
protected function reasonFromRequest(Request $request): ?string
|
|
1411
|
+
{
|
|
1412
|
+
$reason = $request->input('reason');
|
|
1413
|
+
if (!is_string($reason) || trim($reason) === '') {
|
|
1414
|
+
return null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return Str::limit(trim($reason), 1000, '');
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1317
1420
|
/**
|
|
1318
1421
|
* Mark a specific place (waypoint, pickup, or drop-off) as the current
|
|
1319
1422
|
* destination for an order payload.
|
|
@@ -1444,8 +1547,8 @@ class OrderController extends Controller
|
|
|
1444
1547
|
return $this->apiError('Unable to capture QR code data.');
|
|
1445
1548
|
}
|
|
1446
1549
|
|
|
1447
|
-
// validate
|
|
1448
|
-
if ($
|
|
1550
|
+
// validate: the scan must name this subject, in any code format still in circulation
|
|
1551
|
+
if (TrackingCode::matches($code, $subject)) {
|
|
1449
1552
|
// create verification proof
|
|
1450
1553
|
$proof = $this->createProof([
|
|
1451
1554
|
'company_uuid' => session('company'),
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Exceptions\OrderEditLockedException;
|
|
5
6
|
use Fleetbase\FleetOps\Http\Requests\CreatePayloadRequest;
|
|
6
7
|
use Fleetbase\FleetOps\Http\Requests\UpdatePayloadRequest;
|
|
7
8
|
use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
|
|
8
9
|
use Fleetbase\FleetOps\Http\Resources\v1\Payload as PayloadResource;
|
|
10
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
9
11
|
use Fleetbase\FleetOps\Models\Payload;
|
|
10
12
|
use Fleetbase\FleetOps\Models\Place;
|
|
13
|
+
use Fleetbase\FleetOps\Support\OrderEdit;
|
|
11
14
|
use Fleetbase\Http\Controllers\Controller;
|
|
12
15
|
use Illuminate\Http\Request;
|
|
13
16
|
use Illuminate\Support\Arr;
|
|
@@ -103,6 +106,18 @@ class PayloadController extends Controller
|
|
|
103
106
|
$return = $routeShape['return'];
|
|
104
107
|
$hasWaypointsField = $routeShape['has_waypoints_field'];
|
|
105
108
|
$hasRouteEndpointFields = $routeShape['has_route_endpoint_fields'];
|
|
109
|
+
$entitiesMode = OrderEdit::entitiesMode($input['entities_mode'] ?? null);
|
|
110
|
+
|
|
111
|
+
// when the payload belongs to an order, refuse changes the order's status locks
|
|
112
|
+
$order = $this->orderForPayload($payload);
|
|
113
|
+
$edit = $order ? $this->beginOrderEdit($order) : null;
|
|
114
|
+
if ($edit) {
|
|
115
|
+
try {
|
|
116
|
+
$edit->guard($edit->requestedChanges(Arr::only($input, ['pickup', 'dropoff', 'return', 'waypoints', 'entities', 'cod_amount']), $entitiesMode));
|
|
117
|
+
} catch (OrderEditLockedException $exception) {
|
|
118
|
+
return $this->lockedEditResponse($exception);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
106
121
|
|
|
107
122
|
// pickup assignment
|
|
108
123
|
if ($pickup) {
|
|
@@ -126,21 +141,29 @@ class PayloadController extends Controller
|
|
|
126
141
|
$payload->removeWaypoints();
|
|
127
142
|
}
|
|
128
143
|
|
|
129
|
-
// set entities
|
|
130
|
-
if ($entities) {
|
|
144
|
+
// set entities: appended, or with `entities_mode=replace` the full list
|
|
145
|
+
if ($entitiesMode === OrderEdit::ENTITIES_REPLACE && is_array($entities)) {
|
|
146
|
+
$payload->syncEntities($entities);
|
|
147
|
+
} elseif ($entities) {
|
|
131
148
|
$payload->setEntities($entities);
|
|
132
149
|
}
|
|
133
150
|
|
|
134
|
-
// update the payload
|
|
135
|
-
$payload->fill(
|
|
151
|
+
// update the payload; empty values are skipped, but a cash-to-collect of 0 is kept
|
|
152
|
+
$payload->fill($this->payloadUpdateFillInput($input));
|
|
136
153
|
|
|
137
154
|
// save the payload
|
|
138
155
|
$payload->save();
|
|
139
156
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
$
|
|
157
|
+
if ($edit) {
|
|
158
|
+
// the order's edit moves the current waypoint only when the route changed, keeps
|
|
159
|
+
// an order under way on its stop and announces the change
|
|
160
|
+
$this->commitOrderEdit($edit);
|
|
161
|
+
} else {
|
|
162
|
+
// set the first / current waypoint from the effective route shape
|
|
163
|
+
$firstWaypoint = $payload->getPickupOrFirstWaypoint();
|
|
164
|
+
if ($firstWaypoint instanceof Place) {
|
|
165
|
+
$payload->setCurrentWaypoint($firstWaypoint);
|
|
166
|
+
}
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
// make sure entities and waypoints is loaded
|
|
@@ -237,6 +260,51 @@ class PayloadController extends Controller
|
|
|
237
260
|
return Arr::only($input, ['type', 'provider', 'meta', 'cod_amount', 'cod_currency', 'cod_payment_method']);
|
|
238
261
|
}
|
|
239
262
|
|
|
263
|
+
/**
|
|
264
|
+
* The payload attributes an update fills: empty values are skipped as before,
|
|
265
|
+
* except a numeric cash-to-collect, so 0 clears it.
|
|
266
|
+
*/
|
|
267
|
+
protected function payloadUpdateFillInput(array $input): array
|
|
268
|
+
{
|
|
269
|
+
$fill = $this->payloadFillInputFromInput($input);
|
|
270
|
+
$cod = $fill['cod_amount'] ?? null;
|
|
271
|
+
$fill = array_filter($fill);
|
|
272
|
+
|
|
273
|
+
if (is_numeric($cod)) {
|
|
274
|
+
$fill['cod_amount'] = $cod;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return $fill;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
protected function orderForPayload(Payload $payload): ?Order
|
|
281
|
+
{
|
|
282
|
+
if (!$payload->uuid) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
return Order::where('payload_uuid', $payload->uuid)->first();
|
|
288
|
+
} catch (\Throwable) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
protected function beginOrderEdit(Order $order): OrderEdit
|
|
294
|
+
{
|
|
295
|
+
return OrderEdit::begin($order);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
protected function commitOrderEdit(OrderEdit $edit): array
|
|
299
|
+
{
|
|
300
|
+
return $edit->commit();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
protected function lockedEditResponse(OrderEditLockedException $exception)
|
|
304
|
+
{
|
|
305
|
+
return response()->apiError($exception->getMessage(), 422, $exception->toResponseData());
|
|
306
|
+
}
|
|
307
|
+
|
|
240
308
|
protected function newPayload(array $input): Payload
|
|
241
309
|
{
|
|
242
310
|
return new Payload($input);
|
|
@@ -146,7 +146,7 @@ class PlaceController extends Controller
|
|
|
146
146
|
try {
|
|
147
147
|
$place = $this->findPlaceOrFail($id);
|
|
148
148
|
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
149
|
-
return $this->apiError('Place resource not found.');
|
|
149
|
+
return $this->apiError('Place resource not found.', 404);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// get request input
|
|
@@ -253,7 +253,7 @@ class PlaceController extends Controller
|
|
|
253
253
|
try {
|
|
254
254
|
$place = $this->findPlaceOrFail($id);
|
|
255
255
|
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
256
|
-
return $this->apiError('Place resource not found.');
|
|
256
|
+
return $this->apiError('Place resource not found.', 404);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
return $this->placeResource($place);
|
|
@@ -269,7 +269,7 @@ class PlaceController extends Controller
|
|
|
269
269
|
try {
|
|
270
270
|
$place = $this->findPlaceOrFail($id);
|
|
271
271
|
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
272
|
-
return $this->apiError('Place resource not found.');
|
|
272
|
+
return $this->apiError('Place resource not found.', 404);
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
$place->delete();
|
|
@@ -127,8 +127,10 @@ class TrackingNumberController extends Controller
|
|
|
127
127
|
// validate request inputs
|
|
128
128
|
$code = $request->input('code');
|
|
129
129
|
|
|
130
|
-
//
|
|
131
|
-
|
|
130
|
+
// the order, waypoint, entity or place the code points at, in any format still in
|
|
131
|
+
// circulation: a tracking url, a bare tracking number or public id, or the bare
|
|
132
|
+
// owner uuid printed on older labels
|
|
133
|
+
$model = $this->findQrModel($code);
|
|
132
134
|
|
|
133
135
|
// if no model response with error
|
|
134
136
|
if (!$model) {
|
|
@@ -179,37 +181,9 @@ class TrackingNumberController extends Controller
|
|
|
179
181
|
return new DeletedResource($trackingNumber);
|
|
180
182
|
}
|
|
181
183
|
|
|
182
|
-
protected function findQrModel(
|
|
184
|
+
protected function findQrModel(string $code)
|
|
183
185
|
{
|
|
184
|
-
|
|
185
|
-
// wrapped in its typed resource — raw rows have no model class.
|
|
186
|
-
$modelMap = [
|
|
187
|
-
'entities' => \Fleetbase\FleetOps\Models\Entity::class,
|
|
188
|
-
'orders' => \Fleetbase\FleetOps\Models\Order::class,
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
foreach ($tables as $table) {
|
|
192
|
-
$modelClass = $modelMap[$table] ?? null;
|
|
193
|
-
if (!$modelClass) {
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
$model = $modelClass::where($where)->first();
|
|
198
|
-
if ($model) {
|
|
199
|
-
return $model;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// No raw-row fallback. Utils::findModel() was called with the ARRAY of table names
|
|
204
|
-
// and, when nothing matched, passed that array to DB::table() — which stringified
|
|
205
|
-
// it to the literal table name "Array" and threw SQLSTATE[42S02]. Every
|
|
206
|
-
// unresolvable code became a 500 instead of the 400 this method's caller already
|
|
207
|
-
// handles.
|
|
208
|
-
//
|
|
209
|
-
// The fallback could not have helped anyway: qrModelResource() serializes through
|
|
210
|
-
// a typed resource keyed on the model class, and a raw stdClass row has none. The
|
|
211
|
-
// mapped models above are the endpoint's whole contract.
|
|
212
|
-
return null;
|
|
186
|
+
return TrackingNumber::findOwnerByCode($code, session('company'));
|
|
213
187
|
}
|
|
214
188
|
|
|
215
189
|
protected function qrModelResource($model)
|
|
@@ -17,7 +17,9 @@ use Fleetbase\FleetOps\Models\Driver;
|
|
|
17
17
|
use Fleetbase\FleetOps\Models\Vehicle;
|
|
18
18
|
use Fleetbase\FleetOps\Models\Vendor;
|
|
19
19
|
use Fleetbase\FleetOps\Models\Warranty;
|
|
20
|
+
use Fleetbase\FleetOps\Support\ApiCacheThrottle;
|
|
20
21
|
use Fleetbase\FleetOps\Support\GeofenceIntersectionService;
|
|
22
|
+
use Fleetbase\FleetOps\Support\TrackingPublisher;
|
|
21
23
|
use Fleetbase\FleetOps\Support\Utils;
|
|
22
24
|
use Fleetbase\Http\Controllers\Controller;
|
|
23
25
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
@@ -296,8 +298,11 @@ class VehicleController extends Controller
|
|
|
296
298
|
|
|
297
299
|
$vehicle->updateQuietly($positionData);
|
|
298
300
|
$vehicle->createPosition($positionData);
|
|
301
|
+
// Location writes are quiet and frequent: invalidate cached lists at most once a window
|
|
302
|
+
ApiCacheThrottle::invalidate($vehicle, $vehicle->company_uuid);
|
|
299
303
|
|
|
300
304
|
broadcast(new VehicleLocationChanged($vehicle));
|
|
305
|
+
app(TrackingPublisher::class)->vehicleMoved($vehicle);
|
|
301
306
|
|
|
302
307
|
try {
|
|
303
308
|
$newLocation = new Point($latitude, $longitude);
|
|
@@ -19,6 +19,7 @@ use Fleetbase\FleetOps\Support\Utils;
|
|
|
19
19
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
20
20
|
use Fleetbase\Http\Requests\ImportRequest;
|
|
21
21
|
use Fleetbase\Models\User;
|
|
22
|
+
use Fleetbase\Support\ApiModelCache;
|
|
22
23
|
use Illuminate\Http\Request;
|
|
23
24
|
use Illuminate\Support\Facades\DB;
|
|
24
25
|
use Illuminate\Support\Facades\Mail;
|
|
@@ -266,6 +267,7 @@ class ContactController extends FleetOpsController
|
|
|
266
267
|
protected function bulkUpdateCustomerContext(string $modelClass, array $filter, array $replacement): void
|
|
267
268
|
{
|
|
268
269
|
$modelClass::where($filter)->update($replacement);
|
|
270
|
+
ApiModelCache::invalidateModelCache(new $modelClass(), session('company'));
|
|
269
271
|
}
|
|
270
272
|
|
|
271
273
|
protected function customerPortalIssuesForContact(Contact $contact)
|
|
@@ -350,6 +352,7 @@ class ContactController extends FleetOpsController
|
|
|
350
352
|
protected function saveContactMetaQuietly(Contact $contact, array $meta): void
|
|
351
353
|
{
|
|
352
354
|
$contact->forceFill(['meta' => $meta])->saveQuietly();
|
|
355
|
+
$contact->invalidateApiCache();
|
|
353
356
|
}
|
|
354
357
|
|
|
355
358
|
protected function isCustomerPortalInstalled(): bool
|
|
@@ -595,6 +595,16 @@ class DriverController extends FleetOpsController
|
|
|
595
595
|
'driver_assigned_uuid' => null,
|
|
596
596
|
'updated_at' => now(),
|
|
597
597
|
]);
|
|
598
|
+
Order::invalidateApiCacheManually(session('company'));
|
|
599
|
+
|
|
600
|
+
// The bulk update bypasses the order observer, so the driver taken off each
|
|
601
|
+
// order is told here
|
|
602
|
+
foreach ($orders as $order) {
|
|
603
|
+
$previousDriverUuid = $order->driver_assigned_uuid;
|
|
604
|
+
if ($previousDriverUuid) {
|
|
605
|
+
$order->notifyDriverUnassigned($previousDriverUuid);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
598
608
|
}
|
|
599
609
|
|
|
600
610
|
protected function freshOrders($orders, array $with)
|
|
@@ -250,10 +250,17 @@ class FleetController extends FleetOpsController
|
|
|
250
250
|
|
|
251
251
|
protected static function deleteFleetDriverAssignment(string $fleetUuid, string $driverUuid): mixed
|
|
252
252
|
{
|
|
253
|
-
|
|
253
|
+
$deleted = FleetDriver::where([
|
|
254
254
|
'fleet_uuid' => $fleetUuid,
|
|
255
255
|
'driver_uuid' => $driverUuid,
|
|
256
256
|
])->delete();
|
|
257
|
+
|
|
258
|
+
// Deleted without model events; fleet_drivers rows carry no company
|
|
259
|
+
if ($deleted) {
|
|
260
|
+
FleetDriver::invalidateApiCacheManually();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return $deleted;
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
protected static function fleetVehicleAssignmentExists(string $fleetUuid, string $vehicleUuid): bool
|
|
@@ -274,10 +281,17 @@ class FleetController extends FleetOpsController
|
|
|
274
281
|
|
|
275
282
|
protected static function deleteFleetVehicleAssignment(string $fleetUuid, string $vehicleUuid): mixed
|
|
276
283
|
{
|
|
277
|
-
|
|
284
|
+
$deleted = FleetVehicle::where([
|
|
278
285
|
'fleet_uuid' => $fleetUuid,
|
|
279
286
|
'vehicle_uuid' => $vehicleUuid,
|
|
280
287
|
])->delete();
|
|
288
|
+
|
|
289
|
+
// Deleted without model events; fleet_vehicles rows carry no company
|
|
290
|
+
if ($deleted) {
|
|
291
|
+
FleetVehicle::invalidateApiCacheManually();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return $deleted;
|
|
281
295
|
}
|
|
282
296
|
|
|
283
297
|
protected static function invalidateOperationsMonitor(): void
|
|
@@ -111,8 +111,7 @@ class InspectionSubmissionController extends FleetOpsController
|
|
|
111
111
|
|
|
112
112
|
public function submit(string $id): JsonResponse
|
|
113
113
|
{
|
|
114
|
-
$submission =
|
|
115
|
-
->orWhere('public_id', $id)
|
|
114
|
+
$submission = $this->submissionQuery($id)
|
|
116
115
|
->with(['itemResults', 'vehicle', 'driver', 'form'])
|
|
117
116
|
->firstOrFail();
|
|
118
117
|
|
|
@@ -127,8 +126,7 @@ class InspectionSubmissionController extends FleetOpsController
|
|
|
127
126
|
|
|
128
127
|
public function createIssue(string $id): JsonResponse
|
|
129
128
|
{
|
|
130
|
-
$submission =
|
|
131
|
-
->orWhere('public_id', $id)
|
|
129
|
+
$submission = $this->submissionQuery($id)
|
|
132
130
|
->with(['itemResults', 'vehicle', 'driver', 'form'])
|
|
133
131
|
->firstOrFail();
|
|
134
132
|
|
|
@@ -145,8 +143,7 @@ class InspectionSubmissionController extends FleetOpsController
|
|
|
145
143
|
|
|
146
144
|
public function createWorkOrder(string $id): JsonResponse
|
|
147
145
|
{
|
|
148
|
-
$submission =
|
|
149
|
-
->orWhere('public_id', $id)
|
|
146
|
+
$submission = $this->submissionQuery($id)
|
|
150
147
|
->with(['itemResults', 'vehicle', 'driver', 'form'])
|
|
151
148
|
->firstOrFail();
|
|
152
149
|
|
|
@@ -164,8 +161,7 @@ class InspectionSubmissionController extends FleetOpsController
|
|
|
164
161
|
|
|
165
162
|
public function resolve(string $id): JsonResponse
|
|
166
163
|
{
|
|
167
|
-
$submission =
|
|
168
|
-
->orWhere('public_id', $id)
|
|
164
|
+
$submission = $this->submissionQuery($id)
|
|
169
165
|
->firstOrFail();
|
|
170
166
|
|
|
171
167
|
$submission->update([
|
|
@@ -180,6 +176,15 @@ class InspectionSubmissionController extends FleetOpsController
|
|
|
180
176
|
]);
|
|
181
177
|
}
|
|
182
178
|
|
|
179
|
+
/** A submission by uuid or public id, only ever within the session's company. */
|
|
180
|
+
protected function submissionQuery(string $id)
|
|
181
|
+
{
|
|
182
|
+
return InspectionSubmission::where('company_uuid', session('company'))
|
|
183
|
+
->where(function ($query) use ($id) {
|
|
184
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
183
188
|
protected function syncItemResultsFromRequest(Request $request, InspectionSubmission $submission): void
|
|
184
189
|
{
|
|
185
190
|
$items = static::arrayInput($request, 'inspection_submission.item_results', 'item_results');
|
|
@@ -38,6 +38,7 @@ class IssueController extends FleetOpsController
|
|
|
38
38
|
if ($assignee && $assignee->type === 'customer') {
|
|
39
39
|
$issue->assigned_to_uuid = null;
|
|
40
40
|
$issue->saveQuietly();
|
|
41
|
+
$issue->invalidateApiCache();
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -58,6 +59,7 @@ class IssueController extends FleetOpsController
|
|
|
58
59
|
if ($order) {
|
|
59
60
|
$issue->order_uuid = $order->uuid;
|
|
60
61
|
$issue->saveQuietly();
|
|
62
|
+
$issue->invalidateApiCache();
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
}
|
|
@@ -194,8 +194,10 @@ class MaintenanceController extends FleetOpsController
|
|
|
194
194
|
|
|
195
195
|
protected function findMaintenanceForLineItem(string $id): Maintenance
|
|
196
196
|
{
|
|
197
|
-
return Maintenance::where('
|
|
198
|
-
->
|
|
197
|
+
return Maintenance::where('company_uuid', session('company'))
|
|
198
|
+
->where(function ($query) use ($id) {
|
|
199
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
200
|
+
})
|
|
199
201
|
->firstOrFail();
|
|
200
202
|
}
|
|
201
203
|
|