feat: world-прайслист как заглушка для отсутствующих цен
Там, где для LOT нет цены в estimate/warehouse/competitor, подставляется цена из world-прайслиста. Такие ячейки в таблицах «Цена покупки»/«Цена продажи» подсвечиваются (amber), участвуют в «Итого» и убирают красную «*». В CSV-экспорте добавлена колонка «Заглушка (world)» с перечнем столбцов, где сработал фолбэк. Добавлены Tx-версии GetLatestLocalPricelistBySource/GetLocalPricesForLots, чтобы резолв прайслистов внутри транзакции не дедлочил single-connection пул. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
498cbf5490
commit
70a3ff255f
@@ -1351,8 +1351,17 @@ func (l *LocalDB) GetLatestLocalPricelist() (*LocalPricelist, error) {
|
||||
|
||||
// GetLatestLocalPricelistBySource returns the most recently synced active pricelist for a source.
|
||||
func (l *LocalDB) GetLatestLocalPricelistBySource(source string) (*LocalPricelist, error) {
|
||||
return GetLatestLocalPricelistBySourceTx(l.db, source)
|
||||
}
|
||||
|
||||
// GetLatestLocalPricelistBySourceTx is the transaction-scoped variant of
|
||||
// GetLatestLocalPricelistBySource. Callers already inside an l.db.Transaction(...) callback
|
||||
// must use this with the given tx instead of the method above — the connection pool has a
|
||||
// single connection (see New()), so querying via l.db while the transaction holds that
|
||||
// connection deadlocks forever.
|
||||
func GetLatestLocalPricelistBySourceTx(tx *gorm.DB, source string) (*LocalPricelist, error) {
|
||||
var pricelist LocalPricelist
|
||||
if err := l.db.
|
||||
if err := tx.
|
||||
Where("source = ? AND is_active = ?", source, true).
|
||||
Where("EXISTS (SELECT 1 FROM local_pricelist_items WHERE local_pricelist_items.pricelist_id = local_pricelists.id)").
|
||||
Order("created_at DESC, id DESC").
|
||||
@@ -1545,6 +1554,13 @@ func (l *LocalDB) GetLocalPriceForLot(pricelistID uint, lotName string) (float64
|
||||
// legacy rows that were stored in mixed case before normalization was enforced at sync time.
|
||||
// Keys in the returned map are uppercased (matching the input lotNames).
|
||||
func (l *LocalDB) GetLocalPricesForLots(pricelistID uint, lotNames []string) (map[string]float64, error) {
|
||||
return GetLocalPricesForLotsTx(l.db, pricelistID, lotNames)
|
||||
}
|
||||
|
||||
// GetLocalPricesForLotsTx is the transaction-scoped variant of GetLocalPricesForLots. See
|
||||
// GetLatestLocalPricelistBySourceTx for why callers inside an l.db.Transaction(...) callback
|
||||
// must use this with the given tx instead of the method above.
|
||||
func GetLocalPricesForLotsTx(tx *gorm.DB, pricelistID uint, lotNames []string) (map[string]float64, error) {
|
||||
result := make(map[string]float64, len(lotNames))
|
||||
if len(lotNames) == 0 {
|
||||
return result, nil
|
||||
@@ -1556,7 +1572,7 @@ func (l *LocalDB) GetLocalPricesForLots(pricelistID uint, lotNames []string) (ma
|
||||
}
|
||||
var rows []row
|
||||
// Use UPPER(lot_name) so rows synced before normalization (mixed-case) are still matched.
|
||||
if err := l.db.Model(&LocalPricelistItem{}).
|
||||
if err := tx.Model(&LocalPricelistItem{}).
|
||||
Select("lot_name, price").
|
||||
Where("pricelist_id = ? AND UPPER(lot_name) IN ?", pricelistID, lotNames).
|
||||
Find(&rows).Error; err != nil {
|
||||
|
||||
@@ -10,11 +10,12 @@ const (
|
||||
PricelistSourceEstimate PricelistSource = "estimate"
|
||||
PricelistSourceWarehouse PricelistSource = "warehouse"
|
||||
PricelistSourceCompetitor PricelistSource = "competitor"
|
||||
PricelistSourceWorld PricelistSource = "world"
|
||||
)
|
||||
|
||||
func (s PricelistSource) IsValid() bool {
|
||||
switch s {
|
||||
case PricelistSourceEstimate, PricelistSourceWarehouse, PricelistSourceCompetitor:
|
||||
case PricelistSourceEstimate, PricelistSourceWarehouse, PricelistSourceCompetitor, PricelistSourceWorld:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -27,6 +28,8 @@ func NormalizePricelistSource(source string) PricelistSource {
|
||||
return PricelistSourceWarehouse
|
||||
case PricelistSourceCompetitor:
|
||||
return PricelistSourceCompetitor
|
||||
case PricelistSourceWorld:
|
||||
return PricelistSourceWorld
|
||||
default:
|
||||
return PricelistSourceEstimate
|
||||
}
|
||||
|
||||
+81
-34
@@ -88,15 +88,18 @@ type ProjectPricingExportConfig struct {
|
||||
}
|
||||
|
||||
type ProjectPricingExportRow struct {
|
||||
LotDisplay string
|
||||
VendorPN string
|
||||
Description string
|
||||
Quantity int
|
||||
BOMTotal *float64
|
||||
Estimate *float64
|
||||
Stock *float64
|
||||
Competitor *float64
|
||||
ManualPrice *float64 // proportional share of the user-defined total price
|
||||
LotDisplay string
|
||||
VendorPN string
|
||||
Description string
|
||||
Quantity int
|
||||
BOMTotal *float64
|
||||
Estimate *float64
|
||||
Stock *float64
|
||||
Competitor *float64
|
||||
ManualPrice *float64 // proportional share of the user-defined total price
|
||||
EstimateWorld bool
|
||||
StockWorld bool
|
||||
CompetitorWorld bool
|
||||
}
|
||||
|
||||
// ToCSV writes project export data in the new structured CSV format.
|
||||
@@ -411,14 +414,17 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts
|
||||
bomTotal = vendorRowTotal(row)
|
||||
}
|
||||
block.Rows = append(block.Rows, ProjectPricingExportRow{
|
||||
LotDisplay: mapping.LotName,
|
||||
VendorPN: row.VendorPartnumber,
|
||||
Description: description,
|
||||
Quantity: lotQty,
|
||||
BOMTotal: bomTotal,
|
||||
Estimate: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Estimate }),
|
||||
Stock: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Stock }),
|
||||
Competitor: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Competitor }),
|
||||
LotDisplay: mapping.LotName,
|
||||
VendorPN: row.VendorPartnumber,
|
||||
Description: description,
|
||||
Quantity: lotQty,
|
||||
BOMTotal: bomTotal,
|
||||
Estimate: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Estimate }),
|
||||
Stock: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Stock }),
|
||||
Competitor: computeSingleLotTotal(priceMap, mapping.LotName, lotQty, func(p pricingLevels) *float64 { return p.Competitor }),
|
||||
EstimateWorld: priceMap[mapping.LotName].EstimateWorld,
|
||||
StockWorld: priceMap[mapping.LotName].StockWorld,
|
||||
CompetitorWorld: priceMap[mapping.LotName].CompetitorWorld,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -433,13 +439,16 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts
|
||||
}
|
||||
estimate := estimateOnlyTotal(priceMap[lot].Estimate, item.UnitPrice, item.Quantity)
|
||||
block.Rows = append(block.Rows, ProjectPricingExportRow{
|
||||
LotDisplay: lot,
|
||||
VendorPN: "—",
|
||||
Description: componentDescriptions[lot],
|
||||
Quantity: exportPositiveInt(item.Quantity, 1),
|
||||
Estimate: estimate,
|
||||
Stock: totalForUnitPrice(priceMap[lot].Stock, item.Quantity),
|
||||
Competitor: totalForUnitPrice(priceMap[lot].Competitor, item.Quantity),
|
||||
LotDisplay: lot,
|
||||
VendorPN: "—",
|
||||
Description: componentDescriptions[lot],
|
||||
Quantity: exportPositiveInt(item.Quantity, 1),
|
||||
Estimate: estimate,
|
||||
Stock: totalForUnitPrice(priceMap[lot].Stock, item.Quantity),
|
||||
Competitor: totalForUnitPrice(priceMap[lot].Competitor, item.Quantity),
|
||||
EstimateWorld: priceMap[lot].EstimateWorld && priceMap[lot].Estimate != nil && *priceMap[lot].Estimate > 0,
|
||||
StockWorld: priceMap[lot].StockWorld,
|
||||
CompetitorWorld: priceMap[lot].CompetitorWorld,
|
||||
})
|
||||
}
|
||||
if opts.isDDP() {
|
||||
@@ -466,13 +475,16 @@ func (s *ExportService) buildPricingExportBlock(cfg *models.Configuration, opts
|
||||
}
|
||||
estimate := estimateOnlyTotal(priceMap[item.LotName].Estimate, item.UnitPrice, item.Quantity)
|
||||
block.Rows = append(block.Rows, ProjectPricingExportRow{
|
||||
LotDisplay: item.LotName,
|
||||
VendorPN: "—",
|
||||
Description: componentDescriptions[item.LotName],
|
||||
Quantity: exportPositiveInt(item.Quantity, 1),
|
||||
Estimate: estimate,
|
||||
Stock: totalForUnitPrice(priceMap[item.LotName].Stock, item.Quantity),
|
||||
Competitor: totalForUnitPrice(priceMap[item.LotName].Competitor, item.Quantity),
|
||||
LotDisplay: item.LotName,
|
||||
VendorPN: "—",
|
||||
Description: componentDescriptions[item.LotName],
|
||||
Quantity: exportPositiveInt(item.Quantity, 1),
|
||||
Estimate: estimate,
|
||||
Stock: totalForUnitPrice(priceMap[item.LotName].Stock, item.Quantity),
|
||||
Competitor: totalForUnitPrice(priceMap[item.LotName].Competitor, item.Quantity),
|
||||
EstimateWorld: priceMap[item.LotName].EstimateWorld && priceMap[item.LotName].Estimate != nil && *priceMap[item.LotName].Estimate > 0,
|
||||
StockWorld: priceMap[item.LotName].StockWorld,
|
||||
CompetitorWorld: priceMap[item.LotName].CompetitorWorld,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -585,9 +597,12 @@ func sortItemsByCategory(items []ExportItem, categoryOrder map[string]int) {
|
||||
}
|
||||
|
||||
type pricingLevels struct {
|
||||
Estimate *float64
|
||||
Stock *float64
|
||||
Competitor *float64
|
||||
Estimate *float64
|
||||
Stock *float64
|
||||
Competitor *float64
|
||||
EstimateWorld bool
|
||||
StockWorld bool
|
||||
CompetitorWorld bool
|
||||
}
|
||||
|
||||
func (s *ExportService) resolvePricingTotals(cfg *models.Configuration, localCfg *localdb.LocalConfiguration, opts ProjectPricingExportOptions) map[string]pricingLevels {
|
||||
@@ -621,20 +636,35 @@ func (s *ExportService) resolvePricingTotals(cfg *models.Configuration, localCfg
|
||||
}
|
||||
}
|
||||
|
||||
var worldID *uint
|
||||
if latest, err := s.localDB.GetLatestLocalPricelistBySource("world"); err == nil && latest != nil {
|
||||
worldID = &latest.ServerID
|
||||
}
|
||||
|
||||
estimatePrices := s.batchLookupPrices(estimateID, lots)
|
||||
stockPrices := s.batchLookupPrices(warehouseID, lots)
|
||||
competitorPrices := s.batchLookupPrices(competitorID, lots)
|
||||
worldPrices := s.batchLookupPrices(worldID, lots)
|
||||
|
||||
for _, lot := range lots {
|
||||
level := pricingLevels{}
|
||||
if p, ok := estimatePrices[lot]; ok {
|
||||
level.Estimate = floatPtr(p)
|
||||
} else if p, ok := worldPrices[lot]; ok && p > 0 {
|
||||
level.Estimate = floatPtr(p)
|
||||
level.EstimateWorld = true
|
||||
}
|
||||
if p, ok := stockPrices[lot]; ok {
|
||||
level.Stock = floatPtr(p)
|
||||
} else if p, ok := worldPrices[lot]; ok && p > 0 {
|
||||
level.Stock = floatPtr(p)
|
||||
level.StockWorld = true
|
||||
}
|
||||
if p, ok := competitorPrices[lot]; ok {
|
||||
level.Competitor = floatPtr(p)
|
||||
} else if p, ok := worldPrices[lot]; ok && p > 0 {
|
||||
level.Competitor = floatPtr(p)
|
||||
level.CompetitorWorld = true
|
||||
}
|
||||
result[lot] = level
|
||||
}
|
||||
@@ -786,6 +816,7 @@ func pricingCSVHeaders(opts ProjectPricingExportOptions) []string {
|
||||
if opts.ManualPrice != nil && *opts.ManualPrice > 0 {
|
||||
headers = append(headers, "Ручная цена")
|
||||
}
|
||||
headers = append(headers, "Заглушка (world)")
|
||||
return headers
|
||||
}
|
||||
|
||||
@@ -815,6 +846,21 @@ func pricingCSVRow(row ProjectPricingExportRow, opts ProjectPricingExportOptions
|
||||
if opts.ManualPrice != nil && *opts.ManualPrice > 0 {
|
||||
record = append(record, formatMoneyValue(row.ManualPrice))
|
||||
}
|
||||
var worldCols []string
|
||||
if row.EstimateWorld {
|
||||
worldCols = append(worldCols, "Estimate")
|
||||
}
|
||||
if row.StockWorld {
|
||||
worldCols = append(worldCols, "Stock")
|
||||
}
|
||||
if row.CompetitorWorld {
|
||||
worldCols = append(worldCols, "Конкуренты")
|
||||
}
|
||||
comment := ""
|
||||
if len(worldCols) > 0 {
|
||||
comment = "world: " + strings.Join(worldCols, ", ")
|
||||
}
|
||||
record = append(record, comment)
|
||||
return record
|
||||
}
|
||||
|
||||
@@ -844,6 +890,7 @@ func pricingConfigSummaryRow(cfg ProjectPricingExportConfig, opts ProjectPricing
|
||||
if opts.ManualPrice != nil && *opts.ManualPrice > 0 {
|
||||
record = append(record, formatMoneyValue(opts.ManualPrice))
|
||||
}
|
||||
record = append(record, "")
|
||||
return record
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ type PriceLevelsItem struct {
|
||||
DeltaCompEstimatePct *float64 `json:"delta_comp_estimate_pct"`
|
||||
DeltaCompWhAbs *float64 `json:"delta_comp_wh_abs"`
|
||||
DeltaCompWhPct *float64 `json:"delta_comp_wh_pct"`
|
||||
EstimateFromWorld bool `json:"estimate_from_world"`
|
||||
WarehouseFromWorld bool `json:"warehouse_from_world"`
|
||||
CompetitorFromWorld bool `json:"competitor_from_world"`
|
||||
PriceMissing []string `json:"price_missing"`
|
||||
}
|
||||
|
||||
@@ -239,6 +242,29 @@ func (s *QuoteService) CalculatePriceLevels(req *PriceLevelsRequest) (*PriceLeve
|
||||
}
|
||||
}
|
||||
|
||||
var worldID uint
|
||||
if req.PricelistIDs != nil {
|
||||
if explicitID, ok := req.PricelistIDs[string(models.PricelistSourceWorld)]; ok && explicitID > 0 {
|
||||
worldID = explicitID
|
||||
}
|
||||
}
|
||||
if worldID == 0 && s.pricelistRepo != nil {
|
||||
if latest, err := s.pricelistRepo.GetLatestActiveBySource(string(models.PricelistSourceWorld)); err == nil {
|
||||
worldID = latest.ID
|
||||
}
|
||||
}
|
||||
if worldID == 0 && s.localDB != nil {
|
||||
if localPL, err := s.localDB.GetLatestLocalPricelistBySource(string(models.PricelistSourceWorld)); err == nil && localPL != nil {
|
||||
worldID = localPL.ServerID
|
||||
}
|
||||
}
|
||||
worldPrices := map[string]float64{}
|
||||
if worldID != 0 {
|
||||
if prices, err := s.lookupPricesByPricelistID(worldID, lotNames, req.NoCache); err == nil {
|
||||
worldPrices = prices
|
||||
}
|
||||
}
|
||||
|
||||
for _, reqItem := range req.Items {
|
||||
responseLotName := originalLotNames[reqItem.LotName]
|
||||
if responseLotName == "" {
|
||||
@@ -253,14 +279,26 @@ func (s *QuoteService) CalculatePriceLevels(req *PriceLevelsRequest) (*PriceLeve
|
||||
if p, ok := levelBySource[models.PricelistSourceEstimate].prices[reqItem.LotName]; ok && p > 0 {
|
||||
price := p
|
||||
item.EstimatePrice = &price
|
||||
} else if wp, ok := worldPrices[reqItem.LotName]; ok && wp > 0 {
|
||||
price := wp
|
||||
item.EstimatePrice = &price
|
||||
item.EstimateFromWorld = true
|
||||
}
|
||||
if p, ok := levelBySource[models.PricelistSourceWarehouse].prices[reqItem.LotName]; ok && p > 0 {
|
||||
price := p
|
||||
item.WarehousePrice = &price
|
||||
} else if wp, ok := worldPrices[reqItem.LotName]; ok && wp > 0 {
|
||||
price := wp
|
||||
item.WarehousePrice = &price
|
||||
item.WarehouseFromWorld = true
|
||||
}
|
||||
if p, ok := levelBySource[models.PricelistSourceCompetitor].prices[reqItem.LotName]; ok && p > 0 {
|
||||
price := p
|
||||
item.CompetitorPrice = &price
|
||||
} else if wp, ok := worldPrices[reqItem.LotName]; ok && wp > 0 {
|
||||
price := wp
|
||||
item.CompetitorPrice = &price
|
||||
item.CompetitorFromWorld = true
|
||||
}
|
||||
|
||||
if item.EstimatePrice == nil {
|
||||
|
||||
@@ -82,6 +82,51 @@ func TestCalculatePriceLevels_UsesExplicitPricelistIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculatePriceLevels_WorldFallback(t *testing.T) {
|
||||
db := newPriceLevelsTestDB(t)
|
||||
repo := repository.NewPricelistRepository(db)
|
||||
service := NewQuoteService(repo, nil)
|
||||
|
||||
seedPricelistWithItem(t, repo, "estimate", "CPU_Z", 100)
|
||||
seedPricelistWithItem(t, repo, "world", "CPU_Z", 150)
|
||||
|
||||
result, err := service.CalculatePriceLevels(&PriceLevelsRequest{
|
||||
Items: []struct {
|
||||
LotName string `json:"lot_name"`
|
||||
Quantity int `json:"quantity"`
|
||||
}{
|
||||
{LotName: "CPU_Z", Quantity: 1},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CalculatePriceLevels returned error: %v", err)
|
||||
}
|
||||
item := result.Items[0]
|
||||
|
||||
if item.EstimatePrice == nil || *item.EstimatePrice != 100 {
|
||||
t.Fatalf("expected native estimate 100, got %#v", item.EstimatePrice)
|
||||
}
|
||||
if item.EstimateFromWorld {
|
||||
t.Fatalf("expected estimate_from_world false when native price exists")
|
||||
}
|
||||
|
||||
if item.WarehousePrice == nil || *item.WarehousePrice != 150 {
|
||||
t.Fatalf("expected world-fallback warehouse 150, got %#v", item.WarehousePrice)
|
||||
}
|
||||
if !item.WarehouseFromWorld {
|
||||
t.Fatalf("expected warehouse_from_world true")
|
||||
}
|
||||
if item.CompetitorPrice == nil || *item.CompetitorPrice != 150 {
|
||||
t.Fatalf("expected world-fallback competitor 150, got %#v", item.CompetitorPrice)
|
||||
}
|
||||
if !item.CompetitorFromWorld {
|
||||
t.Fatalf("expected competitor_from_world true")
|
||||
}
|
||||
if len(item.PriceMissing) != 0 {
|
||||
t.Fatalf("expected no price_missing after world fallback, got %#v", item.PriceMissing)
|
||||
}
|
||||
}
|
||||
|
||||
func newPriceLevelsTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
||||
|
||||
@@ -165,7 +165,7 @@ func (s *LocalConfigurationService) ImportVendorWorkspaceToProject(projectUUID s
|
||||
|
||||
if len(imported.DirectItems) > 0 {
|
||||
items = imported.DirectItems
|
||||
estimatePricelist, _ := s.localDB.GetLatestLocalPricelistBySource("estimate")
|
||||
estimatePricelist, _ := localdb.GetLatestLocalPricelistBySourceTx(tx, "estimate")
|
||||
if estimatePricelist != nil {
|
||||
estimatePricelistID = &estimatePricelist.ServerID
|
||||
}
|
||||
@@ -173,7 +173,7 @@ func (s *LocalConfigurationService) ImportVendorWorkspaceToProject(projectUUID s
|
||||
totalPrice = &val
|
||||
} else {
|
||||
var prepErr error
|
||||
groupRows, items, totalPrice, estimatePricelistID, prepErr = s.prepareImportedConfiguration(imported.Rows, imported.ServerCount, bookRepo)
|
||||
groupRows, items, totalPrice, estimatePricelistID, prepErr = s.prepareImportedConfiguration(tx, imported.Rows, imported.ServerCount, bookRepo)
|
||||
if prepErr != nil {
|
||||
return fmt.Errorf("prepare imported configuration group %s: %w", imported.GroupID, prepErr)
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func (s *LocalConfigurationService) ImportVendorWorkspaceToProject(projectUUID s
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *LocalConfigurationService) prepareImportedConfiguration(rows []localdb.VendorSpecItem, serverCount int, bookRepo *repository.PartnumberBookRepository) (localdb.VendorSpec, localdb.LocalConfigItems, *float64, *uint, error) {
|
||||
func (s *LocalConfigurationService) prepareImportedConfiguration(tx *gorm.DB, rows []localdb.VendorSpecItem, serverCount int, bookRepo *repository.PartnumberBookRepository) (localdb.VendorSpec, localdb.LocalConfigItems, *float64, *uint, error) {
|
||||
resolver := NewVendorSpecResolver(bookRepo)
|
||||
resolved, err := resolver.Resolve(append([]localdb.VendorSpecItem(nil), rows...))
|
||||
if err != nil {
|
||||
@@ -242,13 +242,13 @@ func (s *LocalConfigurationService) prepareImportedConfiguration(rows []localdb.
|
||||
canonical = append(canonical, row)
|
||||
}
|
||||
|
||||
estimatePricelist, _ := s.localDB.GetLatestLocalPricelistBySource("estimate")
|
||||
estimatePricelist, _ := localdb.GetLatestLocalPricelistBySourceTx(tx, "estimate")
|
||||
var serverPricelistID *uint
|
||||
if estimatePricelist != nil {
|
||||
serverPricelistID = &estimatePricelist.ServerID
|
||||
}
|
||||
|
||||
items := aggregateVendorSpecToItems(canonical, estimatePricelist, s.localDB)
|
||||
items := aggregateVendorSpecToItemsTx(tx, canonical, estimatePricelist)
|
||||
totalValue := items.Total()
|
||||
if serverCount > 1 {
|
||||
totalValue *= float64(serverCount)
|
||||
@@ -257,7 +257,7 @@ func (s *LocalConfigurationService) prepareImportedConfiguration(rows []localdb.
|
||||
return canonical, items, totalPrice, serverPricelistID, nil
|
||||
}
|
||||
|
||||
func aggregateVendorSpecToItems(spec localdb.VendorSpec, estimatePricelist *localdb.LocalPricelist, local *localdb.LocalDB) localdb.LocalConfigItems {
|
||||
func aggregateVendorSpecToItemsTx(tx *gorm.DB, spec localdb.VendorSpec, estimatePricelist *localdb.LocalPricelist) localdb.LocalConfigItems {
|
||||
if len(spec) == 0 {
|
||||
return localdb.LocalConfigItems{}
|
||||
}
|
||||
@@ -276,8 +276,8 @@ func aggregateVendorSpecToItems(spec localdb.VendorSpec, estimatePricelist *loca
|
||||
sort.Strings(order)
|
||||
|
||||
var priceMap map[string]float64
|
||||
if estimatePricelist != nil && local != nil && len(order) > 0 {
|
||||
priceMap, _ = local.GetLocalPricesForLots(estimatePricelist.ID, order)
|
||||
if estimatePricelist != nil && len(order) > 0 {
|
||||
priceMap, _ = localdb.GetLocalPricesForLotsTx(tx, estimatePricelist.ID, order)
|
||||
}
|
||||
|
||||
items := make(localdb.LocalConfigItems, 0, len(order))
|
||||
|
||||
Reference in New Issue
Block a user