Competitor pricelist: aggregate all competitors, rebuild without re-import

- Add GetLatestQuotesAllCompetitors() repo method: latest quote per
  (competitor_id, partnumber) across all active competitors
- Add RebuildPricelist() service method: loads all quotes, applies each
  competitor's discount, aggregates with weighted_median per lot,
  creates single combined competitor pricelist
- Add POST /api/competitors/pricelist handler + route
- JS: "Создать прайслист" on competitor tab calls new endpoint instead
  of the generic one that required explicit items

This allows recreating the competitor pricelist after new lot mappings
are added, without requiring a new file upload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-03-13 08:17:44 +03:00
parent 592d77e30b
commit ec182abe99
5 changed files with 182 additions and 1 deletions

View File

@@ -294,6 +294,38 @@ func (h *CompetitorHandler) SetActive(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"is_active": req.IsActive})
}
// RebuildPricelist creates a new competitor pricelist from all stored quotes of active competitors.
func (h *CompetitorHandler) RebuildPricelist(c *gin.Context) {
createdBy := h.dbUsername
if createdBy == "" {
createdBy = "admin"
}
taskID := h.taskManager.Submit(tasks.TaskTypeCompetitorImport, func(_ context.Context, progressCb func(int, string)) (map[string]interface{}, error) {
result, err := h.importService.RebuildPricelist(createdBy, func(p services.CompetitorImportProgress) {
var progress int
if p.Total > 0 {
progress = int(float64(p.Current) / float64(p.Total) * 100)
}
progressCb(progress, p.Message)
})
if err != nil {
return nil, err
}
progressCb(100, result.PricelistVer)
return map[string]interface{}{
"rows_total": result.RowsTotal,
"inserted": result.Inserted,
"skipped": result.Skipped,
"unmapped": result.Unmapped,
"pricelist_id": result.PricelistID,
"pricelist_version": result.PricelistVer,
}, nil
})
c.JSON(http.StatusAccepted, gin.H{"task_id": taskID})
}
func (h *CompetitorHandler) Import(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {