v0.6.39 ~ added engine registry yield + use correct default asset url…#213
Merged
v0.6.39 ~ added engine registry yield + use correct default asset url…#213
Conversation
… is a real DB column not a computed accessor
…/cell/base across all maintenance controllers
…rvice, calendar, namespace 1. Rename scheduleActions → maintenanceScheduleActions - addon/services/schedule-actions.js → maintenance-schedule-actions.js - app/services/maintenance-schedule-actions.js re-export added - All @service injections and this.scheduleActions refs updated in schedules/index, schedules/index/details, vehicle-actions 2. Convert @Tracked actionButtons/bulkActions/columns → getters - All 5 maintenance index controllers now use get() instead of @Tracked - Prevents Glimmer reactivity assertion errors on render 3. Fix broken @service menuService injection - All 5 details controllers: @service menuService → @service('universe/menu-service') menuService 4. Rename schedule/ component namespace → maintenance-schedule/ - addon/components/schedule/ → addon/components/maintenance-schedule/ - app/components/maintenance-schedule/ re-exports added - Templates updated: Schedule::Form/Details → MaintenanceSchedule::Form/Details - Class names updated to MaintenanceScheduleFormComponent etc. 5. Add calendar visualization to MaintenanceSchedule::Details - details.js: computeOccurrences() + buildCalendarGrid() helpers - Navigable month calendar with scheduled dates highlighted in blue - Upcoming occurrences list (next 6 dates) - Only shown for time-based schedules (interval_method === 'time')
…-orders index getters The sed-based getter conversion left actionButtons and bulkActions getters without their closing } in two controllers: - maintenances/index.js: actionButtons and bulkActions both missing } - work-orders/index.js: bulkActions missing } schedules/index.js, equipment/index.js, and parts/index.js were unaffected.
…O tab, vehicle prefill, cost-panel re-export - ProcessMaintenanceTriggers: auto-generate WO code (WO-YYYYMMDD-XXXXX) and set opened_at on creation - WorkOrder::Details: full details component with overview, assignment, scheduling, and cost breakdown panels (cost breakdown reads from meta.completion_data, shown only when status is closed) - WorkOrder::Form: add prepareForSave action that packs completion tracked fields into meta before save - work-orders new/edit controllers: track formComponent and call prepareForSave before workOrder.save() - Schedules details: add Work Orders tab (route + template) showing all WOs created by this schedule - vehicle-actions: fix subject_type to use namespaced type strings (fleet-ops:vehicle etc) so schedule form pre-selects the correct asset type when opened from the vehicles index row dropdown - app/components/maintenance/cost-panel.js: add missing re-export shim - app/components/maintenance/panel-header.js: add missing re-export shim
…hip accessors
Replace all raw _type / _uuid attr reads and writes with proper
@belongsTo relationship accessors across the maintenance module.
Changes:
- addon/models/maintenance-schedule.js
• Replace subject_type/subject_uuid/subject_name attrs with
@belongsTo('maintenance-subject', {polymorphic:true}) subject
• Replace default_assignee_type/default_assignee_uuid attrs with
@belongsTo('facilitator', {polymorphic:true}) default_assignee
• Add interval_method attr (was missing)
• Remove obsolete raw type/uuid attrs
- addon/components/maintenance-schedule/form.js
• Add MODEL_TO_TYPE + ASSIGNEE_MODEL_TO_TYPE reverse-lookup maps
• Constructor now reads type from resource.subject.constructor.modelName
and resource.default_assignee.constructor.modelName instead of raw attrs
• onSubjectTypeChange / onAssigneeTypeChange clear the relationship
instead of writing _type/_uuid
• assignSubject / assignDefaultAssignee set the relationship only
- addon/components/maintenance-schedule/form.hbs
• @selectedModel binding updated from defaultAssignee → default_assignee
- addon/components/maintenance-schedule/details.hbs
• Asset field reads subject.displayName|name instead of subject_name
- addon/components/work-order/form.js
• Add TARGET_MODEL_TO_TYPE + ASSIGNEE_MODEL_TO_TYPE reverse-lookup maps
• Constructor reads type from target/assignee relationship model names
• onTargetTypeChange / onAssigneeTypeChange clear relationship only
• assignTarget / assignAssignee set relationship only
- addon/components/work-order/details.hbs
• Assignment panel uses target.displayName / assignee.displayName
• Schedule panel uses schedule.name instead of schedule_uuid
- addon/components/maintenance/form.js
• Add MAINTAINABLE_MODEL_TO_TYPE + PERFORMED_BY_MODEL_TO_TYPE maps
• Constructor reads type from maintainable/performed_by relationship
• onMaintainableTypeChange / onPerformedByTypeChange clear relationship
• assignMaintainable / assignPerformedBy set relationship only
- addon/components/maintenance/form.hbs
• @selectedModel binding updated from performedBy → performed_by
- addon/components/maintenance/details.hbs
• Maintainable / Performed By fields use relationship accessors
- addon/services/vehicle-actions.js
• scheduleMaintenance: pass { subject: vehicle } only
• createWorkOrder: pass { target: vehicle } only
• logMaintenance: pass { maintainable: vehicle } only
- addon/components/vehicle/details/schedules.js
• Fix service injection: @service scheduleActions → @service('maintenance-schedule-actions')
- addon/components/vehicle/details/schedules.hbs
• Add Schedule button passes { subject: @vehicle }
- addon/components/vehicle/details/work-orders.hbs
• Create Work Order button passes { target: @vehicle }
- addon/components/vehicle/details/maintenance-history.hbs
• Log Maintenance button passes { maintainable: @vehicle }
…se/fleetops into feat/complete-maintenance-module
… non-embedded relationship display Three fixes applied: 1. Remove console.fleet-ops. route prefix (controllers + routes) All transitionTo and tab route strings in addon/controllers/maintenance/ and addon/routes/maintenance/ were incorrectly prefixed with 'console.fleet-ops.'. The host router adds the engine prefix automatically, so the routes should start with 'maintenance.' directly. Fixed across all 15 controller files and 8 route files. 2. Add _name fallback attrs to details templates The subject, default_assignee, target, assignee, maintainable, and performed_by relationships are not sideloaded in the server response. Details templates now use (or relationship.displayName relationship.name resource._name) so the server-side convenience field is shown when the relationship object has not yet been loaded. 3. Serializer embedded attrs cleanup (fleetops-data companion commit) Removed embedded: always declarations for non-sideloaded polymorphic relationships in maintenance-schedule, work-order, and maintenance serializers to prevent Ember Data from expecting nested objects that the server never returns.
…ic relationships Backend changes: - Add $with = ['subject', 'defaultAssignee'] to MaintenanceSchedule model - Add $with = ['target', 'assignee'] to WorkOrder model (remove from $hidden) - Add $with = ['maintainable', 'performedBy'] to Maintenance model (remove from $hidden) - Create Http/Resources/v1/MaintenanceSchedule.php resource transformer - Embeds subject and default_assignee via whenLoaded() - Outputs raw PHP class name for _type fields (serializer maps them) - Create Http/Resources/v1/WorkOrder.php resource transformer - Embeds target and assignee via whenLoaded() - Create Http/Resources/v1/Maintenance.php resource transformer - Embeds maintainable and performed_by via whenLoaded() The resource transformers ensure that polymorphic relationship objects are always included in API responses, enabling the frontend to use embedded: always in its serializers without needing a separate request to load the related records.
…formers All existing FleetbaseResource subclasses declare toArray($request) without a return type annotation. Adding ': array' caused PHP to dispatch withCustomFields() through __call() on the JsonResource base class instead of the concrete class, resulting in BadMethodCallException.
withCustomFields() is a method provided by the HasCustomFields trait on the model. FleetbaseResource proxies it via DelegatesToResource to the underlying model instance. WorkOrder and Maintenance already had the trait applied; MaintenanceSchedule was missing both the import and the use statement, causing the BadMethodCallException when the resource transformer called withCustomFields().
…c objects Ember Data resolves the model for an embedded polymorphic belongsTo by reading the 'type' field inside the embedded object. Without it, Ember Data fell back to the model's own 'type' attribute (e.g. 'fliit_asset') which is not a valid model name. Each resource transformer now: - Calls Utils::toEmberResourceType() to convert PHP class names to shorthands for the *_type fields on the parent record. - Injects type: 'maintenance-subject' (or 'facilitator') into the embedded object so Ember Data resolves the correct abstract polymorphic model. - Injects subject_type / facilitator_type into the embedded object with the concrete subtype (e.g. 'maintenance-subject-vehicle'). Mirrors the exact pattern used by Order::setFacilitatorType().
…ttern, add cents conversion
…se/fleetops into feat/complete-maintenance-module
… maintenance module - cost-panel.hbs: add @onchange to labor_cost, tax MoneyInput fields; add @onchange={{this.setDraftUnitCost}} to inline edit/add row MoneyInput; fix column widths using colgroup percentage layout; use format-currency helper (cents-based) throughout totals summary and read rows - cost-panel.js: add setDraftUnitCost @action to receive cents from MoneyInput @onchange; document that draftUnitCost and all monetary values are in cents; clarify startEdit loads unit_cost already in cents from API - part/form.hbs: add @onchange={{fn (mut @resource.unit_cost)}} and @onchange={{fn (mut @resource.msrp)}} to MoneyInput fields - equipment/form.hbs: add @onchange={{fn (mut @resource.purchase_price)}} to MoneyInput field - work-order-actions.js: fix prepareForSave to not double-convert cents; MoneyInput @onchange already emits cents so toCents (x100) was wrong; replaced with toIntCents (parseInt only) - server models: add Money cast for all monetary attributes (cents storage) - migration: fix parts table monetary columns to BIGINT for cents storage
… Order email
## Import Functionality (Equipment, Parts, Maintenances, Work Orders, Maintenance Schedules)
### Backend
- Added Import classes: EquipmentImport, PartImport, MaintenanceImport, WorkOrderImport, MaintenanceScheduleImport
- Each implements Laravel Excel's ToModel and WithHeadingRow contracts
- Resolves related records (vehicles, vendors, equipment) by name/public_id
- Monetary values expected in cents (integers) matching Money cast storage
- Added createFromImport() static method to all five Eloquent models
- Added import() action to all five HTTP controllers (EquipmentController, PartController, MaintenanceController, WorkOrderController, MaintenanceScheduleController)
- Registered POST import routes for all five resources in routes.php
- POST /work-orders/{id}/send route registered for Send Work Order feature
### Frontend
- Added Import toolbar button (type: magic, icon: upload) to all five index controllers
- Import templates must be uploaded to S3 at:
flb-assets.s3.ap-southeast-1.amazonaws.com/import-templates/
- Fleetbase_Equipment_Import_Template.xlsx
- Fleetbase_Part_Import_Template.xlsx
- Fleetbase_Maintenance_Import_Template.xlsx
- Fleetbase_Work_Order_Import_Template.xlsx
- Fleetbase_Maintenance_Schedule_Import_Template.xlsx
## Send Work Order Email
### Backend
- Added WorkOrderDispatched Mail class (server/src/Mail/WorkOrderDispatched.php)
- Added work-order-dispatched Blade email template
- Added sendEmail() action to WorkOrderController
- Resolves assignee email, validates vendor has email, sends mail, logs activity
### Frontend
- Added sendEmail @action to WorkOrderActionsService
- Shows confirmation modal before sending
- POSTs to work-orders/{id}/send
- Shows success/error notification
- Added 'Send Work Order to Vendor' row action in work-orders index controller
## Monetary Attribute Type Fix (fleetops-data)
- Note: @attr('string') fix for monetary fields is in fleetops-data repo (separate commit)
…se/fleetops into feat/complete-maintenance-module
1. Monetary getters (laborCost, tax, partsCost, totalCost, lineTotal, draftLineTotal) now wrap values with parseInt(numbersOnly(...)) via a _toCents() helper. Required because monetary attrs are @attr('string') on the Ember model. Without this, string arithmetic produced NaN. 2. Line item mutations (add, edit, remove) are now purely in-memory. Previous implementation made individual API requests to fleet-ops/maintenances/{id}/line-items which was broken for new unsaved records and used the wrong URI prefix. Now each mutation writes a new array back onto @resource.line_items via _commitItems() and recomputes parts_cost and total_cost locally. The parent form save() call persists everything in a single request for both create and edit flows. 3. Removed ember-concurrency tasks from addLineItem, saveEdit, removeLineItem - now plain @actions. Updated HBS to use plain fn/onClick instead of perform for the affected buttons. 4. Removed @service fetch injection (no longer needed).
… details
- Created modals/send-work-order.hbs — a component-based confirmation modal
that displays:
- Work order subject (or target_name fallback), public_id, status, and
due date so the user can confirm they have the correct work order
- Vendor card showing name, email, and phone so the user knows exactly
who the email will be sent to
- Amber warning when no vendor is assigned
- Red error banner when the vendor has no email address on file, with
the Send button disabled to prevent a failed send
- Created modals/send-work-order.js — minimal Glimmer component class
- Updated work-order-actions.js sendEmail action:
- Replaced modalsManager.confirm() with modalsManager.show() using the
new component-based modal
- Resolves vendorName, vendorEmail, vendorPhone from workOrder.assignee
with fallback to assignee_name for display-only contexts
- Disables the accept button when vendorEmail is absent
- Runs the POST request inside modal.startLoading()/modal.done() for
proper loading state feedback
- Updated work-orders/index/details.js:
- Added sendEmail @action that delegates to workOrderActions.sendEmail
- Added 'Send to Vendor' button (paper-plane icon) as the first
actionButton in the details panel toolbar
… on boot - Removed 'slug' from $fillable — the work_orders table has no slug column, causing the SQLSTATE[42S22] column not found error on insert - Added boot() method with a static::creating() hook that generates a unique WO-XXXXXXXX code (8 random uppercase chars) when no code is provided - Added Illuminate\Support\Str import for Str::random()
…and maintenances - Add public_id column directly into create_equipments_table, create_parts_table, create_work_orders_table, and create_maintenances_table migrations so the column is always present on fresh installs (HasPublicId::generatePublicId queries this column before insert; missing column caused SQLSTATE 42S22 errors) - The existing add_public_id_to_maintenance_tables migration already has Schema::hasColumn guards so it remains safe to run on both fresh and existing DBs - Remove photo_uuid from Equipment and Part fillable arrays (no such column exists in either table; caused SQLSTATE 42S22 on insert) - Remove slug from Maintenance fillable (no slug column in maintenances table) - Change unit_cost/msrp in create_parts_table migration from decimal(12,2) to bigInteger (cents) to match the Money cast and monetary storage standard - Guard fix_monetary_columns_in_parts_table migration with column type check so it is idempotent on fresh installs that already have bigInteger columns
- Revert the incorrect public_id additions to the original create_* migrations. Those migrations have already shipped in previous releases and would never re-run on existing installs. The existing add_public_id_to_maintenance_tables migration (with Schema::hasColumn guards) is the correct mechanism for existing deployments. - Add new migration 2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables which adds the photo_uuid FK column to equipments and parts tables. Both models have a photo() BelongsTo relationship and getPhotoUrlAttribute() accessor but the backing column was missing from the original create migrations. The migration is guarded with Schema::hasTable and Schema::hasColumn checks so it is safe to run on both fresh and existing databases. - Restore photo_uuid to Equipment and Part $fillable arrays now that the column will exist after the migration runs.
…panels via universe
… labels, single expand/collapse toggle, default greedy engine - Add RouteSequencingEngine: optimize_routes mode now groups orders by already-assigned vehicle and sequences stops using nearest-neighbour TSP with pickup-before-dropoff precedence constraints, instead of re-running the allocation engine - OrchestrationController: optimize_routes mode uses RouteSequencingEngine instead of allocate() so vehicle assignments are preserved - orchestrator-workbench.js: add getStopLabel() action returning A/B/C... letter labels matching the map markers - plan-viewer.hbs: stop rows now show coloured letter badges (A, B, C...) matching the map markers instead of plain sequence numbers - plan-viewer.js/hbs: replace two expand/collapse buttons with a single toggle link that shows 'Expand all' or 'Collapse all' based on state - phase-builder.js: default engine changed from 'vroom' to 'greedy'
…ut DB commit - Frontend: pass prior_assignments (current proposedPlan) in every phase run request so the server can use them for phase-aware resolution - Server: accept prior_assignments keyed by order_id; for assign_drivers mode, use prior phase vehicle assignments to find eligible orders even before the plan is committed to the DB; augment Order models with the prior-phase vehicle_assigned_uuid so DriverAssignmentEngine can group correctly; for assign_vehicles mode, exclude orders already assigned in a prior uncommitted phase to prevent double-assignment
…mode DriverAssignmentEngine: - Change require_active_shift default to false — drivers are available by default unless explicitly restricted - Remove hard online + vehicle_uuid=null filters; treat online status and active shift as soft scoring bonuses instead of hard gates - Drivers with NO schedule items are always available; only drivers with a schedule but no active shift are excluded when require_active_shift is true - Add standalone mode: when orders have no vehicle_assigned_uuid, assign each order to the nearest available vehicle + best matching driver pair in a single pass (no prior Assign Vehicles phase required) - Add online bonus (+30) and proximity-aware vehicle selection in standalone mode OrchestrationController: - assign_drivers standalone: when no prior_assignments and no DB vehicle assignments exist, use all selected orders (not just vehicle-assigned ones) so the engine can run in standalone mode
location is a spatial Point cast attribute, not a relationship — ->with(['location']) throws 'undefined relationship' error. Removed from the with() call; ->location is accessed directly as a model attribute.
…ver relation location is a spatial Point cast on Driver, not a relationship. Removed from ->with(['location', 'scheduleItems']) in the prior-phase vehicle resolution query.
location is a spatial cast attribute on Vehicle and Driver models, NOT a relationship — it cannot be used in ->load() or ->with(). Replaced: $orders->load(['vehicle.driver.location', 'vehicle.location']) With: $orders->load(['vehicle', 'vehicle.driver']) The RouteSequencingEngine accesses $vehicle->location and $driver->location directly as cast attributes, which works correctly without eager-loading.
…+dropoff, per-vehicle routes, timeline, marker labels Issue 1 + 4 — Group by driver (not just vehicle) when assign_drivers phase ran: - Add @Tracked ranPhaseTypes Set to track which phase modes have been executed - Populate ranPhaseTypes in _runSinglePhase, reset on discardPlan/clearRunError - Add hasDriverPhase getter (true when assign_drivers has run) - Pass @hasDriverPhase to PlanViewer component - plan-viewer.hbs: show driver icon + driver name as primary label on route cards when hasDriverPhase is true; vehicle becomes the secondary sub-label - plan-viewer.js calendarResources: use driver name as resource row title when hasDriverPhase is true - plan-viewer.js renderResourceLabel: swap primary/secondary labels based on hasDriverPhase so timeline rows show driver first Issue 2 — Show pickup AND dropoff in route stop list (not just dropoff): - plan-viewer.hbs: render 'pickup → dropoff' address line when pickup.address is present; fall back to dropoff-only for delivery-only orders Issue 3 — Draw one route polyline per vehicle group (not one merged route): - Replace leaflet-routing-machine addRoutingControl() loop with direct OSRM route/v1 HTTP calls per group (fixes the single-control-per-map limitation documented at perliedman/leaflet-routing-machine#219) - Import polyline from @fleetbase/ember-core/utils/polyline and getRoutingHost from @fleetbase/ember-core/utils/get-routing-host - Decode OSRM polyline geometry and draw each group as L.polyline with the group's routeColor; fit map bounds to all drawn routes after rendering - _clearRoutingControls now removes plain Leaflet layers (not routing controls) Issue 5 — Match map marker letter labels to route list sequence labels: - planByVehicle getter now annotates each order item with _labelledStops: an array of stops where label = getStopLabel(orderIdx) for single-stop orders, or getStopLabel(orderIdx) + stopNumber for multi-stop orders (e.g. A, B, C for single-stop; A1/A2, B1/B2 for pickup+dropoff) - orchestrator-workbench.hbs map markers now iterate item._labelledStops instead of calling getOrderStops — labels now always match the route list
…ne rendering
Issue 1 (driver grouping):
- Root cause: optimize_routes called $orders->load(['vehicle','vehicle.driver'])
which reloaded from DB and OVERWROTE the in-memory setRelation('driver') set
by the augmentation loop, losing the uncommitted driver assignment.
- Fix: replace ->load() with a per-order selective load that only fetches from
DB when the vehicle relation is not already loaded (standalone mode).
Multi-phase runs preserve the prior-phase driver via the augmentation loop.
Issue 2 (only 2 of 4 polylines drawn):
- Root cause: _placeCoords required loc.type === 'Point' (strict GeoJSON check)
but LaravelMysqlSpatial SpatialExpression may serialize without the type field.
Also used truthy check (if lat && lng) which fails for coordinate 0.
- Fix: remove type requirement, check only for coordinates array; use isFinite()
instead of truthy check; add fallbacks for flat lat/lng and array formats.
Issue 3 (only dropoff shown in route stop list):
- Root cause: plan-viewer.hbs used item.order.payload.pickup.address directly,
which fails when the location format is not parsed by _placeCoords.
- Fix: use item._firstStop.address / item._lastStop.address (pre-computed by
_getOrderStops in planByVehicle getter) so address display is format-agnostic
and consistent with the map markers.
- Added _firstStop/_lastStop convenience properties to annotated order items
to avoid needing a sub/minus template helper for last-index access.
…, add waypoint POD/notes columns
…roup in plan-viewer
…lBack() when no active transaction
… maintenance/schedules, and operations/scheduler
…e-only, add constraint detail views, clarify route limits
…mns + DateTimeInput UI)
…llback: created_at)
JS fixes: - orchestrator-import.js: wire SAMPLE_ROW into downloadTemplate for dynamic XLSX generation (fixes no-unused-vars); wrap nullish coalescing in parens (prettier) - orchestrator-workbench.js: remove unused routeStyleForStatus import; prefix unused _vehicle param with underscore - orchestrator/plan-viewer.js: wrap ternary nullish coalescing branches in parens (prettier) - maintenance/schedules/index.js: wrap nullish coalescing in parens (prettier) HBS fixes (static → Tailwind, dynamic → html-safe): - orchestrator-workbench.hbs: h-80 / max-h-96 replace inline height styles - driver/schedule.hbs: text-[9px] replaces inline font-size:9px - maintenance/cost-panel.hbs: w-[X%] classes replace col width styles; remove autofocus attribute - maintenance/details.hbs: w-[X%] classes replace col width styles - orchestrator/order-pool.hbs: w-8 / class replaces inline width/vertical-align on search icon - orchestrator/plan-viewer.hbs: html-safe helper for all three dynamic routeColor style bindings - settings/scheduling.hbs: html-safe helper for dynamic template.color style binding - operations/scheduler/index.hbs: w-8 / class replaces inline width/vertical-align on search icon - orchestrator/resource-panel.hbs: div[role=button] → <button type=button> for vehicle and driver cards (require-presentational-children) - order/schedule-card.hbs: remove redundant (fn @onselect) wrapper → @onselect directly - templates/application.hbs: template-lint-disable no-potential-path-strings for registry key string
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
… for place, driver, vehicle icons