Files
lti-api/internal/modules/purchases/services/fifo_stock_v2_helper.go
T
2026-04-01 16:14:07 +07:00

149 lines
4.1 KiB
Go

package service
import (
"context"
"errors"
"fmt"
"strings"
"time"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
"gorm.io/gorm"
)
const (
purchaseInFunctionCode = "PURCHASE_IN"
purchaseStockableLane = "STOCKABLE"
purchaseSourceTable = "purchase_items"
)
func reflowPurchaseScope(
ctx context.Context,
fifoStockV2Svc commonSvc.FifoStockV2Service,
tx *gorm.DB,
productWarehouseID uint,
asOf *time.Time,
) error {
if fifoStockV2Svc == nil {
return fmt.Errorf("FIFO v2 service is not available")
}
if productWarehouseID == 0 {
return fmt.Errorf("product warehouse id is required")
}
flagGroupCode, err := resolvePurchaseFlagGroupByProductWarehouse(ctx, tx, productWarehouseID)
if err != nil {
return err
}
if strings.TrimSpace(flagGroupCode) == "" {
return fmt.Errorf("flag group code is not found for product warehouse %d", productWarehouseID)
}
_, err = fifoStockV2Svc.Reflow(ctx, commonSvc.FifoStockV2ReflowRequest{
FlagGroupCode: flagGroupCode,
ProductWarehouseID: productWarehouseID,
AsOf: asOf,
Tx: tx,
})
if err != nil {
return err
}
_, err = fifoStockV2Svc.Recalculate(ctx, commonSvc.FifoStockV2RecalculateRequest{
ProductWarehouseIDs: []uint{productWarehouseID},
FlagGroupCodes: []string{flagGroupCode},
AsOf: asOf,
FixDrift: true,
Tx: tx,
})
return err
}
func resolvePurchaseFlagGroupByProductWarehouse(ctx context.Context, tx *gorm.DB, productWarehouseID uint) (string, error) {
type row struct {
FlagGroupCode string `gorm:"column:flag_group_code"`
}
var selected row
err := tx.WithContext(ctx).
Table("fifo_stock_v2_route_rules rr").
Select("rr.flag_group_code").
Joins("JOIN fifo_stock_v2_flag_groups fg ON fg.code = rr.flag_group_code AND fg.is_active = TRUE").
Where("rr.is_active = TRUE").
Where("rr.lane = ?", purchaseStockableLane).
Where("rr.function_code = ?", purchaseInFunctionCode).
Where("rr.source_table = ?", purchaseSourceTable).
Where(`
EXISTS (
SELECT 1
FROM product_warehouses pw
JOIN flags f ON f.flagable_id = pw.product_id
JOIN fifo_stock_v2_flag_members fm ON fm.flag_name = f.name AND fm.is_active = TRUE
WHERE pw.id = ?
AND f.flagable_type = ?
AND fm.flag_group_code = rr.flag_group_code
)
`, productWarehouseID, entity.FlagableTypeProduct).
Order("rr.id ASC").
Limit(1).
Take(&selected).Error
if err == nil {
return strings.TrimSpace(selected.FlagGroupCode), nil
}
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return "", err
}
type categoryRow struct {
CategoryCode string `gorm:"column:category_code"`
}
var category categoryRow
err = tx.WithContext(ctx).
Table("product_warehouses pw").
Select("pc.code AS category_code").
Joins("JOIN products p ON p.id = pw.product_id").
Joins("JOIN product_categories pc ON pc.id = p.product_category_id").
Where("pw.id = ?", productWarehouseID).
Limit(1).
Take(&category).Error
if err != nil {
return "", err
}
flagGroupCode := utils.LegacyFlagGroupCodeByProductCategoryCode(category.CategoryCode)
if flagGroupCode == "" {
return "", gorm.ErrRecordNotFound
}
var matched int64
err = tx.WithContext(ctx).
Table("fifo_stock_v2_route_rules rr").
Joins("JOIN fifo_stock_v2_flag_groups fg ON fg.code = rr.flag_group_code AND fg.is_active = TRUE").
Where("rr.is_active = TRUE").
Where("rr.lane = ?", purchaseStockableLane).
Where("rr.function_code = ?", purchaseInFunctionCode).
Where("rr.source_table = ?", purchaseSourceTable).
Where("rr.flag_group_code = ?", flagGroupCode).
Count(&matched).Error
if err != nil {
return "", err
}
if matched == 0 {
return "", gorm.ErrRecordNotFound
}
return flagGroupCode, nil
}
func assignEarliestAsOf(m map[uint]time.Time, productWarehouseID uint, asOf time.Time) {
if productWarehouseID == 0 {
return
}
if current, ok := m[productWarehouseID]; !ok || asOf.Before(current) {
m[productWarehouseID] = asOf
}
}