reinstate profit loss overview
This commit is contained in:
@@ -410,11 +410,49 @@ func (p *AnalyticsProcessorImpl) GetProfitLossAnalytics(ctx context.Context, req
|
||||
return nil, fmt.Errorf("date_from cannot be after date_to")
|
||||
}
|
||||
|
||||
result, err := p.analyticsRepo.GetProfitLossAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo)
|
||||
if req.GroupBy == "" {
|
||||
req.GroupBy = "day"
|
||||
}
|
||||
|
||||
result, err := p.analyticsRepo.GetProfitLossAnalytics(ctx, req.OrganizationID, req.OutletID, req.DateFrom, req.DateTo, req.GroupBy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get profit/loss analytics: %w", err)
|
||||
}
|
||||
|
||||
data := make([]models.ProfitLossData, len(result.Data))
|
||||
for i, item := range result.Data {
|
||||
data[i] = models.ProfitLossData{
|
||||
Date: item.Date,
|
||||
Revenue: item.Revenue,
|
||||
Cost: item.Cost,
|
||||
GrossProfit: item.GrossProfit,
|
||||
GrossProfitMargin: item.GrossProfitMargin,
|
||||
Tax: item.Tax,
|
||||
Discount: item.Discount,
|
||||
NetProfit: item.NetProfit,
|
||||
NetProfitMargin: item.NetProfitMargin,
|
||||
Orders: item.Orders,
|
||||
}
|
||||
}
|
||||
|
||||
productData := make([]models.ProductProfitData, len(result.ProductData))
|
||||
for i, item := range result.ProductData {
|
||||
productData[i] = models.ProductProfitData{
|
||||
ProductID: item.ProductID,
|
||||
ProductName: item.ProductName,
|
||||
CategoryID: item.CategoryID,
|
||||
CategoryName: item.CategoryName,
|
||||
QuantitySold: item.QuantitySold,
|
||||
Revenue: item.Revenue,
|
||||
Cost: item.Cost,
|
||||
GrossProfit: item.GrossProfit,
|
||||
GrossProfitMargin: item.GrossProfitMargin,
|
||||
AveragePrice: item.AveragePrice,
|
||||
AverageCost: item.AverageCost,
|
||||
ProfitPerUnit: item.ProfitPerUnit,
|
||||
}
|
||||
}
|
||||
|
||||
todayPromosi := getExpenseAmountByCategory(result.TodayExpenseByCategory, "promosi")
|
||||
todayLainLain := getExpenseAmountByCategory(result.TodayExpenseByCategory, "lain")
|
||||
todayTotalOps := todayPromosi + todayLainLain
|
||||
@@ -513,10 +551,26 @@ func (p *AnalyticsProcessorImpl) GetProfitLossAnalytics(ctx context.Context, req
|
||||
}
|
||||
|
||||
return &models.ProfitLossAnalyticsResponse{
|
||||
OrganizationID: req.OrganizationID,
|
||||
OutletID: req.OutletID,
|
||||
DateFrom: req.DateFrom,
|
||||
DateTo: req.DateTo,
|
||||
OrganizationID: req.OrganizationID,
|
||||
OutletID: req.OutletID,
|
||||
DateFrom: req.DateFrom,
|
||||
DateTo: req.DateTo,
|
||||
GroupBy: req.GroupBy,
|
||||
Summary: models.ProfitLossSummary{
|
||||
TotalRevenue: result.Summary.TotalRevenue,
|
||||
TotalCost: result.Summary.TotalCost,
|
||||
GrossProfit: result.Summary.GrossProfit,
|
||||
GrossProfitMargin: result.Summary.GrossProfitMargin,
|
||||
TotalTax: result.Summary.TotalTax,
|
||||
TotalDiscount: result.Summary.TotalDiscount,
|
||||
NetProfit: result.Summary.NetProfit,
|
||||
NetProfitMargin: result.Summary.NetProfitMargin,
|
||||
TotalOrders: result.Summary.TotalOrders,
|
||||
AverageProfit: result.Summary.AverageProfit,
|
||||
ProfitabilityRatio: result.Summary.ProfitabilityRatio,
|
||||
},
|
||||
Data: data,
|
||||
ProductData: productData,
|
||||
MainSummary: mainSummary,
|
||||
OperationalExpenses: opsItems,
|
||||
OperationalExpensesTotal: opsTotal,
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
|
||||
type analyticsRepositoryStub struct {
|
||||
purchasingResult *entities.PurchasingAnalytics
|
||||
profitLossResult *entities.ProfitLossAnalytics
|
||||
profitLossGroup string
|
||||
}
|
||||
|
||||
func (analyticsRepositoryStub) GetPaymentMethodAnalytics(context.Context, uuid.UUID, *uuid.UUID, time.Time, time.Time) ([]*entities.PaymentMethodAnalytics, error) {
|
||||
@@ -40,8 +42,9 @@ func (analyticsRepositoryStub) GetDashboardOverview(context.Context, uuid.UUID,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (analyticsRepositoryStub) GetProfitLossAnalytics(context.Context, uuid.UUID, *uuid.UUID, time.Time, time.Time) (*entities.ProfitLossAnalytics, error) {
|
||||
return nil, nil
|
||||
func (s analyticsRepositoryStub) GetProfitLossAnalytics(_ context.Context, _ uuid.UUID, _ *uuid.UUID, _, _ time.Time, groupBy string) (*entities.ProfitLossAnalytics, error) {
|
||||
s.profitLossGroup = groupBy
|
||||
return s.profitLossResult, nil
|
||||
}
|
||||
|
||||
type expenseRepositoryStub struct{}
|
||||
@@ -88,3 +91,77 @@ func TestAnalyticsProcessorGetPurchasingAnalyticsPassesOutletName(t *testing.T)
|
||||
require.Equal(t, outletName, *result.OutletName)
|
||||
require.Equal(t, float64(125), result.Summary.TotalPurchases)
|
||||
}
|
||||
|
||||
func TestAnalyticsProcessorGetProfitLossAnalyticsMapsOverviewAndReportFields(t *testing.T) {
|
||||
productID := uuid.New()
|
||||
categoryID := uuid.New()
|
||||
now := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)
|
||||
processor := NewAnalyticsProcessorImpl(analyticsRepositoryStub{
|
||||
profitLossResult: &entities.ProfitLossAnalytics{
|
||||
Summary: entities.ProfitLossSummary{
|
||||
TotalRevenue: 1000,
|
||||
TotalCost: 400,
|
||||
GrossProfit: 600,
|
||||
GrossProfitMargin: 60,
|
||||
TotalTax: 50,
|
||||
TotalDiscount: 25,
|
||||
NetProfit: 575,
|
||||
NetProfitMargin: 57.5,
|
||||
TotalOrders: 10,
|
||||
AverageProfit: 57.5,
|
||||
ProfitabilityRatio: 150,
|
||||
},
|
||||
Data: []entities.ProfitLossData{
|
||||
{
|
||||
Date: now,
|
||||
Revenue: 1000,
|
||||
Cost: 400,
|
||||
GrossProfit: 600,
|
||||
GrossProfitMargin: 60,
|
||||
Tax: 50,
|
||||
Discount: 25,
|
||||
NetProfit: 575,
|
||||
NetProfitMargin: 57.5,
|
||||
Orders: 10,
|
||||
},
|
||||
},
|
||||
ProductData: []entities.ProductProfitData{
|
||||
{
|
||||
ProductID: productID,
|
||||
ProductName: "Nasi",
|
||||
CategoryID: categoryID,
|
||||
CategoryName: "Food",
|
||||
QuantitySold: 5,
|
||||
Revenue: 500,
|
||||
Cost: 200,
|
||||
GrossProfit: 300,
|
||||
GrossProfitMargin: 60,
|
||||
AveragePrice: 100,
|
||||
AverageCost: 40,
|
||||
ProfitPerUnit: 60,
|
||||
},
|
||||
},
|
||||
TodayRevenue: 1000,
|
||||
TodayCost: 400,
|
||||
MtdRevenue: 2000,
|
||||
MtdCost: 800,
|
||||
},
|
||||
}, expenseRepositoryStub{})
|
||||
|
||||
result, err := processor.GetProfitLossAnalytics(context.Background(), &models.ProfitLossAnalyticsRequest{
|
||||
OrganizationID: uuid.New(),
|
||||
DateFrom: now,
|
||||
DateTo: now,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, "day", result.GroupBy)
|
||||
require.Equal(t, float64(1000), result.Summary.TotalRevenue)
|
||||
require.Len(t, result.Data, 1)
|
||||
require.Equal(t, float64(575), result.Data[0].NetProfit)
|
||||
require.Len(t, result.ProductData, 1)
|
||||
require.Equal(t, productID, result.ProductData[0].ProductID)
|
||||
require.NotEmpty(t, result.MainSummary)
|
||||
require.Equal(t, "total_omset", result.MainSummary[0].ID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user