From 568835c586ffbbb2bd41911b2dac9d8d5f7c0792 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sat, 25 Apr 2026 12:49:00 +0200 Subject: [PATCH] fix(#918): RFID auto-match handles Quick-Add and rejects non-Bambu brands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `find_matching_untagged_spool` is supposed to attach an incoming Bambu RFID UUID to a pre-existing manually-logged spool of the same material/color so users who log inventory before scanning don't end up with duplicate rows. Two bugs meant it almost never worked for the actual reporting workflow: 1. Subtype filter was strict. AMS reports `tray_sub_brands="PLA Basic"` → matcher required `Spool.subtype = 'Basic'` exactly. The form's Quick-Add mode only requires `material`, so bulk-logged rows have `subtype=NULL` and were always excluded → duplicate on first AMS read. 2. Brand wasn't filtered. The docstring claimed brand was matched but the WHERE clause didn't include it, so a same-color Polymaker (or any non-Bambu) untagged row could acquire a Bambu UUID — silent data corruption. Fix in the same query: subtype prefers exact match but accepts NULL as fallback (CASE in ORDER BY ensures exact wins when both exist); brand restricted to NULL or LOWER(brand) LIKE '%bambu%' (covers 'Bambu', 'Bambu Lab', 'BambuLab', 'bambu lab' — the spellings users actually type). 6 regression tests added in test_spool_tag_matcher.py. --- CHANGELOG.md | 1 + backend/app/services/spool_tag_matcher.py | 42 ++++- .../unit/services/test_spool_tag_matcher.py | 168 ++++++++++++++++++ 3 files changed, 202 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1b9150a..ac0ab3dfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to Bambuddy will be documented in this file. - **Settings page: permission-gated instead of admin-only** — the Settings sidebar entry has always been visible to any user holding `settings:read`, but the route guard required admin role, so a non-admin with `settings:read` would see the entry, click it, and get silently redirected back to the dashboard. The route guard now matches the sidebar: any user with `settings:read` can open the page, and the individual tabs / cards continue to enforce their own per-feature permissions (`users:read`, `groups:update`, `oidc:*`, etc. — many of them admin-only, some not). Group editor routes moved to permission-based guards too (`groups:create` for `/groups/new`, `groups:update` for `/groups/:id/edit`), so permission delegation works end-to-end. Admins retain full access since admins implicitly hold every permission. ### Fixed +- **Bambu RFID auto-match created duplicate inventory rows for Quick-Add and non-Bambu-branded spools** ([#918](https://github.com/maziggy/bambuddy/issues/918)) — `find_matching_untagged_spool` is supposed to attach a Bambu RFID UID to a pre-existing manually-logged spool of the same material/color so users who log inventory before scanning don't end up with a duplicate row on first AMS read. Two bugs in the matcher meant it almost never worked for the actual reporting workflow: **(1)** the subtype filter was strict — when the AMS tray reports `tray_sub_brands="PLA Basic"` the matcher required `Spool.subtype = 'Basic'` exactly, so any Quick-Add row (Quick-Add only requires `material`, leaving `subtype=NULL`) was excluded and duplicated on first AMS read. **(2)** the docstring claimed it filtered on brand but the WHERE clause didn't, so a same-color *Polymaker* untagged spool would silently acquire a Bambu Lab tray UUID, leaving the user with `brand="Polymaker"` but a Bambu UUID — silent data corruption. Both bugs are addressed in the same query: subtype now prefers an exact match but accepts a NULL-subtype row as fallback (with a `CASE` in `ORDER BY` so an exact match still wins when both exist), and brand is now restricted to "contains 'bambu' (case-insensitive)" or NULL — matching `'Bambu'` (the form's `DEFAULT_BRANDS` value), `'Bambu Lab'` (the catalog value), `'BambuLab'`, `'bambu lab'`, etc., while rejecting any explicitly-named third-party brand. 6 new regression tests in `test_spool_tag_matcher.py` cover the NULL-subtype fallback, exact-subtype-wins-over-NULL ordering, non-Bambu brand rejection, NULL brand acceptance, all four Bambu brand spelling variants, and the full Quick-Add scenario (`brand=NULL` + `subtype=NULL`). The broader UI proposals in #918 (manual override / merge / disambiguation prompt) are intentionally out of scope — once the matcher works, the duplicate-on-RFID complaint that motivated those proposals goes away. Thanks to @ViridityCorn for the report and pointing at the right function, and to @Arn0uDz for confirming with a 20-spool repro. - **Swagger UI link in Settings → API Keys rendered a blank page** — the global CSP applied by `security_headers_middleware` set `script-src 'self'` and `style-src 'self' 'unsafe-inline' https://fonts.googleapis.com`, which blocked both the inline `