An archive's filament_color was parsed verbatim from the print job's
3MF (filament_colour in project_settings.config) — the slicer's
filament-slot colour, which a user picks independently of the exact
hex they curate on the Bambuddy inventory spool. So a print from a
#000000 inventory spool showed #161616 (the slicer's near-black) in
the archive card and the Color Distribution graph, even though usage
tracking correctly decremented the right spool.
Once usage tracking has resolved the print's filament slots to
inventory spools, the spool colours are authoritative. _track_from_3mf
(built-in inventory) and report_usage (Spoolman mode) now overwrite
the archive's filament_color with the slot-ordered, de-duplicated
colours of the matched spools.
The rewrite is all-or-nothing: it only applies when every used slot
resolved to a spool carrying a colour, so a partially-mapped
multi-colour print keeps the 3MF colour rather than silently dropping
the unmatched slots.
Shipped for both inventory modes: built-in spools read Spool.rgba,
Spoolman spools read the spool's filament.color_hex (fetched via
get_spool for tag-less slot-assignment matches). New helpers
_spool_color_to_hex / _archive_colors_from_spools in usage_tracker.py,
reused by spoolman_tracking.py via _apply_spool_colors_to_archive.
Tests: 12 new in test_usage_tracker.py (hex normalisation, the
all-or-nothing rule across single/multi/partial/no-colour/AMS-fallback
cases, end-to-end rewrite), 4 in test_spoolman_tracking.py (Spoolman
rewrite + empty/partial/missing-archive no-ops). 70 tracking tests
green; backend ruff clean.
archives stop reporting near-zero cost (#1344)
Reporter @nicktags hit $0.01 on a 110.3g multi-color print with the
global default filament cost set to $10/kg. archive.py initial cost
calc was correct (~$1.10), then usage_tracker.on_print_complete
overwrote archive.cost with sum(r.cost for r in results) -- where
results only includes AMS trays mapped to a spool in Bambuddy's
inventory. On a multi-color print where 3 of 4 used trays had no
inventory spool, only the one tracked slot's tiny share (~1g) survived
and the archive recorded $0.01.
The overwrite logic dates to #505 (Feb 2026) and is correct for
fully-tracked single-color prints, but the multi-color slicer feature
in 0.2.4 (988c0055) made the partial-inventory state common -- users
slice + print multi-color from Bambuddy without first setting up an
inventory entry for every tray.
Cover the gap: any filament weight not represented in results gets
charged at the global default rate. For a fully-tracked print,
untracked grams = 0 and the top-up adds nothing, so the single-color
behavior is preserved. For a partial print, the missing slots are
priced at the user's documented default rate so the archive cost
reflects the whole print.
Three call sites updated to share the same logic:
- usage_tracker.py: live cost-update on print complete
- archives.py rescan_archive: per-archive manual recalc
- archives.py recalculate_all_costs: bulk recalc button
The AMS remain% delta path charged every tray with a delta, not just
trays involved in the print. Swapping a spool in an UNUSED slot mid-
print made the slot report remain=0 (fresh spool, no tag), versus a
print-start snapshot of 100%, so the originally-assigned spool got
charged the full 1000g.
Build print_used_keys from ams_mapping, tray_change_log, and
tray_now_at_start, and skip fallback for trays not in that set.
Legacy "scan every tray" behavior preserved when none of the three
signals are present.
When auto-archive was off, archive_id was None at print completion so
the entire 3MF tracking path was skipped. AMS remain% fallback also
failed on printers reporting remain=-1. Now searches library files and
previous archives by filename to locate the 3MF without an archive,
and captures the AMS slot-to-tray mapping at print start so it's
available at completion regardless of archive state.
When a spool ran empty during a print and the AMS auto-switched to a
backup, on_ams_change eagerly deleted the SpoolAssignment (fingerprint
mismatch). on_print_complete then queried the now-deleted assignment
and silently dropped usage.
Snapshot all spool assignments into PrintSession at print start. At
completion, prefer the snapshot over live DB queries in both the 3MF
primary path and the AMS remain% delta fallback. Fall back to live
query for backward compatibility with in-flight sessions started
before upgrade.
3MF slicer estimates are now the primary tracking source for ALL spools
(BL and non-BL), with AMS remain% delta as fallback. Per-layer G-code
analysis provides accurate partial usage for failed/cancelled prints.
Fix wrong-spool tracking bug: 3MF slot_id was mapped directly to AMS
tray position, but the printer remaps slicer slots at print time. Now
uses queue ams_mapping for queue prints and tray_now from printer state
for single-filament non-queue prints.
Add filament_grams, filament_details, and progress template variables
to print_complete, print_failed, and print_stopped notification events.
Webhook payloads include per-slot filament breakdown. Per-slot data
stored in archive extra_data during 3MF parsing.