mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-23 23:05:44 +00:00
feat(BE-390): calculation dashboard
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/validations"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DashboardRepository interface {
|
||||
repository.BaseRepository[entity.Dashboard]
|
||||
GetFeedUsageByUom(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]FeedUsageByUom, error)
|
||||
SumDepletions(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumInitialPopulation(ctx context.Context, endDate time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumSapronakCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumBopCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumEkspedisiCost(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumSellingPrice(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (SellingPriceAggregate, error)
|
||||
SumEggProductionWeightGrams(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumEggProductionWeightKg(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
GetRecordingWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]RecordingWeeklyMetric, error)
|
||||
GetUniformityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]UniformityWeeklyMetric, error)
|
||||
GetStandardWeeklyMetrics(ctx context.Context, weeks []int, filters *validation.DashboardFilter) ([]StandardWeeklyMetric, error)
|
||||
GetStandardFcrWeekly(ctx context.Context, weeks []int, filters *validation.DashboardFilter) ([]StandardWeeklyFcrMetric, error)
|
||||
GetComparisonSeries(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonSeries, error)
|
||||
GetComparisonWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType, metric string) ([]ComparisonWeeklyMetric, error)
|
||||
GetComparisonWeeklyUniformityMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonUniformityMetric, error)
|
||||
GetEggQualityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]EggQualityWeeklyMetric, error)
|
||||
GetEggWeightWeeklyGrams(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]WeeklyEggWeightMetric, error)
|
||||
GetFeedUsageWeeklyByUom(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]WeeklyFeedUsageMetric, error)
|
||||
}
|
||||
|
||||
type DashboardRepositoryImpl struct {
|
||||
*repository.BaseRepositoryImpl[entity.Dashboard]
|
||||
}
|
||||
|
||||
func NewDashboardRepository(db *gorm.DB) DashboardRepository {
|
||||
return &DashboardRepositoryImpl{
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.Dashboard](db),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,672 @@
|
||||
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
|
||||
HandDay 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 hand_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
|
||||
}
|
||||
|
||||
source := r.standardSourceSubquery(filters)
|
||||
if source == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var rows []StandardWeeklyFcrMetric
|
||||
db := r.DB().WithContext(ctx).
|
||||
Table("standard_growth_details AS sgd").
|
||||
Select(`
|
||||
sgd.week AS week,
|
||||
COALESCE(AVG(
|
||||
COALESCE(
|
||||
(
|
||||
SELECT fs.fcr_number
|
||||
FROM fcr_standards fs
|
||||
WHERE fs.fcr_id = src.fcr_id
|
||||
AND fs.weight >= CASE WHEN sgd.target_mean_bw > 10 THEN sgd.target_mean_bw / 1000 ELSE sgd.target_mean_bw END
|
||||
ORDER BY fs.weight ASC
|
||||
LIMIT 1
|
||||
),
|
||||
(
|
||||
SELECT fs.fcr_number
|
||||
FROM fcr_standards fs
|
||||
WHERE fs.fcr_id = src.fcr_id
|
||||
ORDER BY fs.weight DESC
|
||||
LIMIT 1
|
||||
)
|
||||
)
|
||||
), 0) AS std_fcr`).
|
||||
Joins("JOIN (?) AS src ON src.production_standard_id = sgd.production_standard_id", source).
|
||||
Where("sgd.week IN ?", weeks)
|
||||
|
||||
if err := db.Group("sgd.week").Order("sgd.week ASC").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.qty * re.weight), 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.qty * re.weight), 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user