@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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import Service from '@ember/service';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { isNone } from '@ember/utils';
|
|
6
|
+
import { addMinutes, areIntervalsOverlapping } from 'date-fns';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* SchedulingService
|
|
10
|
+
*
|
|
11
|
+
* Central domain-logic service for the unified order scheduler.
|
|
12
|
+
* Encapsulates conflict detection, best-fit insertion, single/bulk order
|
|
13
|
+
* assignment, and a client-side undo/redo history stack.
|
|
14
|
+
*
|
|
15
|
+
* Usage in a controller:
|
|
16
|
+
* @service scheduling;
|
|
17
|
+
*
|
|
18
|
+
* const result = await this.scheduling.assignOrder(order, driverId, scheduledAt);
|
|
19
|
+
* if (result.hasConflict) { ... }
|
|
20
|
+
*/
|
|
21
|
+
export default class SchedulingService extends Service {
|
|
22
|
+
@service store;
|
|
23
|
+
@service fetch;
|
|
24
|
+
@service notifications;
|
|
25
|
+
|
|
26
|
+
// -------------------------------------------------------------------------
|
|
27
|
+
// Undo / Redo History Stack
|
|
28
|
+
// -------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/** @type {Array<{orderId, previousDate, newDate, previousDriverId, newDriverId}>} */
|
|
31
|
+
@tracked _history = [];
|
|
32
|
+
|
|
33
|
+
/** @type {Array<{orderId, previousDate, newDate, previousDriverId, newDriverId}>} */
|
|
34
|
+
@tracked _future = [];
|
|
35
|
+
|
|
36
|
+
/** True when there is at least one action to undo. */
|
|
37
|
+
get canUndo() {
|
|
38
|
+
return this._history.length > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True when there is at least one action to redo. */
|
|
42
|
+
get canRedo() {
|
|
43
|
+
return this._future.length > 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Push a new action onto the history stack.
|
|
48
|
+
* Clears the redo stack — a new action invalidates any future states.
|
|
49
|
+
* Capped at 20 entries to prevent unbounded memory growth.
|
|
50
|
+
*
|
|
51
|
+
* @param {Object} entry
|
|
52
|
+
*/
|
|
53
|
+
_pushHistory(entry) {
|
|
54
|
+
const capped = [...this._history, entry].slice(-20);
|
|
55
|
+
this._history = capped;
|
|
56
|
+
this._future = [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Revert the most recent scheduling action.
|
|
61
|
+
*/
|
|
62
|
+
@action async undo() {
|
|
63
|
+
if (!this.canUndo) return;
|
|
64
|
+
const history = [...this._history];
|
|
65
|
+
const entry = history.pop();
|
|
66
|
+
this._history = history;
|
|
67
|
+
this._future = [entry, ...this._future];
|
|
68
|
+
await this._applyHistoryEntry(entry, true);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Re-apply the most recently undone action.
|
|
73
|
+
*/
|
|
74
|
+
@action async redo() {
|
|
75
|
+
if (!this.canRedo) return;
|
|
76
|
+
const [entry, ...rest] = this._future;
|
|
77
|
+
this._future = rest;
|
|
78
|
+
this._history = [...this._history, entry];
|
|
79
|
+
await this._applyHistoryEntry(entry, false);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Apply a history entry in either the undo (reverse) or redo (forward) direction.
|
|
84
|
+
*
|
|
85
|
+
* @param {Object} entry
|
|
86
|
+
* @param {boolean} isUndo true = restore previous state, false = re-apply new state
|
|
87
|
+
*/
|
|
88
|
+
async _applyHistoryEntry(entry, isUndo) {
|
|
89
|
+
const order = this.store.peekRecord('order', entry.orderId);
|
|
90
|
+
if (!order) return;
|
|
91
|
+
const targetDate = isUndo ? entry.previousDate : entry.newDate;
|
|
92
|
+
const targetDriver = isUndo ? entry.previousDriverId : entry.newDriverId;
|
|
93
|
+
const payload = {
|
|
94
|
+
order: order.id,
|
|
95
|
+
scheduled_at: targetDate instanceof Date ? targetDate.toISOString() : targetDate,
|
|
96
|
+
};
|
|
97
|
+
if (targetDriver !== undefined) {
|
|
98
|
+
payload.driver_id = targetDriver;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
await this.fetch.patch('orders/schedule', payload);
|
|
102
|
+
order.set('scheduled_at', targetDate);
|
|
103
|
+
if (targetDriver !== undefined) {
|
|
104
|
+
order.set('driver_assigned_uuid', targetDriver);
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.notifications.serverError(error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
// Conflict Detection
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns all orders assigned to `driverId` whose time window overlaps
|
|
117
|
+
* with the proposed [start, end] interval.
|
|
118
|
+
*
|
|
119
|
+
* The `excludeOrderId` parameter allows the caller to exclude the order
|
|
120
|
+
* being rescheduled from its own conflict check.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} driverId
|
|
123
|
+
* @param {Date} start
|
|
124
|
+
* @param {Date} end
|
|
125
|
+
* @param {string} [excludeOrderId]
|
|
126
|
+
* @returns {Array<Model>} Conflicting order records
|
|
127
|
+
*/
|
|
128
|
+
checkConflicts(driverId, start, end, excludeOrderId = null) {
|
|
129
|
+
return this.store.peekAll('order').filter((o) => {
|
|
130
|
+
if (o.id === excludeOrderId) return false;
|
|
131
|
+
if (o.driver_assigned_uuid !== driverId) return false;
|
|
132
|
+
if (isNone(o.scheduled_at)) return false;
|
|
133
|
+
const oStart = new Date(o.scheduled_at);
|
|
134
|
+
const oEnd = addMinutes(oStart, o.estimated_duration ?? 60);
|
|
135
|
+
return areIntervalsOverlapping({ start, end }, { start: oStart, end: oEnd }, { inclusive: false });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// -------------------------------------------------------------------------
|
|
140
|
+
// Best-Fit Insertion
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Calculates the optimal `scheduled_at` time for `order` when dropped
|
|
145
|
+
* onto a driver's row without a specific time target.
|
|
146
|
+
*
|
|
147
|
+
* Primary strategy: POST to the Fleetbase routing API for a travel-time-
|
|
148
|
+
* aware recommendation.
|
|
149
|
+
* Fallback: Append the order after the last existing order for that driver.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} driverId
|
|
152
|
+
* @param {Model} order
|
|
153
|
+
* @returns {Promise<Date>}
|
|
154
|
+
*/
|
|
155
|
+
async findBestFit(driverId, order) {
|
|
156
|
+
// Try the server-side routing API first
|
|
157
|
+
try {
|
|
158
|
+
const response = await this.fetch.post('orders/best-fit', {
|
|
159
|
+
driver_id: driverId,
|
|
160
|
+
order_id: order.id,
|
|
161
|
+
});
|
|
162
|
+
if (response?.recommended_scheduled_at) {
|
|
163
|
+
return new Date(response.recommended_scheduled_at);
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// Non-critical — fall through to heuristic
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Heuristic fallback: find the end time of the last order for this driver
|
|
170
|
+
const driverOrders = this.store
|
|
171
|
+
.peekAll('order')
|
|
172
|
+
.filter((o) => o.driver_assigned_uuid === driverId && !isNone(o.scheduled_at))
|
|
173
|
+
.sortBy('scheduled_at');
|
|
174
|
+
|
|
175
|
+
if (driverOrders.length === 0) {
|
|
176
|
+
// No existing orders — use now as the starting point
|
|
177
|
+
return new Date();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const lastOrder = driverOrders[driverOrders.length - 1];
|
|
181
|
+
return addMinutes(new Date(lastOrder.scheduled_at), lastOrder.estimated_duration ?? 60);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
// Single Order Assignment
|
|
186
|
+
// -------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Assigns a single order to a driver at a given datetime.
|
|
190
|
+
*
|
|
191
|
+
* Flow:
|
|
192
|
+
* 1. Conflict check (unless `options.skipConflictCheck` is true)
|
|
193
|
+
* 2. Persist via a targeted PATCH to the schedule endpoint
|
|
194
|
+
* (does NOT trigger dispatch or change the order status)
|
|
195
|
+
* 3. Reflect the change in the local Ember Data store
|
|
196
|
+
* 4. On failure: notify and return error
|
|
197
|
+
*
|
|
198
|
+
* @param {Model} order
|
|
199
|
+
* @param {string} driverId
|
|
200
|
+
* @param {Date} scheduledAt
|
|
201
|
+
* @param {Object} [options]
|
|
202
|
+
* @param {boolean} [options.skipConflictCheck=false]
|
|
203
|
+
* @returns {Promise<{hasConflict: boolean, conflicts?: Array, error?: Error}>}
|
|
204
|
+
*/
|
|
205
|
+
async assignOrder(order, driverId, scheduledAt, options = {}) {
|
|
206
|
+
const duration = order.estimated_duration ?? 60;
|
|
207
|
+
const end = addMinutes(scheduledAt, duration);
|
|
208
|
+
const previousDate = order.scheduled_at;
|
|
209
|
+
const previousDriverId = order.driver_assigned_uuid;
|
|
210
|
+
|
|
211
|
+
if (!options.skipConflictCheck && driverId) {
|
|
212
|
+
const conflicts = this.checkConflicts(driverId, scheduledAt, end, order.id);
|
|
213
|
+
if (conflicts.length > 0) {
|
|
214
|
+
return { hasConflict: true, conflicts };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const payload = {
|
|
219
|
+
order: order.id,
|
|
220
|
+
scheduled_at: scheduledAt instanceof Date ? scheduledAt.toISOString() : scheduledAt,
|
|
221
|
+
};
|
|
222
|
+
if (driverId) {
|
|
223
|
+
payload.driver_id = driverId;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
// Use the dedicated schedule endpoint so the backend only sets
|
|
228
|
+
// scheduled_at / driver_assigned_uuid without triggering dispatch.
|
|
229
|
+
await this.fetch.patch('orders/schedule', payload);
|
|
230
|
+
|
|
231
|
+
// Reflect the change locally without triggering another save
|
|
232
|
+
order.set('scheduled_at', scheduledAt);
|
|
233
|
+
if (driverId) {
|
|
234
|
+
order.set('driver_assigned_uuid', driverId);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this._pushHistory({
|
|
238
|
+
orderId: order.id,
|
|
239
|
+
previousDate,
|
|
240
|
+
newDate: scheduledAt,
|
|
241
|
+
previousDriverId,
|
|
242
|
+
newDriverId: driverId,
|
|
243
|
+
});
|
|
244
|
+
return { hasConflict: false };
|
|
245
|
+
} catch (error) {
|
|
246
|
+
this.notifications.serverError(error);
|
|
247
|
+
return { hasConflict: false, error };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Removes the schedule assignment from an order (sets `scheduled_at` to null).
|
|
253
|
+
*
|
|
254
|
+
* @param {Model} order
|
|
255
|
+
*/
|
|
256
|
+
async unscheduleOrder(order) {
|
|
257
|
+
const previousDate = order.scheduled_at;
|
|
258
|
+
const previousDriverId = order.driver_assigned_uuid;
|
|
259
|
+
try {
|
|
260
|
+
await this.fetch.patch('orders/schedule', { order: order.id, scheduled_at: null });
|
|
261
|
+
order.set('scheduled_at', null);
|
|
262
|
+
this._pushHistory({
|
|
263
|
+
orderId: order.id,
|
|
264
|
+
previousDate,
|
|
265
|
+
newDate: null,
|
|
266
|
+
previousDriverId,
|
|
267
|
+
newDriverId: null,
|
|
268
|
+
});
|
|
269
|
+
} catch (error) {
|
|
270
|
+
this.notifications.serverError(error);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// -------------------------------------------------------------------------
|
|
275
|
+
// Bulk Assignment
|
|
276
|
+
// -------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Assigns multiple orders to a driver on a given date in a single operation.
|
|
280
|
+
*
|
|
281
|
+
* Attempts a batch API call first. On failure, falls back to sequential saves.
|
|
282
|
+
*
|
|
283
|
+
* @param {Array<Model>} orders
|
|
284
|
+
* @param {string} driverId
|
|
285
|
+
* @param {Date} date
|
|
286
|
+
*/
|
|
287
|
+
async bulkAssign(orders, driverId, date) {
|
|
288
|
+
const orderIds = orders.map((o) => o.id);
|
|
289
|
+
try {
|
|
290
|
+
await this.fetch.post('orders/bulk-schedule', {
|
|
291
|
+
order_ids: orderIds,
|
|
292
|
+
driver_id: driverId,
|
|
293
|
+
scheduled_at: date instanceof Date ? date.toISOString() : date,
|
|
294
|
+
});
|
|
295
|
+
} catch {
|
|
296
|
+
// Fallback: sequential saves
|
|
297
|
+
for (const order of orders) {
|
|
298
|
+
try {
|
|
299
|
+
order.set('scheduled_at', date);
|
|
300
|
+
order.set('driver_assigned_uuid', driverId);
|
|
301
|
+
await order.save();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
order.rollbackAttributes();
|
|
304
|
+
this.notifications.serverError(error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Reflect the bulk change in the local store
|
|
311
|
+
orders.forEach((order) => {
|
|
312
|
+
order.set('scheduled_at', date);
|
|
313
|
+
order.set('driver_assigned_uuid', driverId);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -1,9 +1,65 @@
|
|
|
1
|
-
import ResourceActionService from '@fleetbase/ember-core/services/resource-action';
|
|
1
|
+
import ResourceActionService, { inject as service } from '@fleetbase/ember-core/services/resource-action';
|
|
2
2
|
import leafletIcon from '@fleetbase/ember-core/utils/leaflet-icon';
|
|
3
3
|
import config from 'ember-get-config';
|
|
4
4
|
import { action } from '@ember/object';
|
|
5
|
+
import { isArray } from '@ember/array';
|
|
6
|
+
import { dasherize } from '@ember/string';
|
|
5
7
|
|
|
6
8
|
export default class VehicleActionsService extends ResourceActionService {
|
|
9
|
+
@service('universe/menu-service') menuService;
|
|
10
|
+
@service maintenanceScheduleActions;
|
|
11
|
+
@service workOrderActions;
|
|
12
|
+
@service maintenanceActions;
|
|
13
|
+
|
|
14
|
+
get registeredTabs() {
|
|
15
|
+
const registeredTabs = this.menuService.getMenuItems('fleet-ops:component:vehicle:details');
|
|
16
|
+
return (isArray(registeredTabs) ? registeredTabs : [])
|
|
17
|
+
.map((tab) => {
|
|
18
|
+
delete tab.route;
|
|
19
|
+
if (!tab.key) {
|
|
20
|
+
tab.key = tab.id ?? dasherize(tab.label ?? tab.title);
|
|
21
|
+
}
|
|
22
|
+
return tab;
|
|
23
|
+
})
|
|
24
|
+
.filter((tab) => !tab.component);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get panelTabs() {
|
|
28
|
+
return [
|
|
29
|
+
{
|
|
30
|
+
key: 'overview',
|
|
31
|
+
label: 'Overview',
|
|
32
|
+
component: 'vehicle/details',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'positions',
|
|
36
|
+
label: 'Positions',
|
|
37
|
+
component: 'positions-replay',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'devices',
|
|
41
|
+
label: 'Devices',
|
|
42
|
+
component: 'device/manager',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'schedules',
|
|
46
|
+
label: 'Schedules',
|
|
47
|
+
component: 'vehicle/details/schedules',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'work-orders',
|
|
51
|
+
label: 'Work Orders',
|
|
52
|
+
component: 'vehicle/details/work-orders',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: 'maintenance-history',
|
|
56
|
+
label: 'Maintenance',
|
|
57
|
+
component: 'vehicle/details/maintenance-history',
|
|
58
|
+
},
|
|
59
|
+
...this.registeredTabs,
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
7
63
|
constructor() {
|
|
8
64
|
super(...arguments);
|
|
9
65
|
this.initialize('vehicle', { status: 'active' });
|
|
@@ -68,12 +124,7 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
68
124
|
},
|
|
69
125
|
},
|
|
70
126
|
],
|
|
71
|
-
tabs:
|
|
72
|
-
{
|
|
73
|
-
label: this.intl.t('common.overview'),
|
|
74
|
-
component: 'vehicle/details',
|
|
75
|
-
},
|
|
76
|
-
],
|
|
127
|
+
tabs: this.panelTabs,
|
|
77
128
|
...options,
|
|
78
129
|
});
|
|
79
130
|
},
|
|
@@ -120,6 +171,18 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
120
171
|
},
|
|
121
172
|
};
|
|
122
173
|
|
|
174
|
+
@action scheduleMaintenance(vehicle) {
|
|
175
|
+
this.maintenanceScheduleActions.modal.create({ subject: vehicle });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@action createWorkOrder(vehicle) {
|
|
179
|
+
this.workOrderActions.modal.create({ target: vehicle });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@action logMaintenance(vehicle) {
|
|
183
|
+
this.maintenanceActions.modal.create({ maintainable: vehicle });
|
|
184
|
+
}
|
|
185
|
+
|
|
123
186
|
@action locate(vehicle, options = {}) {
|
|
124
187
|
const { latitude, longitude, location } = vehicle;
|
|
125
188
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { inject as service } from '@ember/service';
|
|
2
|
+
import OrchestrationEngineInterfaceService from './orchestration-engine-interface';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* VroomOrchestrationEngineService
|
|
6
|
+
*
|
|
7
|
+
* Frontend adapter for the VROOM orchestration engine. Delegates all computation
|
|
8
|
+
* to the backend OrchestrationController — the frontend adapter's role is to
|
|
9
|
+
* call the correct API endpoint and return the normalized result.
|
|
10
|
+
*
|
|
11
|
+
* This service is registered into the allocation-engine registry via the
|
|
12
|
+
* register-vroom-allocation instance initializer.
|
|
13
|
+
*/
|
|
14
|
+
export default class VroomOrchestrationEngineService extends OrchestrationEngineInterfaceService {
|
|
15
|
+
@service fetch;
|
|
16
|
+
@service notifications;
|
|
17
|
+
|
|
18
|
+
name = 'VROOM';
|
|
19
|
+
identifier = 'vroom';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Run the VROOM orchestration via the backend API.
|
|
23
|
+
*
|
|
24
|
+
* @param {Array} orders Array of order records (or order public_ids).
|
|
25
|
+
* @param {Array} vehicles Array of vehicle records (or vehicle public_ids).
|
|
26
|
+
* @param {Object} options Options forwarded to the backend engine.
|
|
27
|
+
* @returns {Promise<{assignments: Array, unassigned: Array, summary: Object}>}
|
|
28
|
+
*/
|
|
29
|
+
async allocate(orders = [], vehicles = [], options = {}) {
|
|
30
|
+
const orderIds = orders.map((o) => (typeof o === 'string' ? o : o.public_id));
|
|
31
|
+
const vehicleIds = vehicles.map((v) => (typeof v === 'string' ? v : v.public_id));
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = await this.fetch.post('fleet-ops/orchestrator/run', {
|
|
35
|
+
order_ids: orderIds,
|
|
36
|
+
vehicle_ids: vehicleIds,
|
|
37
|
+
options: { ...options, engine: 'vroom' },
|
|
38
|
+
});
|
|
39
|
+
return result;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this.notifications.serverError(error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import ResourceActionService from '@fleetbase/ember-core/services/resource-action';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
2
4
|
|
|
3
5
|
export default class WorkOrderActionsService extends ResourceActionService {
|
|
6
|
+
@service fetch;
|
|
7
|
+
@service notifications;
|
|
4
8
|
constructor() {
|
|
5
9
|
super(...arguments);
|
|
6
10
|
this.initialize('work-order');
|
|
@@ -46,6 +50,82 @@ export default class WorkOrderActionsService extends ResourceActionService {
|
|
|
46
50
|
},
|
|
47
51
|
};
|
|
48
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Packs completion data into workOrder.meta.completion_data before saving.
|
|
55
|
+
*
|
|
56
|
+
* Monetary values (laborCost, partsCost, tax) come from the MoneyInput
|
|
57
|
+
* component's @onChange callback, which already emits **cents** (integers).
|
|
58
|
+
* No further conversion is needed — values are stored as-is.
|
|
59
|
+
*
|
|
60
|
+
* Called by the edit/new controllers before workOrder.save().
|
|
61
|
+
*
|
|
62
|
+
* @param {WorkOrderModel} workOrder
|
|
63
|
+
* @param {Object} completionData — plain object from the form component's @onCompletionChange
|
|
64
|
+
*/
|
|
65
|
+
prepareForSave(workOrder, completionData = {}) {
|
|
66
|
+
if (workOrder.status !== 'closed' || !completionData) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Ensure values are integers (MoneyInput emits cents already)
|
|
71
|
+
const toIntCents = (value) => {
|
|
72
|
+
if (value === null || value === undefined) return null;
|
|
73
|
+
const parsed = parseInt(value, 10);
|
|
74
|
+
return isNaN(parsed) ? null : parsed;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const laborCostCents = toIntCents(completionData.laborCost);
|
|
78
|
+
const partsCostCents = toIntCents(completionData.partsCost);
|
|
79
|
+
const taxCents = toIntCents(completionData.tax);
|
|
80
|
+
const totalCostCents = laborCostCents !== null || partsCostCents !== null || taxCents !== null ? (laborCostCents ?? 0) + (partsCostCents ?? 0) + (taxCents ?? 0) : null;
|
|
81
|
+
|
|
82
|
+
workOrder.meta = {
|
|
83
|
+
...(workOrder.meta ?? {}),
|
|
84
|
+
completion_data: {
|
|
85
|
+
odometer: completionData.odometer ? parseFloat(completionData.odometer) : null,
|
|
86
|
+
engine_hours: completionData.engineHours ? parseFloat(completionData.engineHours) : null,
|
|
87
|
+
labor_cost: laborCostCents,
|
|
88
|
+
parts_cost: partsCostCents,
|
|
89
|
+
tax: taxCents,
|
|
90
|
+
total_cost: totalCostCents,
|
|
91
|
+
currency: workOrder.currency ?? 'USD',
|
|
92
|
+
notes: completionData.notes ?? null,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@action sendEmail(workOrder, options = {}) {
|
|
98
|
+
const assignee = workOrder.assignee;
|
|
99
|
+
console.log('Assignee:', assignee);
|
|
100
|
+
const vendorName = assignee?.name ?? workOrder.assignee_name ?? null;
|
|
101
|
+
const vendorEmail = assignee?.email ?? null;
|
|
102
|
+
const vendorPhone = assignee?.phone ?? null;
|
|
103
|
+
|
|
104
|
+
this.modalsManager.show('modals/send-work-order', {
|
|
105
|
+
title: 'Send Work Order to Vendor',
|
|
106
|
+
acceptButtonText: 'Send Work Order',
|
|
107
|
+
acceptButtonIcon: 'paper-plane',
|
|
108
|
+
acceptButtonDisabled: !vendorEmail,
|
|
109
|
+
declineButtonText: 'Cancel',
|
|
110
|
+
workOrder,
|
|
111
|
+
vendorName,
|
|
112
|
+
vendorEmail,
|
|
113
|
+
vendorPhone,
|
|
114
|
+
confirm: async (modal) => {
|
|
115
|
+
modal.startLoading();
|
|
116
|
+
try {
|
|
117
|
+
const response = await this.fetch.post(`work-orders/${workOrder.id}/send`);
|
|
118
|
+
this.notifications.success(response?.message ?? `Work order sent to ${vendorName ?? 'vendor'} successfully.`);
|
|
119
|
+
modal.done();
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.notifications.serverError(error);
|
|
122
|
+
modal.stopLoading();
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
...options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
49
129
|
modal = {
|
|
50
130
|
create: (attributes = {}, options = {}, saveOptions = {}) => {
|
|
51
131
|
const workOrder = this.createNewInstance(attributes);
|