package sync import ( "fmt" "log/slog" "time" "git.mchus.pro/mchus/quoteforge/internal/localdb" "git.mchus.pro/mchus/quoteforge/internal/repository" ) // PullPartnumberBooks synchronizes partnumber book snapshots from MariaDB to local SQLite. // It only pulls books that don't exist locally yet (append-only). func (s *Service) PullPartnumberBooks() (int, error) { slog.Info("starting partnumber book pull") mariaDB, err := s.getDB() if err != nil { return 0, fmt.Errorf("database not available: %w", err) } localBookRepo := repository.NewPartnumberBookRepository(s.localDB.DB()) // Query server for all active partnumber books type serverBook struct { ID int `gorm:"column:id"` Version string `gorm:"column:version"` CreatedAt time.Time `gorm:"column:created_at"` IsActive bool `gorm:"column:is_active"` } var serverBooks []serverBook if err := mariaDB.Raw("SELECT id, version, created_at, is_active FROM qt_partnumber_books WHERE is_active = 1 ORDER BY created_at DESC, id DESC").Scan(&serverBooks).Error; err != nil { return 0, fmt.Errorf("querying server partnumber books: %w", err) } pulled := 0 for _, sb := range serverBooks { // Check if already exists locally var existing localdb.LocalPartnumberBook err := s.localDB.DB().Where("server_id = ?", sb.ID).First(&existing).Error if err == nil { // Already exists continue } // Save the book localBook := &localdb.LocalPartnumberBook{ ServerID: sb.ID, Version: sb.Version, CreatedAt: sb.CreatedAt, IsActive: sb.IsActive, } if err := localBookRepo.SaveBook(localBook); err != nil { slog.Warn("failed to save local partnumber book", "server_id", sb.ID, "error", err) continue } // Pull items for this book type serverItem struct { Partnumber string `gorm:"column:partnumber"` LotName string `gorm:"column:lot_name"` IsPrimaryPN bool `gorm:"column:is_primary_pn"` Description string `gorm:"column:description"` } var serverItems []serverItem if err := mariaDB.Raw("SELECT partnumber, lot_name, is_primary_pn, description FROM qt_partnumber_book_items WHERE book_id = ?", sb.ID).Scan(&serverItems).Error; err != nil { slog.Warn("failed to query server partnumber book items", "book_id", sb.ID, "error", err) continue } localItems := make([]localdb.LocalPartnumberBookItem, 0, len(serverItems)) for _, si := range serverItems { localItems = append(localItems, localdb.LocalPartnumberBookItem{ BookID: localBook.ID, Partnumber: si.Partnumber, LotName: si.LotName, IsPrimaryPN: si.IsPrimaryPN, Description: si.Description, }) } if err := localBookRepo.SaveBookItems(localItems); err != nil { slog.Warn("failed to save local partnumber book items", "book_id", localBook.ID, "error", err) continue } slog.Info("pulled partnumber book", "version", sb.Version, "items", len(localItems)) pulled++ } slog.Info("partnumber book pull completed", "pulled", pulled) return pulled, nil }