[Detail Bug] Catalog: Multi-SKU KNX hardware shows wrong order number/width in Project Configure panel
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 4
- Forks
- 0
- Avg merge
- 15h 38m
- Merged PRs (30d)
- 37
Description
Detail Bug Report
Introduced in #28 by @kewde on Sep 7, 2026
Summary
- Context:
upload_knxprodingests a.knxprodinto the catalog DB. In_ingest_hardware, each hardware row's product-level fields (name,order_number,is_rail_mounted,width_mm,description,default_language) are taken from oneProduct— the first element ofreg.products_for_hardware(hardware_id). - Bug: A KNX
<Hardware>is permitted by schema and parser to have multiple<Product>children (the same device under several SKUs / order numbers)._ingest_hardwarecollapses that list toproducts[0]and silently discards every other SKU's payload. - Actual vs. expected: expected — every product/SKU attached to a hardware is represented (or, at minimum, the collision is surfaced, the way this codebase handles "data that can't be fully represented" elsewhere). Actual — non-first SKUs' display attributes vanish without a warning, and the single
Hardwarerow carries a SKU that may not be the one a catalog entry sells. - Impact: For any multi-SKU hardware whose
<Hardware>carries N>1<Product>and a<CatalogItem>references a non-first product,list_productswould return aProductSummaryrow whoseproduct_ref_idandorder_numbercome from different products. More concretely, the project configure panel rendersorder_number,is_rail_mountedandwidth_mmfor an added device unconditionally from the catalogHardwarerow, so such a catalog item's entry would surface the wrongorder_numberand potentially wrongwidth_mmfor the device the operator added.
Code with Bug
packages/catalog/src/xknxmono/catalog/core/upload.py:
products = list(reg.products_for_hardware(hardware_id).values())
product = products[0] if products else None # <-- BUG 🔴 silently keeps only the first SKU; products[1:] are dropped
session.merge(
Hardware(
id=hw.id,
manufacturer_id=mfr_id,
name=product.name if product else hw.name,
order_number=product.order_number if product else None,
is_rail_mounted=product.rail_mounted if product else None,
width_mm=product.width_mm if product else None,
description=product.raw.visible_description if product else None,
default_language=product.raw.default_language if product else None,
...
)
)
packages/catalog/src/xknxmono/catalog/core/upload.py (catalog item can reference a different SKU than the one ingested into Hardware):
session.merge(
CatalogSectionProduct(
id=item.id,
...
product_ref_id=item.product_ref_id, # <-- BUG 🔴 can reference a non-first SKU whose display payload was discarded
...
)
)
Explanation
- The KNX schema and parser allow multiple
<Product>elements per<Hardware>(registry storeshardware_to_productas a list, and the parser collects all products with nolen == 1guard). The catalog DB model (Hardware) has only a single set of product-level columns, so_ingest_hardwareresolves the mismatch by selectingproducts[0]and discarding the rest. - If a
CatalogItemreferences a non-first SKU viaCatalogSectionProduct.product_ref_id, the DB now contains inconsistent information: the SKU reference points to (e.g.) “Beta”, butHardware.order_number/width_mmreflect “Alpha”. - This inconsistency is surfaced in two places:
list_productsreturns aProductSummarythat pairsproduct_ref_idfromCatalogSectionProductwith display fields (likeorder_number) sourced via theHardwarerow, so a single row can describe two different SKUs.- The Project Configure panel resolves hardware by program ref and renders
hardware.order_number/hardware.width_mm/hardware.is_rail_mountedunconditionally, so a device added from a non-first SKU can display the wrong order number (and possibly width) with no ingest-time warning.
Codebase Inconsistency
- The ingestion path persists
CatalogSectionProduct.product_ref_id(SKU-specific) but populatesHardware.order_number(and other display fields) fromproducts[0]without reconciling it againstproduct_ref_id. This allowsproduct_ref_idandorder_numberto refer to different SKUs in the same returned/rendered entity.
Recommended Fix
- Detect and surface the unrepresentable case at ingest time: in
_ingest_hardware, warn or raise whenlen(products) > 1instead of silently dropping SKUs. - To fully fix the operator-facing mismatch, ensure per-SKU display fields (
order_number,width_mm, etc.) are sourced from the SKU referenced byproduct_ref_id(e.g., persist a per-SKU table keyed byProduct.idand join onCatalogSectionProduct.product_ref_id, or otherwise resolve product metadata byproduct_ref_idwhen buildingProductSummary/hardware info).
History
This bug was introduced in commit ab3210b. The original products[0] silent-drop in _ingest_hardware (first written in dcc7b8c, May 2026) was for its whole first quarter only a latent data-loss with no operator-visible surface, but commit ab3210b "feat(knx-gui): rename Manufacturer section to Metadata, add full device metadata (#28)" added both xknxmono.catalog.get_hardware_by_program (the catalog read that fetches the stale Hardware row) and the project-side CONFIGURE_ORDER_NUMBER/CONFIGURE_RAIL_MOUNTED/CONFIGURE_WIDTH rendering that consumes hardware.* unconditionally — turning the silent ingest-time drop into an operator-facing wrong-render exactly on the surface this report identifies (metadata_section.py:79,95,101). The escalating commit is therefore ab3210b; the deeper latent mechanism traces back to dcc7b8c's original _ingest_hardware, and c75ed30's ProductSummary (June 2026) was an intermediate escalation that paired product_ref_id with Hardware.order_number in a single row.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/catalog/src/xknxmono/catalog/core/upload.py at _ingest_hardware and trace products_for_hardware through CatalogSectionProduct.product_ref_id. Then inspect metadata_section.py at the configure metadata rendering mentioned in the report. Done means multi-SKU hardware no longer produces mismatched product references and display fields, with the chosen ingest or per-SKU handling documented and covered by relevant checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100