Refactor partnumber book catalog storage
This commit is contained in:
@@ -71,8 +71,8 @@ qt_lot_bundle_items: bundle_lot_name, lot_name, qty
|
||||
| `qt_lot_bundles` | Определения бандлов (bundle LOT → описание) |
|
||||
| `qt_lot_bundle_items` | Состав бандла: `(bundle_lot_name, lot_name, qty)` |
|
||||
| `qt_vendor_partnumber_seen` | Реестр seen-записей (уникально по `partnumber`) + флаг `is_ignored` |
|
||||
| `qt_partnumber_books` | Версионированные снимки маппинга для QuoteForge (пишет PriceForge) |
|
||||
| `qt_partnumber_book_items` | Строки снимка: `(book_id, partnumber, lot_name, is_primary_pn, description)`; бандлы развёрнуты; пустые lot_name и ignored PN исключены |
|
||||
| `qt_partnumber_books` | Версионированные книги PN; каждая книга хранит `partnumbers_json` — список PN, входящих в книгу |
|
||||
| `qt_partnumber_book_items` | Глобальный source-of-truth каталог `partnumber -> lots_json`; без дубликатов по `partnumber`; `lots_json` хранит список `{lot_name, qty}` |
|
||||
|
||||
Миграции:
|
||||
- `migrations/023_vendor_partnumber_global_mapping.sql`
|
||||
@@ -80,18 +80,47 @@ qt_lot_bundle_items: bundle_lot_name, lot_name, qty
|
||||
- `migrations/026_add_partnumber_books.sql`
|
||||
- `migrations/027_fix_partnumber_books_version_length.sql`
|
||||
- `migrations/028_add_description_to_partnumber_book_items.sql`
|
||||
- `migrations/029_change_partnumber_book_items_to_lots_json.sql`
|
||||
|
||||
---
|
||||
|
||||
## Partnumber Book — инварианты снимка
|
||||
|
||||
При формировании `qt_partnumber_book_items` обязательно:
|
||||
При формировании partnumber book обязательно:
|
||||
|
||||
1. **Пустой `lot_name` исключается** — `TRIM(lot_name) = ''` или NULL не попадают в снимок.
|
||||
2. **Ignored PN исключаются** — партномера с `qt_vendor_partnumber_seen.is_ignored = true` не попадают в снимок.
|
||||
3. **Бандлы разворачиваются** — каждая строка содержит конечный `lot_name` компонента, не имя бандла.
|
||||
4. **`description`** берётся из `lot_partnumbers.description`; для развёрнутых бандлов — из родительской записи partnumber.
|
||||
5. **Удаление items при retention** — явное (`DELETE WHERE book_id IN (...)`), не через FK CASCADE.
|
||||
1. **`qt_partnumber_book_items` содержит одну строку на `partnumber`** — дубликаты по PN не допускаются.
|
||||
2. **`lots_json`** содержит полный состав PN как JSON-массив объектов `{lot_name, qty}`.
|
||||
3. **Ignored PN исключаются** — партномера с `qt_vendor_partnumber_seen.is_ignored = true` не попадают ни в каталог, ни в `partnumbers_json` книги.
|
||||
4. **Бандлы не разворачиваются в отдельные строки** — bundle PN сохраняется одной записью, а состав компонентов уходит в `lots_json`.
|
||||
5. **`description`** берётся из `lot_partnumbers.description`.
|
||||
6. **`qt_partnumber_books.partnumbers_json`** хранит отсортированный список PN, входящих в конкретную книгу.
|
||||
7. **Конфликт разных составов для одного PN недопустим** — если при построении книги один и тот же PN даёт разные `lots_json`, создание snapshot должно завершиться ошибкой.
|
||||
|
||||
## QuoteForge Read Contract
|
||||
|
||||
QuoteForge must read the active book header first:
|
||||
|
||||
```sql
|
||||
SELECT id, version, created_at, created_by, partnumbers_json
|
||||
FROM qt_partnumber_books
|
||||
WHERE is_active = 1
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT 1;
|
||||
```
|
||||
|
||||
Then load item payloads from the catalog by PN list:
|
||||
|
||||
```sql
|
||||
SELECT partnumber, lots_json, is_primary_pn, description
|
||||
FROM qt_partnumber_book_items
|
||||
WHERE partnumber IN (...PNs from partnumbers_json...);
|
||||
```
|
||||
|
||||
Contract notes:
|
||||
- `partnumbers_json` is the membership snapshot of the selected book.
|
||||
- `qt_partnumber_book_items` is the current canonical catalog of PN compositions.
|
||||
- `lots_json` format is JSON array: `[{"lot_name":"CPU_X","qty":2},{"lot_name":"RAM_X","qty":4}]`
|
||||
- QuoteForge must not expect `lot_name` column in `qt_partnumber_book_items` anymore.
|
||||
|
||||
---
|
||||
|
||||
@@ -101,7 +130,7 @@ qt_lot_bundle_items: bundle_lot_name, lot_name, qty
|
||||
|------|------|
|
||||
| Миграция | `migrations/023_vendor_partnumber_global_mapping.sql` |
|
||||
| Миграция | `migrations/025_dedup_vendor_seen_by_partnumber.sql` |
|
||||
| Миграции снимков | `migrations/026–028` |
|
||||
| Миграции снимков | `migrations/026–029` |
|
||||
| Резолвер | `internal/lotmatch/matcher.go` |
|
||||
| Сервис маппингов | `internal/services/vendor_mapping.go` |
|
||||
| Сервис снимков | `internal/services/partnumber_book.go` |
|
||||
|
||||
Reference in New Issue
Block a user