Supplier catalogs are global supplier data, not a projection of a product
technology. Every parsed source row is retained in supplier_products (the
domain-facing alias is SupplierPriceItem) with its raw row, source section,
source page, supplier dimensions, unit prices, and match state. An item can
remain unmatched or be ignored indefinitely; neither state removes it from the
catalog.
SupplierPriceList is revised per supplier. New uploads are DRAFT; publishing
sets published_at, freezes the offer rows, archives the previous published
revision, and makes confirmed offers available to new calculations. Calculation
price snapshots continue to retain their source supplier item, revision, price,
and dimensions. Publishing a later revision does not update an existing
snapshot. CSV and XLSX imports retain their raw columns. PDF extraction is
deliberately marked uncertain and remains a human-review workflow; it preserves
page and section labels where present. It cannot guarantee table structure for
arbitrary supplier PDFs.
Canonical Material records hold normalized identity (including family,
subfamily, manufacturer, product code, and grade). MaterialMapping links a
supplier offer to a canonical material. A confirmed supplier alias stores a
stable supplier SKU/section identity so a later revision can reuse the mapping.
Only an exact remembered alias is automatically confirmed. Structured
category/thickness matches are suggestions and require review; high-confidence
suggestions can be explicitly bulk-confirmed. No import replaces a canonical
material.
Technologies continue to select canonical material IDs (and may filter by
canonical family/subfamily). They do not reference supplier items. Supplier
offers are looked up through confirmed mappings after a canonical material is
selected. This keeps products such as mirror acrylic, rods, tubes, and adhesives
available even when no current technology uses them.
API workflow #
POST /api/suppliers/{supplier_id}/price-listswithfilecreates a draft.GET /api/price-lists/{id}/productsreviews every retained supplier item.- Link through
POST /api/supplier-products/{id}/mapping, create a canonical
material from a reviewed item, or mark an item ignored. POST /api/price-lists/{id}/publishpublishes the immutable revision. The
older/activatepath remains as a compatibility alias.- Confirmed offers appear under
/api/materials/{id}/offersand can be selected
for future calculations. Existing snapshots remain frozen.
The parser runs in the existing restricted subprocess and retains existing file,
row, cell, decompression, and PDF page limits. The IGEPA-shaped regression PDF
is generated in backend/tests/test_supplier_price_lists.py; the original IGEPA
PDF was not present in this repository/runtime, so supplier-specific layout
accuracy against that exact document remains to be validated when the source is
available.