mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'development' of https://gitlab.com/mbugroup/lti-api into feat/BE/US-281-adjustment_recording
This commit is contained in:
@@ -14,6 +14,7 @@ Makefile
|
|||||||
docker-compose.local.yml
|
docker-compose.local.yml
|
||||||
docker-compose.yaml
|
docker-compose.yaml
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
Dockerfile.local
|
||||||
.gitlab-ci.yml
|
.gitlab-ci.yml
|
||||||
# Go build cache
|
# Go build cache
|
||||||
.gocache/
|
.gocache/
|
||||||
|
|||||||
@@ -228,6 +228,14 @@ func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error {
|
|||||||
Page: c.QueryInt("page", 1),
|
Page: c.QueryInt("page", 1),
|
||||||
Limit: c.QueryInt("limit", 10),
|
Limit: c.QueryInt("limit", 10),
|
||||||
}
|
}
|
||||||
|
if raw := c.Query("kandang_id"); raw != "" {
|
||||||
|
kandangInt, convErr := strconv.Atoi(raw)
|
||||||
|
if convErr != nil || kandangInt <= 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
|
||||||
|
}
|
||||||
|
kandangUint := uint(kandangInt)
|
||||||
|
query.KandangID = &kandangUint
|
||||||
|
}
|
||||||
|
|
||||||
if query.Page < 1 || query.Limit < 1 {
|
if query.Page < 1 || query.Limit < 1 {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||||
@@ -404,7 +412,18 @@ func (u *ClosingController) GetClosingDataProduksi(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := u.ClosingService.GetClosingDataProduksi(c, uint(id))
|
var kandangID *uint
|
||||||
|
if raw := c.Query("kandang_id"); raw != "" {
|
||||||
|
kandangInt, convErr := strconv.Atoi(raw)
|
||||||
|
if convErr != nil || kandangInt <= 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
|
||||||
|
}
|
||||||
|
kandangUint := uint(kandangInt)
|
||||||
|
kandangID = &kandangUint
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := u.ClosingService.GetClosingDataProduksi(c, uint(id), kandangID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,33 +65,44 @@ type ClosingPurchaseDTO struct {
|
|||||||
FinalPopulation int `json:"final_population"`
|
FinalPopulation int `json:"final_population"`
|
||||||
FeedIn float64 `json:"feed_in"`
|
FeedIn float64 `json:"feed_in"`
|
||||||
FeedUsed float64 `json:"feed_used"`
|
FeedUsed float64 `json:"feed_used"`
|
||||||
FeedUsedPerHead float64 `json:"feed_used_per_head"`
|
// FeedUsedPerHead float64 `json:"feed_used_per_head"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClosingSalesDTO struct {
|
type ClosingSalesDTO struct {
|
||||||
SalesPopulation int `json:"sales_population"`
|
SalesPopulation int `json:"sales_population"`
|
||||||
SalesWeight float64 `json:"sales_weight"`
|
SalesWeight float64 `json:"sales_weight"`
|
||||||
AverageWeight float64 `json:"average_weight"`
|
AverageWeight float64 `json:"avg_weight"`
|
||||||
AverageSellingPrice float64 `json:"chicken_average_selling_price"`
|
AverageSellingPrice float64 `json:"avg_selling_price"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClosingEggSalesDTO struct {
|
type ClosingEggSalesDTO struct {
|
||||||
EggPieces int `json:"egg_pieces"`
|
EggPieces int `json:"egg_pieces"`
|
||||||
EggMassKg float64 `json:"egg_mass_kg"`
|
EggMassKg float64 `json:"egg_mass"`
|
||||||
AverageEggWeightKg float64 `json:"average_egg_weight_kg"`
|
AverageEggWeightKg float64 `json:"avg_egg_weight"`
|
||||||
AverageSellingPrice float64 `json:"egg_average_selling_price"`
|
AverageSellingPrice float64 `json:"avg_selling_price"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClosingPerformanceDTO struct {
|
type ClosingPerformanceDTO struct {
|
||||||
Depletion float64 `json:"depletion"`
|
Depletion float64 `json:"depletion"`
|
||||||
Age float64 `json:"age_day"`
|
Age float64 `json:"age_day"`
|
||||||
MortalityStd float64 `json:"mortality_std"`
|
MortalityStd float64 `json:"mor_std"`
|
||||||
MortalityAct float64 `json:"mortality_act"`
|
MortalityAct float64 `json:"mor_act"`
|
||||||
DeffMortality float64 `json:"deff_mortality"`
|
DeffMortality float64 `json:"mor_diff"`
|
||||||
FcrStd float64 `json:"fcr_std"`
|
FcrStd float64 `json:"fcr_std"`
|
||||||
FcrAct float64 `json:"fcr_act"`
|
FcrAct float64 `json:"fcr_act"`
|
||||||
DeffFcr float64 `json:"deff_fcr"`
|
DeffFcr float64 `json:"fcr_diff"`
|
||||||
Awg float64 `json:"awg"`
|
AwgAct float64 `json:"awg_act"`
|
||||||
|
AwgStd float64 `json:"awg_std"`
|
||||||
|
FeedIntake float64 `json:"feed_intake"`
|
||||||
|
FeedIntakeStd float64 `json:"feed_intake_std"`
|
||||||
|
HenDayAct *float64 `json:"hen_day_act,omitempty"`
|
||||||
|
HendayStd *float64 `json:"hen_day_std,omitempty"`
|
||||||
|
EggMass *float64 `json:"egg_mass,omitempty"`
|
||||||
|
EggMassStd *float64 `json:"egg_mass_std,omitempty"`
|
||||||
|
EggWeight *float64 `json:"egg_weight,omitempty"`
|
||||||
|
EggWeightStd *float64 `json:"egg_weight_std,omitempty"`
|
||||||
|
HenHouseAct *float64 `json:"hen_housed_act,omitempty"`
|
||||||
|
HenHouseStd *float64 `json:"hen_housed_std,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClosingSalesGroupDTO struct {
|
type ClosingSalesGroupDTO struct {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
|
sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services"
|
||||||
rExpenseRealization "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
rExpenseRealization "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||||
rMarketings "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
rMarketings "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
|
rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
||||||
rChickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
rChickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||||
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"
|
||||||
@@ -33,11 +34,13 @@ func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
expenseRealizationRepo := rExpenseRealization.NewExpenseRealizationRepository(db)
|
expenseRealizationRepo := rExpenseRealization.NewExpenseRealizationRepository(db)
|
||||||
chickinRepo := rChickin.NewChickinRepository(db)
|
chickinRepo := rChickin.NewChickinRepository(db)
|
||||||
recordingRepo := rRecording.NewRecordingRepository(db)
|
recordingRepo := rRecording.NewRecordingRepository(db)
|
||||||
|
standardGrowthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db)
|
||||||
|
productionStandardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db)
|
||||||
purchaseRepo := rPurchase.NewPurchaseRepository(db)
|
purchaseRepo := rPurchase.NewPurchaseRepository(db)
|
||||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||||
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||||
|
|
||||||
closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, projectFlockKandangRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, expenseRealizationRepo, projectBudgetRepo, chickinRepo, purchaseRepo, recordingRepo, validate)
|
closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, projectFlockKandangRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, expenseRealizationRepo, projectBudgetRepo, chickinRepo, purchaseRepo, recordingRepo, standardGrowthDetailRepo, productionStandardDetailRepo, validate)
|
||||||
sapronakService := sClosing.NewSapronakService(closingRepo, projectFlockKandangRepo, validate)
|
sapronakService := sClosing.NewSapronakService(closingRepo, projectFlockKandangRepo, validate)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type ClosingRepository interface {
|
|||||||
repository.BaseRepository[entity.ProjectFlock]
|
repository.BaseRepository[entity.ProjectFlock]
|
||||||
GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error)
|
GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error)
|
||||||
SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error)
|
SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error)
|
||||||
|
SumProjectChickinUsageByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
||||||
SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
||||||
SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error)
|
SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error)
|
||||||
SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error)
|
SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error)
|
||||||
@@ -166,6 +167,23 @@ func (r *ClosingRepositoryImpl) SumFeedPurchaseAndUsedByProjectFlockKandangIDs(c
|
|||||||
return purchaseAgg.TotalIn, usageAgg.TotalUsed, nil
|
return purchaseAgg.TotalIn, usageAgg.TotalUsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ClosingRepositoryImpl) SumProjectChickinUsageByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||||
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var total float64
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Model(&entity.ProjectChickin{}).
|
||||||
|
Where("project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
||||||
|
Select("COALESCE(SUM(usage_qty), 0)").
|
||||||
|
Scan(&total).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
func (r *ClosingRepositoryImpl) SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||||
if len(projectFlockKandangIDs) == 0 {
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
expenseRealizationRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
expenseRealizationRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||||
marketingDeliveryProductRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
marketingDeliveryProductRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
marketingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
marketingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
|
productionStandardRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
||||||
chickinRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
chickinRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||||
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
recordingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
recordingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||||
@@ -35,8 +36,8 @@ type ClosingService interface {
|
|||||||
GetProjectFlockByID(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error)
|
GetProjectFlockByID(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error)
|
||||||
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error)
|
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error)
|
||||||
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
|
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
|
||||||
|
GetClosingDataProduksi(ctx *fiber.Ctx, projectFlockID uint, kandangID *uint) (*dto.ClosingProductionReportDTO, error)
|
||||||
GetOverhead(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.OverheadListDTO, error)
|
GetOverhead(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.OverheadListDTO, error)
|
||||||
GetClosingDataProduksi(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error)
|
|
||||||
GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.ClosingSapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error)
|
GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.ClosingSapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error)
|
||||||
GetClosingKeuangan(ctx *fiber.Ctx, projectFlockID uint) (*dto.ReportResponse, error)
|
GetClosingKeuangan(ctx *fiber.Ctx, projectFlockID uint) (*dto.ReportResponse, error)
|
||||||
GetExpeditionHPP(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error)
|
GetExpeditionHPP(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error)
|
||||||
@@ -56,9 +57,11 @@ type closingService struct {
|
|||||||
ChickinRepo chickinRepository.ProjectChickinRepository
|
ChickinRepo chickinRepository.ProjectChickinRepository
|
||||||
PurchaseRepo purchaseRepository.PurchaseRepository
|
PurchaseRepo purchaseRepository.PurchaseRepository
|
||||||
RecordingRepo recordingRepository.RecordingRepository
|
RecordingRepo recordingRepository.RecordingRepository
|
||||||
|
StandardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository
|
||||||
|
ProductionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, projectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository, projectBudgetRepo projectflockRepository.ProjectBudgetRepository, chickinRepo chickinRepository.ProjectChickinRepository, purchaseRepo purchaseRepository.PurchaseRepository, recordingRepo recordingRepository.RecordingRepository, validate *validator.Validate) ClosingService {
|
func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, projectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository, projectBudgetRepo projectflockRepository.ProjectBudgetRepository, chickinRepo chickinRepository.ProjectChickinRepository, purchaseRepo purchaseRepository.PurchaseRepository, recordingRepo recordingRepository.RecordingRepository, standardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository, productionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository, validate *validator.Validate) ClosingService {
|
||||||
return &closingService{
|
return &closingService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Validate: validate,
|
Validate: validate,
|
||||||
@@ -73,6 +76,8 @@ func NewClosingService(repo repository.ClosingRepository, projectFlockRepo proje
|
|||||||
ChickinRepo: chickinRepo,
|
ChickinRepo: chickinRepo,
|
||||||
PurchaseRepo: purchaseRepo,
|
PurchaseRepo: purchaseRepo,
|
||||||
RecordingRepo: recordingRepo,
|
RecordingRepo: recordingRepo,
|
||||||
|
StandardGrowthDetailRepo: standardGrowthDetailRepo,
|
||||||
|
ProductionStandardDetailRepo: productionStandardDetailRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +215,7 @@ func (s closingService) GetClosingSapronak(c *fiber.Ctx, projectFlockID uint, pa
|
|||||||
|
|
||||||
var projectFlockKandangIDs []uint
|
var projectFlockKandangIDs []uint
|
||||||
if params.Type == validation.SapronakTypeOutgoing {
|
if params.Type == validation.SapronakTypeOutgoing {
|
||||||
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
projectFlockKandangIDs, err = s.getProjectFlockKandangIDs(c.Context(), projectFlockID, params.KandangID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to fetch project flock kandang IDs for project flock %d: %+v", projectFlockID, err)
|
s.Log.Errorf("Failed to fetch project flock kandang IDs for project flock %d: %+v", projectFlockID, err)
|
||||||
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang")
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang")
|
||||||
@@ -290,12 +295,15 @@ func (s closingService) getWarehouseIDsByProjectFlock(ctx context.Context, proje
|
|||||||
return ids, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s closingService) getProjectFlockKandangIDs(ctx context.Context, projectFlockID uint) ([]uint, error) {
|
func (s closingService) getProjectFlockKandangIDs(ctx context.Context, projectFlockID uint, kandangID *uint) ([]uint, error) {
|
||||||
var ids []uint
|
var ids []uint
|
||||||
err := s.Repository.DB().WithContext(ctx).
|
query := s.Repository.DB().WithContext(ctx).
|
||||||
Model(&entity.ProjectFlockKandang{}).
|
Model(&entity.ProjectFlockKandang{}).
|
||||||
Where("project_flock_id = ?", projectFlockID).
|
Where("project_flock_id = ?", projectFlockID)
|
||||||
Pluck("id", &ids).Error
|
if kandangID != nil {
|
||||||
|
query = query.Where("kandang_id = ?", *kandangID)
|
||||||
|
}
|
||||||
|
err := query.Order("id ASC").Pluck("id", &ids).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -554,12 +562,22 @@ func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, proj
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error) {
|
func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint, kandangID *uint) (*dto.ClosingProductionReportDTO, error) {
|
||||||
if projectFlockID == 0 {
|
if projectFlockID == 0 {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
||||||
}
|
}
|
||||||
|
|
||||||
project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withClosingRelations)
|
projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID, kandangID)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "No project flock kandang found")
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withRelations)
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found")
|
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found")
|
||||||
}
|
}
|
||||||
@@ -568,19 +586,29 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
||||||
}
|
}
|
||||||
|
|
||||||
var population float64
|
population, err := s.Repository.SumProjectChickinUsageByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
||||||
for _, history := range project.KandangHistory {
|
if err != nil {
|
||||||
for _, chickin := range history.Chickins {
|
s.Log.Errorf("Failed to sum population for project flock %d: %+v", projectFlockID, err)
|
||||||
population += chickin.UsageQty
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch population data")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isGrowing := strings.EqualFold(project.Category, string(utils.ProjectFlockCategoryGrowing))
|
isGrowing := strings.EqualFold(project.Category, string(utils.ProjectFlockCategoryGrowing))
|
||||||
|
|
||||||
projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
currentWeek, err := s.determineProductionWeek(c.Context(), projectFlockKandangIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err)
|
s.Log.Errorf("Failed to determine production week for project flock %d: %+v", projectFlockID, err)
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine production week")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetAverages, err := s.RecordingRepo.GetAverageTargetMetricsByProjectFlockKandangID(c.Context(), projectFlockKandangIDs[0], !isGrowing)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to calculate target metrics for project flock %d: %+v", projectFlockID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch target metrics data")
|
||||||
|
}
|
||||||
|
var fcrActFromRecording *float64
|
||||||
|
if targetAverages.FcrCount > 0 {
|
||||||
|
fcrAvg := targetAverages.FcrAvg
|
||||||
|
fcrActFromRecording = &fcrAvg
|
||||||
}
|
}
|
||||||
|
|
||||||
feedIn, feedUsed, err := s.Repository.SumFeedPurchaseAndUsedByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
feedIn, feedUsed, err := s.Repository.SumFeedPurchaseAndUsedByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
||||||
@@ -589,6 +617,40 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch feed purchase data")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch feed purchase data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
averageFeedIntake := targetAverages.FeedIntakeAvg
|
||||||
|
|
||||||
|
feedIntakeStd := 0.0
|
||||||
|
var mortalityStdFromGrowth *float64
|
||||||
|
if project.ProductionStandardId > 0 && currentWeek > 0 && s.StandardGrowthDetailRepo != nil {
|
||||||
|
growthDetail, growthErr := s.StandardGrowthDetailRepo.GetByStandardIDAndWeek(c.Context(), project.ProductionStandardId, currentWeek)
|
||||||
|
if growthErr != nil {
|
||||||
|
if !errors.Is(growthErr, gorm.ErrRecordNotFound) {
|
||||||
|
s.Log.Errorf("Failed to fetch growth detail for project flock %d: %+v", projectFlockID, growthErr)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch growth standard data")
|
||||||
|
}
|
||||||
|
} else if growthDetail != nil {
|
||||||
|
if growthDetail.FeedIntake != nil {
|
||||||
|
feedIntakeStd = *growthDetail.FeedIntake
|
||||||
|
}
|
||||||
|
if growthDetail.MaxDepletion != nil {
|
||||||
|
mortalityStdFromGrowth = growthDetail.MaxDepletion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var productionStandardDetail *entity.ProductionStandardDetail
|
||||||
|
if project.ProductionStandardId > 0 && currentWeek > 0 && s.ProductionStandardDetailRepo != nil {
|
||||||
|
productionStandardDetail, err = s.ProductionStandardDetailRepo.GetByStandardIDAndWeek(c.Context(), project.ProductionStandardId, currentWeek)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
productionStandardDetail = nil
|
||||||
|
} else {
|
||||||
|
s.Log.Errorf("Failed to fetch production standard detail for project flock %d: %+v", projectFlockID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch production standard detail data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
claimCulling, err := s.Repository.SumClaimCullingByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
claimCulling, err := s.Repository.SumClaimCullingByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to sum claim culling for project flock %d: %+v", projectFlockID, err)
|
s.Log.Errorf("Failed to sum claim culling for project flock %d: %+v", projectFlockID, err)
|
||||||
@@ -611,10 +673,10 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales age data")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales age data")
|
||||||
}
|
}
|
||||||
|
|
||||||
feedUsedPerHead := 0.0
|
// feedUsedPerHead := 0.0
|
||||||
if population > 0 {
|
// if population > 0 {
|
||||||
feedUsedPerHead = feedUsed / population
|
// feedUsedPerHead = feedUsed / population
|
||||||
}
|
// }
|
||||||
|
|
||||||
purchase := dto.ClosingPurchaseDTO{
|
purchase := dto.ClosingPurchaseDTO{
|
||||||
InitialPopulation: int(population),
|
InitialPopulation: int(population),
|
||||||
@@ -622,7 +684,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
FinalPopulation: int(finalPopulation),
|
FinalPopulation: int(finalPopulation),
|
||||||
FeedIn: feedIn,
|
FeedIn: feedIn,
|
||||||
FeedUsed: feedUsed,
|
FeedUsed: feedUsed,
|
||||||
FeedUsedPerHead: feedUsedPerHead,
|
// FeedUsedPerHead: feedUsedPerHead,
|
||||||
}
|
}
|
||||||
|
|
||||||
chickenFlagNames := []string{string(utils.FlagPullet)}
|
chickenFlagNames := []string{string(utils.FlagPullet)}
|
||||||
@@ -655,6 +717,9 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards)
|
chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards)
|
||||||
|
if fcrActFromRecording != nil {
|
||||||
|
chickenPerformance.FcrAct = *fcrActFromRecording
|
||||||
|
}
|
||||||
|
|
||||||
var eggSales *dto.ClosingEggSalesDTO
|
var eggSales *dto.ClosingEggSalesDTO
|
||||||
var eggPerformance *dto.ClosingPerformanceDTO
|
var eggPerformance *dto.ClosingPerformanceDTO
|
||||||
@@ -702,6 +767,9 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards)
|
eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards)
|
||||||
|
if fcrActFromRecording != nil {
|
||||||
|
eggPerf.FcrAct = *fcrActFromRecording
|
||||||
|
}
|
||||||
eggPerformance = &eggPerf
|
eggPerformance = &eggPerf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,15 +786,63 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
DeffMortality: chickenPerformance.DeffMortality,
|
DeffMortality: chickenPerformance.DeffMortality,
|
||||||
}
|
}
|
||||||
if eggPerformance != nil {
|
if eggPerformance != nil {
|
||||||
performance.FcrStd = eggPerformance.FcrStd
|
// performance.FcrStd = eggPerformance.FcrStd
|
||||||
performance.FcrAct = eggPerformance.FcrAct
|
performance.FcrAct = eggPerformance.FcrAct
|
||||||
performance.DeffFcr = eggPerformance.DeffFcr
|
// performance.DeffFcr = eggPerformance.DeffFcr
|
||||||
performance.Awg = eggPerformance.Awg
|
performance.AwgAct = eggPerformance.AwgAct
|
||||||
} else {
|
} else {
|
||||||
performance.FcrStd = chickenPerformance.FcrStd
|
// performance.FcrStd = chickenPerformance.FcrStd
|
||||||
performance.FcrAct = chickenPerformance.FcrAct
|
performance.FcrAct = chickenPerformance.FcrAct
|
||||||
performance.DeffFcr = chickenPerformance.DeffFcr
|
// performance.DeffFcr = chickenPerformance.DeffFcr
|
||||||
performance.Awg = chickenPerformance.Awg
|
performance.AwgAct = chickenPerformance.AwgAct
|
||||||
|
}
|
||||||
|
performance.FeedIntake = averageFeedIntake
|
||||||
|
performance.FeedIntakeStd = feedIntakeStd
|
||||||
|
if targetAverages.CumDepletionRateCount > 0 {
|
||||||
|
performance.MortalityAct = targetAverages.CumDepletionRateAvg
|
||||||
|
performance.DeffMortality = performance.MortalityAct - performance.MortalityStd
|
||||||
|
}
|
||||||
|
if mortalityStdFromGrowth != nil {
|
||||||
|
performance.MortalityStd = *mortalityStdFromGrowth
|
||||||
|
performance.DeffMortality = performance.MortalityAct - performance.MortalityStd
|
||||||
|
}
|
||||||
|
if !isGrowing {
|
||||||
|
if targetAverages.HenDayCount > 0 {
|
||||||
|
henDayAct := targetAverages.HenDayAvg
|
||||||
|
performance.HenDayAct = &henDayAct
|
||||||
|
}
|
||||||
|
if targetAverages.HenHouseCount > 0 {
|
||||||
|
henHouseAct := targetAverages.HenHouseAvg
|
||||||
|
performance.HenHouseAct = &henHouseAct
|
||||||
|
}
|
||||||
|
if targetAverages.EggWeightCount > 0 {
|
||||||
|
eggWeight := targetAverages.EggWeightAvg
|
||||||
|
performance.EggWeight = &eggWeight
|
||||||
|
}
|
||||||
|
if targetAverages.EggMassCount > 0 {
|
||||||
|
eggMass := targetAverages.EggMassAvg
|
||||||
|
performance.EggMass = &eggMass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
performance.DeffFcr = performance.FcrStd - performance.FcrAct
|
||||||
|
if productionStandardDetail != nil {
|
||||||
|
if productionStandardDetail.StandardFCR != nil {
|
||||||
|
performance.FcrStd = *productionStandardDetail.StandardFCR
|
||||||
|
}
|
||||||
|
if !isGrowing {
|
||||||
|
if productionStandardDetail.TargetHenDayProduction != nil {
|
||||||
|
performance.HendayStd = productionStandardDetail.TargetHenDayProduction
|
||||||
|
}
|
||||||
|
if productionStandardDetail.TargetHenHouseProduction != nil {
|
||||||
|
performance.HenHouseStd = productionStandardDetail.TargetHenHouseProduction
|
||||||
|
}
|
||||||
|
if productionStandardDetail.TargetEggWeight != nil {
|
||||||
|
performance.EggWeightStd = productionStandardDetail.TargetEggWeight
|
||||||
|
}
|
||||||
|
if productionStandardDetail.TargetEggMass != nil {
|
||||||
|
performance.EggMassStd = productionStandardDetail.TargetEggMass
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := dto.ClosingProductionReportDTO{
|
result := dto.ClosingProductionReportDTO{
|
||||||
@@ -772,6 +888,46 @@ func (s closingService) calculateAverageSalesAge(ctx context.Context, projectFlo
|
|||||||
return totalAgeWeeks / totalQty, nil
|
return totalAgeWeeks / totalQty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s closingService) determineProductionWeek(ctx context.Context, projectFlockKandangIDs []uint) (int, error) {
|
||||||
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
firstKandangID := projectFlockKandangIDs[0]
|
||||||
|
|
||||||
|
var chickin entity.ProjectChickin
|
||||||
|
if err := s.Repository.DB().WithContext(ctx).
|
||||||
|
Where("project_flock_kandang_id = ?", firstKandangID).
|
||||||
|
Order("chick_in_date ASC").
|
||||||
|
First(&chickin).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
recording, err := s.RecordingRepo.GetLatestByProjectFlockKandangID(ctx, firstKandangID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if recording == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if recording.RecordDatetime.Before(chickin.ChickInDate) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := recording.RecordDatetime.Sub(chickin.ChickInDate)
|
||||||
|
weekFloat := elapsed.Hours() / (24 * 7)
|
||||||
|
week := int(math.Ceil(weekFloat))
|
||||||
|
if week <= 0 {
|
||||||
|
week = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return week, nil
|
||||||
|
}
|
||||||
|
|
||||||
func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO {
|
func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO {
|
||||||
mortalityStd, fcrStd := closestFcrValues(standards, averageWeight)
|
mortalityStd, fcrStd := closestFcrValues(standards, averageWeight)
|
||||||
|
|
||||||
@@ -802,7 +958,7 @@ func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopul
|
|||||||
FcrStd: fcrStd,
|
FcrStd: fcrStd,
|
||||||
FcrAct: fcrAct,
|
FcrAct: fcrAct,
|
||||||
DeffFcr: deffFcr,
|
DeffFcr: deffFcr,
|
||||||
Awg: awg,
|
AwgAct: awg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ClosingSapronakQuery struct {
|
type ClosingSapronakQuery struct {
|
||||||
Type string `query:"type" validate:"required,oneof=incoming outgoing"`
|
Type string `query:"type" validate:"required,oneof=incoming outgoing"`
|
||||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
|
KandangID *uint `query:"kandang_id" validate:"omitempty,gt=0"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,30 @@ type RecordingRepository interface {
|
|||||||
GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error)
|
GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error)
|
||||||
GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error)
|
GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error)
|
||||||
GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeightKg float64, err error)
|
GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeightKg float64, err error)
|
||||||
|
GetAverageTargetMetricsByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint, includeTargets bool) (RecordingTargetAverages, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordingRepositoryImpl struct {
|
type RecordingRepositoryImpl struct {
|
||||||
*repository.BaseRepositoryImpl[entity.Recording]
|
*repository.BaseRepositoryImpl[entity.Recording]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecordingTargetAverages struct {
|
||||||
|
HenDayAvg float64
|
||||||
|
HenDayCount int64
|
||||||
|
HenHouseAvg float64
|
||||||
|
HenHouseCount int64
|
||||||
|
EggWeightAvg float64
|
||||||
|
EggWeightCount int64
|
||||||
|
EggMassAvg float64
|
||||||
|
EggMassCount int64
|
||||||
|
FeedIntakeAvg float64
|
||||||
|
FeedIntakeCount int64
|
||||||
|
FcrAvg float64
|
||||||
|
FcrCount int64
|
||||||
|
CumDepletionRateAvg float64
|
||||||
|
CumDepletionRateCount int64
|
||||||
|
}
|
||||||
|
|
||||||
func NewRecordingRepository(db *gorm.DB) RecordingRepository {
|
func NewRecordingRepository(db *gorm.DB) RecordingRepository {
|
||||||
return &RecordingRepositoryImpl{
|
return &RecordingRepositoryImpl{
|
||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.Recording](db),
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.Recording](db),
|
||||||
@@ -442,6 +460,67 @@ func (r *RecordingRepositoryImpl) GetTotalEggProductionWeightByProjectFlockID(ct
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RecordingRepositoryImpl) GetAverageTargetMetricsByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint, includeTargets bool) (RecordingTargetAverages, error) {
|
||||||
|
var row struct {
|
||||||
|
HenDayTotal float64
|
||||||
|
HenHouseTotal float64
|
||||||
|
EggWeightTotal float64
|
||||||
|
EggMassTotal float64
|
||||||
|
FeedIntakeTotal float64
|
||||||
|
FcrTotal float64
|
||||||
|
CumDepletionRateTotal float64
|
||||||
|
TotalCount int64
|
||||||
|
}
|
||||||
|
|
||||||
|
selectParts := []string{
|
||||||
|
"COALESCE(SUM(feed_intake), 0) AS feed_intake_total",
|
||||||
|
"COALESCE(SUM(fcr_value), 0) AS fcr_total",
|
||||||
|
"COALESCE(SUM(cum_depletion_rate), 0) AS cum_depletion_rate_total",
|
||||||
|
"COUNT(*) AS total_count",
|
||||||
|
}
|
||||||
|
if includeTargets {
|
||||||
|
selectParts = append([]string{
|
||||||
|
"COALESCE(SUM(hen_day), 0) AS hen_day_total",
|
||||||
|
"COALESCE(SUM(hen_house), 0) AS hen_house_total",
|
||||||
|
"COALESCE(SUM(egg_weight), 0) AS egg_weight_total",
|
||||||
|
"COALESCE(SUM(egg_mass), 0) AS egg_mass_total",
|
||||||
|
}, selectParts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Table("recordings").
|
||||||
|
Select(strings.Join(selectParts, ", ")).
|
||||||
|
Where("project_flock_kandangs_id = ? AND deleted_at IS NULL", projectFlockKandangID).
|
||||||
|
Scan(&row).Error; err != nil {
|
||||||
|
return RecordingTargetAverages{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := RecordingTargetAverages{
|
||||||
|
FeedIntakeCount: row.TotalCount,
|
||||||
|
FcrCount: row.TotalCount,
|
||||||
|
CumDepletionRateCount: row.TotalCount,
|
||||||
|
}
|
||||||
|
if includeTargets {
|
||||||
|
result.HenDayCount = row.TotalCount
|
||||||
|
result.HenHouseCount = row.TotalCount
|
||||||
|
result.EggWeightCount = row.TotalCount
|
||||||
|
result.EggMassCount = row.TotalCount
|
||||||
|
}
|
||||||
|
if row.TotalCount > 0 {
|
||||||
|
if includeTargets {
|
||||||
|
result.HenDayAvg = row.HenDayTotal / float64(row.TotalCount)
|
||||||
|
result.HenHouseAvg = row.HenHouseTotal / float64(row.TotalCount)
|
||||||
|
result.EggWeightAvg = row.EggWeightTotal / float64(row.TotalCount)
|
||||||
|
result.EggMassAvg = row.EggMassTotal / float64(row.TotalCount)
|
||||||
|
}
|
||||||
|
result.FeedIntakeAvg = row.FeedIntakeTotal / float64(row.TotalCount)
|
||||||
|
result.FcrAvg = row.FcrTotal / float64(row.TotalCount)
|
||||||
|
result.CumDepletionRateAvg = row.CumDepletionRateTotal
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func nextRecordingDay(days []int) int {
|
func nextRecordingDay(days []int) int {
|
||||||
if len(days) == 0 {
|
if len(days) == 0 {
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -31,11 +31,9 @@ func NewDebtSupplierRepository(db *gorm.DB) DebtSupplierRepository {
|
|||||||
|
|
||||||
func resolveDebtSupplierDateColumn(filterBy string) string {
|
func resolveDebtSupplierDateColumn(filterBy string) string {
|
||||||
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
||||||
case "receive_date":
|
|
||||||
return "purchases.receive_date"
|
|
||||||
case "po_date":
|
case "po_date":
|
||||||
return "purchases.po_date"
|
return "purchases.po_date"
|
||||||
case "do_date", "received_date", "":
|
case "received_date", "":
|
||||||
return "purchase_items.received_date"
|
return "purchase_items.received_date"
|
||||||
default:
|
default:
|
||||||
return "purchase_items.received_date"
|
return "purchase_items.received_date"
|
||||||
@@ -130,7 +128,7 @@ func (r *debtSupplierRepositoryImpl) GetPurchasesBySuppliers(ctx context.Context
|
|||||||
Preload("Warehouse.Area").
|
Preload("Warehouse.Area").
|
||||||
Order("purchase_items.id ASC")
|
Order("purchase_items.id ASC")
|
||||||
|
|
||||||
if strings.EqualFold(strings.TrimSpace(filters.FilterBy), "do_date") || strings.EqualFold(strings.TrimSpace(filters.FilterBy), "received_date") || strings.TrimSpace(filters.FilterBy) == "" {
|
if strings.EqualFold(strings.TrimSpace(filters.FilterBy), "received_date") || strings.TrimSpace(filters.FilterBy) == "" {
|
||||||
if filters.StartDate != "" {
|
if filters.StartDate != "" {
|
||||||
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||||
db = db.Where("DATE(purchase_items.received_date) >= ?", dateFrom)
|
db = db.Where("DATE(purchase_items.received_date) >= ?", dateFrom)
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ func (r *productionResultRepositoryImpl) GetRecordingsByProjectFlockKandang(
|
|||||||
dataQuery := r.db.WithContext(ctx).
|
dataQuery := r.db.WithContext(ctx).
|
||||||
Model(&entity.Recording{}).
|
Model(&entity.Recording{}).
|
||||||
Where("project_flock_kandangs_id = ?", projectFlockKandangID).
|
Where("project_flock_kandangs_id = ?", projectFlockKandangID).
|
||||||
Preload("BodyWeights").
|
|
||||||
Preload("Eggs", func(db *gorm.DB) *gorm.DB {
|
Preload("Eggs", func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Select("recording_eggs.*, f.name AS product_flag_name").
|
return db.Select("recording_eggs.*, f.name AS product_flag_name").
|
||||||
Joins("LEFT JOIN product_warehouses pw ON pw.id = recording_eggs.product_warehouse_id").
|
Joins("LEFT JOIN product_warehouses pw ON pw.id = recording_eggs.product_warehouse_id").
|
||||||
|
|||||||
@@ -642,7 +642,7 @@ func (s *repportService) GetPurchaseSupplier(c *fiber.Ctx, params *validation.Pu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error) {
|
func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error) {
|
||||||
if params.FilterBy == "" || strings.EqualFold(strings.TrimSpace(params.FilterBy), "do_date") {
|
if params.FilterBy == "" {
|
||||||
params.FilterBy = "received_date"
|
params.FilterBy = "received_date"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,25 +681,8 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
}
|
}
|
||||||
|
|
||||||
purchasesBySupplier := make(map[uint][]entity.Purchase, len(supplierIDs))
|
purchasesBySupplier := make(map[uint][]entity.Purchase, len(supplierIDs))
|
||||||
references := make([]string, 0)
|
|
||||||
seenRefs := make(map[string]struct{})
|
|
||||||
for _, purchase := range purchases {
|
for _, purchase := range purchases {
|
||||||
supplierID := purchase.SupplierId
|
purchasesBySupplier[purchase.SupplierId] = append(purchasesBySupplier[purchase.SupplierId], purchase)
|
||||||
purchasesBySupplier[supplierID] = append(purchasesBySupplier[supplierID], purchase)
|
|
||||||
|
|
||||||
reference := purchase.PrNumber
|
|
||||||
if purchase.PoNumber != nil && strings.TrimSpace(*purchase.PoNumber) != "" {
|
|
||||||
reference = *purchase.PoNumber
|
|
||||||
}
|
|
||||||
if _, exists := seenRefs[reference]; !exists {
|
|
||||||
seenRefs[reference] = struct{}{}
|
|
||||||
references = append(references, reference)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentTotals, err := s.DebtSupplierRepo.GetPaymentTotalsByReferences(c.Context(), supplierIDs, references)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentsBySupplier := make(map[uint][]entity.Payment, len(supplierIDs))
|
paymentsBySupplier := make(map[uint][]entity.Payment, len(supplierIDs))
|
||||||
@@ -724,6 +707,14 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
now := time.Now().In(location)
|
now := time.Now().In(location)
|
||||||
|
|
||||||
result := make([]dto.DebtSupplierDTO, 0, len(supplierIDs))
|
result := make([]dto.DebtSupplierDTO, 0, len(supplierIDs))
|
||||||
|
type debtSupplierRowItem struct {
|
||||||
|
Row dto.DebtSupplierRowDTO
|
||||||
|
SortTime time.Time
|
||||||
|
Order int
|
||||||
|
DeltaBalance float64
|
||||||
|
CountTotals bool
|
||||||
|
}
|
||||||
|
|
||||||
for _, supplierID := range supplierIDs {
|
for _, supplierID := range supplierIDs {
|
||||||
supplier, exists := supplierMap[supplierID]
|
supplier, exists := supplierMap[supplierID]
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -731,23 +722,13 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialBalance := initialPaymentTotals[supplierID] - initialPurchaseTotals[supplierID]
|
initialBalance := initialPaymentTotals[supplierID] - initialPurchaseTotals[supplierID]
|
||||||
|
|
||||||
items := purchasesBySupplier[supplierID]
|
items := purchasesBySupplier[supplierID]
|
||||||
paymentItems := paymentsBySupplier[supplierID]
|
paymentItems := paymentsBySupplier[supplierID]
|
||||||
rows := make([]dto.DebtSupplierRowDTO, 0, len(items)+len(paymentItems))
|
|
||||||
total := dto.DebtSupplierTotalDTO{}
|
total := dto.DebtSupplierTotalDTO{}
|
||||||
|
|
||||||
type debtSupplierRowItem struct {
|
|
||||||
Row dto.DebtSupplierRowDTO
|
|
||||||
SortTime time.Time
|
|
||||||
Order int
|
|
||||||
DeltaBalance float64
|
|
||||||
CountTotals bool
|
|
||||||
}
|
|
||||||
|
|
||||||
combinedRows := make([]debtSupplierRowItem, 0, len(items)+len(paymentItems))
|
combinedRows := make([]debtSupplierRowItem, 0, len(items)+len(paymentItems))
|
||||||
for _, purchase := range items {
|
for _, purchase := range items {
|
||||||
row := buildDebtSupplierRow(purchase, paymentTotals, now, location)
|
row := buildDebtSupplierRow(purchase, now, location)
|
||||||
sortTime := resolveDebtSupplierSortTime(purchase, params.FilterBy, location)
|
sortTime := resolveDebtSupplierSortTime(purchase, params.FilterBy, location)
|
||||||
combinedRows = append(combinedRows, debtSupplierRowItem{
|
combinedRows = append(combinedRows, debtSupplierRowItem{
|
||||||
Row: row,
|
Row: row,
|
||||||
@@ -780,6 +761,7 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
balance := initialBalance
|
balance := initialBalance
|
||||||
for i := range combinedRows {
|
for i := range combinedRows {
|
||||||
balance += combinedRows[i].DeltaBalance
|
balance += combinedRows[i].DeltaBalance
|
||||||
|
combinedRows[i].Row.DebtPrice = balance
|
||||||
combinedRows[i].Row.Balance = balance
|
combinedRows[i].Row.Balance = balance
|
||||||
|
|
||||||
if combinedRows[i].CountTotals {
|
if combinedRows[i].CountTotals {
|
||||||
@@ -788,13 +770,13 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
total.Aging = row.Aging
|
total.Aging = row.Aging
|
||||||
}
|
}
|
||||||
total.TotalPrice += row.TotalPrice
|
total.TotalPrice += row.TotalPrice
|
||||||
total.PaymentPrice += row.PaymentPrice
|
|
||||||
total.DebtPrice += row.DebtPrice
|
|
||||||
} else {
|
} else {
|
||||||
combinedRows[i].Row.DebtPrice = balance
|
total.PaymentPrice += combinedRows[i].Row.PaymentPrice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
total.DebtPrice = balance
|
||||||
|
|
||||||
|
rows := make([]dto.DebtSupplierRowDTO, 0, len(combinedRows))
|
||||||
sortDesc := strings.EqualFold(params.SortOrder, "desc")
|
sortDesc := strings.EqualFold(params.SortOrder, "desc")
|
||||||
if sortDesc {
|
if sortDesc {
|
||||||
for i := len(combinedRows) - 1; i >= 0; i-- {
|
for i := len(combinedRows) - 1; i >= 0; i-- {
|
||||||
@@ -823,18 +805,13 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
return result, totalSuppliers, nil
|
return result, totalSuppliers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildDebtSupplierRow(purchase entity.Purchase, paymentTotals map[string]float64, now time.Time, loc *time.Location) dto.DebtSupplierRowDTO {
|
func buildDebtSupplierRow(purchase entity.Purchase, now time.Time, loc *time.Location) dto.DebtSupplierRowDTO {
|
||||||
prNumber := purchase.PrNumber
|
prNumber := purchase.PrNumber
|
||||||
poNumber := ""
|
poNumber := ""
|
||||||
if purchase.PoNumber != nil {
|
if purchase.PoNumber != nil {
|
||||||
poNumber = *purchase.PoNumber
|
poNumber = *purchase.PoNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
reference := prNumber
|
|
||||||
if strings.TrimSpace(poNumber) != "" {
|
|
||||||
reference = poNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
prDate := purchase.CreatedAt.In(loc)
|
prDate := purchase.CreatedAt.In(loc)
|
||||||
startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc)
|
startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc)
|
||||||
endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||||
@@ -877,9 +854,6 @@ func buildDebtSupplierRow(purchase entity.Purchase, paymentTotals map[string]flo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentPrice := paymentTotals[reference]
|
|
||||||
debtPrice := paymentPrice - totalPrice
|
|
||||||
|
|
||||||
dueDate := ""
|
dueDate := ""
|
||||||
dueStatus := "-"
|
dueStatus := "-"
|
||||||
if purchase.DueDate != nil && !purchase.DueDate.IsZero() {
|
if purchase.DueDate != nil && !purchase.DueDate.IsZero() {
|
||||||
@@ -893,10 +867,6 @@ func buildDebtSupplierRow(purchase entity.Purchase, paymentTotals map[string]flo
|
|||||||
}
|
}
|
||||||
|
|
||||||
status := "Belum Lunas"
|
status := "Belum Lunas"
|
||||||
if debtPrice >= 0 {
|
|
||||||
status = "Lunas"
|
|
||||||
}
|
|
||||||
|
|
||||||
poDate := ""
|
poDate := ""
|
||||||
if purchase.PoDate != nil && !purchase.PoDate.IsZero() {
|
if purchase.PoDate != nil && !purchase.PoDate.IsZero() {
|
||||||
poDate = purchase.PoDate.In(loc).Format("2006-01-02")
|
poDate = purchase.PoDate.In(loc).Format("2006-01-02")
|
||||||
@@ -913,10 +883,11 @@ func buildDebtSupplierRow(purchase entity.Purchase, paymentTotals map[string]flo
|
|||||||
DueDate: dueDate,
|
DueDate: dueDate,
|
||||||
DueStatus: dueStatus,
|
DueStatus: dueStatus,
|
||||||
TotalPrice: totalPrice,
|
TotalPrice: totalPrice,
|
||||||
PaymentPrice: paymentPrice,
|
PaymentPrice: 0,
|
||||||
DebtPrice: debtPrice,
|
DebtPrice: 0,
|
||||||
Status: status,
|
Status: status,
|
||||||
TravelNumber: travelNumber,
|
TravelNumber: travelNumber,
|
||||||
|
Balance: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,32 +917,30 @@ func buildDebtSupplierPaymentRow(payment entity.Payment, loc *time.Location) dto
|
|||||||
DebtPrice: 0,
|
DebtPrice: 0,
|
||||||
Status: "Pembayaran",
|
Status: "Pembayaran",
|
||||||
TravelNumber: "-",
|
TravelNumber: "-",
|
||||||
|
Balance: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveDebtSupplierSortTime(purchase entity.Purchase, filterBy string, loc *time.Location) time.Time {
|
func resolveDebtSupplierSortTime(purchase entity.Purchase, filterBy string, loc *time.Location) time.Time {
|
||||||
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
if strings.EqualFold(strings.TrimSpace(filterBy), "po_date") {
|
||||||
case "po_date":
|
|
||||||
if purchase.PoDate != nil && !purchase.PoDate.IsZero() {
|
if purchase.PoDate != nil && !purchase.PoDate.IsZero() {
|
||||||
return purchase.PoDate.In(loc)
|
return purchase.PoDate.In(loc)
|
||||||
}
|
}
|
||||||
case "pr_date":
|
}
|
||||||
return purchase.CreatedAt.In(loc)
|
|
||||||
default:
|
earliest := time.Time{}
|
||||||
earliest := time.Time{}
|
for _, item := range purchase.Items {
|
||||||
for _, item := range purchase.Items {
|
if item.ReceivedDate == nil || item.ReceivedDate.IsZero() {
|
||||||
if item.ReceivedDate == nil || item.ReceivedDate.IsZero() {
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
received := item.ReceivedDate.In(loc)
|
|
||||||
if earliest.IsZero() || received.Before(earliest) {
|
|
||||||
earliest = received
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !earliest.IsZero() {
|
received := item.ReceivedDate.In(loc)
|
||||||
return earliest
|
if earliest.IsZero() || received.Before(earliest) {
|
||||||
|
earliest = received
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !earliest.IsZero() {
|
||||||
|
return earliest
|
||||||
|
}
|
||||||
|
|
||||||
return purchase.CreatedAt.In(loc)
|
return purchase.CreatedAt.In(loc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ type DebtSupplierQuery struct {
|
|||||||
SupplierIDs []int64 `query:"-" validate:"omitempty,dive,gt=0"`
|
SupplierIDs []int64 `query:"-" validate:"omitempty,dive,gt=0"`
|
||||||
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
FilterBy string `query:"filter_by" validate:"omitempty,oneof=received_date po_date pr_date do_date"`
|
FilterBy string `query:"filter_by" validate:"omitempty,oneof=received_date po_date"`
|
||||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user