180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"apskel-pos-be/internal/entities"
|
|
"apskel-pos-be/internal/models"
|
|
"apskel-pos-be/internal/repository"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type HPPProcessor interface {
|
|
GetStandardHPP(ctx context.Context, req *models.StandardHPPRequest) (*models.StandardHPPResponse, error)
|
|
GetRealHPP(ctx context.Context, req *models.RealHPPRequest) (*models.RealHPPResponse, error)
|
|
}
|
|
|
|
type HPPProcessorImpl struct {
|
|
hppRepo repository.HPPRepository
|
|
}
|
|
|
|
func NewHPPProcessorImpl(hppRepo repository.HPPRepository) *HPPProcessorImpl {
|
|
return &HPPProcessorImpl{
|
|
hppRepo: hppRepo,
|
|
}
|
|
}
|
|
|
|
func (p *HPPProcessorImpl) GetStandardHPP(ctx context.Context, req *models.StandardHPPRequest) (*models.StandardHPPResponse, error) {
|
|
products, err := p.hppRepo.GetStandardHPP(ctx, req.OrganizationID, req.ProductID, req.CategoryID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get standard HPP: %w", err)
|
|
}
|
|
|
|
productIDs := make([]uuid.UUID, 0)
|
|
for _, pp := range products {
|
|
if pp.HasRecipe {
|
|
productIDs = append(productIDs, pp.ProductID)
|
|
}
|
|
}
|
|
|
|
ingredients, err := p.hppRepo.GetStandardHPPIngredients(ctx, req.OrganizationID, productIDs)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get standard HPP ingredients: %w", err)
|
|
}
|
|
|
|
productData := make([]models.StandardHPPProduct, 0, len(products))
|
|
var totalHPP float64
|
|
var hppCount int64
|
|
|
|
for _, pp := range products {
|
|
productData = append(productData, models.StandardHPPProduct{
|
|
ProductID: pp.ProductID,
|
|
ProductName: pp.ProductName,
|
|
ProductSku: pp.ProductSku,
|
|
CategoryID: pp.CategoryID,
|
|
CategoryName: pp.CategoryName,
|
|
SellingPrice: pp.SellingPrice,
|
|
ProductCost: pp.ProductCost,
|
|
StandardCost: pp.StandardCost,
|
|
StandardHPPPercentage: pp.StandardHPPPercentage,
|
|
HasRecipe: pp.HasRecipe,
|
|
})
|
|
if pp.HasRecipe {
|
|
totalHPP += pp.StandardHPPPercentage
|
|
hppCount++
|
|
}
|
|
}
|
|
|
|
ingredientData := make([]models.StandardHPPIngredient, 0, len(ingredients))
|
|
for _, ing := range ingredients {
|
|
ingredientData = append(ingredientData, models.StandardHPPIngredient{
|
|
ProductID: ing.ProductID,
|
|
IngredientID: ing.IngredientID,
|
|
IngredientName: ing.IngredientName,
|
|
Quantity: ing.Quantity,
|
|
UnitName: ing.UnitName,
|
|
CostPerUnit: ing.CostPerUnit,
|
|
WastePercentage: ing.WastePercentage,
|
|
TotalCost: ing.TotalCost,
|
|
})
|
|
}
|
|
|
|
var avgHPP float64
|
|
if hppCount > 0 {
|
|
avgHPP = totalHPP / float64(hppCount)
|
|
}
|
|
|
|
return &models.StandardHPPResponse{
|
|
OrganizationID: req.OrganizationID,
|
|
Summary: models.HPPSummary{
|
|
AverageStandardHPP: avgHPP,
|
|
AverageRealHPP: 0,
|
|
HPPVariance: 0,
|
|
TotalProducts: int64(len(products)),
|
|
},
|
|
Products: productData,
|
|
Ingredients: ingredientData,
|
|
}, nil
|
|
}
|
|
|
|
func (p *HPPProcessorImpl) GetRealHPP(ctx context.Context, req *models.RealHPPRequest) (*models.RealHPPResponse, error) {
|
|
if req.DateFrom.After(req.DateTo) {
|
|
return nil, fmt.Errorf("date_from cannot be after date_to")
|
|
}
|
|
|
|
if req.GroupBy == "" {
|
|
req.GroupBy = "day"
|
|
}
|
|
|
|
products, err := p.hppRepo.GetRealHPP(ctx, req.OrganizationID, req.OutletID, req.ProductID, req.CategoryID, req.DateFrom, req.DateTo)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get real HPP: %w", err)
|
|
}
|
|
|
|
var timeSeries []*entities.RealHPPTimeSeries
|
|
if req.GroupBy != "" {
|
|
timeSeries, err = p.hppRepo.GetRealHPPTimeSeries(ctx, req.OrganizationID, req.OutletID, req.ProductID, req.CategoryID, req.DateFrom, req.DateTo, req.GroupBy)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get real HPP time series: %w", err)
|
|
}
|
|
}
|
|
|
|
productData := make([]models.RealHPPProduct, 0, len(products))
|
|
var totalHPP float64
|
|
var totalRevenue float64
|
|
var hppCount int64
|
|
|
|
for _, pp := range products {
|
|
productData = append(productData, models.RealHPPProduct{
|
|
ProductID: pp.ProductID,
|
|
ProductName: pp.ProductName,
|
|
ProductSku: pp.ProductSku,
|
|
CategoryID: pp.CategoryID,
|
|
CategoryName: pp.CategoryName,
|
|
SellingPrice: pp.SellingPrice,
|
|
RealTotalCost: pp.RealTotalCost,
|
|
RealTotalRevenue: pp.RealTotalRevenue,
|
|
RealHPPPercentage: pp.RealHPPPercentage,
|
|
TotalQuantitySold: pp.TotalQuantitySold,
|
|
TotalOrders: pp.TotalOrders,
|
|
})
|
|
if pp.RealTotalRevenue > 0 {
|
|
totalHPP += pp.RealHPPPercentage
|
|
totalRevenue += pp.RealTotalRevenue
|
|
hppCount++
|
|
}
|
|
}
|
|
|
|
var avgHPP float64
|
|
if hppCount > 0 {
|
|
avgHPP = totalHPP / float64(hppCount)
|
|
}
|
|
|
|
tsData := make([]models.RealHPPTimeSeries, 0, len(timeSeries))
|
|
for _, ts := range timeSeries {
|
|
tsData = append(tsData, models.RealHPPTimeSeries{
|
|
Date: ts.Date,
|
|
RealTotalCost: ts.RealTotalCost,
|
|
RealTotalRevenue: ts.RealTotalRevenue,
|
|
RealHPPPercentage: ts.RealHPPPercentage,
|
|
TotalOrders: ts.TotalOrders,
|
|
})
|
|
}
|
|
|
|
return &models.RealHPPResponse{
|
|
OrganizationID: req.OrganizationID,
|
|
OutletID: req.OutletID,
|
|
DateFrom: req.DateFrom,
|
|
DateTo: req.DateTo,
|
|
GroupBy: req.GroupBy,
|
|
Summary: models.HPPSummary{
|
|
AverageRealHPP: avgHPP,
|
|
TotalProducts: int64(len(products)),
|
|
},
|
|
Products: productData,
|
|
TimeSeries: tsData,
|
|
}, nil
|
|
}
|