Merge branch 'feat/BE/US-281-adjustment_recording' into 'development'

feat(BE-281): adjustment recording table with handhouse and deleting weight...

See merge request mbugroup/lti-api!122
This commit is contained in:
Hafizh A. Y.
2025-12-31 03:01:09 +00:00
9 changed files with 394 additions and 259 deletions
@@ -0,0 +1,54 @@
BEGIN;
CREATE TABLE IF NOT EXISTS recording_bws (
id BIGSERIAL PRIMARY KEY,
recording_id BIGINT NOT NULL,
avg_weight NUMERIC(8,2) NOT NULL,
qty NUMERIC(15,3) NOT NULL DEFAULT 1,
total_weight NUMERIC(10,3) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT fk_recording_bws_recording
FOREIGN KEY (recording_id) REFERENCES recordings(id) ON DELETE CASCADE,
CONSTRAINT chk_recording_bws_nonneg
CHECK (avg_weight >= 0 AND qty >= 0 AND total_weight >= 0)
);
CREATE INDEX IF NOT EXISTS idx_recording_bws_recording
ON recording_bws (recording_id);
ALTER TABLE recordings
DROP CONSTRAINT IF EXISTS chk_recordings_nonnegatives_v3;
ALTER TABLE recordings
DROP COLUMN IF EXISTS hand_day,
DROP COLUMN IF EXISTS hand_house,
DROP COLUMN IF EXISTS feed_intake,
DROP COLUMN IF EXISTS egg_mesh,
DROP COLUMN IF EXISTS egg_weight;
ALTER TABLE recordings
ADD CONSTRAINT chk_recordings_nonnegatives_v2 CHECK (
(total_depletion_qty IS NULL OR total_depletion_qty >= 0) AND
(cum_depletion_rate IS NULL OR cum_depletion_rate >= 0) AND
(daily_gain IS NULL OR daily_gain >= 0) AND
(avg_daily_gain IS NULL OR avg_daily_gain >= 0) AND
(cum_intake IS NULL OR cum_intake >= 0) AND
(fcr_value IS NULL OR fcr_value >= 0) AND
(total_chick_qty IS NULL OR total_chick_qty >= 0)
);
ALTER TABLE recording_eggs
DROP CONSTRAINT IF EXISTS chk_recording_eggs_qty;
ALTER TABLE recording_eggs
ALTER COLUMN weight TYPE NUMERIC(10,3) USING weight::NUMERIC(10,3);
ALTER TABLE recording_eggs
ADD CONSTRAINT chk_recording_eggs_qty CHECK (
qty >= 0 AND (weight IS NULL OR weight >= 0)
);
COMMIT;
@@ -0,0 +1,44 @@
BEGIN;
ALTER TABLE recordings
DROP CONSTRAINT IF EXISTS chk_recordings_nonnegatives_v2;
ALTER TABLE recordings
ADD COLUMN IF NOT EXISTS hand_day NUMERIC(15,3),
ADD COLUMN IF NOT EXISTS hand_house NUMERIC(15,3),
ADD COLUMN IF NOT EXISTS feed_intake NUMERIC(15,3),
ADD COLUMN IF NOT EXISTS egg_mesh NUMERIC(15,3),
ADD COLUMN IF NOT EXISTS egg_weight NUMERIC(15,3);
ALTER TABLE recordings
ADD CONSTRAINT chk_recordings_nonnegatives_v3 CHECK (
(total_depletion_qty IS NULL OR total_depletion_qty >= 0) AND
(cum_depletion_rate IS NULL OR cum_depletion_rate >= 0) AND
(daily_gain IS NULL OR daily_gain >= 0) AND
(avg_daily_gain IS NULL OR avg_daily_gain >= 0) AND
(cum_intake IS NULL OR cum_intake >= 0) AND
(fcr_value IS NULL OR fcr_value >= 0) AND
(total_chick_qty IS NULL OR total_chick_qty >= 0) AND
(hand_day IS NULL OR hand_day >= 0) AND
(hand_house IS NULL OR hand_house >= 0) AND
(feed_intake IS NULL OR feed_intake >= 0) AND
(egg_mesh IS NULL OR egg_mesh >= 0) AND
(egg_weight IS NULL OR egg_weight >= 0)
);
ALTER TABLE recording_eggs
ALTER COLUMN weight TYPE NUMERIC(15,3) USING weight::NUMERIC(15,3);
ALTER TABLE recording_eggs
DROP CONSTRAINT IF EXISTS chk_recording_eggs_qty;
ALTER TABLE recording_eggs
ADD CONSTRAINT chk_recording_eggs_qty CHECK (
qty >= 0 AND
(weight IS NULL OR weight >= 0)
);
DROP INDEX IF EXISTS idx_recording_bws_recording;
DROP TABLE IF EXISTS recording_bws;
COMMIT;
+13 -3
View File
@@ -13,11 +13,14 @@ type Recording struct {
Day *int `gorm:"column:day"` Day *int `gorm:"column:day"`
TotalDepletionQty *float64 `gorm:"column:total_depletion_qty"` TotalDepletionQty *float64 `gorm:"column:total_depletion_qty"`
CumDepletionRate *float64 `gorm:"column:cum_depletion_rate"` CumDepletionRate *float64 `gorm:"column:cum_depletion_rate"`
DailyGain *float64 `gorm:"column:daily_gain"`
AvgDailyGain *float64 `gorm:"column:avg_daily_gain"`
CumIntake *int `gorm:"column:cum_intake"` CumIntake *int `gorm:"column:cum_intake"`
FcrValue *float64 `gorm:"column:fcr_value"` FcrValue *float64 `gorm:"column:fcr_value"`
TotalChickQty *float64 `gorm:"column:total_chick_qty"` TotalChickQty *float64 `gorm:"column:total_chick_qty"`
HandDay *float64 `gorm:"column:hand_day"`
HandHouse *float64 `gorm:"column:hand_house"`
FeedIntake *float64 `gorm:"column:feed_intake"`
EggMesh *float64 `gorm:"column:egg_mesh"`
EggWeight *float64 `gorm:"column:egg_weight"`
CreatedBy uint `gorm:"column:created_by"` CreatedBy uint `gorm:"column:created_by"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"`
@@ -25,10 +28,17 @@ type Recording struct {
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"` ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"` CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
BodyWeights []RecordingBW `gorm:"foreignKey:RecordingId;references:Id"`
Depletions []RecordingDepletion `gorm:"foreignKey:RecordingId;references:Id"` Depletions []RecordingDepletion `gorm:"foreignKey:RecordingId;references:Id"`
Stocks []RecordingStock `gorm:"foreignKey:RecordingId;references:Id"` Stocks []RecordingStock `gorm:"foreignKey:RecordingId;references:Id"`
Eggs []RecordingEgg `gorm:"foreignKey:RecordingId;references:Id"` Eggs []RecordingEgg `gorm:"foreignKey:RecordingId;references:Id"`
LatestApproval *Approval `gorm:"-" json:"-"` LatestApproval *Approval `gorm:"-" json:"-"`
StandardHandDay *float64 `gorm:"-"`
StandardHandHouse *float64 `gorm:"-"`
StandardFeedIntake *float64 `gorm:"-"`
StandardMaxDepletion *float64 `gorm:"-"`
StandardEggMesh *float64 `gorm:"-"`
StandardEggWeight *float64 `gorm:"-"`
StandardFcr *float64 `gorm:"-"`
} }
-15
View File
@@ -1,15 +0,0 @@
package entities
import "time"
type RecordingBW struct {
Id uint `gorm:"primaryKey"`
RecordingId uint `gorm:"column:recording_id;not null;index"`
AvgWeight float64 `gorm:"column:avg_weight;not null"`
Qty float64 `gorm:"column:qty;not null"`
TotalWeight float64 `gorm:"column:total_weight;not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
Recording Recording `gorm:"foreignKey:RecordingId;references:Id"`
}
@@ -22,11 +22,21 @@ type RecordingRelationDTO struct {
ProjectFlockCategory string `json:"project_flock_category"` ProjectFlockCategory string `json:"project_flock_category"`
TotalDepletionQty float64 `json:"total_depletion_qty"` TotalDepletionQty float64 `json:"total_depletion_qty"`
CumDepletionRate float64 `json:"cum_depletion_rate"` CumDepletionRate float64 `json:"cum_depletion_rate"`
DailyGain float64 `json:"daily_gain"`
AvgDailyGain float64 `json:"avg_daily_gain"`
CumIntake int `json:"cum_intake"` CumIntake int `json:"cum_intake"`
FcrValue float64 `json:"fcr_value"` FcrValue float64 `json:"fcr_value"`
TotalChickQty float64 `json:"total_chick_qty"` TotalChickQty float64 `json:"total_chick_qty"`
HandDay float64 `json:"hand_day"`
HandHouse float64 `json:"hand_house"`
FeedIntake float64 `json:"feed_intake"`
EggMesh float64 `json:"egg_mesh"`
EggWeight float64 `json:"egg_weight"`
StandardHandDay *float64 `json:"hand_day_std,omitempty"`
StandardHandHouse *float64 `json:"hand_house_std,omitempty"`
StandardFeedIntake *float64 `json:"feed_intake_std,omitempty"`
StandardMaxDepletion *float64 `json:"max_depletion_std,omitempty"`
StandardEggMesh *float64 `json:"egg_mesh_std,omitempty"`
StandardEggWeight *float64 `json:"egg_weight_std,omitempty"`
StandardFcr *float64 `json:"fcr_std,omitempty"`
Approval approvalDTO.ApprovalRelationDTO `json:"approval"` Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
} }
@@ -39,16 +49,9 @@ type RecordingListDTO struct {
type RecordingDetailDTO struct { type RecordingDetailDTO struct {
RecordingListDTO RecordingListDTO
BodyWeights []RecordingBodyWeightDTO `json:"body_weights"` Depletions []RecordingDepletionDTO `json:"depletions"`
Depletions []RecordingDepletionDTO `json:"depletions"` Stocks []RecordingStockDTO `json:"stocks"`
Stocks []RecordingStockDTO `json:"stocks"` Eggs []RecordingEggDTO `json:"eggs"`
Eggs []RecordingEggDTO `json:"eggs"`
}
type RecordingBodyWeightDTO struct {
AvgWeight float64 `json:"avg_weight"`
Qty float64 `json:"qty"`
TotalWeight float64 `json:"total_weight"`
} }
type RecordingDepletionDTO struct { type RecordingDepletionDTO struct {
@@ -88,11 +91,14 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
day int day int
totalDepletionQty float64 totalDepletionQty float64
cumDepletionRate float64 cumDepletionRate float64
dailyGain float64
avgDailyGain float64
cumIntake int cumIntake int
fcrValue float64 fcrValue float64
totalChickQty float64 totalChickQty float64
handDay float64
handHouse float64
feedIntake float64
eggMesh float64
eggWeight float64
) )
if e.Day != nil { if e.Day != nil {
@@ -104,12 +110,6 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
if e.CumDepletionRate != nil { if e.CumDepletionRate != nil {
cumDepletionRate = *e.CumDepletionRate cumDepletionRate = *e.CumDepletionRate
} }
if e.DailyGain != nil {
dailyGain = *e.DailyGain
}
if e.AvgDailyGain != nil {
avgDailyGain = *e.AvgDailyGain
}
if e.CumIntake != nil { if e.CumIntake != nil {
cumIntake = *e.CumIntake cumIntake = *e.CumIntake
} }
@@ -119,6 +119,21 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
if e.TotalChickQty != nil { if e.TotalChickQty != nil {
totalChickQty = *e.TotalChickQty totalChickQty = *e.TotalChickQty
} }
if e.HandDay != nil {
handDay = *e.HandDay
}
if e.HandHouse != nil {
handHouse = *e.HandHouse
}
if e.FeedIntake != nil {
feedIntake = *e.FeedIntake
}
if e.EggMesh != nil {
eggMesh = *e.EggMesh
}
if e.EggWeight != nil {
eggWeight = *e.EggWeight
}
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.ProjectFlock.Id != 0 { if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.ProjectFlock.Id != 0 {
category := e.ProjectFlockKandang.ProjectFlock.Category category := e.ProjectFlockKandang.ProjectFlock.Category
@@ -139,11 +154,21 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
ProjectFlockCategory: projectFlockCategory, ProjectFlockCategory: projectFlockCategory,
TotalDepletionQty: totalDepletionQty, TotalDepletionQty: totalDepletionQty,
CumDepletionRate: cumDepletionRate, CumDepletionRate: cumDepletionRate,
DailyGain: dailyGain,
AvgDailyGain: avgDailyGain,
CumIntake: cumIntake, CumIntake: cumIntake,
FcrValue: fcrValue, FcrValue: fcrValue,
TotalChickQty: totalChickQty, TotalChickQty: totalChickQty,
HandDay: handDay,
HandHouse: handHouse,
FeedIntake: feedIntake,
EggMesh: eggMesh,
EggWeight: eggWeight,
StandardHandDay: e.StandardHandDay,
StandardHandHouse: e.StandardHandHouse,
StandardFeedIntake: e.StandardFeedIntake,
StandardMaxDepletion: e.StandardMaxDepletion,
StandardEggMesh: e.StandardEggMesh,
StandardEggWeight: e.StandardEggWeight,
StandardFcr: e.StandardFcr,
Approval: latestApproval, Approval: latestApproval,
} }
} }
@@ -183,25 +208,12 @@ func ToRecordingDetailDTO(e entity.Recording) RecordingDetailDTO {
return RecordingDetailDTO{ return RecordingDetailDTO{
RecordingListDTO: listDTO, RecordingListDTO: listDTO,
BodyWeights: ToRecordingBodyWeightDTOs(e.BodyWeights),
Depletions: ToRecordingDepletionDTOs(e.Depletions), Depletions: ToRecordingDepletionDTOs(e.Depletions),
Stocks: ToRecordingStockDTOs(e.Stocks), Stocks: ToRecordingStockDTOs(e.Stocks),
Eggs: eggs, Eggs: eggs,
} }
} }
func ToRecordingBodyWeightDTOs(bodyWeights []entity.RecordingBW) []RecordingBodyWeightDTO {
result := make([]RecordingBodyWeightDTO, len(bodyWeights))
for i, bw := range bodyWeights {
result[i] = RecordingBodyWeightDTO{
AvgWeight: bw.AvgWeight,
Qty: bw.Qty,
TotalWeight: bw.TotalWeight,
}
}
return result
}
func ToRecordingDepletionDTOs(depletions []entity.RecordingDepletion) []RecordingDepletionDTO { func ToRecordingDepletionDTOs(depletions []entity.RecordingDepletion) []RecordingDepletionDTO {
result := make([]RecordingDepletionDTO, len(depletions)) result := make([]RecordingDepletionDTO, len(depletions))
for i, d := range depletions { for i, d := range depletions {
@@ -20,9 +20,6 @@ type RecordingRepository interface {
GetLatestByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (*entity.Recording, error) GetLatestByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (*entity.Recording, error)
GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error) GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error)
CreateBodyWeights(tx *gorm.DB, bodyWeights []entity.RecordingBW) error
DeleteBodyWeights(tx *gorm.DB, recordingID uint) error
CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error
DeleteStocks(tx *gorm.DB, recordingID uint) error DeleteStocks(tx *gorm.DB, recordingID uint) error
ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error) ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error)
@@ -42,10 +39,11 @@ type RecordingRepository interface {
SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error) SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error)
FindPreviousRecording(tx *gorm.DB, projectFlockKandangId uint, currentDay int) (*entity.Recording, error) FindPreviousRecording(tx *gorm.DB, projectFlockKandangId uint, currentDay int) (*entity.Recording, error)
GetTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) GetTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error)
GetAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error) GetTotalChickinByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error)
GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error)
GetFcrID(tx *gorm.DB, projectFlockKandangId uint) (uint, error) GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error)
GetFcrStandardWeightKg(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error) GetCumulativeEggQtyByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error)
GetFcrStandardNumber(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error)
GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error)
GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error) GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error)
GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error) GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error)
@@ -67,7 +65,6 @@ func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB {
Preload("CreatedUser"). Preload("CreatedUser").
Preload("ProjectFlockKandang"). Preload("ProjectFlockKandang").
Preload("ProjectFlockKandang.ProjectFlock"). Preload("ProjectFlockKandang.ProjectFlock").
Preload("BodyWeights").
Preload("Depletions"). Preload("Depletions").
Preload("Depletions.ProductWarehouse"). Preload("Depletions.ProductWarehouse").
Preload("Depletions.ProductWarehouse.Product"). Preload("Depletions.ProductWarehouse.Product").
@@ -114,17 +111,6 @@ func (r *RecordingRepositoryImpl) GenerateNextDay(tx *gorm.DB, projectFlockKanda
return nextRecordingDay(days), nil return nextRecordingDay(days), nil
} }
func (r *RecordingRepositoryImpl) CreateBodyWeights(tx *gorm.DB, bodyWeights []entity.RecordingBW) error {
if len(bodyWeights) == 0 {
return nil
}
return tx.Create(&bodyWeights).Error
}
func (r *RecordingRepositoryImpl) DeleteBodyWeights(tx *gorm.DB, recordingID uint) error {
return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingBW{}).Error
}
func (r *RecordingRepositoryImpl) CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error { func (r *RecordingRepositoryImpl) CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error {
if len(stocks) == 0 { if len(stocks) == 0 {
return nil return nil
@@ -293,21 +279,18 @@ func (r *RecordingRepositoryImpl) GetTotalChick(tx *gorm.DB, projectFlockKandang
return int64(math.Round(total)), nil return int64(math.Round(total)), nil
} }
func (r *RecordingRepositoryImpl) GetAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error) { func (r *RecordingRepositoryImpl) GetTotalChickinByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error) {
var result struct { if projectFlockKandangId == 0 {
TotalWeight float64
TotalQty float64
}
if err := tx.Model(&entity.RecordingBW{}).
Select("COALESCE(SUM(total_weight), 0) AS total_weight, COALESCE(SUM(qty), 0) AS total_qty").
Where("recording_id = ?", recordingID).
Scan(&result).Error; err != nil {
return 0, err
}
if result.TotalQty == 0 {
return 0, nil return 0, nil
} }
return result.TotalWeight / result.TotalQty, nil
var result float64
err := tx.
Table("project_chickins").
Select("COALESCE(SUM(project_chickins.usage_qty), 0)").
Where("project_chickins.project_flock_kandang_id = ?", projectFlockKandangId).
Scan(&result).Error
return result, err
} }
func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) { func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) {
@@ -344,22 +327,48 @@ func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID u
return total, nil return total, nil
} }
func (r *RecordingRepositoryImpl) GetFcrID(tx *gorm.DB, projectFlockKandangId uint) (uint, error) { func (r *RecordingRepositoryImpl) GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error) {
if recordingID == 0 {
return 0, 0, nil
}
var result struct { var result struct {
FcrID uint TotalQty float64
TotalWeightGrams float64
} }
if err := tx.Table("project_flock_kandangs"). err = tx.
Select("project_flocks.fcr_id AS fcr_id"). Table("recording_eggs").
Joins("JOIN project_flocks ON project_flocks.id = project_flock_kandangs.project_flock_id"). Select("COALESCE(SUM(recording_eggs.qty), 0) AS total_qty, COALESCE(SUM(recording_eggs.qty * COALESCE(recording_eggs.weight, 0)), 0) AS total_weight_grams").
Where("project_flock_kandangs.id = ?", projectFlockKandangId). Where("recording_eggs.recording_id = ?", recordingID).
Scan(&result).Error; err != nil { Scan(&result).Error
return 0, err if err != nil {
return 0, 0, err
} }
return result.FcrID, nil return result.TotalQty, result.TotalWeightGrams, nil
} }
func (r *RecordingRepositoryImpl) GetFcrStandardWeightKg(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error) { func (r *RecordingRepositoryImpl) GetCumulativeEggQtyByProjectFlockKandang(
if fcrId == 0 { tx *gorm.DB,
projectFlockKandangId uint,
recordTime time.Time,
) (float64, error) {
if projectFlockKandangId == 0 {
return 0, nil
}
var result float64
err := tx.
Table("recording_eggs").
Select("COALESCE(SUM(recording_eggs.qty), 0)").
Joins("JOIN recordings ON recordings.id = recording_eggs.recording_id").
Where("recordings.project_flock_kandangs_id = ?", projectFlockKandangId).
Where("recordings.record_datetime <= ?", recordTime).
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 return 0, false, nil
} }
@@ -382,49 +391,12 @@ func (r *RecordingRepositoryImpl) GetFcrStandardWeightKg(tx *gorm.DB, fcrId uint
return 0, false, err return 0, false, err
} }
weight := standard.Weight return standard.FcrNumber, true, nil
if weight > 10 {
return weight / 1000, true, nil
}
return weight, true, nil
} }
func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) { func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) {
if projectFlockID == 0 { // Body-weight tracking is removed; keep stub for report compatibility.
return 0, 0, nil return 0, 0, nil
}
totalChickinQty, err := r.getTotalChickinQtyByProjectFlockID(ctx, projectFlockID)
if err != nil {
return 0, 0, err
}
totalDepletion, err := r.GetTotalDepletionByProjectFlockID(ctx, projectFlockID)
if err != nil {
return 0, 0, err
}
actualQty := totalChickinQty - totalDepletion
avgWeight, err := r.GetLatestAvgWeightByProjectFlockID(ctx, projectFlockID)
if err != nil {
return 0, 0, err
}
totalWeight = actualQty * avgWeight
return totalWeight, actualQty, nil
}
func (r *RecordingRepositoryImpl) getTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
var result float64
err := r.DB().WithContext(ctx).
Table("project_chickins").
Select("COALESCE(SUM(project_chickins.usage_qty), 0)").
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = project_chickins.project_flock_kandang_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Scan(&result).Error
return result, err
} }
func (r *RecordingRepositoryImpl) GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) { func (r *RecordingRepositoryImpl) GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
@@ -440,16 +412,8 @@ func (r *RecordingRepositoryImpl) GetTotalDepletionByProjectFlockID(ctx context.
} }
func (r *RecordingRepositoryImpl) GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) { func (r *RecordingRepositoryImpl) GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
var result float64 // Body-weight tracking is removed; keep stub for report compatibility.
err := r.DB().WithContext(ctx). return 0, nil
Table("recording_bws").
Select("COALESCE(AVG(recording_bws.avg_weight), 0)").
Joins("JOIN recordings ON recordings.id = recording_bws.recording_id").
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = recordings.project_flock_kandangs_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Where("recordings.record_datetime = (SELECT MAX(record_datetime) FROM recordings r2 WHERE r2.project_flock_kandangs_id IN (SELECT id FROM project_flock_kandangs WHERE project_flock_id = ?))", projectFlockID).
Scan(&result).Error
return result, err
} }
func (r *RecordingRepositoryImpl) GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) { func (r *RecordingRepositoryImpl) GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
@@ -9,6 +9,7 @@ import (
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"
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
rProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
@@ -121,6 +122,9 @@ func (s recordingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
if err := s.attachLatestApprovals(c.Context(), recordings); err != nil { if err := s.attachLatestApprovals(c.Context(), recordings); err != nil {
return nil, 0, err return nil, 0, err
} }
if err := s.attachProductionStandards(c.Context(), recordings); err != nil {
return nil, 0, err
}
return recordings, total, nil return recordings, total, nil
} }
@@ -138,6 +142,9 @@ func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, erro
if err := s.attachLatestApproval(c.Context(), recording); err != nil { if err := s.attachLatestApproval(c.Context(), recording); err != nil {
return nil, err return nil, err
} }
if err := s.attachProductionStandard(c.Context(), recording); err != nil {
return nil, err
}
return recording, nil return recording, nil
} }
@@ -233,12 +240,6 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
return err return err
} }
mappedBodyWeights := recordingutil.MapBodyWeights(createdRecording.Id, req.BodyWeights)
if err := s.Repository.CreateBodyWeights(tx, mappedBodyWeights); err != nil {
s.Log.Errorf("Failed to persist body weights: %+v", err)
return err
}
mappedStocks := recordingutil.MapStocks(createdRecording.Id, req.Stocks) mappedStocks := recordingutil.MapStocks(createdRecording.Id, req.Stocks)
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil { if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
s.Log.Errorf("Failed to persist stocks: %+v", err) s.Log.Errorf("Failed to persist stocks: %+v", err)
@@ -261,7 +262,7 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
return err return err
} }
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, mappedDepletions, nil, nil, nil, mappedEggs)); err != nil { if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, mappedDepletions, nil, mappedEggs)); err != nil {
s.Log.Errorf("Failed to adjust product warehouses: %+v", err) s.Log.Errorf("Failed to adjust product warehouses: %+v", err)
return err return err
} }
@@ -291,7 +292,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
return nil, err return nil, err
} }
if req.BodyWeights == nil && req.Stocks == nil && req.Depletions == nil && req.Eggs == nil { if req.Stocks == nil && req.Depletions == nil && req.Eggs == nil {
return s.GetOne(c, id) return s.GetOne(c, id)
} }
@@ -311,12 +312,11 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
} }
recordingEntity = recording recordingEntity = recording
hasBodyChanges := req.BodyWeights != nil
hasStockChanges := req.Stocks != nil hasStockChanges := req.Stocks != nil
hasDepletionChanges := req.Depletions != nil hasDepletionChanges := req.Depletions != nil
hasEggChanges := req.Eggs != nil hasEggChanges := req.Eggs != nil
if !hasBodyChanges && !hasStockChanges && !hasDepletionChanges && !hasEggChanges { if !hasStockChanges && !hasDepletionChanges && !hasEggChanges {
return nil return nil
} }
@@ -346,17 +346,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
} }
} }
if hasBodyChanges {
if err := s.Repository.DeleteBodyWeights(tx, recordingEntity.Id); err != nil {
s.Log.Errorf("Failed to clear body weights: %+v", err)
return err
}
if err := s.Repository.CreateBodyWeights(tx, recordingutil.MapBodyWeights(recordingEntity.Id, req.BodyWeights)); err != nil {
s.Log.Errorf("Failed to update body weights: %+v", err)
return err
}
}
if hasStockChanges { if hasStockChanges {
existingStocks, err := s.Repository.ListStocks(tx, recordingEntity.Id) existingStocks, err := s.Repository.ListStocks(tx, recordingEntity.Id)
if err != nil { if err != nil {
@@ -402,7 +391,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
return err return err
} }
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(existingDepletions, mappedDepletions, nil, nil, nil, nil)); err != nil { if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(existingDepletions, mappedDepletions, nil, nil)); err != nil {
s.Log.Errorf("Failed to adjust product warehouses for depletions: %+v", err) s.Log.Errorf("Failed to adjust product warehouses for depletions: %+v", err)
return err return err
} }
@@ -426,13 +415,13 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
return err return err
} }
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, nil, nil, existingEggs, mappedEggs)); err != nil { if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, mappedEggs)); err != nil {
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err) s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
return err return err
} }
} }
if hasBodyChanges || hasStockChanges || hasDepletionChanges { if hasStockChanges || hasDepletionChanges {
if err := s.computeAndUpdateMetrics(ctx, tx, recordingEntity); err != nil { if err := s.computeAndUpdateMetrics(ctx, tx, recordingEntity); err != nil {
s.Log.Errorf("Failed to recompute recording metrics: %+v", err) s.Log.Errorf("Failed to recompute recording metrics: %+v", err)
return err return err
@@ -596,7 +585,7 @@ func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error {
return err return err
} }
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(oldDepletions, nil, nil, nil, oldEggs, nil)); err != nil { if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(oldDepletions, nil, oldEggs, nil)); err != nil {
return err return err
} }
@@ -724,7 +713,6 @@ func (s *recordingService) ReleaseRecordingStocks(ctx context.Context, tx *gorm.
func buildWarehouseDeltas( func buildWarehouseDeltas(
oldDepletions, newDepletions []entity.RecordingDepletion, oldDepletions, newDepletions []entity.RecordingDepletion,
oldStocks, newStocks []entity.RecordingStock,
oldEggs, newEggs []entity.RecordingEgg, oldEggs, newEggs []entity.RecordingEgg,
) map[uint]float64 { ) map[uint]float64 {
deltas := make(map[uint]float64) deltas := make(map[uint]float64)
@@ -775,7 +763,6 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
var prevCumDepletionQty float64 var prevCumDepletionQty float64
var prevCumIntake float64 var prevCumIntake float64
var prevAvgWeight float64
if prevRecording != nil { if prevRecording != nil {
if prevRecording.TotalDepletionQty != nil { if prevRecording.TotalDepletionQty != nil {
prevCumDepletionQty = *prevRecording.TotalDepletionQty prevCumDepletionQty = *prevRecording.TotalDepletionQty
@@ -783,10 +770,6 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
if prevRecording.CumIntake != nil { if prevRecording.CumIntake != nil {
prevCumIntake = float64(*prevRecording.CumIntake) prevCumIntake = float64(*prevRecording.CumIntake)
} }
prevAvgWeight, err = s.Repository.GetAverageBodyWeight(tx, prevRecording.Id)
if err != nil {
return fmt.Errorf("getAverageBodyWeight(prev): %w", err)
}
} }
totalChick, err := s.Repository.GetTotalChick(tx, recording.ProjectFlockKandangId) totalChick, err := s.Repository.GetTotalChick(tx, recording.ProjectFlockKandangId)
@@ -794,20 +777,25 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
return fmt.Errorf("getTotalChick: %w", err) return fmt.Errorf("getTotalChick: %w", err)
} }
currentAvgWeight, err := s.Repository.GetAverageBodyWeight(tx, recording.Id)
if err != nil {
return fmt.Errorf("getAverageBodyWeight(current): %w", err)
}
usageInGrams, err := s.Repository.GetFeedUsageInGrams(tx, recording.Id) usageInGrams, err := s.Repository.GetFeedUsageInGrams(tx, recording.Id)
if err != nil { if err != nil {
return fmt.Errorf("getFeedUsageInGrams: %w", err) return fmt.Errorf("getFeedUsageInGrams: %w", err)
} }
currentAvgGrams := recordingutil.ToGrams(currentAvgWeight) totalEggQty, totalEggWeightGrams, err := s.Repository.GetEggSummaryByRecording(tx, recording.Id)
currentAvgKg := recordingutil.GramsToKg(currentAvgGrams) if err != nil {
prevAvgGrams := recordingutil.ToGrams(prevAvgWeight) return fmt.Errorf("getEggSummaryByRecording: %w", err)
prevAvgKg := recordingutil.GramsToKg(prevAvgGrams) }
cumulativeEggQty, err := s.Repository.GetCumulativeEggQtyByProjectFlockKandang(tx, recording.ProjectFlockKandangId, recording.RecordDatetime)
if err != nil {
return fmt.Errorf("getCumulativeEggQtyByProjectFlockKandang: %w", err)
}
initialChickin, err := s.Repository.GetTotalChickinByProjectFlockKandang(tx, recording.ProjectFlockKandangId)
if err != nil {
return fmt.Errorf("getTotalChickinByProjectFlockKandang: %w", err)
}
currentDepletion := float64(totalDepletionQty) currentDepletion := float64(totalDepletionQty)
cumDepletionQty := prevCumDepletionQty + currentDepletion cumDepletionQty := prevCumDepletionQty + currentDepletion
@@ -840,24 +828,64 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
recording.CumDepletionRate = nil recording.CumDepletionRate = nil
} }
if currentAvgGrams > 0 && prevAvgGrams > 0 { var feedIntake float64
dailyGainKg := (currentAvgGrams - prevAvgGrams) / 1000 if remainingChick > 0 && usageInGrams > 0 {
updates["daily_gain"] = dailyGainKg feedIntake = (usageInGrams / remainingChick) * 1000
recording.DailyGain = &dailyGainKg updates["feed_intake"] = feedIntake
recording.FeedIntake = &feedIntake
} else { } else {
dailyGainKg := 0.0 updates["feed_intake"] = gorm.Expr("NULL")
updates["daily_gain"] = dailyGainKg recording.FeedIntake = nil
recording.DailyGain = &dailyGainKg
} }
if currentAvgKg > 0 && remainingChick > 0 { var handDay float64
avgDailyGain := (currentAvgKg - prevAvgKg) / remainingChick if remainingChick > 0 && totalEggQty >= 0 {
updates["avg_daily_gain"] = avgDailyGain handDay = (totalEggQty / remainingChick) * 100
recording.AvgDailyGain = &avgDailyGain updates["hand_day"] = handDay
recording.HandDay = &handDay
} else { } else {
avgDailyGain := 0.0 updates["hand_day"] = gorm.Expr("NULL")
updates["avg_daily_gain"] = avgDailyGain recording.HandDay = nil
recording.AvgDailyGain = &avgDailyGain }
var handHouse float64
if initialChickin > 0 && cumulativeEggQty >= 0 {
handHouse = cumulativeEggQty / initialChickin
updates["hand_house"] = handHouse
recording.HandHouse = &handHouse
} else {
updates["hand_house"] = gorm.Expr("NULL")
recording.HandHouse = nil
}
var eggMesh float64
if remainingChick > 0 && totalEggWeightGrams > 0 {
eggMesh = (totalEggWeightGrams / remainingChick) * 1000
updates["egg_mesh"] = eggMesh
recording.EggMesh = &eggMesh
} else {
updates["egg_mesh"] = gorm.Expr("NULL")
recording.EggMesh = nil
}
var eggWeight float64
if totalEggQty > 0 && totalEggWeightGrams > 0 {
eggWeight = (totalEggWeightGrams / totalEggQty) * 1000
updates["egg_weight"] = eggWeight
recording.EggWeight = &eggWeight
} else {
updates["egg_weight"] = gorm.Expr("NULL")
recording.EggWeight = nil
}
var fcrValue float64
if usageInGrams > 0 && totalEggWeightGrams > 0 {
fcrValue = totalEggWeightGrams / usageInGrams
updates["fcr_value"] = fcrValue
recording.FcrValue = &fcrValue
} else {
updates["fcr_value"] = gorm.Expr("NULL")
recording.FcrValue = nil
} }
if usageInGrams > 0 && totalChick > 0 { if usageInGrams > 0 && totalChick > 0 {
@@ -882,16 +910,6 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
recording.CumIntake = nil recording.CumIntake = nil
} }
if usageInGrams > 0 && currentAvgKg > 0 {
feedUsageKg := usageInGrams / 1000
fcrValue := feedUsageKg / currentAvgKg
updates["fcr_value"] = fcrValue
recording.FcrValue = &fcrValue
} else {
updates["fcr_value"] = gorm.Expr("NULL")
recording.FcrValue = nil
}
if err := s.Repository.WithTx(tx).PatchOne(ctx, recording.Id, updates, nil); err != nil { if err := s.Repository.WithTx(tx).PatchOne(ctx, recording.Id, updates, nil); err != nil {
return err return err
} }
@@ -997,6 +1015,104 @@ func (s *recordingService) attachLatestApproval(ctx context.Context, item *entit
return nil return nil
} }
type productionStandardValues struct {
HandDay *float64
HandHouse *float64
FeedIntake *float64
MaxDepletion *float64
EggMesh *float64
EggWeight *float64
}
func (s *recordingService) attachProductionStandards(ctx context.Context, items []entity.Recording) error {
if len(items) == 0 {
return nil
}
for i := range items {
if err := s.attachProductionStandard(ctx, &items[i]); err != nil {
s.Log.Warnf("Unable to load production standard for recording %d: %+v", items[i].Id, err)
}
}
return nil
}
func (s *recordingService) attachProductionStandard(ctx context.Context, item *entity.Recording) error {
if item == nil || item.Id == 0 {
return nil
}
if item.Day == nil || *item.Day <= 0 {
return nil
}
if item.ProjectFlockKandang == nil || item.ProjectFlockKandang.ProjectFlock.Id == 0 {
return nil
}
standardID := item.ProjectFlockKandang.ProjectFlock.ProductionStandardId
if standardID == 0 {
return nil
}
week := ((int(*item.Day) - 1) / 7) + 1
if week <= 0 {
return nil
}
category := strings.ToUpper(item.ProjectFlockKandang.ProjectFlock.Category)
db := s.Repository.DB()
standardDetailRepo := rProductionStandard.NewProductionStandardDetailRepository(db)
growthDetailRepo := rProductionStandard.NewStandardGrowthDetailRepository(db)
var standard productionStandardValues
var standardFcr *float64
if category == string(utils.ProjectFlockCategoryLaying) {
detail, err := standardDetailRepo.GetByStandardIDAndWeek(ctx, standardID, week)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if detail != nil {
standard.HandDay = detail.TargetHenDayProduction
standard.HandHouse = detail.TargetHenHouseProduction
standard.EggWeight = detail.TargetEggWeight
standard.EggMesh = detail.TargetEggMass
}
}
growthDetail, err := growthDetailRepo.GetByStandardIDAndWeek(ctx, standardID, week)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if growthDetail != nil {
standard.FeedIntake = growthDetail.FeedIntake
standard.MaxDepletion = growthDetail.MaxDepletion
if category == string(utils.ProjectFlockCategoryLaying) && growthDetail.TargetMeanBw != nil && item.ProjectFlockKandang.ProjectFlock.FcrId > 0 {
targetWeight := *growthDetail.TargetMeanBw
if targetWeight > 10 {
targetWeight = targetWeight / 1000
}
if targetWeight > 0 {
fcrStd, ok, err := s.Repository.GetFcrStandardNumber(db, item.ProjectFlockKandang.ProjectFlock.FcrId, targetWeight)
if err != nil {
return err
}
if ok {
standardFcr = &fcrStd
}
}
}
}
item.StandardHandDay = standard.HandDay
item.StandardHandHouse = standard.HandHouse
item.StandardFeedIntake = standard.FeedIntake
item.StandardMaxDepletion = standard.MaxDepletion
item.StandardEggMesh = standard.EggMesh
item.StandardEggWeight = standard.EggWeight
item.StandardFcr = standardFcr
return nil
}
func uniqueUintSlice(values []uint) []uint { func uniqueUintSlice(values []uint) []uint {
if len(values) == 0 { if len(values) == 0 {
return nil return nil
@@ -1,12 +1,6 @@
package validation package validation
type ( type (
BodyWeight struct {
AvgWeight float64 `json:"avg_weight" validate:"required"`
Qty float64 `json:"qty" validate:"required,gt=0"`
TotalWeight *float64 `json:"total_weight,omitempty" validate:"omitempty,gte=0"`
}
Stock struct { Stock struct {
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"` ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
Qty float64 `json:"qty" validate:"required,gte=0"` Qty float64 `json:"qty" validate:"required,gte=0"`
@@ -27,14 +21,12 @@ type (
type Create struct { type Create struct {
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"` ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
BodyWeights []BodyWeight `json:"body_weights" validate:"dive"`
Stocks []Stock `json:"stocks" validate:"dive"` Stocks []Stock `json:"stocks" validate:"dive"`
Depletions []Depletion `json:"depletions" validate:"dive"` Depletions []Depletion `json:"depletions" validate:"dive"`
Eggs []Egg `json:"eggs" validate:"omitempty,dive"` Eggs []Egg `json:"eggs" validate:"omitempty,dive"`
} }
type Update struct { type Update struct {
BodyWeights []BodyWeight `json:"body_weights,omitempty" validate:"omitempty,dive"`
Stocks []Stock `json:"stocks,omitempty" validate:"omitempty,dive"` Stocks []Stock `json:"stocks,omitempty" validate:"omitempty,dive"`
Depletions []Depletion `json:"depletions,omitempty" validate:"omitempty,dive"` Depletions []Depletion `json:"depletions,omitempty" validate:"omitempty,dive"`
Eggs []Egg `json:"eggs,omitempty" validate:"omitempty,dive"` Eggs []Egg `json:"eggs,omitempty" validate:"omitempty,dive"`
@@ -5,31 +5,6 @@ import (
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
) )
func MapBodyWeights(recordingID uint, items []validation.BodyWeight) []entity.RecordingBW {
if len(items) == 0 {
return nil
}
result := make([]entity.RecordingBW, 0, len(items))
for _, item := range items {
var totalWeight float64
if item.TotalWeight != nil {
totalWeight = *item.TotalWeight
}
if totalWeight <= 0 {
totalWeight = item.AvgWeight * item.Qty
}
result = append(result, entity.RecordingBW{
RecordingId: recordingID,
AvgWeight: item.AvgWeight,
Qty: item.Qty,
TotalWeight: totalWeight,
})
}
return result
}
func MapStocks(recordingID uint, items []validation.Stock) []entity.RecordingStock { func MapStocks(recordingID uint, items []validation.Stock) []entity.RecordingStock {
if len(items) == 0 { if len(items) == 0 {
return nil return nil
@@ -86,20 +61,3 @@ func MapEggs(recordingID uint, createdBy uint, items []validation.Egg) []entity.
} }
return result return result
} }
func ToGrams(weight float64) float64 {
if weight <= 0 {
return 0
}
if weight < 10 {
return weight * 1000
}
return weight
}
func GramsToKg(grams float64) float64 {
if grams <= 0 {
return 0
}
return grams / 1000
}