mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'fix/delivery-order' into feat/open-api-v1
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
-- Remove convertion fields from marketing_delivery_products table
|
||||
ALTER TABLE marketing_delivery_products
|
||||
DROP COLUMN IF EXISTS weight_per_convertion;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add convertion fields to marketing_delivery_products table
|
||||
ALTER TABLE marketing_delivery_products
|
||||
ADD COLUMN IF NOT EXISTS weight_per_convertion NUMERIC(15, 3);
|
||||
|
||||
@@ -12,6 +12,7 @@ type MarketingDeliveryProduct struct {
|
||||
UnitPrice float64 `gorm:"type:numeric(15,3)"`
|
||||
TotalWeight float64 `gorm:"type:numeric(15,3)"`
|
||||
AvgWeight float64 `gorm:"type:numeric(15,3)"`
|
||||
WeightPerConvertion *float64 `gorm:"type:numeric(15,3)"`
|
||||
TotalPrice float64 `gorm:"type:numeric(15,3)"`
|
||||
DeliveryDate *time.Time `gorm:"type:timestamptz"`
|
||||
VehicleNumber string `gorm:"type:varchar(50)"`
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||
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"
|
||||
@@ -105,6 +106,15 @@ func applyDashboardFilters(db *gorm.DB, filters *validation.DashboardFilter) *go
|
||||
return db
|
||||
}
|
||||
|
||||
func dashboardUniformityWeekExpr() string {
|
||||
return fmt.Sprintf(`CASE
|
||||
WHEN u.uniform_date IS NULL OR pc.chick_in_date IS NULL THEN 0
|
||||
WHEN u.uniform_date::date < pc.chick_in_date THEN 0
|
||||
WHEN UPPER(pf.category) = 'LAYING' THEN (((u.uniform_date::date - pc.chick_in_date)::int) / 7) + %d
|
||||
ELSE (((u.uniform_date::date - pc.chick_in_date)::int) / 7) + 1
|
||||
END`, config.LayingWeekStart())
|
||||
}
|
||||
|
||||
func (r *DashboardRepositoryImpl) GetRecordingWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]RecordingWeeklyMetric, error) {
|
||||
var rows []RecordingWeeklyMetric
|
||||
|
||||
@@ -140,21 +150,29 @@ func (r *DashboardRepositoryImpl) GetRecordingWeeklyMetrics(ctx context.Context,
|
||||
|
||||
func (r *DashboardRepositoryImpl) GetUniformityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]UniformityWeeklyMetric, error) {
|
||||
var rows []UniformityWeeklyMetric
|
||||
weekExpr := dashboardUniformityWeekExpr()
|
||||
|
||||
db := r.DB().WithContext(ctx).
|
||||
Table("project_flock_kandang_uniformity AS u").
|
||||
Select(`u.week AS week,
|
||||
Select(fmt.Sprintf(`%s AS week,
|
||||
COALESCE(AVG(u.uniformity), 0) AS uniformity,
|
||||
COALESCE(AVG((u.chart_data->'statistics'->>'average_weight')::numeric), 0) AS average_weight,
|
||||
MAX(u.uniform_date) AS uniform_date`).
|
||||
COALESCE(AVG((u.chart_data->'statistics'->>'average_weight')::numeric), 0) AS average_weight`, weekExpr)).
|
||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = u.project_flock_kandang_id").
|
||||
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
||||
Joins(`JOIN (
|
||||
SELECT project_flock_kandang_id, MIN(chick_in_date)::date AS chick_in_date
|
||||
FROM project_chickins
|
||||
WHERE deleted_at IS NULL
|
||||
GROUP BY project_flock_kandang_id
|
||||
) AS pc ON pc.project_flock_kandang_id = u.project_flock_kandang_id`).
|
||||
Where("u.uniform_date IS NOT NULL").
|
||||
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end)
|
||||
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end).
|
||||
Where("u.uniform_date::date >= pc.chick_in_date")
|
||||
|
||||
db = applyDashboardFilters(db, filters)
|
||||
|
||||
if err := db.Group("u.week").Order("u.week ASC").Scan(&rows).Error; err != nil {
|
||||
if err := db.Group("week").Order("week ASC").Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -520,23 +538,31 @@ func (r *DashboardRepositoryImpl) GetComparisonWeeklyUniformityMetrics(ctx conte
|
||||
}
|
||||
|
||||
var rows []ComparisonUniformityMetric
|
||||
weekExpr := dashboardUniformityWeekExpr()
|
||||
db := r.DB().WithContext(ctx).
|
||||
Table("project_flock_kandang_uniformity AS u").
|
||||
Select(fmt.Sprintf(`u.week AS week,
|
||||
Select(fmt.Sprintf(`%s 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)).
|
||||
COALESCE(AVG((u.chart_data->'statistics'->>'average_weight')::numeric), 0) AS average_weight`, weekExpr, 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").
|
||||
Joins(`JOIN (
|
||||
SELECT project_flock_kandang_id, MIN(chick_in_date)::date AS chick_in_date
|
||||
FROM project_chickins
|
||||
WHERE deleted_at IS NULL
|
||||
GROUP BY project_flock_kandang_id
|
||||
) AS pc ON pc.project_flock_kandang_id = u.project_flock_kandang_id`).
|
||||
Where("u.uniform_date IS NOT NULL").
|
||||
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end)
|
||||
Where("u.uniform_date >= ? AND u.uniform_date < ?", start, end).
|
||||
Where("u.uniform_date::date >= pc.chick_in_date")
|
||||
|
||||
db = applyDashboardFilters(db, filters)
|
||||
|
||||
groupBy := fmt.Sprintf("u.week, %s", groupExpr)
|
||||
orderBy := fmt.Sprintf("u.week ASC, %s", orderExpr)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -275,10 +275,10 @@ func (s dashboardService) buildPerformanceCharts(ctx context.Context, params *va
|
||||
cumFeed := 0.0
|
||||
|
||||
for _, week := range weeks {
|
||||
rec := recordingMap[week]
|
||||
uni := uniformityMap[week]
|
||||
std := standardMap[week]
|
||||
stdFcr := standardFcrMap[week]
|
||||
rec, hasRec := recordingMap[week]
|
||||
uni, hasUni := uniformityMap[week]
|
||||
std, hasStd := standardMap[week]
|
||||
stdFcr, hasStdFcr := standardFcrMap[week]
|
||||
weekEgg := weeklyEggMap[week]
|
||||
weekFeed := weeklyFeedMap[week]
|
||||
|
||||
@@ -294,38 +294,69 @@ func (s dashboardService) buildPerformanceCharts(ctx context.Context, params *va
|
||||
actFcrCum = cumFeed / cumEgg
|
||||
}
|
||||
|
||||
bodyWeightDataset = append(bodyWeightDataset, map[string]interface{}{
|
||||
"week": week,
|
||||
"body_weight": roundTo(uni.AverageWeight, 2),
|
||||
"std_body_weight": roundTo(std.StdBodyWeight, 2),
|
||||
})
|
||||
bodyWeightDatasetIndexByWeek[week] = len(bodyWeightDataset) - 1
|
||||
bodyWeightRow := map[string]interface{}{
|
||||
"week": week,
|
||||
}
|
||||
if hasUni {
|
||||
bodyWeightRow["body_weight"] = roundTo(uni.AverageWeight, 2)
|
||||
}
|
||||
if hasStd {
|
||||
bodyWeightRow["std_body_weight"] = roundTo(std.StdBodyWeight, 2)
|
||||
}
|
||||
if len(bodyWeightRow) > 1 {
|
||||
bodyWeightDataset = append(bodyWeightDataset, bodyWeightRow)
|
||||
}
|
||||
|
||||
performanceDataset = append(performanceDataset, map[string]interface{}{
|
||||
"week": week,
|
||||
"act_laying": roundTo(rec.HenDay, 2),
|
||||
"std_laying": roundTo(std.StdLaying, 2),
|
||||
"act_egg_weight": roundTo(rec.EggWeight, 2),
|
||||
"std_egg_weight": roundTo(std.StdEggWeight, 2),
|
||||
"act_feed_intake": roundTo(rec.FeedIntake, 2),
|
||||
"std_feed_intake": roundTo(std.StdFeedIntake, 2),
|
||||
"act_uniformity": roundTo(uni.Uniformity, 2),
|
||||
"std_uniformity": roundTo(std.StdUniformity, 2),
|
||||
})
|
||||
performanceRow := map[string]interface{}{
|
||||
"week": week,
|
||||
}
|
||||
if hasRec {
|
||||
performanceRow["act_laying"] = roundTo(rec.HenDay, 2)
|
||||
performanceRow["act_egg_weight"] = roundTo(rec.EggWeight, 2)
|
||||
performanceRow["act_feed_intake"] = roundTo(rec.FeedIntake, 2)
|
||||
}
|
||||
if hasUni {
|
||||
performanceRow["act_uniformity"] = roundTo(uni.Uniformity, 2)
|
||||
}
|
||||
if hasStd {
|
||||
performanceRow["std_laying"] = roundTo(std.StdLaying, 2)
|
||||
performanceRow["std_egg_weight"] = roundTo(std.StdEggWeight, 2)
|
||||
performanceRow["std_feed_intake"] = roundTo(std.StdFeedIntake, 2)
|
||||
performanceRow["std_uniformity"] = roundTo(std.StdUniformity, 2)
|
||||
}
|
||||
if len(performanceRow) > 1 {
|
||||
performanceDataset = append(performanceDataset, performanceRow)
|
||||
}
|
||||
|
||||
fcrDataset = append(fcrDataset, map[string]interface{}{
|
||||
"week": week,
|
||||
"act_fcr": roundTo(actFcr, 2),
|
||||
"std_fcr": roundTo(stdFcr, 2),
|
||||
"act_fcr_cum": roundTo(actFcrCum, 2),
|
||||
"std_fcr_cum": roundTo(stdFcr, 2),
|
||||
})
|
||||
fcrRow := map[string]interface{}{
|
||||
"week": week,
|
||||
}
|
||||
if weekEgg > 0 && weekFeed > 0 {
|
||||
fcrRow["act_fcr"] = roundTo(actFcr, 2)
|
||||
}
|
||||
if cumEgg > 0 && cumFeed > 0 {
|
||||
fcrRow["act_fcr_cum"] = roundTo(actFcrCum, 2)
|
||||
}
|
||||
if hasStdFcr {
|
||||
fcrRow["std_fcr"] = roundTo(stdFcr, 2)
|
||||
fcrRow["std_fcr_cum"] = roundTo(stdFcr, 2)
|
||||
}
|
||||
if len(fcrRow) > 1 {
|
||||
fcrDataset = append(fcrDataset, fcrRow)
|
||||
}
|
||||
|
||||
deplesiDataset = append(deplesiDataset, map[string]interface{}{
|
||||
"week": week,
|
||||
"act_deplesi": roundTo(rec.CumDepletionRate, 2),
|
||||
"std_deplesi": roundTo(std.StdDepletion, 2),
|
||||
})
|
||||
deplesiRow := map[string]interface{}{
|
||||
"week": week,
|
||||
}
|
||||
if hasRec {
|
||||
deplesiRow["act_deplesi"] = roundTo(rec.CumDepletionRate, 2)
|
||||
}
|
||||
if hasStd {
|
||||
deplesiRow["std_deplesi"] = roundTo(std.StdDepletion, 2)
|
||||
}
|
||||
if len(deplesiRow) > 1 {
|
||||
deplesiDataset = append(deplesiDataset, deplesiRow)
|
||||
}
|
||||
}
|
||||
|
||||
bodyWeightDataset = extendBodyWeightDatasetUntilEndDate(
|
||||
|
||||
@@ -49,26 +49,30 @@ type MarketingDetailDTO struct {
|
||||
}
|
||||
|
||||
type MarketingDeliveryProductDTO struct {
|
||||
Id uint `json:"id"`
|
||||
MarketingProductId uint `json:"marketing_product_id"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
TotalWeight float64 `json:"total_weight"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
DeliveryDate *time.Time `json:"delivery_date"`
|
||||
VehicleNumber string `json:"vehicle_number"`
|
||||
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
||||
Id uint `json:"id"`
|
||||
MarketingProductId uint `json:"marketing_product_id"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
TotalWeight float64 `json:"total_weight"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
DeliveryDate *time.Time `json:"delivery_date"`
|
||||
VehicleNumber string `json:"vehicle_number"`
|
||||
ConvertionUnit *string `json:"-"`
|
||||
WeightPerConvertion *float64 `json:"-"`
|
||||
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
||||
}
|
||||
|
||||
type DeliveryItemDTO struct {
|
||||
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
TotalWeight float64 `json:"total_weight"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
VehicleNumber string `json:"vehicle_number"`
|
||||
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
TotalWeight float64 `json:"total_weight"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
WeightPerConvertion *float64 `json:"weight_per_convertion"`
|
||||
TotalPeti *float64 `json:"total_peti"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
VehicleNumber string `json:"vehicle_number"`
|
||||
}
|
||||
|
||||
type DeliveryGroupDTO struct {
|
||||
@@ -147,15 +151,16 @@ func ToDeliveryMarketingProductDTO(e entity.MarketingProduct, marketingType stri
|
||||
|
||||
func ToMarketingDeliveryProductDTO(e entity.MarketingDeliveryProduct) MarketingDeliveryProductDTO {
|
||||
return MarketingDeliveryProductDTO{
|
||||
Id: e.Id,
|
||||
MarketingProductId: e.MarketingProductId,
|
||||
Qty: e.UsageQty,
|
||||
UnitPrice: e.UnitPrice,
|
||||
TotalWeight: e.TotalWeight,
|
||||
AvgWeight: e.AvgWeight,
|
||||
TotalPrice: e.TotalPrice,
|
||||
DeliveryDate: e.DeliveryDate,
|
||||
VehicleNumber: e.VehicleNumber,
|
||||
Id: e.Id,
|
||||
MarketingProductId: e.MarketingProductId,
|
||||
Qty: e.UsageQty,
|
||||
UnitPrice: e.UnitPrice,
|
||||
TotalWeight: e.TotalWeight,
|
||||
AvgWeight: e.AvgWeight,
|
||||
TotalPrice: e.TotalPrice,
|
||||
DeliveryDate: e.DeliveryDate,
|
||||
VehicleNumber: e.VehicleNumber,
|
||||
WeightPerConvertion: e.WeightPerConvertion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,6 +290,7 @@ func enrichDeliveryProductDTOsWithWarehouse(deliveryProductDTOs []MarketingDeliv
|
||||
mapped := productwarehouseDTO.ToProductWarehouseNestedDTO(product.ProductWarehouse)
|
||||
deliveryProductDTOs[i].ProductWarehouse = &mapped
|
||||
}
|
||||
deliveryProductDTOs[i].ConvertionUnit = product.ConvertionUnit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,13 +328,21 @@ func groupDeliveryProducts(products []MarketingDeliveryProductDTO, soNumber stri
|
||||
}
|
||||
|
||||
deliveryItem := DeliveryItemDTO{
|
||||
ProductWarehouse: product.ProductWarehouse,
|
||||
Qty: product.Qty,
|
||||
UnitPrice: product.UnitPrice,
|
||||
TotalWeight: product.TotalWeight,
|
||||
AvgWeight: product.AvgWeight,
|
||||
TotalPrice: product.TotalPrice,
|
||||
VehicleNumber: product.VehicleNumber,
|
||||
ProductWarehouse: product.ProductWarehouse,
|
||||
Qty: product.Qty,
|
||||
UnitPrice: product.UnitPrice,
|
||||
TotalWeight: product.TotalWeight,
|
||||
AvgWeight: product.AvgWeight,
|
||||
WeightPerConvertion: product.WeightPerConvertion,
|
||||
TotalPrice: product.TotalPrice,
|
||||
VehicleNumber: product.VehicleNumber,
|
||||
}
|
||||
if product.ConvertionUnit != nil &&
|
||||
strings.EqualFold(*product.ConvertionUnit, "PETI") &&
|
||||
product.WeightPerConvertion != nil &&
|
||||
*product.WeightPerConvertion > 0 {
|
||||
totalPeti := product.TotalWeight / *product.WeightPerConvertion
|
||||
deliveryItem.TotalPeti = &totalPeti
|
||||
}
|
||||
group.Deliveries = append(group.Deliveries, deliveryItem)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -375,11 +376,12 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
||||
itemDeliveryDate = &parsedDate
|
||||
}
|
||||
|
||||
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week)
|
||||
totalWeight, totalPrice := s.resolveDeliveryTotals(marketing.MarketingType, requestedProduct, foundMarketingProduct)
|
||||
|
||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||
deliveryProduct.AvgWeight = requestedProduct.AvgWeight
|
||||
deliveryProduct.WeightPerConvertion = requestedProduct.WeightPerConvertion
|
||||
deliveryProduct.TotalWeight = totalWeight
|
||||
deliveryProduct.TotalPrice = totalPrice
|
||||
deliveryProduct.DeliveryDate = itemDeliveryDate
|
||||
@@ -498,11 +500,12 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
||||
itemDeliveryDate = deliveryProduct.DeliveryDate
|
||||
}
|
||||
|
||||
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week)
|
||||
totalWeight, totalPrice := s.resolveDeliveryTotals(marketing.MarketingType, requestedProduct, foundMarketingProduct)
|
||||
|
||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||
deliveryProduct.AvgWeight = requestedProduct.AvgWeight
|
||||
deliveryProduct.WeightPerConvertion = requestedProduct.WeightPerConvertion
|
||||
deliveryProduct.TotalWeight = totalWeight
|
||||
deliveryProduct.TotalPrice = totalPrice
|
||||
deliveryProduct.DeliveryDate = itemDeliveryDate
|
||||
@@ -541,20 +544,53 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
||||
return s.getMarketingWithDeliveries(c, id)
|
||||
}
|
||||
|
||||
func (s *deliveryOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int) (totalWeight, totalPrice float64) {
|
||||
func (s *deliveryOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, _ *float64) (totalWeight, totalPrice float64) {
|
||||
if marketingType == string(utils.MarketingTypeTrading) {
|
||||
totalWeight = 0
|
||||
totalPrice = qty * unitPrice
|
||||
totalPrice = math.Round(qty*unitPrice*100) / 100
|
||||
} else if marketingType == string(utils.MarketingTypeAyamPullet) && week != nil && *week > 0 {
|
||||
totalWeight = qty * avgWeight
|
||||
totalPrice = unitPrice * float64(*week) * qty
|
||||
totalWeight = math.Round(qty*avgWeight*100) / 100
|
||||
totalPrice = math.Round(unitPrice*float64(*week)*qty*100) / 100
|
||||
} else {
|
||||
totalWeight = qty * avgWeight
|
||||
totalPrice = totalWeight * unitPrice
|
||||
totalWeight = math.Round(qty*avgWeight*100) / 100
|
||||
|
||||
if marketingType == string(utils.MarketingTypeTelur) && convertionUnit != nil {
|
||||
switch *convertionUnit {
|
||||
case string(utils.ConvertionUnitQty):
|
||||
totalPrice = math.Round(qty*unitPrice*100) / 100
|
||||
return totalWeight, totalPrice
|
||||
case string(utils.ConvertionUnitPeti):
|
||||
totalPrice = math.Round(totalWeight*unitPrice*100) / 100
|
||||
return totalWeight, totalPrice
|
||||
}
|
||||
}
|
||||
|
||||
totalPrice = math.Round(totalWeight*unitPrice*100) / 100
|
||||
}
|
||||
return totalWeight, totalPrice
|
||||
}
|
||||
|
||||
func (s *deliveryOrdersService) resolveDeliveryTotals(marketingType string, requestedProduct validation.DeliveryProduct, marketingProduct *entity.MarketingProduct) (totalWeight, totalPrice float64) {
|
||||
totalWeight, totalPrice = s.calculatePriceByMarketingType(
|
||||
marketingType,
|
||||
requestedProduct.Qty,
|
||||
requestedProduct.AvgWeight,
|
||||
requestedProduct.UnitPrice,
|
||||
marketingProduct.Week,
|
||||
marketingProduct.ConvertionUnit,
|
||||
marketingProduct.WeightPerConvertion,
|
||||
)
|
||||
|
||||
if requestedProduct.TotalWeight != nil {
|
||||
totalWeight = *requestedProduct.TotalWeight
|
||||
}
|
||||
if requestedProduct.TotalPrice != nil {
|
||||
totalPrice = *requestedProduct.TotalPrice
|
||||
}
|
||||
|
||||
return totalWeight, totalPrice
|
||||
}
|
||||
|
||||
func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64, actorID uint) error {
|
||||
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
||||
|
||||
@@ -815,7 +815,7 @@ func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Cont
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, weightPerConvertion *float64) (totalWeight, totalPrice float64) {
|
||||
func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, _ *float64) (totalWeight, totalPrice float64) {
|
||||
if marketingType == string(utils.MarketingTypeTrading) {
|
||||
totalWeight = 0
|
||||
totalPrice = math.Round(qty*unitPrice*100) / 100
|
||||
@@ -831,11 +831,8 @@ func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string,
|
||||
totalPrice = math.Round(qty*unitPrice*100) / 100
|
||||
return totalWeight, totalPrice
|
||||
case string(utils.ConvertionUnitPeti):
|
||||
if weightPerConvertion != nil && *weightPerConvertion > 0 {
|
||||
totalPeti := totalWeight / *weightPerConvertion
|
||||
totalPrice = math.Round(totalPeti*unitPrice*100) / 100
|
||||
return totalWeight, totalPrice
|
||||
}
|
||||
totalPrice = math.Round(totalWeight*unitPrice*100) / 100
|
||||
return totalWeight, totalPrice
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package validation
|
||||
|
||||
type DeliveryProduct struct {
|
||||
MarketingProductId uint `json:"marketing_product_id" validate:"required,gt=0"`
|
||||
Qty float64 `json:"qty" validate:"omitempty,gte=0"`
|
||||
UnitPrice float64 `json:"unit_price" validate:"omitempty,gte=0"`
|
||||
AvgWeight float64 `json:"avg_weight" validate:"omitempty,gte=0"`
|
||||
DeliveryDate string `json:"delivery_date" validate:"omitempty,datetime=2006-01-02"`
|
||||
VehicleNumber string `json:"vehicle_number" validate:"omitempty,max=50"`
|
||||
MarketingProductId uint `json:"marketing_product_id" validate:"required,gt=0"`
|
||||
Qty float64 `json:"qty" validate:"omitempty,gte=0"`
|
||||
UnitPrice float64 `json:"unit_price" validate:"omitempty,gte=0"`
|
||||
AvgWeight float64 `json:"avg_weight" validate:"omitempty,gte=0"`
|
||||
WeightPerConvertion *float64 `json:"weight_per_convertion" validate:"omitempty,gt=0"`
|
||||
TotalWeight *float64 `json:"total_weight" validate:"omitempty,gte=0"`
|
||||
TotalPrice *float64 `json:"total_price" validate:"omitempty,gte=0"`
|
||||
DeliveryDate string `json:"delivery_date" validate:"omitempty,datetime=2006-01-02"`
|
||||
VehicleNumber string `json:"vehicle_number" validate:"omitempty,max=50"`
|
||||
}
|
||||
|
||||
type DeliveryOrderCreate struct {
|
||||
|
||||
@@ -42,7 +42,9 @@ func (r *UniformityRepositoryImpl) GetAllWithFilters(ctx context.Context, offset
|
||||
func (r *UniformityRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.
|
||||
Preload("ProjectFlockKandang.ProjectFlock").
|
||||
Preload("ProjectFlockKandang.ProjectFlock.Location").
|
||||
Preload("ProjectFlockKandang.Chickins").
|
||||
Preload("ProjectFlockKandang.Kandang.Location")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ func (s uniformityService) GetAll(c *fiber.Ctx, params *validation.Query) ([]ent
|
||||
s.Log.Errorf("Failed to get uniformitys: %+v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
s.normalizeUniformityWeeks(uniformitys)
|
||||
if err := s.attachLatestApprovals(c.Context(), uniformitys); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -125,6 +126,7 @@ func (s uniformityService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlockKa
|
||||
s.Log.Errorf("Failed get uniformity by id: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
s.normalizeUniformityWeek(uniformity)
|
||||
if err := s.attachLatestApproval(c.Context(), uniformity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,6 +137,23 @@ func (s uniformityService) GetSummary(c *fiber.Ctx, id uint) (*entity.ProjectFlo
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
func (s *uniformityService) normalizeUniformityWeeks(items []entity.ProjectFlockKandangUniformity) {
|
||||
for i := range items {
|
||||
s.normalizeUniformityWeek(&items[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *uniformityService) normalizeUniformityWeek(item *entity.ProjectFlockKandangUniformity) {
|
||||
if item == nil || item.UniformDate == nil {
|
||||
return
|
||||
}
|
||||
computedWeek, err := s.computeUniformityWeekForPFK(&item.ProjectFlockKandang, *item.UniformDate)
|
||||
if err != nil || computedWeek <= 0 {
|
||||
return
|
||||
}
|
||||
item.Week = computedWeek
|
||||
}
|
||||
|
||||
func (s uniformityService) GetStandard(c *fiber.Ctx, uniformity *entity.ProjectFlockKandangUniformity) (*utypes.UniformityStandard, error) {
|
||||
if uniformity == nil {
|
||||
return nil, nil
|
||||
@@ -372,24 +391,18 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
category := strings.TrimSpace(pfk.ProjectFlock.Category)
|
||||
if s.ProductionStandardRepo != nil && pfk.ProjectFlock.ProductionStandardId != 0 {
|
||||
if standard, err := s.ProductionStandardRepo.GetByID(c.Context(), pfk.ProjectFlock.ProductionStandardId, nil); err == nil {
|
||||
if strings.TrimSpace(standard.ProjectCategory) != "" {
|
||||
category = standard.ProjectCategory
|
||||
}
|
||||
}
|
||||
computedWeek, err := s.computeUniformityWeekForPFK(pfk, uniformDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
weekBase := 1
|
||||
isLayingCategory := strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying))
|
||||
if isLayingCategory {
|
||||
weekBase = config.LayingWeekStart()
|
||||
}
|
||||
if req.Week < weekBase {
|
||||
if !isLayingCategory {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
|
||||
}
|
||||
// return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
|
||||
isGrowingCategory := strings.EqualFold(strings.TrimSpace(pfk.ProjectFlock.Category), string(utils.ProjectFlockCategoryGrowing))
|
||||
if req.Week > 0 && req.Week != computedWeek {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"project_flock_kandang_id": req.ProjectFlockKandangId,
|
||||
"uniform_date": uniformDate.Format("2006-01-02"),
|
||||
"requested_week": req.Week,
|
||||
"computed_week": computedWeek,
|
||||
}).Warn("Uniformity week mismatch detected; using computed week")
|
||||
}
|
||||
|
||||
var latestWeek int
|
||||
@@ -400,17 +413,14 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
|
||||
Scan(&latestWeek).Error; err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate uniformity week sequence")
|
||||
}
|
||||
if latestWeek == 0 && req.Week != weekBase {
|
||||
if !isLayingCategory {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
|
||||
}
|
||||
// return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
|
||||
if latestWeek > 0 && computedWeek > latestWeek+1 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "week must be sequential without skipping")
|
||||
}
|
||||
// if latestWeek > 0 && req.Week > latestWeek+1 {
|
||||
// return nil, fiber.NewError(fiber.StatusBadRequest, "week must be sequential without skipping")
|
||||
// }
|
||||
|
||||
if err := s.ensureUniqueUniformity(c.Context(), 0, req.ProjectFlockKandangId, req.Week); err != nil {
|
||||
if err := s.ensureUniqueUniformity(c.Context(), 0, req.ProjectFlockKandangId, computedWeek); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -438,7 +448,7 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
|
||||
|
||||
createBody := &entity.ProjectFlockKandangUniformity{
|
||||
Uniformity: calculation.Uniformity,
|
||||
Week: req.Week,
|
||||
Week: computedWeek,
|
||||
Cv: calculation.Cv,
|
||||
ChickQtyOfWeight: calculation.ChickQtyOfWeight,
|
||||
MeanUp: calculation.MeanUp,
|
||||
@@ -467,7 +477,7 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.EqualFold(category, string(utils.ProjectFlockCategoryGrowing)) {
|
||||
if isGrowingCategory {
|
||||
if err := s.updateGrowingFcrForWeek(tx, createBody.ProjectFlockKandangId, createBody.Week, calculation.MeanUp); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -536,9 +546,6 @@ func (s uniformityService) UpdateOne(c *fiber.Ctx, req *validation.Update, id ui
|
||||
}
|
||||
updateBody["project_flock_kandang_id"] = *req.ProjectFlockKandangId
|
||||
}
|
||||
if req.Week != nil {
|
||||
updateBody["week"] = *req.Week
|
||||
}
|
||||
|
||||
if req.Date != nil || req.ProjectFlockKandangId != nil || req.Week != nil {
|
||||
current, err := s.Repository.GetByID(c.Context(), id, nil)
|
||||
@@ -552,15 +559,11 @@ func (s uniformityService) UpdateOne(c *fiber.Ctx, req *validation.Update, id ui
|
||||
if targetDate == nil {
|
||||
targetDate = current.UniformDate
|
||||
}
|
||||
targetWeek := current.Week
|
||||
if req.Week != nil {
|
||||
targetWeek = *req.Week
|
||||
}
|
||||
targetPFKID := current.ProjectFlockKandangId
|
||||
if req.ProjectFlockKandangId != nil {
|
||||
targetPFKID = *req.ProjectFlockKandangId
|
||||
}
|
||||
if targetPFKID != 0 && targetWeek > 0 {
|
||||
if targetPFKID != 0 && targetDate != nil {
|
||||
pfk, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetPFKID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -568,28 +571,21 @@ func (s uniformityService) UpdateOne(c *fiber.Ctx, req *validation.Update, id ui
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
category := strings.TrimSpace(pfk.ProjectFlock.Category)
|
||||
if s.ProductionStandardRepo != nil && pfk.ProjectFlock.ProductionStandardId != 0 {
|
||||
if standard, err := s.ProductionStandardRepo.GetByID(c.Context(), pfk.ProjectFlock.ProductionStandardId, nil); err == nil {
|
||||
if strings.TrimSpace(standard.ProjectCategory) != "" {
|
||||
category = standard.ProjectCategory
|
||||
}
|
||||
}
|
||||
computedWeek, err := s.computeUniformityWeekForPFK(pfk, *targetDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
weekBase := 1
|
||||
isLayingCategory := strings.EqualFold(category, string(utils.ProjectFlockCategoryLaying))
|
||||
if isLayingCategory {
|
||||
weekBase = config.LayingWeekStart()
|
||||
if req.Week != nil && *req.Week != computedWeek {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"uniformity_id": id,
|
||||
"project_flock_kandang_id": targetPFKID,
|
||||
"uniform_date": targetDate.Format("2006-01-02"),
|
||||
"requested_week": *req.Week,
|
||||
"computed_week": computedWeek,
|
||||
}).Warn("Uniformity week mismatch detected on update; using computed week")
|
||||
}
|
||||
if targetWeek < weekBase {
|
||||
if !isLayingCategory {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "week must start from 1 for growing projects")
|
||||
}
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("week must start from %d for laying projects", weekBase))
|
||||
}
|
||||
}
|
||||
if targetDate != nil {
|
||||
if err := s.ensureUniqueUniformity(c.Context(), id, targetPFKID, targetWeek); err != nil {
|
||||
updateBody["week"] = computedWeek
|
||||
if err := s.ensureUniqueUniformity(c.Context(), id, targetPFKID, computedWeek); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -734,6 +730,51 @@ func (s *uniformityService) ensureUniqueUniformity(ctx context.Context, id uint,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *uniformityService) computeUniformityWeekForPFK(pfk *entity.ProjectFlockKandang, uniformDate time.Time) (int, error) {
|
||||
if pfk == nil || pfk.Id == 0 {
|
||||
return 0, fiber.NewError(fiber.StatusBadRequest, "Project flock kandang tidak valid")
|
||||
}
|
||||
|
||||
chickInDate, ok := earliestUniformityChickInDate(pfk.Chickins)
|
||||
if !ok {
|
||||
return 0, fiber.NewError(fiber.StatusBadRequest, "Tanggal chick in tidak ditemukan")
|
||||
}
|
||||
|
||||
chickInDay := normalizeUniformityDateOnlyUTC(chickInDate)
|
||||
uniformDay := normalizeUniformityDateOnlyUTC(uniformDate)
|
||||
if uniformDay.Before(chickInDay) {
|
||||
return 0, fiber.NewError(fiber.StatusBadRequest, "Uniformity date tidak boleh sebelum tanggal chick in")
|
||||
}
|
||||
|
||||
diff := int(uniformDay.Sub(chickInDay).Hours() / 24)
|
||||
weekBase := 1
|
||||
if strings.EqualFold(strings.TrimSpace(pfk.ProjectFlock.Category), string(utils.ProjectFlockCategoryLaying)) {
|
||||
weekBase = config.LayingWeekStart()
|
||||
}
|
||||
|
||||
return (diff / 7) + weekBase, nil
|
||||
}
|
||||
|
||||
func earliestUniformityChickInDate(chickins []entity.ProjectChickin) (time.Time, bool) {
|
||||
var earliest time.Time
|
||||
for _, chickin := range chickins {
|
||||
if chickin.ChickInDate.IsZero() {
|
||||
continue
|
||||
}
|
||||
if earliest.IsZero() || chickin.ChickInDate.Before(earliest) {
|
||||
earliest = chickin.ChickInDate
|
||||
}
|
||||
}
|
||||
if earliest.IsZero() {
|
||||
return time.Time{}, false
|
||||
}
|
||||
return earliest, true
|
||||
}
|
||||
|
||||
func normalizeUniformityDateOnlyUTC(value time.Time) time.Time {
|
||||
return time.Date(value.UTC().Year(), value.UTC().Month(), value.UTC().Day(), 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
func (s uniformityService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := m.EnsureUniformityAccess(c, s.Repository.DB(), id); err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
)
|
||||
|
||||
func TestComputeUniformityWeekForPFK(t *testing.T) {
|
||||
originalWeekStart := config.TransferToLayingGrowingMaxWeek
|
||||
config.TransferToLayingGrowingMaxWeek = 19
|
||||
t.Cleanup(func() {
|
||||
config.TransferToLayingGrowingMaxWeek = originalWeekStart
|
||||
})
|
||||
|
||||
svc := &uniformityService{}
|
||||
baseDate := time.Date(2026, time.January, 1, 9, 30, 0, 0, time.UTC)
|
||||
|
||||
t.Run("growing starts from week one", func(t *testing.T) {
|
||||
pfk := &entity.ProjectFlockKandang{
|
||||
Id: 1,
|
||||
ProjectFlock: entity.ProjectFlock{
|
||||
Category: string(utils.ProjectFlockCategoryGrowing),
|
||||
},
|
||||
Chickins: []entity.ProjectChickin{
|
||||
{ChickInDate: baseDate},
|
||||
},
|
||||
}
|
||||
|
||||
week, err := svc.computeUniformityWeekForPFK(pfk, baseDate)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if week != 1 {
|
||||
t.Fatalf("expected week 1, got %d", week)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("laying uses configured week base and earliest chick in", func(t *testing.T) {
|
||||
pfk := &entity.ProjectFlockKandang{
|
||||
Id: 2,
|
||||
ProjectFlock: entity.ProjectFlock{
|
||||
Category: string(utils.ProjectFlockCategoryLaying),
|
||||
},
|
||||
Chickins: []entity.ProjectChickin{
|
||||
{ChickInDate: baseDate.AddDate(0, 0, 4)},
|
||||
{ChickInDate: baseDate},
|
||||
},
|
||||
}
|
||||
|
||||
week, err := svc.computeUniformityWeekForPFK(pfk, baseDate.AddDate(0, 0, 7))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if week != 20 {
|
||||
t.Fatalf("expected week 20, got %d", week)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("rejects date before chick in", func(t *testing.T) {
|
||||
pfk := &entity.ProjectFlockKandang{
|
||||
Id: 3,
|
||||
ProjectFlock: entity.ProjectFlock{
|
||||
Category: string(utils.ProjectFlockCategoryLaying),
|
||||
},
|
||||
Chickins: []entity.ProjectChickin{
|
||||
{ChickInDate: baseDate},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := svc.computeUniformityWeekForPFK(pfk, baseDate.AddDate(0, 0, -1)); err == nil {
|
||||
t.Fatal("expected error for date before chick in")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
type Create struct {
|
||||
Date string `form:"date" validate:"required"`
|
||||
ProjectFlockKandangId uint `form:"project_flock_kandang_id" validate:"required,number,min=1"`
|
||||
Week int `form:"week" validate:"required,min=1"`
|
||||
Week int `form:"week" validate:"omitempty,min=1"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
@@ -120,14 +120,14 @@ func ParseCreate(c *fiber.Ctx) (*Create, *multipart.FileHeader, error) {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
||||
}
|
||||
|
||||
week := 0
|
||||
weekStr := strings.TrimSpace(c.FormValue("week"))
|
||||
if weekStr == "" {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, "week is required")
|
||||
}
|
||||
|
||||
week, err := strconv.Atoi(weekStr)
|
||||
if err != nil || week <= 0 {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, "week is required")
|
||||
if weekStr != "" {
|
||||
parsedWeek, err := strconv.Atoi(weekStr)
|
||||
if err != nil || parsedWeek <= 0 {
|
||||
return nil, nil, fiber.NewError(fiber.StatusBadRequest, "week is invalid")
|
||||
}
|
||||
week = parsedWeek
|
||||
}
|
||||
|
||||
file, err := c.FormFile("document")
|
||||
|
||||
@@ -939,12 +939,27 @@ func (s *repportService) getUniformityByWeek(ctx context.Context, projectFlockKa
|
||||
return result, nil
|
||||
}
|
||||
|
||||
weekExpr := fmt.Sprintf(`CASE
|
||||
WHEN u.uniform_date IS NULL OR pc.chick_in_date IS NULL THEN 0
|
||||
WHEN u.uniform_date::date < pc.chick_in_date THEN 0
|
||||
WHEN UPPER(pf.category) = 'LAYING' THEN (((u.uniform_date::date - pc.chick_in_date)::int) / 7) + %d
|
||||
ELSE (((u.uniform_date::date - pc.chick_in_date)::int) / 7) + 1
|
||||
END`, config.LayingWeekStart())
|
||||
|
||||
var rows []entity.ProjectFlockKandangUniformity
|
||||
if err := s.db.WithContext(ctx).
|
||||
Model(&entity.ProjectFlockKandangUniformity{}).
|
||||
Select("week, uniformity, uniform_date, id, chart_data").
|
||||
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
Where("week IN ?", weeks).
|
||||
Table("project_flock_kandang_uniformity AS u").
|
||||
Select(fmt.Sprintf("%s AS week, u.uniformity, u.uniform_date, u.id, u.chart_data", weekExpr)).
|
||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = u.project_flock_kandang_id").
|
||||
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
||||
Joins(`JOIN (
|
||||
SELECT project_flock_kandang_id, MIN(chick_in_date)::date AS chick_in_date
|
||||
FROM project_chickins
|
||||
WHERE deleted_at IS NULL
|
||||
GROUP BY project_flock_kandang_id
|
||||
) AS pc ON pc.project_flock_kandang_id = u.project_flock_kandang_id`).
|
||||
Where("u.project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
Where(fmt.Sprintf("%s IN ?", weekExpr), weeks).
|
||||
Order("uniform_date DESC").
|
||||
Order("id DESC").
|
||||
Find(&rows).Error; err != nil {
|
||||
|
||||
Reference in New Issue
Block a user