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>
This commit is contained in:
@@ -5,6 +5,56 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-02-21: Partnumber Book Snapshots for QuoteForge
|
||||
|
||||
### Decision
|
||||
|
||||
Implemented versioned snapshots of the `lot_partnumbers` → LOT mapping in `qt_partnumber_books` / `qt_partnumber_book_items`. PriceForge writes; QuoteForge reads (SELECT only).
|
||||
|
||||
### What changed
|
||||
|
||||
- Migration `026`: added `is_primary_pn TINYINT(1) DEFAULT 1` to `lot_partnumbers`; created `qt_partnumber_books` and `qt_partnumber_book_items` tables (version `VARCHAR(20)`, later corrected).
|
||||
- Migration `027`: corrected `version VARCHAR(20) → VARCHAR(30)` — `PNBOOK-YYYY-MM-DD-NNN` is 21 chars and overflowed the original column.
|
||||
- Migration `028`: added `description VARCHAR(10000) NULL` to `qt_partnumber_book_items` — required by QuoteForge sync (`SELECT partnumber, lot_name, is_primary_pn, description`).
|
||||
- Models `PartnumberBook`, `PartnumberBookItem` (with `Description *string`) added to `internal/models/lot.go`; `IsPrimaryPN bool` added to `LotPartnumber`.
|
||||
- Service `internal/services/partnumber_book.go`:
|
||||
- `CreateSnapshot`: expands bundles (QuoteForge is bundle-unaware), copies `description` from `lot_partnumbers` to every expanded row, generates version `PNBOOK-YYYY-MM-DD-NNN`, deactivates previous books and activates new one atomically, then runs GFS retention cleanup.
|
||||
- `expandMappings`: filters out rows where `lot_name` is empty/whitespace; filters out partnumbers marked `is_ignored = true` in `qt_vendor_partnumber_seen`. Only valid PN→LOT pairs enter the snapshot.
|
||||
- `applyRetention`: deletes items explicitly (`DELETE … WHERE book_id IN (…)`) before deleting books — does not rely on FK CASCADE which GORM does not trigger on batch deletes.
|
||||
- `ListBooks`: returns all snapshots ordered newest-first with item counts.
|
||||
- GFS retention policy: 7 daily · 5 weekly · 12 monthly · 10 yearly; applied automatically after each snapshot; active book is never deleted.
|
||||
- Task type `TaskTypePartnumberBookCreate` added to `internal/tasks/task.go`.
|
||||
- Handlers `ListPartnumberBooks` and `CreatePartnumberBook` added to `internal/handlers/pricing.go`; `PartnumberBookService` injected via constructor.
|
||||
- Routes `GET /api/admin/pricing/partnumber-books` and `POST /api/admin/pricing/partnumber-books` registered in `cmd/pfs/main.go`.
|
||||
- UI: "Снимки сопоставлений (Partnumber Books)" section with snapshot table, progress bar, and "Создать снапшот сопоставлений" button added to `web/templates/vendor_mappings.html`.
|
||||
|
||||
### Rationale
|
||||
|
||||
QuoteForge needs a stable, versioned copy of the PN→LOT mapping to resolve BOM line items without live dependency on PriceForge. Snapshots decouple the two systems.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Bundles MUST be expanded: QuoteForge does not know about `qt_lot_bundles`.
|
||||
- Snapshot rows with empty `lot_name` or `is_ignored = true` partnumbers MUST be excluded.
|
||||
- `description` in book items comes from `lot_partnumbers.description`; for expanded bundle rows the description of the parent partnumber mapping is used.
|
||||
- `is_primary_pn` is copied from `lot_partnumbers` into each book item; drives qty logic in QuoteForge.
|
||||
- New snapshot is immediately `is_active=1`; all previous books are deactivated in the same transaction.
|
||||
- Version format: `PNBOOK-YYYY-MM-DD-NNN` (`VARCHAR(30)`), sequential within the same day.
|
||||
- Item deletion during retention MUST be explicit (items first, then books) — FK CASCADE is unreliable with GORM batch deletes.
|
||||
- QuoteForge has `SELECT` permission only on `qt_partnumber_books` and `qt_partnumber_book_items`.
|
||||
|
||||
### Files
|
||||
|
||||
- Migrations: `026_add_partnumber_books.sql`, `027_fix_partnumber_books_version_length.sql`, `028_add_description_to_partnumber_book_items.sql`
|
||||
- Models: `internal/models/lot.go`
|
||||
- Service: `internal/services/partnumber_book.go`
|
||||
- Handler: `internal/handlers/pricing.go`
|
||||
- Tasks: `internal/tasks/task.go`
|
||||
- Routes: `cmd/pfs/main.go`
|
||||
- UI: `web/templates/vendor_mappings.html`
|
||||
|
||||
---
|
||||
|
||||
## 2026-02-20: Pricelist Formation Hardening (Estimate/Warehouse/Meta)
|
||||
|
||||
### Decision
|
||||
|
||||
Reference in New Issue
Block a user