- 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>
169 lines
8.1 KiB
Go
169 lines
8.1 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 the supplier table.
|
|
// PriceForge manages this table for the unified import pipeline.
|
|
type Supplier struct {
|
|
SupplierName string `gorm:"column:supplier_name;primaryKey;size:255" json:"supplier_name"`
|
|
SupplierComment string `gorm:"column:supplier_comment;size:10000" json:"supplier_comment,omitempty"`
|
|
SupplierCode string `gorm:"column:supplier_code;size:100;not null;default:'';uniqueIndex:uq_supplier_code" json:"supplier_code"`
|
|
SupplierType string `gorm:"column:supplier_type;type:enum('trader','self','competitor');not null;default:'trader'" json:"supplier_type"`
|
|
PriceUplift float64 `gorm:"column:price_uplift;type:decimal(8,4);not null;default:1.0" json:"price_uplift"`
|
|
DefaultLeadTimeWeeks *int `gorm:"column:default_lead_time_weeks" json:"default_lead_time_weeks,omitempty"`
|
|
DefaultImportFormatCode *string `gorm:"column:default_import_format_code;size:100" json:"default_import_format_code,omitempty"`
|
|
IsActive bool `gorm:"column:is_active;not null;default:true" json:"is_active"`
|
|
}
|
|
|
|
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 is deprecated and kept only for in-memory compatibility in tests.
|
|
// Runtime must use qt_partnumber_book_items.lots_json instead of the lot_partnumbers table.
|
|
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"`
|
|
}
|
|
|
|
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"`
|
|
PartnumbersJSON string `gorm:"column:partnumbers_json;type:longtext;not null" json:"partnumbers_json"`
|
|
}
|
|
|
|
func (PartnumberBook) TableName() string {
|
|
return "qt_partnumber_books"
|
|
}
|
|
|
|
type PartnumberBookLot struct {
|
|
LotName string `json:"lot_name"`
|
|
Qty float64 `json:"qty"`
|
|
}
|
|
|
|
// PartnumberBookItem is the current source-of-truth row for one partnumber.
|
|
// lots_json stores the resolved LOT composition with quantities.
|
|
type PartnumberBookItem struct {
|
|
ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
|
|
Partnumber string `gorm:"column:partnumber;size:255;not null;uniqueIndex:uq_qt_partnumber_book_items_partnumber" json:"partnumber"`
|
|
LotsJSON string `gorm:"column:lots_json;type:longtext;not null" json:"lots_json"`
|
|
Description *string `gorm:"column:description;size:10000" json:"description,omitempty"`
|
|
}
|
|
|
|
func (PartnumberBookItem) TableName() string {
|
|
return "qt_partnumber_book_items"
|
|
}
|
|
|
|
// LotBundle is deprecated and kept only for compatibility with legacy tests.
|
|
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"
|
|
}
|
|
|
|
// LotBundleItem is deprecated and kept only for compatibility with legacy tests.
|
|
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"`
|
|
IsPattern bool `gorm:"column:is_pattern;not null;default:false" json:"is_pattern,omitempty"`
|
|
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"
|
|
}
|
|
|
|
// 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"
|
|
}
|