mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
init depresiasi
This commit is contained in:
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
@@ -23,6 +24,7 @@ type HppCostRepository interface {
|
||||
GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, startDate *time.Time, endDate *time.Time) (float64, float64, error)
|
||||
GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error)
|
||||
GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error)
|
||||
GetManualDepreciationCostByProjectFlockID(ctx context.Context, projectFlockId uint) (float64, error)
|
||||
}
|
||||
|
||||
type HppRepositoryImpl struct {
|
||||
@@ -48,12 +50,32 @@ func (r *HppRepositoryImpl) GetProjectFlockKandangIDs(ctx context.Context, proje
|
||||
}
|
||||
|
||||
func (r *HppRepositoryImpl) GetDocCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
||||
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
||||
usableProjectChickin := fifo.UsableKeyProjectChickin.String()
|
||||
|
||||
var total float64
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("project_chickins AS pc").
|
||||
Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)").
|
||||
Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyProjectChickin.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeTraceChickin).
|
||||
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||
Select(`
|
||||
COALESCE(SUM(sa.qty * CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
|
||||
ELSE 0
|
||||
END), 0)`,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
).
|
||||
Joins(
|
||||
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND (sa.stockable_type = ? OR sa.stockable_type = ?) AND sa.status = ? AND sa.allocation_purpose = ?",
|
||||
usableProjectChickin,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
entity.StockAllocationStatusActive,
|
||||
entity.StockAllocationPurposeTraceChickin,
|
||||
).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
|
||||
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
||||
Where("pc.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
|
||||
Scan(&total).Error
|
||||
if err != nil {
|
||||
@@ -85,7 +107,7 @@ func (r *HppRepositoryImpl) GetExpedisionCost(ctx context.Context, projectFlockK
|
||||
Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id").
|
||||
Joins("JOIN flags AS f ON f.flagable_id = en.nonstock_id AND f.flagable_type = ?", entity.FlagableTypeNonstock).
|
||||
Where("en.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
|
||||
Where("f.name = ?", utils.FlagEkspedisi).
|
||||
// Where("f.name = ?", utils.FlagEkspedisi).
|
||||
Scan(&total).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -100,15 +122,35 @@ func (r *HppRepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlockKa
|
||||
date = &now
|
||||
}
|
||||
|
||||
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
||||
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
||||
usableRecordingStock := fifo.UsableKeyRecordingStock.String()
|
||||
|
||||
var total float64
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)").
|
||||
Select(`
|
||||
COALESCE(SUM(sa.qty * CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
|
||||
ELSE 0
|
||||
END), 0)`,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
).
|
||||
Joins("JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||
Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id").
|
||||
Joins("JOIN flags AS f ON f.flagable_id = pw.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume).
|
||||
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||
Joins(
|
||||
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND (sa.stockable_type = ? OR sa.stockable_type = ?) AND sa.status = ? AND sa.allocation_purpose = ?",
|
||||
usableRecordingStock,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
entity.StockAllocationStatusActive,
|
||||
entity.StockAllocationPurposeConsume,
|
||||
).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
|
||||
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
||||
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||
Where("r.record_datetime <= ?", *date).
|
||||
Where("f.name = ?", utils.FlagPakan).
|
||||
@@ -132,15 +174,34 @@ func (r *HppRepositoryImpl) GetOvkUsageCost(ctx context.Context, projectFlockKan
|
||||
utils.FlagVitamin,
|
||||
utils.FlagKimia,
|
||||
}
|
||||
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
||||
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
||||
usableRecordingStock := fifo.UsableKeyRecordingStock.String()
|
||||
|
||||
var total float64
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)").
|
||||
Select(`
|
||||
COALESCE(SUM(sa.qty * CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
|
||||
ELSE 0
|
||||
END), 0)`,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
).
|
||||
Joins("JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||
Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id").
|
||||
Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume).
|
||||
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||
Joins(
|
||||
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND (sa.stockable_type = ? OR sa.stockable_type = ?) AND sa.status = ? AND sa.allocation_purpose = ?",
|
||||
usableRecordingStock,
|
||||
stockablePurchase,
|
||||
stockableAdjustment,
|
||||
entity.StockAllocationStatusActive,
|
||||
entity.StockAllocationPurposeConsume,
|
||||
).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
|
||||
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
||||
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||
Where("r.record_datetime <= ?", *date).
|
||||
Where("EXISTS (SELECT 1 FROM flags f WHERE f.flagable_id = pw.product_id AND f.flagable_type = ? AND f.name IN ?)", entity.FlagableTypeProduct, flags).
|
||||
@@ -169,22 +230,28 @@ func (r *HppRepositoryImpl) GetTotalPopulation(ctx context.Context, projectFlock
|
||||
func (r *HppRepositoryImpl) GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error) {
|
||||
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
||||
stockableTransferIn := fifo.StockableKeyStockTransferIn.String()
|
||||
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
||||
usableProjectChickin := fifo.UsableKeyProjectChickin.String()
|
||||
|
||||
var total float64
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("project_chickins AS pc").
|
||||
Select(`
|
||||
COALESCE(SUM(sa.qty * CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(tpi.price, 0)
|
||||
ELSE 0
|
||||
END), 0)`,
|
||||
stockablePurchase, stockableTransferIn).
|
||||
COALESCE(SUM(sa.qty * CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(tpi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
|
||||
ELSE 0
|
||||
END), 0)`,
|
||||
stockablePurchase,
|
||||
stockableTransferIn,
|
||||
stockableAdjustment,
|
||||
).
|
||||
Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND sa.status = ? AND sa.allocation_purpose = ?", usableProjectChickin, entity.StockAllocationStatusActive, entity.StockAllocationPurposeTraceChickin).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
|
||||
Joins("LEFT JOIN stock_allocations AS tsa ON tsa.usable_type = ? AND tsa.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa.stockable_type = ? AND tsa.status = ? AND tsa.allocation_purpose = ?", stockableTransferIn, stockableTransferIn, stockablePurchase, entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume).
|
||||
Joins("LEFT JOIN purchase_items AS tpi ON tpi.id = tsa.stockable_id").
|
||||
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
||||
Where("pc.project_flock_kandang_id = ?", projectFlockKandangId).
|
||||
Scan(&total).Error
|
||||
if err != nil {
|
||||
@@ -215,6 +282,33 @@ func (r *HppRepositoryImpl) GetEggProduksiPiecesAndWeightKgByProjectFlockKandang
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
var adjustmentTotalWeight float64
|
||||
adjustmentSubQuery := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select("DISTINCT ast.id AS adjustment_id, ast.price AS price").
|
||||
Joins("JOIN recording_eggs AS re ON re.recording_id = r.id").
|
||||
Joins("JOIN stock_transfer_details AS std ON std.dest_product_warehouse_id = re.product_warehouse_id").
|
||||
Joins(
|
||||
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = std.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?",
|
||||
fifo.UsableKeyStockTransferOut.String(),
|
||||
fifo.StockableKeyAdjustmentIn.String(),
|
||||
entity.StockAllocationStatusActive,
|
||||
entity.StockAllocationPurposeConsume,
|
||||
).
|
||||
Joins("JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND ast.product_warehouse_id = std.source_product_warehouse_id").
|
||||
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||
Where("r.record_datetime <= ?", *date)
|
||||
|
||||
err = r.db.WithContext(ctx).
|
||||
Table("(?) AS adjustment_sources", adjustmentSubQuery).
|
||||
Select("COALESCE(SUM(adjustment_sources.price), 0)").
|
||||
Scan(&adjustmentTotalWeight).Error
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
totals.TotalWeightKg += adjustmentTotalWeight
|
||||
|
||||
return totals.TotalPieces, totals.TotalWeightKg, nil
|
||||
}
|
||||
|
||||
@@ -311,3 +405,25 @@ func (r *HppRepositoryImpl) GetTransferSourceSummary(ctx context.Context, projec
|
||||
|
||||
return summary.ProjectFlockID, summary.TotalQty, nil
|
||||
}
|
||||
|
||||
func (r *HppRepositoryImpl) GetManualDepreciationCostByProjectFlockID(ctx context.Context, projectFlockId uint) (float64, error) {
|
||||
type row struct {
|
||||
TotalCost float64
|
||||
}
|
||||
|
||||
var selected row
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("farm_depreciation_manual_inputs").
|
||||
Select("total_cost").
|
||||
Where("project_flock_id = ?", projectFlockId).
|
||||
Limit(1).
|
||||
Take(&selected).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return selected.TotalCost, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -39,77 +40,108 @@ func NewHppService(hppRepo commonRepo.HppCostRepository) HppService {
|
||||
}
|
||||
|
||||
func (s *hppService) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
|
||||
logHpp("CalculateHppCost", "start project_flock_kandang_id=%d input_date=%s", projectFlockKandangId, formatTimePtr(date))
|
||||
if date == nil {
|
||||
now := time.Now()
|
||||
date = &now
|
||||
}
|
||||
logHpp("CalculateHppCost", "normalized_date=%s", formatTimePtr(date))
|
||||
|
||||
location, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err != nil {
|
||||
logHpp("CalculateHppCost", "load_location_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
logHpp("CalculateHppCost", "location=%s", location.String())
|
||||
|
||||
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location)
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
logHpp("CalculateHppCost", "start_of_day=%s end_of_day=%s", startOfDay.Format(time.RFC3339), endOfDay.Format(time.RFC3339))
|
||||
|
||||
depresiasiTransfer, err := s.GetDepresiasiTransfer(projectFlockKandangId, &endOfDay)
|
||||
if err != nil {
|
||||
logHpp("CalculateHppCost", "get_depresiasi_transfer_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
logHpp("CalculateHppCost", "depresiasi_transfer=%f", depresiasiTransfer)
|
||||
|
||||
totalProductionCost, err := s.GetTotalProductionCost(projectFlockKandangId, &endOfDay, depresiasiTransfer)
|
||||
if err != nil {
|
||||
logHpp("CalculateHppCost", "get_total_production_cost_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay)
|
||||
|
||||
logHpp("CalculateHppCost", "total_production_cost=%f", totalProductionCost)
|
||||
result, err := s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay)
|
||||
if err != nil {
|
||||
logHpp("CalculateHppCost", "get_hpp_estimation_dan_realisasi_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
logHpp("CalculateHppCost", "done estimation=%+v real=%+v", result.Estimation, result.Real)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *hppService) GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error) {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "start source_project_flock_id=%d input_date=%s", sourceProjectFlockID, formatTimePtr(date))
|
||||
if date == nil {
|
||||
now := time.Now()
|
||||
date = &now
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "normalized_date=%s", formatTimePtr(date))
|
||||
|
||||
if s.hppRepo == nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "repo_nil return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_project_flock_kandang_ids_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "kandang_ids=%v", kandangIDs)
|
||||
|
||||
docCost, err := s.hppRepo.GetDocCost(context.Background(), kandangIDs)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_doc_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "doc_cost=%f", docCost)
|
||||
|
||||
budgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), sourceProjectFlockID)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_budget_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "budget_cost=%f", budgetCost)
|
||||
|
||||
expedisionCost, err := s.hppRepo.GetExpedisionCost(context.Background(), kandangIDs)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_expedision_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "expedision_cost=%f", expedisionCost)
|
||||
|
||||
feedCost, err := s.hppRepo.GetFeedUsageCost(context.Background(), kandangIDs, date)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_feed_usage_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "feed_cost=%f", feedCost)
|
||||
|
||||
ovkCost, err := s.hppRepo.GetOvkUsageCost(context.Background(), kandangIDs, date)
|
||||
if err != nil {
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "get_ovk_usage_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "ovk_cost=%f", ovkCost)
|
||||
|
||||
return docCost + budgetCost + expedisionCost + feedCost + ovkCost, nil
|
||||
total := docCost + budgetCost + expedisionCost + feedCost + ovkCost
|
||||
logHpp("GetTotalDepresiasiFlockGrowing", "done total=%f", total)
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *hppService) GetTotalProductionCost(projectFlockKandangId uint, endDate *time.Time, depresiasiTransfer float64) (float64, error) {
|
||||
logHpp("GetTotalProductionCost", "start project_flock_kandang_id=%d end_date=%s depresiasi_transfer=%f", projectFlockKandangId, formatTimePtr(endDate), depresiasiTransfer)
|
||||
// if date == nil {
|
||||
// now := time.Now()
|
||||
// date = &now
|
||||
@@ -117,125 +149,248 @@ func (s *hppService) GetTotalProductionCost(projectFlockKandangId uint, endDate
|
||||
|
||||
costPullet, err := s.hppRepo.GetPulletCost(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
logHpp("GetTotalProductionCost", "get_pullet_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalProductionCost", "cost_pullet=%f", costPullet)
|
||||
|
||||
costFeed, err := s.hppRepo.GetFeedUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetTotalProductionCost", "get_feed_usage_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalProductionCost", "cost_feed=%f", costFeed)
|
||||
|
||||
costOvk, err := s.hppRepo.GetOvkUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetTotalProductionCost", "get_ovk_usage_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalProductionCost", "cost_ovk=%f", costOvk)
|
||||
|
||||
costExpedision, err := s.hppRepo.GetExpedisionCost(context.Background(), []uint{projectFlockKandangId})
|
||||
if err != nil {
|
||||
logHpp("GetTotalProductionCost", "get_expedision_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalProductionCost", "cost_expedision=%f", costExpedision)
|
||||
|
||||
costBudget, err := s.GetBudgetKandangLaying(projectFlockKandangId, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetTotalProductionCost", "get_budget_kandang_laying_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetTotalProductionCost", "cost_budget=%f", costBudget)
|
||||
|
||||
return depresiasiTransfer + costPullet + costFeed + costOvk + costExpedision + costBudget, nil
|
||||
// fmt.Println(costBudget, costExpedision, costOvk, costFeed, costPullet, depresiasiTransfer)
|
||||
|
||||
// depresiasiTransfer = 0
|
||||
|
||||
total := depresiasiTransfer + costPullet + costFeed + costOvk + costExpedision + costBudget
|
||||
logHpp("GetTotalProductionCost", "done total=%f", total)
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *hppService) GetBudgetKandangLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
|
||||
logHpp("GetBudgetKandangLaying", "start project_flock_kandang_id=%d end_date=%s", projectFlockKandangId, formatTimePtr(endDate))
|
||||
// if date == nil {
|
||||
// now := time.Now()
|
||||
// date = &now
|
||||
// }
|
||||
|
||||
if s.hppRepo == nil {
|
||||
logHpp("GetBudgetKandangLaying", "repo_nil return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
projectFlockId, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
logHpp("GetBudgetKandangLaying", "get_project_flock_id_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetBudgetKandangLaying", "project_flock_id=%d", projectFlockId)
|
||||
|
||||
projectFlockKandangIds, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockId)
|
||||
if err != nil {
|
||||
logHpp("GetBudgetKandangLaying", "get_project_flock_kandang_ids_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetBudgetKandangLaying", "project_flock_kandang_ids=%v", projectFlockKandangIds)
|
||||
|
||||
eggProduksiPiecesFlock, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), projectFlockKandangIds, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetBudgetKandangLaying", "get_egg_produksi_pieces_flock_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_flock=%f", eggProduksiPiecesFlock)
|
||||
|
||||
eggProduksiPiecesKandang, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetBudgetKandangLaying", "get_egg_produksi_pieces_kandang_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_kandang=%f", eggProduksiPiecesKandang)
|
||||
|
||||
totalBudgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), projectFlockId)
|
||||
if err != nil {
|
||||
logHpp("GetBudgetKandangLaying", "get_budget_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetBudgetKandangLaying", "total_budget_cost=%f", totalBudgetCost)
|
||||
|
||||
if eggProduksiPiecesFlock == 0 {
|
||||
logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_flock_zero return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return (totalBudgetCost * eggProduksiPiecesKandang) / eggProduksiPiecesFlock, nil
|
||||
result := (totalBudgetCost * eggProduksiPiecesKandang) / eggProduksiPiecesFlock
|
||||
logHpp("GetBudgetKandangLaying", "done result=%f", result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *hppService) GetDepresiasiTransfer(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
|
||||
// if endDate == nil {
|
||||
// now := time.Now()
|
||||
// endDate = &now
|
||||
// }
|
||||
logHpp("GetDepresiasiTransfer", "start project_flock_kandang_id=%d end_date=%s", projectFlockKandangId, formatTimePtr(endDate))
|
||||
if endDate == nil {
|
||||
now := time.Now()
|
||||
endDate = &now
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "normalized_end_date=%s", formatTimePtr(endDate))
|
||||
|
||||
if s.hppRepo == nil {
|
||||
logHpp("GetDepresiasiTransfer", "repo_nil return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
logHpp("GetDepresiasiTransfer", "get_transfer_source_summary_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "source_project_flock_id=%d transfer_total_qty=%f", sourceProjectFlockID, transferTotalQty)
|
||||
if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
|
||||
logHpp("GetDepresiasiTransfer", "use_manual_fallback=true")
|
||||
result, fallbackErr := s.getManualDepresiasiTransferFallback(projectFlockKandangId)
|
||||
if fallbackErr != nil {
|
||||
logHpp("GetDepresiasiTransfer", "manual_fallback_error=%v", fallbackErr)
|
||||
return 0, fallbackErr
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "done_fallback result=%f", result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
|
||||
if err != nil {
|
||||
logHpp("GetDepresiasiTransfer", "get_project_flock_kandang_ids_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "kandang_ids_growing=%v", kandangIDsGrowing)
|
||||
|
||||
totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
|
||||
if err != nil {
|
||||
logHpp("GetDepresiasiTransfer", "get_total_population_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "total_population_flock_growing=%f", totalPopulationFlockGrowing)
|
||||
if totalPopulationFlockGrowing == 0 {
|
||||
logHpp("GetDepresiasiTransfer", "total_population_flock_growing_zero return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
totalDepresiasiFlockGrowing, err := s.GetTotalDepresiasiFlockGrowing(sourceProjectFlockID, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetDepresiasiTransfer", "get_total_depresiasi_flock_growing_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("GetDepresiasiTransfer", "total_depresiasi_flock_growing=%f", totalDepresiasiFlockGrowing)
|
||||
|
||||
return (totalDepresiasiFlockGrowing * transferTotalQty) / totalPopulationFlockGrowing, nil
|
||||
result := (totalDepresiasiFlockGrowing * transferTotalQty) / totalPopulationFlockGrowing
|
||||
logHpp("GetDepresiasiTransfer", "done result=%f", result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *hppService) getManualDepresiasiTransferFallback(projectFlockKandangId uint) (float64, error) {
|
||||
logHpp("getManualDepresiasiTransferFallback", "start project_flock_kandang_id=%d", projectFlockKandangId)
|
||||
projectFlockID, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
logHpp("getManualDepresiasiTransferFallback", "get_project_flock_id_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("getManualDepresiasiTransferFallback", "project_flock_id=%d", projectFlockID)
|
||||
if projectFlockID == 0 {
|
||||
logHpp("getManualDepresiasiTransferFallback", "project_flock_id_zero return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
manualCost, err := s.hppRepo.GetManualDepreciationCostByProjectFlockID(context.Background(), projectFlockID)
|
||||
if err != nil {
|
||||
logHpp("getManualDepresiasiTransferFallback", "get_manual_depreciation_cost_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("getManualDepresiasiTransferFallback", "manual_cost=%f", manualCost)
|
||||
if manualCost <= 0 {
|
||||
logHpp("getManualDepresiasiTransferFallback", "manual_cost_non_positive return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockID)
|
||||
if err != nil {
|
||||
logHpp("getManualDepresiasiTransferFallback", "get_project_flock_kandang_ids_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("getManualDepresiasiTransferFallback", "kandang_ids=%v", kandangIDs)
|
||||
if len(kandangIDs) == 0 {
|
||||
logHpp("getManualDepresiasiTransferFallback", "kandang_ids_empty return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
totalUsageQty, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDs)
|
||||
if err != nil {
|
||||
logHpp("getManualDepresiasiTransferFallback", "get_total_usage_qty_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("getManualDepresiasiTransferFallback", "total_usage_qty=%f", totalUsageQty)
|
||||
if totalUsageQty <= 0 {
|
||||
logHpp("getManualDepresiasiTransferFallback", "total_usage_qty_non_positive return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
kandangUsageQty, err := s.hppRepo.GetTotalPopulation(context.Background(), []uint{projectFlockKandangId})
|
||||
if err != nil {
|
||||
logHpp("getManualDepresiasiTransferFallback", "get_kandang_usage_qty_error=%v", err)
|
||||
return 0, err
|
||||
}
|
||||
logHpp("getManualDepresiasiTransferFallback", "kandang_usage_qty=%f", kandangUsageQty)
|
||||
if kandangUsageQty <= 0 {
|
||||
logHpp("getManualDepresiasiTransferFallback", "kandang_usage_qty_non_positive return=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
result := manualCost * (kandangUsageQty / totalUsageQty)
|
||||
logHpp("getManualDepresiasiTransferFallback", "done result=%f", result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) {
|
||||
logHpp("GetHppEstimationDanRealisasi", "start total_production_cost=%f project_flock_kandang_id=%d start_date=%s end_date=%s", totalProductionCost, projectFlockKandangId, formatTimePtr(startDate), formatTimePtr(endDate))
|
||||
|
||||
if s.hppRepo == nil {
|
||||
logHpp("GetHppEstimationDanRealisasi", "repo_nil return_empty_response")
|
||||
return &HppCostResponse{}, nil
|
||||
}
|
||||
|
||||
estimPieces, estimWeightKg, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetHppEstimationDanRealisasi", "get_egg_produksi_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
logHpp("GetHppEstimationDanRealisasi", "estim_pieces=%f estim_weight_kg=%f", estimPieces, estimWeightKg)
|
||||
|
||||
realPieces, realWeightKg, err := s.hppRepo.GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, startDate, endDate)
|
||||
if err != nil {
|
||||
logHpp("GetHppEstimationDanRealisasi", "get_egg_terjual_error=%v", err)
|
||||
return nil, err
|
||||
}
|
||||
logHpp("GetHppEstimationDanRealisasi", "real_pieces=%f real_weight_kg=%f", realPieces, realWeightKg)
|
||||
|
||||
estimation := HppCostDetail{
|
||||
Total: totalProductionCost,
|
||||
@@ -248,6 +403,7 @@ func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, p
|
||||
if estimPieces > 0 {
|
||||
estimation.HargaButir = roundToTwoDecimals(totalProductionCost / estimPieces)
|
||||
}
|
||||
logHpp("GetHppEstimationDanRealisasi", "estimation=%+v", estimation)
|
||||
|
||||
real := HppCostDetail{
|
||||
Total: totalProductionCost,
|
||||
@@ -260,13 +416,29 @@ func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, p
|
||||
if realPieces > 0 {
|
||||
real.HargaButir = roundToTwoDecimals(totalProductionCost / realPieces)
|
||||
}
|
||||
logHpp("GetHppEstimationDanRealisasi", "real=%+v", real)
|
||||
|
||||
return &HppCostResponse{
|
||||
result := &HppCostResponse{
|
||||
Estimation: estimation,
|
||||
Real: real,
|
||||
}, nil
|
||||
}
|
||||
logHpp("GetHppEstimationDanRealisasi", "done response=%+v", *result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func roundToTwoDecimals(value float64) float64 {
|
||||
return math.Round(value*100) / 100
|
||||
result := math.Round(value*100) / 100
|
||||
logHpp("roundToTwoDecimals", "input=%f output=%f", value, result)
|
||||
return result
|
||||
}
|
||||
|
||||
func formatTimePtr(value *time.Time) string {
|
||||
if value == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return value.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func logHpp(method, format string, args ...any) {
|
||||
log.Printf("[HPP][%s] "+format, append([]any{method}, args...)...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const farmDepreciationSnapshotTable = "farm_depreciation_snapshots"
|
||||
|
||||
func NormalizeDateOnlyUTC(value time.Time) time.Time {
|
||||
if value.IsZero() {
|
||||
return value
|
||||
}
|
||||
v := value.UTC()
|
||||
return time.Date(v.Year(), v.Month(), v.Day(), 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
func MinNonZeroDateOnlyUTC(values ...time.Time) time.Time {
|
||||
var out time.Time
|
||||
for _, value := range values {
|
||||
if value.IsZero() {
|
||||
continue
|
||||
}
|
||||
normalized := NormalizeDateOnlyUTC(value)
|
||||
if out.IsZero() || normalized.Before(out) {
|
||||
out = normalized
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func InvalidateFarmDepreciationSnapshotsFromDate(ctx context.Context, db *gorm.DB, farmIDs []uint, fromDate time.Time) error {
|
||||
if db == nil {
|
||||
return nil
|
||||
}
|
||||
if fromDate.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
fromDate = NormalizeDateOnlyUTC(fromDate)
|
||||
query := db.WithContext(ctx).
|
||||
Table(farmDepreciationSnapshotTable).
|
||||
Where("period_date >= ?", fromDate)
|
||||
if len(farmIDs) > 0 {
|
||||
query = query.Where("project_flock_id IN ?", farmIDs)
|
||||
}
|
||||
return query.Delete(nil).Error
|
||||
}
|
||||
|
||||
func ResolveProjectFlockIDsByProjectFlockKandangIDs(ctx context.Context, db *gorm.DB, pfkIDs []uint) ([]uint, error) {
|
||||
if db == nil || len(pfkIDs) == 0 {
|
||||
return []uint{}, nil
|
||||
}
|
||||
|
||||
var projectFlockIDs []uint
|
||||
if err := db.WithContext(ctx).
|
||||
Table("project_flock_kandangs").
|
||||
Distinct("project_flock_id").
|
||||
Where("id IN ?", pfkIDs).
|
||||
Pluck("project_flock_id", &projectFlockIDs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return projectFlockIDs, nil
|
||||
}
|
||||
|
||||
func ResolveProjectFlockIDsByExpenseID(ctx context.Context, db *gorm.DB, expenseID uint) ([]uint, error) {
|
||||
if db == nil || expenseID == 0 {
|
||||
return []uint{}, nil
|
||||
}
|
||||
|
||||
query := `
|
||||
WITH direct_farms AS (
|
||||
SELECT DISTINCT pfk.project_flock_id
|
||||
FROM expense_nonstocks ens
|
||||
JOIN project_flock_kandangs pfk ON pfk.id = ens.project_flock_kandang_id
|
||||
WHERE ens.expense_id = @expense_id
|
||||
),
|
||||
json_farms AS (
|
||||
SELECT DISTINCT (jsonb_array_elements_text(e.project_flock_id::jsonb))::bigint AS project_flock_id
|
||||
FROM expenses e
|
||||
WHERE e.id = @expense_id
|
||||
AND e.project_flock_id IS NOT NULL
|
||||
)
|
||||
SELECT DISTINCT project_flock_id
|
||||
FROM (
|
||||
SELECT project_flock_id FROM direct_farms
|
||||
UNION ALL
|
||||
SELECT project_flock_id FROM json_farms
|
||||
) x
|
||||
`
|
||||
|
||||
var ids []uint
|
||||
if err := db.WithContext(ctx).Raw(query, map[string]any{
|
||||
"expense_id": expenseID,
|
||||
}).Scan(&ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
Reference in New Issue
Block a user