Add filament override for model-based queue assignment (#486)

When scheduling a print to "any printer" (model-based assignment),
users can now override the 3MF's original filament choices per slot.
The override dropdown shows compatible filaments loaded across all
printers of the selected model, filtered by type and nozzle (H2D).
The scheduler matches against overridden type/color instead of the
original 3MF values, preferring printers with exact color matches.

Backend:
- New GET /printers/available-filaments endpoint (aggregates loaded
  filaments across printers of a model, includes extruder_id for
  dual-nozzle nozzle-aware filtering)
- New filament_overrides JSON column on print_queue table
- Scheduler applies overrides before AMS mapping, clears tray_info_idx
  on overridden slots so color matching takes priority
- Printer selection prefers printers with most override color matches

Frontend:
- New FilamentOverride component with per-slot override dropdowns
- Type-filtered options (PLA slots only show PLA)
- Nozzle-aware filtering (H2D: only shows filaments on correct extruder)
- Integrated into PrintModal for model-based queue mode

Tests:
- 11 backend unit tests (color match counting, override application)
- 12 frontend tests (rendering, type/nozzle filtering, interactions)

i18n: All 6 locales (en, de, fr, it, ja, pt-BR)
This commit is contained in:
maziggy
2026-02-22 16:43:16 +01:00
parent 7a1b22cff0
commit 38af5df050
23 changed files with 1077 additions and 114 deletions
+8
View File
@@ -1222,6 +1222,7 @@ export interface PrintQueueItem {
auto_off_after: boolean;
manual_start: boolean; // Requires manual trigger to start (staged)
ams_mapping: number[] | null; // AMS slot mapping for multi-color prints
filament_overrides: Array<{ slot_id: number; type: string; color: string }> | null; // Filament overrides for model-based assignment
plate_id: number | null; // Plate ID for multi-plate 3MF files
// Print options
bed_levelling: boolean;
@@ -1251,6 +1252,7 @@ export interface PrintQueueItemCreate {
printer_id?: number | null; // null = unassigned
target_model?: string | null; // Target printer model (mutually exclusive with printer_id)
target_location?: string | null; // Target location filter (only used with target_model)
filament_overrides?: Array<{ slot_id: number; type: string; color: string }> | null;
// Either archive_id OR library_file_id must be provided
archive_id?: number | null;
library_file_id?: number | null;
@@ -1273,6 +1275,7 @@ export interface PrintQueueItemUpdate {
printer_id?: number | null; // null = unassign
target_model?: string | null; // Target printer model (mutually exclusive with printer_id)
target_location?: string | null; // Target location filter (only used with target_model)
filament_overrides?: Array<{ slot_id: number; type: string; color: string }> | null;
position?: number;
scheduled_time?: string | null;
require_previous_success?: boolean;
@@ -2296,6 +2299,11 @@ export const api = {
),
getDeveloperModeWarnings: () =>
request<{ printer_id: number; name: string }[]>('/printers/developer-mode-warnings'),
getAvailableFilaments: (model: string, location?: string) => {
const params = new URLSearchParams({ model });
if (location) params.set('location', location);
return request<Array<{ type: string; color: string; tray_info_idx: string; extruder_id: number | null }>>(`/printers/available-filaments?${params}`);
},
getPrinterStatus: (id: number) =>
request<PrinterStatus>(`/printers/${id}/status`),
refreshPrinterStatus: (id: number) =>