Unified Quote Journal (parts_log) v3
- New unified append-only quote log table parts_log replaces three separate log tables (stock_log, partnumber_log_competitors, lot_log) - Migrations 042-049: extend supplier, create parts_log/import_formats/ ignore_rules, rework qt_lot_metadata composite PK, add lead_time_weeks to pricelist_items, backfill data, migrate ignore rules - New services: PartsLogBackfillService, ImportFormatService, UnifiedImportService; new world pricelist type (all supplier types) - qt_lot_metadata PK changed to (lot_name, pricelist_type); all queries now filter WHERE pricelist_type='estimate' - Fix pre-existing bug: qt_component_usage_stats column names quotes_last30d/quotes_last7d (no underscore) — added explicit gorm tags - Bible: full table inventory, baseline schema snapshot, updated pricelist/ data-rules/api/history/architecture docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,9 +11,11 @@ Categories are **always** taken from `lot.lot_category` (table `lot`).
|
||||
|
||||
### When creating pricelists
|
||||
|
||||
1. **Estimate**: include only components with `current_price > 0` and `is_hidden = 0`, then load `lot.lot_category`.
|
||||
2. **Warehouse**: load `lot.lot_category` for all positions.
|
||||
3. Persist into `lot_category` column of `qt_pricelist_items`.
|
||||
1. **Estimate**: include only components with `current_price > 0` and `is_hidden = 0` (`pricelist_type='estimate'`), then load `lot.lot_category`.
|
||||
2. **Warehouse**: load `lot.lot_category` for all positions (`pricelist_type='warehouse'`).
|
||||
3. **Competitor**: load `lot.lot_category` for all positions (`pricelist_type='competitor'`).
|
||||
4. **World**: load `lot.lot_category` for all positions (`pricelist_type='world'`).
|
||||
5. Persist into `lot_category` column of `qt_pricelist_items`.
|
||||
|
||||
### Model
|
||||
|
||||
@@ -34,14 +36,57 @@ type PricelistItem struct {
|
||||
|
||||
## Price Method
|
||||
|
||||
- Warehouse pricelist: `price_method` = `"weighted_avg"` (quantity-weighted average).
|
||||
- Estimate pricelist: method determined by per-component settings.
|
||||
- **Estimate**: method determined by per-component settings in `qt_lot_metadata` (`pricelist_type='estimate'`). Default: `median`.
|
||||
- **Warehouse**: `weighted_avg` (quantity-weighted average). Default `period_days=7`.
|
||||
- **Competitor**: `weighted_median`. `qty NULL` → weight = 1.
|
||||
- **World**: `weighted_median` across all supplier types. `qty NULL` → weight = 1.
|
||||
|
||||
---
|
||||
|
||||
## Warehouse: Filtering by qt_partnumber_book_items
|
||||
## supplier_type
|
||||
|
||||
**Rule (CRITICAL)**: only positions with a matching record in `qt_partnumber_book_items` are included in the warehouse pricelist. Unmapped partnumbers are completely excluded.
|
||||
The `supplier` table `supplier_type` column determines which pricelist(s) a supplier's quotes feed:
|
||||
|
||||
| supplier_type | Feeds into |
|
||||
|---------------|-----------|
|
||||
| `trader` | estimate, world |
|
||||
| `self` | warehouse, world |
|
||||
| `competitor` | competitor, world |
|
||||
|
||||
**price_uplift**: divisor applied at import. `effective_price = raw_price / price_uplift`. All prices in `parts_log` are always in USD.
|
||||
|
||||
**Defaults by type**:
|
||||
| supplier_type | price_uplift | default_lead_time_weeks |
|
||||
|---------------|-------------|------------------------|
|
||||
| trader | 1.0 | NULL |
|
||||
| self | 1.0 | 0 |
|
||||
| competitor | 1.3 | 2 |
|
||||
|
||||
---
|
||||
|
||||
## offer_type
|
||||
|
||||
`parts_log.offer_type` ENUM: `'private'` or `'public'` (default `'public'`).
|
||||
|
||||
Selected at import time. Both types are currently included in all pricelist calculations.
|
||||
|
||||
---
|
||||
|
||||
## qt_ignore_rules
|
||||
|
||||
Replaces the `is_ignored` mechanism in `qt_vendor_partnumber_seen`. Checked at import before mapping.
|
||||
|
||||
| field | match_type | behavior |
|
||||
|-------|-----------|---------|
|
||||
| partnumber / vendor / description | exact / glob / contains | DROP row if matched |
|
||||
|
||||
Order of checks at import: **ignore rules → partnumber book → write to parts_log**.
|
||||
|
||||
---
|
||||
|
||||
## Filtering by qt_partnumber_book_items
|
||||
|
||||
**Rule (CRITICAL)**: only `parts_log` rows with a non-NULL `lot_name` are included in any pricelist. `lot_name` is populated by the daily backfill job (`PartsLogBackfillService`) which resolves partnumbers via `qt_partnumber_book_items`. Rows that cannot be resolved remain with `lot_name=NULL` and are excluded from all pricelists.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user