Fix logic recording transition

This commit is contained in:
ragilap
2026-03-10 17:02:45 +07:00
parent 3a8cc47fa0
commit 333cb9e136
13 changed files with 411 additions and 335 deletions
@@ -496,10 +496,6 @@ func (s *fifoStockV2Service) Reflow(ctx context.Context, req ReflowRequest) (*Re
if len(rollbackRes.Details) > 0 { if len(rollbackRes.Details) > 0 {
result.Rollback.Details = append(result.Rollback.Details, rollbackRes.Details...) result.Rollback.Details = append(result.Rollback.Details, rollbackRes.Details...)
} }
minDesired := rollbackRes.ReleasedQty + usableRow.PendingQuantity
if desiredQty < minDesired {
desiredQty = minDesired
}
if desiredQty <= 0 { if desiredQty <= 0 {
continue continue
+4
View File
@@ -259,6 +259,10 @@ func defaultString(v, def string) string {
return v return v
} }
func LayingWeekStart() int {
return TransferToLayingGrowingMaxWeek
}
func joinPath(parts ...string) string { func joinPath(parts ...string) string {
out := make([]string, 0, len(parts)) out := make([]string, 0, len(parts))
for _, part := range parts { for _, part := range parts {
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"gitlab.com/mbugroup/lti-api.git/internal/config"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
m "gitlab.com/mbugroup/lti-api.git/internal/middleware" m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
@@ -343,17 +344,22 @@ func (s productionStandardService) EnsureWeekStart(ctx context.Context, standard
return nil return nil
} }
layingWeekStart := config.LayingWeekStart()
switch strings.ToUpper(category) { switch strings.ToUpper(category) {
case string(utils.ProjectFlockCategoryLaying): case string(utils.ProjectFlockCategoryLaying):
details, err := s.ProductionStandardDetailRepo.GetByProductionStandardID(ctx, standardID) details, err := s.ProductionStandardDetailRepo.GetByProductionStandardID(ctx, standardID)
if err != nil { if err != nil {
return err return err
} }
startWeek := 0 if len(details) == 0 {
if len(details) > 0 { return fiber.NewError(
startWeek = details[0].Week fiber.StatusBadRequest,
"Standart production tidak tersedia untuk kategori laying",
)
} }
if startWeek != 18 { startWeek := details[0].Week
if startWeek > layingWeekStart {
return fiber.NewError(fiber.StatusBadRequest, "Week tidak sesuai dengan standart kategori project flock") return fiber.NewError(fiber.StatusBadRequest, "Week tidak sesuai dengan standart kategori project flock")
} }
case string(utils.ProjectFlockCategoryGrowing): case string(utils.ProjectFlockCategoryGrowing):
@@ -361,10 +367,13 @@ func (s productionStandardService) EnsureWeekStart(ctx context.Context, standard
if err != nil { if err != nil {
return err return err
} }
startWeek := 0 if len(details) == 0 {
if len(details) > 0 { return fiber.NewError(
startWeek = details[0].Week fiber.StatusBadRequest,
"Standart production tidak tersedia untuk kategori growing",
)
} }
startWeek := details[0].Week
if startWeek != 1 { if startWeek != 1 {
return fiber.NewError(fiber.StatusBadRequest, "Week tidak sesuai dengan standart kategori project flock") return fiber.NewError(fiber.StatusBadRequest, "Week tidak sesuai dengan standart kategori project flock")
} }
@@ -381,7 +390,7 @@ func (s productionStandardService) EnsureWeekAvailable(ctx context.Context, stan
upperCategory := strings.ToUpper(category) upperCategory := strings.ToUpper(category)
weekBase := 1 weekBase := 1
if upperCategory == string(utils.ProjectFlockCategoryLaying) { if upperCategory == string(utils.ProjectFlockCategoryLaying) {
weekBase = 18 weekBase = config.LayingWeekStart()
} }
week := ((day - 1) / 7) + weekBase week := ((day - 1) / 7) + weekBase
if week <= 0 { if week <= 0 {
@@ -3,6 +3,7 @@ package dto
import ( import (
"time" "time"
"gitlab.com/mbugroup/lti-api.git/internal/config"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
areaRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" areaRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
flockRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto" flockRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
@@ -35,13 +36,13 @@ type ChickinRelationDTO struct {
} }
type ProjectFlockDTO struct { type ProjectFlockDTO struct {
Id uint `json:"id"` Id uint `json:"id"`
Period int `json:"period"` Period int `json:"period"`
Category string `json:"category"` Category string `json:"category"`
Flock *flockRelationDTO.FlockRelationDTO `json:"flock"` Flock *flockRelationDTO.FlockRelationDTO `json:"flock"`
Area *areaRelationDTO.AreaRelationDTO `json:"area"` Area *areaRelationDTO.AreaRelationDTO `json:"area"`
StandardFcr *float64 `json:"standard_fcr"` StandardFcr *float64 `json:"standard_fcr"`
Location *locationRelationDTO.LocationRelationDTO `json:"location"` Location *locationRelationDTO.LocationRelationDTO `json:"location"`
} }
type ProjectFlockKandangDTO struct { type ProjectFlockKandangDTO struct {
@@ -123,13 +124,13 @@ func ToProjectFlockDTO(pfk entity.ProjectFlockKandang) ProjectFlockDTO {
location = &mapped location = &mapped
} }
return ProjectFlockDTO{ return ProjectFlockDTO{
Id: e.Id, Id: e.Id,
Period: pfk.Period, Period: pfk.Period,
Category: e.Category, Category: e.Category,
Flock: flock, Flock: flock,
Area: area, Area: area,
StandardFcr: resolveProjectFlockStandardFcr(e), StandardFcr: resolveProjectFlockStandardFcr(e),
Location: location, Location: location,
} }
} }
@@ -219,7 +220,7 @@ func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 {
} }
week := 1 week := 1
if e.Category == string(utils.ProjectFlockCategoryLaying) { if e.Category == string(utils.ProjectFlockCategoryLaying) {
week = 18 week = config.LayingWeekStart()
} }
for _, detail := range e.ProductionStandard.ProductionStandardDetails { for _, detail := range e.ProductionStandard.ProductionStandardDetails {
if detail.Week == week && detail.StandardFCR != nil { if detail.Week == week && detail.StandardFCR != nil {
@@ -6,6 +6,7 @@ import (
"math" "math"
"strconv" "strconv"
"strings" "strings"
"time"
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/dto" "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/dto"
@@ -272,10 +273,20 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error {
projectFlockId := c.QueryInt("project_flock_id", 0) projectFlockId := c.QueryInt("project_flock_id", 0)
kandangId := c.QueryInt("kandang_id", 0) kandangId := c.QueryInt("kandang_id", 0)
withPopulation := c.QueryBool("withpopulation", false) withPopulation := c.QueryBool("withpopulation", false)
recordDateRaw := strings.TrimSpace(c.Query("record_date", ""))
var recordDate *time.Time
if projectFlockId == 0 || kandangId == 0 { if projectFlockId == 0 || kandangId == 0 {
return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id or kandang_id") return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id or kandang_id")
} }
if recordDateRaw != "" {
parsed, err := time.Parse("2006-01-02", recordDateRaw)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "record_date must be in YYYY-MM-DD format")
}
utc := parsed.UTC()
recordDate = &utc
}
result, availableStock, err := u.ProjectflockService.GetProjectFlockKandangByProjectAndKandang(c, uint(projectFlockId), uint(kandangId)) result, availableStock, err := u.ProjectflockService.GetProjectFlockKandangByProjectAndKandang(c, uint(projectFlockId), uint(kandangId))
if err != nil { if err != nil {
@@ -300,7 +311,7 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error {
mapped := warehouseDTO.ToWarehouseRelationDTO(*warehouse) mapped := warehouseDTO.ToWarehouseRelationDTO(*warehouse)
dtoResult.Warehouse = &mapped dtoResult.Warehouse = &mapped
} }
if isTransition, isLaying, serr := u.ProjectflockService.GetProjectFlockKandangTransferState(c, result.Id); serr != nil { if isTransition, isLaying, serr := u.ProjectflockService.GetProjectFlockKandangTransferStateAtDate(c, result.Id, recordDate); serr != nil {
return serr return serr
} else { } else {
dtoResult.IsTransition = isTransition dtoResult.IsTransition = isTransition
@@ -3,6 +3,7 @@ package dto
import ( import (
"time" "time"
"gitlab.com/mbugroup/lti-api.git/internal/config"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto" approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
@@ -25,17 +26,17 @@ type ProjectFlockRelationDTO struct {
type ProjectFlockListDTO struct { type ProjectFlockListDTO struct {
ProjectFlockRelationDTO ProjectFlockRelationDTO
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
Category string `json:"category"` Category string `json:"category"`
StandardFcr *float64 `json:"standard_fcr,omitempty"` StandardFcr *float64 `json:"standard_fcr,omitempty"`
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"` Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"`
ProjectBudgets []ProjectBudgetDTO `json:"project_budgets,omitempty"` ProjectBudgets []ProjectBudgetDTO `json:"project_budgets,omitempty"`
CreatedUser *userDTO.UserRelationDTO `json:"created_user"` CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
Approval approvalDTO.ApprovalRelationDTO `json:"approval"` Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
} }
type KandangWithProjectFlockIdDTO struct { type KandangWithProjectFlockIdDTO struct {
@@ -203,7 +204,7 @@ func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 {
} }
week := 1 week := 1
if e.Category == string(utils.ProjectFlockCategoryLaying) { if e.Category == string(utils.ProjectFlockCategoryLaying) {
week = 18 week = config.LayingWeekStart()
} }
for _, detail := range e.ProductionStandard.ProductionStandardDetails { for _, detail := range e.ProductionStandard.ProductionStandardDetails {
if detail.Week == week && detail.StandardFCR != nil { if detail.Week == week && detail.StandardFCR != nil {
@@ -46,6 +46,7 @@ type ProjectflockService interface {
GetProjectFlockKandangPopulation(ctx *fiber.Ctx, projectFlockKandangID uint) (float64, error) GetProjectFlockKandangPopulation(ctx *fiber.Ctx, projectFlockKandangID uint) (float64, error)
GetProjectFlockKandangChickinDate(ctx *fiber.Ctx, projectFlockKandangID uint) (*time.Time, error) GetProjectFlockKandangChickinDate(ctx *fiber.Ctx, projectFlockKandangID uint) (*time.Time, error)
GetProjectFlockKandangTransferState(ctx *fiber.Ctx, projectFlockKandangID uint) (bool, bool, error) GetProjectFlockKandangTransferState(ctx *fiber.Ctx, projectFlockKandangID uint) (bool, bool, error)
GetProjectFlockKandangTransferStateAtDate(ctx *fiber.Ctx, projectFlockKandangID uint, referenceDate *time.Time) (bool, bool, error)
GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error) GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error) GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error) Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
@@ -544,6 +545,10 @@ func (s projectflockService) GetProjectFlockKandangChickinDate(ctx *fiber.Ctx, p
} }
func (s projectflockService) GetProjectFlockKandangTransferState(ctx *fiber.Ctx, projectFlockKandangID uint) (bool, bool, error) { func (s projectflockService) GetProjectFlockKandangTransferState(ctx *fiber.Ctx, projectFlockKandangID uint) (bool, bool, error) {
return s.GetProjectFlockKandangTransferStateAtDate(ctx, projectFlockKandangID, nil)
}
func (s projectflockService) GetProjectFlockKandangTransferStateAtDate(ctx *fiber.Ctx, projectFlockKandangID uint, referenceDate *time.Time) (bool, bool, error) {
if projectFlockKandangID == 0 || s.TransferLayingRepo == nil || s.PivotRepo == nil { if projectFlockKandangID == 0 || s.TransferLayingRepo == nil || s.PivotRepo == nil {
return false, false, nil return false, false, nil
} }
@@ -593,9 +598,12 @@ func (s projectflockService) GetProjectFlockKandangTransferState(ctx *fiber.Ctx,
economicCutoffDate = physicalMoveDate economicCutoffDate = physicalMoveDate
} }
referenceDate := normalizeDateOnlyUTC(time.Now().UTC()) reference := normalizeDateOnlyUTC(time.Now().UTC())
isTransition := !referenceDate.Before(physicalMoveDate) && referenceDate.Before(economicCutoffDate) if referenceDate != nil && !referenceDate.IsZero() {
isLaying := !referenceDate.Before(economicCutoffDate) reference = normalizeDateOnlyUTC(referenceDate.UTC())
}
isTransition := !reference.Before(physicalMoveDate) && reference.Before(economicCutoffDate)
isLaying := !reference.Before(economicCutoffDate)
return isTransition, isLaying, nil return isTransition, isLaying, nil
} }
@@ -5,6 +5,7 @@ import (
"strings" "strings"
"time" "time"
"gitlab.com/mbugroup/lti-api.git/internal/config"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto" approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/dto" productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/dto"
@@ -308,7 +309,7 @@ func recordingWeekValue(e entity.Recording) int {
} }
weekBase := 1 weekBase := 1
if isLayingRecording(e) { if isLayingRecording(e) {
weekBase = 18 weekBase = config.LayingWeekStart()
} }
return ((day - 1) / 7) + weekBase return ((day - 1) / 7) + weekBase
} }
@@ -71,6 +71,7 @@ type RecordingRepository interface {
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) GetAverageTargetMetricsByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint, includeTargets bool) (RecordingTargetAverages, error)
GetProjectFlockKandangIDsByPopulationWarehouseIDs(ctx context.Context, tx *gorm.DB, productWarehouseIDs []uint) ([]uint, error)
ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error
ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error) ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error)
} }
@@ -874,6 +875,34 @@ func (r *RecordingRepositoryImpl) GetAverageTargetMetricsByProjectFlockKandangID
return result, nil return result, nil
} }
func (r *RecordingRepositoryImpl) GetProjectFlockKandangIDsByPopulationWarehouseIDs(
ctx context.Context,
tx *gorm.DB,
productWarehouseIDs []uint,
) ([]uint, error) {
if len(productWarehouseIDs) == 0 {
return nil, nil
}
db := r.DB().WithContext(ctx)
if tx != nil {
db = tx.WithContext(ctx)
}
var kandangIDs []uint
if err := db.Table("project_flock_populations pfp").
Select("DISTINCT pc.project_flock_kandang_id").
Joins("JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
Where("pfp.product_warehouse_id IN ?", productWarehouseIDs).
Where("pfp.deleted_at IS NULL").
Where("pc.deleted_at IS NULL").
Pluck("pc.project_flock_kandang_id", &kandangIDs).Error; err != nil {
return nil, err
}
return kandangIDs, nil
}
func (r *RecordingRepositoryImpl) ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error { func (r *RecordingRepositoryImpl) ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error {
if projectFlockKandangID == 0 { if projectFlockKandangID == 0 {
return nil return nil
@@ -1039,11 +1039,75 @@ func (s *recordingService) evaluatePopulationMutationState(ctx context.Context,
populationCanChange := true populationCanChange := true
if category == strings.ToUpper(string(utils.ProjectFlockCategoryGrowing)) { if category == strings.ToUpper(string(utils.ProjectFlockCategoryGrowing)) {
populationCanChange = !(transferExecuted && !recordDate.Before(transferDate)) populationCanChange = !(transferExecuted && !recordDate.Before(transferDate))
if transferExecuted && !recordDate.Before(transferDate) {
hasTargetLayingRecording, checkErr := s.hasAnyRecordingOnTransferTargets(ctx, transfer)
if checkErr != nil {
s.Log.Errorf("Failed to resolve target laying recording state for transfer %d: %+v", transfer.Id, checkErr)
return true, false, false, false, nil, time.Time{}, fiber.NewError(fiber.StatusInternalServerError, "Gagal memvalidasi status transisi recording")
}
if hasTargetLayingRecording {
isTransition = false
isLaying = true
} else {
today := normalizeDateOnlyUTC(time.Now().UTC())
if !today.Before(economicCutoffDate) {
isTransition = true
isLaying = false
}
}
}
} }
return populationCanChange, transferExecuted, isTransition, isLaying, transfer, transferDate, nil return populationCanChange, transferExecuted, isTransition, isLaying, transfer, transferDate, nil
} }
func (s *recordingService) hasAnyRecordingOnTransferTargets(ctx context.Context, transfer *entity.LayingTransfer) (bool, error) {
if transfer == nil || transfer.Id == 0 {
return false, nil
}
targetIDs, err := s.transferTargetProjectFlockKandangIDs(ctx, transfer.Id)
if err != nil {
return false, err
}
if len(targetIDs) == 0 {
// Keep existing behavior for legacy or incomplete target mapping.
return true, nil
}
var count int64
err = s.Repository.DB().
WithContext(ctx).
Table("recordings").
Where("deleted_at IS NULL").
Where("project_flock_kandangs_id IN ?", targetIDs).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (s *recordingService) transferTargetProjectFlockKandangIDs(ctx context.Context, transferID uint) ([]uint, error) {
if transferID == 0 {
return nil, nil
}
var targetIDs []uint
err := s.Repository.DB().
WithContext(ctx).
Table("laying_transfer_targets").
Where("laying_transfer_id = ?", transferID).
Where("deleted_at IS NULL").
Pluck("target_project_flock_kandang_id", &targetIDs).Error
if err != nil {
return nil, err
}
return targetIDs, nil
}
func (s *recordingService) ensurePopulationMutationAllowed(ctx context.Context, recording *entity.Recording, operation string) error { func (s *recordingService) ensurePopulationMutationAllowed(ctx context.Context, recording *entity.Recording, operation string) error {
populationCanChange, _, _, _, transfer, transferDate, err := s.evaluatePopulationMutationState(ctx, recording) populationCanChange, _, _, _, transfer, transferDate, err := s.evaluatePopulationMutationState(ctx, recording)
if err != nil { if err != nil {
@@ -1091,19 +1155,16 @@ func (s *recordingService) ensureDepletionMutationAllowed(ctx context.Context, r
category = strings.ToUpper(strings.TrimSpace(pfk.ProjectFlock.Category)) category = strings.ToUpper(strings.TrimSpace(pfk.ProjectFlock.Category))
} }
if !shouldGuardDepletionMutation(category) {
return nil
}
var ( var (
transfer *entity.LayingTransfer transfer *entity.LayingTransfer
err error err error
) )
switch category { transfer, err = s.TransferLayingRepo.GetLatestApprovedBySourceKandang(ctx, recording.ProjectFlockKandangId)
case strings.ToUpper(string(utils.ProjectFlockCategoryGrowing)):
transfer, err = s.TransferLayingRepo.GetLatestApprovedBySourceKandang(ctx, recording.ProjectFlockKandangId)
case strings.ToUpper(string(utils.ProjectFlockCategoryLaying)):
transfer, err = s.TransferLayingRepo.GetLatestApprovedByTargetKandang(ctx, recording.ProjectFlockKandangId)
default:
return nil
}
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil return nil
@@ -1132,6 +1193,10 @@ func (s *recordingService) ensureDepletionMutationAllowed(ctx context.Context, r
) )
} }
func shouldGuardDepletionMutation(category string) bool {
return strings.EqualFold(strings.TrimSpace(category), string(utils.ProjectFlockCategoryGrowing))
}
func (s *recordingService) tryAutoExecuteTransferForRecordingCreate(c *fiber.Ctx, pfk *entity.ProjectFlockKandang, recordTime time.Time) error { func (s *recordingService) tryAutoExecuteTransferForRecordingCreate(c *fiber.Ctx, pfk *entity.ProjectFlockKandang, recordTime time.Time) error {
if pfk == nil || pfk.Id == 0 || s.TransferLayingRepo == nil || s.TransferLayingSvc == nil { if pfk == nil || pfk.Id == 0 || s.TransferLayingRepo == nil || s.TransferLayingSvc == nil {
return nil return nil
@@ -2026,10 +2091,7 @@ func (s *recordingService) reflowApplyRecordingStocks(
} }
s.logStockTrace("reflow_apply:done", *refreshed, fmt.Sprintf("desired=%.3f used=%.3f pending=%.3f", desiredTotal, actualUsage, actualPending)) s.logStockTrace("reflow_apply:done", *refreshed, fmt.Sprintf("desired=%.3f used=%.3f pending=%.3f", desiredTotal, actualUsage, actualPending))
logDecrease := actualUsage logDecrease := recordingStockRollbackQty(*refreshed)
if actualPending > 0 {
logDecrease += actualPending
}
if logDecrease > 0 && shouldWriteLog { if logDecrease > 0 && shouldWriteLog {
log := &entity.StockLog{ log := &entity.StockLog{
ProductWarehouseId: refreshed.ProductWarehouseId, ProductWarehouseId: refreshed.ProductWarehouseId,
@@ -2073,11 +2135,8 @@ func (s *recordingService) reflowResetRecordingStocks(
continue continue
} }
currentUsage := 0.0 rollbackQty := recordingStockRollbackQty(stock)
if stock.UsageQty != nil { s.logStockTrace("reflow_reset:start", stock, fmt.Sprintf("rollback_qty=%.3f", rollbackQty))
currentUsage = *stock.UsageQty
}
s.logStockTrace("reflow_reset:start", stock, "")
if err := s.Repository.UpdateStockUsage(tx, stock.Id, 0, 0); err != nil { if err := s.Repository.UpdateStockUsage(tx, stock.Id, 0, 0); err != nil {
return err return err
@@ -2094,13 +2153,13 @@ func (s *recordingService) reflowResetRecordingStocks(
s.Log.Errorf("Failed to reflow FIFO v2 rollback for recording stock %d: %+v", stock.Id, err) s.Log.Errorf("Failed to reflow FIFO v2 rollback for recording stock %d: %+v", stock.Id, err)
return err return err
} }
s.logStockTrace("reflow_reset:done", stock, "") s.logStockTrace("reflow_reset:done", stock, fmt.Sprintf("rollback_qty=%.3f", rollbackQty))
if currentUsage > 0 && shouldWriteLog { if rollbackQty > 0 && shouldWriteLog {
log := &entity.StockLog{ log := &entity.StockLog{
ProductWarehouseId: stock.ProductWarehouseId, ProductWarehouseId: stock.ProductWarehouseId,
CreatedBy: actorID, CreatedBy: actorID,
Increase: currentUsage, Increase: rollbackQty,
LoggableType: string(utils.StockLogTypeRecording), LoggableType: string(utils.StockLogTypeRecording),
LoggableId: stock.RecordingId, LoggableId: stock.RecordingId,
Notes: note, Notes: note,
@@ -2114,6 +2173,24 @@ func (s *recordingService) reflowResetRecordingStocks(
return nil return nil
} }
func recordingStockRollbackQty(stock entity.RecordingStock) float64 {
usage := 0.0
if stock.UsageQty != nil {
usage = *stock.UsageQty
}
pending := 0.0
if stock.PendingQty != nil {
pending = *stock.PendingQty
}
if usage < 0 {
usage = 0
}
if pending < 0 {
pending = 0
}
return usage + pending
}
type desiredStock struct { type desiredStock struct {
Usage float64 Usage float64
Pending float64 Pending float64
@@ -2627,19 +2704,8 @@ func (s *recordingService) resyncPopulationUsageForDepletions(
} }
if len(sourceWarehouseIDs) > 0 { if len(sourceWarehouseIDs) > 0 {
db := s.Repository.DB().WithContext(ctx) sourceKandangIDs, err := s.Repository.GetProjectFlockKandangIDsByPopulationWarehouseIDs(ctx, tx, sourceWarehouseIDs)
if tx != nil { if err != nil {
db = tx.WithContext(ctx)
}
var sourceKandangIDs []uint
if err := db.Table("project_flock_populations pfp").
Select("DISTINCT pc.project_flock_kandang_id").
Joins("JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
Where("pfp.product_warehouse_id IN ?", sourceWarehouseIDs).
Where("pfp.deleted_at IS NULL").
Where("pc.deleted_at IS NULL").
Pluck("pc.project_flock_kandang_id", &sourceKandangIDs).Error; err != nil {
return err return err
} }
@@ -2651,62 +2717,7 @@ func (s *recordingService) resyncPopulationUsageForDepletions(
} }
for kandangID := range kandangIDs { for kandangID := range kandangIDs {
if err := s.resyncPopulationUsageByProjectFlockKandang(ctx, tx, kandangID); err != nil { if err := s.Repository.ResyncProjectFlockPopulationUsage(ctx, tx, kandangID); err != nil {
return err
}
}
return nil
}
func (s *recordingService) resyncPopulationUsageByProjectFlockKandang(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error {
if projectFlockKandangID == 0 {
return nil
}
db := s.Repository.DB().WithContext(ctx)
if tx != nil {
db = tx.WithContext(ctx)
}
var populationIDs []uint
if err := db.Table("project_flock_populations pfp").
Select("pfp.id").
Joins("JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
Where("pc.project_flock_kandang_id = ?", projectFlockKandangID).
Pluck("pfp.id", &populationIDs).Error; err != nil {
return err
}
if len(populationIDs) == 0 {
return nil
}
type usageRow struct {
StockableID uint `gorm:"column:stockable_id"`
Used float64 `gorm:"column:used"`
}
var usageRows []usageRow
if err := db.Table("stock_allocations").
Select("stockable_id, COALESCE(SUM(qty), 0) AS used").
Where("stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
Where("status = ?", entity.StockAllocationStatusActive).
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
Where("stockable_id IN ?", populationIDs).
Group("stockable_id").
Scan(&usageRows).Error; err != nil {
return err
}
if err := db.Model(&entity.ProjectFlockPopulation{}).
Where("id IN ?", populationIDs).
Update("total_used_qty", 0).Error; err != nil {
return err
}
for _, row := range usageRows {
if err := db.Model(&entity.ProjectFlockPopulation{}).
Where("id = ?", row.StockableID).
Update("total_used_qty", row.Used).Error; err != nil {
return err return err
} }
} }
@@ -13,6 +13,7 @@ import (
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
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"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
m "gitlab.com/mbugroup/lti-api.git/internal/middleware" m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories" rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
@@ -380,12 +381,13 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
} }
} }
weekBase := 1 weekBase := 1
if strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying)) { isLayingCategory := strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying))
weekBase = 18 if isLayingCategory {
weekBase = config.LayingWeekStart()
} }
if req.Week < weekBase { if req.Week < weekBase {
if weekBase == 18 { if isLayingCategory {
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 18 for laying projects") return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
} }
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects") return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
} }
@@ -399,8 +401,8 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate uniformity week sequence") return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate uniformity week sequence")
} }
if latestWeek == 0 && req.Week != weekBase { if latestWeek == 0 && req.Week != weekBase {
if weekBase == 18 { if isLayingCategory {
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 18 for laying projects") return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
} }
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects") return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
} }
@@ -575,12 +577,13 @@ func (s uniformityService) UpdateOne(c *fiber.Ctx, req *validation.Update, id ui
} }
} }
weekBase := 1 weekBase := 1
if strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying)) { isLayingCategory := strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying))
weekBase = 18 if isLayingCategory {
weekBase = config.LayingWeekStart()
} }
if targetWeek < weekBase { if targetWeek < weekBase {
if weekBase == 18 { if isLayingCategory {
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 18 for laying projects") return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
} }
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects") return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
} }
@@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"gitlab.com/mbugroup/lti-api.git/internal/config"
m "gitlab.com/mbugroup/lti-api.git/internal/middleware" m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
"gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto" "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto"
repportRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/repositories" repportRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/repositories"
@@ -256,11 +257,11 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.
const ( const (
recordsPerWeek = 7 recordsPerWeek = 7
defaultStartWoa = 18
defaultStdBw = 1951 defaultStdBw = 1951
defaultBw = 0 defaultBw = 0
defaultUniformText = "90% up" defaultUniformText = "90% up"
) )
defaultStartWoa := config.LayingWeekStart()
if params.Limit <= 0 { if params.Limit <= 0 {
params.Limit = 10 params.Limit = 10
+193 -192
View File
@@ -6,6 +6,7 @@ import (
"strings" "strings"
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"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories" rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
"gitlab.com/mbugroup/lti-api.git/internal/utils" "gitlab.com/mbugroup/lti-api.git/internal/utils"
@@ -13,31 +14,31 @@ import (
) )
type warnLogger interface { type warnLogger interface {
Warnf(format string, args ...any) Warnf(format string, args ...any)
} }
type productWarehouseExistsRepo interface { type productWarehouseExistsRepo interface {
ExistsByID(ctx context.Context, id uint) (bool, error) ExistsByID(ctx context.Context, id uint) (bool, error)
} }
type recordingValidationRepo interface { type recordingValidationRepo interface {
ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error) ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error)
} }
func EnsureProductWarehousesExist(ctx context.Context, repo productWarehouseExistsRepo, ids []uint) error { func EnsureProductWarehousesExist(ctx context.Context, repo productWarehouseExistsRepo, ids []uint) error {
if repo == nil || len(ids) == 0 { if repo == nil || len(ids) == 0 {
return nil return nil
} }
for _, id := range ids { for _, id := range ids {
ok, err := repo.ExistsByID(ctx, id) ok, err := repo.ExistsByID(ctx, id)
if err != nil { if err != nil {
return err return err
} }
if !ok { if !ok {
return fmt.Errorf("product warehouse %d not found", id) return fmt.Errorf("product warehouse %d not found", id)
} }
} }
return nil return nil
} }
func EnsureProductWarehousesByFlags(ctx context.Context, repo recordingValidationRepo, ids []uint, flags []string, label string) error { func EnsureProductWarehousesByFlags(ctx context.Context, repo recordingValidationRepo, ids []uint, flags []string, label string) error {
@@ -82,212 +83,212 @@ func EnsureProductWarehousesByFlagsForItems[T any](
} }
func ComputeDepletionRate(prevRecording *entity.Recording, currentDepletion float64, totalChick int64) float64 { func ComputeDepletionRate(prevRecording *entity.Recording, currentDepletion float64, totalChick int64) float64 {
base := 0.0 base := 0.0
if prevRecording != nil && prevRecording.TotalChickQty != nil && *prevRecording.TotalChickQty > 0 { if prevRecording != nil && prevRecording.TotalChickQty != nil && *prevRecording.TotalChickQty > 0 {
base = *prevRecording.TotalChickQty base = *prevRecording.TotalChickQty
} else if totalChick > 0 { } else if totalChick > 0 {
base = float64(totalChick) + currentDepletion base = float64(totalChick) + currentDepletion
} }
if base <= 0 { if base <= 0 {
return 0 return 0
} }
return (currentDepletion / base) * 100 return (currentDepletion / base) * 100
} }
func AttachLatestApprovals(ctx context.Context, items []entity.Recording, approvalSvc commonSvc.ApprovalService, logger warnLogger) error { func AttachLatestApprovals(ctx context.Context, items []entity.Recording, approvalSvc commonSvc.ApprovalService, logger warnLogger) error {
if len(items) == 0 || approvalSvc == nil { if len(items) == 0 || approvalSvc == nil {
return nil return nil
} }
ids := make([]uint, 0, len(items)) ids := make([]uint, 0, len(items))
visited := make(map[uint]struct{}, len(items)) visited := make(map[uint]struct{}, len(items))
for _, item := range items { for _, item := range items {
if item.Id == 0 { if item.Id == 0 {
continue continue
} }
if _, ok := visited[item.Id]; ok { if _, ok := visited[item.Id]; ok {
continue continue
} }
visited[item.Id] = struct{}{} visited[item.Id] = struct{}{}
ids = append(ids, item.Id) ids = append(ids, item.Id)
} }
if len(ids) == 0 { if len(ids) == 0 {
return nil return nil
} }
latestMap, err := approvalSvc.LatestByTargets(ctx, utils.ApprovalWorkflowRecording, ids, func(db *gorm.DB) *gorm.DB { latestMap, err := approvalSvc.LatestByTargets(ctx, utils.ApprovalWorkflowRecording, ids, func(db *gorm.DB) *gorm.DB {
return db.Preload("ActionUser") return db.Preload("ActionUser")
}) })
if err != nil { if err != nil {
if logger != nil { if logger != nil {
logger.Warnf("Unable to load latest approvals for recordings: %+v", err) logger.Warnf("Unable to load latest approvals for recordings: %+v", err)
} }
return nil return nil
} }
if len(latestMap) == 0 { if len(latestMap) == 0 {
return nil return nil
} }
for i := range items { for i := range items {
if items[i].Id == 0 { if items[i].Id == 0 {
continue continue
} }
if approval, ok := latestMap[items[i].Id]; ok { if approval, ok := latestMap[items[i].Id]; ok {
items[i].LatestApproval = approval items[i].LatestApproval = approval
} }
} }
return nil return nil
} }
func AttachLatestApproval(ctx context.Context, item *entity.Recording, approvalSvc commonSvc.ApprovalService, logger warnLogger) error { func AttachLatestApproval(ctx context.Context, item *entity.Recording, approvalSvc commonSvc.ApprovalService, logger warnLogger) error {
if item == nil || item.Id == 0 || approvalSvc == nil { if item == nil || item.Id == 0 || approvalSvc == nil {
return nil return nil
} }
latest, err := approvalSvc.LatestByTarget(ctx, utils.ApprovalWorkflowRecording, item.Id, func(db *gorm.DB) *gorm.DB { latest, err := approvalSvc.LatestByTarget(ctx, utils.ApprovalWorkflowRecording, item.Id, func(db *gorm.DB) *gorm.DB {
return db.Preload("ActionUser") return db.Preload("ActionUser")
}) })
if err != nil { if err != nil {
if logger != nil { if logger != nil {
logger.Warnf("Unable to load approvals for recording %d: %+v", item.Id, err) logger.Warnf("Unable to load approvals for recording %d: %+v", item.Id, err)
} }
return nil return nil
} }
item.LatestApproval = latest item.LatestApproval = latest
return nil return nil
} }
type productionStandardValues struct { type productionStandardValues struct {
HenDay *float64 HenDay *float64
HenHouse *float64 HenHouse *float64
FeedIntake *float64 FeedIntake *float64
MaxDepletion *float64 MaxDepletion *float64
EggMass *float64 EggMass *float64
EggWeight *float64 EggWeight *float64
} }
func AttachProductionStandards(ctx context.Context, db *gorm.DB, warnOnly bool, logger warnLogger, items ...*entity.Recording) error { func AttachProductionStandards(ctx context.Context, db *gorm.DB, warnOnly bool, logger warnLogger, items ...*entity.Recording) error {
if len(items) == 0 { if len(items) == 0 {
return nil return nil
} }
type standardKey struct { type standardKey struct {
standardID uint standardID uint
week int week int
} }
type standardCacheEntry struct { type standardCacheEntry struct {
values productionStandardValues values productionStandardValues
fcr *float64 fcr *float64
} }
if db == nil { if db == nil {
return nil return nil
} }
standardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db) standardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db)
growthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db) growthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db)
cache := make(map[standardKey]standardCacheEntry, len(items)) cache := make(map[standardKey]standardCacheEntry, len(items))
standardIDs := make(map[uint]struct{}, len(items)) standardIDs := make(map[uint]struct{}, len(items))
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
} }
if item.ProjectFlockKandang.ProjectFlock.ProductionStandardId > 0 { if item.ProjectFlockKandang.ProjectFlock.ProductionStandardId > 0 {
standardIDs[item.ProjectFlockKandang.ProjectFlock.ProductionStandardId] = struct{}{} standardIDs[item.ProjectFlockKandang.ProjectFlock.ProductionStandardId] = struct{}{}
} }
} }
standardDetailByStd := make(map[uint]map[int]*entity.ProductionStandardDetail, len(standardIDs)) standardDetailByStd := make(map[uint]map[int]*entity.ProductionStandardDetail, len(standardIDs))
growthDetailByStd := make(map[uint]map[int]*entity.StandardGrowthDetail, len(standardIDs)) growthDetailByStd := make(map[uint]map[int]*entity.StandardGrowthDetail, len(standardIDs))
for standardID := range standardIDs { for standardID := range standardIDs {
details, err := standardDetailRepo.GetByProductionStandardID(ctx, standardID) details, err := standardDetailRepo.GetByProductionStandardID(ctx, standardID)
if err != nil { if err != nil {
if warnOnly { if warnOnly {
if logger != nil { if logger != nil {
logger.Warnf("Unable to preload production standard detail for standard %d: %+v", standardID, err) logger.Warnf("Unable to preload production standard detail for standard %d: %+v", standardID, err)
} }
} else { } else {
return err return err
} }
continue continue
} }
detailMap := make(map[int]*entity.ProductionStandardDetail, len(details)) detailMap := make(map[int]*entity.ProductionStandardDetail, len(details))
for i := range details { for i := range details {
detail := details[i] detail := details[i]
detailMap[detail.Week] = &detail detailMap[detail.Week] = &detail
} }
standardDetailByStd[standardID] = detailMap standardDetailByStd[standardID] = detailMap
growths, err := growthDetailRepo.GetByProductionStandardID(ctx, standardID) growths, err := growthDetailRepo.GetByProductionStandardID(ctx, standardID)
if err != nil { if err != nil {
if warnOnly { if warnOnly {
if logger != nil { if logger != nil {
logger.Warnf("Unable to preload standard growth detail for standard %d: %+v", standardID, err) logger.Warnf("Unable to preload standard growth detail for standard %d: %+v", standardID, err)
} }
} else { } else {
return err return err
} }
continue continue
} }
growthMap := make(map[int]*entity.StandardGrowthDetail, len(growths)) growthMap := make(map[int]*entity.StandardGrowthDetail, len(growths))
for i := range growths { for i := range growths {
growth := growths[i] growth := growths[i]
growthMap[growth.Week] = &growth growthMap[growth.Week] = &growth
} }
growthDetailByStd[standardID] = growthMap growthDetailByStd[standardID] = growthMap
} }
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
} }
standardID := item.ProjectFlockKandang.ProjectFlock.ProductionStandardId standardID := item.ProjectFlockKandang.ProjectFlock.ProductionStandardId
if standardID == 0 { if standardID == 0 {
continue continue
} }
week := RecordingWeekValue(*item) week := RecordingWeekValue(*item)
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)
continue continue
} }
values := productionStandardValues{} values := productionStandardValues{}
var fcr *float64 var fcr *float64
if detailMap, ok := standardDetailByStd[standardID]; ok { if detailMap, ok := standardDetailByStd[standardID]; ok {
if detail, ok := detailMap[week]; ok { if detail, ok := detailMap[week]; ok {
values.HenDay = detail.TargetHenDayProduction values.HenDay = detail.TargetHenDayProduction
values.HenHouse = detail.TargetHenHouseProduction values.HenHouse = detail.TargetHenHouseProduction
values.EggMass = detail.TargetEggMass values.EggMass = detail.TargetEggMass
values.EggWeight = detail.TargetEggWeight values.EggWeight = detail.TargetEggWeight
fcr = detail.StandardFCR fcr = detail.StandardFCR
} }
} }
if growthMap, ok := growthDetailByStd[standardID]; ok { if growthMap, ok := growthDetailByStd[standardID]; ok {
if growth, ok := growthMap[week]; ok { if growth, ok := growthMap[week]; ok {
values.FeedIntake = growth.FeedIntake values.FeedIntake = growth.FeedIntake
values.MaxDepletion = growth.MaxDepletion values.MaxDepletion = growth.MaxDepletion
} }
} }
cache[cacheKey] = standardCacheEntry{values: values, fcr: fcr} cache[cacheKey] = standardCacheEntry{values: values, fcr: fcr}
applyProductionStandardValues(item, values, fcr) applyProductionStandardValues(item, values, fcr)
} }
return nil return nil
} }
func applyProductionStandardValues(item *entity.Recording, values productionStandardValues, fcr *float64) { func applyProductionStandardValues(item *entity.Recording, values productionStandardValues, fcr *float64) {
item.StandardHenDay = values.HenDay item.StandardHenDay = values.HenDay
item.StandardHenHouse = values.HenHouse item.StandardHenHouse = values.HenHouse
item.StandardFeedIntake = values.FeedIntake item.StandardFeedIntake = values.FeedIntake
item.StandardMaxDepletion = values.MaxDepletion item.StandardMaxDepletion = values.MaxDepletion
item.StandardEggMass = values.EggMass item.StandardEggMass = values.EggMass
item.StandardEggWeight = values.EggWeight item.StandardEggWeight = values.EggWeight
item.StandardFcr = fcr item.StandardFcr = fcr
} }
func RecordingWeekValue(e entity.Recording) int { func RecordingWeekValue(e entity.Recording) int {
@@ -297,7 +298,7 @@ func RecordingWeekValue(e entity.Recording) int {
} }
weekBase := 1 weekBase := 1
if IsLayingRecording(e) { if IsLayingRecording(e) {
weekBase = 18 weekBase = config.LayingWeekStart()
} }
return ((day - 1) / 7) + weekBase return ((day - 1) / 7) + weekBase
} }