mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 15:25:43 +00:00
[FIX/BE-US] adjustment recording and purchase stock log
This commit is contained in:
@@ -83,7 +83,7 @@ func (r *ConstantRepositoryImpl) GetConstants() map[string]interface{} {
|
|||||||
"KANDANG",
|
"KANDANG",
|
||||||
},
|
},
|
||||||
"stock_log": map[string][]string{
|
"stock_log": map[string][]string{
|
||||||
"log_types": []string{"TRANSFER", "ADJUSTMENT"},
|
"log_types": []string{"TRANSFER", "ADJUSTMENT", "MARKETING", "CHICKIN", "PURCHASE", "RECORDING"},
|
||||||
"transaction_types": []string{"INCREASE", "DECREASE"},
|
"transaction_types": []string{"INCREASE", "DECREASE"},
|
||||||
},
|
},
|
||||||
"supplier_categories": []string{
|
"supplier_categories": []string{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
rRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
rRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||||
sRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
sRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||||
|
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"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
projectFlockPopulationRepo := rProjectFlock.NewProjectFlockPopulationRepository(db)
|
projectFlockPopulationRepo := rProjectFlock.NewProjectFlockPopulationRepository(db)
|
||||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||||
stockAllocationRepo := commonRepo.NewStockAllocationRepository(db)
|
stockAllocationRepo := commonRepo.NewStockAllocationRepository(db)
|
||||||
|
stockLogRepo := rStockLogs.NewStockLogRepository(db)
|
||||||
productionStandardRepo := rProductionStandard.NewProductionStandardRepository(db)
|
productionStandardRepo := rProductionStandard.NewProductionStandardRepository(db)
|
||||||
productionStandardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db)
|
productionStandardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db)
|
||||||
standardGrowthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db)
|
standardGrowthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db)
|
||||||
@@ -113,6 +115,7 @@ func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
approvalRepo,
|
approvalRepo,
|
||||||
approvalService,
|
approvalService,
|
||||||
fifoService,
|
fifoService,
|
||||||
|
stockLogRepo,
|
||||||
productionStandardService,
|
productionStandardService,
|
||||||
validate,
|
validate,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
sProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/services"
|
sProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/services"
|
||||||
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||||
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||||
@@ -39,8 +40,8 @@ type RecordingService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RecordingFIFOIntegrationService interface {
|
type RecordingFIFOIntegrationService interface {
|
||||||
ConsumeRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error
|
ConsumeRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock, note string, actorID uint) error
|
||||||
ReleaseRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error
|
ReleaseRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock, note string, actorID uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordingStockUsableKey = fifo.UsableKeyRecordingStock
|
var recordingStockUsableKey = fifo.UsableKeyRecordingStock
|
||||||
@@ -57,6 +58,7 @@ type recordingService struct {
|
|||||||
ApprovalSvc commonSvc.ApprovalService
|
ApprovalSvc commonSvc.ApprovalService
|
||||||
ProductionStandardSvc sProductionStandard.ProductionStandardService
|
ProductionStandardSvc sProductionStandard.ProductionStandardService
|
||||||
FifoSvc commonSvc.FifoService
|
FifoSvc commonSvc.FifoService
|
||||||
|
StockLogRepo rStockLogs.StockLogRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecordingService(
|
func NewRecordingService(
|
||||||
@@ -67,6 +69,7 @@ func NewRecordingService(
|
|||||||
approvalRepo commonRepo.ApprovalRepository,
|
approvalRepo commonRepo.ApprovalRepository,
|
||||||
approvalSvc commonSvc.ApprovalService,
|
approvalSvc commonSvc.ApprovalService,
|
||||||
fifoSvc commonSvc.FifoService,
|
fifoSvc commonSvc.FifoService,
|
||||||
|
stockLogRepo rStockLogs.StockLogRepository,
|
||||||
productionStandardSvc sProductionStandard.ProductionStandardService,
|
productionStandardSvc sProductionStandard.ProductionStandardService,
|
||||||
validate *validator.Validate,
|
validate *validator.Validate,
|
||||||
) RecordingService {
|
) RecordingService {
|
||||||
@@ -81,6 +84,7 @@ func NewRecordingService(
|
|||||||
ApprovalSvc: approvalSvc,
|
ApprovalSvc: approvalSvc,
|
||||||
ProductionStandardSvc: productionStandardSvc,
|
ProductionStandardSvc: productionStandardSvc,
|
||||||
FifoSvc: fifoSvc,
|
FifoSvc: fifoSvc,
|
||||||
|
StockLogRepo: stockLogRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +92,14 @@ func NewRecordingFIFOIntegrationService(
|
|||||||
repo repository.RecordingRepository,
|
repo repository.RecordingRepository,
|
||||||
productWarehouseRepo rProductWarehouse.ProductWarehouseRepository,
|
productWarehouseRepo rProductWarehouse.ProductWarehouseRepository,
|
||||||
fifoSvc commonSvc.FifoService,
|
fifoSvc commonSvc.FifoService,
|
||||||
|
stockLogRepo rStockLogs.StockLogRepository,
|
||||||
) RecordingFIFOIntegrationService {
|
) RecordingFIFOIntegrationService {
|
||||||
return &recordingService{
|
return &recordingService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Repository: repo,
|
Repository: repo,
|
||||||
ProductWarehouseRepo: productWarehouseRepo,
|
ProductWarehouseRepo: productWarehouseRepo,
|
||||||
FifoSvc: fifoSvc,
|
FifoSvc: fifoSvc,
|
||||||
|
StockLogRepo: stockLogRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +280,8 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyStockDesiredQuantities(mappedStocks, stockDesired, s.FifoSvc != nil)
|
applyStockDesiredQuantities(mappedStocks, stockDesired, s.FifoSvc != nil)
|
||||||
if err := s.consumeRecordingStocks(ctx, tx, mappedStocks); err != nil {
|
note := fmt.Sprintf("Recording-Create#%d", createdRecording.Id)
|
||||||
|
if err := s.consumeRecordingStocks(ctx, tx, mappedStocks, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +300,8 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions); err != nil {
|
note := fmt.Sprintf("Recording-Create#%d", createdRecording.Id)
|
||||||
|
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,7 +312,8 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.replenishRecordingEggs(ctx, tx, mappedEggs); err != nil {
|
note := fmt.Sprintf("Recording-Create#%d", createdRecording.Id)
|
||||||
|
if err := s.replenishRecordingEggs(ctx, tx, mappedEggs, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,6 +355,10 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
|
actorID, err := m.ActorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var recordingEntity *entity.Recording
|
var recordingEntity *entity.Recording
|
||||||
var updatedRecording *entity.Recording
|
var updatedRecording *entity.Recording
|
||||||
@@ -431,14 +444,16 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasStockChanges {
|
if hasStockChanges {
|
||||||
if err := s.syncRecordingStocks(ctx, tx, recordingEntity.Id, existingStocks, req.Stocks); err != nil {
|
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||||
|
if err := s.syncRecordingStocks(ctx, tx, recordingEntity.Id, existingStocks, req.Stocks, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasDepletionChanges {
|
if hasDepletionChanges {
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.releaseRecordingDepletions(ctx, tx, existingDepletions); err != nil {
|
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||||
|
if err := s.releaseRecordingDepletions(ctx, tx, existingDepletions, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -464,7 +479,8 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions); err != nil {
|
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||||
|
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,6 +496,28 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
|||||||
if err := ensureRecordingEggsUnused(existingEggs); err != nil {
|
if err := ensureRecordingEggsUnused(existingEggs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if s.StockLogRepo != nil {
|
||||||
|
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||||
|
logs := make([]*entity.StockLog, 0, len(existingEggs))
|
||||||
|
for _, egg := range existingEggs {
|
||||||
|
if egg.ProductWarehouseId == 0 || egg.Qty <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logs = append(logs, &entity.StockLog{
|
||||||
|
ProductWarehouseId: egg.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Decrease: float64(egg.Qty),
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: recordingEntity.Id,
|
||||||
|
Notes: note,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(logs) > 0 {
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateMany(ctx, logs, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, nil)); err != nil {
|
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, nil)); err != nil {
|
||||||
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
|
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
|
||||||
return err
|
return err
|
||||||
@@ -498,7 +536,8 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.replenishRecordingEggs(ctx, tx, mappedEggs); err != nil {
|
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||||
|
if err := s.replenishRecordingEggs(ctx, tx, mappedEggs, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -675,7 +714,7 @@ func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
if err := s.releaseRecordingDepletions(ctx, tx, oldDepletions); err != nil {
|
if err := s.releaseRecordingDepletions(ctx, tx, oldDepletions, "", 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -697,7 +736,7 @@ func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.releaseRecordingStocks(ctx, tx, oldStocks); err != nil {
|
if err := s.releaseRecordingStocks(ctx, tx, oldStocks, "", 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,10 +795,19 @@ func (s *recordingService) ensureProductWarehousesExist(c *fiber.Ctx, stocks []v
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error {
|
func (s *recordingService) consumeRecordingStocks(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
stocks []entity.RecordingStock,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
if len(stocks) == 0 || s.FifoSvc == nil {
|
if len(stocks) == 0 || s.FifoSvc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(note) != "" && s.StockLogRepo == nil {
|
||||||
|
return errors.New("stock log repository is not available")
|
||||||
|
}
|
||||||
|
|
||||||
for _, stock := range stocks {
|
for _, stock := range stocks {
|
||||||
if stock.Id == 0 {
|
if stock.Id == 0 {
|
||||||
@@ -792,15 +840,42 @@ func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm.
|
|||||||
if err := s.Repository.UpdateStockUsage(tx, stock.Id, result.UsageQuantity, result.PendingQuantity); err != nil {
|
if err := s.Repository.UpdateStockUsage(tx, stock.Id, result.UsageQuantity, result.PendingQuantity); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDecrease := result.UsageQuantity
|
||||||
|
if result.PendingQuantity > 0 {
|
||||||
|
logDecrease += result.PendingQuantity
|
||||||
|
}
|
||||||
|
if logDecrease > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: stock.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Decrease: logDecrease,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: stock.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) consumeRecordingDepletions(ctx context.Context, tx *gorm.DB, depletions []entity.RecordingDepletion) error {
|
func (s *recordingService) consumeRecordingDepletions(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
depletions []entity.RecordingDepletion,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
if len(depletions) == 0 || s.FifoSvc == nil {
|
if len(depletions) == 0 || s.FifoSvc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(note) != "" && s.StockLogRepo == nil {
|
||||||
|
return errors.New("stock log repository is not available")
|
||||||
|
}
|
||||||
|
|
||||||
for _, depletion := range depletions {
|
for _, depletion := range depletions {
|
||||||
if depletion.Id == 0 {
|
if depletion.Id == 0 {
|
||||||
@@ -832,19 +907,67 @@ func (s *recordingService) consumeRecordingDepletions(ctx context.Context, tx *g
|
|||||||
if err := s.Repository.UpdateDepletionPending(tx, depletion.Id, result.PendingQuantity); err != nil {
|
if err := s.Repository.UpdateDepletionPending(tx, depletion.Id, result.PendingQuantity); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDecrease := result.UsageQuantity
|
||||||
|
if result.PendingQuantity > 0 {
|
||||||
|
logDecrease += result.PendingQuantity
|
||||||
|
}
|
||||||
|
if logDecrease > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: sourceWarehouseID,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Decrease: logDecrease,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: depletion.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destDelta := depletion.Qty + depletion.PendingQty
|
||||||
|
if depletion.ProductWarehouseId != 0 && destDelta > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: depletion.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Increase: destDelta,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: depletion.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) ConsumeRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error {
|
func (s *recordingService) ConsumeRecordingStocks(
|
||||||
return s.consumeRecordingStocks(ctx, tx, stocks)
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
stocks []entity.RecordingStock,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
|
return s.consumeRecordingStocks(ctx, tx, stocks, note, actorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) releaseRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error {
|
func (s *recordingService) releaseRecordingStocks(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
stocks []entity.RecordingStock,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
if len(stocks) == 0 || s.FifoSvc == nil {
|
if len(stocks) == 0 || s.FifoSvc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(note) != "" && s.StockLogRepo == nil {
|
||||||
|
return errors.New("stock log repository is not available")
|
||||||
|
}
|
||||||
|
|
||||||
for _, stock := range stocks {
|
for _, stock := range stocks {
|
||||||
if stock.Id == 0 {
|
if stock.Id == 0 {
|
||||||
@@ -863,15 +986,38 @@ func (s *recordingService) releaseRecordingStocks(ctx context.Context, tx *gorm.
|
|||||||
if err := s.Repository.UpdateStockUsage(tx, stock.Id, 0, 0); err != nil {
|
if err := s.Repository.UpdateStockUsage(tx, stock.Id, 0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stock.UsageQty != nil && *stock.UsageQty > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: stock.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Increase: *stock.UsageQty,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: stock.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) releaseRecordingDepletions(ctx context.Context, tx *gorm.DB, depletions []entity.RecordingDepletion) error {
|
func (s *recordingService) releaseRecordingDepletions(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
depletions []entity.RecordingDepletion,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
if len(depletions) == 0 || s.FifoSvc == nil {
|
if len(depletions) == 0 || s.FifoSvc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(note) != "" && s.StockLogRepo == nil {
|
||||||
|
return errors.New("stock log repository is not available")
|
||||||
|
}
|
||||||
|
|
||||||
for _, depletion := range depletions {
|
for _, depletion := range depletions {
|
||||||
if depletion.Id == 0 {
|
if depletion.Id == 0 {
|
||||||
@@ -898,13 +1044,52 @@ func (s *recordingService) releaseRecordingDepletions(ctx context.Context, tx *g
|
|||||||
if err := s.Repository.UpdateDepletionPending(tx, depletion.Id, 0); err != nil {
|
if err := s.Repository.UpdateDepletionPending(tx, depletion.Id, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logIncrease := depletion.Qty
|
||||||
|
if depletion.PendingQty > 0 {
|
||||||
|
logIncrease += depletion.PendingQty
|
||||||
|
}
|
||||||
|
if logIncrease > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: sourceWarehouseID,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Increase: logIncrease,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: depletion.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destDelta := depletion.Qty + depletion.PendingQty
|
||||||
|
if depletion.ProductWarehouseId != 0 && destDelta > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: depletion.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Decrease: destDelta,
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: depletion.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) ReleaseRecordingStocks(ctx context.Context, tx *gorm.DB, stocks []entity.RecordingStock) error {
|
func (s *recordingService) ReleaseRecordingStocks(
|
||||||
return s.releaseRecordingStocks(ctx, tx, stocks)
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
stocks []entity.RecordingStock,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
|
return s.releaseRecordingStocks(ctx, tx, stocks, note, actorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) resolvePopulationWarehouseID(ctx context.Context, projectFlockKandangID uint) (uint, error) {
|
func (s *recordingService) resolvePopulationWarehouseID(ctx context.Context, projectFlockKandangID uint) (uint, error) {
|
||||||
@@ -963,27 +1148,48 @@ func (s *recordingService) adjustProductWarehouseQuantities(ctx context.Context,
|
|||||||
return s.ProductWarehouseRepo.AdjustQuantities(ctx, deltas, func(*gorm.DB) *gorm.DB { return tx })
|
return s.ProductWarehouseRepo.AdjustQuantities(ctx, deltas, func(*gorm.DB) *gorm.DB { return tx })
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *recordingService) replenishRecordingEggs(ctx context.Context, tx *gorm.DB, eggs []entity.RecordingEgg) error {
|
func (s *recordingService) replenishRecordingEggs(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *gorm.DB,
|
||||||
|
eggs []entity.RecordingEgg,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
|
) error {
|
||||||
if len(eggs) == 0 || s.FifoSvc == nil {
|
if len(eggs) == 0 || s.FifoSvc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(note) != "" && s.StockLogRepo == nil {
|
||||||
|
return errors.New("stock log repository is not available")
|
||||||
|
}
|
||||||
|
|
||||||
for _, egg := range eggs {
|
for _, egg := range eggs {
|
||||||
if egg.Id == 0 || egg.ProductWarehouseId == 0 || egg.Qty <= 0 {
|
if egg.Id == 0 || egg.ProductWarehouseId == 0 || egg.Qty <= 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
note := fmt.Sprintf("Recording egg #%d", egg.Id)
|
|
||||||
if _, err := s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
if _, err := s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||||
StockableKey: fifo.StockableKeyRecordingEgg,
|
StockableKey: fifo.StockableKeyRecordingEgg,
|
||||||
StockableID: egg.Id,
|
StockableID: egg.Id,
|
||||||
ProductWarehouseID: egg.ProductWarehouseId,
|
ProductWarehouseID: egg.ProductWarehouseId,
|
||||||
Quantity: float64(egg.Qty),
|
Quantity: float64(egg.Qty),
|
||||||
Note: ¬e,
|
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
s.Log.Errorf("Failed to replenish FIFO stock for recording egg %d: %+v", egg.Id, err)
|
s.Log.Errorf("Failed to replenish FIFO stock for recording egg %d: %+v", egg.Id, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(note) != "" && actorID != 0 {
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: egg.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Increase: float64(egg.Qty),
|
||||||
|
LoggableType: string(utils.StockLogTypeRecording),
|
||||||
|
LoggableId: egg.RecordingId,
|
||||||
|
Notes: note,
|
||||||
|
}
|
||||||
|
if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1034,6 +1240,8 @@ func (s *recordingService) syncRecordingStocks(
|
|||||||
recordingID uint,
|
recordingID uint,
|
||||||
existing []entity.RecordingStock,
|
existing []entity.RecordingStock,
|
||||||
incoming []validation.Stock,
|
incoming []validation.Stock,
|
||||||
|
note string,
|
||||||
|
actorID uint,
|
||||||
) error {
|
) error {
|
||||||
if s.FifoSvc == nil {
|
if s.FifoSvc == nil {
|
||||||
if err := s.Repository.DeleteStocks(tx, recordingID); err != nil {
|
if err := s.Repository.DeleteStocks(tx, recordingID); err != nil {
|
||||||
@@ -1080,7 +1288,7 @@ func (s *recordingService) syncRecordingStocks(
|
|||||||
leftovers = append(leftovers, list...)
|
leftovers = append(leftovers, list...)
|
||||||
}
|
}
|
||||||
if len(leftovers) > 0 {
|
if len(leftovers) > 0 {
|
||||||
if err := s.releaseRecordingStocks(ctx, tx, leftovers); err != nil {
|
if err := s.releaseRecordingStocks(ctx, tx, leftovers, note, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ids := make([]uint, 0, len(leftovers))
|
ids := make([]uint, 0, len(leftovers))
|
||||||
@@ -1099,7 +1307,7 @@ func (s *recordingService) syncRecordingStocks(
|
|||||||
if len(stocksToConsume) == 0 {
|
if len(stocksToConsume) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return s.consumeRecordingStocks(ctx, tx, stocksToConsume)
|
return s.consumeRecordingStocks(ctx, tx, stocksToConsume, note, actorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
type eggTotals struct {
|
type eggTotals struct {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
||||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
projectFlockKandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
projectFlockKandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
rPurchase "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
rPurchase "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/validations"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
@@ -830,9 +831,16 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
|||||||
receivingAction = entity.ApprovalActionUpdated
|
receivingAction = entity.ApprovalActionUpdated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
noteSuffix := "receive"
|
||||||
|
if receivingAction == entity.ApprovalActionUpdated {
|
||||||
|
noteSuffix = "edit-receive"
|
||||||
|
}
|
||||||
|
receiveNote := fmt.Sprintf("%s#%s", strings.TrimSpace(*purchase.PoNumber), noteSuffix)
|
||||||
|
|
||||||
transactionErr := s.PurchaseRepo.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
transactionErr := s.PurchaseRepo.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
||||||
repoTx := rPurchase.NewPurchaseRepository(tx)
|
repoTx := rPurchase.NewPurchaseRepository(tx)
|
||||||
pwRepoTx := rProductWarehouse.NewProductWarehouseRepository(tx)
|
pwRepoTx := rProductWarehouse.NewProductWarehouseRepository(tx)
|
||||||
|
stockLogRepoTx := rStockLogs.NewStockLogRepository(tx)
|
||||||
|
|
||||||
deltas := make(map[uint]float64)
|
deltas := make(map[uint]float64)
|
||||||
affected := make(map[uint]struct{})
|
affected := make(map[uint]struct{})
|
||||||
@@ -849,6 +857,11 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
|||||||
pwID uint
|
pwID uint
|
||||||
qty float64
|
qty float64
|
||||||
}, 0, len(prepared))
|
}, 0, len(prepared))
|
||||||
|
logEntries := make([]struct {
|
||||||
|
itemID uint
|
||||||
|
pwID uint
|
||||||
|
delta float64
|
||||||
|
}, 0, len(prepared))
|
||||||
|
|
||||||
for _, prep := range prepared {
|
for _, prep := range prepared {
|
||||||
item := prep.item
|
item := prep.item
|
||||||
@@ -869,6 +882,13 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
|||||||
newPWID = &pwID
|
newPWID = &pwID
|
||||||
|
|
||||||
deltaQty := prep.receivedQty - item.TotalQty
|
deltaQty := prep.receivedQty - item.TotalQty
|
||||||
|
if newPWID != nil && deltaQty != 0 {
|
||||||
|
logEntries = append(logEntries, struct {
|
||||||
|
itemID uint
|
||||||
|
pwID uint
|
||||||
|
delta float64
|
||||||
|
}{itemID: item.Id, pwID: *newPWID, delta: deltaQty})
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case deltaQty > 0 && newPWID != nil:
|
case deltaQty > 0 && newPWID != nil:
|
||||||
if s.FifoSvc != nil {
|
if s.FifoSvc != nil {
|
||||||
@@ -993,6 +1013,33 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(logEntries) > 0 {
|
||||||
|
logs := make([]*entity.StockLog, 0, len(logEntries))
|
||||||
|
for _, entry := range logEntries {
|
||||||
|
if entry.pwID == 0 || entry.delta == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log := &entity.StockLog{
|
||||||
|
ProductWarehouseId: entry.pwID,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
LoggableType: string(utils.StockLogTypePurchase),
|
||||||
|
LoggableId: purchase.Id,
|
||||||
|
Notes: receiveNote,
|
||||||
|
}
|
||||||
|
if entry.delta > 0 {
|
||||||
|
log.Increase = entry.delta
|
||||||
|
} else {
|
||||||
|
log.Decrease = -entry.delta
|
||||||
|
}
|
||||||
|
logs = append(logs, log)
|
||||||
|
}
|
||||||
|
if len(logs) > 0 {
|
||||||
|
if err := stockLogRepoTx.CreateMany(c.Context(), logs, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(affected) > 0 {
|
if len(affected) > 0 {
|
||||||
if err := pwRepoTx.CleanupEmpty(c.Context(), affected); err != nil {
|
if err := pwRepoTx.CleanupEmpty(c.Context(), affected); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ const (
|
|||||||
StockLogTypeTransfer StockLogType = "TRANSFER"
|
StockLogTypeTransfer StockLogType = "TRANSFER"
|
||||||
StockLogTypeMarketing StockLogType = "MARKETING"
|
StockLogTypeMarketing StockLogType = "MARKETING"
|
||||||
StockLogTypeChikin StockLogType = "CHICKIN"
|
StockLogTypeChikin StockLogType = "CHICKIN"
|
||||||
|
StockLogTypePurchase StockLogType = "PURCHASE"
|
||||||
|
StockLogTypeRecording StockLogType = "RECORDING"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user