Feat[BE]: refactor stock log handling and introduce new log types for adjustments and transfers

This commit is contained in:
aguhh18
2025-12-26 09:19:39 +07:00
parent 3e575d96a7
commit 12e5706318
8 changed files with 83 additions and 36 deletions
@@ -42,7 +42,6 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
fifoService := commonSvc.NewFifoService(db, stockAllocationRepo, productWarehouseRepo, utils.Log)
userRepo := rUser.NewUserRepository(db)
// Register PROJECT_CHICKIN as usable
if err := fifoService.RegisterUsable(fifo.UsableConfig{
Key: fifo.UsableKeyProjectChickin,
Table: "project_chickins",
@@ -16,6 +16,7 @@ import (
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/validations"
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
@@ -48,6 +49,7 @@ type chickinService struct {
ProjectflockPopulationRepo rProjectFlock.ProjectFlockPopulationRepository
ProjectChickinDetailRepo repository.ProjectChickinDetailRepository
FifoSvc commonSvc.FifoService
StockLogRepo rStockLogs.StockLogRepository
}
func NewChickinService(repo repository.ProjectChickinRepository, kandangRepo KandangRepo.KandangRepository, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, projectFlockRepo rProjectFlock.ProjectflockRepository, projectflockkandangRepo rProjectFlock.ProjectFlockKandangRepository, projectflockpopulationRepo rProjectFlock.ProjectFlockPopulationRepository, projectChickinDetailRepo repository.ProjectChickinDetailRepository, validate *validator.Validate, fifoSvc commonSvc.FifoService) ChickinService {
@@ -63,6 +65,7 @@ func NewChickinService(repo repository.ProjectChickinRepository, kandangRepo Kan
ProjectflockPopulationRepo: projectflockpopulationRepo,
ProjectChickinDetailRepo: projectChickinDetailRepo,
FifoSvc: fifoSvc,
StockLogRepo: rStockLogs.NewStockLogRepository(repo.DB()),
}
}
@@ -135,7 +138,7 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
return nil, err
}
newChikins := make([]*entity.ProjectChickin, 0)
chickinQtyMap := make(map[uint]float64) // Store desired qty for each chickin index
chickinQtyMap := make(map[uint]float64)
for idx, chickinReq := range req.ChickinRequests {
@@ -197,13 +200,11 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
for idx, chickin := range newChikins {
desiredQty := chickinQtyMap[uint(idx)]
if err := s.ConsumeChickinStocks(c.Context(), dbTransaction, chickin, desiredQty); err != nil {
if err := s.ConsumeChickinStocks(c.Context(), dbTransaction, chickin, desiredQty, actorID); err != nil {
return err
}
}
// Note: FIFO Consume already adjusts product warehouse quantities, no need to adjust again
latest, err := approvalSvcTx.LatestByTarget(c.Context(), utils.ApprovalWorkflowChickin, projectFlockKandang.Id, nil)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get latest approval")
@@ -306,8 +307,13 @@ func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
return err
}
actorID, err := m.ActorIDFromContext(c)
if err != nil {
return err
}
if chickin.UsageQty > 0 {
if err := s.ReleaseChickinStocks(c.Context(), s.Repository.DB(), chickin); err != nil {
if err := s.ReleaseChickinStocks(c.Context(), s.Repository.DB(), chickin, actorID); err != nil {
return err
}
@@ -461,7 +467,7 @@ func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entit
for _, chickin := range chickins {
if err := s.ReleaseChickinStocks(c.Context(), dbTransaction, &chickin); err != nil {
if err := s.ReleaseChickinStocks(c.Context(), dbTransaction, &chickin, actorID); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to release stock for rejected chickin %d: %v", chickin.Id, err))
}
@@ -591,7 +597,7 @@ func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []enti
return nil
}
func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin, desiredQty float64) error {
func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin, desiredQty float64, actorID uint) error {
if chickin == nil || s.FifoSvc == nil {
return nil
}
@@ -622,14 +628,35 @@ func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB,
return err
}
if result.UsageQuantity > 0 {
decreaseLog := &entity.StockLog{
Decrease: result.UsageQuantity,
LoggableType: string(utils.StockLogTypeChikin),
LoggableId: chickin.Id,
ProductWarehouseId: chickin.ProductWarehouseId,
CreatedBy: actorID,
Notes: fmt.Sprintf("Chickin #%d", chickin.Id),
}
if err := s.StockLogRepo.CreateOne(ctx, decreaseLog, nil); err != nil {
s.Log.Errorf("Failed to create stock log for chickin %d: %+v", chickin.Id, err)
}
}
return nil
}
func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin) error {
func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB, chickin *entity.ProjectChickin, actorID uint) error {
if chickin == nil || s.FifoSvc == nil {
return nil
}
var currentUsage float64
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Select("usage_qty").Scan(&currentUsage).Error; err != nil {
s.Log.Warnf("Failed to get current usage for chickin %d: %+v", chickin.Id, err)
currentUsage = 0
}
if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{
UsableKey: chickinUsableKey,
UsableID: chickin.Id,
@@ -646,6 +673,22 @@ func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB,
return err
}
// Create stock log for the restoration
if currentUsage > 0 {
increaseLog := &entity.StockLog{
Increase: currentUsage,
LoggableType: string(utils.StockLogTypeChikin),
LoggableId: chickin.Id,
ProductWarehouseId: chickin.ProductWarehouseId,
CreatedBy: actorID,
Notes: fmt.Sprintf("Chickin #%d - Stock released", chickin.Id),
}
if err := s.StockLogRepo.CreateOne(ctx, increaseLog, nil); err != nil {
s.Log.Errorf("Failed to create stock log for chickin %d: %+v", chickin.Id, err)
// Don't return error here, stock already released
}
}
return nil
}