mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
726 lines
25 KiB
Go
726 lines
25 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/validations"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type SellingPriceAggregate struct {
|
|
TotalPrice float64
|
|
TotalWeight float64
|
|
}
|
|
|
|
type FeedUsageByUom struct {
|
|
TotalQty float64
|
|
UomName string
|
|
}
|
|
|
|
type RecordingWeeklyMetric struct {
|
|
Week int
|
|
HenDay float64
|
|
EggWeight float64
|
|
FeedIntake float64
|
|
FcrValue float64
|
|
CumDepletionRate float64
|
|
}
|
|
|
|
type UniformityWeeklyMetric struct {
|
|
Week int
|
|
Uniformity float64
|
|
AverageWeight float64
|
|
}
|
|
|
|
type StandardWeeklyMetric struct {
|
|
Week int
|
|
StdLaying float64
|
|
StdEggWeight float64
|
|
StdFeedIntake float64
|
|
StdUniformity float64
|
|
StdDepletion float64
|
|
StdBodyWeight float64
|
|
}
|
|
|
|
type StandardWeeklyFcrMetric struct {
|
|
Week int
|
|
StdFcr float64
|
|
}
|
|
|
|
type ComparisonSeries struct {
|
|
Id uint
|
|
Label string
|
|
}
|
|
|
|
type ComparisonWeeklyMetric struct {
|
|
Week int
|
|
SeriesId uint
|
|
Value float64
|
|
}
|
|
|
|
type ComparisonUniformityMetric struct {
|
|
Week int
|
|
SeriesId uint
|
|
Uniformity float64
|
|
AverageWeight float64
|
|
}
|
|
|
|
type EggQualityWeeklyMetric struct {
|
|
Week int
|
|
NormalQty float64
|
|
AbnormalQty float64
|
|
TotalQty float64
|
|
}
|
|
|
|
type WeeklyEggWeightMetric struct {
|
|
Week int
|
|
EggWeightGrams float64
|
|
}
|
|
|
|
type WeeklyFeedUsageMetric struct {
|
|
Week int
|
|
TotalQty float64
|
|
UomName string
|
|
}
|
|
|
|
func applyDashboardFilters(db *gorm.DB, filters *validation.DashboardFilter) *gorm.DB {
|
|
if filters == nil {
|
|
return db
|
|
}
|
|
if len(filters.FlockIds) > 0 {
|
|
db = db.Where("pfk.project_flock_id IN ?", filters.FlockIds)
|
|
}
|
|
if len(filters.KandangIds) > 0 {
|
|
db = db.Where("k.id IN ?", filters.KandangIds)
|
|
}
|
|
if len(filters.LokasiIds) > 0 {
|
|
db = db.Where("k.location_id IN ?", filters.LokasiIds)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetRecordingWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]RecordingWeeklyMetric, error) {
|
|
var rows []RecordingWeeklyMetric
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recordings AS r").
|
|
Select(`((r.day - 1) / 7 + 1) AS week,
|
|
COALESCE(AVG(r.hen_day), 0) AS hen_day,
|
|
COALESCE(AVG(r.egg_weight), 0) AS egg_weight,
|
|
COALESCE(AVG(r.feed_intake), 0) AS feed_intake,
|
|
COALESCE(AVG(r.fcr_value), 0) AS fcr_value,
|
|
COALESCE(AVG(r.cum_depletion_rate), 0) AS cum_depletion_rate`).
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("r.day IS NOT NULL AND r.day > 0")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("week").Order("week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetUniformityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]UniformityWeeklyMetric, error) {
|
|
var rows []UniformityWeeklyMetric
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("project_flock_kandang_uniformity AS u").
|
|
Select(`u.week AS week,
|
|
COALESCE(AVG(u.uniformity), 0) AS uniformity,
|
|
COALESCE(AVG((u.chart_data->'statistics'->>'average_weight')::numeric), 0) AS average_weight`).
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = u.project_flock_kandang_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("u.uniform_date IS NOT NULL").
|
|
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end)
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("u.week").Order("u.week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetStandardWeeklyMetrics(ctx context.Context, weeks []int, filters *validation.DashboardFilter) ([]StandardWeeklyMetric, error) {
|
|
if len(weeks) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
standardIDs := r.standardIDSubquery(filters)
|
|
if standardIDs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var rows []StandardWeeklyMetric
|
|
db := r.DB().WithContext(ctx).
|
|
Table("standard_growth_details AS sgd").
|
|
Select(`sgd.week AS week,
|
|
COALESCE(AVG(psd.target_hen_day_production), 0) AS std_laying,
|
|
COALESCE(AVG(psd.target_egg_weight), 0) AS std_egg_weight,
|
|
COALESCE(AVG(sgd.feed_intake), 0) AS std_feed_intake,
|
|
COALESCE(AVG(sgd.min_uniformity), 0) AS std_uniformity,
|
|
COALESCE(AVG(sgd.max_depletion), 0) AS std_depletion,
|
|
COALESCE(AVG(sgd.target_mean_bw), 0) AS std_body_weight`).
|
|
Joins("LEFT JOIN production_standard_details AS psd ON psd.production_standard_id = sgd.production_standard_id AND psd.week = sgd.week").
|
|
Where("sgd.week IN ?", weeks).
|
|
Where("sgd.production_standard_id IN (?)", standardIDs)
|
|
|
|
if err := db.Group("sgd.week").Order("sgd.week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetStandardFcrWeekly(ctx context.Context, weeks []int, filters *validation.DashboardFilter) ([]StandardWeeklyFcrMetric, error) {
|
|
if len(weeks) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
filterClause := ""
|
|
filterArgs := make([]interface{}, 0)
|
|
if filters != nil {
|
|
if len(filters.FlockIds) > 0 {
|
|
filterClause += " AND pf.id IN ?"
|
|
filterArgs = append(filterArgs, filters.FlockIds)
|
|
}
|
|
if len(filters.KandangIds) > 0 {
|
|
filterClause += " AND k.id IN ?"
|
|
filterArgs = append(filterArgs, filters.KandangIds)
|
|
}
|
|
if len(filters.LokasiIds) > 0 {
|
|
filterClause += " AND k.location_id IN ?"
|
|
filterArgs = append(filterArgs, filters.LokasiIds)
|
|
}
|
|
}
|
|
|
|
query := fmt.Sprintf(`
|
|
WITH src AS (
|
|
SELECT DISTINCT pf.production_standard_id, pf.fcr_id
|
|
FROM project_flocks pf
|
|
JOIN project_flock_kandangs pfk ON pfk.project_flock_id = pf.id
|
|
JOIN kandangs k ON k.id = pfk.kandang_id
|
|
WHERE pf.production_standard_id > 0 AND pf.fcr_id > 0
|
|
%s
|
|
),
|
|
actual AS (
|
|
SELECT u.week AS week,
|
|
pf.fcr_id AS fcr_id,
|
|
AVG((u.chart_data->'statistics'->>'average_weight')::numeric) AS avg_weight
|
|
FROM project_flock_kandang_uniformity u
|
|
JOIN project_flock_kandangs pfk ON pfk.id = u.project_flock_kandang_id
|
|
JOIN project_flocks pf ON pf.id = pfk.project_flock_id
|
|
JOIN kandangs k ON k.id = pfk.kandang_id
|
|
WHERE u.week IN ? AND u.uniform_date IS NOT NULL AND pf.fcr_id > 0
|
|
%s
|
|
GROUP BY u.week, pf.fcr_id
|
|
),
|
|
target AS (
|
|
SELECT sgd.week AS week,
|
|
src.fcr_id AS fcr_id,
|
|
AVG(sgd.target_mean_bw) AS target_mean_bw
|
|
FROM standard_growth_details sgd
|
|
JOIN src ON src.production_standard_id = sgd.production_standard_id
|
|
WHERE sgd.week IN ?
|
|
GROUP BY sgd.week, src.fcr_id
|
|
),
|
|
weights AS (
|
|
SELECT COALESCE(a.week, t.week) AS week,
|
|
COALESCE(a.fcr_id, t.fcr_id) AS fcr_id,
|
|
COALESCE(
|
|
CASE WHEN a.avg_weight > 10 THEN a.avg_weight / 1000 ELSE a.avg_weight END,
|
|
CASE WHEN t.target_mean_bw > 10 THEN t.target_mean_bw / 1000 ELSE t.target_mean_bw END
|
|
) AS weight
|
|
FROM actual a
|
|
FULL OUTER JOIN target t ON t.week = a.week AND t.fcr_id = a.fcr_id
|
|
)
|
|
SELECT w.week AS week,
|
|
COALESCE(AVG(
|
|
COALESCE(
|
|
(SELECT fs.fcr_number
|
|
FROM fcr_standards fs
|
|
WHERE fs.fcr_id = w.fcr_id
|
|
AND fs.weight >= w.weight
|
|
ORDER BY fs.weight ASC
|
|
LIMIT 1),
|
|
(SELECT fs.fcr_number
|
|
FROM fcr_standards fs
|
|
WHERE fs.fcr_id = w.fcr_id
|
|
ORDER BY fs.weight DESC
|
|
LIMIT 1)
|
|
)
|
|
), 0) AS std_fcr
|
|
FROM weights w
|
|
GROUP BY w.week
|
|
ORDER BY w.week ASC
|
|
`, filterClause, filterClause)
|
|
|
|
args := make([]interface{}, 0, len(filterArgs)*2+2)
|
|
args = append(args, filterArgs...)
|
|
args = append(args, weeks)
|
|
args = append(args, filterArgs...)
|
|
args = append(args, weeks)
|
|
|
|
var rows []StandardWeeklyFcrMetric
|
|
if err := r.DB().WithContext(ctx).Raw(query, args...).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumEggProductionWeightGrams(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
var total float64
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_eggs AS re").
|
|
Select("COALESCE(SUM(re.weight * 1000), 0)").
|
|
Joins("JOIN recordings AS r ON r.id = re.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumEggProductionWeightKg(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
grams, err := r.SumEggProductionWeightGrams(ctx, start, end, filters)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return grams / 1000, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetFeedUsageByUom(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]FeedUsageByUom, error) {
|
|
var rows []FeedUsageByUom
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_stocks AS rs").
|
|
Select("COALESCE(SUM(rs.usage_qty), 0) + COALESCE(SUM(rs.pending_qty), 0) AS total_qty, LOWER(uoms.name) AS uom_name").
|
|
Joins("JOIN recordings AS r ON r.id = rs.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id").
|
|
Joins("JOIN products AS p ON p.id = pw.product_id").
|
|
Joins("JOIN uoms ON uoms.id = p.uom_id").
|
|
Joins("JOIN flags AS f ON f.flagable_id = p.id AND f.flagable_type = ? AND UPPER(f.name) = ?", entity.FlagableTypeProduct, "PAKAN").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("LOWER(uoms.name)").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumDepletions(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
var total float64
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_depletions AS rd").
|
|
Select("COALESCE(SUM(rd.qty), 0)").
|
|
Joins("JOIN recordings AS r ON r.id = rd.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumInitialPopulation(ctx context.Context, endDate time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
var total float64
|
|
endOfDate := endDate.AddDate(0, 0, 1)
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("project_chickins AS pc").
|
|
Select("COALESCE(SUM(pc.usage_qty), 0)").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = pc.project_flock_kandang_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("pc.chick_in_date < ?", endOfDate).
|
|
Where("pc.deleted_at IS NULL")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumSellingPrice(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (SellingPriceAggregate, error) {
|
|
var result SellingPriceAggregate
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("marketing_delivery_products AS mdp").
|
|
Select("COALESCE(SUM(mdp.total_price), 0) AS total_price, COALESCE(SUM(mdp.total_weight), 0) AS total_weight").
|
|
Joins("JOIN marketing_products AS mp ON mp.id = mdp.marketing_product_id").
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = mp.product_warehouse_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = pw.project_flock_kandang_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("mdp.delivery_date IS NOT NULL").
|
|
Where("mdp.delivery_date >= ? AND mdp.delivery_date < ?", start, end)
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Scan(&result).Error; err != nil {
|
|
return SellingPriceAggregate{}, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumSapronakCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
var total float64
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("purchase_items AS pi").
|
|
Select("COALESCE(SUM(pi.total_price), 0) AS total").
|
|
Joins("JOIN products AS p ON p.id = pi.product_id").
|
|
Joins("JOIN flags AS f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
|
Joins("LEFT JOIN product_warehouses AS pw ON pw.id = pi.product_warehouse_id").
|
|
Joins("LEFT JOIN project_flock_kandangs AS pfk ON pfk.id = COALESCE(pi.project_flock_kandang_id, pw.project_flock_kandang_id)").
|
|
Joins("LEFT JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("f.name IN ?", []utils.FlagType{utils.FlagDOC, utils.FlagPakan, utils.FlagOVK}).
|
|
Where("pi.received_date IS NOT NULL").
|
|
Where("pi.received_date >= ? AND pi.received_date < ?", start, end)
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumBopCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
return r.sumExpenseRealization(ctx, start, end, filters, func(db *gorm.DB) *gorm.DB {
|
|
return db.
|
|
Where("e.category = ?", utils.ExpenseCategoryBOP).
|
|
Joins("LEFT JOIN nonstocks AS n ON n.id = en.nonstock_id").
|
|
Joins("LEFT JOIN flags AS f ON f.flagable_id = n.id AND f.flagable_type = ? AND f.name = ?", entity.FlagableTypeNonstock, utils.FlagEkspedisi).
|
|
Where("f.id IS NULL")
|
|
})
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) SumEkspedisiCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error) {
|
|
return r.sumExpenseRealization(ctx, start, end, filters, func(db *gorm.DB) *gorm.DB {
|
|
return db.
|
|
Joins("JOIN nonstocks AS n ON n.id = en.nonstock_id").
|
|
Joins("JOIN flags AS f ON f.flagable_id = n.id AND f.flagable_type = ?", entity.FlagableTypeNonstock).
|
|
Where("f.name = ?", utils.FlagEkspedisi)
|
|
})
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) sumExpenseRealization(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, modifier func(*gorm.DB) *gorm.DB) (float64, error) {
|
|
var total float64
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("expense_realizations AS er").
|
|
Select("COALESCE(SUM(er.qty * er.price), 0) AS total").
|
|
Joins("JOIN expense_nonstocks AS en ON en.id = er.expense_nonstock_id").
|
|
Joins("JOIN expenses AS e ON e.id = en.expense_id").
|
|
Joins("LEFT JOIN project_flock_kandangs AS pfk ON pfk.id = en.project_flock_kandang_id").
|
|
Joins("LEFT JOIN kandangs AS k ON k.id = COALESCE(en.kandang_id, pfk.kandang_id)").
|
|
Where("e.realization_date >= ? AND e.realization_date < ?", start, end)
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if modifier != nil {
|
|
db = modifier(db)
|
|
}
|
|
|
|
if err := db.Scan(&total).Error; err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return total, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) standardIDSubquery(filters *validation.DashboardFilter) *gorm.DB {
|
|
db := r.DB().
|
|
Table("project_flocks AS pf").
|
|
Select("DISTINCT pf.production_standard_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("pf.production_standard_id > 0")
|
|
|
|
if filters != nil {
|
|
if len(filters.FlockIds) > 0 {
|
|
db = db.Where("pf.id IN ?", filters.FlockIds)
|
|
}
|
|
if len(filters.KandangIds) > 0 {
|
|
db = db.Where("k.id IN ?", filters.KandangIds)
|
|
}
|
|
if len(filters.LokasiIds) > 0 {
|
|
db = db.Where("k.location_id IN ?", filters.LokasiIds)
|
|
}
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) standardSourceSubquery(filters *validation.DashboardFilter) *gorm.DB {
|
|
db := r.DB().
|
|
Table("project_flocks AS pf").
|
|
Select("DISTINCT pf.production_standard_id, pf.fcr_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("pf.production_standard_id > 0").
|
|
Where("pf.fcr_id > 0")
|
|
|
|
if filters != nil {
|
|
if len(filters.FlockIds) > 0 {
|
|
db = db.Where("pf.id IN ?", filters.FlockIds)
|
|
}
|
|
if len(filters.KandangIds) > 0 {
|
|
db = db.Where("k.id IN ?", filters.KandangIds)
|
|
}
|
|
if len(filters.LokasiIds) > 0 {
|
|
db = db.Where("k.location_id IN ?", filters.LokasiIds)
|
|
}
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetComparisonSeries(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonSeries, error) {
|
|
seriesExpr, labelExpr, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rows []ComparisonSeries
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recordings AS r").
|
|
Select(fmt.Sprintf("%s AS id, %s AS label", seriesExpr, labelExpr)).
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
|
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group(groupExpr).Order(orderExpr).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetComparisonWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType, metric string) ([]ComparisonWeeklyMetric, error) {
|
|
seriesExpr, _, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
metricExpr, err := comparisonMetricColumn(metric)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rows []ComparisonWeeklyMetric
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recordings AS r").
|
|
Select(fmt.Sprintf(`((r.day - 1) / 7 + 1) AS week,
|
|
%s AS series_id,
|
|
COALESCE(AVG(%s), 0) AS value`, seriesExpr, metricExpr)).
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
|
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("r.day IS NOT NULL AND r.day > 0")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
groupBy := fmt.Sprintf("week, %s", groupExpr)
|
|
orderBy := fmt.Sprintf("week ASC, %s", orderExpr)
|
|
if err := db.Group(groupBy).Order(orderBy).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetComparisonWeeklyUniformityMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonUniformityMetric, error) {
|
|
seriesExpr, _, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rows []ComparisonUniformityMetric
|
|
db := r.DB().WithContext(ctx).
|
|
Table("project_flock_kandang_uniformity AS u").
|
|
Select(fmt.Sprintf(`u.week AS week,
|
|
%s AS series_id,
|
|
COALESCE(AVG(u.uniformity), 0) AS uniformity,
|
|
COALESCE(AVG((u.chart_data->'statistics'->>'average_weight')::numeric), 0) AS average_weight`, seriesExpr)).
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = u.project_flock_kandang_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
|
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
Where("u.uniform_date IS NOT NULL").
|
|
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end)
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
groupBy := fmt.Sprintf("u.week, %s", groupExpr)
|
|
orderBy := fmt.Sprintf("u.week ASC, %s", orderExpr)
|
|
if err := db.Group(groupBy).Order(orderBy).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetEggQualityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]EggQualityWeeklyMetric, error) {
|
|
var rows []EggQualityWeeklyMetric
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_eggs AS re").
|
|
Select(`
|
|
((r.day - 1) / 7 + 1) AS week,
|
|
COALESCE(SUM(CASE WHEN f.name = ? THEN re.qty ELSE 0 END), 0) AS normal_qty,
|
|
COALESCE(SUM(CASE WHEN f.name IN (?, ?, ?) THEN re.qty ELSE 0 END), 0) AS abnormal_qty,
|
|
COALESCE(SUM(re.qty), 0) AS total_qty`,
|
|
utils.FlagTelurUtuh,
|
|
utils.FlagTelurPutih,
|
|
utils.FlagTelurRetak,
|
|
utils.FlagTelurPecah,
|
|
).
|
|
Joins("JOIN recordings AS r ON r.id = re.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = re.product_warehouse_id").
|
|
Joins("JOIN products AS p ON p.id = pw.product_id").
|
|
Joins("JOIN flags AS f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
|
Where("f.name IN ?", []utils.FlagType{utils.FlagTelurUtuh, utils.FlagTelurPutih, utils.FlagTelurRetak, utils.FlagTelurPecah}).
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("r.day IS NOT NULL AND r.day > 0")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("week").Order("week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetEggWeightWeeklyGrams(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]WeeklyEggWeightMetric, error) {
|
|
var rows []WeeklyEggWeightMetric
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_eggs AS re").
|
|
Select(`
|
|
((r.day - 1) / 7 + 1) AS week,
|
|
COALESCE(SUM(re.weight * 1000), 0) AS egg_weight_grams`).
|
|
Joins("JOIN recordings AS r ON r.id = re.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("r.day IS NOT NULL AND r.day > 0")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("week").Order("week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *DashboardRepositoryImpl) GetFeedUsageWeeklyByUom(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]WeeklyFeedUsageMetric, error) {
|
|
var rows []WeeklyFeedUsageMetric
|
|
|
|
db := r.DB().WithContext(ctx).
|
|
Table("recording_stocks AS rs").
|
|
Select(`
|
|
((r.day - 1) / 7 + 1) AS week,
|
|
COALESCE(SUM(rs.usage_qty), 0) + COALESCE(SUM(rs.pending_qty), 0) AS total_qty,
|
|
LOWER(uoms.name) AS uom_name`).
|
|
Joins("JOIN recordings AS r ON r.id = rs.recording_id").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id").
|
|
Joins("JOIN products AS p ON p.id = pw.product_id").
|
|
Joins("JOIN uoms ON uoms.id = p.uom_id").
|
|
Joins("JOIN flags AS f ON f.flagable_id = p.id AND f.flagable_type = ? AND UPPER(f.name) = ?", entity.FlagableTypeProduct, "PAKAN").
|
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
Where("r.deleted_at IS NULL").
|
|
Where("r.day IS NOT NULL AND r.day > 0")
|
|
|
|
db = applyDashboardFilters(db, filters)
|
|
|
|
if err := db.Group("week, LOWER(uoms.name)").Order("week ASC").Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func comparisonSeriesColumns(comparisonType string) (string, string, string, string, error) {
|
|
switch strings.ToUpper(strings.TrimSpace(comparisonType)) {
|
|
case validation.ComparisonTypeFarm:
|
|
return "loc.id", "loc.name", "loc.id, loc.name", "loc.name", nil
|
|
case validation.ComparisonTypeFlock:
|
|
return "pf.id", "pf.flock_name", "pf.id, pf.flock_name", "pf.flock_name", nil
|
|
case validation.ComparisonTypeKandang:
|
|
return "k.id", "k.name", "k.id, k.name", "k.name", nil
|
|
default:
|
|
return "", "", "", "", fmt.Errorf("invalid comparison_type")
|
|
}
|
|
}
|
|
|
|
func comparisonMetricColumn(metric string) (string, error) {
|
|
switch strings.ToLower(strings.TrimSpace(metric)) {
|
|
case validation.MetricFcr:
|
|
return "r.fcr_value", nil
|
|
case validation.MetricMortality:
|
|
return "r.cum_depletion_rate", nil
|
|
case validation.MetricLaying:
|
|
return "r.hen_day", nil
|
|
case validation.MetricEggWeight:
|
|
return "r.egg_weight", nil
|
|
case validation.MetricFeedIntake:
|
|
return "r.feed_intake", nil
|
|
default:
|
|
return "", fmt.Errorf("invalid metric")
|
|
}
|
|
}
|