mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
FIX[BE]: add laying transfer source and target repositories to transfer laying service
This commit is contained in:
@@ -26,6 +26,8 @@ type TransferLayingModule struct{}
|
||||
|
||||
func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
transferLayingRepo := rTransferLaying.NewTransferLayingRepository(db)
|
||||
layingTransferSourceRepo := rTransferLaying.NewLayingTransferSourceRepository(db)
|
||||
layingTransferTargetRepo := rTransferLaying.NewLayingTransferTargetRepository(db)
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
projectFlockRepo := rProjectFlock.NewProjectflockRepository(db)
|
||||
projectFlockKandangRepo := rProjectFlock.NewProjectFlockKandangRepository(db)
|
||||
@@ -80,6 +82,8 @@ func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, val
|
||||
|
||||
transferLayingService := sTransferLaying.NewTransferLayingService(
|
||||
transferLayingRepo,
|
||||
layingTransferSourceRepo,
|
||||
layingTransferTargetRepo,
|
||||
projectFlockRepo,
|
||||
projectFlockKandangRepo,
|
||||
projectFlockPopulationRepo,
|
||||
|
||||
@@ -41,6 +41,8 @@ type transferLayingService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.TransferLayingRepository
|
||||
LayingTransferSourceRepo repository.LayingTransferSourceRepository
|
||||
LayingTransferTargetRepo repository.LayingTransferTargetRepository
|
||||
ProjectFlockRepo ProjectFlockRepository.ProjectflockRepository
|
||||
ProjectFlockKandangRepo ProjectFlockRepository.ProjectFlockKandangRepository
|
||||
ProjectFlockPopulationRepo ProjectFlockRepository.ProjectFlockPopulationRepository
|
||||
@@ -53,6 +55,8 @@ type transferLayingService struct {
|
||||
|
||||
func NewTransferLayingService(
|
||||
repo repository.TransferLayingRepository,
|
||||
layingTransferSourceRepo repository.LayingTransferSourceRepository,
|
||||
layingTransferTargetRepo repository.LayingTransferTargetRepository,
|
||||
projectFlockRepo ProjectFlockRepository.ProjectflockRepository,
|
||||
projectFlockKandangRepo ProjectFlockRepository.ProjectFlockKandangRepository,
|
||||
projectFlockPopulationRepo ProjectFlockRepository.ProjectFlockPopulationRepository,
|
||||
@@ -66,6 +70,8 @@ func NewTransferLayingService(
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
LayingTransferSourceRepo: layingTransferSourceRepo,
|
||||
LayingTransferTargetRepo: layingTransferTargetRepo,
|
||||
ProjectFlockRepo: projectFlockRepo,
|
||||
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
||||
ProjectFlockPopulationRepo: projectFlockPopulationRepo,
|
||||
@@ -262,7 +268,11 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
repoTx := s.Repository.WithTx(dbTransaction)
|
||||
sourceRepoTx := repository.NewLayingTransferSourceRepository(dbTransaction)
|
||||
targetRepoTx := repository.NewLayingTransferTargetRepository(dbTransaction)
|
||||
pwRepoTx := rInventory.NewProductWarehouseRepository(dbTransaction)
|
||||
|
||||
if err := repoTx.CreateOne(c.Context(), createBody, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat record transfer laying")
|
||||
@@ -278,7 +288,7 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
PendingUsageQty: 0, // Di-set 0, biarkan FIFO Consume yang handle saat Approval
|
||||
ProductWarehouseId: &productWarehouseId,
|
||||
}
|
||||
if err := dbTransaction.Create(&source).Error; err != nil {
|
||||
if err := sourceRepoTx.CreateOne(c.Context(), &source, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat sumber transfer")
|
||||
}
|
||||
|
||||
@@ -304,8 +314,8 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
for _, sourceDetail := range req.SourceKandangs {
|
||||
if pwID, ok := sourceWarehouseMap[sourceDetail.ProjectFlockKandangId]; ok {
|
||||
// Get product warehouse untuk ambil product ID
|
||||
var sourcePW entity.ProductWarehouse
|
||||
if err := dbTransaction.First(&sourcePW, pwID).Error; err == nil {
|
||||
sourcePW, err := pwRepoTx.GetByID(c.Context(), pwID, nil)
|
||||
if err == nil {
|
||||
sourceProductID = sourcePW.ProductId
|
||||
break
|
||||
}
|
||||
@@ -317,22 +327,19 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
}
|
||||
|
||||
// Cari product warehouse di target berdasarkan: warehouse + project_flock_kandang + PRODUCT
|
||||
var targetPW entity.ProductWarehouse
|
||||
err = dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ? AND product_id = ?",
|
||||
targetWarehouse.Id, targetDetail.ProjectFlockKandangId, sourceProductID).
|
||||
First(&targetPW).Error
|
||||
targetPW, err := pwRepoTx.FindByProductWarehouseAndPfk(c.Context(), sourceProductID, targetWarehouse.Id, &targetDetail.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// Create baru dengan product yang sama dengan source
|
||||
targetPW = entity.ProductWarehouse{
|
||||
newTargetPW := entity.ProductWarehouse{
|
||||
ProductId: sourceProductID,
|
||||
WarehouseId: targetWarehouse.Id,
|
||||
ProjectFlockKandangId: &targetDetail.ProjectFlockKandangId,
|
||||
Quantity: 0,
|
||||
}
|
||||
if err := dbTransaction.Create(&targetPW).Error; err != nil {
|
||||
if err := pwRepoTx.CreateOne(c.Context(), &newTargetPW, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal membuat product warehouse untuk kandang tujuan %d: %v", targetDetail.ProjectFlockKandangId, err))
|
||||
}
|
||||
targetPW = &newTargetPW
|
||||
} else {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mendapatkan product warehouse untuk kandang tujuan %d: %v", targetDetail.ProjectFlockKandangId, err))
|
||||
}
|
||||
@@ -345,7 +352,7 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
TotalUsed: 0,
|
||||
ProductWarehouseId: &targetPW.Id,
|
||||
}
|
||||
if err := dbTransaction.Create(&target).Error; err != nil {
|
||||
if err := targetRepoTx.CreateOne(c.Context(), &target, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat target transfer")
|
||||
}
|
||||
}
|
||||
@@ -407,16 +414,18 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
repoTx := s.Repository.WithTx(dbTransaction)
|
||||
sourceRepo := s.LayingTransferSourceRepo.WithTx(dbTransaction)
|
||||
targetRepo := s.LayingTransferTargetRepo.WithTx(dbTransaction)
|
||||
|
||||
// Hapus old sources dan targets
|
||||
for _, oldSource := range existingTransfer.Sources {
|
||||
if err := dbTransaction.Delete(&oldSource).Error; err != nil {
|
||||
if err := sourceRepo.DeleteOne(c.Context(), oldSource.Id); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete old source")
|
||||
}
|
||||
}
|
||||
|
||||
for _, oldTarget := range existingTransfer.Targets {
|
||||
if err := dbTransaction.Delete(&oldTarget).Error; err != nil {
|
||||
if err := targetRepo.DeleteOne(c.Context(), oldTarget.Id); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete old target")
|
||||
}
|
||||
}
|
||||
@@ -458,11 +467,13 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
PendingUsageQty: sourceDetail.Quantity,
|
||||
ProductWarehouseId: &productWarehouseId,
|
||||
}
|
||||
if err := dbTransaction.Create(&source).Error; err != nil {
|
||||
if err := sourceRepo.CreateOne(c.Context(), &source, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer source")
|
||||
}
|
||||
}
|
||||
|
||||
pwRepo := rInventory.NewProductWarehouseRepository(dbTransaction)
|
||||
|
||||
for _, targetDetail := range req.TargetKandangs {
|
||||
targetprojectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
@@ -483,8 +494,8 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
firstSourceKandangID := req.SourceKandangs[0].ProjectFlockKandangId
|
||||
populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), firstSourceKandangID)
|
||||
if err == nil && len(populations) > 0 && populations[0].ProductWarehouseId > 0 {
|
||||
var sourcePW entity.ProductWarehouse
|
||||
if err := dbTransaction.First(&sourcePW, populations[0].ProductWarehouseId).Error; err == nil {
|
||||
sourcePW, err := pwRepo.GetByID(c.Context(), populations[0].ProductWarehouseId, nil)
|
||||
if err == nil {
|
||||
sourceProductID = sourcePW.ProductId
|
||||
}
|
||||
}
|
||||
@@ -494,23 +505,20 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get product from source warehouse")
|
||||
}
|
||||
|
||||
// Cari product warehouse di target berdasarkan: warehouse + project_flock_kandang + PRODUCT
|
||||
var targetPW entity.ProductWarehouse
|
||||
err = dbTransaction.Where("warehouse_id = ? AND project_flock_kandang_id = ? AND product_id = ?",
|
||||
targetWarehouse.Id, targetDetail.ProjectFlockKandangId, sourceProductID).
|
||||
First(&targetPW).Error
|
||||
targetPW, err := pwRepo.FindByProductWarehouseAndPfk(c.Context(), sourceProductID, targetWarehouse.Id, &targetDetail.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// Create baru dengan product yang sama dengan source
|
||||
targetPW = entity.ProductWarehouse{
|
||||
|
||||
newTargetPW := entity.ProductWarehouse{
|
||||
ProductId: sourceProductID,
|
||||
WarehouseId: targetWarehouse.Id,
|
||||
ProjectFlockKandangId: &targetDetail.ProjectFlockKandangId,
|
||||
Quantity: 0,
|
||||
}
|
||||
if err := dbTransaction.Create(&targetPW).Error; err != nil {
|
||||
if err := pwRepo.CreateOne(c.Context(), &newTargetPW, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to create product warehouse for target kandang %d: %v", targetDetail.ProjectFlockKandangId, err))
|
||||
}
|
||||
targetPW = &newTargetPW
|
||||
} else {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get product warehouse for target kandang %d: %v", targetDetail.ProjectFlockKandangId, err))
|
||||
}
|
||||
@@ -523,7 +531,7 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
TotalUsed: 0,
|
||||
ProductWarehouseId: &targetPW.Id,
|
||||
}
|
||||
if err := dbTransaction.Create(&target).Error; err != nil {
|
||||
if err := targetRepo.CreateOne(c.Context(), &target, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer target")
|
||||
}
|
||||
}
|
||||
@@ -551,6 +559,7 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
}
|
||||
|
||||
approvalRepo := commonRepo.NewApprovalRepository(s.Repository.DB())
|
||||
|
||||
latestApproval, err := approvalRepo.LatestByTarget(c.Context(), string(utils.ApprovalWorkflowTransferToLaying), id, nil)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
|
||||
@@ -565,8 +574,6 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
repoTx := s.Repository.WithTx(dbTransaction)
|
||||
|
||||
// Delete transfer - cascade akan menghapus sources dan targets
|
||||
// FIFO akan menangani stock allocation cleanup via foreign key constraints
|
||||
if err := repoTx.DeleteOne(c.Context(), id); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete transfer laying")
|
||||
}
|
||||
@@ -618,6 +625,9 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
repoTx := s.Repository.WithTx(dbTransaction)
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
// Gunakan repo baru untuk transaction scope agar bisa akses method custom
|
||||
sourceRepoTx := repository.NewLayingTransferSourceRepository(dbTransaction)
|
||||
targetRepoTx := repository.NewLayingTransferTargetRepository(dbTransaction)
|
||||
|
||||
for _, approvableID := range approvableIDs {
|
||||
@@ -643,7 +653,7 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
|
||||
if action == entity.ApprovalActionApproved {
|
||||
|
||||
sources, err := repository.NewLayingTransferSourceRepository(dbTransaction).GetByLayingTransferId(c.Context(), approvableID)
|
||||
sources, err := sourceRepoTx.GetByLayingTransferId(c.Context(), approvableID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil sources transfer")
|
||||
}
|
||||
@@ -677,18 +687,14 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal consume FIFO stock: %v", err))
|
||||
}
|
||||
|
||||
// Update source usage tracking
|
||||
if err := dbTransaction.Model(&entity.LayingTransferSource{}).
|
||||
Where("id = ?", source.Id).
|
||||
Updates(map[string]interface{}{
|
||||
if err := sourceRepoTx.PatchOne(c.Context(), source.Id, map[string]interface{}{
|
||||
"usage_qty": source.UsageQty + consumeResult.UsageQuantity,
|
||||
"pending_usage_qty": consumeResult.PendingQuantity,
|
||||
}).Error; err != nil {
|
||||
}, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal update source usage qty")
|
||||
}
|
||||
}
|
||||
|
||||
// Replenish ke target warehouse
|
||||
for _, target := range targets {
|
||||
if target.ProductWarehouseId == nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Target product warehouse tidak ditemukan untuk transfer %d", approvableID))
|
||||
@@ -707,9 +713,9 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal replenish stock ke target warehouse: %v", err))
|
||||
}
|
||||
|
||||
if err := dbTransaction.Model(&entity.LayingTransferTarget{}).
|
||||
Where("id = ?", target.Id).
|
||||
Update("total_qty", replenishResult.AddedQuantity).Error; err != nil {
|
||||
if err := targetRepoTx.PatchOne(c.Context(), target.Id, map[string]interface{}{
|
||||
"total_qty": replenishResult.AddedQuantity,
|
||||
}, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Gagal update target total qty")
|
||||
}
|
||||
}
|
||||
@@ -777,9 +783,8 @@ func (s *transferLayingService) getOrCreateProductWarehouse(ctx context.Context,
|
||||
newWarehouse := &entity.ProductWarehouse{
|
||||
ProductId: productID,
|
||||
WarehouseId: warehouseID,
|
||||
ProjectFlockKandangId: projectFlockKandangId, // Set flock ID agar bisa di-chickin di target flock
|
||||
ProjectFlockKandangId: projectFlockKandangId,
|
||||
Quantity: quantity,
|
||||
// CreatedBy: actorID,
|
||||
}
|
||||
|
||||
if err := productWarehouseRepoTx.CreateOne(ctx, newWarehouse, nil); err != nil {
|
||||
@@ -830,7 +835,7 @@ func (s *transferLayingService) validateKandangOwnership(
|
||||
) error {
|
||||
|
||||
for _, kandangID := range kandangIDs {
|
||||
// validasi terlebih dahulu apakah kandangnya itu ada atau gak
|
||||
|
||||
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(ctx, kandangID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
Reference in New Issue
Block a user