mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 23:35:43 +00:00
add paired adjustment triger depletion adjustment
This commit is contained in:
+214
-25
@@ -2,12 +2,12 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -15,9 +15,19 @@ import (
|
||||
type AdjustmentStockRepository interface {
|
||||
CreateOne(ctx context.Context, data *entity.AdjustmentStock, modifier func(*gorm.DB) *gorm.DB) error
|
||||
GetByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.AdjustmentStock, error)
|
||||
GetByIDForUpdate(ctx context.Context, id uint) (*entity.AdjustmentStock, error)
|
||||
FindKandangIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (uint, error)
|
||||
FindProductIDByProductWarehouseID(ctx context.Context, productWarehouseID uint) (uint, error)
|
||||
FindRoutesByFunctionCode(ctx context.Context, productID uint, functionCode string) ([]AdjustmentRouteResolution, error)
|
||||
FindOverconsumeRule(ctx context.Context, lane, flagGroupCode, functionCode string) (*bool, error)
|
||||
LoadDownstreamDependencies(ctx context.Context, stockableType string, stockableIDs []uint) ([]AdjustmentDownstreamDependency, error)
|
||||
FindAyamSourceProductWarehouse(ctx context.Context, warehouseID uint, projectFlockKandangID uint) (*entity.ProductWarehouse, error)
|
||||
IsAyamProduct(ctx context.Context, productID uint) (bool, error)
|
||||
CountActiveConsumeAllocationsByUsable(ctx context.Context, usableType string, usableID uint) (int64, error)
|
||||
UpdateTotalQty(ctx context.Context, id uint, qty float64) error
|
||||
UpdatePairedAdjustmentID(ctx context.Context, id uint, pairedID uint) error
|
||||
DeleteStockLogsByAdjustmentID(ctx context.Context, adjustmentID uint) error
|
||||
DeleteAdjustmentByID(ctx context.Context, id uint) error
|
||||
ResyncProjectFlockPopulationUsage(ctx context.Context, projectFlockKandangID uint) error
|
||||
FindHistory(ctx context.Context, filter AdjustmentHistoryFilter, modifier func(*gorm.DB) *gorm.DB) ([]*entity.AdjustmentStock, int64, error)
|
||||
WithTx(tx *gorm.DB) AdjustmentStockRepository
|
||||
DB() *gorm.DB
|
||||
@@ -44,6 +54,13 @@ type AdjustmentHistoryFilter struct {
|
||||
Limit int
|
||||
}
|
||||
|
||||
type AdjustmentDownstreamDependency struct {
|
||||
UsableType string `gorm:"column:usable_type"`
|
||||
UsableID uint64 `gorm:"column:usable_id"`
|
||||
FunctionCode string `gorm:"column:function_code"`
|
||||
FlagGroupCode string `gorm:"column:flag_group_code"`
|
||||
}
|
||||
|
||||
type adjustmentStockRepositoryImpl struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
@@ -73,6 +90,17 @@ func (r *adjustmentStockRepositoryImpl) GetByID(ctx context.Context, id uint, mo
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) GetByIDForUpdate(ctx context.Context, id uint) (*entity.AdjustmentStock, error) {
|
||||
var record entity.AdjustmentStock
|
||||
if err := r.db.WithContext(ctx).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("id = ?", id).
|
||||
Take(&record).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindKandangIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (uint, error) {
|
||||
type pfkRow struct {
|
||||
KandangID uint `gorm:"column:kandang_id"`
|
||||
@@ -91,6 +119,21 @@ func (r *adjustmentStockRepositoryImpl) FindKandangIDByProjectFlockKandangID(ctx
|
||||
return pfk.KandangID, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindProductIDByProductWarehouseID(ctx context.Context, productWarehouseID uint) (uint, error) {
|
||||
type productRow struct {
|
||||
ProductID uint `gorm:"column:product_id"`
|
||||
}
|
||||
var row productRow
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table("product_warehouses").
|
||||
Select("product_id").
|
||||
Where("id = ?", productWarehouseID).
|
||||
Take(&row).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return row.ProductID, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindRoutesByFunctionCode(
|
||||
ctx context.Context,
|
||||
productID uint,
|
||||
@@ -122,37 +165,183 @@ func (r *adjustmentStockRepositoryImpl) FindRoutesByFunctionCode(
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindOverconsumeRule(
|
||||
func (r *adjustmentStockRepositoryImpl) LoadDownstreamDependencies(
|
||||
ctx context.Context,
|
||||
lane string,
|
||||
flagGroupCode string,
|
||||
functionCode string,
|
||||
) (*bool, error) {
|
||||
type selectedRow struct {
|
||||
AllowOverconsume bool `gorm:"column:allow_overconsume"`
|
||||
stockableType string,
|
||||
stockableIDs []uint,
|
||||
) ([]AdjustmentDownstreamDependency, error) {
|
||||
if strings.TrimSpace(stockableType) == "" || len(stockableIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var selected selectedRow
|
||||
var rows []AdjustmentDownstreamDependency
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("fifo_stock_v2_overconsume_rules").
|
||||
Select("allow_overconsume").
|
||||
Where("is_active = TRUE").
|
||||
Where("lane = ?", lane).
|
||||
Where("(flag_group_code IS NULL OR flag_group_code = ?)", flagGroupCode).
|
||||
Where("(function_code IS NULL OR function_code = ?)", functionCode).
|
||||
Order("CASE WHEN flag_group_code IS NULL THEN 1 ELSE 0 END ASC").
|
||||
Order("CASE WHEN function_code IS NULL THEN 1 ELSE 0 END ASC").
|
||||
Order("priority ASC, id ASC").
|
||||
Limit(1).
|
||||
Take(&selected).Error
|
||||
Table("stock_allocations").
|
||||
Select("usable_type, usable_id, COALESCE(function_code,'') AS function_code, COALESCE(flag_group_code,'') AS flag_group_code").
|
||||
Where("stockable_type = ?", strings.ToUpper(strings.TrimSpace(stockableType))).
|
||||
Where("stockable_id IN ?", stockableIDs).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
||||
Where("deleted_at IS NULL").
|
||||
Where(
|
||||
"(usable_type <> ? OR EXISTS (SELECT 1 FROM project_chickins pc WHERE pc.id = stock_allocations.usable_id AND pc.deleted_at IS NULL))",
|
||||
"PROJECT_CHICKIN",
|
||||
).
|
||||
Group("usable_type, usable_id, function_code, flag_group_code").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &selected.AllowOverconsume, nil
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindAyamSourceProductWarehouse(
|
||||
ctx context.Context,
|
||||
warehouseID uint,
|
||||
projectFlockKandangID uint,
|
||||
) (*entity.ProductWarehouse, error) {
|
||||
var sourcePW entity.ProductWarehouse
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&entity.ProductWarehouse{}).
|
||||
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
Where(`
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM flags f
|
||||
JOIN fifo_stock_v2_flag_members fm ON fm.flag_name = f.name AND fm.is_active = TRUE
|
||||
WHERE f.flagable_type = ?
|
||||
AND f.flagable_id = product_warehouses.product_id
|
||||
AND fm.flag_group_code = ?
|
||||
)
|
||||
`, entity.FlagableTypeProduct, "AYAM").
|
||||
Order(gorm.Expr("CASE WHEN warehouse_id = ? THEN 0 ELSE 1 END ASC", warehouseID)).
|
||||
Order("id ASC").
|
||||
Take(&sourcePW).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sourcePW, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) IsAyamProduct(ctx context.Context, productID uint) (bool, error) {
|
||||
if productID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table("flags f").
|
||||
Joins("JOIN fifo_stock_v2_flag_members fm ON fm.flag_name = f.name AND fm.flag_group_code = ? AND fm.is_active = TRUE", "AYAM").
|
||||
Where("f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("f.flagable_id = ?", productID).
|
||||
Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) CountActiveConsumeAllocationsByUsable(
|
||||
ctx context.Context,
|
||||
usableType string,
|
||||
usableID uint,
|
||||
) (int64, error) {
|
||||
if strings.TrimSpace(usableType) == "" || usableID == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("stock_allocations").
|
||||
Where("usable_type = ?", strings.ToUpper(strings.TrimSpace(usableType))).
|
||||
Where("usable_id = ?", usableID).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
||||
Where("deleted_at IS NULL").
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) UpdateTotalQty(ctx context.Context, id uint, qty float64) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entity.AdjustmentStock{}).
|
||||
Where("id = ?", id).
|
||||
Update("total_qty", qty).Error
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) UpdatePairedAdjustmentID(ctx context.Context, id uint, pairedID uint) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entity.AdjustmentStock{}).
|
||||
Where("id = ?", id).
|
||||
Update("paired_adjustment_id", pairedID).Error
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) DeleteStockLogsByAdjustmentID(ctx context.Context, adjustmentID uint) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Where("loggable_type = ? AND loggable_id = ?", string(utils.StockLogTypeAdjustment), adjustmentID).
|
||||
Delete(&entity.StockLog{}).Error
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) DeleteAdjustmentByID(ctx context.Context, id uint) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Where("id = ?", id).
|
||||
Delete(&entity.AdjustmentStock{}).Error
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) ResyncProjectFlockPopulationUsage(ctx context.Context, projectFlockKandangID uint) error {
|
||||
if projectFlockKandangID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
idsSubquery := `
|
||||
SELECT pfp.id
|
||||
FROM project_flock_populations pfp
|
||||
JOIN project_chickins pc ON pc.id = pfp.project_chickin_id
|
||||
WHERE pc.project_flock_kandang_id = ?
|
||||
`
|
||||
|
||||
updateWithAlloc := `
|
||||
UPDATE project_flock_populations p
|
||||
SET total_used_qty = COALESCE(a.used, 0)
|
||||
FROM (
|
||||
SELECT stockable_id, SUM(qty) AS used
|
||||
FROM stock_allocations
|
||||
WHERE stockable_type = 'PROJECT_FLOCK_POPULATION'
|
||||
AND status = 'ACTIVE'
|
||||
AND allocation_purpose = 'CONSUME'
|
||||
GROUP BY stockable_id
|
||||
) a
|
||||
WHERE p.id = a.stockable_id
|
||||
AND p.id IN (` + idsSubquery + `)
|
||||
`
|
||||
|
||||
resetMissing := `
|
||||
UPDATE project_flock_populations p
|
||||
SET total_used_qty = 0
|
||||
WHERE p.id IN (` + idsSubquery + `)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM stock_allocations sa
|
||||
WHERE sa.stockable_type = 'PROJECT_FLOCK_POPULATION'
|
||||
AND sa.status = 'ACTIVE'
|
||||
AND sa.allocation_purpose = 'CONSUME'
|
||||
AND sa.stockable_id = p.id
|
||||
)
|
||||
`
|
||||
|
||||
db := r.db.WithContext(ctx)
|
||||
if err := db.Exec(updateWithAlloc, projectFlockKandangID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec(resetMissing, projectFlockKandangID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *adjustmentStockRepositoryImpl) FindHistory(
|
||||
|
||||
Reference in New Issue
Block a user