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"
@@ -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"
@@ -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 {
case strings.ToUpper(string(utils.ProjectFlockCategoryGrowing)):
transfer, err = s.TransferLayingRepo.GetLatestApprovedBySourceKandang(ctx, recording.ProjectFlockKandangId) 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
@@ -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"
@@ -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
} }