package models import ( "time" ) type PricelistSource string const ( PricelistSourceEstimate PricelistSource = "estimate" PricelistSourceWarehouse PricelistSource = "warehouse" PricelistSourceCompetitor PricelistSource = "competitor" PricelistSourceWorld PricelistSource = "world" ) func (s PricelistSource) IsValid() bool { switch s { case PricelistSourceEstimate, PricelistSourceWarehouse, PricelistSourceCompetitor, PricelistSourceWorld: return true default: return false } } func NormalizePricelistSource(source string) PricelistSource { switch PricelistSource(source) { case PricelistSourceWarehouse: return PricelistSourceWarehouse case PricelistSourceCompetitor: return PricelistSourceCompetitor case PricelistSourceWorld: return PricelistSourceWorld default: return PricelistSourceEstimate } } // Pricelist represents a versioned snapshot of prices type Pricelist struct { ID uint `gorm:"primaryKey" json:"id"` Source string `gorm:"size:20;not null;default:'estimate';uniqueIndex:idx_qt_pricelists_source_version,priority:1;index:idx_qt_pricelists_source_created_at,priority:1" json:"source"` Version string `gorm:"size:20;not null;uniqueIndex:idx_qt_pricelists_source_version,priority:2" json:"version"` // Format: YYYY-MM-DD-NNN Notification string `gorm:"size:500" json:"notification"` // Notification shown in configurator CreatedAt time.Time `gorm:"index:idx_qt_pricelists_source_created_at,priority:2,sort:desc" json:"created_at"` CreatedBy string `gorm:"size:100" json:"created_by"` IsActive bool `gorm:"default:true" json:"is_active"` UsageCount int `gorm:"default:0" json:"usage_count"` ExpiresAt *time.Time `json:"expires_at"` ItemCount int `gorm:"-" json:"item_count,omitempty"` // Virtual field for display } func (Pricelist) TableName() string { return "qt_pricelists" } // PricelistItem represents a single item in a pricelist type PricelistItem struct { ID uint `gorm:"primaryKey" json:"id"` PricelistID uint `gorm:"not null;index:idx_pricelist_lot" json:"pricelist_id"` LotName string `gorm:"size:255;not null;index:idx_pricelist_lot" json:"lot_name"` LotCategory *string `gorm:"column:lot_category;size:50" json:"category,omitempty"` Price float64 `gorm:"type:decimal(12,2);not null" json:"price"` PriceMethod string `gorm:"size:20" json:"price_method"` // Price calculation settings (snapshot from qt_lot_metadata) PricePeriodDays int `gorm:"default:90" json:"price_period_days"` PriceCoefficient float64 `gorm:"type:decimal(5,2);default:0" json:"price_coefficient"` ManualPrice *float64 `gorm:"type:decimal(12,2)" json:"manual_price,omitempty"` MetaPrices string `gorm:"size:1000" json:"meta_prices,omitempty"` // lead_time_weeks: 99 if no data available, NULL if not applicable. LeadTimeWeeks *int `gorm:"column:lead_time_weeks" json:"lead_time_weeks,omitempty"` // Virtual fields for display (not stored in qt_pricelist_items table) // LotDescription is populated via JOIN in SQL queries. // AvailableQty and Partnumbers are populated programmatically for warehouse/competitor pricelists. // CompetitorNames and PriceSpreadPct are populated for competitor pricelists. LotDescription string `gorm:"-" json:"lot_description,omitempty"` AvailableQty *float64 `gorm:"-" json:"available_qty,omitempty"` Partnumbers []string `gorm:"-" json:"partnumbers,omitempty"` CompetitorNames []string `gorm:"-" json:"competitor_names,omitempty"` PriceSpreadPct *float64 `gorm:"-" json:"price_spread_pct,omitempty"` PartnumberQtys map[string]float64 `gorm:"-" json:"partnumber_qtys,omitempty"` // pn → qty from competitor quotes } func (PricelistItem) TableName() string { return "qt_pricelist_items" } // PricelistSummary is used for list views type PricelistSummary struct { ID uint `json:"id"` Source string `json:"source"` Version string `json:"version"` Notification string `json:"notification"` CreatedAt time.Time `json:"created_at"` CreatedBy string `json:"created_by"` IsActive bool `json:"is_active"` UsageCount int `json:"usage_count"` ExpiresAt *time.Time `json:"expires_at"` ItemCount int64 `json:"item_count"` }