Files
PriceForge/bible/vendor-mapping.md
Michael Chus a4457a0a28 Add partnumber book snapshots for QuoteForge integration
- Migrations 026-028: qt_partnumber_books + qt_partnumber_book_items
  tables; is_primary_pn on lot_partnumbers; version VARCHAR(30);
  description VARCHAR(10000) on items (required by QuoteForge sync)
- Service: CreateSnapshot expands bundles, filters empty lot_name and
  ignored PNs, copies description, activates new book atomically,
  applies GFS retention (7d/5w/12m/10y) with explicit item deletion
- Task type TaskTypePartnumberBookCreate; handlers ListPartnumberBooks
  and CreatePartnumberBook; routes GET/POST /api/admin/pricing/partnumber-books
- UI: snapshot list + "Создать снапшот сопоставлений" button with
  progress polling on /vendor-mappings page
- Bible: history, api, background-tasks, vendor-mapping updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 22:16:16 +03:00

5.3 KiB
Raw Blame History

Vendor Mapping (Сопоставление partnumber → LOT)

Решение зафиксировано: 2026-02-18

Концепция

lot_partnumbers — канонический контракт сопоставления для внешнего конфигуратора.

Маппинг строго 1:1 по ключу (vendor, partnumber)lot_name.


Правила

1. Единственная запись на ключ

Запрещено создавать несколько строк для одного ключа (vendor, partnumber).

2. Порядок резолвинга (фиксированный)

1. Точное совпадение:  vendor + partnumber  → lot_name
2. Fallback:           vendor='' + partnumber → lot_name

Резолвер: internal/lotmatch/matcher.go

3. Составные маппинги — через бандлы

Если один внешний partnumber соответствует нескольким LOT — использовать внутренние бандл-таблицы:

lot_partnumbers:  (vendor, partnumber) → bundle_lot_name
qt_lot_bundles:   bundle_lot_name → [item1, item2, ...]
qt_lot_bundle_items: bundle_lot_name, lot_name, qty
  • Bundle LOT — внутренний, скрыт в обычном UI LOT по умолчанию.
  • Bundle expansion происходит в PriceForge: при расчёте warehouse-прайслиста и при создании partnumber book snapshot.

4. Ignore-логика

  • Не использовать stock_ignore_rules для новой логики.
  • Использовать qt_vendor_partnumber_seen.is_ignored.
  • qt_vendor_partnumber_seen хранится в формате 1 строка на partnumber (vendor/source не участвуют в уникальности).
  • Ignore применяется по partnumber (одинаково для записей с vendor и без vendor).

5. Клиентская совместимость

  • Клиент потребляет LOT-based прайслисты (как обычно).
  • Bundle expansion/allocation происходит только внутри PriceForge.

Allocation при отсутствии estimate

Если у bundle-компонента нет estimate-цены:

  1. Fallback: взять из предыдущего активного warehouse-прайслиста.
  2. Если нет предыдущего — цена 0.

Таблицы БД

Таблица Назначение
lot_partnumbers Канонические маппинги (vendor, partnumber)lot_name; колонка is_primary_pn — ведущий PN для qty в BOM
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 исключены

Миграции:

  • migrations/023_vendor_partnumber_global_mapping.sql
  • migrations/025_dedup_vendor_seen_by_partnumber.sql
  • migrations/026_add_partnumber_books.sql
  • migrations/027_fix_partnumber_books_version_length.sql
  • migrations/028_add_description_to_partnumber_book_items.sql

Partnumber Book — инварианты снимка

При формировании qt_partnumber_book_items обязательно:

  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.

Связанные модули

Роль Файл
Миграция migrations/023_vendor_partnumber_global_mapping.sql
Миграция migrations/025_dedup_vendor_seen_by_partnumber.sql
Миграции снимков migrations/026028
Резолвер internal/lotmatch/matcher.go
Сервис маппингов internal/services/vendor_mapping.go
Сервис снимков internal/services/partnumber_book.go
API internal/handlers/pricing.go
Warehouse calc internal/warehouse/snapshot.go
Stock import seen/ignore internal/services/stock_import.go
Модели internal/models/lot.go, internal/models/configuration.go
Роутинг cmd/pfs/main.go