@fleetbase/fleetops-engine 0.6.38 → 0.6.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/activity/form.hbs +2 -12
- package/addon/components/activity/logic-builder.hbs +1 -1
- package/addon/components/custom-entity/form.hbs +4 -20
- package/addon/components/device/form.hbs +3 -15
- package/addon/components/device/panel-header.hbs +13 -2
- package/addon/components/driver/details.hbs +33 -2
- package/addon/components/driver/form.hbs +42 -0
- package/addon/components/driver/panel-header.hbs +8 -1
- package/addon/components/driver/schedule.hbs +115 -76
- package/addon/components/driver/schedule.js +349 -157
- package/addon/components/driver-onboard-settings.hbs +2 -8
- package/addon/components/entity-field-editing-settings.hbs +2 -5
- package/addon/components/equipment/card.hbs +49 -0
- package/addon/components/equipment/card.js +6 -0
- package/addon/components/equipment/details.hbs +83 -44
- package/addon/components/equipment/form.hbs +111 -41
- package/addon/components/equipment/form.js +78 -10
- package/addon/components/equipment/panel-header.hbs +36 -0
- package/addon/components/equipment/panel-header.js +2 -0
- package/addon/components/fleet/driver-listing.hbs +3 -1
- package/addon/components/fleet/vehicle-listing.hbs +4 -7
- package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
- package/addon/components/fuel-report/form.hbs +1 -5
- package/addon/components/layout/fleet-ops-sidebar.hbs +40 -36
- package/addon/components/layout/fleet-ops-sidebar.js +61 -10
- package/addon/components/maintenance/cost-panel.hbs +176 -0
- package/addon/components/maintenance/cost-panel.js +241 -0
- package/addon/components/maintenance/details.hbs +123 -60
- package/addon/components/maintenance/form.hbs +138 -78
- package/addon/components/maintenance/form.js +131 -6
- package/addon/components/maintenance/panel-header.hbs +31 -0
- package/addon/components/maintenance/panel-header.js +2 -0
- package/addon/components/maintenance-schedule/details.hbs +260 -0
- package/addon/components/maintenance-schedule/details.js +158 -0
- package/addon/components/maintenance-schedule/form.hbs +287 -0
- package/addon/components/maintenance-schedule/form.js +199 -0
- package/addon/components/map/container.hbs +1 -1
- package/addon/components/map/drawer/device-event-listing.hbs +1 -1
- package/addon/components/map/drawer/driver-listing.hbs +1 -6
- package/addon/components/map/drawer/place-listing.hbs +1 -6
- package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
- package/addon/components/map/drawer.hbs +8 -1
- package/addon/components/map/leaflet-live-map.hbs +2 -1
- package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
- package/addon/components/map/toolbar.hbs +4 -29
- package/addon/components/modals/add-driver-shift.hbs +155 -0
- package/addon/components/modals/add-driver-shift.js +210 -0
- package/addon/components/modals/bulk-assign-orders.hbs +67 -0
- package/addon/components/modals/driver-shift.hbs +43 -0
- package/addon/components/modals/driver-shift.js +56 -0
- package/addon/components/modals/entity-form.hbs +5 -28
- package/addon/components/modals/orchestrator-import.hbs +351 -0
- package/addon/components/modals/orchestrator-import.js +807 -0
- package/addon/components/modals/order-config-new-status.hbs +1 -5
- package/addon/components/modals/scheduling-conflict.hbs +47 -0
- package/addon/components/modals/scheduling-conflict.js +53 -0
- package/addon/components/modals/send-work-order.hbs +91 -0
- package/addon/components/modals/send-work-order.js +3 -0
- package/addon/components/modals/service-area-form.hbs +1 -6
- package/addon/components/modals/set-driver-availability.hbs +50 -0
- package/addon/components/modals/set-driver-availability.js +57 -0
- package/addon/components/modals/update-order-activity.hbs +13 -9
- package/addon/components/modals/user-form.hbs +1 -5
- package/addon/components/modals/vehicle-form.hbs +17 -102
- package/addon/components/modals/vendor-form.hbs +15 -82
- package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
- package/addon/components/orchestrator/card-fields-settings.js +134 -0
- package/addon/components/orchestrator/order-pool.hbs +264 -0
- package/addon/components/orchestrator/order-pool.js +394 -0
- package/addon/components/orchestrator/phase-builder.hbs +162 -0
- package/addon/components/orchestrator/phase-builder.js +162 -0
- package/addon/components/orchestrator/plan-viewer.hbs +278 -0
- package/addon/components/orchestrator/plan-viewer.js +342 -0
- package/addon/components/orchestrator/resource-panel.hbs +301 -0
- package/addon/components/orchestrator/resource-panel.js +106 -0
- package/addon/components/orchestrator-workbench.hbs +318 -0
- package/addon/components/orchestrator-workbench.js +1087 -0
- package/addon/components/order/details/custom-fields.hbs +10 -1
- package/addon/components/order/details/detail.hbs +37 -2
- package/addon/components/order/details/detail.js +0 -1
- package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
- package/addon/components/order/details/notes.hbs +1 -7
- package/addon/components/order/details/route.hbs +1 -5
- package/addon/components/order/form/details.hbs +44 -10
- package/addon/components/order/form/details.js +57 -0
- package/addon/components/order/form/notes.hbs +1 -7
- package/addon/components/order/form/payload.hbs +1 -7
- package/addon/components/order/form/route.hbs +3 -15
- package/addon/components/order/header.hbs +1 -7
- package/addon/components/order/route-editor.hbs +4 -25
- package/addon/components/order/schedule-card.hbs +102 -95
- package/addon/components/order/schedule-card.js +8 -3
- package/addon/components/order-config-manager/details.hbs +2 -10
- package/addon/components/order-config-manager/entities.hbs +1 -7
- package/addon/components/order-config-manager.hbs +1 -6
- package/addon/components/part/card.hbs +49 -0
- package/addon/components/part/card.js +6 -0
- package/addon/components/part/details.hbs +102 -56
- package/addon/components/part/form.hbs +131 -56
- package/addon/components/part/form.js +78 -11
- package/addon/components/part/panel-header.hbs +36 -0
- package/addon/components/part/panel-header.js +2 -0
- package/addon/components/place/form.hbs +1 -7
- package/addon/components/sensor/details.hbs +1 -1
- package/addon/components/sensor/form.hbs +5 -3
- package/addon/components/sensor/panel-header.hbs +8 -1
- package/addon/components/service-area/form.hbs +1 -6
- package/addon/components/service-rate/form.hbs +12 -60
- package/addon/components/telematic/form.hbs +6 -2
- package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
- package/addon/components/vehicle/details/maintenance-history.js +32 -0
- package/addon/components/vehicle/details/schedules.hbs +40 -0
- package/addon/components/vehicle/details/schedules.js +32 -0
- package/addon/components/vehicle/details/work-orders.hbs +42 -0
- package/addon/components/vehicle/details/work-orders.js +32 -0
- package/addon/components/vehicle/details.hbs +30 -0
- package/addon/components/vehicle/form.hbs +39 -0
- package/addon/components/vehicle/panel-header.hbs +19 -49
- package/addon/components/warranty/details.hbs +3 -2
- package/addon/components/warranty/form.hbs +3 -17
- package/addon/components/work-order/details.hbs +135 -40
- package/addon/components/work-order/form.hbs +178 -45
- package/addon/components/work-order/form.js +197 -4
- package/addon/components/work-order/panel-header.hbs +31 -0
- package/addon/components/work-order/panel-header.js +2 -0
- package/addon/controllers/connectivity/devices/index/details.js +1 -1
- package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
- package/addon/controllers/maintenance/equipment/index/details.js +36 -1
- package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
- package/addon/controllers/maintenance/equipment/index/new.js +32 -1
- package/addon/controllers/maintenance/equipment/index.js +127 -113
- package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
- package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
- package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
- package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
- package/addon/controllers/maintenance/maintenances/index.js +191 -0
- package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
- package/addon/controllers/maintenance/parts/index/details.js +36 -1
- package/addon/controllers/maintenance/parts/index/edit.js +56 -1
- package/addon/controllers/maintenance/parts/index/new.js +32 -1
- package/addon/controllers/maintenance/parts/index.js +135 -113
- package/addon/controllers/maintenance/schedules/index/details.js +115 -0
- package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
- package/addon/controllers/maintenance/schedules/index/new.js +33 -0
- package/addon/controllers/maintenance/schedules/index.js +280 -0
- package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
- package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
- package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
- package/addon/controllers/maintenance/work-orders/index.js +105 -113
- package/addon/controllers/management/drivers/index/details.js +6 -1
- package/addon/controllers/management/vehicles/index/details.js +65 -0
- package/addon/controllers/management/vehicles/index.js +18 -0
- package/addon/controllers/operations/orchestrator.js +10 -0
- package/addon/controllers/operations/orders/index.js +6 -0
- package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
- package/addon/controllers/operations/scheduler/index.js +799 -275
- package/addon/controllers/operations/scheduler.js +21 -0
- package/addon/controllers/settings/orchestrator.js +70 -0
- package/addon/controllers/settings/scheduling.js +155 -0
- package/addon/extension.js +19 -0
- package/addon/instance-initializers/register-vroom-allocation.js +27 -0
- package/addon/models/maintenance-schedule.js +61 -0
- package/addon/routes/maintenance/equipment/index/details.js +27 -1
- package/addon/routes/maintenance/equipment/index/edit.js +27 -1
- package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
- package/addon/routes/maintenance/maintenances/index/details.js +29 -0
- package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
- package/addon/routes/maintenance/maintenances/index/new.js +3 -0
- package/addon/routes/maintenance/maintenances/index.js +23 -0
- package/addon/routes/maintenance/maintenances.js +3 -0
- package/addon/routes/maintenance/parts/index/details.js +27 -1
- package/addon/routes/maintenance/parts/index/edit.js +27 -1
- package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
- package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
- package/addon/routes/maintenance/schedules/index/details.js +25 -0
- package/addon/routes/maintenance/schedules/index/edit.js +25 -0
- package/addon/routes/maintenance/schedules/index/new.js +2 -0
- package/addon/routes/maintenance/schedules/index.js +21 -0
- package/addon/routes/maintenance/schedules.js +2 -0
- package/addon/routes/maintenance/work-orders/index/details.js +27 -1
- package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
- package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
- package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
- package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
- package/addon/routes/operations/orchestrator.js +23 -0
- package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
- package/addon/routes/operations/scheduler/index.js +48 -26
- package/addon/routes/operations/scheduler.js +14 -1
- package/addon/routes/settings/orchestrator.js +27 -0
- package/addon/routes/settings/scheduling.js +3 -0
- package/addon/routes.js +31 -1
- package/addon/services/driver-actions.js +40 -7
- package/addon/services/driver-scheduling.js +4 -1
- package/addon/services/equipment-actions.js +15 -5
- package/addon/services/leaflet-map-manager.js +14 -6
- package/addon/services/maintenance-actions.js +17 -14
- package/addon/services/maintenance-schedule-actions.js +118 -0
- package/addon/services/orchestration-engine-interface.js +49 -0
- package/addon/services/orchestration-engine.js +74 -0
- package/addon/services/order-actions.js +15 -0
- package/addon/services/order-allocation.js +116 -0
- package/addon/services/part-actions.js +12 -2
- package/addon/services/scheduling.js +316 -0
- package/addon/services/vehicle-actions.js +70 -7
- package/addon/services/vroom-allocation-engine.js +45 -0
- package/addon/services/work-order-actions.js +80 -0
- package/addon/styles/fleetops-engine.css +1658 -0
- package/addon/templates/analytics/reports/index/edit.hbs +1 -1
- package/addon/templates/analytics/reports/index/new.hbs +1 -1
- package/addon/templates/application.hbs +6 -1
- package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
- package/addon/templates/connectivity/devices.hbs +0 -1
- package/addon/templates/connectivity/events/index/details.hbs +0 -1
- package/addon/templates/connectivity/events.hbs +0 -1
- package/addon/templates/connectivity/sensors.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
- package/addon/templates/connectivity/telematics.hbs +0 -1
- package/addon/templates/connectivity/tracking.hbs +0 -1
- package/addon/templates/connectivity.hbs +0 -1
- package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
- package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
- package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
- package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
- package/addon/templates/maintenance/equipment/index.hbs +48 -13
- package/addon/templates/maintenance/equipment.hbs +0 -1
- package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
- package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
- package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
- package/addon/templates/maintenance/maintenances/index.hbs +14 -0
- package/addon/templates/maintenance/maintenances.hbs +1 -0
- package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
- package/addon/templates/maintenance/parts/index/details.hbs +15 -2
- package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
- package/addon/templates/maintenance/parts/index/new.hbs +1 -2
- package/addon/templates/maintenance/parts/index.hbs +48 -13
- package/addon/templates/maintenance/parts.hbs +0 -1
- package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
- package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
- package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
- package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
- package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
- package/addon/templates/maintenance/schedules/index.hbs +40 -0
- package/addon/templates/maintenance/schedules.hbs +1 -0
- package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
- package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
- package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
- package/addon/templates/maintenance/work-orders.hbs +0 -1
- package/addon/templates/management/contacts/customers/edit.hbs +1 -2
- package/addon/templates/management/contacts/customers/new.hbs +1 -2
- package/addon/templates/management/contacts/customers.hbs +1 -1
- package/addon/templates/management/contacts/index/edit.hbs +1 -2
- package/addon/templates/management/contacts/index/new.hbs +1 -2
- package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
- package/addon/templates/management/drivers/index/edit.hbs +1 -2
- package/addon/templates/management/drivers/index/new.hbs +1 -2
- package/addon/templates/management/fleets/index/edit.hbs +1 -2
- package/addon/templates/management/fleets/index/new.hbs +1 -2
- package/addon/templates/management/fleets/index.hbs +1 -2
- package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
- package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
- package/addon/templates/management/fuel-reports/index.hbs +1 -2
- package/addon/templates/management/issues/index/edit.hbs +1 -2
- package/addon/templates/management/issues/index/new.hbs +1 -2
- package/addon/templates/management/issues/index.hbs +1 -2
- package/addon/templates/management/places/index/details/activity.hbs +0 -1
- package/addon/templates/management/places/index/details/comments.hbs +0 -1
- package/addon/templates/management/places/index/details/documents.hbs +0 -1
- package/addon/templates/management/places/index/details/map.hbs +0 -1
- package/addon/templates/management/places/index/details/operations.hbs +0 -1
- package/addon/templates/management/places/index/details/performance.hbs +0 -1
- package/addon/templates/management/places/index/details/rules.hbs +0 -1
- package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
- package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
- package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
- package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -1
- package/addon/templates/management/vehicles/index/edit.hbs +1 -2
- package/addon/templates/management/vehicles/index/new.hbs +1 -2
- package/addon/templates/management/vendors/index/edit.hbs +1 -2
- package/addon/templates/management/vendors/index/new.hbs +1 -2
- package/addon/templates/management/vendors/index.hbs +1 -2
- package/addon/templates/management/vendors/integrated.hbs +1 -2
- package/addon/templates/operations/orchestrator.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +6 -1
- package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
- package/addon/templates/operations/scheduler/index.hbs +147 -88
- package/addon/templates/operations/scheduler.hbs +7 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +65 -0
- package/addon/templates/settings/payments/index.hbs +1 -5
- package/addon/templates/settings/scheduling.hbs +82 -0
- package/addon/utils/create-full-calendar-event-from-order.js +52 -14
- package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
- package/addon/utils/fleet-ops-options.js +254 -0
- package/addon/utils/route-colors.js +99 -0
- package/addon/utils/to-calendar-date.js +70 -0
- package/app/components/driver/schedule.js +1 -0
- package/app/components/maintenance/cost-panel.js +1 -0
- package/app/components/maintenance/panel-header.js +1 -0
- package/app/components/maintenance-schedule/details.js +1 -0
- package/app/components/maintenance-schedule/form.js +1 -0
- package/app/components/modals/add-driver-shift.js +1 -0
- package/app/components/modals/bulk-assign-orders.js +1 -0
- package/app/components/modals/driver-shift.js +1 -0
- package/app/components/modals/orchestrator-import.js +1 -0
- package/app/components/modals/scheduling-conflict.js +1 -0
- package/app/components/modals/send-work-order.js +1 -0
- package/app/components/modals/set-driver-availability.js +1 -0
- package/app/components/orchestrator/card-fields-settings.js +1 -0
- package/app/components/orchestrator/order-pool.js +1 -0
- package/app/components/orchestrator/phase-builder.js +1 -0
- package/app/components/orchestrator/plan-viewer.js +1 -0
- package/app/components/orchestrator/resource-panel.js +1 -0
- package/app/components/orchestrator-workbench.js +1 -0
- package/app/components/vehicle/details/maintenance-history.js +1 -0
- package/app/components/vehicle/details/schedules.js +1 -0
- package/app/components/vehicle/details/work-orders.js +1 -0
- package/app/controllers/operations/orchestrator.js +1 -0
- package/app/controllers/settings/orchestrator.js +1 -0
- package/app/controllers/settings/scheduling.js +1 -0
- package/app/routes/operations/orchestrator.js +1 -0
- package/app/routes/settings/orchestrator.js +1 -0
- package/app/routes/settings/scheduling.js +1 -0
- package/app/services/maintenance-schedule-actions.js +1 -0
- package/app/services/orchestration-engine-interface.js +1 -0
- package/app/services/orchestration-engine.js +1 -0
- package/app/services/order-allocation.js +1 -0
- package/app/services/scheduling.js +1 -0
- package/app/services/vroom-allocation-engine.js +1 -0
- package/app/templates/settings/scheduling.js +1 -0
- package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
- package/app/utils/route-colors.js +1 -0
- package/composer.json +5 -3
- package/extension.json +1 -1
- package/package.json +6 -5
- package/server/config/fleetops.php +20 -1
- package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
- package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
- package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
- package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
- package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
- package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
- package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
- package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
- package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
- package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
- package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
- package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
- package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
- package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
- package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
- package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
- package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
- package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
- package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
- package/server/src/Auth/Schemas/FleetOps.php +44 -0
- package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
- package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
- package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
- package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
- package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
- package/server/src/Http/Resources/v1/Driver.php +1 -0
- package/server/src/Http/Resources/v1/Maintenance.php +138 -0
- package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
- package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
- package/server/src/Http/Resources/v1/Order.php +7 -4
- package/server/src/Http/Resources/v1/Waypoint.php +7 -0
- package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
- package/server/src/Imports/EquipmentImport.php +32 -0
- package/server/src/Imports/MaintenanceImport.php +32 -0
- package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
- package/server/src/Imports/PartImport.php +32 -0
- package/server/src/Imports/WorkOrderImport.php +32 -0
- package/server/src/Jobs/ProcessAllocationJob.php +119 -0
- package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
- package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
- package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
- package/server/src/Mail/WorkOrderDispatched.php +58 -0
- package/server/src/Models/Asset.php +2 -2
- package/server/src/Models/Device.php +1 -1
- package/server/src/Models/Driver.php +82 -4
- package/server/src/Models/Equipment.php +62 -2
- package/server/src/Models/Maintenance.php +127 -9
- package/server/src/Models/MaintenanceSchedule.php +353 -0
- package/server/src/Models/Manifest.php +214 -0
- package/server/src/Models/ManifestStop.php +162 -0
- package/server/src/Models/Order.php +70 -0
- package/server/src/Models/OrderConfig.php +5 -2
- package/server/src/Models/Part.php +69 -3
- package/server/src/Models/Payload.php +7 -2
- package/server/src/Models/Place.php +1 -1
- package/server/src/Models/Sensor.php +1 -1
- package/server/src/Models/ServiceQuote.php +1 -1
- package/server/src/Models/Vehicle.php +20 -1
- package/server/src/Models/Warranty.php +1 -1
- package/server/src/Models/Waypoint.php +7 -1
- package/server/src/Models/WorkOrder.php +122 -12
- package/server/src/Notifications/DriverShiftChanged.php +110 -0
- package/server/src/Observers/WorkOrderObserver.php +107 -0
- package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
- package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
- package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
- package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
- package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
- package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
- package/server/src/Providers/EventServiceProvider.php +7 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
- package/server/src/routes.php +65 -4
- package/translations/ar-ae.yml +44 -12
- package/translations/bg-bg.yaml +51 -10
- package/translations/en-us.yaml +444 -1
- package/translations/fr-fr.yaml +51 -10
- package/translations/mn-mn.yaml +51 -10
- package/translations/pt-br.yaml +51 -10
- package/translations/ru-ru.yaml +51 -10
- package/translations/vi-vn.yaml +48 -12
|
@@ -1036,4 +1036,46 @@ class OrderController extends FleetOpsController
|
|
|
1036
1036
|
|
|
1037
1037
|
return new OrderResource($order);
|
|
1038
1038
|
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Schedule an order: set scheduled_at and optionally assign a driver.
|
|
1042
|
+
* This endpoint intentionally does NOT trigger dispatch or change the
|
|
1043
|
+
* order status, so it is safe to call from the scheduler UI.
|
|
1044
|
+
*
|
|
1045
|
+
* @return \Illuminate\Http\Response
|
|
1046
|
+
*/
|
|
1047
|
+
public function scheduleOrder(Request $request)
|
|
1048
|
+
{
|
|
1049
|
+
$orderId = $request->input('order');
|
|
1050
|
+
$scheduledAt = $request->input('scheduled_at');
|
|
1051
|
+
$driverId = $request->input('driver_id');
|
|
1052
|
+
|
|
1053
|
+
$order = Order::findById($orderId);
|
|
1054
|
+
if (!$order) {
|
|
1055
|
+
return response()->error('No order found to schedule.');
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if ($scheduledAt) {
|
|
1059
|
+
$order->scheduled_at = \Carbon\Carbon::parse($scheduledAt);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if ($driverId) {
|
|
1063
|
+
// Resolve by uuid or public_id
|
|
1064
|
+
$driver = Driver::where('uuid', $driverId)
|
|
1065
|
+
->orWhere('public_id', $driverId)
|
|
1066
|
+
->first();
|
|
1067
|
+
if ($driver) {
|
|
1068
|
+
$order->driver_assigned_uuid = $driver->uuid;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
$order->saveQuietly();
|
|
1073
|
+
|
|
1074
|
+
return response()->json([
|
|
1075
|
+
'status' => 'OK',
|
|
1076
|
+
'message' => 'Order scheduled',
|
|
1077
|
+
'order' => $order->uuid,
|
|
1078
|
+
'scheduled_at' => $order->scheduled_at,
|
|
1079
|
+
]);
|
|
1080
|
+
}
|
|
1039
1081
|
}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
|
|
6
|
+
use Fleetbase\FleetOps\Imports\PartImport;
|
|
7
|
+
use Fleetbase\Http\Requests\ImportRequest;
|
|
8
|
+
use Maatwebsite\Excel\Facades\Excel;
|
|
6
9
|
|
|
7
10
|
class PartController extends FleetOpsController
|
|
8
11
|
{
|
|
@@ -12,4 +15,28 @@ class PartController extends FleetOpsController
|
|
|
12
15
|
* @var string
|
|
13
16
|
*/
|
|
14
17
|
public $resource = 'part';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Process import files (excel, csv) into Part records.
|
|
21
|
+
*
|
|
22
|
+
* @return \Illuminate\Http\Response
|
|
23
|
+
*/
|
|
24
|
+
public function import(ImportRequest $request)
|
|
25
|
+
{
|
|
26
|
+
$disk = $request->input('disk', config('filesystems.default'));
|
|
27
|
+
$files = $request->resolveFilesFromIds();
|
|
28
|
+
$importedCount = 0;
|
|
29
|
+
|
|
30
|
+
foreach ($files as $file) {
|
|
31
|
+
try {
|
|
32
|
+
$import = new PartImport();
|
|
33
|
+
Excel::import($import, $file->path, $disk);
|
|
34
|
+
$importedCount += $import->imported;
|
|
35
|
+
} catch (\Throwable $e) {
|
|
36
|
+
return response()->error('Invalid file, unable to process.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return response()->json(['status' => 'ok', 'message' => 'Import completed', 'imported' => $importedCount]);
|
|
41
|
+
}
|
|
15
42
|
}
|
|
@@ -210,4 +210,122 @@ class SettingController extends Controller
|
|
|
210
210
|
|
|
211
211
|
return response()->json($routingSettings);
|
|
212
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Retrieve driver scheduling settings for the current company.
|
|
216
|
+
*
|
|
217
|
+
* @return \Illuminate\Http\JsonResponse
|
|
218
|
+
*/
|
|
219
|
+
public function getSchedulingSettings()
|
|
220
|
+
{
|
|
221
|
+
$defaults = [
|
|
222
|
+
'horizon_days' => 60,
|
|
223
|
+
'default_shift_duration' => 8,
|
|
224
|
+
'hos_daily_limit' => 11,
|
|
225
|
+
'hos_weekly_limit' => 70,
|
|
226
|
+
'auto_activate_schedule' => true,
|
|
227
|
+
'notify_drivers_on_shift_change' => false,
|
|
228
|
+
];
|
|
229
|
+
$settings = Setting::lookupFromCompany('fleet-ops.scheduling-settings', $defaults);
|
|
230
|
+
|
|
231
|
+
return response()->json($settings);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Save driver scheduling settings for the current company.
|
|
236
|
+
*
|
|
237
|
+
* @return \Illuminate\Http\JsonResponse
|
|
238
|
+
*/
|
|
239
|
+
public function saveSchedulingSettings(Request $request)
|
|
240
|
+
{
|
|
241
|
+
$settings = [
|
|
242
|
+
'horizon_days' => (int) $request->input('horizon_days', 60),
|
|
243
|
+
'default_shift_duration' => (int) $request->input('default_shift_duration', 8),
|
|
244
|
+
'hos_daily_limit' => (int) $request->input('hos_daily_limit', 11),
|
|
245
|
+
'hos_weekly_limit' => (int) $request->input('hos_weekly_limit', 70),
|
|
246
|
+
'auto_activate_schedule' => (bool) $request->input('auto_activate_schedule', true),
|
|
247
|
+
'notify_drivers_on_shift_change' => (bool) $request->input('notify_drivers_on_shift_change', false),
|
|
248
|
+
];
|
|
249
|
+
Setting::configureCompany('fleet-ops.scheduling-settings', $settings);
|
|
250
|
+
|
|
251
|
+
return response()->json($settings);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Retrieve order allocation settings for the current company.
|
|
256
|
+
*
|
|
257
|
+
* @return \Illuminate\Http\JsonResponse
|
|
258
|
+
*/
|
|
259
|
+
public function getOrchestratorSettings()
|
|
260
|
+
{
|
|
261
|
+
$defaults = [
|
|
262
|
+
'allocation_engine' => 'vroom',
|
|
263
|
+
'auto_allocate_on_create' => false,
|
|
264
|
+
'auto_reallocate_on_complete' => false,
|
|
265
|
+
'max_travel_time_seconds' => 3600,
|
|
266
|
+
'balance_workload' => false,
|
|
267
|
+
];
|
|
268
|
+
$settings = Setting::lookupFromCompany('fleet-ops.allocation-settings', $defaults);
|
|
269
|
+
|
|
270
|
+
return response()->json($settings);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Save order allocation settings for the current company.
|
|
275
|
+
*
|
|
276
|
+
* @return \Illuminate\Http\JsonResponse
|
|
277
|
+
*/
|
|
278
|
+
public function saveOrchestratorSettings(Request $request)
|
|
279
|
+
{
|
|
280
|
+
$settings = [
|
|
281
|
+
'allocation_engine' => $request->input('allocation_engine', 'vroom'),
|
|
282
|
+
'auto_allocate_on_create' => (bool) $request->input('auto_allocate_on_create', false),
|
|
283
|
+
'auto_reallocate_on_complete' => (bool) $request->input('auto_reallocate_on_complete', false),
|
|
284
|
+
'max_travel_time_seconds' => (int) $request->input('max_travel_time_seconds', 3600),
|
|
285
|
+
'balance_workload' => (bool) $request->input('balance_workload', false),
|
|
286
|
+
];
|
|
287
|
+
Setting::configureCompany('fleet-ops.allocation-settings', $settings);
|
|
288
|
+
|
|
289
|
+
return response()->json($settings);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Retrieve orchestrator order card field settings for the current company.
|
|
294
|
+
*
|
|
295
|
+
* @return \Illuminate\Http\JsonResponse
|
|
296
|
+
*/
|
|
297
|
+
public function getOrchestratorCardFields()
|
|
298
|
+
{
|
|
299
|
+
$defaults = [
|
|
300
|
+
'standard' => ['tracking', 'status', 'scheduled_at', 'customer', 'dropoff'],
|
|
301
|
+
'byConfig' => (object) [],
|
|
302
|
+
'meta' => [],
|
|
303
|
+
];
|
|
304
|
+
$settings = Setting::lookupFromCompany('fleet-ops.orchestrator-card-fields', $defaults);
|
|
305
|
+
|
|
306
|
+
return response()->json(['settings' => $settings]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Save orchestrator order card field settings for the current company.
|
|
311
|
+
*
|
|
312
|
+
* @return \Illuminate\Http\JsonResponse
|
|
313
|
+
*/
|
|
314
|
+
public function saveOrchestratorCardFields(Request $request)
|
|
315
|
+
{
|
|
316
|
+
$settings = $request->input('settings', []);
|
|
317
|
+
|
|
318
|
+
$normalized = [
|
|
319
|
+
'standard' => $settings['standard'] ?? ['tracking', 'status', 'scheduled_at', 'customer', 'dropoff'],
|
|
320
|
+
'byConfig' => $settings['byConfig'] ?? [],
|
|
321
|
+
'meta' => $settings['meta'] ?? [],
|
|
322
|
+
];
|
|
323
|
+
Setting::configureCompany('fleet-ops.orchestrator-card-fields', $normalized);
|
|
324
|
+
|
|
325
|
+
return response()->json([
|
|
326
|
+
'status' => 'ok',
|
|
327
|
+
'message' => 'Orchestrator card fields saved.',
|
|
328
|
+
'settings' => $normalized,
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
213
331
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1\Traits;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
6
|
+
use Fleetbase\Models\Schedule;
|
|
7
|
+
use Illuminate\Http\JsonResponse;
|
|
8
|
+
use Illuminate\Http\Request;
|
|
9
|
+
use Illuminate\Support\Facades\DB;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* DriverSchedulingTrait.
|
|
13
|
+
*
|
|
14
|
+
* Provides scheduling-related controller methods for the DriverController.
|
|
15
|
+
* These methods expose the core-api scheduling relationships (ScheduleItem,
|
|
16
|
+
* ScheduleAvailability) through FleetOps-specific driver endpoints.
|
|
17
|
+
*
|
|
18
|
+
* Routes:
|
|
19
|
+
* GET /int/v1/drivers/{id}/schedule-items
|
|
20
|
+
* GET /int/v1/drivers/{id}/availabilities
|
|
21
|
+
* GET /int/v1/drivers/{id}/hos-status
|
|
22
|
+
* GET /int/v1/drivers/{id}/active-shift
|
|
23
|
+
*
|
|
24
|
+
* The {id} parameter accepts either the driver's UUID or public_id.
|
|
25
|
+
*/
|
|
26
|
+
trait DriverSchedulingTrait
|
|
27
|
+
{
|
|
28
|
+
/**
|
|
29
|
+
* Regulatory HOS defaults used when no per-schedule override is configured.
|
|
30
|
+
* These match the US FMCSA 11-hour / 70-hour rules as a sensible baseline.
|
|
31
|
+
*/
|
|
32
|
+
private const HOS_DEFAULT_DAILY_LIMIT = 11;
|
|
33
|
+
private const HOS_DEFAULT_WEEKLY_LIMIT = 70;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve a Driver by UUID or public_id, throwing 404 if not found.
|
|
37
|
+
*/
|
|
38
|
+
private function resolveDriver(string $id): Driver
|
|
39
|
+
{
|
|
40
|
+
return Driver::where('uuid', $id)
|
|
41
|
+
->orWhere('public_id', $id)
|
|
42
|
+
->firstOrFail();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Return all ScheduleItem records assigned to this driver.
|
|
47
|
+
* Supports optional date range filtering via start_at and end_at query params.
|
|
48
|
+
*
|
|
49
|
+
* GET /int/v1/drivers/{id}/schedule-items
|
|
50
|
+
*/
|
|
51
|
+
public function scheduleItems(string $id, Request $request): JsonResponse
|
|
52
|
+
{
|
|
53
|
+
$driver = $this->resolveDriver($id);
|
|
54
|
+
$query = $driver->scheduleItems();
|
|
55
|
+
|
|
56
|
+
if ($request->filled('start_at')) {
|
|
57
|
+
$query->where('start_at', '>=', $request->input('start_at'));
|
|
58
|
+
}
|
|
59
|
+
if ($request->filled('end_at')) {
|
|
60
|
+
$query->where('end_at', '<=', $request->input('end_at'));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return response()->json(['data' => $query->orderBy('start_at')->get()]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Return all ScheduleAvailability records for this driver.
|
|
68
|
+
* Supports optional date range filtering via start_at and end_at query params.
|
|
69
|
+
*
|
|
70
|
+
* GET /int/v1/drivers/{id}/availabilities
|
|
71
|
+
*/
|
|
72
|
+
public function availabilities(string $id, Request $request): JsonResponse
|
|
73
|
+
{
|
|
74
|
+
$driver = $this->resolveDriver($id);
|
|
75
|
+
$query = $driver->availabilities();
|
|
76
|
+
|
|
77
|
+
if ($request->filled('start_at')) {
|
|
78
|
+
$query->where('start_at', '>=', $request->input('start_at'));
|
|
79
|
+
}
|
|
80
|
+
if ($request->filled('end_at')) {
|
|
81
|
+
$query->where('end_at', '<=', $request->input('end_at'));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return response()->json(['data' => $query->orderBy('start_at')->get()]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Return the driver's Hours of Service (HOS) status.
|
|
89
|
+
*
|
|
90
|
+
* ## Data source
|
|
91
|
+
*
|
|
92
|
+
* The `hos_source` field on the driver's active Schedule determines how
|
|
93
|
+
* driving hours are accumulated:
|
|
94
|
+
*
|
|
95
|
+
* - `schedule` (default): Hours are derived from the driver's ScheduleItem
|
|
96
|
+
* records. Any shift whose `start_at` is in the past (i.e. has already
|
|
97
|
+
* started) is counted. For ongoing shifts (no `end_at` or end_at > NOW()),
|
|
98
|
+
* elapsed time up to NOW() is used. This gives a meaningful "hours
|
|
99
|
+
* scheduled/worked so far" reading immediately, without requiring
|
|
100
|
+
* clock-in/clock-out or telematics data.
|
|
101
|
+
*
|
|
102
|
+
* - `telematics` (future): Accumulated from GPS movement data.
|
|
103
|
+
* - `manual` (future): Manually logged by a dispatcher.
|
|
104
|
+
*
|
|
105
|
+
* ## Limits
|
|
106
|
+
*
|
|
107
|
+
* Limits are resolved in priority order:
|
|
108
|
+
* 1. Per-schedule `hos_daily_limit` / `hos_weekly_limit` (if set on the Schedule)
|
|
109
|
+
* 2. Hard-coded regulatory defaults: 11h daily / 70h weekly (US FMCSA baseline)
|
|
110
|
+
*
|
|
111
|
+
* GET /int/v1/drivers/{id}/hos-status
|
|
112
|
+
*/
|
|
113
|
+
public function hosStatus(string $id): JsonResponse
|
|
114
|
+
{
|
|
115
|
+
$driver = $this->resolveDriver($id);
|
|
116
|
+
|
|
117
|
+
// ── Resolve the driver's active schedule ──────────────────────────────
|
|
118
|
+
/** @var Schedule|null $schedule */
|
|
119
|
+
$schedule = $driver->schedules()
|
|
120
|
+
->where('status', 'active')
|
|
121
|
+
->latest('created_at')
|
|
122
|
+
->first();
|
|
123
|
+
|
|
124
|
+
// ── Determine HOS limits (per-schedule → global default) ──────────────
|
|
125
|
+
$dailyLimit = ($schedule && $schedule->hos_daily_limit)
|
|
126
|
+
? (int) $schedule->hos_daily_limit
|
|
127
|
+
: self::HOS_DEFAULT_DAILY_LIMIT;
|
|
128
|
+
|
|
129
|
+
$weeklyLimit = ($schedule && $schedule->hos_weekly_limit)
|
|
130
|
+
? (int) $schedule->hos_weekly_limit
|
|
131
|
+
: self::HOS_DEFAULT_WEEKLY_LIMIT;
|
|
132
|
+
|
|
133
|
+
// ── Determine HOS source ──────────────────────────────────────────────
|
|
134
|
+
$hosSource = $schedule ? ($schedule->hos_source ?? 'schedule') : 'schedule';
|
|
135
|
+
|
|
136
|
+
// ── Calculate hours based on source ───────────────────────────────────
|
|
137
|
+
[$dailyHours, $weeklyHours] = match ($hosSource) {
|
|
138
|
+
// Future sources (telematics, manual) will be added here
|
|
139
|
+
default => $this->calculateHosFromSchedule($driver),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return response()->json([
|
|
143
|
+
'daily_hours' => $dailyHours,
|
|
144
|
+
'weekly_hours' => $weeklyHours,
|
|
145
|
+
'daily_limit' => $dailyLimit,
|
|
146
|
+
'weekly_limit' => $weeklyLimit,
|
|
147
|
+
'hos_source' => $hosSource,
|
|
148
|
+
'is_compliant' => $dailyHours < $dailyLimit && $weeklyHours < $weeklyLimit,
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Calculate HOS hours from the driver's ScheduleItem records.
|
|
154
|
+
*
|
|
155
|
+
* Counts all shifts whose `start_at` is in the past (i.e. the shift has
|
|
156
|
+
* already started). For ongoing shifts (end_at is null or in the future),
|
|
157
|
+
* elapsed time up to NOW() is used via LEAST(COALESCE(end_at, NOW()), NOW()).
|
|
158
|
+
*
|
|
159
|
+
* Cancelled shifts are excluded. All other statuses (scheduled, in_progress,
|
|
160
|
+
* completed) are included — a shift that has started counts toward HOS
|
|
161
|
+
* regardless of whether it has been formally marked completed.
|
|
162
|
+
*
|
|
163
|
+
* @return array{float, float} [dailyHours, weeklyHours]
|
|
164
|
+
*/
|
|
165
|
+
private function calculateHosFromSchedule(Driver $driver): array
|
|
166
|
+
{
|
|
167
|
+
$now = now();
|
|
168
|
+
$startOfDay = $now->copy()->startOfDay();
|
|
169
|
+
$startOfWeek = $now->copy()->startOfWeek();
|
|
170
|
+
|
|
171
|
+
// Duration expression: elapsed minutes from shift start to MIN(end_at, NOW()).
|
|
172
|
+
// This caps ongoing shifts at the current moment so future time is never counted.
|
|
173
|
+
$durationExpr = DB::raw(
|
|
174
|
+
'TIMESTAMPDIFF(MINUTE, start_at, LEAST(COALESCE(end_at, NOW()), NOW()))'
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Daily: shifts that started today and have already begun
|
|
178
|
+
$dailyMinutes = $driver->scheduleItems()
|
|
179
|
+
->where('start_at', '>=', $startOfDay)
|
|
180
|
+
->where('start_at', '<=', $now)
|
|
181
|
+
->where('status', '!=', 'cancelled')
|
|
182
|
+
->sum($durationExpr);
|
|
183
|
+
|
|
184
|
+
// Weekly: shifts that started this week and have already begun
|
|
185
|
+
$weeklyMinutes = $driver->scheduleItems()
|
|
186
|
+
->where('start_at', '>=', $startOfWeek)
|
|
187
|
+
->where('start_at', '<=', $now)
|
|
188
|
+
->where('status', '!=', 'cancelled')
|
|
189
|
+
->sum($durationExpr);
|
|
190
|
+
|
|
191
|
+
return [
|
|
192
|
+
round((float) $dailyMinutes / 60, 1),
|
|
193
|
+
round((float) $weeklyMinutes / 60, 1),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Return the driver's currently active shift for today.
|
|
199
|
+
* Used by OrchestrationPayloadBuilder to inject time_window constraints.
|
|
200
|
+
*
|
|
201
|
+
* GET /int/v1/drivers/{id}/active-shift
|
|
202
|
+
*/
|
|
203
|
+
public function activeShift(string $id): JsonResponse
|
|
204
|
+
{
|
|
205
|
+
$driver = $this->resolveDriver($id);
|
|
206
|
+
$shift = $driver->activeShiftFor(now());
|
|
207
|
+
|
|
208
|
+
if (!$shift) {
|
|
209
|
+
return response()->json(['data' => null]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return response()->json(['data' => $shift]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
|
|
6
|
+
use Fleetbase\FleetOps\Imports\WorkOrderImport;
|
|
7
|
+
use Fleetbase\FleetOps\Mail\WorkOrderDispatched;
|
|
8
|
+
use Fleetbase\FleetOps\Models\WorkOrder;
|
|
9
|
+
use Fleetbase\Http\Requests\ImportRequest;
|
|
10
|
+
use Illuminate\Http\JsonResponse;
|
|
11
|
+
use Illuminate\Support\Facades\Mail;
|
|
12
|
+
use Maatwebsite\Excel\Facades\Excel;
|
|
6
13
|
|
|
7
14
|
class WorkOrderController extends FleetOpsController
|
|
8
15
|
{
|
|
@@ -12,4 +19,65 @@ class WorkOrderController extends FleetOpsController
|
|
|
12
19
|
* @var string
|
|
13
20
|
*/
|
|
14
21
|
public $resource = 'work-order';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Process import files (excel, csv) into WorkOrder records.
|
|
25
|
+
*
|
|
26
|
+
* @return \Illuminate\Http\Response
|
|
27
|
+
*/
|
|
28
|
+
public function import(ImportRequest $request)
|
|
29
|
+
{
|
|
30
|
+
$disk = $request->input('disk', config('filesystems.default'));
|
|
31
|
+
$files = $request->resolveFilesFromIds();
|
|
32
|
+
$importedCount = 0;
|
|
33
|
+
|
|
34
|
+
foreach ($files as $file) {
|
|
35
|
+
try {
|
|
36
|
+
$import = new WorkOrderImport();
|
|
37
|
+
Excel::import($import, $file->path, $disk);
|
|
38
|
+
$importedCount += $import->imported;
|
|
39
|
+
} catch (\Throwable $e) {
|
|
40
|
+
return response()->error('Invalid file, unable to process.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return response()->json(['status' => 'ok', 'message' => 'Import completed', 'imported' => $importedCount]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Send a work order email to the assigned vendor.
|
|
49
|
+
* POST /work-orders/{id}/send.
|
|
50
|
+
*/
|
|
51
|
+
public function sendEmail(string $id): JsonResponse
|
|
52
|
+
{
|
|
53
|
+
$workOrder = WorkOrder::where('uuid', $id)
|
|
54
|
+
->orWhere('public_id', $id)
|
|
55
|
+
->with(['assignee', 'target'])
|
|
56
|
+
->firstOrFail();
|
|
57
|
+
|
|
58
|
+
// Resolve recipient email from the assignee (vendor or contact)
|
|
59
|
+
$assignee = $workOrder->assignee;
|
|
60
|
+
|
|
61
|
+
if (!$assignee) {
|
|
62
|
+
return response()->json(['error' => 'This work order has no assigned vendor.'], 422);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$email = $assignee->email ?? null;
|
|
66
|
+
|
|
67
|
+
if (!$email) {
|
|
68
|
+
return response()->json(['error' => 'The assigned vendor has no email address on file.'], 422);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Mail::to($email)->send(new WorkOrderDispatched($workOrder));
|
|
72
|
+
|
|
73
|
+
activity('work_order_sent')
|
|
74
|
+
->performedOn($workOrder)
|
|
75
|
+
->withProperties(['sent_to' => $email])
|
|
76
|
+
->log('Work order emailed to vendor');
|
|
77
|
+
|
|
78
|
+
return response()->json([
|
|
79
|
+
'status' => 'ok',
|
|
80
|
+
'message' => 'Work order successfully sent to ' . $email,
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
15
83
|
}
|
|
@@ -50,6 +50,7 @@ class Driver extends FleetbaseResource
|
|
|
50
50
|
'jobs' => $this->whenLoaded('jobs', fn () => $this->getJobs()),
|
|
51
51
|
'vendor' => $this->whenLoaded('vendor', fn () => new Vendor($this->vendor)),
|
|
52
52
|
'fleets' => $this->whenLoaded('fleets', fn () => Fleet::collection($this->fleets()->without('drivers')->get())),
|
|
53
|
+
'current_shift' => $this->whenLoaded('currentShift', fn () => $this->currentShift),
|
|
53
54
|
'location' => $this->wasRecentlyCreated ? new Point(0, 0) : Utils::castPoint($this->location),
|
|
54
55
|
'heading' => (int) data_get($this, 'heading', 0),
|
|
55
56
|
'altitude' => (int) data_get($this, 'altitude', 0),
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
6
|
+
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
7
|
+
use Fleetbase\Support\Http;
|
|
8
|
+
use Illuminate\Support\Str;
|
|
9
|
+
|
|
10
|
+
class Maintenance extends FleetbaseResource
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* Transform the resource into an array.
|
|
14
|
+
*
|
|
15
|
+
* @param \Illuminate\Http\Request $request
|
|
16
|
+
*
|
|
17
|
+
* @return array
|
|
18
|
+
*/
|
|
19
|
+
public function toArray($request)
|
|
20
|
+
{
|
|
21
|
+
return $this->withCustomFields([
|
|
22
|
+
'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
|
|
23
|
+
'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
|
|
24
|
+
'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
|
|
25
|
+
'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
|
|
26
|
+
'work_order_uuid' => $this->when(Http::isInternalRequest(), $this->work_order_uuid),
|
|
27
|
+
'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
|
|
28
|
+
'updated_by_uuid' => $this->when(Http::isInternalRequest(), $this->updated_by_uuid),
|
|
29
|
+
// Polymorphic maintainable
|
|
30
|
+
'maintainable_uuid' => $this->when(Http::isInternalRequest(), $this->maintainable_uuid),
|
|
31
|
+
'maintainable_type' => $this->when(Http::isInternalRequest(), $this->maintainable_type ? Utils::toEmberResourceType($this->maintainable_type) : null),
|
|
32
|
+
'maintainable' => $this->whenLoaded('maintainable', function () {
|
|
33
|
+
return $this->setMaintainableType($this->transformMorphResource($this->maintainable));
|
|
34
|
+
}),
|
|
35
|
+
// Polymorphic performed_by
|
|
36
|
+
'performed_by_uuid' => $this->when(Http::isInternalRequest(), $this->performed_by_uuid),
|
|
37
|
+
'performed_by_type' => $this->when(Http::isInternalRequest(), $this->performed_by_type ? Utils::toEmberResourceType($this->performed_by_type) : null),
|
|
38
|
+
'performed_by' => $this->whenLoaded('performedBy', function () {
|
|
39
|
+
return $this->setPerformedByType($this->transformMorphResource($this->performedBy));
|
|
40
|
+
}),
|
|
41
|
+
// Core attributes
|
|
42
|
+
'type' => $this->type,
|
|
43
|
+
'status' => $this->status,
|
|
44
|
+
'priority' => $this->priority,
|
|
45
|
+
'odometer' => $this->odometer,
|
|
46
|
+
'engine_hours' => $this->engine_hours,
|
|
47
|
+
'summary' => $this->summary,
|
|
48
|
+
'notes' => $this->notes,
|
|
49
|
+
'line_items' => data_get($this, 'line_items', []),
|
|
50
|
+
'labor_cost' => $this->labor_cost,
|
|
51
|
+
'parts_cost' => $this->parts_cost,
|
|
52
|
+
'tax' => $this->tax,
|
|
53
|
+
'total_cost' => $this->total_cost,
|
|
54
|
+
'currency' => $this->currency,
|
|
55
|
+
'attachments' => data_get($this, 'attachments', []),
|
|
56
|
+
'meta' => data_get($this, 'meta', (object) []),
|
|
57
|
+
'slug' => $this->slug,
|
|
58
|
+
// Computed
|
|
59
|
+
'maintainable_name' => $this->maintainable_name,
|
|
60
|
+
'work_order_subject' => $this->work_order_subject,
|
|
61
|
+
'performed_by_name' => $this->performed_by_name,
|
|
62
|
+
'duration_hours' => $this->duration_hours,
|
|
63
|
+
'is_overdue' => $this->is_overdue,
|
|
64
|
+
'days_until_due' => $this->days_until_due,
|
|
65
|
+
'cost_breakdown' => $this->cost_breakdown,
|
|
66
|
+
// Dates
|
|
67
|
+
'scheduled_at' => $this->scheduled_at,
|
|
68
|
+
'started_at' => $this->started_at,
|
|
69
|
+
'completed_at' => $this->completed_at,
|
|
70
|
+
'updated_at' => $this->updated_at,
|
|
71
|
+
'created_at' => $this->created_at,
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Inject the abstract 'maintenance-subject' type into the embedded maintainable object
|
|
77
|
+
* so Ember Data can resolve the correct polymorphic model.
|
|
78
|
+
*
|
|
79
|
+
* subject_type must be the Ember Data model name for the concrete subtype,
|
|
80
|
+
* e.g. 'maintenance-subject-vehicle' or 'maintenance-subject-equipment'.
|
|
81
|
+
* We use the bare PHP class basename so we get 'vehicle' / 'equipment' rather
|
|
82
|
+
* than the full 'fleet-ops:vehicle' string that toEmberResourceType() would return.
|
|
83
|
+
*/
|
|
84
|
+
protected function setMaintainableType(?array $resolved): ?array
|
|
85
|
+
{
|
|
86
|
+
if (empty($resolved)) {
|
|
87
|
+
return $resolved;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
$bareSlug = Str::kebab(class_basename($this->maintainable_type ?? ''));
|
|
91
|
+
|
|
92
|
+
data_set($resolved, 'type', 'maintenance-subject-' . $bareSlug);
|
|
93
|
+
data_set($resolved, 'subject_type', 'maintenance-subject-' . $bareSlug);
|
|
94
|
+
|
|
95
|
+
return $resolved;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Inject the abstract 'facilitator' type into the embedded performed_by object
|
|
100
|
+
* so Ember Data can resolve the correct polymorphic model.
|
|
101
|
+
*
|
|
102
|
+
* facilitator_type must be the Ember Data model name for the concrete subtype,
|
|
103
|
+
* e.g. 'facilitator-vendor' or 'facilitator-contact'.
|
|
104
|
+
* We use the bare PHP class basename so we get 'vendor' / 'contact' rather
|
|
105
|
+
* than the full 'fleet-ops:vendor' string that toEmberResourceType() would return.
|
|
106
|
+
*/
|
|
107
|
+
protected function setPerformedByType(?array $resolved): ?array
|
|
108
|
+
{
|
|
109
|
+
if (empty($resolved)) {
|
|
110
|
+
return $resolved;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
$bareSlug = Str::kebab(class_basename($this->performed_by_type ?? ''));
|
|
114
|
+
|
|
115
|
+
data_set($resolved, 'type', 'facilitator-' . $bareSlug);
|
|
116
|
+
data_set($resolved, 'facilitator_type', 'facilitator-' . $bareSlug);
|
|
117
|
+
|
|
118
|
+
return $resolved;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the appropriate resource transformer for a polymorphic morph target
|
|
123
|
+
* and return its array representation.
|
|
124
|
+
*/
|
|
125
|
+
protected function transformMorphResource($model): ?array
|
|
126
|
+
{
|
|
127
|
+
if (!$model) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
$resourceClass = \Fleetbase\Support\Find::httpResourceForModel($model);
|
|
132
|
+
if ($resourceClass) {
|
|
133
|
+
return (new $resourceClass($model))->resolve();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return (new \Illuminate\Http\Resources\Json\JsonResource($model))->resolve();
|
|
137
|
+
}
|
|
138
|
+
}
|