fix: first push implementation fifo v2 to all stockable and useable

This commit is contained in:
Hafizh A. Y
2026-02-27 15:21:55 +07:00
parent a2de21e351
commit e7e065c320
28 changed files with 1469 additions and 164 deletions
@@ -105,6 +105,7 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanForAgeChickD
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = product_warehouses.project_flock_kandang_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Where("flags.name IN (?)", []string{
string(utils.FlagAyam),
string(utils.FlagAyamAfkir),
string(utils.FlagAyamCulling),
string(utils.FlagPullet),
@@ -158,9 +159,12 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanByCategory(c
string(utils.FlagTelurPecah),
string(utils.FlagTelurPutih),
string(utils.FlagTelurRetak),
string(utils.FlagTelurPapacal),
string(utils.FlagTelurJumbo),
})
} else {
db = db.Where("flags.name IN (?)", []string{
string(utils.FlagAyam),
string(utils.FlagDOC),
string(utils.FlagPullet),
string(utils.FlagLayer),
@@ -327,12 +331,12 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
switch filters.MarketingType {
case "ayam":
db = db.Where("flags.name IN (?)", []string{
string(utils.FlagDOC), string(utils.FlagPullet), string(utils.FlagLayer),
string(utils.FlagAyam), string(utils.FlagDOC), string(utils.FlagPullet), string(utils.FlagLayer),
})
case "telur":
db = db.Where("flags.name IN (?)", []string{
string(utils.FlagTelur), string(utils.FlagTelurUtuh), string(utils.FlagTelurPecah),
string(utils.FlagTelurPutih), string(utils.FlagTelurRetak),
string(utils.FlagTelurPutih), string(utils.FlagTelurRetak), string(utils.FlagTelurPapacal), string(utils.FlagTelurJumbo),
})
case "trading":
db = db.Where("flags.name IN (?)", []string{
@@ -549,8 +549,29 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
return fiber.NewError(fiber.StatusInternalServerError, "Delivery product not found")
}
route, err := commonSvc.ResolveFifoStockV2RouteByProductWarehouseIDAndLane(
ctx,
tx,
marketingProduct.ProductWarehouseId,
"MARKETING_OUT",
commonSvc.FifoStockV2LaneUsable,
)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to resolve FIFO v2 marketing route")
}
if route == nil {
return fiber.NewError(
fiber.StatusBadRequest,
fmt.Sprintf("Product warehouse %d tidak mendukung transaksi Marketing pada matrix FIFO v2", marketingProduct.ProductWarehouseId),
)
}
usableKey := fifo.UsableKey(route.LegacyTypeKey)
if route.LegacyTypeKey == "" {
usableKey = fifo.UsableKeyMarketingDelivery
}
result, err := s.FifoSvc.Consume(ctx, commonSvc.StockConsumeRequest{
UsableKey: fifo.UsableKeyMarketingDelivery,
UsableKey: usableKey,
UsableID: deliveryProduct.Id,
ProductWarehouseID: marketingProduct.ProductWarehouseId,
Quantity: requestedQty,
@@ -603,6 +624,27 @@ func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gor
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
}
route, err := commonSvc.ResolveFifoStockV2RouteByProductWarehouseIDAndLane(
ctx,
tx,
marketingProduct.ProductWarehouseId,
"MARKETING_OUT",
commonSvc.FifoStockV2LaneUsable,
)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to resolve FIFO v2 marketing route")
}
if route == nil {
return fiber.NewError(
fiber.StatusBadRequest,
fmt.Sprintf("Product warehouse %d tidak mendukung transaksi Marketing pada matrix FIFO v2", marketingProduct.ProductWarehouseId),
)
}
usableKey := fifo.UsableKey(route.LegacyTypeKey)
if route.LegacyTypeKey == "" {
usableKey = fifo.UsableKeyMarketingDelivery
}
deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx)
currentUsage, err := deliveryProductRepo.GetUsageQty(ctx, deliveryProduct.Id)
if err != nil {
@@ -614,7 +656,7 @@ func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gor
}
if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{
UsableKey: fifo.UsableKeyMarketingDelivery,
UsableKey: usableKey,
UsableID: deliveryProduct.Id,
Tx: tx,
}); err != nil {
@@ -48,6 +48,8 @@ type salesOrdersService struct {
ProjectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository
}
const marketingFunctionCodeOut = "MARKETING_OUT"
func NewSalesOrdersService(marketingRepo repository.MarketingRepository, customerRepo customerRepo.CustomerRepository, productWarehouseRepo productWarehouseRepo.ProductWarehouseRepository, userRepo userRepo.UserRepository, approvalSvc commonSvc.ApprovalService, fifoSvc commonSvc.FifoService, warehouseRepo warehouseRepo.WarehouseRepository,
projectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository, validate *validator.Validate) SalesOrdersService {
return &salesOrdersService{
@@ -74,6 +76,36 @@ func (s salesOrdersService) withRelations(db *gorm.DB) *gorm.DB {
Preload("Products.ProductWarehouse.Warehouse")
}
func (s salesOrdersService) resolveMarketingUsableKey(ctx context.Context, tx *gorm.DB, productWarehouseID uint) (fifo.UsableKey, error) {
if productWarehouseID == 0 {
return "", fiber.NewError(fiber.StatusBadRequest, "Product warehouse tidak valid untuk transaksi Marketing")
}
route, err := commonSvc.ResolveFifoStockV2RouteByProductWarehouseIDAndLane(
ctx,
tx,
productWarehouseID,
marketingFunctionCodeOut,
commonSvc.FifoStockV2LaneUsable,
)
if err != nil {
return "", fiber.NewError(fiber.StatusInternalServerError, "Failed to resolve FIFO v2 marketing route")
}
if route == nil {
return "", fiber.NewError(
fiber.StatusBadRequest,
fmt.Sprintf("Product warehouse %d tidak mendukung transaksi Marketing pada matrix FIFO v2", productWarehouseID),
)
}
usableKey := fifo.UsableKey(strings.TrimSpace(route.LegacyTypeKey))
if usableKey == "" {
usableKey = fifo.UsableKeyMarketingDelivery
}
return usableKey, nil
}
func (s salesOrdersService) getOne(c *fiber.Ctx, id uint) (*entity.Marketing, error) {
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
return nil, err
@@ -376,8 +408,12 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
if qtyDiff < 0 {
return fiber.NewError(fiber.StatusBadRequest, "Cannot decrease quantity after stock has been allocated. Please delete and create new product.")
} else if qtyDiff > 0 {
_, err := s.FifoSvc.Consume(c.Context(), commonSvc.StockConsumeRequest{
UsableKey: fifo.UsableKeyMarketingDelivery,
usableKey, err := s.resolveMarketingUsableKey(c.Context(), dbTransaction, rp.ProductWarehouseId)
if err != nil {
return err
}
_, err = s.FifoSvc.Consume(c.Context(), commonSvc.StockConsumeRequest{
UsableKey: usableKey,
UsableID: deliveryProduct.Id,
ProductWarehouseID: rp.ProductWarehouseId,
Quantity: qtyDiff,
@@ -438,9 +474,13 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
if deliveryProduct.DeliveryDate != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Cannot delete marketing product %d because it has been delivered", old.Id))
}
usableKey, err := s.resolveMarketingUsableKey(c.Context(), dbTransaction, old.ProductWarehouseId)
if err != nil {
return err
}
if err := s.FifoSvc.ReleaseUsage(c.Context(), commonSvc.StockReleaseRequest{
UsableKey: fifo.UsableKeyMarketingDelivery,
UsableKey: usableKey,
UsableID: deliveryProduct.Id,
Tx: dbTransaction,
}); err != nil {
@@ -522,9 +562,22 @@ func (s salesOrdersService) DeleteOne(c *fiber.Ctx, id uint) error {
if len(marketing.Products) > 0 {
deliveryProducts, err := marketingDeliveryProductRepoTx.GetByMarketingId(c.Context(), marketing.Id)
if err == nil && len(deliveryProducts) > 0 {
deliveryUsableKeyByProductID := make(map[uint]fifo.UsableKey, len(marketing.Products))
for _, product := range marketing.Products {
usableKey, err := s.resolveMarketingUsableKey(c.Context(), dbTransaction, product.ProductWarehouseId)
if err != nil {
return err
}
deliveryUsableKeyByProductID[product.Id] = usableKey
}
for _, dp := range deliveryProducts {
usableKey, ok := deliveryUsableKeyByProductID[dp.MarketingProductId]
if !ok {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product untuk delivery %d tidak ditemukan", dp.Id))
}
if err := s.FifoSvc.ReleaseUsage(c.Context(), commonSvc.StockReleaseRequest{
UsableKey: fifo.UsableKeyMarketingDelivery,
UsableKey: usableKey,
UsableID: dp.Id,
Tx: dbTransaction,
}); err != nil {