package repository import ( "git.mchus.pro/mchus/quoteforge/internal/localdb" "gorm.io/gorm" ) // PartnumberBookRepository provides read-only access to local partnumber book snapshots. type PartnumberBookRepository struct { db *gorm.DB } func NewPartnumberBookRepository(db *gorm.DB) *PartnumberBookRepository { return &PartnumberBookRepository{db: db} } // GetActiveBook returns the most recently active local partnumber book. func (r *PartnumberBookRepository) GetActiveBook() (*localdb.LocalPartnumberBook, error) { var book localdb.LocalPartnumberBook err := r.db.Where("is_active = 1").Order("created_at DESC, id DESC").First(&book).Error if err != nil { return nil, err } return &book, nil } // GetBookItems returns all items for the given local book ID. func (r *PartnumberBookRepository) GetBookItems(bookID uint) ([]localdb.LocalPartnumberBookItem, error) { var items []localdb.LocalPartnumberBookItem err := r.db.Where("book_id = ?", bookID).Find(&items).Error return items, err } // GetBookItemsPage returns items for the given local book ID with optional search and pagination. func (r *PartnumberBookRepository) GetBookItemsPage(bookID uint, search string, page, perPage int) ([]localdb.LocalPartnumberBookItem, int64, error) { if page < 1 { page = 1 } if perPage < 1 { perPage = 100 } query := r.db.Model(&localdb.LocalPartnumberBookItem{}).Where("book_id = ?", bookID) trimmedSearch := "%" + search + "%" if search != "" { query = query.Where("partnumber LIKE ? OR lot_name LIKE ?", trimmedSearch, trimmedSearch) } var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, err } var items []localdb.LocalPartnumberBookItem err := query. Order("partnumber ASC, lot_name ASC, id ASC"). Offset((page - 1) * perPage). Limit(perPage). Find(&items).Error return items, total, err } // FindLotByPartnumber looks up a partnumber in the active book and returns the matching items. func (r *PartnumberBookRepository) FindLotByPartnumber(bookID uint, partnumber string) ([]localdb.LocalPartnumberBookItem, error) { var items []localdb.LocalPartnumberBookItem err := r.db.Where("book_id = ? AND partnumber = ?", bookID, partnumber).Find(&items).Error return items, err } // ListBooks returns all local partnumber books ordered newest first. func (r *PartnumberBookRepository) ListBooks() ([]localdb.LocalPartnumberBook, error) { var books []localdb.LocalPartnumberBook err := r.db.Order("created_at DESC, id DESC").Find(&books).Error return books, err } // SaveBook saves a new partnumber book snapshot. func (r *PartnumberBookRepository) SaveBook(book *localdb.LocalPartnumberBook) error { return r.db.Save(book).Error } // SaveBookItems bulk-inserts items for a book snapshot. func (r *PartnumberBookRepository) SaveBookItems(items []localdb.LocalPartnumberBookItem) error { if len(items) == 0 { return nil } return r.db.CreateInBatches(items, 500).Error } // CountBookItems returns the number of items for a given local book ID. func (r *PartnumberBookRepository) CountBookItems(bookID uint) int64 { var count int64 r.db.Model(&localdb.LocalPartnumberBookItem{}).Where("book_id = ?", bookID).Count(&count) return count } func (r *PartnumberBookRepository) CountDistinctLots(bookID uint) int64 { var count int64 r.db.Model(&localdb.LocalPartnumberBookItem{}). Where("book_id = ?", bookID). Distinct("lot_name"). Count(&count) return count } func (r *PartnumberBookRepository) CountPrimaryItems(bookID uint) int64 { var count int64 r.db.Model(&localdb.LocalPartnumberBookItem{}). Where("book_id = ? AND is_primary_pn = ?", bookID, true). Count(&count) return count }