mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'development' into 'staging'
Development See merge request mbugroup/lti-api!246
This commit is contained in:
@@ -0,0 +1,307 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HppCostRepository interface {
|
||||||
|
GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error)
|
||||||
|
GetDocCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
||||||
|
GetBudgetCostByProjectFlockId(ctx context.Context, projectFlockId uint) (float64, error)
|
||||||
|
GetExpedisionCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
||||||
|
GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error)
|
||||||
|
GetOvkUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error)
|
||||||
|
GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
|
||||||
|
GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error)
|
||||||
|
GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error)
|
||||||
|
GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error)
|
||||||
|
GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error)
|
||||||
|
GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HppRepositoryImpl struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHppCostRepository(db *gorm.DB) HppCostRepository {
|
||||||
|
return &HppRepositoryImpl{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error) {
|
||||||
|
var ids []uint
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_flock_kandangs").
|
||||||
|
Select("id").
|
||||||
|
Where("project_flock_id = ?", projectFlockId).
|
||||||
|
Scan(&ids).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetDocCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_chickins AS pc").
|
||||||
|
Select("COALESCE(SUM(pc.usage_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 = ?", fifo.UsableKeyProjectChickin.String(), fifo.StockableKeyPurchaseItems.String()).
|
||||||
|
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||||
|
Where("pc.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetBudgetCostByProjectFlockId(ctx context.Context, projectFlockId uint) (float64, error) {
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_budgets AS pb").
|
||||||
|
Select("COALESCE(SUM(pb.qty * pb.price), 0)").
|
||||||
|
Where("pb.project_flock_id = ?", projectFlockId).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetExpedisionCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("expense_nonstocks AS en").
|
||||||
|
Select("COALESCE(SUM(er.qty * er.price), 0)").
|
||||||
|
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).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("recordings AS r").
|
||||||
|
Select("COALESCE(SUM(rs.usage_qty * COALESCE(pi.price, 0)), 0)").
|
||||||
|
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 = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String()).
|
||||||
|
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||||
|
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Where("r.record_datetime <= ?", *date).
|
||||||
|
Where("f.name = ?", utils.FlagPakan).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetOvkUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := []utils.FlagType{
|
||||||
|
utils.FlagOVK,
|
||||||
|
utils.FlagObat,
|
||||||
|
utils.FlagVitamin,
|
||||||
|
utils.FlagKimia,
|
||||||
|
}
|
||||||
|
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("recordings AS r").
|
||||||
|
Select("COALESCE(SUM(rs.usage_qty * COALESCE(pi.price, 0)), 0)").
|
||||||
|
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 = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String()).
|
||||||
|
Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id").
|
||||||
|
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Where("r.record_datetime <= ?", *date).
|
||||||
|
Where("f.name IN ?", flags).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) {
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_chickins AS pc").
|
||||||
|
Select("COALESCE(SUM(pc.usage_qty), 0)").
|
||||||
|
Where("pc.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error) {
|
||||||
|
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
||||||
|
stockableTransferIn := fifo.StockableKeyStockTransferIn.String()
|
||||||
|
usableProjectChickin := fifo.UsableKeyProjectChickin.String()
|
||||||
|
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_chickins AS pc").
|
||||||
|
Select(`
|
||||||
|
COALESCE(SUM(pc.usage_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).
|
||||||
|
Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id", usableProjectChickin).
|
||||||
|
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 = ?", stockableTransferIn, stockableTransferIn, stockablePurchase).
|
||||||
|
Joins("LEFT JOIN purchase_items AS tpi ON tpi.id = tsa.stockable_id").
|
||||||
|
Where("pc.project_flock_kandang_id = ?", projectFlockKandangId).
|
||||||
|
Scan(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
var totals struct {
|
||||||
|
TotalPieces float64
|
||||||
|
TotalWeightKg float64
|
||||||
|
}
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("recordings AS r").
|
||||||
|
Select("COALESCE(SUM(re.qty), 0) AS total_pieces, COALESCE(SUM(re.weight), 0)AS total_weight_kg").
|
||||||
|
Joins("JOIN recording_eggs AS re ON re.recording_id = r.id").
|
||||||
|
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Where("r.record_datetime <= ?", *date).
|
||||||
|
Scan(&totals).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return totals.TotalPieces, totals.TotalWeightKg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(
|
||||||
|
ctx context.Context,
|
||||||
|
projectFlockKandangIDs []uint,
|
||||||
|
date *time.Time,
|
||||||
|
) (float64, float64, error) {
|
||||||
|
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
type subResult struct {
|
||||||
|
UsableID uint
|
||||||
|
MdpUsageQty float64
|
||||||
|
MdpWeight float64
|
||||||
|
}
|
||||||
|
|
||||||
|
subQuery := r.db.WithContext(ctx).
|
||||||
|
Table("recordings AS r").
|
||||||
|
Select(`
|
||||||
|
DISTINCT sa.usable_id,
|
||||||
|
mdp.usage_qty AS mdp_usage_qty,
|
||||||
|
mdp.total_weight AS mdp_weight
|
||||||
|
`).
|
||||||
|
Joins("JOIN recording_eggs re ON re.recording_id = r.id").
|
||||||
|
Joins(
|
||||||
|
"JOIN stock_allocations sa ON sa.stockable_type = ? AND sa.stockable_id = re.id AND sa.usable_type = ?",
|
||||||
|
fifo.StockableKeyRecordingEgg.String(),
|
||||||
|
fifo.UsableKeyMarketingDelivery.String(),
|
||||||
|
).
|
||||||
|
Joins("JOIN marketing_delivery_products mdp ON mdp.id = sa.usable_id").
|
||||||
|
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||||
|
Where("r.record_datetime <= ?", *date)
|
||||||
|
|
||||||
|
var totals struct {
|
||||||
|
TotalPieces float64
|
||||||
|
TotalWeight float64
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("(?) AS x", subQuery).
|
||||||
|
Select(`
|
||||||
|
COALESCE(SUM(x.mdp_usage_qty), 0) AS total_pieces,
|
||||||
|
COALESCE(SUM(x.mdp_weight), 0) AS total_weight
|
||||||
|
`).
|
||||||
|
Scan(&totals).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return totals.TotalPieces, totals.TotalWeight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error) {
|
||||||
|
var projectFlockID uint
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("project_flock_kandangs").
|
||||||
|
Select("project_flock_id").
|
||||||
|
Where("id = ?", projectFlockKandangId).
|
||||||
|
Scan(&projectFlockID).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectFlockID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HppRepositoryImpl) GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error) {
|
||||||
|
var summary struct {
|
||||||
|
ProjectFlockID uint
|
||||||
|
TotalQty float64
|
||||||
|
}
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("laying_transfer_targets AS ltt").
|
||||||
|
Select("lt.from_project_flock_id AS project_flock_id, COALESCE(SUM(ltt.total_qty), 0) AS total_qty").
|
||||||
|
Joins("JOIN laying_transfers AS lt ON lt.id = ltt.laying_transfer_id").
|
||||||
|
Where("ltt.target_project_flock_kandang_id = ?", projectFlockKandangId).
|
||||||
|
Group("lt.from_project_flock_id").
|
||||||
|
Scan(&summary).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary.ProjectFlockID, summary.TotalQty, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HppService interface {
|
||||||
|
CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error)
|
||||||
|
GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error)
|
||||||
|
GetTotalProductionCost(projectFlockKandangId uint, date *time.Time, totalDepresiasiGrowing float64) (float64, error)
|
||||||
|
GetBudgetKandangLaying(projectFlockKandangId uint, date *time.Time) (float64, error)
|
||||||
|
GetDepresiasiTransfer(projectFlockKandangId uint, date *time.Time) (float64, error)
|
||||||
|
GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HppCostResponse struct {
|
||||||
|
Estimation HppCostDetail `json:"estimation"`
|
||||||
|
Real HppCostDetail `json:"real"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HppCostDetail struct {
|
||||||
|
HargaKg float64 `json:"harga_kg"`
|
||||||
|
HargaButir float64 `json:"harga_butir"`
|
||||||
|
Total float64 `json:"total"`
|
||||||
|
Kg float64 `json:"kg"`
|
||||||
|
Butir float64 `json:"butir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hppService struct {
|
||||||
|
hppRepo commonRepo.HppCostRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHppService(hppRepo commonRepo.HppCostRepository) HppService {
|
||||||
|
return &hppService{hppRepo: hppRepo}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
depresiasiTransfer, err := s.GetDepresiasiTransfer(projectFlockKandangId, date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalProductionCost, err := s.GetTotalProductionCost(projectFlockKandangId, date, depresiasiTransfer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, date)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hppRepo == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
docCost, err := s.hppRepo.GetDocCost(context.Background(), kandangIDs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
budgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), sourceProjectFlockID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expedisionCost, err := s.hppRepo.GetExpedisionCost(context.Background(), kandangIDs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
feedCost, err := s.hppRepo.GetFeedUsageCost(context.Background(), kandangIDs, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ovkCost, err := s.hppRepo.GetOvkUsageCost(context.Background(), kandangIDs, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return docCost + budgetCost + expedisionCost + feedCost + ovkCost, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) GetTotalProductionCost(projectFlockKandangId uint, date *time.Time, depresiasiTransfer float64) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
costPullet, err := s.hppRepo.GetPulletCost(context.Background(), projectFlockKandangId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
costFeed, err := s.hppRepo.GetFeedUsageCost(context.Background(), []uint{projectFlockKandangId}, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
costOvk, err := s.hppRepo.GetOvkUsageCost(context.Background(), []uint{projectFlockKandangId}, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
costExpedision, err := s.hppRepo.GetExpedisionCost(context.Background(), []uint{projectFlockKandangId})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
costBudget, err := s.GetBudgetKandangLaying(projectFlockKandangId, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return depresiasiTransfer + costPullet + costFeed + costOvk + costExpedision + costBudget, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) GetBudgetKandangLaying(projectFlockKandangId uint, date *time.Time) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hppRepo == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockId, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockKandangIds, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eggProduksiPiecesFlock, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), projectFlockKandangIds, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eggProduksiPiecesKandang, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBudgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), projectFlockId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if eggProduksiPiecesFlock == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (totalBudgetCost * eggProduksiPiecesKandang) / eggProduksiPiecesFlock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) GetDepresiasiTransfer(projectFlockKandangId uint, date *time.Time) (float64, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hppRepo == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if totalPopulationFlockGrowing == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDepresiasiFlockGrowing, err := s.GetTotalDepresiasiFlockGrowing(sourceProjectFlockID, date)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return (totalDepresiasiFlockGrowing * transferTotalQty) / totalPopulationFlockGrowing, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
|
||||||
|
if date == nil {
|
||||||
|
now := time.Now()
|
||||||
|
date = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hppRepo == nil {
|
||||||
|
return &HppCostResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
estimPieces, estimWeightKg, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
realPieces, realWeightKg, err := s.hppRepo.GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
estimation := HppCostDetail{
|
||||||
|
Total: totalProductionCost,
|
||||||
|
Kg: estimWeightKg,
|
||||||
|
Butir: estimPieces,
|
||||||
|
}
|
||||||
|
if estimWeightKg > 0 {
|
||||||
|
estimation.HargaKg = roundToTwoDecimals(totalProductionCost / estimWeightKg)
|
||||||
|
}
|
||||||
|
if estimPieces > 0 {
|
||||||
|
estimation.HargaButir = roundToTwoDecimals(totalProductionCost / estimPieces)
|
||||||
|
}
|
||||||
|
|
||||||
|
real := HppCostDetail{
|
||||||
|
Total: totalProductionCost,
|
||||||
|
Kg: realWeightKg,
|
||||||
|
Butir: realPieces,
|
||||||
|
}
|
||||||
|
if realWeightKg > 0 {
|
||||||
|
real.HargaKg = roundToTwoDecimals(totalProductionCost / realWeightKg)
|
||||||
|
}
|
||||||
|
if realPieces > 0 {
|
||||||
|
real.HargaButir = roundToTwoDecimals(totalProductionCost / realPieces)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HppCostResponse{
|
||||||
|
Estimation: estimation,
|
||||||
|
Real: real,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundToTwoDecimals(value float64) float64 {
|
||||||
|
return math.Round(value*100) / 100
|
||||||
|
}
|
||||||
@@ -44,7 +44,12 @@ type PenjualanRealisasiResponseDTO struct {
|
|||||||
|
|
||||||
func ToSalesDTO(e entity.MarketingDeliveryProduct) SalesDTO {
|
func ToSalesDTO(e entity.MarketingDeliveryProduct) SalesDTO {
|
||||||
|
|
||||||
ageInDay, ageInWeeks := calculateAgeFromChickin(e.MarketingProduct.ProductWarehouse.ProjectFlockKandang, e.DeliveryDate)
|
productFlags := make([]string, len(e.MarketingProduct.ProductWarehouse.Product.Flags))
|
||||||
|
for i, f := range e.MarketingProduct.ProductWarehouse.Product.Flags {
|
||||||
|
productFlags[i] = f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
ageInDay, ageInWeeks := calculateAgeFromChickin(e.MarketingProduct.ProductWarehouse.ProjectFlockKandang, e.DeliveryDate, productFlags)
|
||||||
|
|
||||||
var product *productDTO.ProductRelationDTO
|
var product *productDTO.ProductRelationDTO
|
||||||
if e.MarketingProduct.ProductWarehouse.Product.Id != 0 {
|
if e.MarketingProduct.ProductWarehouse.Product.Id != 0 {
|
||||||
@@ -126,11 +131,17 @@ func ToPenjualanRealisasiResponseDTO(e []entity.MarketingDeliveryProduct) Penjua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, deliveryDate *time.Time) (int, int) {
|
func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, deliveryDate *time.Time, productFlags []string) (int, int) {
|
||||||
if projectFlockKandang == nil || deliveryDate == nil || len(projectFlockKandang.Chickins) == 0 {
|
if projectFlockKandang == nil || deliveryDate == nil || len(projectFlockKandang.Chickins) == 0 {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, flag := range productFlags {
|
||||||
|
if flag == "OVK" || flag == "PAKAN" {
|
||||||
|
return 0, 0 //
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
earliestChickinDate := projectFlockKandang.Chickins[0].ChickInDate
|
earliestChickinDate := projectFlockKandang.Chickins[0].ChickInDate
|
||||||
for _, chickin := range projectFlockKandang.Chickins {
|
for _, chickin := range projectFlockKandang.Chickins {
|
||||||
if chickin.ChickInDate.Before(earliestChickinDate) {
|
if chickin.ChickInDate.Before(earliestChickinDate) {
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ func ToSapronakProjectAggregatedFromReport(report *SapronakReportDTO, flag strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
for idx, item := range group.Items {
|
for idx, item := range group.Items {
|
||||||
productKey := strings.ToUpper(flagKey + "|" + item.ProductName)
|
productKey := strings.ToUpper(flagKey + "|" + item.ProductName + "|" + item.NoReferensi + "|" + formatDate(item.Tanggal))
|
||||||
baseRow := SapronakCategoryRowDTO{
|
baseRow := SapronakCategoryRowDTO{
|
||||||
ID: idx + 1,
|
ID: idx + 1,
|
||||||
Date: formatDate(item.Tanggal),
|
Date: formatDate(item.Tanggal),
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ type ClosingRepository interface {
|
|||||||
FetchSapronakUsageDetails(ctx context.Context, pfkID uint) (map[uint][]SapronakDetailRow, error)
|
FetchSapronakUsageDetails(ctx context.Context, pfkID uint) (map[uint][]SapronakDetailRow, error)
|
||||||
FetchSapronakChickinUsage(ctx context.Context, pfkID uint) ([]SapronakUsageRow, error)
|
FetchSapronakChickinUsage(ctx context.Context, pfkID uint) ([]SapronakUsageRow, error)
|
||||||
FetchSapronakChickinUsageDetails(ctx context.Context, pfkID uint) (map[uint][]SapronakDetailRow, error)
|
FetchSapronakChickinUsageDetails(ctx context.Context, pfkID uint) (map[uint][]SapronakDetailRow, error)
|
||||||
|
FetchSapronakUsageAllocatedDetails(ctx context.Context, projectFlockKandangID uint) (map[uint][]SapronakDetailRow, error)
|
||||||
FetchSapronakAdjustments(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
FetchSapronakAdjustments(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
||||||
FetchSapronakTransfers(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
FetchSapronakTransfers(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
||||||
FetchSapronakSales(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, error)
|
FetchSapronakSales(ctx context.Context, projectFlockKandangID uint) (map[uint][]SapronakDetailRow, error)
|
||||||
GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error)
|
GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,6 +873,74 @@ func (r *ClosingRepositoryImpl) FetchSapronakChickinUsageDetails(ctx context.Con
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ClosingRepositoryImpl) FetchSapronakUsageAllocatedDetails(ctx context.Context, projectFlockKandangID uint) (map[uint][]SapronakDetailRow, error) {
|
||||||
|
if projectFlockKandangID == 0 {
|
||||||
|
return map[uint][]SapronakDetailRow{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
query := r.withCtx(ctx).
|
||||||
|
Table("stock_allocations AS sa").
|
||||||
|
Select(`
|
||||||
|
pw.product_id AS product_id,
|
||||||
|
p.name AS product_name,
|
||||||
|
f.name AS flag,
|
||||||
|
COALESCE(
|
||||||
|
pi.received_date,
|
||||||
|
st.transfer_date,
|
||||||
|
lt.transfer_date,
|
||||||
|
sl.created_at,
|
||||||
|
pc.chick_in_date,
|
||||||
|
r.record_datetime
|
||||||
|
) AS date,
|
||||||
|
COALESCE(
|
||||||
|
po.po_number,
|
||||||
|
st.movement_number,
|
||||||
|
lt.transfer_number,
|
||||||
|
CONCAT('ADJ-', ast.id),
|
||||||
|
CONCAT('CHICKIN-', pc.id),
|
||||||
|
CAST(r.id AS TEXT),
|
||||||
|
''
|
||||||
|
) AS reference,
|
||||||
|
0 AS qty_in,
|
||||||
|
COALESCE(SUM(sa.qty), 0) AS qty_out,
|
||||||
|
COALESCE(pi.price, p.product_price, 0) AS price
|
||||||
|
`).
|
||||||
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
||||||
|
Joins("JOIN products p ON p.id = pw.product_id").
|
||||||
|
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
|
Joins("LEFT JOIN recording_stocks rs ON rs.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyRecordingStock.String()).
|
||||||
|
Joins("LEFT JOIN recordings r ON r.id = rs.recording_id").
|
||||||
|
Joins("LEFT JOIN project_chickins pc_used ON pc_used.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
||||||
|
Joins("LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyPurchaseItems.String()).
|
||||||
|
Joins("LEFT JOIN purchases po ON po.id = pi.purchase_id").
|
||||||
|
Joins("LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyStockTransferIn.String()).
|
||||||
|
Joins("LEFT JOIN stock_transfers st ON st.id = std.stock_transfer_id").
|
||||||
|
Joins("LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyTransferToLayingIn.String()).
|
||||||
|
Joins("LEFT JOIN laying_transfers lt ON lt.id = ltt.laying_transfer_id").
|
||||||
|
Joins("LEFT JOIN adjustment_stocks ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyAdjustmentIn.String()).
|
||||||
|
Joins("LEFT JOIN stock_logs sl ON sl.id = ast.stock_log_id").
|
||||||
|
Joins("LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = ?", fifo.StockableKeyProjectFlockPopulation.String()).
|
||||||
|
Joins("LEFT JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
|
||||||
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
||||||
|
Where("f.name IN ?", sapronakFlagsAll).
|
||||||
|
Where(`
|
||||||
|
(sa.usable_type = ? AND r.project_flock_kandangs_id = ?)
|
||||||
|
OR
|
||||||
|
(sa.usable_type = ? AND pc_used.project_flock_kandang_id = ?)
|
||||||
|
`,
|
||||||
|
fifo.UsableKeyRecordingStock.String(), projectFlockKandangID,
|
||||||
|
fifo.UsableKeyProjectChickin.String(), projectFlockKandangID,
|
||||||
|
).
|
||||||
|
Group(`
|
||||||
|
pw.product_id, p.name, f.name,
|
||||||
|
pi.received_date, st.transfer_date, lt.transfer_date, sl.created_at, pc.chick_in_date, r.record_datetime,
|
||||||
|
po.po_number, st.movement_number, lt.transfer_number, ast.id, pc.id, r.id,
|
||||||
|
pi.price, p.product_price
|
||||||
|
`)
|
||||||
|
|
||||||
|
return scanAndGroupDetails(query)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) incomingPurchaseBase(ctx context.Context, kandangID uint) *gorm.DB {
|
func (r *ClosingRepositoryImpl) incomingPurchaseBase(ctx context.Context, kandangID uint) *gorm.DB {
|
||||||
return r.withCtx(ctx).
|
return r.withCtx(ctx).
|
||||||
Table("purchase_items AS pi").
|
Table("purchase_items AS pi").
|
||||||
@@ -1131,7 +1200,7 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
|||||||
return incoming, outgoing, nil
|
return incoming, outgoing, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, error) {
|
func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, projectFlockKandangID uint) (map[uint][]SapronakDetailRow, error) {
|
||||||
query := r.withCtx(ctx).
|
query := r.withCtx(ctx).
|
||||||
Table("stock_allocations AS sa").
|
Table("stock_allocations AS sa").
|
||||||
Select(`
|
Select(`
|
||||||
@@ -1148,15 +1217,55 @@ func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, kandangI
|
|||||||
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
||||||
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
||||||
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
||||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
|
||||||
Joins("JOIN products p ON p.id = pw.product_id").
|
Joins("JOIN products p ON p.id = pw.product_id").
|
||||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
||||||
Where("w.kandang_id = ?", kandangID).
|
Where("pw.project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
Where("f.name IN ?", sapronakFlagsAll).
|
Where("f.name IN ?", sapronakFlagsAll).
|
||||||
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
||||||
|
|
||||||
return scanAndGroupDetails(query)
|
sales, err := scanAndGroupDetails(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonFifoQuery := r.withCtx(ctx).
|
||||||
|
Table("marketing_delivery_products AS mdp").
|
||||||
|
Select(`
|
||||||
|
pw.product_id AS product_id,
|
||||||
|
p.name AS product_name,
|
||||||
|
f.name AS flag,
|
||||||
|
COALESCE(mdp.delivery_date, mdp.created_at) AS date,
|
||||||
|
COALESCE(m.so_number, '') AS reference,
|
||||||
|
0 AS qty_in,
|
||||||
|
COALESCE(mdp.usage_qty, 0) AS qty_out,
|
||||||
|
COALESCE(mdp.unit_price, mp.unit_price, 0) AS price
|
||||||
|
`).
|
||||||
|
Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
||||||
|
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
||||||
|
Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
|
||||||
|
Joins("JOIN products p ON p.id = pw.product_id").
|
||||||
|
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
|
Joins("LEFT JOIN stock_allocations sa ON sa.usable_id = mdp.id AND sa.usable_type = ? AND sa.status = ?",
|
||||||
|
fifo.UsableKeyMarketingDelivery.String(),
|
||||||
|
entity.StockAllocationStatusActive,
|
||||||
|
).
|
||||||
|
Where("mdp.usage_qty > 0").
|
||||||
|
Where("sa.id IS NULL").
|
||||||
|
Where("pw.project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
|
Where("f.name IN ?", sapronakFlagsAll).
|
||||||
|
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
||||||
|
|
||||||
|
nonFifoSales, err := scanAndGroupDetails(nonFifoQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for pid, rows := range nonFifoSales {
|
||||||
|
sales[pid] = append(sales[pid], rows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sales, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error) {
|
func (r *ClosingRepositoryImpl) GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error) {
|
||||||
|
|||||||
@@ -743,6 +743,10 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine production week")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine production week")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isGrowing && currentWeek != 0 {
|
||||||
|
currentWeek = currentWeek + 17
|
||||||
|
}
|
||||||
|
|
||||||
targetAverages, err := s.RecordingRepo.GetAverageTargetMetricsByProjectFlockKandangID(c.Context(), projectFlockKandangIDs[0], !isGrowing)
|
targetAverages, err := s.RecordingRepo.GetAverageTargetMetricsByProjectFlockKandangID(c.Context(), projectFlockKandangIDs[0], !isGrowing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to calculate target metrics for project flock %d: %+v", projectFlockID, err)
|
s.Log.Errorf("Failed to calculate target metrics for project flock %d: %+v", projectFlockID, err)
|
||||||
|
|||||||
@@ -347,6 +347,14 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, 0, err
|
return nil, nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
usageAllocatedDetails, err := s.Repository.FetchSapronakUsageAllocatedDetails(ctx, pfk.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, 0, err
|
||||||
|
}
|
||||||
|
if len(usageAllocatedDetails) > 0 {
|
||||||
|
usageDetailsRows = usageAllocatedDetails
|
||||||
|
chickinUsageDetailsRows = map[uint][]repository.SapronakDetailRow{}
|
||||||
|
}
|
||||||
adjIncomingRows, adjOutgoingRows, err := s.Repository.FetchSapronakAdjustments(ctx, pfk.KandangId)
|
adjIncomingRows, adjOutgoingRows, err := s.Repository.FetchSapronakAdjustments(ctx, pfk.KandangId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, 0, err
|
return nil, nil, 0, 0, err
|
||||||
@@ -355,7 +363,7 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, 0, err
|
return nil, nil, 0, 0, err
|
||||||
}
|
}
|
||||||
salesOutRows, err := s.Repository.FetchSapronakSales(ctx, pfk.KandangId)
|
salesOutRows, err := s.Repository.FetchSapronakSales(ctx, pfk.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, 0, err
|
return nil, nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
newLog.Increase = afterQuantity
|
newLog.Increase = afterQuantity
|
||||||
} else {
|
} else {
|
||||||
if productWarehouse.Quantity < req.Quantity {
|
if productWarehouse.Quantity < req.Quantity {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock. Current: %.2f, Requested: %.2f", productWarehouse.Quantity, req.Quantity))
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Stok tidak mencukupi untuk pengurangan. Stok saat ini: %.2f, Jumlah yang akan dikurangi: %.2f", productWarehouse.Quantity, req.Quantity))
|
||||||
}
|
}
|
||||||
afterQuantity -= req.Quantity
|
afterQuantity -= req.Quantity
|
||||||
newLog.Decrease = afterQuantity
|
newLog.Decrease = afterQuantity
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ func (s transferService) GetOne(c *fiber.Ctx, id uint) (*entity.StockTransfer, e
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Transfer dengan ID %d tidak ditemukan", id))
|
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Transfer dengan ID %d tidak ditemukan", id))
|
||||||
}
|
}
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengambil data transfer dengan ID %d", id))
|
s.Log.Errorf("Failed to fetch transfer by ID %d: %+v", id, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data transfer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return transferPtr, nil
|
return transferPtr, nil
|
||||||
@@ -142,7 +143,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Produk dengan ID %d tidak ditemukan di gudang asal (ID: %d)", product.ProductID, req.SourceWarehouseID))
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Produk dengan ID %d tidak ditemukan di gudang asal (ID: %d)", product.ProductID, req.SourceWarehouseID))
|
||||||
}
|
}
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengecek stok produk %d di gudang asal", product.ProductID))
|
s.Log.Errorf("Failed to fetch product warehouse for product_id=%d, warehouse_id=%d: %+v", product.ProductID, req.SourceWarehouseID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengecek stok produk")
|
||||||
}
|
}
|
||||||
if sourcePW.Quantity < product.ProductQty {
|
if sourcePW.Quantity < product.ProductQty {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Stok produk %d di gudang asal tidak mencukupi. Tersedia: %.2f, Diminta: %.2f", product.ProductID, sourcePW.Quantity, product.ProductQty))
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Stok produk %d di gudang asal tidak mencukupi. Tersedia: %.2f, Diminta: %.2f", product.ProductID, sourcePW.Quantity, product.ProductQty))
|
||||||
@@ -163,13 +165,16 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if destPfkID > 0 {
|
||||||
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), destPfkID)
|
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), destPfkID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data project flock untuk gudang tujuan")
|
s.Log.Errorf("Failed to fetch project flock kandang by ID %d: %+v", destPfkID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data project flock")
|
||||||
}
|
}
|
||||||
if projectFlockKandang.ClosedAt != nil {
|
if projectFlockKandang.ClosedAt != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Project flock untuk gudang tujuan sudah ditutup (closing) pada %s", projectFlockKandang.ClosedAt.Format("2006-01-02")))
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Project flock untuk gudang tujuan sudah ditutup (closing) pada %s", projectFlockKandang.ClosedAt.Format("2006-01-02")))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actorID, err := m.ActorIDFromContext(c)
|
actorID, err := m.ActorIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -196,7 +201,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Supplier dengan ID %d tidak ditemukan", delivery.SupplierID))
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Supplier dengan ID %d tidak ditemukan", delivery.SupplierID))
|
||||||
}
|
}
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengambil data supplier dengan ID %d", delivery.SupplierID))
|
s.Log.Errorf("Failed to fetch supplier by ID %d: %+v", delivery.SupplierID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data supplier")
|
||||||
}
|
}
|
||||||
if supplier.Category != string(utils.SupplierCategoryBOP) {
|
if supplier.Category != string(utils.SupplierCategoryBOP) {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Supplier '%s' (ID: %d) bukan kategori BOP. Kategori saat ini: %s", supplier.Name, delivery.SupplierID, supplier.Category))
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Supplier '%s' (ID: %d) bukan kategori BOP. Kategori saat ini: %s", supplier.Name, delivery.SupplierID, supplier.Category))
|
||||||
@@ -205,7 +211,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
|
|
||||||
movementNumber, err := s.StockTransferRepo.GenerateMovementNumber(c.Context())
|
movementNumber, err := s.StockTransferRepo.GenerateMovementNumber(c.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat nomor movement transfer")
|
s.Log.Errorf("Failed to generate movement number: %+v", err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat nomor transfer")
|
||||||
}
|
}
|
||||||
|
|
||||||
transferDate, _ := utils.ParseDateString(req.TransferDate)
|
transferDate, _ := utils.ParseDateString(req.TransferDate)
|
||||||
@@ -245,14 +252,16 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Produk %d tidak ditemukan di gudang asal (ID: %d)", product.ProductID, req.SourceWarehouseID))
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Produk %d tidak ditemukan di gudang asal (ID: %d)", product.ProductID, req.SourceWarehouseID))
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengambil data product warehouse untuk produk %d di gudang asal", product.ProductID))
|
s.Log.Errorf("Failed to fetch source product warehouse for product_id=%d, warehouse_id=%d: %+v", product.ProductID, req.SourceWarehouseID, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data stok gudang asal")
|
||||||
}
|
}
|
||||||
|
|
||||||
destPW, err := productWarehouseRepoTX.GetProductWarehouseByProductAndWarehouseID(
|
destPW, err := productWarehouseRepoTX.GetProductWarehouseByProductAndWarehouseID(
|
||||||
c.Context(), uint(product.ProductID), uint(req.DestinationWarehouseID),
|
c.Context(), uint(product.ProductID), uint(req.DestinationWarehouseID),
|
||||||
)
|
)
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengambil data product warehouse untuk produk %d di gudang tujuan", product.ProductID))
|
s.Log.Errorf("Failed to fetch dest product warehouse for product_id=%d, warehouse_id=%d: %+v", product.ProductID, req.DestinationWarehouseID, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data stok gudang tujuan")
|
||||||
}
|
}
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
@@ -261,7 +270,6 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set ProjectFlockKandangId hanya jika ada kandang
|
|
||||||
var pfkID *uint
|
var pfkID *uint
|
||||||
if projectFlockKandangID > 0 {
|
if projectFlockKandangID > 0 {
|
||||||
pfkID = &projectFlockKandangID
|
pfkID = &projectFlockKandangID
|
||||||
@@ -274,7 +282,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
ProjectFlockKandangId: pfkID,
|
ProjectFlockKandangId: pfkID,
|
||||||
}
|
}
|
||||||
if err := productWarehouseRepoTX.CreateOne(c.Context(), destPW, nil); err != nil {
|
if err := productWarehouseRepoTX.CreateOne(c.Context(), destPW, nil); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal membuat product warehouse untuk produk %d di gudang tujuan", product.ProductID))
|
s.Log.Errorf("Failed to create product warehouse for product_id=%d, warehouse_id=%d: %+v", product.ProductID, req.DestinationWarehouseID, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat data stok gudang tujuan")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,9 +373,9 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
Files: documentFiles,
|
Files: documentFiles,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.WithError(err).Errorf("Failed to upload document for delivery %d (delivery_id: %d, filename: %s)",
|
s.Log.Errorf("Failed to upload document for delivery %d (delivery_id=%d, filename=%s): %+v",
|
||||||
deliveryIdx+1, delivery.Id, file.Filename)
|
deliveryIdx+1, delivery.Id, file.Filename, err)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to upload document for delivery %d: %v", deliveryIdx+1, err))
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal mengunggah dokumen")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +401,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
"usage_qty": consumeResult.UsageQuantity,
|
"usage_qty": consumeResult.UsageQuantity,
|
||||||
"pending_qty": consumeResult.PendingQuantity,
|
"pending_qty": consumeResult.PendingQuantity,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengupdate tracking usage untuk produk %d", product.ProductID))
|
s.Log.Errorf("Failed to update tracking usage for detail_id=%d, product_id=%d: %+v", detail.Id, product.ProductID, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal memperbarui data tracking")
|
||||||
}
|
}
|
||||||
|
|
||||||
note := fmt.Sprintf("Transfer #%s", entityTransfer.MovementNumber)
|
note := fmt.Sprintf("Transfer #%s", entityTransfer.MovementNumber)
|
||||||
@@ -405,7 +415,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
Tx: tx,
|
Tx: tx,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal menambah stok untuk produk %d di gudang tujuan. Error: %v", product.ProductID, err))
|
s.Log.Errorf("Failed to replenish stock for product_id=%d, pw_id=%d, qty=%.2f: %+v", product.ProductID, *detail.DestProductWarehouseID, product.ProductQty, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal menambah stok gudang tujuan")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Model(&entity.StockTransferDetail{}).
|
if err := tx.Model(&entity.StockTransferDetail{}).
|
||||||
@@ -413,7 +424,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
Updates(map[string]interface{}{
|
Updates(map[string]interface{}{
|
||||||
"total_qty": replenishResult.AddedQuantity,
|
"total_qty": replenishResult.AddedQuantity,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengupdate tracking total untuk produk %d", product.ProductID))
|
s.Log.Errorf("Failed to update tracking total for detail_id=%d, product_id=%d: %+v", detail.Id, product.ProductID, err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Gagal memperbarui data tracking")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +459,10 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal memproses transfer. Error: %v", err))
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||||
|
return nil, fiberErr
|
||||||
|
}
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.GetOne(c, uint(entityTransfer.Id))
|
result, err := s.GetOne(c, uint(entityTransfer.Id))
|
||||||
@@ -457,7 +472,8 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques
|
|||||||
|
|
||||||
if len(expensePayloads) > 0 {
|
if len(expensePayloads) > 0 {
|
||||||
if err := s.notifyExpenseItemsDelivered(c, entityTransfer.Id, expensePayloads); err != nil {
|
if err := s.notifyExpenseItemsDelivered(c, entityTransfer.Id, expensePayloads); err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal sinkronisasi data expense untuk transfer %s. Silakan cek manual di module expense", entityTransfer.MovementNumber))
|
s.Log.Errorf("Failed to sync expense for transfer_id=%d, movement_number=%s: %+v", entityTransfer.Id, entityTransfer.MovementNumber, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal sinkronisasi data expense. Silakan cek manual di module expense")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,10 +493,10 @@ func (s *transferService) getActiveProjectFlockKandangID(ctx context.Context, wa
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return 0, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Gudang dengan ID %d tidak ditemukan", warehouseID))
|
return 0, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Gudang dengan ID %d tidak ditemukan", warehouseID))
|
||||||
}
|
}
|
||||||
return 0, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Gagal mengambil data gudang dengan ID %d", warehouseID))
|
s.Log.Errorf("Failed to fetch warehouse by ID %d: %+v", warehouseID, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data gudang")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika warehouse tidak punya kandang_id, return 0 tanpa error
|
|
||||||
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -490,7 +506,8 @@ func (s *transferService) getActiveProjectFlockKandangID(ctx context.Context, wa
|
|||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tidak ada project flock aktif untuk kandang %d", *warehouse.KandangId))
|
return 0, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tidak ada project flock aktif untuk kandang %d", *warehouse.KandangId))
|
||||||
}
|
}
|
||||||
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data project flock kandang yang aktif")
|
s.Log.Errorf("Failed to fetch active project flock kandang for kandang_id=%d: %+v", *warehouse.KandangId, err)
|
||||||
|
return 0, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data project flock")
|
||||||
}
|
}
|
||||||
|
|
||||||
return uint(projectFlockKandang.Id), nil
|
return uint(projectFlockKandang.Id), nil
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
rCustomer "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/repositories"
|
rCustomer "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/repositories"
|
||||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
|
rShared "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
@@ -32,6 +33,7 @@ func (MarketingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
userRepo := rUser.NewUserRepository(db)
|
userRepo := rUser.NewUserRepository(db)
|
||||||
customerRepo := rCustomer.NewCustomerRepository(db)
|
customerRepo := rCustomer.NewCustomerRepository(db)
|
||||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||||
|
stockLogRepo := rShared.NewStockLogRepository(db)
|
||||||
|
|
||||||
stockAllocationRepo := commonRepo.NewStockAllocationRepository(db)
|
stockAllocationRepo := commonRepo.NewStockAllocationRepository(db)
|
||||||
fifoService := commonSvc.NewFifoService(db, stockAllocationRepo, productWarehouseRepo, utils.Log)
|
fifoService := commonSvc.NewFifoService(db, stockAllocationRepo, productWarehouseRepo, utils.Log)
|
||||||
@@ -63,7 +65,7 @@ func (MarketingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||||
|
|
||||||
salesOrdersService := service.NewSalesOrdersService(marketingRepo, customerRepo, productWarehouseRepo, userRepo, approvalSvc, warehouseRepo, projectFlockKandangRepo, validate)
|
salesOrdersService := service.NewSalesOrdersService(marketingRepo, customerRepo, productWarehouseRepo, userRepo, approvalSvc, warehouseRepo, projectFlockKandangRepo, validate)
|
||||||
deliveryOrdersService := service.NewDeliveryOrdersService(marketingRepo, marketingProductRepo, marketingDeliveryProductRepo, approvalSvc, fifoService, validate)
|
deliveryOrdersService := service.NewDeliveryOrdersService(marketingRepo, marketingProductRepo, marketingDeliveryProductRepo, stockLogRepo, approvalSvc, fifoService, validate)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
RegisterRoutes(router, userService, salesOrdersService, deliveryOrdersService)
|
RegisterRoutes(router, userService, salesOrdersService, deliveryOrdersService)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
|
||||||
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/validations"
|
||||||
|
rShared "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ type deliveryOrdersService struct {
|
|||||||
MarketingRepo marketingRepo.MarketingRepository
|
MarketingRepo marketingRepo.MarketingRepository
|
||||||
MarketingProductRepo marketingRepo.MarketingProductRepository
|
MarketingProductRepo marketingRepo.MarketingProductRepository
|
||||||
MarketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository
|
MarketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository
|
||||||
|
StockLogRepo rShared.StockLogRepository
|
||||||
ApprovalSvc commonSvc.ApprovalService
|
ApprovalSvc commonSvc.ApprovalService
|
||||||
FifoSvc commonSvc.FifoService
|
FifoSvc commonSvc.FifoService
|
||||||
}
|
}
|
||||||
@@ -42,6 +44,7 @@ func NewDeliveryOrdersService(
|
|||||||
marketingRepo marketingRepo.MarketingRepository,
|
marketingRepo marketingRepo.MarketingRepository,
|
||||||
marketingProductRepo marketingRepo.MarketingProductRepository,
|
marketingProductRepo marketingRepo.MarketingProductRepository,
|
||||||
marketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository,
|
marketingDeliveryProductRepo marketingRepo.MarketingDeliveryProductRepository,
|
||||||
|
stockLogRepo rShared.StockLogRepository,
|
||||||
approvalSvc commonSvc.ApprovalService,
|
approvalSvc commonSvc.ApprovalService,
|
||||||
fifoSvc commonSvc.FifoService,
|
fifoSvc commonSvc.FifoService,
|
||||||
validate *validator.Validate,
|
validate *validator.Validate,
|
||||||
@@ -51,6 +54,7 @@ func NewDeliveryOrdersService(
|
|||||||
MarketingRepo: marketingRepo,
|
MarketingRepo: marketingRepo,
|
||||||
MarketingProductRepo: marketingProductRepo,
|
MarketingProductRepo: marketingProductRepo,
|
||||||
MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
|
MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
|
||||||
|
StockLogRepo: stockLogRepo,
|
||||||
ApprovalSvc: approvalSvc,
|
ApprovalSvc: approvalSvc,
|
||||||
FifoSvc: fifoSvc,
|
FifoSvc: fifoSvc,
|
||||||
}
|
}
|
||||||
@@ -247,7 +251,6 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
itemDeliveryDate = &parsedDate
|
itemDeliveryDate = &parsedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cek apakah product punya flag PAKAN atau OVK
|
|
||||||
isPakanOrOVK := false
|
isPakanOrOVK := false
|
||||||
if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
|
if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
|
||||||
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
|
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
|
||||||
@@ -258,14 +261,13 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hitung total_weight dan total_price berdasarkan flag
|
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||||
var totalPrice float64
|
var totalPrice float64
|
||||||
if isPakanOrOVK {
|
if isPakanOrOVK {
|
||||||
// PAKAN atau OVK: qty × unit_price
|
|
||||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
||||||
} else {
|
} else {
|
||||||
// Produk lain: total_weight × unit_price
|
|
||||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
totalPrice = totalWeight * requestedProduct.UnitPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
|
|
||||||
if requestedProduct.Qty > 0 {
|
if requestedProduct.Qty > 0 {
|
||||||
|
|
||||||
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty); err != nil {
|
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +329,12 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.MarketingRepo.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
actorID, err := m.ActorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.MarketingRepo.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||||
|
|
||||||
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
||||||
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||||
@@ -390,14 +397,13 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hitung total_weight dan total_price berdasarkan flag
|
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||||
var totalPrice float64
|
var totalPrice float64
|
||||||
if isPakanOrOVK {
|
if isPakanOrOVK {
|
||||||
// PAKAN atau OVK: qty × unit_price
|
|
||||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
||||||
} else {
|
} else {
|
||||||
// Produk lain: total_weight × unit_price
|
|
||||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
totalPrice = totalWeight * requestedProduct.UnitPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,13 +418,13 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
if requestedProduct.Qty != oldRequestedQty {
|
if requestedProduct.Qty != oldRequestedQty {
|
||||||
|
|
||||||
if oldRequestedQty > 0 {
|
if oldRequestedQty > 0 {
|
||||||
if err := s.releaseDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct); err != nil {
|
if err := s.releaseDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestedProduct.Qty > 0 {
|
if requestedProduct.Qty > 0 {
|
||||||
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty); err != nil {
|
if err := s.consumeDeliveryStock(c.Context(), dbTransaction, deliveryProduct, foundMarketingProduct, requestedProduct.Qty, actorID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,7 +449,7 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
return s.getMarketingWithDeliveries(c, id)
|
return s.getMarketingWithDeliveries(c, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64) error {
|
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 {
|
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
||||||
}
|
}
|
||||||
@@ -463,6 +469,20 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
|
|||||||
|
|
||||||
deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx)
|
deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx)
|
||||||
|
|
||||||
|
if err == nil && result.UsageQuantity > 0 {
|
||||||
|
if actorID > 0 {
|
||||||
|
decreaseLog := &entity.StockLog{
|
||||||
|
Decrease: result.UsageQuantity,
|
||||||
|
LoggableType: string(utils.StockLogTypeMarketing),
|
||||||
|
LoggableId: deliveryProduct.Id,
|
||||||
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Notes: "",
|
||||||
|
}
|
||||||
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pwRepo := productWarehouseRepo.NewProductWarehouseRepository(tx)
|
pwRepo := productWarehouseRepo.NewProductWarehouseRepository(tx)
|
||||||
pw, err2 := pwRepo.GetByID(ctx, marketingProduct.ProductWarehouseId, nil)
|
pw, err2 := pwRepo.GetByID(ctx, marketingProduct.ProductWarehouseId, nil)
|
||||||
@@ -483,6 +503,19 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
|
|||||||
if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, requestedQty, 0); err != nil {
|
if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, requestedQty, 0); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if actorID > 0 {
|
||||||
|
decreaseLog := &entity.StockLog{
|
||||||
|
Decrease: requestedQty,
|
||||||
|
LoggableType: string(utils.StockLogTypeMarketing),
|
||||||
|
LoggableId: deliveryProduct.Id,
|
||||||
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Notes: "",
|
||||||
|
}
|
||||||
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +526,7 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct) error {
|
func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, actorID uint) error {
|
||||||
if deliveryProduct == nil || deliveryProduct.Id == 0 {
|
if deliveryProduct == nil || deliveryProduct.Id == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -520,6 +553,18 @@ func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gor
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if actorID > 0 && currentUsage > 0 {
|
||||||
|
increaseLog := &entity.StockLog{
|
||||||
|
Increase: currentUsage,
|
||||||
|
LoggableType: string(utils.StockLogTypeMarketing),
|
||||||
|
LoggableId: deliveryProduct.Id,
|
||||||
|
ProductWarehouseId: marketingProduct.ProductWarehouseId,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
Notes: "",
|
||||||
|
}
|
||||||
|
s.StockLogRepo.WithTx(tx).CreateOne(ctx, increaseLog, nil)
|
||||||
|
}
|
||||||
|
|
||||||
if err := deliveryProductRepo.ResetFifoFields(ctx, deliveryProduct.Id); err != nil {
|
if err := deliveryProductRepo.ResetFifoFields(ctx, deliveryProduct.Id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,14 +311,11 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hitung total_weight dan total_price berdasarkan flag
|
|
||||||
totalWeight := rp.Qty * rp.AvgWeight
|
totalWeight := rp.Qty * rp.AvgWeight
|
||||||
var totalPrice float64
|
var totalPrice float64
|
||||||
if isPakanOrOVK {
|
if isPakanOrOVK {
|
||||||
// PAKAN atau OVK: qty × unit_price
|
|
||||||
totalPrice = rp.Qty * rp.UnitPrice
|
totalPrice = rp.Qty * rp.UnitPrice
|
||||||
} else {
|
} else {
|
||||||
// Produk lain: total_weight × unit_price
|
|
||||||
totalPrice = totalWeight * rp.UnitPrice
|
totalPrice = totalWeight * rp.UnitPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,6 +287,11 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error {
|
|||||||
} else {
|
} else {
|
||||||
dtoResult.AvailableQuantity = population
|
dtoResult.AvailableQuantity = population
|
||||||
}
|
}
|
||||||
|
if chickinDate, err := u.ProjectflockService.GetProjectFlockKandangChickinDate(c, result.Id); err != nil {
|
||||||
|
return err
|
||||||
|
} else if chickinDate != nil {
|
||||||
|
dtoResult.ChickInDate = chickinDate
|
||||||
|
}
|
||||||
if warehouse, werr := u.ProjectflockService.GetWarehouseByKandangID(c, result.KandangId); werr != nil {
|
if warehouse, werr := u.ProjectflockService.GetWarehouseByKandangID(c, result.KandangId); werr != nil {
|
||||||
return werr
|
return werr
|
||||||
} else if warehouse != nil {
|
} else if warehouse != nil {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
||||||
@@ -38,6 +40,7 @@ type ProjectFlockKandangDTO struct {
|
|||||||
ProjectFlock *ProjectFlockWithPivotDTO `json:"project_flock,omitempty"`
|
ProjectFlock *ProjectFlockWithPivotDTO `json:"project_flock,omitempty"`
|
||||||
AvailableQuantity float64 `json:"available_quantity"`
|
AvailableQuantity float64 `json:"available_quantity"`
|
||||||
Population *float64 `json:"population,omitempty"`
|
Population *float64 `json:"population,omitempty"`
|
||||||
|
ChickInDate *time.Time `json:"chick_in_date,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDTO {
|
func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDTO {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
@@ -42,6 +43,7 @@ type ProjectflockService interface {
|
|||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, float64, error)
|
GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, float64, error)
|
||||||
GetProjectFlockKandangPopulation(ctx *fiber.Ctx, projectFlockKandangID uint) (float64, error)
|
GetProjectFlockKandangPopulation(ctx *fiber.Ctx, projectFlockKandangID uint) (float64, error)
|
||||||
|
GetProjectFlockKandangChickinDate(ctx *fiber.Ctx, projectFlockKandangID uint) (*time.Time, error)
|
||||||
GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
|
GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
|
||||||
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
|
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
|
||||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
|
||||||
@@ -459,6 +461,35 @@ func (s projectflockService) GetProjectFlockKandangPopulation(ctx *fiber.Ctx, pr
|
|||||||
return total, nil
|
return total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s projectflockService) GetProjectFlockKandangChickinDate(ctx *fiber.Ctx, projectFlockKandangID uint) (*time.Time, error) {
|
||||||
|
if s.PopulationRepo == nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Project flock population repository is not configured")
|
||||||
|
}
|
||||||
|
if projectFlockKandangID == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
populations, err := s.PopulationRepo.GetByProjectFlockKandangID(ctx.Context(), projectFlockKandangID)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch populations for project flock kandang %d: %+v", projectFlockKandangID, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandang chick in date")
|
||||||
|
}
|
||||||
|
|
||||||
|
var earliest *time.Time
|
||||||
|
for _, pop := range populations {
|
||||||
|
if pop.ProjectChickin == nil || pop.ProjectChickin.ChickInDate.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
chickinDate := pop.ProjectChickin.ChickInDate
|
||||||
|
if earliest == nil || chickinDate.Before(*earliest) {
|
||||||
|
copy := chickinDate
|
||||||
|
earliest = ©
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return earliest, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s projectflockService) GetProjectFlockKandangByParams(ctx *fiber.Ctx, idStr string, projectFlockIdStr string, kandangIdStr string) (*entity.ProjectFlockKandang, float64, error) {
|
func (s projectflockService) GetProjectFlockKandangByParams(ctx *fiber.Ctx, idStr string, projectFlockIdStr string, kandangIdStr string) (*entity.ProjectFlockKandang, float64, error) {
|
||||||
idStr = strings.TrimSpace(idStr)
|
idStr = strings.TrimSpace(idStr)
|
||||||
projectFlockIdStr = strings.TrimSpace(projectFlockIdStr)
|
projectFlockIdStr = strings.TrimSpace(projectFlockIdStr)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
||||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||||
@@ -82,7 +84,16 @@ func (u *RecordingController) GetNextDay(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
return fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDay, err := u.RecordingService.GetNextDay(c, uint(projectFlockID))
|
recordTime := time.Now().UTC()
|
||||||
|
if recordDate := strings.TrimSpace(c.Query("record_date")); recordDate != "" {
|
||||||
|
parsed, err := time.Parse("2006-01-02", recordDate)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "record_date must be in YYYY-MM-DD format")
|
||||||
|
}
|
||||||
|
recordTime = parsed.UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
nextDay, err := u.RecordingService.GetNextDay(c, uint(projectFlockID), recordTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
sProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/services"
|
sProductionStandard "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/services"
|
||||||
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||||
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
||||||
|
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
type RecordingService interface {
|
type RecordingService interface {
|
||||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Recording, int64, error)
|
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Recording, int64, error)
|
||||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.Recording, error)
|
GetOne(ctx *fiber.Ctx, id uint) (*entity.Recording, error)
|
||||||
GetNextDay(ctx *fiber.Ctx, projectFlockKandangId uint) (int, error)
|
GetNextDay(ctx *fiber.Ctx, projectFlockKandangId uint, recordTime time.Time) (int, error)
|
||||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Recording, error)
|
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Recording, error)
|
||||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Recording, error)
|
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Recording, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
@@ -160,12 +160,15 @@ func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, erro
|
|||||||
return recording, nil
|
return recording, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s recordingService) GetNextDay(c *fiber.Ctx, projectFlockKandangId uint) (int, error) {
|
func (s recordingService) GetNextDay(c *fiber.Ctx, projectFlockKandangId uint, recordTime time.Time) (int, error) {
|
||||||
if projectFlockKandangId == 0 {
|
if projectFlockKandangId == 0 {
|
||||||
return 0, fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
return 0, fiber.NewError(fiber.StatusBadRequest, "project_flock_kandang_id is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
day, err := s.computeRecordingDay(c.Context(), projectFlockKandangId, time.Now().UTC())
|
if recordTime.IsZero() {
|
||||||
|
recordTime = time.Now().UTC()
|
||||||
|
}
|
||||||
|
day, err := s.computeRecordingDay(c.Context(), projectFlockKandangId, recordTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to compute recording day for project_flock_kandang_id=%d: %+v", projectFlockKandangId, err)
|
s.Log.Errorf("Failed to compute recording day for project_flock_kandang_id=%d: %+v", projectFlockKandangId, err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ func (s *uniformityService) CreateOne(c *fiber.Ctx, req *validation.Create, file
|
|||||||
var latestWeek int
|
var latestWeek int
|
||||||
if err := s.Repository.DB().WithContext(c.Context()).
|
if err := s.Repository.DB().WithContext(c.Context()).
|
||||||
Model(&entity.ProjectFlockKandangUniformity{}).
|
Model(&entity.ProjectFlockKandangUniformity{}).
|
||||||
Where("project_flock_kandang_id = ? AND deleted_at IS NULL", req.ProjectFlockKandangId).
|
Where("project_flock_kandang_id = ?", req.ProjectFlockKandangId).
|
||||||
Select("COALESCE(MAX(week), 0)").
|
Select("COALESCE(MAX(week), 0)").
|
||||||
Scan(&latestWeek).Error; err != nil {
|
Scan(&latestWeek).Error; err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate uniformity week sequence")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to validate uniformity week sequence")
|
||||||
|
|||||||
@@ -190,16 +190,13 @@ func ToSummaryFromDTOItems(items []RepportMarketingItemDTO) *Summary {
|
|||||||
totalWeightKg += item.TotalWeightKg
|
totalWeightKg += item.TotalWeightKg
|
||||||
totalSalesAmount += int64(item.SalesAmount)
|
totalSalesAmount += int64(item.SalesAmount)
|
||||||
totalHppAmount += int64(item.HppAmount)
|
totalHppAmount += int64(item.HppAmount)
|
||||||
avgSalesPrice += item.SalesPricePerKg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalHppPricePerKg := float64(0)
|
totalHppPricePerKg := float64(0)
|
||||||
|
|
||||||
if totalWeightKg > 0 {
|
if totalWeightKg > 0 {
|
||||||
totalHppPricePerKg = float64(totalHppAmount) / totalWeightKg
|
totalHppPricePerKg = float64(totalHppAmount) / totalWeightKg
|
||||||
}
|
avgSalesPrice = float64(totalSalesAmount) / totalWeightKg
|
||||||
|
|
||||||
if len(items) > 0 {
|
|
||||||
avgSalesPrice = avgSalesPrice / float64(len(items))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalQty > 0 {
|
if totalQty > 0 {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
chickinRepository := chickinRepo.NewChickinRepository(db)
|
chickinRepository := chickinRepo.NewChickinRepository(db)
|
||||||
recordingRepository := recordingRepo.NewRecordingRepository(db)
|
recordingRepository := recordingRepo.NewRecordingRepository(db)
|
||||||
approvalRepository := commonRepo.NewApprovalRepository(db)
|
approvalRepository := commonRepo.NewApprovalRepository(db)
|
||||||
|
hppCostRepository := commonRepo.NewHppCostRepository(db)
|
||||||
purchaseSupplierRepository := repportRepo.NewPurchaseSupplierRepository(db)
|
purchaseSupplierRepository := repportRepo.NewPurchaseSupplierRepository(db)
|
||||||
debtSupplierRepository := repportRepo.NewDebtSupplierRepository(db)
|
debtSupplierRepository := repportRepo.NewDebtSupplierRepository(db)
|
||||||
hppPerKandangRepository := repportRepo.NewHppPerKandangRepository(db)
|
hppPerKandangRepository := repportRepo.NewHppPerKandangRepository(db)
|
||||||
@@ -43,7 +44,8 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
userRepository := rUser.NewUserRepository(db)
|
userRepository := rUser.NewUserRepository(db)
|
||||||
|
|
||||||
approvalSvc := approvalService.NewApprovalService(approvalRepository)
|
approvalSvc := approvalService.NewApprovalService(approvalRepository)
|
||||||
repportService := sRepport.NewRepportService(db, validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, chickinRepository, recordingRepository, approvalSvc, purchaseSupplierRepository, debtSupplierRepository, hppPerKandangRepository, productionResultRepository, customerPaymentRepository, customerRepository, standardGrowthDetailRepository, productionStandardDetailRepository)
|
hppSvc := approvalService.NewHppService(hppCostRepository)
|
||||||
|
repportService := sRepport.NewRepportService(db, validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, chickinRepository, recordingRepository, approvalSvc, hppSvc, purchaseSupplierRepository, debtSupplierRepository, hppPerKandangRepository, productionResultRepository, customerPaymentRepository, customerRepository, standardGrowthDetailRepository, productionStandardDetailRepository)
|
||||||
userService := sUser.NewUserService(userRepository, validate)
|
userService := sUser.NewUserService(userRepository, validate)
|
||||||
|
|
||||||
RepportRoutes(router, userService, repportService)
|
RepportRoutes(router, userService, repportService)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ type HppPerKandangRow struct {
|
|||||||
// RemainingChickenBirds float64
|
// RemainingChickenBirds float64
|
||||||
// RemainingChickenWeight float64
|
// RemainingChickenWeight float64
|
||||||
EggProductionWeightKgRemaining float64
|
EggProductionWeightKgRemaining float64
|
||||||
EggProductionPiecesRemaining float64
|
// EggProductionPiecesRemaining float64
|
||||||
EggProductionTotalWeightKg float64
|
// EggProductionTotalWeightKg float64
|
||||||
EggProductionTotalPieces float64
|
// EggProductionTotalPieces float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type HppPerKandangCostRow struct {
|
type HppPerKandangCostRow struct {
|
||||||
@@ -50,7 +49,7 @@ type HppPerKandangSupplierRow struct {
|
|||||||
type HppPerKandangRepository interface {
|
type HppPerKandangRepository interface {
|
||||||
GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error)
|
GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error)
|
||||||
GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error)
|
GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error)
|
||||||
GetEggProductionByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error)
|
GetWeightRemainingByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type hppPerKandangRepository struct {
|
type hppPerKandangRepository struct {
|
||||||
@@ -133,60 +132,6 @@ func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, en
|
|||||||
func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error) {
|
func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error) {
|
||||||
var rows []HppPerKandangCostRow
|
var rows []HppPerKandangCostRow
|
||||||
|
|
||||||
purchaseStockableKey := fifo.StockableKeyPurchaseItems.String()
|
|
||||||
transferStockableKey := fifo.StockableKeyStockTransferIn.String()
|
|
||||||
|
|
||||||
latestApproval := r.db.WithContext(ctx).
|
|
||||||
Table("approvals AS a").
|
|
||||||
Select("a.approvable_id, a.action").
|
|
||||||
Joins(`
|
|
||||||
JOIN (
|
|
||||||
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
|
||||||
FROM approvals
|
|
||||||
WHERE approvable_type = ?
|
|
||||||
GROUP BY approvable_id
|
|
||||||
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
|
||||||
string(utils.ApprovalWorkflowRecording),
|
|
||||||
)
|
|
||||||
|
|
||||||
query := r.db.WithContext(ctx).
|
|
||||||
Table("recordings AS r").
|
|
||||||
Select(`
|
|
||||||
pfk.id AS project_flock_kandang_id,
|
|
||||||
COALESCE(SUM(CASE
|
|
||||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
|
||||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0)
|
|
||||||
ELSE 0
|
|
||||||
END), 0) AS feed_cost,
|
|
||||||
COALESCE(SUM(CASE
|
|
||||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
|
||||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0)
|
|
||||||
ELSE 0
|
|
||||||
END), 0) AS ovk_cost`,
|
|
||||||
utils.FlagPakan, transferStockableKey, utils.FlagPakan,
|
|
||||||
utils.FlagOVK, transferStockableKey, utils.FlagOVK).
|
|
||||||
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("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
|
||||||
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
|
||||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
|
||||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
|
||||||
Joins("LEFT JOIN stock_transfer_details AS std ON std.id = sa.stockable_id AND sa.stockable_type = ?", transferStockableKey).
|
|
||||||
Joins("LEFT JOIN stock_transfers AS st ON st.id = std.stock_transfer_id").
|
|
||||||
Joins("LEFT JOIN purchase_items AS tpi ON tpi.product_id = std.product_id AND tpi.warehouse_id = st.from_warehouse_id").
|
|
||||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
|
||||||
Joins("LEFT JOIN flags AS tf ON tf.flagable_id = std.product_id AND tf.flagable_type = ?", entity.FlagableTypeProduct).
|
|
||||||
Where("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
|
||||||
Where("r.record_datetime < ?", end).
|
|
||||||
Where("r.deleted_at IS NULL").
|
|
||||||
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected))
|
|
||||||
|
|
||||||
query = query.Group("pfk.id").Order("pfk.id ASC")
|
|
||||||
|
|
||||||
if err := query.Scan(&rows).Error; err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
docRows := make([]struct {
|
docRows := make([]struct {
|
||||||
ProjectFlockKandangID uint
|
ProjectFlockKandangID uint
|
||||||
DocCost float64
|
DocCost float64
|
||||||
@@ -262,130 +207,10 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
budgetRows := make([]struct {
|
return rows, docSuppliers, nil
|
||||||
ProjectFlockKandangID uint
|
|
||||||
BudgetCost float64
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
pfkUsageSub := r.db.
|
|
||||||
Table("project_chickins AS pc").
|
|
||||||
Select(`
|
|
||||||
pc.project_flock_kandang_id,
|
|
||||||
SUM(pc.usage_qty) AS kandang_usage_qty`).
|
|
||||||
Group("pc.project_flock_kandang_id")
|
|
||||||
|
|
||||||
projectUsageSub := r.db.
|
|
||||||
Table("project_chickins AS pc").
|
|
||||||
Select(`
|
|
||||||
pfk.project_flock_id,
|
|
||||||
SUM(pc.usage_qty) AS project_usage_qty`).
|
|
||||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = pc.project_flock_kandang_id").
|
|
||||||
Group("pfk.project_flock_id")
|
|
||||||
|
|
||||||
budgetQuery := r.db.WithContext(ctx).
|
|
||||||
Table("project_flock_kandangs AS pfk").
|
|
||||||
Select(`
|
|
||||||
pfk.id AS project_flock_kandang_id,
|
|
||||||
COALESCE(SUM((pb.qty * pb.price) * COALESCE(k_usage.kandang_usage_qty, 0) / NULLIF(p_usage.project_usage_qty, 0)), 0) AS budget_cost`).
|
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
||||||
Joins("JOIN project_budgets AS pb ON pb.project_flock_id = pfk.project_flock_id").
|
|
||||||
Joins("LEFT JOIN (?) AS k_usage ON k_usage.project_flock_kandang_id = pfk.id", pfkUsageSub).
|
|
||||||
Joins("LEFT JOIN (?) AS p_usage ON p_usage.project_flock_id = pfk.project_flock_id", projectUsageSub).
|
|
||||||
Where("pfk.id IN (?)", projectFlockKandangIDs).
|
|
||||||
Group("pfk.id")
|
|
||||||
// budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
if err := budgetQuery.Scan(&budgetRows).Error; err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, budget := range budgetRows {
|
|
||||||
entry, ok := costMap[budget.ProjectFlockKandangID]
|
|
||||||
if !ok {
|
|
||||||
rows = append(rows, HppPerKandangCostRow{
|
|
||||||
ProjectFlockKandangID: budget.ProjectFlockKandangID,
|
|
||||||
})
|
|
||||||
entry = &rows[len(rows)-1]
|
|
||||||
costMap[budget.ProjectFlockKandangID] = entry
|
|
||||||
}
|
|
||||||
entry.BudgetCost += budget.BudgetCost
|
|
||||||
}
|
|
||||||
|
|
||||||
expenseRows := make([]struct {
|
|
||||||
ProjectFlockKandangID uint
|
|
||||||
ExpenseCost float64
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
expenseQuery := r.db.WithContext(ctx).
|
|
||||||
Table("project_flock_kandangs AS pfk").
|
|
||||||
Select(`
|
|
||||||
pfk.id AS project_flock_kandang_id,
|
|
||||||
COALESCE(SUM(er.qty * er.price), 0) AS expense_cost`).
|
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
||||||
Joins("JOIN expense_nonstocks AS en ON en.project_flock_kandang_id = pfk.id").
|
|
||||||
Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id").
|
|
||||||
Where("pfk.id IN (?)", projectFlockKandangIDs).
|
|
||||||
Group("pfk.id")
|
|
||||||
// expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
if err := expenseQuery.Scan(&expenseRows).Error; err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, exp := range expenseRows {
|
|
||||||
entry, ok := costMap[exp.ProjectFlockKandangID]
|
|
||||||
if !ok {
|
|
||||||
rows = append(rows, HppPerKandangCostRow{
|
|
||||||
ProjectFlockKandangID: exp.ProjectFlockKandangID,
|
|
||||||
})
|
|
||||||
entry = &rows[len(rows)-1]
|
|
||||||
costMap[exp.ProjectFlockKandangID] = entry
|
|
||||||
}
|
|
||||||
entry.ExpenseCost += exp.ExpenseCost
|
|
||||||
}
|
|
||||||
|
|
||||||
feedSuppliers := make([]HppPerKandangSupplierRow, 0)
|
|
||||||
|
|
||||||
feedQuery := r.db.WithContext(ctx).
|
|
||||||
Table("recordings AS r").
|
|
||||||
Select("DISTINCT pfk.id AS project_flock_kandang_id, s.id AS supplier_id, s.name AS supplier_name, s.alias AS supplier_alias").
|
|
||||||
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 locations AS loc ON loc.id = k.location_id").
|
|
||||||
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
|
||||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
|
||||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
|
||||||
Joins("LEFT JOIN purchases AS pur ON pur.id = pi.purchase_id").
|
|
||||||
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
|
|
||||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
|
||||||
Where("f.name IN ?", []utils.FlagType{utils.FlagPakan, utils.FlagOVK}).
|
|
||||||
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
|
||||||
Where("r.record_datetime < ?", end).
|
|
||||||
Where("r.deleted_at IS NULL")
|
|
||||||
// feedQuery = applyLocationFilters(feedQuery, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
if err := feedQuery.Scan(&feedSuppliers).Error; err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range feedSuppliers {
|
|
||||||
if _, exists := costMap[feedSuppliers[i].ProjectFlockKandangID]; !exists {
|
|
||||||
rows = append(rows, HppPerKandangCostRow{
|
|
||||||
ProjectFlockKandangID: feedSuppliers[i].ProjectFlockKandangID,
|
|
||||||
})
|
|
||||||
costMap[feedSuppliers[i].ProjectFlockKandangID] = &rows[len(rows)-1]
|
|
||||||
}
|
|
||||||
feedSuppliers[i].Category = "FEED"
|
|
||||||
}
|
|
||||||
|
|
||||||
supplierRows := append(docSuppliers, feedSuppliers...)
|
|
||||||
|
|
||||||
return rows, supplierRows, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *hppPerKandangRepository) GetEggProductionByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error) {
|
func (r *hppPerKandangRepository) GetWeightRemainingByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error) {
|
||||||
if len(projectFlockKandangIDs) == 0 {
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
return map[uint]HppPerKandangRow{}, nil
|
return map[uint]HppPerKandangRow{}, nil
|
||||||
}
|
}
|
||||||
@@ -406,9 +231,9 @@ func (r *hppPerKandangRepository) GetEggProductionByProjectFlockKandangIDs(ctx c
|
|||||||
type eggRow struct {
|
type eggRow struct {
|
||||||
ProjectFlockKandangID uint
|
ProjectFlockKandangID uint
|
||||||
EggProductionWeightKgRemaining float64
|
EggProductionWeightKgRemaining float64
|
||||||
EggProductionPiecesRemaining float64
|
// EggProductionPiecesRemaining float64
|
||||||
EggProductionTotalWeightKg float64
|
// EggProductionTotalWeightKg float64
|
||||||
EggProductionTotalPieces float64
|
// EggProductionTotalPieces float64
|
||||||
}
|
}
|
||||||
|
|
||||||
eggRows := make([]eggRow, 0)
|
eggRows := make([]eggRow, 0)
|
||||||
@@ -416,10 +241,7 @@ func (r *hppPerKandangRepository) GetEggProductionByProjectFlockKandangIDs(ctx c
|
|||||||
Table("recordings AS r").
|
Table("recordings AS r").
|
||||||
Select(`
|
Select(`
|
||||||
r.project_flock_kandangs_id AS project_flock_kandang_id,
|
r.project_flock_kandangs_id AS project_flock_kandang_id,
|
||||||
COALESCE(SUM((re.total_qty - re.total_used) * re.weight / 1000), 0) AS egg_production_weight_kg_remaining,
|
COALESCE((SUM(re.weight) / NULLIF(SUM(re.total_qty), 0)) * SUM(re.total_qty - re.total_used), 0) AS egg_production_weight_kg_remaining`).
|
||||||
COALESCE(SUM(re.total_qty - re.total_used), 0) AS egg_production_pieces_remaining,
|
|
||||||
COALESCE(SUM(re.weight / 1000), 0) AS egg_production_total_weight_kg,
|
|
||||||
COALESCE(SUM(re.total_qty), 0) AS egg_production_total_pieces`).
|
|
||||||
Joins("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
Joins("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
||||||
Joins("LEFT JOIN recording_eggs AS re ON re.recording_id = r.id").
|
Joins("LEFT JOIN recording_eggs AS re ON re.recording_id = r.id").
|
||||||
Where("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
Where("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
||||||
@@ -437,9 +259,9 @@ func (r *hppPerKandangRepository) GetEggProductionByProjectFlockKandangIDs(ctx c
|
|||||||
result[row.ProjectFlockKandangID] = HppPerKandangRow{
|
result[row.ProjectFlockKandangID] = HppPerKandangRow{
|
||||||
ProjectFlockKandangID: row.ProjectFlockKandangID,
|
ProjectFlockKandangID: row.ProjectFlockKandangID,
|
||||||
EggProductionWeightKgRemaining: row.EggProductionWeightKgRemaining,
|
EggProductionWeightKgRemaining: row.EggProductionWeightKgRemaining,
|
||||||
EggProductionPiecesRemaining: row.EggProductionPiecesRemaining,
|
// EggProductionPiecesRemaining: row.EggProductionPiecesRemaining,
|
||||||
EggProductionTotalWeightKg: row.EggProductionTotalWeightKg,
|
// EggProductionTotalWeightKg: row.EggProductionTotalWeightKg,
|
||||||
EggProductionTotalPieces: row.EggProductionTotalPieces,
|
// EggProductionTotalPieces: row.EggProductionTotalPieces,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ type repportService struct {
|
|||||||
ChickinRepo chickinRepo.ProjectChickinRepository
|
ChickinRepo chickinRepo.ProjectChickinRepository
|
||||||
RecordingRepo recordingRepo.RecordingRepository
|
RecordingRepo recordingRepo.RecordingRepository
|
||||||
ApprovalSvc approvalService.ApprovalService
|
ApprovalSvc approvalService.ApprovalService
|
||||||
|
HppSvc approvalService.HppService
|
||||||
PurchaseSupplierRepo repportRepo.PurchaseSupplierRepository
|
PurchaseSupplierRepo repportRepo.PurchaseSupplierRepository
|
||||||
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
||||||
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
||||||
@@ -85,6 +86,7 @@ func NewRepportService(
|
|||||||
chickinRepo chickinRepo.ProjectChickinRepository,
|
chickinRepo chickinRepo.ProjectChickinRepository,
|
||||||
recordingRepo recordingRepo.RecordingRepository,
|
recordingRepo recordingRepo.RecordingRepository,
|
||||||
approvalSvc approvalService.ApprovalService,
|
approvalSvc approvalService.ApprovalService,
|
||||||
|
hppSvc approvalService.HppService,
|
||||||
purchaseSupplierRepo repportRepo.PurchaseSupplierRepository,
|
purchaseSupplierRepo repportRepo.PurchaseSupplierRepository,
|
||||||
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
||||||
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
||||||
@@ -104,6 +106,7 @@ func NewRepportService(
|
|||||||
ChickinRepo: chickinRepo,
|
ChickinRepo: chickinRepo,
|
||||||
RecordingRepo: recordingRepo,
|
RecordingRepo: recordingRepo,
|
||||||
ApprovalSvc: approvalSvc,
|
ApprovalSvc: approvalSvc,
|
||||||
|
HppSvc: hppSvc,
|
||||||
PurchaseSupplierRepo: purchaseSupplierRepo,
|
PurchaseSupplierRepo: purchaseSupplierRepo,
|
||||||
DebtSupplierRepo: debtSupplierRepo,
|
DebtSupplierRepo: debtSupplierRepo,
|
||||||
HppPerKandangRepo: hppPerKandangRepo,
|
HppPerKandangRepo: hppPerKandangRepo,
|
||||||
@@ -215,8 +218,23 @@ func (s *repportService) GetMarketing(c *fiber.Ctx, params *validation.Marketing
|
|||||||
projectFlockIDMap[projectFlockID] = true
|
projectFlockIDMap[projectFlockID] = true
|
||||||
|
|
||||||
category := projectFlockKandang.ProjectFlock.Category
|
category := projectFlockKandang.ProjectFlock.Category
|
||||||
hppPerKg := s.calculateHppPricePerKg(c.Context(), projectFlockID, category)
|
if utils.ProjectFlockCategory(category) == utils.ProjectFlockCategoryLaying {
|
||||||
hppMap[projectFlockID] = hppPerKg
|
if s.HppSvc != nil {
|
||||||
|
hppCost, err := s.HppSvc.CalculateHppCost(projectFlockID, nil)
|
||||||
|
if err != nil {
|
||||||
|
hppMap[projectFlockID] = 0.0
|
||||||
|
} else if hppCost != nil {
|
||||||
|
hppMap[projectFlockID] = hppCost.Real.HargaKg
|
||||||
|
} else {
|
||||||
|
hppMap[projectFlockID] = 0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hppMap[projectFlockID] = 0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
hppMap[projectFlockID] = 0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,81 +243,6 @@ func (s *repportService) GetMarketing(c *fiber.Ctx, params *validation.Marketing
|
|||||||
return items, total, nil
|
return items, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *repportService) calculateHppPricePerKg(ctx context.Context, projectFlockID uint, category string) float64 {
|
|
||||||
totalCost := s.getTotalProjectCost(ctx, projectFlockID)
|
|
||||||
if totalCost == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
chickinQty, err := s.ChickinRepo.GetTotalChickinQtyByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Warnf("HPP calculation: Failed to get chickin qty for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
depletion, err := s.RecordingRepo.GetTotalDepletionByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Warnf("HPP calculation: Failed to get depletion for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
avgWeight, err := s.RecordingRepo.GetLatestAvgWeightByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Warnf("HPP calculation: Failed to get avg weight for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalWeight float64
|
|
||||||
if utils.ProjectFlockCategory(category) == utils.ProjectFlockCategoryGrowing {
|
|
||||||
totalWeight = (chickinQty - depletion) * avgWeight
|
|
||||||
} else {
|
|
||||||
eggWeight, err := s.RecordingRepo.GetTotalEggProductionWeightByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Warnf("HPP calculation: Failed to get egg weight for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
}
|
|
||||||
totalWeight = (chickinQty-depletion)*avgWeight + eggWeight
|
|
||||||
}
|
|
||||||
|
|
||||||
if totalWeight == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
hppPricePerKg := totalCost / totalWeight
|
|
||||||
return hppPricePerKg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *repportService) getTotalProjectCost(ctx context.Context, projectFlockID uint) float64 {
|
|
||||||
if projectFlockID == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
purchases, err := s.PurchaseRepo.GetItemsByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("getTotalProjectCost: GetItemsByProjectFlockID error for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
cost := float64(0)
|
|
||||||
purchaseCost := float64(0)
|
|
||||||
for _, p := range purchases {
|
|
||||||
purchaseCost += p.TotalPrice
|
|
||||||
}
|
|
||||||
cost += purchaseCost
|
|
||||||
|
|
||||||
realizations, err := s.ExpenseRealizationRepo.GetByProjectFlockID(ctx, projectFlockID)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Warnf("getTotalProjectCost: GetByProjectFlockID error for project flock ID %d: %v", projectFlockID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bopCost := float64(0)
|
|
||||||
for _, r := range realizations {
|
|
||||||
if r.ExpenseNonstock != nil && r.ExpenseNonstock.Expense != nil &&
|
|
||||||
r.ExpenseNonstock.Expense.Category == string(utils.ExpenseCategoryBOP) {
|
|
||||||
bopCost += r.Price * r.Qty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cost += bopCost
|
|
||||||
|
|
||||||
return cost
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error) {
|
func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error) {
|
||||||
if err := s.Validate.Struct(params); err != nil {
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -1573,16 +1516,16 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
eggMap, err := s.HppPerKandangRepo.GetEggProductionByProjectFlockKandangIDs(ctx.Context(), startOfDay, endOfDay, validPfkIDs)
|
eggMap, err := s.HppPerKandangRepo.GetWeightRemainingByProjectFlockKandangIDs(ctx.Context(), startOfDay, endOfDay, validPfkIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for pfkID, egg := range eggMap {
|
for pfkID, egg := range eggMap {
|
||||||
if rowIdx, ok := pfkIndex[pfkID]; ok {
|
if rowIdx, ok := pfkIndex[pfkID]; ok {
|
||||||
repoRows[rowIdx].EggProductionWeightKgRemaining = egg.EggProductionWeightKgRemaining
|
repoRows[rowIdx].EggProductionWeightKgRemaining = egg.EggProductionWeightKgRemaining
|
||||||
repoRows[rowIdx].EggProductionPiecesRemaining = egg.EggProductionPiecesRemaining
|
// repoRows[rowIdx].EggProductionPiecesRemaining = egg.EggProductionPiecesRemaining
|
||||||
repoRows[rowIdx].EggProductionTotalWeightKg = egg.EggProductionTotalWeightKg
|
// repoRows[rowIdx].EggProductionTotalWeightKg = egg.EggProductionTotalWeightKg
|
||||||
repoRows[rowIdx].EggProductionTotalPieces = egg.EggProductionTotalPieces
|
// repoRows[rowIdx].EggProductionTotalPieces = egg.EggProductionTotalPieces
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1590,12 +1533,12 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
costMap := make(map[uint]HppCostAggregate, len(costRows))
|
costMap := make(map[uint]HppCostAggregate, len(costRows))
|
||||||
for _, row := range costRows {
|
for _, row := range costRows {
|
||||||
costMap[row.ProjectFlockKandangID] = HppCostAggregate{
|
costMap[row.ProjectFlockKandangID] = HppCostAggregate{
|
||||||
FeedCost: row.FeedCost,
|
// FeedCost: row.FeedCost,
|
||||||
OvkCost: row.OvkCost,
|
// OvkCost: row.OvkCost,
|
||||||
DocCost: row.DocCost,
|
DocCost: row.DocCost,
|
||||||
DocQty: row.DocQty,
|
DocQty: row.DocQty,
|
||||||
BudgetCost: row.BudgetCost,
|
// BudgetCost: row.BudgetCost,
|
||||||
ExpenseCost: row.ExpenseCost,
|
// ExpenseCost: row.ExpenseCost,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1669,19 +1612,33 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
eggPiecesFloatRemaining := row.EggProductionPiecesRemaining
|
var eggPiecesFloatRemaining float64
|
||||||
|
eggRemainingWeightFloatRemaining := row.EggProductionWeightKgRemaining
|
||||||
|
var eggTotalPiecesFloat float64
|
||||||
|
var eggWeightFloat float64
|
||||||
|
eggHpp := 0.0
|
||||||
|
if s.HppSvc != nil {
|
||||||
|
hppCost, err := s.HppSvc.CalculateHppCost(row.ProjectFlockKandangID, &endOfDay)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if hppCost != nil {
|
||||||
|
// eggRemainingWeightFloatRemaining = hppCost.Estimation.Kg - hppCost.Real.Kg
|
||||||
|
eggPiecesFloatRemaining = hppCost.Estimation.Butir - hppCost.Real.Butir
|
||||||
|
eggHpp = hppCost.Estimation.HargaKg
|
||||||
|
eggTotalPiecesFloat = hppCost.Estimation.Butir
|
||||||
|
eggWeightFloat = hppCost.Estimation.Kg
|
||||||
|
}
|
||||||
|
}
|
||||||
if math.IsNaN(eggPiecesFloatRemaining) || math.IsInf(eggPiecesFloatRemaining, 0) {
|
if math.IsNaN(eggPiecesFloatRemaining) || math.IsInf(eggPiecesFloatRemaining, 0) {
|
||||||
eggPiecesFloatRemaining = 0
|
eggPiecesFloatRemaining = 0
|
||||||
}
|
}
|
||||||
eggTotalPiecesFloat := row.EggProductionTotalPieces
|
|
||||||
if math.IsNaN(eggTotalPiecesFloat) || math.IsInf(eggTotalPiecesFloat, 0) {
|
if math.IsNaN(eggTotalPiecesFloat) || math.IsInf(eggTotalPiecesFloat, 0) {
|
||||||
eggTotalPiecesFloat = 0
|
eggTotalPiecesFloat = 0
|
||||||
}
|
}
|
||||||
eggRemainingWeightFloatRemaining := row.EggProductionWeightKgRemaining
|
|
||||||
if math.IsNaN(eggRemainingWeightFloatRemaining) || math.IsInf(eggRemainingWeightFloatRemaining, 0) {
|
if math.IsNaN(eggRemainingWeightFloatRemaining) || math.IsInf(eggRemainingWeightFloatRemaining, 0) {
|
||||||
eggRemainingWeightFloatRemaining = 0
|
eggRemainingWeightFloatRemaining = 0
|
||||||
}
|
}
|
||||||
eggWeightFloat := row.EggProductionTotalWeightKg
|
|
||||||
if math.IsNaN(eggWeightFloat) || math.IsInf(eggWeightFloat, 0) {
|
if math.IsNaN(eggWeightFloat) || math.IsInf(eggWeightFloat, 0) {
|
||||||
eggWeightFloat = 0
|
eggWeightFloat = 0
|
||||||
}
|
}
|
||||||
@@ -1705,11 +1662,6 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
rangeKey := weightRangeKey{Min: weightMin, Max: weightMax}
|
rangeKey := weightRangeKey{Min: weightMin, Max: weightMax}
|
||||||
|
|
||||||
costEntry := costMap[row.ProjectFlockKandangID]
|
costEntry := costMap[row.ProjectFlockKandangID]
|
||||||
totalCost := costEntry.FeedCost + costEntry.OvkCost + costEntry.DocCost + costEntry.BudgetCost + costEntry.ExpenseCost
|
|
||||||
eggHpp := 0.0
|
|
||||||
if eggWeightFloat > 0 {
|
|
||||||
eggHpp = (totalCost / eggWeightFloat) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
rowEggPieces := int64(math.Round(eggPiecesFloatRemaining))
|
rowEggPieces := int64(math.Round(eggPiecesFloatRemaining))
|
||||||
rowEggValue := int64(eggHpp * eggRemainingWeightFloatRemaining)
|
rowEggValue := int64(eggHpp * eggRemainingWeightFloatRemaining)
|
||||||
|
|||||||
Reference in New Issue
Block a user