WIP: save current pricing and pricelist changes

This commit is contained in:
Mikhail Chusavitin
2026-02-06 19:07:22 +03:00
parent 29035ddc5a
commit b965c6bb95
18 changed files with 1263 additions and 182 deletions

View File

@@ -1,11 +1,14 @@
package handlers
import (
"errors"
"fmt"
"io"
"net/http"
"strconv"
"git.mchus.pro/mchus/quoteforge/internal/localdb"
"git.mchus.pro/mchus/quoteforge/internal/models"
"git.mchus.pro/mchus/quoteforge/internal/services/pricelist"
"github.com/gin-gonic/gin"
)
@@ -24,6 +27,7 @@ func (h *PricelistHandler) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "20"))
activeOnly := c.DefaultQuery("active_only", "false") == "true"
source := c.Query("source")
var (
pricelists any
@@ -32,9 +36,9 @@ func (h *PricelistHandler) List(c *gin.Context) {
)
if activeOnly {
pricelists, total, err = h.service.ListActive(page, perPage)
pricelists, total, err = h.service.ListActiveBySource(page, perPage, source)
} else {
pricelists, total, err = h.service.List(page, perPage)
pricelists, total, err = h.service.ListBySource(page, perPage, source)
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
@@ -45,11 +49,21 @@ func (h *PricelistHandler) List(c *gin.Context) {
if total == 0 && h.localDB != nil {
localPLs, err := h.localDB.GetLocalPricelists()
if err == nil && len(localPLs) > 0 {
if source != "" {
filtered := localPLs[:0]
for _, lpl := range localPLs {
if lpl.Source == source {
filtered = append(filtered, lpl)
}
}
localPLs = filtered
}
// Convert to PricelistSummary format
summaries := make([]map[string]interface{}, len(localPLs))
for i, lpl := range localPLs {
summaries[i] = map[string]interface{}{
"id": lpl.ServerID,
"source": lpl.Source,
"version": lpl.Version,
"created_by": "sync",
"item_count": 0, // Not tracked
@@ -108,13 +122,33 @@ func (h *PricelistHandler) Create(c *gin.Context) {
return
}
var req struct {
Source string `json:"source"`
Items []struct {
LotName string `json:"lot_name"`
Price float64 `json:"price"`
} `json:"items"`
}
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
source := string(models.NormalizePricelistSource(req.Source))
// Get the database username as the creator
createdBy := h.localDB.GetDBUser()
if createdBy == "" {
createdBy = "unknown"
}
sourceItems := make([]pricelist.CreateItemInput, 0, len(req.Items))
for _, item := range req.Items {
sourceItems = append(sourceItems, pricelist.CreateItemInput{
LotName: item.LotName,
Price: item.Price,
})
}
pl, err := h.service.CreateFromCurrentPrices(createdBy)
pl, err := h.service.CreateForSourceWithProgress(createdBy, source, sourceItems, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@@ -134,10 +168,30 @@ func (h *PricelistHandler) CreateWithProgress(c *gin.Context) {
return
}
var req struct {
Source string `json:"source"`
Items []struct {
LotName string `json:"lot_name"`
Price float64 `json:"price"`
} `json:"items"`
}
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
source := string(models.NormalizePricelistSource(req.Source))
createdBy := h.localDB.GetDBUser()
if createdBy == "" {
createdBy = "unknown"
}
sourceItems := make([]pricelist.CreateItemInput, 0, len(req.Items))
for _, item := range req.Items {
sourceItems = append(sourceItems, pricelist.CreateItemInput{
LotName: item.LotName,
Price: item.Price,
})
}
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
@@ -146,7 +200,7 @@ func (h *PricelistHandler) CreateWithProgress(c *gin.Context) {
flusher, ok := c.Writer.(http.Flusher)
if !ok {
pl, err := h.service.CreateFromCurrentPrices(createdBy)
pl, err := h.service.CreateForSourceWithProgress(createdBy, source, sourceItems, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@@ -161,7 +215,7 @@ func (h *PricelistHandler) CreateWithProgress(c *gin.Context) {
}
sendProgress(gin.H{"current": 0, "total": 4, "status": "starting", "message": "Запуск..."})
pl, err := h.service.CreateFromCurrentPricesWithProgress(createdBy, func(p pricelist.CreateProgress) {
pl, err := h.service.CreateForSourceWithProgress(createdBy, source, sourceItems, func(p pricelist.CreateProgress) {
sendProgress(gin.H{
"current": p.Current,
"total": p.Total,
@@ -269,8 +323,14 @@ func (h *PricelistHandler) GetItems(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
pl, _ := h.service.GetByID(uint(id))
source := ""
if pl != nil {
source = pl.Source
}
c.JSON(http.StatusOK, gin.H{
"source": source,
"items": items,
"total": total,
"page": page,
@@ -286,15 +346,18 @@ func (h *PricelistHandler) CanWrite(c *gin.Context) {
// GetLatest returns the most recent active pricelist
func (h *PricelistHandler) GetLatest(c *gin.Context) {
source := c.DefaultQuery("source", string(models.PricelistSourceEstimate))
source = string(models.NormalizePricelistSource(source))
// Try to get from server first
pl, err := h.service.GetLatestActive()
pl, err := h.service.GetLatestActiveBySource(source)
if err != nil {
// If offline or no server pricelists, try to get from local cache
if h.localDB == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "no database available"})
return
}
localPL, localErr := h.localDB.GetLatestLocalPricelist()
localPL, localErr := h.localDB.GetLatestLocalPricelistBySource(source)
if localErr != nil {
// No local pricelists either
c.JSON(http.StatusNotFound, gin.H{
@@ -306,6 +369,7 @@ func (h *PricelistHandler) GetLatest(c *gin.Context) {
// Return local pricelist
c.JSON(http.StatusOK, gin.H{
"id": localPL.ServerID,
"source": localPL.Source,
"version": localPL.Version,
"created_by": "sync",
"item_count": 0, // Not tracked in local pricelists