mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
507 lines
19 KiB
Go
507 lines
19 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sirupsen/logrus"
|
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
|
|
expenseRealizationRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
|
marketingDeliveryProductRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
|
chickinRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
|
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
|
recordingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ClosingKeuanganService handles closing keuangan business logic
|
|
type ClosingKeuanganService interface {
|
|
GetClosingKeuangan(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingKeuanganData, error)
|
|
GetClosingKeuanganByKandang(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID uint) (*dto.ClosingKeuanganData, error)
|
|
}
|
|
|
|
// CostData holds all cost-related information
|
|
type CostData struct {
|
|
FeedCost float64
|
|
OvkCost float64
|
|
ChickenCost float64
|
|
ExpeditionCost float64
|
|
BudgetOperational float64
|
|
RealizationOperational float64
|
|
}
|
|
|
|
// ProductionData holds all production and sales related information
|
|
type ProductionData struct {
|
|
TotalPopulationIn float64
|
|
TotalDepletion float64
|
|
TotalWeightProduced float64
|
|
TotalEggWeightKg float64
|
|
TotalWeightSold float64
|
|
TotalSalesAmount float64
|
|
}
|
|
|
|
type closingKeuanganService struct {
|
|
Log *logrus.Logger
|
|
ProjectFlockRepo projectflockRepository.ProjectflockRepository
|
|
ProjectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository
|
|
MarketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository
|
|
ExpenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository
|
|
ProjectBudgetRepo projectflockRepository.ProjectBudgetRepository
|
|
ChickinRepo chickinRepository.ProjectChickinRepository
|
|
RecordingRepo recordingRepository.RecordingRepository
|
|
HppSvc commonSvc.HppService
|
|
HppRepo commonRepo.HppCostRepository
|
|
}
|
|
|
|
func NewClosingKeuanganService(
|
|
projectFlockRepo projectflockRepository.ProjectflockRepository,
|
|
projectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository,
|
|
marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository,
|
|
expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository,
|
|
projectBudgetRepo projectflockRepository.ProjectBudgetRepository,
|
|
chickinRepo chickinRepository.ProjectChickinRepository,
|
|
recordingRepo recordingRepository.RecordingRepository,
|
|
hppSvc commonSvc.HppService,
|
|
hppRepo commonRepo.HppCostRepository,
|
|
) ClosingKeuanganService {
|
|
return &closingKeuanganService{
|
|
Log: utils.Log,
|
|
ProjectFlockRepo: projectFlockRepo,
|
|
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
|
MarketingDeliveryProductRepo: marketingDeliveryProductRepo,
|
|
ExpenseRealizationRepo: expenseRealizationRepo,
|
|
ProjectBudgetRepo: projectBudgetRepo,
|
|
ChickinRepo: chickinRepo,
|
|
RecordingRepo: recordingRepo,
|
|
HppSvc: hppSvc,
|
|
HppRepo: hppRepo,
|
|
}
|
|
}
|
|
|
|
func (s closingKeuanganService) GetClosingKeuangan(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingKeuanganData, error) {
|
|
|
|
if err := commonSvc.EnsureRelations(c.Context(),
|
|
commonSvc.RelationCheck{Name: "Project Flock", ID: &projectFlockID, Exists: s.ProjectFlockRepo.IdExists},
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
projectFlock, err := s.ProjectFlockRepo.GetByID(c.Context(), projectFlockID, nil)
|
|
if err != nil {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
|
}
|
|
|
|
projectFlockKandangs, err := s.ProjectFlockKandangRepo.GetByProjectFlockID(c.Context(), projectFlockID)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch kandangs")
|
|
}
|
|
|
|
return s.calculateClosingKeuangan(c, projectFlock, projectFlockKandangs)
|
|
}
|
|
|
|
func (s closingKeuanganService) GetClosingKeuanganByKandang(c *fiber.Ctx, projectFlockID uint, projectFlockKandangID uint) (*dto.ClosingKeuanganData, error) {
|
|
|
|
if err := commonSvc.EnsureRelations(c.Context(),
|
|
commonSvc.RelationCheck{Name: "Project Flock", ID: &projectFlockID, Exists: s.ProjectFlockRepo.IdExists},
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
projectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), projectFlockKandangID)
|
|
if err != nil {
|
|
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock kandang not found")
|
|
}
|
|
if projectFlockKandang.ProjectFlockId != projectFlockID {
|
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Project flock kandang does not belong to this project flock")
|
|
}
|
|
|
|
projectFlock, err := s.ProjectFlockRepo.GetByID(c.Context(), projectFlockID, nil)
|
|
if err != nil {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
|
}
|
|
|
|
projectFlockKandangs := []entity.ProjectFlockKandang{*projectFlockKandang}
|
|
|
|
return s.calculateClosingKeuangan(c, projectFlock, projectFlockKandangs)
|
|
}
|
|
|
|
func (s closingKeuanganService) calculateClosingKeuangan(c *fiber.Ctx, projectFlock *entity.ProjectFlock, projectFlockKandangs []entity.ProjectFlockKandang) (*dto.ClosingKeuanganData, error) {
|
|
|
|
var projectFlockKandangIDs []uint
|
|
for _, projectFlockKandang := range projectFlockKandangs {
|
|
projectFlockKandangIDs = append(projectFlockKandangIDs, projectFlockKandang.Id)
|
|
}
|
|
|
|
isPerKandang := len(projectFlockKandangs) == 1
|
|
var projectFlockKandangID *uint
|
|
if isPerKandang {
|
|
kandangID := projectFlockKandangs[0].Id
|
|
projectFlockKandangID = &kandangID
|
|
}
|
|
|
|
costs, err := s.calculateCosts(c, projectFlock, projectFlockKandangs, projectFlockKandangIDs, projectFlockKandangID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
productionData, err := s.calculateProductionData(c, projectFlock, projectFlockKandangIDs, projectFlockKandangID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hppSection := s.buildHPPSection(c, projectFlock, projectFlockKandangs, costs, productionData)
|
|
|
|
profitLossSection := s.buildProfitLossSection(projectFlock, costs, productionData)
|
|
|
|
data := dto.ToClosingKeuanganData(hppSection, profitLossSection)
|
|
return &data, nil
|
|
}
|
|
|
|
func (s closingKeuanganService) calculateCosts(c *fiber.Ctx, projectFlock *entity.ProjectFlock, projectFlockKandangs []entity.ProjectFlockKandang, projectFlockKandangIDs []uint, projectFlockKandangID *uint) (*CostData, error) {
|
|
costs := &CostData{}
|
|
var err error
|
|
|
|
costs.FeedCost, err = s.HppRepo.GetFeedUsageCost(c.Context(), projectFlockKandangIDs, nil)
|
|
if err != nil {
|
|
costs.FeedCost = 0
|
|
}
|
|
|
|
costs.OvkCost, err = s.HppRepo.GetOvkUsageCost(c.Context(), projectFlockKandangIDs, nil)
|
|
if err != nil {
|
|
costs.OvkCost = 0
|
|
}
|
|
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
for _, projectFlockKandang := range projectFlockKandangs {
|
|
depresiasiCost, err := s.HppSvc.GetDepresiasiTransfer(projectFlockKandang.Id, nil)
|
|
if err == nil {
|
|
costs.ChickenCost += depresiasiCost
|
|
}
|
|
pulletCost, err := s.HppRepo.GetPulletCost(c.Context(), projectFlockKandang.Id)
|
|
if err == nil {
|
|
costs.ChickenCost += pulletCost
|
|
}
|
|
}
|
|
} else {
|
|
for _, projectFlockKandang := range projectFlockKandangs {
|
|
pulletCost, err := s.HppRepo.GetPulletCost(c.Context(), projectFlockKandang.Id)
|
|
if err == nil {
|
|
costs.ChickenCost += pulletCost
|
|
}
|
|
}
|
|
}
|
|
|
|
costs.ExpeditionCost, err = s.HppRepo.GetExpedisionCost(c.Context(), projectFlockKandangIDs)
|
|
if err != nil {
|
|
costs.ExpeditionCost = 0
|
|
}
|
|
|
|
if budgets, err := s.ProjectBudgetRepo.GetByProjectFlockID(c.Context(), projectFlock.Id); err == nil {
|
|
totalBudget := 0.0
|
|
for _, budget := range budgets {
|
|
totalBudget += budget.Price * budget.Qty
|
|
}
|
|
if projectFlockKandangID != nil {
|
|
allKandangs, errKandang := s.ProjectFlockKandangRepo.GetByProjectFlockID(c.Context(), projectFlock.Id)
|
|
if errKandang == nil && len(allKandangs) > 0 {
|
|
costs.BudgetOperational = totalBudget / float64(len(allKandangs))
|
|
}
|
|
} else {
|
|
costs.BudgetOperational = totalBudget
|
|
}
|
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
s.Log.Errorf("Failed to fetch budgets for project_flock_id=%d: %+v", projectFlock.Id, err)
|
|
}
|
|
|
|
if realizations, err := s.ExpenseRealizationRepo.GetClosingOverhead(c.Context(), projectFlock.Id, projectFlockKandangID); err == nil {
|
|
for _, realization := range realizations {
|
|
amount := realization.Price * realization.Qty
|
|
isEkspedisi := realization.ExpenseNonstock != nil &&
|
|
realization.ExpenseNonstock.Nonstock != nil &&
|
|
containsFlag(realization.ExpenseNonstock.Nonstock.Flags, "EKSPEDISI")
|
|
if !isEkspedisi {
|
|
costs.RealizationOperational += amount
|
|
}
|
|
}
|
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
s.Log.Errorf("Failed to fetch realizations for project_flock_id=%d: %+v", projectFlock.Id, err)
|
|
}
|
|
|
|
return costs, nil
|
|
}
|
|
|
|
func (s closingKeuanganService) calculateProductionData(c *fiber.Ctx, projectFlock *entity.ProjectFlock, projectFlockKandangIDs []uint, projectFlockKandangID *uint) (*ProductionData, error) {
|
|
data := &ProductionData{}
|
|
var err error
|
|
|
|
data.TotalPopulationIn, err = s.HppRepo.GetTotalPopulation(c.Context(), projectFlockKandangIDs)
|
|
if err != nil {
|
|
data.TotalPopulationIn = 0
|
|
}
|
|
|
|
if projectFlockKandangID != nil {
|
|
data.TotalDepletion, err = s.RecordingRepo.GetTotalDepletionByProjectFlockKandangID(c.Context(), *projectFlockKandangID)
|
|
} else {
|
|
data.TotalDepletion, err = s.RecordingRepo.GetTotalDepletionByProjectFlockID(c.Context(), projectFlock.Id)
|
|
}
|
|
if err != nil {
|
|
data.TotalDepletion = 0
|
|
}
|
|
|
|
if projectFlockKandangID != nil {
|
|
data.TotalWeightProduced, err = s.RecordingRepo.GetTotalWeightProducedFromUniformityByProjectFlockKandangID(c.Context(), *projectFlockKandangID)
|
|
} else {
|
|
data.TotalWeightProduced, err = s.RecordingRepo.GetTotalWeightProducedFromUniformityByProjectFlockID(c.Context(), projectFlock.Id)
|
|
}
|
|
if err != nil {
|
|
data.TotalWeightProduced = 0
|
|
}
|
|
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
_, data.TotalEggWeightKg, err = s.HppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(c.Context(), projectFlockKandangIDs, nil)
|
|
if err != nil {
|
|
data.TotalEggWeightKg = 0
|
|
}
|
|
}
|
|
|
|
var deliveryProducts []entity.MarketingDeliveryProduct
|
|
if projectFlockKandangID != nil {
|
|
deliveryProducts, err = s.MarketingDeliveryProductRepo.GetClosingPenjualanByCategory(c.Context(), projectFlock.Id, projectFlockKandangID, projectFlock.Category)
|
|
} else {
|
|
deliveryProducts, err = s.MarketingDeliveryProductRepo.GetClosingPenjualanByCategory(c.Context(), projectFlock.Id, nil, projectFlock.Category)
|
|
}
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data penjualan")
|
|
}
|
|
|
|
for _, delivery := range deliveryProducts {
|
|
if delivery.MarketingProduct.ProductWarehouse.Product.Id == 0 {
|
|
continue
|
|
}
|
|
data.TotalWeightSold += delivery.TotalWeight
|
|
data.TotalSalesAmount += delivery.TotalPrice
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
func (s closingKeuanganService) buildHPPSection(c *fiber.Ctx, projectFlock *entity.ProjectFlock, projectFlockKandangs []entity.ProjectFlockKandang, costs *CostData, production *ProductionData) dto.HPPSection {
|
|
|
|
actualPopulation := production.TotalPopulationIn - production.TotalDepletion
|
|
totalWeightProduced := production.TotalWeightProduced
|
|
totalEggWeightKg := production.TotalEggWeightKg
|
|
|
|
weightForCalculation := totalWeightProduced
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
weightForCalculation = totalEggWeightKg
|
|
}
|
|
|
|
calculateMetrics := func(amount float64) (rpPerBird, rpPerKg float64) {
|
|
if actualPopulation > 0 {
|
|
rpPerBird = amount / actualPopulation
|
|
}
|
|
if weightForCalculation > 0 {
|
|
rpPerKg = amount / weightForCalculation
|
|
}
|
|
return
|
|
}
|
|
|
|
createHPPItem := func(id uint, category, code, label string, budgetAmount, realizationAmount float64) dto.HPPItem {
|
|
budgetRpPerBird, budgetRpPerKg := calculateMetrics(budgetAmount)
|
|
realizationRpPerBird, realizationRpPerKg := calculateMetrics(realizationAmount)
|
|
return dto.ToHPPItem(
|
|
id,
|
|
category,
|
|
code,
|
|
label,
|
|
dto.ToFinancialMetrics(budgetRpPerBird, budgetRpPerKg, budgetAmount),
|
|
dto.ToFinancialMetrics(realizationRpPerBird, realizationRpPerKg, realizationAmount),
|
|
)
|
|
}
|
|
|
|
hppItems := []dto.HPPItem{}
|
|
|
|
hppItems = append(hppItems, createHPPItem(1, "purchase", string(dto.HPPCodePakan), "Pembelian Pakan", costs.FeedCost, costs.FeedCost))
|
|
hppItems = append(hppItems, createHPPItem(2, "purchase", string(dto.HPPCodeOVK), "Pembelian OVK", costs.OvkCost, costs.OvkCost))
|
|
|
|
docCode := string(dto.HPPCodeDOC)
|
|
docLabel := "Pembelian DOC"
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
docCode = string(dto.HPPCodeDepresiasi)
|
|
docLabel = "Depresiasi"
|
|
}
|
|
hppItems = append(hppItems, createHPPItem(3, "purchase", docCode, docLabel, costs.ChickenCost, costs.ChickenCost))
|
|
hppItems = append(hppItems, createHPPItem(4, "overhead", string(dto.HPPCodeOverhead), "Pengeluaran Overhead", costs.BudgetOperational, costs.RealizationOperational))
|
|
hppItems = append(hppItems, createHPPItem(5, "overhead", string(dto.HPPCodeEkspedisi), "Beban Ekspedisi", costs.ExpeditionCost, costs.ExpeditionCost))
|
|
|
|
totalBudgetHpp := costs.FeedCost + costs.OvkCost + costs.ChickenCost + costs.BudgetOperational + costs.ExpeditionCost
|
|
totalRealizationHpp := costs.FeedCost + costs.OvkCost + costs.ChickenCost + costs.RealizationOperational + costs.ExpeditionCost
|
|
|
|
hppBudgetRpPerBird, hppBudgetRpPerKg := calculateMetrics(totalBudgetHpp)
|
|
hppRealizationRpPerBird, hppRealizationRpPerKg := calculateMetrics(totalRealizationHpp)
|
|
|
|
var eggBudgeting, eggRealization *dto.FinancialMetrics
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
accumulateEggMetrics := func(metrics **dto.FinancialMetrics, amount, rpPerKg float64) {
|
|
if *metrics == nil {
|
|
*metrics = &dto.FinancialMetrics{
|
|
RpPerBird: 0,
|
|
RpPerKg: rpPerKg,
|
|
Amount: amount,
|
|
}
|
|
} else {
|
|
(*metrics).Amount += amount
|
|
if totalEggWeightKg > 0 {
|
|
(*metrics).RpPerKg = (*metrics).Amount / totalEggWeightKg
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, projectFlockKandang := range projectFlockKandangs {
|
|
hppResponse, err := s.HppSvc.CalculateHppCost(projectFlockKandang.Id, nil)
|
|
if err == nil {
|
|
accumulateEggMetrics(&eggBudgeting, hppResponse.Estimation.Total, hppResponse.Estimation.HargaKg)
|
|
accumulateEggMetrics(&eggRealization, hppResponse.Real.Total, hppResponse.Real.HargaKg)
|
|
}
|
|
}
|
|
}
|
|
|
|
hppSummary := dto.ToHPPSummary(
|
|
"HPP",
|
|
dto.ToFinancialMetrics(hppBudgetRpPerBird, hppBudgetRpPerKg, totalBudgetHpp),
|
|
dto.ToFinancialMetrics(hppRealizationRpPerBird, hppRealizationRpPerKg, totalRealizationHpp),
|
|
eggBudgeting,
|
|
eggRealization,
|
|
)
|
|
|
|
return dto.ToHPPSection(hppItems, hppSummary)
|
|
}
|
|
|
|
func (s closingKeuanganService) buildProfitLossSection(projectFlock *entity.ProjectFlock, costs *CostData, production *ProductionData) dto.ProfitLossSection {
|
|
|
|
totalPopulationIn := production.TotalPopulationIn
|
|
totalWeightProduced := production.TotalWeightProduced
|
|
totalEggWeightKg := production.TotalEggWeightKg
|
|
totalSalesAmount := production.TotalSalesAmount
|
|
totalWeightSold := production.TotalWeightSold
|
|
|
|
weightForSales := totalWeightSold
|
|
weightForCalculation := totalWeightProduced
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
weightForSales = totalWeightSold
|
|
weightForCalculation = totalEggWeightKg
|
|
}
|
|
|
|
calculateProfitLossMetrics := func(amount float64) (rpPerBird, rpPerKg float64) {
|
|
if totalPopulationIn > 0 {
|
|
rpPerBird = amount / totalPopulationIn
|
|
}
|
|
if weightForSales > 0 {
|
|
rpPerKg = amount / weightForSales
|
|
}
|
|
return
|
|
}
|
|
|
|
actualPopulation := production.TotalPopulationIn - production.TotalDepletion
|
|
|
|
calculateMetrics := func(amount float64) (rpPerBird, rpPerKg float64) {
|
|
if actualPopulation > 0 {
|
|
rpPerBird = amount / actualPopulation
|
|
}
|
|
if weightForCalculation > 0 {
|
|
rpPerKg = amount / weightForCalculation
|
|
}
|
|
return
|
|
}
|
|
|
|
plItems := []dto.ProfitLossItem{}
|
|
|
|
salesRpPerBird, salesRpPerKg := calculateProfitLossMetrics(totalSalesAmount)
|
|
salesLabel := "Penjualan Ayam"
|
|
if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
|
salesLabel = "Penjualan Telur"
|
|
}
|
|
plItems = append(plItems, dto.ToProfitLossItem(
|
|
string(dto.PLCodeSales),
|
|
salesLabel,
|
|
"income",
|
|
salesRpPerBird,
|
|
salesRpPerKg,
|
|
totalSalesAmount,
|
|
))
|
|
|
|
totalSapronakAmount := costs.ChickenCost + costs.FeedCost + costs.OvkCost
|
|
_, sapronakRpPerKg := calculateMetrics(totalSapronakAmount)
|
|
sapronakRpPerBird := 0.0
|
|
for _, amount := range []float64{costs.ChickenCost, costs.FeedCost, costs.OvkCost} {
|
|
rpPerBird, _ := calculateMetrics(amount)
|
|
sapronakRpPerBird += rpPerBird
|
|
}
|
|
sapronakLabel := "Pengeluaran Sapronak"
|
|
plItems = append(plItems, dto.ToProfitLossItem(
|
|
string(dto.PLCodeSapronak),
|
|
sapronakLabel,
|
|
"purchase",
|
|
sapronakRpPerBird,
|
|
sapronakRpPerKg,
|
|
totalSapronakAmount,
|
|
))
|
|
|
|
overheadRpPerBird, overheadRpPerKg := calculateProfitLossMetrics(costs.RealizationOperational)
|
|
plItems = append(plItems, dto.ToProfitLossItem(
|
|
string(dto.PLCodeOverhead),
|
|
"Overhead",
|
|
"overhead",
|
|
overheadRpPerBird,
|
|
overheadRpPerKg,
|
|
costs.RealizationOperational,
|
|
))
|
|
|
|
ekspedisiRpPerBird, ekspedisiRpPerKg := calculateProfitLossMetrics(costs.ExpeditionCost)
|
|
plItems = append(plItems, dto.ToProfitLossItem(
|
|
string(dto.PLCodeEkspedisi),
|
|
"Ekspedisi",
|
|
"overhead",
|
|
ekspedisiRpPerBird,
|
|
ekspedisiRpPerKg,
|
|
costs.ExpeditionCost,
|
|
))
|
|
|
|
costOfGoodsSold := costs.ChickenCost + costs.FeedCost + costs.OvkCost
|
|
costOfGoodsSoldRpPerBird := sapronakRpPerBird
|
|
costOfGoodsSoldRpPerKg := sapronakRpPerKg
|
|
|
|
grossProfit := totalSalesAmount - costOfGoodsSold
|
|
grossProfitRpPerBird := salesRpPerBird - costOfGoodsSoldRpPerBird
|
|
grossProfitRpPerKg := salesRpPerKg - costOfGoodsSoldRpPerKg
|
|
|
|
totalOperatingExpenses := costs.RealizationOperational + costs.ExpeditionCost
|
|
totalOperatingExpensesRpPerBird := overheadRpPerBird + ekspedisiRpPerBird
|
|
totalOperatingExpensesRpPerKg := overheadRpPerKg + ekspedisiRpPerKg
|
|
|
|
netProfit := grossProfit - totalOperatingExpenses
|
|
netProfitRpPerBird := grossProfitRpPerBird - totalOperatingExpensesRpPerBird
|
|
netProfitRpPerKg := grossProfitRpPerKg - totalOperatingExpensesRpPerKg
|
|
|
|
plSummary := dto.ToProfitLossSummary(
|
|
dto.ToFinancialMetrics(grossProfitRpPerBird, grossProfitRpPerKg, grossProfit),
|
|
dto.ToFinancialMetrics(totalOperatingExpensesRpPerBird, totalOperatingExpensesRpPerKg, totalOperatingExpenses),
|
|
dto.ToFinancialMetrics(netProfitRpPerBird, netProfitRpPerKg, netProfit),
|
|
)
|
|
|
|
return dto.ToProfitLossSection(plItems, plSummary)
|
|
}
|
|
|
|
func containsFlag(flags []entity.Flag, name string) bool {
|
|
for _, flag := range flags {
|
|
if flag.Name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|