Merge branch 'fix/week-record' into 'development'

[FIX][BE]: adjust calculate for week at recording list

See merge request mbugroup/lti-api!520
This commit is contained in:
Giovanni Gabriel Septriadi
2026-05-08 05:15:42 +00:00
3 changed files with 93 additions and 2 deletions
+1
View File
@@ -43,6 +43,7 @@ type Recording struct {
StandardEggMass *float64 `gorm:"-"` StandardEggMass *float64 `gorm:"-"`
StandardEggWeight *float64 `gorm:"-"` StandardEggWeight *float64 `gorm:"-"`
StandardFcr *float64 `gorm:"-"` StandardFcr *float64 `gorm:"-"`
StandardWeek *int `gorm:"-"`
PopulationCanChange *bool `gorm:"-"` PopulationCanChange *bool `gorm:"-"`
TransferExecuted *bool `gorm:"-"` TransferExecuted *bool `gorm:"-"`
IsTransition *bool `gorm:"-"` IsTransition *bool `gorm:"-"`
@@ -314,9 +314,13 @@ func toRecordingProjectFlockDTO(e entity.Recording) RecordingProjectFlockDTO {
result.Period = pfk.Period result.Period = pfk.Period
if pfk.ProjectFlock.ProductionStandard.Id != 0 { if pfk.ProjectFlock.ProductionStandard.Id != 0 {
week := recordingWeekValue(e)
if e.StandardWeek != nil && *e.StandardWeek > 0 {
week = *e.StandardWeek
}
result.ProductionStandart = &RecordingProductionStandardDTO{ result.ProductionStandart = &RecordingProductionStandardDTO{
Id: pfk.ProjectFlock.ProductionStandard.Id, Id: pfk.ProjectFlock.ProductionStandard.Id,
Week: recordingWeekValue(e), Week: week,
Name: pfk.ProjectFlock.ProductionStandard.Name, Name: pfk.ProjectFlock.ProductionStandard.Name,
HenDayStd: floatValue(e.StandardHenDay), HenDayStd: floatValue(e.StandardHenDay),
HenHouseStd: floatValue(e.StandardHenHouse), HenHouseStd: floatValue(e.StandardHenHouse),
+87 -1
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"strings" "strings"
"time"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
"gitlab.com/mbugroup/lti-api.git/internal/config" "gitlab.com/mbugroup/lti-api.git/internal/config"
@@ -243,6 +244,31 @@ func AttachProductionStandards(ctx context.Context, db *gorm.DB, warnOnly bool,
growthDetailByStd[standardID] = growthMap growthDetailByStd[standardID] = growthMap
} }
// Batch-load laying transfer targets → source PFK chick_in_dates
// untuk menentukan actual chicken week (bukan hardcode LayingWeekStart offset)
type transferChickIn struct {
TargetPFKID uint
ChickInDate time.Time
}
layingPFKIDs := collectLayingPFKIDs(items)
sourceChickInByTarget := make(map[uint]time.Time, len(layingPFKIDs))
if len(layingPFKIDs) > 0 {
var results []transferChickIn
db.Raw(`
SELECT ltt.target_project_flock_kandang_id AS target_pfk_id, pc.chick_in_date
FROM laying_transfer_targets ltt
JOIN laying_transfer_sources lts ON lts.laying_transfer_id = ltt.laying_transfer_id
JOIN project_chickins pc ON pc.project_flock_kandang_id = lts.source_project_flock_kandang_id
WHERE ltt.target_project_flock_kandang_id IN ?
AND ltt.deleted_at IS NULL
AND lts.deleted_at IS NULL
AND pc.deleted_at IS NULL
`, layingPFKIDs).Scan(&results)
for _, r := range results {
sourceChickInByTarget[r.TargetPFKID] = r.ChickInDate
}
}
for _, item := range items { for _, item := range items {
if item == nil || item.ProjectFlockKandang == nil || item.ProjectFlockKandang.ProjectFlock.Id == 0 { if item == nil || item.ProjectFlockKandang == nil || item.ProjectFlockKandang.ProjectFlock.Id == 0 {
continue continue
@@ -251,7 +277,8 @@ func AttachProductionStandards(ctx context.Context, db *gorm.DB, warnOnly bool,
if standardID == 0 { if standardID == 0 {
continue continue
} }
week := RecordingWeekValue(*item) week := computeTransferAwareWeek(item, sourceChickInByTarget)
item.StandardWeek = &week
cacheKey := standardKey{standardID: standardID, week: week} cacheKey := standardKey{standardID: standardID, week: week}
if cached, ok := cache[cacheKey]; ok { if cached, ok := cache[cacheKey]; ok {
applyProductionStandardValues(item, cached.values, cached.fcr) applyProductionStandardValues(item, cached.values, cached.fcr)
@@ -291,6 +318,65 @@ func applyProductionStandardValues(item *entity.Recording, values productionStan
item.StandardFcr = fcr item.StandardFcr = fcr
} }
// collectLayingPFKIDs mengumpulkan semua project_flock_kandang_id dari recording laying
func collectLayingPFKIDs(items []*entity.Recording) []uint {
seen := make(map[uint]struct{})
var ids []uint
for _, item := range items {
if item == nil || item.ProjectFlockKandang == nil {
continue
}
if strings.EqualFold(item.ProjectFlockKandang.ProjectFlock.Category, string(utils.ProjectFlockCategoryLaying)) {
id := item.ProjectFlockKandang.Id
if _, ok := seen[id]; !ok {
seen[id] = struct{}{}
ids = append(ids, id)
}
}
}
return ids
}
// computeTransferAwareWeek menghitung production standard week untuk recording.
// Laying dengan transfer: actual chicken age dari source PFK chick_in_date.
// Laying cut-over (tanpa transfer): langsung dari recording.day (tanpa offset LayingWeekStart).
// Non-laying: ((day-1)/7) + 1.
func computeTransferAwareWeek(item *entity.Recording, sourceChickInByTarget map[uint]time.Time) int {
day := intValue(item.Day)
if item == nil || item.ProjectFlockKandang == nil {
if day > 0 {
return ((day - 1) / 7) + 1
}
return 0
}
isLaying := strings.EqualFold(item.ProjectFlockKandang.ProjectFlock.Category, string(utils.ProjectFlockCategoryLaying))
if !isLaying {
if day > 0 {
return ((day - 1) / 7) + 1
}
return 0
}
// Laying recording — cek apakah PFK ini adalah target dari laying transfer
if sourceChickIn, ok := sourceChickInByTarget[item.ProjectFlockKandang.Id]; ok && !sourceChickIn.IsZero() {
// Ada laying transfer: hitung umur aktual dari source PFK chick_in_date
rDate := time.Date(item.RecordDatetime.Year(), item.RecordDatetime.Month(), item.RecordDatetime.Day(), 0, 0, 0, 0, item.RecordDatetime.Location())
sDate := time.Date(sourceChickIn.Year(), sourceChickIn.Month(), sourceChickIn.Day(), 0, 0, 0, 0, sourceChickIn.Location())
actualDay := int(rDate.Sub(sDate).Hours() / 24)
if actualDay > 0 {
return ((actualDay - 1) / 7) + 1
}
return 0
}
// Cut-over laying (tanpa transfer): chick_in_date di PFK sudah umur asli DOC
if day > 0 {
return ((day - 1) / 7) + 1
}
return 0
}
func RecordingWeekValue(e entity.Recording) int { func RecordingWeekValue(e entity.Recording) int {
day := intValue(e.Day) day := intValue(e.Day)
if day <= 0 { if day <= 0 {