- Migration 029: local_partnumber_books, local_partnumber_book_items, vendor_spec TEXT column on local_configurations - Models: LocalPartnumberBook, LocalPartnumberBookItem, VendorSpec, VendorSpecItem with JSON Valuer/Scanner - Repository: PartnumberBookRepository (GetActiveBook, FindLotByPartnumber, SaveBook/Items, ListBooks, CountBookItems) - Service: VendorSpecResolver 3-step resolution (book → manual suggestion → unresolved) + AggregateLOTs with is_primary_pn qty logic - Sync: PullPartnumberBooks append-only pull from qt_partnumber_books - Handlers: VendorSpecHandler (GET/PUT/resolve/apply), PartnumberBooksHandler - Routes: /api/configs/:uuid/vendor-spec*, /api/partnumber-books, /api/sync/partnumber-books, /partnumber-books page - UI: 3 top-level tabs [Estimate][BOM вендора][Ценообразование]; Excel paste, PN resolution, inline LOT autocomplete, pricing table - Bible: 03-database.md updated, 09-vendor-spec.md added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.3 KiB
Go
67 lines
2.3 KiB
Go
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
|
|
}
|
|
|
|
// 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
|
|
}
|