# Change History > Architectural decisions and significant refactoring are recorded here. > Every architectural decision MUST be documented in this file. --- ## 2026-02-20: Seen Registry Deduplication by Partnumber ### Decision Changed `qt_vendor_partnumber_seen` semantics to one row per `partnumber` (vendor/source are no longer part of uniqueness). ### Rationale - Eliminates duplicate seen rows when the same partnumber appears both with vendor and without vendor. - Keeps ignore behavior consistent regardless of vendor presence. - Simplifies operational cleanup and prevents re-creation of vendor/no-vendor duplicates. ### Constraints - `partnumber` is now the unique key in seen registry. - Ignore checks are resolved by `partnumber` only. - Stock provenance must be preserved (`source_type='stock'`) when stock data exists for the partnumber. ### Files - Migration: `migrations/025_dedup_vendor_seen_by_partnumber.sql` - Service: `internal/services/stock_import.go` - Service: `internal/services/vendor_mapping.go` - Model: `internal/models/lot.go` --- ## 2026-02-18: Global Vendor Partnumber Mapping ### Decision Implemented global vendor partnumber → LOT mapping with bundle support and seen-based ignore logic. ### Key rules - `lot_partnumbers` is the canonical mapping contract (1:1 per key). - Composite mappings use internal bundle tables (`qt_lot_bundles`, `qt_lot_bundle_items`). - Ignore logic moved from `stock_ignore_rules` to `qt_vendor_partnumber_seen.is_ignored`. - Resolver order: exact `(vendor, partnumber)` → fallback `(vendor='', partnumber)`. ### Rationale - Preserves external/client contracts (`lot_partnumbers`, LOT-based pricelists). - Avoids multi-row ambiguity in `lot_partnumbers`. - Supports complex assembled vendor SKUs without client-side changes. - Centralizes ignore behavior across all sources via seen-registry. ### Constraints - Bundle LOT is internal and must stay hidden in regular LOT list by default. - Resolver order is mandatory and fixed. - Bundle allocation for missing estimate: fallback from previous active warehouse pricelist; if absent → `0`. ### Files - Migration: `migrations/023_vendor_partnumber_global_mapping.sql` - Backfill: `migrations/024_backfill_vendor_seen_from_stock_and_ignore.sql` - Resolver: `internal/lotmatch/matcher.go` - Service: `internal/services/vendor_mapping.go` - Warehouse calc: `internal/warehouse/snapshot.go` - Stock import: `internal/services/stock_import.go` - API: `internal/handlers/pricing.go`, `cmd/pfs/main.go` --- ## 2026-02-10: LOT Page Refactoring ### Decision Moved LOT management to a dedicated `/lot` page. Removed LOT tab from Pricing Admin. ### What changed - Created `web/templates/lot.html` — two tabs: LOT (component management) and Mappings (partnumber ↔ LOT). - Removed LOT tab from `admin_pricing.html`; default tab changed to `estimate`. - Removed "Сопоставление partnumber → LOT" section from Warehouse tab. - Updated navigation: LOT → `/lot`, Pricing Admin → `/admin/pricing`. - Added `Lot()` handler in `internal/handlers/web.go`. - Added `/lot` route in `cmd/pfs/main.go`. ### Rationale Separation of concerns: LOT/component management is distinct from pricing administration. --- ## Warehouse Pricing: Weighted Average ### Decision Warehouse pricelist uses `weighted_avg` (quantity-weighted average) as the sole price calculation method. Commit `edff712` switched from `weighted_median` to `weighted_avg`. - `price_method` field always contains `"weighted_avg"`. - No `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices` in warehouse pricelists. --- ## Architecture Conventions > All future architectural decisions must be documented here with: > - Date > - Decision summary > - Rationale > - Constraints / invariants > - Affected files