FIX[BE]: Fix Delete one on chickin match with BE standard

This commit is contained in:
aguhh18
2025-10-29 14:19:08 +07:00
parent 7f00a5a7a4
commit a390d1d23a
4 changed files with 194 additions and 138 deletions
@@ -17,6 +17,9 @@ type ProductWarehouseRepository interface {
ExistsByID(ctx context.Context, id uint) (bool, error)
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error)
GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) (*entity.ProductWarehouse, error)
WithTxRepo(tx *gorm.DB) ProductWarehouseRepository
DB() *gorm.DB
}
type ProductWarehouseRepositoryImpl struct {
@@ -31,6 +34,15 @@ func NewProductWarehouseRepository(db *gorm.DB) ProductWarehouseRepository {
}
}
func (r *ProductWarehouseRepositoryImpl) WithTxRepo(tx *gorm.DB) ProductWarehouseRepository {
return &ProductWarehouseRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProductWarehouse](tx),
db: tx,
}
}
func (r *ProductWarehouseRepositoryImpl) DB() *gorm.DB {
return r.db
}
func (r *ProductWarehouseRepositoryImpl) IsProductExist(ctx context.Context, productId uint) (bool, error) {
return repository.Exists[entity.Product](ctx, r.db, productId)
}
@@ -89,3 +101,20 @@ func (r *ProductWarehouseRepositoryImpl) GetByCategoryCodeAndWarehouseID(ctx con
}
return productWarehouses, nil
}
func (r *ProductWarehouseRepositoryImpl) GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) (*entity.ProductWarehouse, error) {
var productWarehouse entity.ProductWarehouse
err := r.db.WithContext(ctx).
Table("product_warehouses").
Select("product_warehouses.*").
Joins("JOIN products ON products.id = product_warehouses.product_id").
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
Order("product_warehouses.created_at DESC").
Limit(1).
First(&productWarehouse).Error
if err != nil {
return nil, err
}
return &productWarehouse, nil
}
@@ -1,6 +1,8 @@
package repository
import (
"context"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm"
@@ -8,6 +10,10 @@ import (
type ProjectChickinDetailRepository interface {
repository.BaseRepository[entity.ProjectChickinDetail]
CreateOne(ctx context.Context, entity *entity.ProjectChickinDetail, modifier func(*gorm.DB) *gorm.DB) error
DeleteMany(ctx context.Context, modifier func(*gorm.DB) *gorm.DB) error
GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error)
WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository
}
type ChickinDetailRepositoryImpl struct {
@@ -19,3 +25,22 @@ func NewChickinDetailRepository(db *gorm.DB) ProjectChickinDetailRepository {
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](db),
}
}
func (r *ChickinDetailRepositoryImpl) WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository {
return &ChickinDetailRepositoryImpl{BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](tx)}
}
func (r *ChickinDetailRepositoryImpl) DB() *gorm.DB {
return r.BaseRepositoryImpl.DB()
}
func (r *ChickinDetailRepositoryImpl) GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error) {
var records []entity.ProjectChickinDetail
if err := r.DB().WithContext(ctx).Where("project_chickin_id = ?", projectChickinID).Find(&records).Error; err != nil {
return nil, err
}
if len(records) == 0 {
return nil, gorm.ErrRecordNotFound
}
return records, nil
}
@@ -77,6 +77,7 @@ func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
}
offset := (params.Page - 1) * params.Limit
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
db = s.withRelations(db)
if params.ProjectFlockKandangId != 0 {
@@ -241,141 +242,127 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
}
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
db := s.Repository.DB()
tx := db.WithContext(c.Context()).Begin()
if tx.Error != nil {
s.Log.Errorf("Failed to begin transaction: %+v", tx.Error)
return tx.Error
}
rollback := func(err error) error {
if rerr := tx.Rollback().Error; rerr != nil {
s.Log.Errorf("Rollback failed: %+v", rerr)
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
chickinRepo := repository.NewChickinRepository(dbTransaction)
projectFlockKandangRepo := s.ProjectflockKandangRepo.WithTx(dbTransaction)
productWarehouseRepo := s.ProductWarehouseRepo.WithTxRepo(dbTransaction)
projectFlockPopulationRepo := s.ProjectflockPopulationRepo.WithTx(dbTransaction)
projectChickinDetailRepo := s.ProjectChickinDetailRepo.WithTxRepo(dbTransaction)
warehouseRepoTx := rWarehouse.NewWarehouseRepository(dbTransaction)
chickin, err := chickinRepo.GetByID(c.Context(), id, nil)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
return err
}
chickinRepoTx := s.Repository.WithTx(tx)
pfkRepoTx := s.ProjectflockKandangRepo.WithTx(tx)
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(tx)
chickin, err := chickinRepoTx.GetByID(c.Context(), id, nil)
if errors.Is(err, gorm.ErrRecordNotFound) {
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
}
population, err := projectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId)
if err != nil {
s.Log.Errorf("Failed get chickin by id: %+v", err)
return rollback(err)
}
var population entity.ProjectFlockPopulation
if err := tx.WithContext(c.Context()).Where("project_flock_kandang_id = ?", chickin.ProjectFlockKandangId).First(&population).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return rollback(fiber.NewError(fiber.StatusNotFound, "Project flock population not found"))
return fiber.NewError(fiber.StatusNotFound, "Project flock population not found")
}
s.Log.Errorf("Failed to get project flock population: %+v", err)
return rollback(err)
return err
}
newReserved := population.ReservedQuantity - chickin.Quantity
if newReserved < 0 {
newReserved = 0
}
if err := tx.WithContext(c.Context()).Model(&entity.ProjectFlockPopulation{}).Where("id = ?", population.Id).Updates(map[string]any{"reserved_quantity": newReserved}).Error; err != nil {
s.Log.Errorf("Failed to update project flock population: %+v", err)
return rollback(err)
err = projectFlockPopulationRepo.PatchOne(c.Context(), population.Id, map[string]any{
"reserved_quantity": newReserved,
}, nil)
if err != nil {
return err
}
restoreFromDetails := func() (bool, error) {
var details []entity.ProjectChickinDetail
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Find(&details).Error; err != nil {
details, err := projectChickinDetailRepo.GetByProjectChickinID(c.Context(), chickin.Id)
if err != nil {
return false, err
}
if len(details) == 0 {
return false, nil
}
for _, d := range details {
var pw entity.ProductWarehouse
if err := tx.WithContext(c.Context()).Where("id = ?", d.ProductWarehouseId).First(&pw).Error; err != nil {
productWarehouse, err := productWarehouseRepo.GetByID(c.Context(), d.ProductWarehouseId, nil)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
continue
}
return false, err
}
updatedQuantity := pw.Quantity + d.Quantity
if err := productWarehouseRepoTx.PatchOne(c.Context(), pw.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
updatedQuantity := productWarehouse.Quantity + d.Quantity
if err := productWarehouseRepo.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
return false, err
}
}
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Delete(&entity.ProjectChickinDetail{}).Error; err != nil {
if err := projectChickinDetailRepo.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB {
return db.Where("project_chickin_id = ?", chickin.Id)
}); err != nil {
return false, err
}
return true, nil
}
restored, err := restoreFromDetails()
if err != nil {
s.Log.Errorf("Failed to restore from chickin details: %+v", err)
return rollback(err)
return err
}
if !restored {
projectflockkandang, err := pfkRepoTx.GetByID(c.Context(), population.ProjectFlockKandangId)
if err != nil {
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
return rollback(err)
}
var warehouse entity.Warehouse
if err := tx.WithContext(c.Context()).Where("kandang_id = ?", projectflockkandang.KandangId).First(&warehouse).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return rollback(fiber.NewError(fiber.StatusNotFound, "Warehouse not found for kandang"))
}
s.Log.Errorf("Failed to get warehouse: %+v", err)
return rollback(err)
}
var productWarehouse entity.ProductWarehouse
err = tx.WithContext(c.Context()).Table("product_warehouses").
Select("product_warehouses.*").
Joins("JOIN products ON products.id = product_warehouses.product_id").
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
Order("product_warehouses.created_at DESC").
First(&productWarehouse).Error
projectflockkandang, err := projectFlockKandangRepo.GetByID(c.Context(), population.ProjectFlockKandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return rollback(fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse"))
return fiber.NewError(fiber.StatusNotFound, "Project flock kandang not found")
}
s.Log.Errorf("Failed to get product warehouse: %+v", err)
return rollback(err)
return err
}
warehouse, err := warehouseRepoTx.GetByKandangID(c.Context(), projectflockkandang.KandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Warehouse not found for kandang")
}
return err
}
productWarehouse, err := productWarehouseRepo.GetLatestByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse")
}
return err
}
updatedQuantity := productWarehouse.Quantity + chickin.Quantity
if err := productWarehouseRepoTx.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
return rollback(err)
if err := productWarehouseRepo.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
return err
}
}
// delete chickin (single place)
if err := chickinRepoTx.DeleteOne(c.Context(), id); err != nil {
if err := chickinRepo.DeleteOne(c.Context(), id); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
s.Log.Errorf("Failed to delete chickin: %+v", err)
return rollback(err)
return err
}
if err := tx.Commit().Error; err != nil {
s.Log.Errorf("Failed to commit transaction: %+v", err)
return rollback(err)
return nil
})
if err != nil {
if ferr, ok := err.(*fiber.Error); ok {
return ferr
}
return err
}
return nil
@@ -385,11 +372,8 @@ func (s *chickinService) Approve(c *fiber.Ctx, id uint) error {
// todo: ini contoh akhir jika sudah approved
chickin, err := s.Repository.GetByID(
c.Context(),
id,
nil,
)
chickin, err := s.Repository.GetByID(c.Context(), id, nil)
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
@@ -9,8 +9,16 @@ import (
)
type ProjectFlockPopulationRepository interface {
repository.BaseRepository[entity.ProjectFlockPopulation]
// domain-specific
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error)
// subset of base repository methods used by services
CreateOne(ctx context.Context, entity *entity.ProjectFlockPopulation, modifier func(*gorm.DB) *gorm.DB) error
PatchOne(ctx context.Context, id uint, updates map[string]any, modifier func(*gorm.DB) *gorm.DB) error
// transaction helpers
WithTx(tx *gorm.DB) ProjectFlockPopulationRepository
DB() *gorm.DB
}
type projectFlockPopulationRepositoryImpl struct {
@@ -23,6 +31,16 @@ func NewProjectFlockPopulationRepository(db *gorm.DB) ProjectFlockPopulationRepo
}
}
func (r *projectFlockPopulationRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockPopulationRepository {
return &projectFlockPopulationRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlockPopulation](tx),
}
}
func (r *projectFlockPopulationRepositoryImpl) DB() *gorm.DB {
return r.BaseRepositoryImpl.DB()
}
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error) {
var record entity.ProjectFlockPopulation
err := r.DB().WithContext(ctx).