mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-25 15:55:44 +00:00
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:
+54
@@ -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;
|
||||||
+44
@@ -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,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:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,18 +49,11 @@ 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 {
|
||||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Qty float64 `json:"qty"`
|
Qty float64 `json:"qty"`
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user