feat(BE-74-76-78-278):adjustment project flock,recording,purchase getall

This commit is contained in:
ragilap
2026-01-10 21:22:54 +07:00
parent 359e982e76
commit 3b2c6f16c3
10 changed files with 294 additions and 94 deletions
@@ -0,0 +1,59 @@
BEGIN;
ALTER TABLE recordings
DROP CONSTRAINT IF EXISTS chk_recordings_nonnegatives_v4;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'hen_day'
) THEN
ALTER TABLE recordings RENAME COLUMN hen_day TO hand_day;
END IF;
END $$;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'hen_house'
) THEN
ALTER TABLE recordings RENAME COLUMN hen_house TO hand_house;
END IF;
END $$;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'egg_mass'
) THEN
ALTER TABLE recordings RENAME COLUMN egg_mass TO egg_mesh;
END IF;
END $$;
ALTER TABLE recordings
ADD COLUMN IF NOT EXISTS daily_gain NUMERIC(7,3),
ADD COLUMN IF NOT EXISTS avg_daily_gain NUMERIC(7,3);
ALTER TABLE recordings
ADD CONSTRAINT chk_recordings_nonnegatives_v3 CHECK (
(total_depletion_qty IS NULL OR total_depletion_qty >= 0) AND
(cum_depletion_rate IS NULL OR cum_depletion_rate >= 0) AND
(daily_gain IS NULL OR daily_gain >= 0) AND
(avg_daily_gain IS NULL OR avg_daily_gain >= 0) AND
(cum_intake IS NULL OR cum_intake >= 0) AND
(fcr_value IS NULL OR fcr_value >= 0) AND
(total_chick_qty IS NULL OR total_chick_qty >= 0) AND
(hand_day IS NULL OR hand_day >= 0) AND
(hand_house IS NULL OR hand_house >= 0) AND
(feed_intake IS NULL OR feed_intake >= 0) AND
(egg_mesh IS NULL OR egg_mesh >= 0) AND
(egg_weight IS NULL OR egg_weight >= 0)
);
COMMIT;
@@ -0,0 +1,57 @@
BEGIN;
ALTER TABLE recordings
DROP CONSTRAINT IF EXISTS chk_recordings_nonnegatives_v3;
ALTER TABLE recordings
DROP COLUMN IF EXISTS daily_gain,
DROP COLUMN IF EXISTS avg_daily_gain;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'hand_day'
) THEN
ALTER TABLE recordings RENAME COLUMN hand_day TO hen_day;
END IF;
END $$;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'hand_house'
) THEN
ALTER TABLE recordings RENAME COLUMN hand_house TO hen_house;
END IF;
END $$;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'recordings' AND column_name = 'egg_mesh'
) THEN
ALTER TABLE recordings RENAME COLUMN egg_mesh TO egg_mass;
END IF;
END $$;
ALTER TABLE recordings
ADD CONSTRAINT chk_recordings_nonnegatives_v4 CHECK (
(total_depletion_qty IS NULL OR total_depletion_qty >= 0) AND
(cum_depletion_rate IS NULL OR cum_depletion_rate >= 0) AND
(cum_intake IS NULL OR cum_intake >= 0) AND
(fcr_value IS NULL OR fcr_value >= 0) AND
(total_chick_qty IS NULL OR total_chick_qty >= 0) AND
(hen_day IS NULL OR hen_day >= 0) AND
(hen_house IS NULL OR hen_house >= 0) AND
(feed_intake IS NULL OR feed_intake >= 0) AND
(egg_mass IS NULL OR egg_mass >= 0) AND
(egg_weight IS NULL OR egg_weight >= 0)
);
COMMIT;
+6 -6
View File
@@ -16,10 +16,10 @@ type Recording struct {
CumIntake *int `gorm:"column:cum_intake"` CumIntake *int `gorm:"column:cum_intake"`
FcrValue *float64 `gorm:"column:fcr_value"` FcrValue *float64 `gorm:"column:fcr_value"`
TotalChickQty *float64 `gorm:"column:total_chick_qty"` TotalChickQty *float64 `gorm:"column:total_chick_qty"`
HandDay *float64 `gorm:"column:hand_day"` HenDay *float64 `gorm:"column:hen_day"`
HandHouse *float64 `gorm:"column:hand_house"` HenHouse *float64 `gorm:"column:hen_house"`
FeedIntake *float64 `gorm:"column:feed_intake"` FeedIntake *float64 `gorm:"column:feed_intake"`
EggMesh *float64 `gorm:"column:egg_mesh"` EggMass *float64 `gorm:"column:egg_mass"`
EggWeight *float64 `gorm:"column:egg_weight"` EggWeight *float64 `gorm:"column:egg_weight"`
CreatedBy uint `gorm:"column:created_by"` CreatedBy uint `gorm:"column:created_by"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
@@ -34,11 +34,11 @@ type Recording struct {
LatestApproval *Approval `gorm:"-" json:"-"` LatestApproval *Approval `gorm:"-" json:"-"`
StandardHandDay *float64 `gorm:"-"` StandardHenDay *float64 `gorm:"-"`
StandardHandHouse *float64 `gorm:"-"` StandardHenHouse *float64 `gorm:"-"`
StandardFeedIntake *float64 `gorm:"-"` StandardFeedIntake *float64 `gorm:"-"`
StandardMaxDepletion *float64 `gorm:"-"` StandardMaxDepletion *float64 `gorm:"-"`
StandardEggMesh *float64 `gorm:"-"` StandardEggMass *float64 `gorm:"-"`
StandardEggWeight *float64 `gorm:"-"` StandardEggWeight *float64 `gorm:"-"`
StandardFcr *float64 `gorm:"-"` StandardFcr *float64 `gorm:"-"`
} }
@@ -23,6 +23,7 @@ type ProductWarehouseRepository interface {
GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint, db *gorm.DB) (*entity.ProductWarehouse, error) GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint, db *gorm.DB) (*entity.ProductWarehouse, error)
GetByFlagAndWarehouseID(ctx context.Context, flagName string, warehouseId uint) ([]entity.ProductWarehouse, error) GetByFlagAndWarehouseID(ctx context.Context, flagName string, warehouseId uint) ([]entity.ProductWarehouse, error)
GetFirstProductByFlag(ctx context.Context, flagName string) (*entity.Product, error) GetFirstProductByFlag(ctx context.Context, flagName string) (*entity.Product, error)
ListProductIDsByFlagPrefixes(ctx context.Context, prefixes []string, visibleStatus *bool) ([]uint, error)
ApplyFlagsFilter(db *gorm.DB, flags []string) *gorm.DB ApplyFlagsFilter(db *gorm.DB, flags []string) *gorm.DB
AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error
GetDetailByID(ctx context.Context, id uint) (*entity.ProductWarehouse, error) GetDetailByID(ctx context.Context, id uint) (*entity.ProductWarehouse, error)
@@ -380,3 +381,38 @@ func (r *ProductWarehouseRepositoryImpl) GetFirstProductByFlag(ctx context.Conte
} }
return &product, nil return &product, nil
} }
func (r *ProductWarehouseRepositoryImpl) ListProductIDsByFlagPrefixes(ctx context.Context, prefixes []string, visibleStatus *bool) ([]uint, error) {
if len(prefixes) == 0 {
return []uint{}, nil
}
db := r.DB().WithContext(ctx).
Model(&entity.Product{}).
Distinct("products.id").
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ?", entity.FlagableTypeProduct)
applied := false
for _, prefix := range prefixes {
if prefix == "" {
continue
}
like := prefix + "%"
if !applied {
db = db.Where("flags.name LIKE ?", like)
applied = true
continue
}
db = db.Or("flags.name LIKE ?", like)
}
if visibleStatus != nil {
db = db.Where("products.is_visible = ?", *visibleStatus)
}
var ids []uint
if err := db.Pluck("products.id", &ids).Error; err != nil {
return nil, err
}
return ids, nil
}
@@ -23,6 +23,7 @@ import (
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations"
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
uniformityRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/repositories" uniformityRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/repositories"
purchaseRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
utils "gitlab.com/mbugroup/lti-api.git/internal/utils" 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"
@@ -308,12 +309,12 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
} }
createBody := &entity.ProjectFlock{ createBody := &entity.ProjectFlock{
AreaId: req.AreaId, AreaId: req.AreaId,
Category: cat, Category: cat,
FcrId: req.FcrId, FcrId: req.FcrId,
ProductionStandardId: req.ProductionStandardId, ProductionStandardId: req.ProductionStandardId,
LocationId: req.LocationId, LocationId: req.LocationId,
CreatedBy: actorID, CreatedBy: actorID,
} }
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error { err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
@@ -823,22 +824,7 @@ func (s projectflockService) detachKandangs(ctx context.Context, dbTransaction *
return nil return nil
} }
blocked, err := s.pivotRepoWithTx(dbTransaction).FindKandangsWithRecordings(ctx, projectFlockID, kandangIDs) // NOTE: Recording constraints are enforced via FK cascade; allow detachment even if recordings exist.
if err != nil {
s.Log.Errorf("Failed to check recordings before detaching kandangs: %+v", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to validate kandang detachment")
}
if len(blocked) > 0 {
names := make([]string, 0, len(blocked))
for _, item := range blocked {
label := fmt.Sprintf("ID %d", item.Id)
if strings.TrimSpace(item.Name) != "" {
label = fmt.Sprintf("%s (%s)", label, item.Name)
}
names = append(names, label)
}
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tidak dapat melepas kandang karena sudah memiliki recording: %s", strings.Join(names, ", ")))
}
pfkIDs, err := s.pivotRepoWithTx(dbTransaction).ListIDsByProjectAndKandang(ctx, projectFlockID, kandangIDs) pfkIDs, err := s.pivotRepoWithTx(dbTransaction).ListIDsByProjectAndKandang(ctx, projectFlockID, kandangIDs)
if err != nil { if err != nil {
@@ -854,6 +840,14 @@ func (s projectflockService) detachKandangs(ctx context.Context, dbTransaction *
return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove uniformity data for project flock kandang") return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove uniformity data for project flock kandang")
} }
db := s.Repository.DB()
if dbTransaction != nil {
db = dbTransaction
}
purchaseRepo := purchaseRepository.NewPurchaseRepository(db)
if err := purchaseRepo.SoftDeleteByProjectFlockKandangIDs(ctx, pfkIDs); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to soft delete purchases for project flock kandang")
}
pwRepo := s.ProductWarehouseRepo pwRepo := s.ProductWarehouseRepo
if dbTransaction != nil { if dbTransaction != nil {
pwRepo = productWarehouseRepository.NewProductWarehouseRepository(dbTransaction) pwRepo = productWarehouseRepository.NewProductWarehouseRepository(dbTransaction)
@@ -906,6 +900,11 @@ func (s projectflockService) ensureProjectFlockKandangProductWarehouses(ctx cont
return nil return nil
} }
projectFlockID := records[0].ProjectFlockId
if projectFlockID == 0 {
return fiber.NewError(fiber.StatusBadRequest, "Project flock id tidak ditemukan")
}
pwRepo := s.ProductWarehouseRepo pwRepo := s.ProductWarehouseRepo
if dbTransaction != nil { if dbTransaction != nil {
pwRepo = productWarehouseRepository.NewProductWarehouseRepository(dbTransaction) pwRepo = productWarehouseRepository.NewProductWarehouseRepository(dbTransaction)
@@ -920,24 +919,34 @@ func (s projectflockService) ensureProjectFlockKandangProductWarehouses(ctx cont
warehouseRepo = warehouseRepository.NewWarehouseRepository(s.Repository.DB()) warehouseRepo = warehouseRepository.NewWarehouseRepository(s.Repository.DB())
} }
flags := []utils.FlagType{ db := s.Repository.DB()
utils.FlagAyamAfkir, if dbTransaction != nil {
utils.FlagAyamCulling, db = dbTransaction
utils.FlagAyamMati, }
utils.FlagTelurPecah, var category string
utils.FlagTelurUtuh, if err := db.WithContext(ctx).
Model(&entity.ProjectFlock{}).
Select("category").
Where("id = ?", projectFlockID).
Scan(&category).Error; err != nil {
return err
}
if strings.TrimSpace(category) == "" {
return fiber.NewError(fiber.StatusBadRequest, "Project flock category tidak ditemukan")
} }
productIDs := make(map[utils.FlagType]uint, len(flags)) prefixes := []string{"AYAM-"}
for _, flag := range flags { if strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying)) {
product, err := pwRepo.GetFirstProductByFlag(ctx, string(flag)) prefixes = append(prefixes, "TELUR")
if err != nil { }
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product untuk flag %s tidak ditemukan", flag)) invisibleOnly := false
} productIDs, err := pwRepo.ListProductIDsByFlagPrefixes(ctx, prefixes, &invisibleOnly)
return err if err != nil {
} return err
productIDs[flag] = product.Id }
if len(productIDs) == 0 {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product dengan flag %s tidak ditemukan", strings.Join(prefixes, ", ")))
} }
for _, record := range records { for _, record := range records {
@@ -953,8 +962,7 @@ func (s projectflockService) ensureProjectFlockKandangProductWarehouses(ctx cont
return err return err
} }
for _, flag := range flags { for _, productID := range productIDs {
productID := productIDs[flag]
if _, err := pwRepo.GetByProductWarehouseAndProjectFlockKandang(ctx, productID, warehouse.Id, record.Id); err == nil { if _, err := pwRepo.GetByProductWarehouseAndProjectFlockKandang(ctx, productID, warehouse.Id, record.Id); err == nil {
continue continue
} else if !errors.Is(err, gorm.ErrRecordNotFound) { } else if !errors.Is(err, gorm.ErrRecordNotFound) {
@@ -25,16 +25,16 @@ type RecordingRelationDTO struct {
CumIntake int `json:"cum_intake"` CumIntake int `json:"cum_intake"`
FcrValue float64 `json:"fcr_value"` FcrValue float64 `json:"fcr_value"`
TotalChickQty float64 `json:"total_chick_qty"` TotalChickQty float64 `json:"total_chick_qty"`
HandDay float64 `json:"hand_day"` HenDay float64 `json:"hen_day"`
HandHouse float64 `json:"hand_house"` HenHouse float64 `json:"hen_house"`
FeedIntake float64 `json:"feed_intake"` FeedIntake float64 `json:"feed_intake"`
EggMesh float64 `json:"egg_mesh"` EggMass float64 `json:"egg_mass"`
EggWeight float64 `json:"egg_weight"` EggWeight float64 `json:"egg_weight"`
StandardHandDay *float64 `json:"hand_day_std,omitempty"` StandardHenDay *float64 `json:"hen_day_std,omitempty"`
StandardHandHouse *float64 `json:"hand_house_std,omitempty"` StandardHenHouse *float64 `json:"hen_house_std,omitempty"`
StandardFeedIntake *float64 `json:"feed_intake_std,omitempty"` StandardFeedIntake *float64 `json:"feed_intake_std,omitempty"`
StandardMaxDepletion *float64 `json:"max_depletion_std,omitempty"` StandardMaxDepletion *float64 `json:"max_depletion_std,omitempty"`
StandardEggMesh *float64 `json:"egg_mesh_std,omitempty"` StandardEggMass *float64 `json:"egg_mass_std,omitempty"`
StandardEggWeight *float64 `json:"egg_weight_std,omitempty"` StandardEggWeight *float64 `json:"egg_weight_std,omitempty"`
StandardFcr *float64 `json:"fcr_std,omitempty"` StandardFcr *float64 `json:"fcr_std,omitempty"`
Approval approvalDTO.ApprovalRelationDTO `json:"approval"` Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
@@ -94,10 +94,10 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
cumIntake int cumIntake int
fcrValue float64 fcrValue float64
totalChickQty float64 totalChickQty float64
handDay float64 henDay float64
handHouse float64 henHouse float64
feedIntake float64 feedIntake float64
eggMesh float64 eggMass float64
eggWeight float64 eggWeight float64
) )
@@ -119,17 +119,17 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
if e.TotalChickQty != nil { if e.TotalChickQty != nil {
totalChickQty = *e.TotalChickQty totalChickQty = *e.TotalChickQty
} }
if e.HandDay != nil { if e.HenDay != nil {
handDay = *e.HandDay henDay = *e.HenDay
} }
if e.HandHouse != nil { if e.HenHouse != nil {
handHouse = *e.HandHouse henHouse = *e.HenHouse
} }
if e.FeedIntake != nil { if e.FeedIntake != nil {
feedIntake = *e.FeedIntake feedIntake = *e.FeedIntake
} }
if e.EggMesh != nil { if e.EggMass != nil {
eggMesh = *e.EggMesh eggMass = *e.EggMass
} }
if e.EggWeight != nil { if e.EggWeight != nil {
eggWeight = *e.EggWeight eggWeight = *e.EggWeight
@@ -157,16 +157,16 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
CumIntake: cumIntake, CumIntake: cumIntake,
FcrValue: fcrValue, FcrValue: fcrValue,
TotalChickQty: totalChickQty, TotalChickQty: totalChickQty,
HandDay: handDay, HenDay: henDay,
HandHouse: handHouse, HenHouse: henHouse,
FeedIntake: feedIntake, FeedIntake: feedIntake,
EggMesh: eggMesh, EggMass: eggMass,
EggWeight: eggWeight, EggWeight: eggWeight,
StandardHandDay: e.StandardHandDay, StandardHenDay: e.StandardHenDay,
StandardHandHouse: e.StandardHandHouse, StandardHenHouse: e.StandardHenHouse,
StandardFeedIntake: e.StandardFeedIntake, StandardFeedIntake: e.StandardFeedIntake,
StandardMaxDepletion: e.StandardMaxDepletion, StandardMaxDepletion: e.StandardMaxDepletion,
StandardEggMesh: e.StandardEggMesh, StandardEggMass: e.StandardEggMass,
StandardEggWeight: e.StandardEggWeight, StandardEggWeight: e.StandardEggWeight,
StandardFcr: e.StandardFcr, StandardFcr: e.StandardFcr,
Approval: latestApproval, Approval: latestApproval,
@@ -16,10 +16,10 @@ func RecordingRoutes(v1 fiber.Router, u user.UserService, s recording.RecordingS
route.Use(m.Auth(u)) route.Use(m.Auth(u))
route.Get("/",m.RequirePermissions(m.P_RecordingGetAll), ctrl.GetAll) route.Get("/",m.RequirePermissions(m.P_RecordingGetAll), ctrl.GetAll)
route.Get("/next-day",m.RequirePermissions(m.P_RecordingNextDay), ctrl.GetNextDay)
route.Get("/:id",m.RequirePermissions(m.P_RecordingGetOne), ctrl.GetOne) route.Get("/:id",m.RequirePermissions(m.P_RecordingGetOne), ctrl.GetOne)
route.Post("/",m.RequirePermissions(m.P_RecordingCreateOne), ctrl.CreateOne) route.Post("/",m.RequirePermissions(m.P_RecordingCreateOne), ctrl.CreateOne)
route.Patch("/:id",m.RequirePermissions(m.P_RecordingUpdateOne), ctrl.UpdateOne) route.Patch("/:id",m.RequirePermissions(m.P_RecordingUpdateOne), ctrl.UpdateOne)
route.Delete("/:id",m.RequirePermissions(m.P_RecordingDeleteOne), ctrl.DeleteOne) route.Delete("/:id",m.RequirePermissions(m.P_RecordingDeleteOne), ctrl.DeleteOne)
route.Get("/next-day",m.RequirePermissions(m.P_RecordingNextDay), ctrl.GetNextDay)
route.Post("/approvals",m.RequirePermissions(m.P_RecordingApproval), ctrl.Approve) route.Post("/approvals",m.RequirePermissions(m.P_RecordingApproval), ctrl.Approve)
} }
@@ -1156,34 +1156,34 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
recording.FeedIntake = nil recording.FeedIntake = nil
} }
var handDay float64 var henDay float64
if remainingChick > 0 && totalEggQty >= 0 { if remainingChick > 0 && totalEggQty >= 0 {
handDay = (totalEggQty / remainingChick) * 100 henDay = (totalEggQty / remainingChick) * 100
updates["hand_day"] = handDay updates["hen_day"] = henDay
recording.HandDay = &handDay recording.HenDay = &henDay
} else { } else {
updates["hand_day"] = gorm.Expr("NULL") updates["hen_day"] = gorm.Expr("NULL")
recording.HandDay = nil recording.HenDay = nil
} }
var handHouse float64 var henHouse float64
if initialChickin > 0 && cumulativeEggQty >= 0 { if initialChickin > 0 && cumulativeEggQty >= 0 {
handHouse = cumulativeEggQty / initialChickin henHouse = cumulativeEggQty / initialChickin
updates["hand_house"] = handHouse updates["hen_house"] = henHouse
recording.HandHouse = &handHouse recording.HenHouse = &henHouse
} else { } else {
updates["hand_house"] = gorm.Expr("NULL") updates["hen_house"] = gorm.Expr("NULL")
recording.HandHouse = nil recording.HenHouse = nil
} }
var eggMesh float64 var eggMass float64
if remainingChick > 0 && totalEggWeightGrams > 0 { if remainingChick > 0 && totalEggWeightGrams > 0 {
eggMesh = (totalEggWeightGrams / remainingChick) * 1000 eggMass = (totalEggWeightGrams / remainingChick) * 1000
updates["egg_mesh"] = eggMesh updates["egg_mass"] = eggMass
recording.EggMesh = &eggMesh recording.EggMass = &eggMass
} else { } else {
updates["egg_mesh"] = gorm.Expr("NULL") updates["egg_mass"] = gorm.Expr("NULL")
recording.EggMesh = nil recording.EggMass = nil
} }
var eggWeight float64 var eggWeight float64
@@ -1334,11 +1334,11 @@ func (s *recordingService) attachLatestApproval(ctx context.Context, item *entit
} }
type productionStandardValues struct { type productionStandardValues struct {
HandDay *float64 HenDay *float64
HandHouse *float64 HenHouse *float64
FeedIntake *float64 FeedIntake *float64
MaxDepletion *float64 MaxDepletion *float64
EggMesh *float64 EggMass *float64
EggWeight *float64 EggWeight *float64
} }
@@ -1389,10 +1389,10 @@ func (s *recordingService) attachProductionStandard(ctx context.Context, item *e
return err return err
} }
if detail != nil { if detail != nil {
standard.HandDay = detail.TargetHenDayProduction standard.HenDay = detail.TargetHenDayProduction
standard.HandHouse = detail.TargetHenHouseProduction standard.HenHouse = detail.TargetHenHouseProduction
standard.EggWeight = detail.TargetEggWeight standard.EggWeight = detail.TargetEggWeight
standard.EggMesh = detail.TargetEggMass standard.EggMass = detail.TargetEggMass
} }
} }
@@ -1420,11 +1420,11 @@ func (s *recordingService) attachProductionStandard(ctx context.Context, item *e
} }
} }
item.StandardHandDay = standard.HandDay item.StandardHenDay = standard.HenDay
item.StandardHandHouse = standard.HandHouse item.StandardHenHouse = standard.HenHouse
item.StandardFeedIntake = standard.FeedIntake item.StandardFeedIntake = standard.FeedIntake
item.StandardMaxDepletion = standard.MaxDepletion item.StandardMaxDepletion = standard.MaxDepletion
item.StandardEggMesh = standard.EggMesh item.StandardEggMass = standard.EggMass
item.StandardEggWeight = standard.EggWeight item.StandardEggWeight = standard.EggWeight
item.StandardFcr = standardFcr item.StandardFcr = standardFcr
@@ -25,6 +25,7 @@ type PurchaseRepository interface {
NextPrNumber(ctx context.Context, tx *gorm.DB) (string, error) NextPrNumber(ctx context.Context, tx *gorm.DB) (string, error)
NextPoNumber(ctx context.Context, tx *gorm.DB) (string, error) NextPoNumber(ctx context.Context, tx *gorm.DB) (string, error)
BackfillProjectFlockKandang(ctx context.Context, purchaseID uint) error BackfillProjectFlockKandang(ctx context.Context, purchaseID uint) error
SoftDeleteByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) error
GetItemsByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.PurchaseItem, error) GetItemsByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.PurchaseItem, error)
GetItemsByWarehouseKandang(ctx context.Context, projectFlockID uint) ([]entity.PurchaseItem, error) GetItemsByWarehouseKandang(ctx context.Context, projectFlockID uint) ([]entity.PurchaseItem, error)
} }
@@ -89,6 +90,44 @@ WHERE pi.purchase_id = ?
return r.DB().WithContext(ctx).Exec(query, purchaseID).Error return r.DB().WithContext(ctx).Exec(query, purchaseID).Error
} }
func (r *PurchaseRepositoryImpl) SoftDeleteByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) error {
if len(projectFlockKandangIDs) == 0 {
return nil
}
return r.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var purchaseIDs []uint
query := `
SELECT pi.purchase_id
FROM purchase_items pi
WHERE pi.project_flock_kandang_id IN (?)
GROUP BY pi.purchase_id
HAVING COUNT(*) = COUNT(CASE WHEN pi.project_flock_kandang_id IN (?) THEN 1 END)
`
if err := tx.Raw(query, projectFlockKandangIDs, projectFlockKandangIDs).Scan(&purchaseIDs).Error; err != nil {
return err
}
now := time.Now().UTC()
if len(purchaseIDs) > 0 {
if err := tx.Model(&entity.Purchase{}).
Where("id IN (?) AND deleted_at IS NULL", purchaseIDs).
Update("deleted_at", now).Error; err != nil {
return err
}
if err := tx.Where("purchase_id IN (?)", purchaseIDs).Delete(&entity.PurchaseItem{}).Error; err != nil {
return err
}
}
deleteItems := tx.Where("project_flock_kandang_id IN (?)", projectFlockKandangIDs)
if len(purchaseIDs) > 0 {
deleteItems = deleteItems.Where("purchase_id NOT IN (?)", purchaseIDs)
}
return deleteItems.Delete(&entity.PurchaseItem{}).Error
})
}
func (r *PurchaseRepositoryImpl) CreateItems(ctx context.Context, purchaseID uint, items []*entity.PurchaseItem) error { func (r *PurchaseRepositoryImpl) CreateItems(ctx context.Context, purchaseID uint, items []*entity.PurchaseItem) error {
if len(items) == 0 { if len(items) == 0 {
return nil return nil
@@ -133,6 +133,7 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
purchases, total, err := s.PurchaseRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { purchases, total, err := s.PurchaseRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
db = s.withRelations(db) db = s.withRelations(db)
db = db.Where("purchases.deleted_at IS NULL")
if params.SupplierID > 0 { if params.SupplierID > 0 {
db = db.Where("supplier_id = ?", params.SupplierID) db = db.Where("supplier_id = ?", params.SupplierID)