|
|
|
@@ -11,6 +11,7 @@ import (
|
|
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
"gorm.io/gorm/clause"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type InventoryRepository interface {
|
|
|
|
@@ -278,7 +279,12 @@ func (r *InventoryRepositoryImpl) UpdateReorderLevel(ctx context.Context, id uui
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *InventoryRepositoryImpl) BulkCreate(ctx context.Context, inventoryItems []*entities.Inventory) error {
|
|
|
|
|
return r.db.WithContext(ctx).CreateInBatches(inventoryItems, 100).Error
|
|
|
|
|
return r.db.WithContext(ctx).
|
|
|
|
|
Clauses(clause.OnConflict{
|
|
|
|
|
Columns: []clause.Column{{Name: "outlet_id"}, {Name: "product_id"}},
|
|
|
|
|
DoNothing: true,
|
|
|
|
|
}).
|
|
|
|
|
CreateInBatches(inventoryItems, 100).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *InventoryRepositoryImpl) BulkUpdate(ctx context.Context, inventoryItems []*entities.Inventory) error {
|
|
|
|
@@ -301,21 +307,25 @@ func (r *InventoryRepositoryImpl) BulkAdjustQuantity(ctx context.Context, adjust
|
|
|
|
|
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
for productID, delta := range adjustments {
|
|
|
|
|
var inventory entities.Inventory
|
|
|
|
|
if err := tx.Where("product_id = ? AND outlet_id = ?", productID, outletID).First(&inventory).Error; err != nil {
|
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
// Inventory doesn't exist, create it with initial quantity
|
|
|
|
|
inventory = entities.Inventory{
|
|
|
|
|
ProductID: productID,
|
|
|
|
|
OutletID: outletID,
|
|
|
|
|
Quantity: 0,
|
|
|
|
|
ReorderLevel: 0,
|
|
|
|
|
}
|
|
|
|
|
if err := tx.Create(&inventory).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create inventory record for product %s: %w", productID, err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
|
|
|
Where("product_id = ? AND outlet_id = ?", productID, outletID).
|
|
|
|
|
First(&inventory).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// Use FirstOrCreate to handle race conditions — avoids duplicate key
|
|
|
|
|
// if another transaction already inserted this row concurrently.
|
|
|
|
|
inventory = entities.Inventory{
|
|
|
|
|
ProductID: productID,
|
|
|
|
|
OutletID: outletID,
|
|
|
|
|
Quantity: 0,
|
|
|
|
|
ReorderLevel: 0,
|
|
|
|
|
}
|
|
|
|
|
if err := tx.Where(entities.Inventory{ProductID: productID, OutletID: outletID}).
|
|
|
|
|
FirstOrCreate(&inventory).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create inventory record for product %s: %w", productID, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inventory.UpdateQuantity(delta)
|
|
|
|
|