Merge branch 'development-before-sso' of https://gitlab.com/mbugroup/lti-api into dev/teguh

This commit is contained in:
aguhh18
2025-11-05 14:03:45 +07:00
47 changed files with 3474 additions and 1810 deletions
@@ -108,7 +108,6 @@ func (s transferLayingService) GetAll(c *fiber.Ctx, params *validation.Query) ([
db = db.Where("transfer_number ILIKE ?", "%"+params.TransferNumber+"%")
}
// Handle sort
sortField := "created_at"
if params.Sort != "" {
sortField = params.Sort
@@ -127,7 +126,6 @@ func (s transferLayingService) GetAll(c *fiber.Ctx, params *validation.Query) ([
return nil, 0, err
}
// Filter by approval status if requested
if params.ApprovalStatus != "" {
var filtered []entity.LayingTransfer
approvalRepo := commonRepo.NewApprovalRepository(s.Repository.DB())
@@ -156,7 +154,6 @@ func (s transferLayingService) GetOne(c *fiber.Ctx, id uint) (*entity.LayingTran
return nil, err
}
// Fetch and populate latest approval
approvalRepo := commonRepo.NewApprovalRepository(s.Repository.DB())
latestApproval, err := approvalRepo.LatestByTarget(c.Context(), string(utils.ApprovalWorkflowTransferToLaying), transferLaying.Id, nil)
if err == nil && latestApproval != nil {
@@ -172,7 +169,6 @@ func (s transferLayingService) GetOneWithApproval(c *fiber.Ctx, id uint) (*entit
return nil, nil, err
}
// Return the LatestApproval that was populated in GetOne
return transferLaying, transferLaying.LatestApproval, nil
}
@@ -181,11 +177,18 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
return nil, err
}
if err := commonSvc.EnsureRelations(c.Context(),
commonSvc.RelationCheck{Name: "Source Project Flock", ID: &req.SourceProjectFlockId, Exists: s.ProjectFlockRepo.IdExists},
commonSvc.RelationCheck{Name: "Target Project Flock", ID: &req.TargetProjectFlockId, Exists: s.ProjectFlockRepo.IdExists},
); err != nil {
return nil, err
if _, err := s.ProjectFlockRepo.GetByID(c.Context(), req.SourceProjectFlockId, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "Source Project Flock not found")
}
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate source project flock")
}
if _, err := s.ProjectFlockRepo.GetByID(c.Context(), req.TargetProjectFlockId, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "Target Project Flock not found")
}
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate target project flock")
}
for _, detail := range req.SourceKandangs {
@@ -288,7 +291,7 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create transfer laying record")
}
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTxRepo(dbTransaction)
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
projectFlockPopulationRepoTx := s.ProjectFlockPopulationRepo.WithTx(dbTransaction)
for _, sourceDetail := range req.SourceKandangs {
@@ -320,7 +323,6 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target project flock kandang")
}
// Get warehouse for this kandang
targetWarehouse, err := s.WarehouseRepo.GetLatestByKandangID(c.Context(), targetPFK.KandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -359,7 +361,6 @@ func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
return nil, err
}
// Check if transfer laying exists
_, err := s.Repository.GetByID(c.Context(), id, nil)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -368,14 +369,12 @@ func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get transfer laying")
}
// Check if latest approval is PENDING (not approved)
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 nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
}
// If latest approval exists and is APPROVED or REJECTED, cannot update
if latestApproval != nil && latestApproval.Action != nil {
action := string(*latestApproval.Action)
if action == string(entity.ApprovalActionApproved) || action == string(entity.ApprovalActionRejected) {
@@ -409,7 +408,7 @@ func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
}
func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
// Verify transfer laying exists
_, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
return db.Preload("Sources.ProductWarehouse").Preload("Targets")
})
@@ -420,14 +419,12 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get transfer laying")
}
// Check if latest approval is PENDING (not approved/rejected)
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")
}
// If latest approval exists and is APPROVED or REJECTED, cannot delete
if latestApproval != nil && latestApproval.Action != nil {
action := string(*latestApproval.Action)
if action == string(entity.ApprovalActionApproved) || action == string(entity.ApprovalActionRejected) {
@@ -435,22 +432,19 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
}
}
// Delete in transaction to handle cascades and qty restoration
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
// Restore source warehouse quantities
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTxRepo(dbTransaction)
// Get source repository for detail info
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
sourceRepoTx := repository.NewLayingTransferSourceRepository(dbTransaction)
sources, err := sourceRepoTx.GetByLayingTransferId(c.Context(), id)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get transfer sources")
}
// Restore quantity for each source that was reduced
for _, source := range sources {
if source.ProductWarehouseId != nil && source.Qty > 0 {
// Add back the quantity that was transferred
if err := productWarehouseRepoTx.PatchOne(c.Context(), *source.ProductWarehouseId, map[string]any{
"quantity": gorm.Expr("quantity + ?", source.Qty),
}, nil); err != nil {
@@ -459,7 +453,6 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
}
}
// Restore project flock population that was reduced
projectFlockPopulationRepoTx := s.ProjectFlockPopulationRepo.WithTx(dbTransaction)
for _, source := range sources {
populations, err := projectFlockPopulationRepoTx.GetByProjectFlockKandangID(c.Context(), source.SourceProjectFlockKandangId)
@@ -467,13 +460,12 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get populations for restoration")
}
// Restore to latest populations first
remainingToRestore := source.Qty
for i := len(populations) - 1; i >= 0 && remainingToRestore > 0; i-- {
pop := populations[i]
restoreAmount := remainingToRestore
if remainingToRestore < pop.TotalQty {
// Cap restore to what can fit in this population
restoreAmount = remainingToRestore
}
@@ -486,7 +478,6 @@ func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
}
}
// Delete the transfer laying (cascade will delete sources and targets)
if err := s.Repository.WithTx(dbTransaction).DeleteOne(c.Context(), id); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete transfer laying")
}
@@ -536,7 +527,7 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
sourceRepoTx := repository.NewLayingTransferSourceRepository(dbTransaction)
targetRepoTx := repository.NewLayingTransferTargetRepository(dbTransaction)
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTxRepo(dbTransaction)
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
for _, approvableID := range approvableIDs {
transfer, err := s.Repository.GetByID(c.Context(), approvableID, nil)
@@ -606,6 +597,7 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
sourceWarehouse.ProductId,
targetWarehouse.Id,
target.Qty,
actorID,
); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create or update product warehouse")
}
@@ -665,9 +657,9 @@ func createApprovalTransferLaying(ctx context.Context, tx *gorm.DB, transferLayi
return err
}
func (s *transferLayingService) getOrCreateProductWarehouse(ctx context.Context, tx *gorm.DB, productID uint, warehouseID uint, quantity float64) (*entity.ProductWarehouse, error) {
func (s *transferLayingService) getOrCreateProductWarehouse(ctx context.Context, tx *gorm.DB, productID uint, warehouseID uint, quantity float64, actorID uint) (*entity.ProductWarehouse, error) {
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTxRepo(tx)
productWarehouseRepoTx := rInventory.NewProductWarehouseRepository(tx)
existing, err := productWarehouseRepoTx.GetProductWarehouseByProductAndWarehouseID(ctx, productID, warehouseID)
if err == nil && existing != nil {
@@ -685,6 +677,7 @@ func (s *transferLayingService) getOrCreateProductWarehouse(ctx context.Context,
ProductId: productID,
WarehouseId: warehouseID,
Quantity: quantity,
CreatedBy: actorID,
}
if err := productWarehouseRepoTx.CreateOne(ctx, newWarehouse, nil); err != nil {