diff --git a/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.down.sql b/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.down.sql new file mode 100644 index 00000000..42991241 --- /dev/null +++ b/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.down.sql @@ -0,0 +1,47 @@ +BEGIN; + +DO $$ +DECLARE + fallback_fcr_id BIGINT; +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flocks' + AND column_name = 'fcr_id' + ) THEN + ALTER TABLE project_flocks + ADD COLUMN fcr_id BIGINT; + END IF; + + SELECT id INTO fallback_fcr_id + FROM fcrs + ORDER BY id ASC + LIMIT 1; + + IF fallback_fcr_id IS NOT NULL THEN + UPDATE project_flocks + SET fcr_id = fallback_fcr_id + WHERE fcr_id IS NULL; + + ALTER TABLE project_flocks + ALTER COLUMN fcr_id SET NOT NULL; + END IF; + + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'project_flocks_fcr_id_fkey' + ) THEN + ALTER TABLE project_flocks + DROP CONSTRAINT project_flocks_fcr_id_fkey; + END IF; + + ALTER TABLE project_flocks + ADD CONSTRAINT project_flocks_fcr_id_fkey + FOREIGN KEY (fcr_id) REFERENCES fcrs(id) + ON DELETE RESTRICT ON UPDATE CASCADE; +END $$; + +COMMIT; diff --git a/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.up.sql b/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.up.sql new file mode 100644 index 00000000..e34e7d92 --- /dev/null +++ b/internal/database/migrations/20260203063959_remove_fcr_id_project_flock.up.sql @@ -0,0 +1,26 @@ +BEGIN; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flocks' + AND column_name = 'fcr_id' + ) THEN + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'project_flocks_fcr_id_fkey' + ) THEN + ALTER TABLE project_flocks + DROP CONSTRAINT project_flocks_fcr_id_fkey; + END IF; + + ALTER TABLE project_flocks + DROP COLUMN fcr_id; + END IF; +END $$; + +COMMIT; diff --git a/internal/entities/projectflock.go b/internal/entities/projectflock.go index 7243c9c4..80d7f886 100644 --- a/internal/entities/projectflock.go +++ b/internal/entities/projectflock.go @@ -11,7 +11,6 @@ type ProjectFlock struct { FlockName string `gorm:"type:varchar(255);not null;uniqueIndex"` AreaId uint `gorm:"not null"` Category string `gorm:"type:varchar(20);not null"` - FcrId uint `gorm:"not null"` ProductionStandardId uint `gorm:"column:production_standard_id"` LocationId uint `gorm:"not null"` CreatedBy uint `gorm:"not null"` @@ -20,7 +19,6 @@ type ProjectFlock struct { DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` Area Area `gorm:"foreignKey:AreaId;references:Id"` - Fcr Fcr `gorm:"foreignKey:FcrId;references:Id"` ProductionStandard ProductionStandard `gorm:"foreignKey:ProductionStandardId;references:Id"` Location Location `gorm:"foreignKey:LocationId;references:Id"` CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"` diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index cd5ce2da..6ec09858 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -24,7 +24,6 @@ type ClosingRepository interface { SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error) SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error) SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error) - GetFcrStandardsByFcrID(ctx context.Context, fcrID uint) ([]entity.FcrStandard, error) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) FetchSapronakIncoming(ctx context.Context, kandangID uint) ([]SapronakIncomingRow, error) FetchSapronakIncomingDetails(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, error) @@ -393,22 +392,6 @@ func (r *ClosingRepositoryImpl) SumRecordingEggQtyByProjectFlockKandangIDsAndFla return agg.TotalQty, nil } -func (r *ClosingRepositoryImpl) GetFcrStandardsByFcrID(ctx context.Context, fcrID uint) ([]entity.FcrStandard, error) { - if fcrID == 0 { - return []entity.FcrStandard{}, nil - } - - var standards []entity.FcrStandard - if err := r.DB().WithContext(ctx). - Where("fcr_id = ?", fcrID). - Order("weight ASC"). - Find(&standards).Error; err != nil { - return nil, err - } - - return standards, nil -} - func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) { db := r.DB().WithContext(ctx) diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index 923a2b1c..71bfcdec 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -836,14 +836,6 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint finalPopulation := population - claimCulling - var standards []entity.FcrStandard - if project.FcrId > 0 { - standards, err = s.Repository.GetFcrStandardsByFcrID(c.Context(), project.FcrId) - if err != nil { - s.Log.Errorf("Failed to fetch FCR standards for project flock %d: %+v", projectFlockID, err) - return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch FCR standard data") - } - } age, err := s.calculateAverageSalesAge(c.Context(), projectFlockID, kandangID) if err != nil { s.Log.Errorf("Failed to calculate sales age for project flock %d: %+v", projectFlockID, err) @@ -893,7 +885,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint chickenDepletion = 0 } - chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards) +chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age) if fcrActFromRecording != nil { chickenPerformance.FcrAct = *fcrActFromRecording } @@ -943,7 +935,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint eggDepletion = 0 } - eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards) + eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age) if fcrActFromRecording != nil { eggPerf.FcrAct = *fcrActFromRecording } @@ -1001,10 +993,10 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint performance.EggMass = eggMass } } - performance.DeffFcr = performance.FcrStd - performance.FcrAct if productionStandardDetail != nil { if productionStandardDetail.StandardFCR != nil { performance.FcrStd = *productionStandardDetail.StandardFCR + performance.DeffFcr = performance.FcrStd - performance.FcrAct } if !isGrowing { if productionStandardDetail.TargetHenDayProduction != nil { @@ -1091,8 +1083,8 @@ func (s closingService) determineProductionWeek(ctx context.Context, projectFloc return week, nil } -func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO { - mortalityStd, fcrStd := closestFcrValues(standards, averageWeight) +func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64) dto.ClosingPerformanceDTO { + mortalityStd, fcrStd := 0.0, 0.0 fcrAct := 0.0 if totalWeight > 0 { @@ -1124,21 +1116,3 @@ func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopul AwgAct: awg, } } - -func closestFcrValues(standards []entity.FcrStandard, averageWeight float64) (float64, float64) { - if len(standards) == 0 || averageWeight <= 0 { - return 0, 0 - } - - closest := standards[0] - minDiff := math.Abs(closest.Weight - averageWeight) - for _, std := range standards[1:] { - diff := math.Abs(std.Weight - averageWeight) - if diff < minDiff { - minDiff = diff - closest = std - } - } - - return closest.Mortality, closest.FcrNumber -} diff --git a/internal/modules/dashboards/repositories/dashboard_stats.repository.go b/internal/modules/dashboards/repositories/dashboard_stats.repository.go index 3c04f9a0..363e6aa5 100644 --- a/internal/modules/dashboards/repositories/dashboard_stats.repository.go +++ b/internal/modules/dashboards/repositories/dashboard_stats.repository.go @@ -444,30 +444,6 @@ func (r *DashboardRepositoryImpl) standardIDSubquery(filters *validation.Dashboa return db } -func (r *DashboardRepositoryImpl) standardSourceSubquery(filters *validation.DashboardFilter) *gorm.DB { - db := r.DB(). - Table("project_flocks AS pf"). - Select("DISTINCT pf.production_standard_id, pf.fcr_id"). - Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id"). - Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id"). - Where("pf.production_standard_id > 0"). - Where("pf.fcr_id > 0") - - if filters != nil { - if len(filters.FlockIds) > 0 { - db = db.Where("pf.id IN ?", filters.FlockIds) - } - if len(filters.KandangIds) > 0 { - db = db.Where("k.id IN ?", filters.KandangIds) - } - if len(filters.LokasiIds) > 0 { - db = db.Where("k.location_id IN ?", filters.LokasiIds) - } - } - - return db -} - func (r *DashboardRepositoryImpl) GetComparisonSeries(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonSeries, error) { seriesExpr, labelExpr, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType) if err != nil { diff --git a/internal/modules/production/chickins/dto/chickin.dto.go b/internal/modules/production/chickins/dto/chickin.dto.go index d53b9491..8a4b0d09 100644 --- a/internal/modules/production/chickins/dto/chickin.dto.go +++ b/internal/modules/production/chickins/dto/chickin.dto.go @@ -5,7 +5,6 @@ import ( entity "gitlab.com/mbugroup/lti-api.git/internal/entities" areaRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" - fcrRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto" flockRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto" kandangRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" @@ -13,6 +12,7 @@ import ( warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils" userRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" + "gitlab.com/mbugroup/lti-api.git/internal/utils" ) // === DTO Structs (ordered) === @@ -40,7 +40,7 @@ type ProjectFlockDTO struct { Category string `json:"category"` Flock *flockRelationDTO.FlockRelationDTO `json:"flock"` Area *areaRelationDTO.AreaRelationDTO `json:"area"` - Fcr *fcrRelationDTO.FcrRelationDTO `json:"fcr"` + StandardFcr *float64 `json:"standard_fcr"` Location *locationRelationDTO.LocationRelationDTO `json:"location"` } @@ -97,10 +97,6 @@ func ToAreaDTO(e entity.Area) areaRelationDTO.AreaRelationDTO { return areaRelationDTO.ToAreaRelationDTO(e) } -func ToFcrDTO(e entity.Fcr) fcrRelationDTO.FcrRelationDTO { - return fcrRelationDTO.ToFcrRelationDTO(e) -} - func ToLocationDTO(e entity.Location) locationRelationDTO.LocationRelationDTO { return locationRelationDTO.ToLocationRelationDTO(e) } @@ -121,11 +117,6 @@ func ToProjectFlockDTO(pfk entity.ProjectFlockKandang) ProjectFlockDTO { mapped := areaRelationDTO.ToAreaRelationDTO(e.Area) area = &mapped } - var fcr *fcrRelationDTO.FcrRelationDTO - if e.Fcr.Id != 0 { - mapped := fcrRelationDTO.ToFcrRelationDTO(e.Fcr) - fcr = &mapped - } var location *locationRelationDTO.LocationRelationDTO if e.Location.Id != 0 { mapped := locationRelationDTO.ToLocationRelationDTO(e.Location) @@ -137,7 +128,7 @@ func ToProjectFlockDTO(pfk entity.ProjectFlockKandang) ProjectFlockDTO { Category: e.Category, Flock: flock, Area: area, - Fcr: fcr, + StandardFcr: resolveProjectFlockStandardFcr(e), Location: location, } } @@ -222,6 +213,22 @@ func ToChickinListDTOs(e []entity.ProjectChickin) []ChickinListDTO { return result } +func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 { + if e.ProductionStandard.Id == 0 || len(e.ProductionStandard.ProductionStandardDetails) == 0 { + return nil + } + week := 1 + if e.Category == string(utils.ProjectFlockCategoryLaying) { + week = 18 + } + for _, detail := range e.ProductionStandard.ProductionStandardDetails { + if detail.Week == week && detail.StandardFCR != nil { + return detail.StandardFCR + } + } + return nil +} + func ToChickinSimpleDTOs(e []entity.ProjectChickin) []ChickinSimpleDTO { result := make([]ChickinSimpleDTO, len(e)) for i, r := range e { diff --git a/internal/modules/production/chickins/services/chickin.service.go b/internal/modules/production/chickins/services/chickin.service.go index 971ee072..a011c579 100644 --- a/internal/modules/production/chickins/services/chickin.service.go +++ b/internal/modules/production/chickins/services/chickin.service.go @@ -81,7 +81,7 @@ func (s chickinService) withRelations(db *gorm.DB) *gorm.DB { Preload("ProjectFlockKandang.Kandang.Pic"). Preload("ProjectFlockKandang.ProjectFlock"). Preload("ProjectFlockKandang.ProjectFlock.Area"). - Preload("ProjectFlockKandang.ProjectFlock.Fcr"). + Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard.ProductionStandardDetails"). Preload("ProjectFlockKandang.ProjectFlock.Location"). Preload("ProjectFlockKandang.ProjectFlock.Location.Area") diff --git a/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go b/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go index c8faf761..8231a551 100644 --- a/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go +++ b/internal/modules/production/project-flock-kandangs/dto/project_flock_kandang.dto.go @@ -8,7 +8,6 @@ import ( approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto" productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto" areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" - fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto" kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto" @@ -31,7 +30,7 @@ type ProjectFlockDTO struct { projectFlockDTO.ProjectFlockRelationDTO Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` Category string `json:"category"` - Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` + StandardFcr *float64 `json:"standard_fcr,omitempty"` ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` @@ -86,7 +85,7 @@ func toProjectFlockDTO(pf *projectFlockDTO.ProjectFlockListDTO) *ProjectFlockDTO ProjectFlockRelationDTO: pf.ProjectFlockRelationDTO, Area: pf.Area, Category: pf.Category, - Fcr: pf.Fcr, + StandardFcr: pf.StandardFcr, ProductionStandard: pf.ProductionStandard, Location: pf.Location, CreatedUser: pf.CreatedUser, diff --git a/internal/modules/production/project_flocks/dto/projectflock.dto.go b/internal/modules/production/project_flocks/dto/projectflock.dto.go index 504d439c..e7240b49 100644 --- a/internal/modules/production/project_flocks/dto/projectflock.dto.go +++ b/internal/modules/production/project_flocks/dto/projectflock.dto.go @@ -6,7 +6,6 @@ import ( entity "gitlab.com/mbugroup/lti-api.git/internal/entities" approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto" areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" - fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto" kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" nonstockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/dto" @@ -28,7 +27,7 @@ type ProjectFlockListDTO struct { ProjectFlockRelationDTO Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` Category string `json:"category"` - Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` + StandardFcr *float64 `json:"standard_fcr,omitempty"` ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"` @@ -99,12 +98,6 @@ func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectF areaSummary = &mapped } - var fcrSummary *fcrDTO.FcrRelationDTO - if e.Fcr.Id != 0 { - mapped := fcrDTO.ToFcrRelationDTO(e.Fcr) - fcrSummary = &mapped - } - var productionStandardSummary *productionStandardDTO.ProductionStandardRelationDTO if e.ProductionStandard.Id != 0 { mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProductionStandard) @@ -129,7 +122,7 @@ func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectF Kandangs: kandangSummaries, ProjectBudgets: ToProjectBudgetDTOs(e.Budgets), Category: e.Category, - Fcr: fcrSummary, + StandardFcr: resolveProjectFlockStandardFcr(e), ProductionStandard: productionStandardSummary, Location: locationSummary, CreatedAt: e.CreatedAt, @@ -204,6 +197,22 @@ func createProjectFlockRelationDTO(e entity.ProjectFlock, period int) ProjectFlo } } +func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 { + if e.ProductionStandard.Id == 0 || len(e.ProductionStandard.ProductionStandardDetails) == 0 { + return nil + } + week := 1 + if e.Category == string(utils.ProjectFlockCategoryLaying) { + week = 18 + } + for _, detail := range e.ProductionStandard.ProductionStandardDetails { + if detail.Week == week && detail.StandardFCR != nil { + return detail.StandardFCR + } + } + return nil +} + func ToProjectBudgetDTO(e entity.ProjectBudget) ProjectBudgetDTO { var nonstockRef *nonstockDTO.NonstockRelationDTO if e.Nonstock != nil && e.Nonstock.Id != 0 { diff --git a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go index 39abfe62..5c055a1d 100644 --- a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go +++ b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go @@ -5,7 +5,6 @@ import ( entity "gitlab.com/mbugroup/lti-api.git/internal/entities" areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" - fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto" kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto" @@ -22,7 +21,7 @@ type ProjectFlockWithPivotDTO struct { ProjectFlockRelationDTO Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` Category string `json:"category"` - Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` + StandardFcr *float64 `json:"standard_fcr,omitempty"` ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` ProductionStandardId uint `json:"production_standard_id"` Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` @@ -67,10 +66,6 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD mapped := areaDTO.ToAreaRelationDTO(e.ProjectFlock.Area) pfLocal.Area = &mapped } - if e.ProjectFlock.Fcr.Id != 0 { - mapped := fcrDTO.ToFcrRelationDTO(e.ProjectFlock.Fcr) - pfLocal.Fcr = &mapped - } if e.ProjectFlock.ProductionStandard.Id != 0 { mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProjectFlock.ProductionStandard) pfLocal.ProductionStandard = &mapped @@ -83,6 +78,7 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD mapped := userDTO.ToUserRelationDTO(e.ProjectFlock.CreatedUser) pfLocal.CreatedUser = &mapped } + pfLocal.StandardFcr = resolveProjectFlockStandardFcr(e.ProjectFlock) for _, k := range e.ProjectFlock.Kandangs { kb := kandangDTO.ToKandangRelationDTO(k) diff --git a/internal/modules/production/project_flocks/repositories/projectflock.repository.go b/internal/modules/production/project_flocks/repositories/projectflock.repository.go index 346f2176..cd7aaba7 100644 --- a/internal/modules/production/project_flocks/repositories/projectflock.repository.go +++ b/internal/modules/production/project_flocks/repositories/projectflock.repository.go @@ -23,7 +23,6 @@ type ProjectflockRepository interface { GetActiveByLocationID(ctx context.Context, locationID uint64) ([]entity.ProjectFlock, error) IdExists(ctx context.Context, id uint) (bool, error) AreaExists(ctx context.Context, id uint) (bool, error) - FcrExists(ctx context.Context, id uint) (bool, error) ProductionStandardExists(ctx context.Context, id uint) (bool, error) LocationExists(ctx context.Context, id uint) (bool, error) } @@ -67,8 +66,8 @@ func (r *ProjectflockRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm return db. Preload("CreatedUser"). Preload("Area"). - Preload("Fcr"). Preload("ProductionStandard"). + Preload("ProductionStandard.ProductionStandardDetails"). Preload("Location"). Preload("Kandangs"). Preload("KandangHistory"). @@ -134,14 +133,12 @@ func (r *ProjectflockRepositoryImpl) applySearchFilters(db *gorm.DB, rawSearch s likeQuery := "%" + normalized + "%" return db. Joins("LEFT JOIN areas ON areas.id = project_flocks.area_id"). - Joins("LEFT JOIN fcrs ON fcrs.id = project_flocks.fcr_id"). Joins("LEFT JOIN production_standards ON production_standards.id = project_flocks.production_standard_id"). Joins("LEFT JOIN locations ON locations.id = project_flocks.location_id"). Joins("LEFT JOIN users AS created_users ON created_users.id = project_flocks.created_by"). Where(` LOWER(areas.name) LIKE ? OR LOWER(project_flocks.category) LIKE ? - OR LOWER(fcrs.name) LIKE ? OR LOWER(production_standards.name) LIKE ? OR LOWER(locations.name) LIKE ? OR LOWER(locations.address) LIKE ? @@ -172,7 +169,6 @@ func (r *ProjectflockRepositoryImpl) applySearchFilters(db *gorm.DB, rawSearch s likeQuery, likeQuery, likeQuery, - likeQuery, ) } @@ -184,10 +180,6 @@ func (r *ProjectflockRepositoryImpl) AreaExists(ctx context.Context, id uint) (b return repository.Exists[entity.Area](ctx, r.DB(), id) } -func (r *ProjectflockRepositoryImpl) FcrExists(ctx context.Context, id uint) (bool, error) { - return repository.Exists[entity.Fcr](ctx, r.DB(), id) -} - func (r *ProjectflockRepositoryImpl) ProductionStandardExists(ctx context.Context, id uint) (bool, error) { return repository.Exists[entity.ProductionStandard](ctx, r.DB(), id) } diff --git a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go index 9f5bb0e2..002c2c58 100644 --- a/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go +++ b/internal/modules/production/project_flocks/repositories/projectflock_kandang.repository.go @@ -117,10 +117,10 @@ func (r *projectFlockKandangRepositoryImpl) GetAllWithFilters(ctx context.Contex Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\""). Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\""). Preload("ProjectFlock"). - Preload("ProjectFlock.Fcr"). Preload("ProjectFlock.Area"). Preload("ProjectFlock.Location"). Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails"). Preload("ProjectFlock.Kandangs"). Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). @@ -208,10 +208,10 @@ func (r *projectFlockKandangRepositoryImpl) GetAllWithFiltersScoped(ctx context. Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\""). Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\""). Preload("ProjectFlock"). - Preload("ProjectFlock.Fcr"). Preload("ProjectFlock.Area"). Preload("ProjectFlock.Location"). Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails"). Preload("ProjectFlock.Kandangs"). Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). @@ -324,10 +324,10 @@ func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint record := new(entity.ProjectFlockKandang) if err := r.db.WithContext(ctx). Preload("ProjectFlock"). - Preload("ProjectFlock.Fcr"). Preload("ProjectFlock.Area"). Preload("ProjectFlock.Location"). Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails"). Preload("ProjectFlock.Kandangs"). Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). @@ -347,10 +347,10 @@ func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx cont if err := r.db.WithContext(ctx). Where("project_flock_id = ? AND kandang_id = ?", projectFlockID, kandangID). Preload("ProjectFlock"). - Preload("ProjectFlock.Fcr"). Preload("ProjectFlock.Area"). Preload("ProjectFlock.Location"). Preload("ProjectFlock.CreatedUser"). + Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails"). Preload("ProjectFlock.Kandangs"). Preload("ProjectFlock.KandangHistory"). Preload("Kandang"). diff --git a/internal/modules/production/project_flocks/services/projectflock.service.go b/internal/modules/production/project_flocks/services/projectflock.service.go index 96e4b6b0..224f43bf 100644 --- a/internal/modules/production/project_flocks/services/projectflock.service.go +++ b/internal/modules/production/project_flocks/services/projectflock.service.go @@ -282,7 +282,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (* if err := commonSvc.EnsureRelations(c.Context(), commonSvc.RelationCheck{Name: "Area", ID: &req.AreaId, Exists: s.Repository.AreaExists}, - commonSvc.RelationCheck{Name: "FCR", ID: &req.FcrId, Exists: s.Repository.FcrExists}, commonSvc.RelationCheck{Name: "Production Standard", ID: &req.ProductionStandardId, Exists: s.Repository.ProductionStandardExists}, commonSvc.RelationCheck{Name: "Location", ID: &req.LocationId, Exists: s.Repository.LocationExists}, ); err != nil { @@ -334,7 +333,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (* createBody := &entity.ProjectFlock{ AreaId: req.AreaId, Category: cat, - FcrId: req.FcrId, ProductionStandardId: req.ProductionStandardId, LocationId: req.LocationId, CreatedBy: actorID, diff --git a/internal/modules/production/project_flocks/validations/projectflock.validation.go b/internal/modules/production/project_flocks/validations/projectflock.validation.go index 1fb48abe..ca347d47 100644 --- a/internal/modules/production/project_flocks/validations/projectflock.validation.go +++ b/internal/modules/production/project_flocks/validations/projectflock.validation.go @@ -4,7 +4,6 @@ type Create struct { FlockName string `json:"flock_name" validate:"required_strict"` AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"` Category string `json:"category" validate:"required_strict"` - FcrId uint `json:"fcr_id" validate:"required_strict,number,gt=0"` ProductionStandardId uint `json:"production_standard_id" validate:"required_strict,number,gt=0"` LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"` KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"` diff --git a/internal/modules/production/recordings/dto/recording.dto.go b/internal/modules/production/recordings/dto/recording.dto.go index 191b9676..ec2f3657 100644 --- a/internal/modules/production/recordings/dto/recording.dto.go +++ b/internal/modules/production/recordings/dto/recording.dto.go @@ -280,10 +280,10 @@ func toRecordingProjectFlockDTO(e entity.Recording) RecordingProjectFlockDTO { } } - if pfk.ProjectFlock.Fcr.Id != 0 || e.StandardFcr != nil { + if pfk.ProjectFlock.ProductionStandard.Id != 0 || e.StandardFcr != nil { result.Fcr = &RecordingFcrDTO{ - Id: pfk.ProjectFlock.Fcr.Id, - Name: pfk.ProjectFlock.Fcr.Name, + Id: pfk.ProjectFlock.ProductionStandard.Id, + Name: pfk.ProjectFlock.ProductionStandard.Name, FcrStd: floatValue(e.StandardFcr), } } diff --git a/internal/modules/production/recordings/repositories/recording.repository.go b/internal/modules/production/recordings/repositories/recording.repository.go index ce4dc0df..b9867d2b 100644 --- a/internal/modules/production/recordings/repositories/recording.repository.go +++ b/internal/modules/production/recordings/repositories/recording.repository.go @@ -46,7 +46,6 @@ type RecordingRepository interface { GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error) GetCumulativeEggQtyByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error) - GetFcrStandardNumber(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error) GetTotalWeightProducedFromUniformityByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) GetTotalWeightProducedFromUniformityByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) @@ -92,7 +91,7 @@ func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB { Preload("ProjectFlockKandang.Kandang.Location"). Preload("ProjectFlockKandang.ProjectFlock"). Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard"). - Preload("ProjectFlockKandang.ProjectFlock.Fcr"). + // Preload("ProjectFlockKandang.ProjectFlock.Fcr"). Preload("Depletions"). Preload("Depletions.ProductWarehouse"). Preload("Depletions.ProductWarehouse.Product"). @@ -448,34 +447,6 @@ func (r *RecordingRepositoryImpl) GetCumulativeEggQtyByProjectFlockKandang( Scan(&result).Error return result, err } - -func (r *RecordingRepositoryImpl) GetFcrStandardNumber(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error) { - if fcrId == 0 || currentWeightKg <= 0 { - return 0, false, nil - } - - var standard entity.FcrStandard - err := tx. - Where("fcr_id = ? AND weight >= ?", fcrId, currentWeightKg). - Order("weight ASC"). - First(&standard).Error - - if errors.Is(err, gorm.ErrRecordNotFound) { - err = tx. - Where("fcr_id = ?", fcrId). - Order("weight DESC"). - First(&standard).Error - if errors.Is(err, gorm.ErrRecordNotFound) { - return 0, false, nil - } - } - if err != nil { - return 0, false, err - } - - return standard.FcrNumber, true, nil -} - func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) { // Body-weight tracking is removed; keep stub for report compatibility. return 0, 0, nil diff --git a/internal/modules/repports/services/repport.service.go b/internal/modules/repports/services/repport.service.go index d45cba62..d417642e 100644 --- a/internal/modules/repports/services/repport.service.go +++ b/internal/modules/repports/services/repport.service.go @@ -1352,10 +1352,12 @@ func buildDebtSupplierRow(purchase entity.Purchase, now time.Time, loc *time.Loc poNumber = *purchase.PoNumber } - prDate := purchase.CreatedAt.In(loc) - startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc) + startDate := resolveDebtSupplierReceivedDate(purchase, loc) endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc) - aging := int(endDate.Sub(startDate).Hours() / 24) + aging := 0 + if !startDate.IsZero() { + aging = int(endDate.Sub(startDate).Hours() / 24) + } totalPrice := 0.0 travelNumber := "-" @@ -1525,8 +1527,10 @@ func isDebtSupplierPaid(totalPrice, paymentTotal float64) bool { } func calculateDebtSupplierAging(purchase entity.Purchase, endDate time.Time, loc *time.Location) int { - prDate := purchase.CreatedAt.In(loc) - startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc) + startDate := resolveDebtSupplierReceivedDate(purchase, loc) + if startDate.IsZero() { + return 0 + } stopDate := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, loc) if stopDate.Before(startDate) { return 0 @@ -1534,6 +1538,23 @@ func calculateDebtSupplierAging(purchase entity.Purchase, endDate time.Time, loc return int(stopDate.Sub(startDate).Hours() / 24) } +func resolveDebtSupplierReceivedDate(purchase entity.Purchase, loc *time.Location) time.Time { + earliest := time.Time{} + for _, item := range purchase.Items { + if item.ReceivedDate == nil || item.ReceivedDate.IsZero() { + continue + } + received := item.ReceivedDate.In(loc) + if earliest.IsZero() || received.Before(earliest) { + earliest = received + } + } + if earliest.IsZero() { + return time.Time{} + } + return time.Date(earliest.Year(), earliest.Month(), earliest.Day(), 0, 0, 0, 0, loc) +} + func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error) { params, filters, err := s.parseHppPerKandangQuery(ctx) if err != nil {