- Drop `expected_discount_pct`, add `price_uplift DECIMAL(8,4) DEFAULT 1.3` to `qt_competitors` (migration 040); formula: effective_price = price / uplift - Extend `LoadLotMetrics` to return per-PN qty map (`pnQtysByLot`) - Add virtual fields `CompetitorNames`, `PriceSpreadPct`, `PartnumberQtys` to `PricelistItem`; populate via `enrichWarehouseItems` / `enrichCompetitorItems` - Competitor quotes filtered to qty > 0 before lot resolution - New "stock layout" on pricelist detail page for warehouse/competitor: Partnumbers column (PN + qty, only qty>0), Поставщик column, no Настройки/Доступно - Spread badge ±N% shown next to price for competitor rows - Bible updated: pricelist.md, history.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
66 lines
3.3 KiB
Go
66 lines
3.3 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// Competitor represents a competitor profile
|
|
type Competitor struct {
|
|
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
|
Name string `gorm:"size:255;not null" json:"name"`
|
|
Code string `gorm:"size:100;not null;uniqueIndex" json:"code"`
|
|
DeliveryBasis string `gorm:"size:50;not null;default:DDP" json:"delivery_basis"`
|
|
Currency string `gorm:"size:10;not null;default:USD" json:"currency"` // currency of prices in file
|
|
PriceUplift float64 `gorm:"type:decimal(8,4);not null;default:1.3" json:"price_uplift"` // effective_price = price / price_uplift
|
|
ColumnMapping []byte `gorm:"type:json" json:"column_mapping,omitempty"`
|
|
IsActive bool `gorm:"not null;default:true" json:"is_active"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
|
}
|
|
|
|
func (Competitor) TableName() string {
|
|
return "qt_competitors"
|
|
}
|
|
|
|
// CompetitorQuote stores a historical price quote from a competitor.
|
|
// Mirrors stock_log structure: partnumber as-is from file, date = quote date entered by user.
|
|
// LOT resolution happens at pricelist build time via current p/n mappings.
|
|
type CompetitorQuote struct {
|
|
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
|
CompetitorID uint64 `gorm:"not null;index" json:"competitor_id"`
|
|
Partnumber string `gorm:"size:255;not null;index" json:"partnumber"`
|
|
Description *string `gorm:"size:500" json:"description,omitempty"`
|
|
Vendor *string `gorm:"size:255" json:"vendor,omitempty"`
|
|
Price float64 `gorm:"type:decimal(12,2);not null" json:"price"` // price in USD (always)
|
|
PriceLoccur *float64 `gorm:"type:decimal(12,2)" json:"price_loccur,omitempty"` // original price in local currency
|
|
Currency *string `gorm:"size:10" json:"currency,omitempty"` // local currency code
|
|
Qty float64 `gorm:"type:decimal(12,4);not null;default:1" json:"qty"`
|
|
Date time.Time `gorm:"type:date;not null" json:"date"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
|
|
Competitor *Competitor `gorm:"foreignKey:CompetitorID" json:"competitor,omitempty"`
|
|
}
|
|
|
|
func (CompetitorQuote) TableName() string {
|
|
return "partnumber_log_competitors"
|
|
}
|
|
|
|
// CompetitorColumnMapping defines how to read Excel columns for a competitor.
|
|
// Competitor files always contain partnumbers (never lot names directly).
|
|
// Partnumbers are resolved to lots via qt_partnumber_book_items.
|
|
type CompetitorColumnMapping struct {
|
|
Sheet int `json:"sheet"`
|
|
HeaderRow int `json:"header_row"`
|
|
PartnumberCol string `json:"partnumber_col"`
|
|
DescriptionCol string `json:"description_col,omitempty"`
|
|
VendorCol string `json:"vendor_col,omitempty"`
|
|
// PriceCol is the legacy single price column (treated as local currency, converted via rateToUSD).
|
|
PriceCol string `json:"price_col,omitempty"`
|
|
// PriceUSDCol is an explicit USD price column (no conversion needed).
|
|
PriceUSDCol string `json:"price_usd_col,omitempty"`
|
|
// PriceLocCurCol is an explicit local-currency price column (requires conversion via rateToUSD).
|
|
PriceLocCurCol string `json:"price_loccur_col,omitempty"`
|
|
QtyCol string `json:"qty_col,omitempty"`
|
|
SkipRows []int `json:"skip_rows,omitempty"`
|
|
}
|