mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-05 03:12:34 -06:00
162 lines
4.8 KiB
Go
162 lines
4.8 KiB
Go
package catalog
|
|
|
|
import (
|
|
"rmser/internal/domain/catalog"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
type pgRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewRepository(db *gorm.DB) catalog.Repository {
|
|
return &pgRepository{db: db}
|
|
}
|
|
|
|
func (r *pgRepository) SaveMeasureUnits(units []catalog.MeasureUnit) error {
|
|
if len(units) == 0 {
|
|
return nil
|
|
}
|
|
return r.db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
UpdateAll: true,
|
|
}).CreateInBatches(units, 100).Error
|
|
}
|
|
|
|
func (r *pgRepository) SaveProducts(products []catalog.Product) error {
|
|
sorted := sortProductsByHierarchy(products)
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// 1. Сохраняем продукты (без контейнеров, чтобы ускорить и не дублировать)
|
|
if err := tx.Omit("Containers").Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
UpdateAll: true,
|
|
}).CreateInBatches(sorted, 100).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// 2. Собираем все контейнеры в один слайс
|
|
var allContainers []catalog.ProductContainer
|
|
for _, p := range products {
|
|
allContainers = append(allContainers, p.Containers...)
|
|
}
|
|
|
|
// 3. Сохраняем контейнеры
|
|
if len(allContainers) > 0 {
|
|
if err := tx.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
UpdateAll: true,
|
|
}).CreateInBatches(allContainers, 100).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (r *pgRepository) GetAll() ([]catalog.Product, error) {
|
|
var products []catalog.Product
|
|
err := r.db.Preload("MainUnit").Find(&products).Error
|
|
return products, err
|
|
}
|
|
|
|
// Вспомогательная функция сортировки (оставляем как была)
|
|
func sortProductsByHierarchy(products []catalog.Product) []catalog.Product {
|
|
if len(products) == 0 {
|
|
return products
|
|
}
|
|
childrenMap := make(map[uuid.UUID][]catalog.Product)
|
|
var roots []catalog.Product
|
|
allIDs := make(map[uuid.UUID]struct{}, len(products))
|
|
|
|
for _, p := range products {
|
|
allIDs[p.ID] = struct{}{}
|
|
}
|
|
|
|
for _, p := range products {
|
|
if p.ParentID == nil {
|
|
roots = append(roots, p)
|
|
} else {
|
|
if _, exists := allIDs[*p.ParentID]; exists {
|
|
childrenMap[*p.ParentID] = append(childrenMap[*p.ParentID], p)
|
|
} else {
|
|
roots = append(roots, p)
|
|
}
|
|
}
|
|
}
|
|
|
|
result := make([]catalog.Product, 0, len(products))
|
|
queue := roots
|
|
for len(queue) > 0 {
|
|
current := queue[0]
|
|
queue = queue[1:]
|
|
result = append(result, current)
|
|
if children, ok := childrenMap[current.ID]; ok {
|
|
queue = append(queue, children...)
|
|
delete(childrenMap, current.ID)
|
|
}
|
|
}
|
|
for _, remaining := range childrenMap {
|
|
result = append(result, remaining...)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetActiveGoods возвращает только активные товары c подгруженной единицей измерения
|
|
// GetActiveGoods оптимизирован: подгружаем Units и Containers
|
|
func (r *pgRepository) GetActiveGoods() ([]catalog.Product, error) {
|
|
var products []catalog.Product
|
|
err := r.db.
|
|
Preload("MainUnit").
|
|
Preload("Containers"). // <-- Подгружаем фасовки
|
|
Where("is_deleted = ? AND type IN ?", false, []string{"GOODS"}).
|
|
Order("name ASC").
|
|
Find(&products).Error
|
|
return products, err
|
|
}
|
|
|
|
func (r *pgRepository) SaveStores(stores []catalog.Store) error {
|
|
if len(stores) == 0 {
|
|
return nil
|
|
}
|
|
return r.db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
UpdateAll: true,
|
|
}).CreateInBatches(stores, 100).Error
|
|
}
|
|
|
|
func (r *pgRepository) GetActiveStores() ([]catalog.Store, error) {
|
|
var stores []catalog.Store
|
|
err := r.db.Where("is_deleted = ?", false).Order("name ASC").Find(&stores).Error
|
|
return stores, err
|
|
}
|
|
|
|
// SaveContainer сохраняет или обновляет одну фасовку
|
|
func (r *pgRepository) SaveContainer(container catalog.ProductContainer) error {
|
|
return r.db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
UpdateAll: true,
|
|
}).Create(&container).Error
|
|
}
|
|
|
|
// Search ищет товары по названию, артикулу или коду (ILIKE)
|
|
func (r *pgRepository) Search(query string) ([]catalog.Product, error) {
|
|
var products []catalog.Product
|
|
|
|
// Оборачиваем в проценты для поиска подстроки
|
|
q := "%" + query + "%"
|
|
|
|
err := r.db.
|
|
Preload("MainUnit").
|
|
Preload("Containers"). // Обязательно грузим фасовки, они нужны для выбора
|
|
Where("is_deleted = ? AND type = ?", false, "GOODS").
|
|
Where("name ILIKE ? OR code ILIKE ? OR num ILIKE ?", q, q, q).
|
|
Order("name ASC").
|
|
Limit(20). // Ограничиваем выдачу, чтобы не перегружать фронт
|
|
Find(&products).Error
|
|
|
|
return products, err
|
|
}
|