Files
PriceForge/internal/models/lot.go
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

168 lines
7.5 KiB
Go

package models
import "time"
// Lot represents existing lot table
type Lot struct {
LotName string `gorm:"column:lot_name;primaryKey;size:255" json:"lot_name"`
LotDescription string `gorm:"column:lot_description;size:10000" json:"lot_description"`
LotCategory *string `gorm:"column:lot_category;size:50" json:"lot_category"`
}
func (Lot) TableName() string {
return "lot"
}
// LotLog represents existing lot_log table (READ-ONLY)
type LotLog struct {
LotLogID uint `gorm:"column:lot_log_id;primaryKey;autoIncrement"`
Lot string `gorm:"column:lot;size:255;not null"`
Supplier string `gorm:"column:supplier;size:255;not null"`
Date time.Time `gorm:"column:date;type:date;not null"`
Price float64 `gorm:"column:price;not null"`
Quality string `gorm:"column:quality;size:255"`
Comments string `gorm:"column:comments;size:15000"`
}
func (LotLog) TableName() string {
return "lot_log"
}
// Supplier represents existing supplier table (READ-ONLY)
type Supplier struct {
SupplierName string `gorm:"column:supplier_name;primaryKey;size:255"`
SupplierComment string `gorm:"column:supplier_comment;size:10000"`
}
func (Supplier) TableName() string {
return "supplier"
}
// StockLog stores warehouse stock snapshots imported from external files.
type StockLog struct {
StockLogID uint `gorm:"column:stock_log_id;primaryKey;autoIncrement"`
Partnumber string `gorm:"column:partnumber;size:255;not null"`
Supplier *string `gorm:"column:supplier;size:255"`
Date time.Time `gorm:"column:date;type:date;not null"`
Price float64 `gorm:"column:price;not null"`
Quality *string `gorm:"column:quality;size:255"`
Comments *string `gorm:"column:comments;size:15000"`
Vendor *string `gorm:"column:vendor;size:255"`
Qty *float64 `gorm:"column:qty"`
}
func (StockLog) TableName() string {
return "stock_log"
}
// LotPartnumber maps external part numbers to internal lots.
type LotPartnumber struct {
Vendor string `gorm:"column:vendor;size:255;primaryKey" json:"vendor"`
Partnumber string `gorm:"column:partnumber;size:255;primaryKey" json:"partnumber"`
LotName string `gorm:"column:lot_name;size:255" json:"lot_name"`
Description *string `gorm:"column:description;size:10000" json:"description,omitempty"`
IsPrimaryPN bool `gorm:"column:is_primary_pn;not null;default:true" json:"is_primary_pn"`
}
func (LotPartnumber) TableName() string {
return "lot_partnumbers"
}
// PartnumberBook is a versioned snapshot of the partnumber→LOT mapping.
// Written by PriceForge; QuoteForge reads via SELECT only.
type PartnumberBook struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
Version string `gorm:"column:version;size:30;not null;uniqueIndex:uq_qt_partnumber_books_version" json:"version"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
CreatedBy string `gorm:"column:created_by;size:100;not null;default:''" json:"created_by"`
IsActive bool `gorm:"column:is_active;not null;default:false" json:"is_active"`
}
func (PartnumberBook) TableName() string {
return "qt_partnumber_books"
}
// PartnumberBookItem is one mapping row in a PartnumberBook snapshot.
// Bundles are expanded: a single partnumber may have multiple rows (one per LOT component).
type PartnumberBookItem struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
BookID uint64 `gorm:"column:book_id;not null" json:"book_id"`
Partnumber string `gorm:"column:partnumber;size:255;not null" json:"partnumber"`
LotName string `gorm:"column:lot_name;size:255;not null" json:"lot_name"`
IsPrimaryPN bool `gorm:"column:is_primary_pn;not null;default:true" json:"is_primary_pn"`
Description *string `gorm:"column:description;size:10000" json:"description,omitempty"`
}
func (PartnumberBookItem) TableName() string {
return "qt_partnumber_book_items"
}
type LotBundle struct {
BundleLotName string `gorm:"column:bundle_lot_name;size:255;primaryKey" json:"bundle_lot_name"`
IsActive bool `gorm:"column:is_active;default:true" json:"is_active"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}
func (LotBundle) TableName() string {
return "qt_lot_bundles"
}
type LotBundleItem struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
BundleLotName string `gorm:"column:bundle_lot_name;size:255;not null;index:uq_qt_lot_bundle_items_bundle_lot,unique" json:"bundle_lot_name"`
LotName string `gorm:"column:lot_name;size:255;not null;index:uq_qt_lot_bundle_items_bundle_lot,unique" json:"lot_name"`
Qty float64 `gorm:"column:qty;not null" json:"qty"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}
func (LotBundleItem) TableName() string {
return "qt_lot_bundle_items"
}
type VendorPartnumberSeen struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
SourceType string `gorm:"column:source_type;size:32;not null" json:"source_type"`
Vendor string `gorm:"column:vendor;size:255;not null;default:'';index:idx_qt_vendor_partnumber_seen_vendor_partnumber" json:"vendor"`
Partnumber string `gorm:"column:partnumber;size:255;not null;uniqueIndex:uq_qt_vendor_partnumber_seen_partnumber;index:idx_qt_vendor_partnumber_seen_vendor_partnumber" json:"partnumber"`
Description *string `gorm:"column:description;size:10000" json:"description,omitempty"`
LastSeenAt time.Time `gorm:"column:last_seen_at;not null" json:"last_seen_at"`
IsIgnored bool `gorm:"column:is_ignored;not null;default:false;index:idx_qt_vendor_partnumber_seen_ignored" json:"is_ignored"`
IgnoredAt *time.Time `gorm:"column:ignored_at" json:"ignored_at,omitempty"`
IgnoredBy *string `gorm:"column:ignored_by;size:100" json:"ignored_by,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}
func (VendorPartnumberSeen) TableName() string {
return "qt_vendor_partnumber_seen"
}
type BOM struct {
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
ExternalBOMID string `gorm:"column:external_bom_id;size:255;not null;index:uq_qt_bom_source_external,unique" json:"external_bom_id"`
SourceSystem string `gorm:"column:source_system;size:100;not null;index:uq_qt_bom_source_external,unique" json:"source_system"`
Payload *string `gorm:"column:payload;type:json" json:"payload,omitempty"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}
func (BOM) TableName() string {
return "qt_bom"
}
// StockIgnoreRule is deprecated and kept for backward compatibility only.
// New logic uses VendorPartnumberSeen.is_ignored.
type StockIgnoreRule struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
Target string `gorm:"column:target;size:20;not null" json:"target"`
MatchType string `gorm:"column:match_type;size:20;not null" json:"match_type"`
Pattern string `gorm:"column:pattern;size:500;not null" json:"pattern"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
}
func (StockIgnoreRule) TableName() string {
return "stock_ignore_rules"
}