Update pricing handler counts and defaults
This commit is contained in:
@@ -211,13 +211,52 @@ func (h *PricingHandler) ListComponents(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get quote counts
|
// Get all lot names for meta expansion
|
||||||
|
var allLotNames []string
|
||||||
|
h.db.Model(&models.LotMetadata{}).Pluck("lot_name", &allLotNames)
|
||||||
|
|
||||||
|
// Build per-component source lots for quote counts
|
||||||
|
componentSources := make(map[string][]string, len(components))
|
||||||
|
uniqueLots := make(map[string]struct{})
|
||||||
lotNames := make([]string, len(components))
|
lotNames := make([]string, len(components))
|
||||||
for i, comp := range components {
|
for i, comp := range components {
|
||||||
lotNames[i] = comp.LotName
|
lotNames[i] = comp.LotName
|
||||||
|
var sourceLots []string
|
||||||
|
if strings.TrimSpace(comp.MetaPrices) != "" {
|
||||||
|
sourceLots = expandMetaPricesWithCache(comp.MetaPrices, comp.LotName, allLotNames)
|
||||||
|
} else {
|
||||||
|
sourceLots = []string{comp.LotName}
|
||||||
|
}
|
||||||
|
componentSources[comp.LotName] = sourceLots
|
||||||
|
for _, ln := range sourceLots {
|
||||||
|
if ln == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uniqueLots[ln] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
counts, _ := h.priceRepo.GetQuoteCounts(lotNames)
|
// Fetch quote counts for all source lots in one query
|
||||||
|
counts := map[string]int64{}
|
||||||
|
if len(uniqueLots) > 0 {
|
||||||
|
allSources := make([]string, 0, len(uniqueLots))
|
||||||
|
for ln := range uniqueLots {
|
||||||
|
allSources = append(allSources, ln)
|
||||||
|
}
|
||||||
|
type Result struct {
|
||||||
|
Lot string
|
||||||
|
Count int64
|
||||||
|
}
|
||||||
|
var results []Result
|
||||||
|
_ = h.db.Model(&models.LotLog{}).
|
||||||
|
Select("lot, COUNT(*) as count").
|
||||||
|
Where("lot IN ?", allSources).
|
||||||
|
Group("lot").
|
||||||
|
Scan(&results).Error
|
||||||
|
for _, r := range results {
|
||||||
|
counts[r.Lot] = r.Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get meta usage information
|
// Get meta usage information
|
||||||
metaUsage := h.getMetaUsageMap(lotNames)
|
metaUsage := h.getMetaUsageMap(lotNames)
|
||||||
@@ -225,9 +264,14 @@ func (h *PricingHandler) ListComponents(c *gin.Context) {
|
|||||||
// Combine components with counts
|
// Combine components with counts
|
||||||
result := make([]ComponentWithCount, len(components))
|
result := make([]ComponentWithCount, len(components))
|
||||||
for i, comp := range components {
|
for i, comp := range components {
|
||||||
|
sourceLots := componentSources[comp.LotName]
|
||||||
|
var quoteCount int64
|
||||||
|
for _, ln := range sourceLots {
|
||||||
|
quoteCount += counts[ln]
|
||||||
|
}
|
||||||
result[i] = ComponentWithCount{
|
result[i] = ComponentWithCount{
|
||||||
LotMetadata: comp,
|
LotMetadata: comp,
|
||||||
QuoteCount: counts[comp.LotName],
|
QuoteCount: quoteCount,
|
||||||
UsedInMeta: metaUsage[comp.LotName],
|
UsedInMeta: metaUsage[comp.LotName],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,6 +566,8 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
force := c.Query("force") == "1" || strings.EqualFold(c.Query("force"), "true")
|
||||||
|
|
||||||
taskID := h.taskManager.Submit(tasks.TaskTypeRecalculate, func(ctx context.Context, progressCb func(int, string)) (map[string]interface{}, error) {
|
taskID := h.taskManager.Submit(tasks.TaskTypeRecalculate, func(ctx context.Context, progressCb func(int, string)) (map[string]interface{}, error) {
|
||||||
// Get all components with their settings
|
// Get all components with their settings
|
||||||
var components []models.LotMetadata
|
var components []models.LotMetadata
|
||||||
@@ -554,7 +600,7 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process components individually to respect their settings
|
// Process components individually to respect their settings
|
||||||
var updated, skipped, manual, unchanged, errors int
|
var updated, skipped, manual, unchanged, errors, noPeriodData int
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
for i, comp := range components {
|
for i, comp := range components {
|
||||||
@@ -586,7 +632,7 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if there are new quotes since last update (using cached dates)
|
// Check if there are new quotes since last update (using cached dates)
|
||||||
if comp.PriceUpdatedAt != nil {
|
if !force && comp.PriceUpdatedAt != nil {
|
||||||
hasNewData := false
|
hasNewData := false
|
||||||
for _, lot := range sourceLots {
|
for _, lot := range sourceLots {
|
||||||
if latestDate, ok := lotLatestDate[lot]; ok {
|
if latestDate, ok := lotLatestDate[lot]; ok {
|
||||||
@@ -609,8 +655,10 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
goto sendProgress
|
goto sendProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noPeriodForComp := false
|
||||||
// If no prices in period, try all time
|
// If no prices in period, try all time
|
||||||
if len(prices) == 0 && periodDays > 0 {
|
if len(prices) == 0 && periodDays > 0 {
|
||||||
|
noPeriodForComp = true
|
||||||
prices, err = h.queryMultipleLotPrices(sourceLots, 0)
|
prices, err = h.queryMultipleLotPrices(sourceLots, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors++
|
errors++
|
||||||
@@ -619,9 +667,15 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(prices) == 0 {
|
if len(prices) == 0 {
|
||||||
|
if noPeriodForComp {
|
||||||
|
noPeriodData++
|
||||||
|
}
|
||||||
skipped++
|
skipped++
|
||||||
goto sendProgress
|
goto sendProgress
|
||||||
}
|
}
|
||||||
|
if noPeriodForComp {
|
||||||
|
noPeriodData++
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate price based on method
|
// Calculate price based on method
|
||||||
var basePrice float64
|
var basePrice float64
|
||||||
@@ -676,15 +730,16 @@ func (h *PricingHandler) RecalculateAll(c *gin.Context) {
|
|||||||
h.statsRepo.UpdatePopularityScores()
|
h.statsRepo.UpdatePopularityScores()
|
||||||
|
|
||||||
// Send final completion message
|
// Send final completion message
|
||||||
progressCb(100, fmt.Sprintf("Пересчёт завершён: обновлено %d из %d компонентов", updated, total))
|
progressCb(100, fmt.Sprintf("Пересчёт завершён: обновлено %d из %d, без данных за период: %d", updated, total, noPeriodData))
|
||||||
|
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"updated": updated,
|
"updated": updated,
|
||||||
"skipped": skipped,
|
"skipped": skipped,
|
||||||
"manual": manual,
|
"manual": manual,
|
||||||
"unchanged": unchanged,
|
"unchanged": unchanged,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
"total": total,
|
"total": total,
|
||||||
|
"no_period_data": noPeriodData,
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1456,8 +1511,7 @@ func (h *PricingHandler) CreateLot(c *gin.Context) {
|
|||||||
|
|
||||||
categoryCode := strings.ToUpper(strings.TrimSpace(req.Category))
|
categoryCode := strings.ToUpper(strings.TrimSpace(req.Category))
|
||||||
if categoryCode == "" {
|
if categoryCode == "" {
|
||||||
parsedCategory, _ := services.ParsePartNumber(lotName)
|
categoryCode = models.DefaultLotCategoryCode
|
||||||
categoryCode = strings.ToUpper(strings.TrimSpace(parsedCategory))
|
|
||||||
}
|
}
|
||||||
if len(categoryCode) > 20 {
|
if len(categoryCode) > 20 {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "category too long (max 20)"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "category too long (max 20)"})
|
||||||
|
|||||||
Reference in New Issue
Block a user