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 }