diff --git a/addon/routes/operations.js b/addon/routes/operations.js index 47e75a4a..674b464d 100644 --- a/addon/routes/operations.js +++ b/addon/routes/operations.js @@ -1,3 +1,25 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; -export default class OperationsRoute extends Route {} +export default class OperationsRoute extends Route { + @service('universe/menu-service') menuService; + @service universe; + + beforeModel(transition) { + if (transition.intent && transition.intent.url) { + // Here we will check if it's an actual virtual route instead of operations + const intendedUrl = transition.intent.url; + const intentSegments = intendedUrl.split('/'); + // Needs to match for section and slug + const section = intentSegments[2]; + const slug = intentSegments[3]; + // This is not a operations route check menu service for a virtual registration match + if (section !== 'operations') { + const menuItem = this.menuService.lookupMenuItem('engine:fleet-ops', slug, null, section); + if (menuItem) { + return this.universe.transitionMenuItem('console.fleet-ops.virtual', menuItem); + } + } + } + } +} diff --git a/composer.json b/composer.json index 1fe655a8..24d241ad 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/fleetops-api", - "version": "0.6.40", + "version": "0.6.41", "description": "Fleet & Transport Management Extension for Fleetbase", "keywords": [ "fleetbase-extension", diff --git a/extension.json b/extension.json index ca0441c4..2d4518a3 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Fleet-Ops", - "version": "0.6.40", + "version": "0.6.41", "description": "Fleet & Transport Management Extension for Fleetbase", "repository": "https://github.com/fleetbase/fleetops", "license": "AGPL-3.0-or-later", diff --git a/package.json b/package.json index cca3d89f..7c6db5f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/fleetops-engine", - "version": "0.6.40", + "version": "0.6.41", "description": "Fleet & Transport Management Extension for Fleetbase", "fleetbase": { "route": "fleet-ops" diff --git a/server/migrations/2026_04_20_000001_make_orchestrator_priority_nullable_on_orders_table.php b/server/migrations/2026_04_20_000001_make_orchestrator_priority_nullable_on_orders_table.php new file mode 100644 index 00000000..b1cd91fb --- /dev/null +++ b/server/migrations/2026_04_20_000001_make_orchestrator_priority_nullable_on_orders_table.php @@ -0,0 +1,43 @@ +unsignedTinyInteger('orchestrator_priority')->default(50)->nullable()->change(); + }); + } + + public function down(): void + { + // First back-fill any NULLs so the NOT NULL constraint can be restored. + \Illuminate\Support\Facades\DB::table('orders') + ->whereNull('orchestrator_priority') + ->update(['orchestrator_priority' => 50]); + + Schema::table('orders', function (Blueprint $table) { + $table->unsignedTinyInteger('orchestrator_priority')->default(50)->nullable(false)->change(); + }); + } +}; diff --git a/server/src/Http/Controllers/Api/v1/OrderController.php b/server/src/Http/Controllers/Api/v1/OrderController.php index 77d45153..8c5e46c7 100644 --- a/server/src/Http/Controllers/Api/v1/OrderController.php +++ b/server/src/Http/Controllers/Api/v1/OrderController.php @@ -299,6 +299,12 @@ public function create(CreateOrderRequest $request) $input['adhoc'] = Utils::isTrue($input['adhoc']) ? 1 : 0; } + // Ensure orchestrator_priority is never null — the column is NOT NULL + // and the DB default is bypassed when Eloquent receives an explicit null. + if (!isset($input['orchestrator_priority']) || !is_numeric($input['orchestrator_priority'])) { + $input['orchestrator_priority'] = 50; + } + if (!isset($input['payload_uuid'])) { return response()->apiError('Attempted to attach invalid payload to order.'); } @@ -527,6 +533,12 @@ public function update($id, UpdateOrderRequest $request) $order->dispatch(); } + // Ensure orchestrator_priority is never null on update either — + // only apply the default when the key was explicitly sent as null/empty. + if (array_key_exists('orchestrator_priority', $input) && !is_numeric($input['orchestrator_priority'])) { + $input['orchestrator_priority'] = 50; + } + // update the order $order->update($input); $order->flushAttributesCache(); diff --git a/server/src/Http/Controllers/Internal/v1/OrderController.php b/server/src/Http/Controllers/Internal/v1/OrderController.php index df8b20ca..d3efaf3f 100644 --- a/server/src/Http/Controllers/Internal/v1/OrderController.php +++ b/server/src/Http/Controllers/Internal/v1/OrderController.php @@ -126,6 +126,12 @@ function ($request, &$input) { $input['order_config_uuid'] = $defaultOrderConfig->uuid; } } + + // Ensure orchestrator_priority is never null — the column is NOT NULL + // and the DB default is bypassed when Eloquent receives an explicit null. + if (!isset($input['orchestrator_priority']) || !is_numeric($input['orchestrator_priority'])) { + $input['orchestrator_priority'] = 50; + } }, function (&$request, Order &$order, &$requestInput) { $input = $request->input('order'); diff --git a/server/src/Models/Order.php b/server/src/Models/Order.php index ecf38b34..1fc75503 100644 --- a/server/src/Models/Order.php +++ b/server/src/Models/Order.php @@ -219,6 +219,20 @@ class Order extends Model 'orchestrator_priority' => 'integer', ]; + /** + * The model's default attribute values. + * + * Ensures that `orchestrator_priority` is never persisted as NULL even when + * the caller omits the field entirely (e.g. the order/form component does + * not require the user to fill in orchestrator constraints). The value + * mirrors the database column default defined in the migration. + * + * @var array + */ + protected $attributes = [ + 'orchestrator_priority' => 50, + ]; + /** * The attributes excluded from the model's JSON form. * @@ -619,6 +633,18 @@ public function getUpdatedByNameAttribute() return data_get($this, 'updatedBy.name'); } + /** + * Set the orchestrator_priority attribute. + * + * Coerces null or non-numeric values to the default priority of 50 so that + * the NOT NULL database constraint is never violated when a user submits + * the order form without filling in the orchestrator constraints section. + */ + public function setOrchestratorPriorityAttribute($value): void + { + $this->attributes['orchestrator_priority'] = is_numeric($value) ? (int) $value : 50; + } + /** * Set the order type attribute, which defaults to `default`. */