Fix pricelist sync upsert and refresh tests

This commit is contained in:
Mikhail Chusavitin
2026-04-28 16:53:38 +03:00
parent 11fd314a65
commit 61d23ef8c4
4 changed files with 178 additions and 61 deletions

View File

@@ -17,6 +17,7 @@ import (
"git.mchus.pro/mchus/quoteforge/internal/repository"
"github.com/google/uuid"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
var ErrOffline = errors.New("database is offline")
@@ -357,6 +358,18 @@ func (s *Service) SyncPricelists() (int, error) {
// Check if pricelist already exists locally
existing, _ := s.localDB.GetLocalPricelistByServerID(pl.ID)
if existing != nil {
existing.Source = pl.Source
existing.Version = pl.Version
existing.Name = pl.Notification
existing.CreatedAt = pl.CreatedAt
existing.SyncedAt = time.Now()
if err := s.localDB.SaveLocalPricelist(existing); err != nil {
if syncErr == nil {
syncErr = fmt.Errorf("refresh existing pricelist %s: %w", pl.Version, err)
}
slog.Warn("failed to refresh existing local pricelist header", "version", pl.Version, "error", err)
continue
}
// Backfill items for legacy/partial local caches where only pricelist metadata exists.
if s.localDB.CountLocalPricelistItems(existing.ID) == 0 {
itemCount, err := s.SyncPricelistItems(existing.ID)
@@ -468,24 +481,29 @@ func (s *Service) syncNewPricelistSnapshot(localPL *localdb.LocalPricelist) (int
}
if err := s.localDB.DB().Transaction(func(tx *gorm.DB) error {
if err := tx.Create(localPL).Error; err != nil {
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "server_id"}},
DoUpdates: clause.Assignments(map[string]interface{}{
"source": localPL.Source,
"version": localPL.Version,
"name": localPL.Name,
"created_at": localPL.CreatedAt,
"synced_at": localPL.SyncedAt,
"is_used": localPL.IsUsed,
}),
}).Create(localPL).Error; err != nil {
return fmt.Errorf("save local pricelist: %w", err)
}
if len(localItems) == 0 {
return nil
if localPL.ID == 0 {
if err := tx.Where("server_id = ?", localPL.ServerID).First(localPL).Error; err != nil {
return fmt.Errorf("reload local pricelist: %w", err)
}
}
for i := range localItems {
localItems[i].PricelistID = localPL.ID
}
batchSize := 500
for i := 0; i < len(localItems); i += batchSize {
end := i + batchSize
if end > len(localItems) {
end = len(localItems)
}
if err := tx.CreateInBatches(localItems[i:end], batchSize).Error; err != nil {
return fmt.Errorf("save local pricelist items: %w", err)
}
if err := replaceLocalPricelistItemsTx(tx, localPL.ID, localItems); err != nil {
return fmt.Errorf("save local pricelist items: %w", err)
}
return nil
}); err != nil {
@@ -496,6 +514,27 @@ func (s *Service) syncNewPricelistSnapshot(localPL *localdb.LocalPricelist) (int
return len(localItems), nil
}
func replaceLocalPricelistItemsTx(tx *gorm.DB, pricelistID uint, items []localdb.LocalPricelistItem) error {
if err := tx.Where("pricelist_id = ?", pricelistID).Delete(&localdb.LocalPricelistItem{}).Error; err != nil {
return err
}
if len(items) == 0 {
return nil
}
batchSize := 500
for i := 0; i < len(items); i += batchSize {
end := i + batchSize
if end > len(items) {
end = len(items)
}
if err := tx.CreateInBatches(items[i:end], batchSize).Error; err != nil {
return err
}
}
return nil
}
func (s *Service) backfillUsedPricelistItemCategories(pricelistRepo *repository.PricelistRepository, activeServerPricelistIDs []uint) {
if s.localDB == nil || pricelistRepo == nil {
return