feat: show report on payment method
All checks were successful
Deploy / build-and-deploy (push) Successful in 2m37s
All checks were successful
Deploy / build-and-deploy (push) Successful in 2m37s
This commit is contained in:
parent
b5295d1c73
commit
047e392ac3
4 changed files with 70 additions and 0 deletions
|
|
@ -16,8 +16,16 @@ type MonthlySpending struct {
|
||||||
Total decimal.Decimal `db:"total"`
|
Total decimal.Decimal `db:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PaymentMethodExpenseSummary struct {
|
||||||
|
PaymentMethodID string `db:"payment_method_id"`
|
||||||
|
PaymentMethodName string `db:"payment_method_name"`
|
||||||
|
PaymentMethodType string `db:"payment_method_type"`
|
||||||
|
TotalAmount decimal.Decimal `db:"total_amount"`
|
||||||
|
}
|
||||||
|
|
||||||
type SpendingReport struct {
|
type SpendingReport struct {
|
||||||
ByTag []*TagExpenseSummary
|
ByTag []*TagExpenseSummary
|
||||||
|
ByPaymentMethod []*PaymentMethodExpenseSummary
|
||||||
DailySpending []*DailySpending
|
DailySpending []*DailySpending
|
||||||
MonthlySpending []*MonthlySpending
|
MonthlySpending []*MonthlySpending
|
||||||
TopExpenses []*ExpenseWithTagsAndMethod
|
TopExpenses []*ExpenseWithTagsAndMethod
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ type ExpenseRepository interface {
|
||||||
GetMonthlySpending(spaceID string, from, to time.Time) ([]*model.MonthlySpending, error)
|
GetMonthlySpending(spaceID string, from, to time.Time) ([]*model.MonthlySpending, error)
|
||||||
GetTopExpenses(spaceID string, from, to time.Time, limit int) ([]*model.Expense, error)
|
GetTopExpenses(spaceID string, from, to time.Time, limit int) ([]*model.Expense, error)
|
||||||
GetIncomeVsExpenseSummary(spaceID string, from, to time.Time) (decimal.Decimal, decimal.Decimal, error)
|
GetIncomeVsExpenseSummary(spaceID string, from, to time.Time) (decimal.Decimal, decimal.Decimal, error)
|
||||||
|
GetExpensesByPaymentMethod(spaceID string, from, to time.Time) ([]*model.PaymentMethodExpenseSummary, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type expenseRepository struct {
|
type expenseRepository struct {
|
||||||
|
|
@ -324,3 +325,23 @@ func (r *expenseRepository) GetIncomeVsExpenseSummary(spaceID string, from, to t
|
||||||
}
|
}
|
||||||
return income, expenseTotal, nil
|
return income, expenseTotal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *expenseRepository) GetExpensesByPaymentMethod(spaceID string, from, to time.Time) ([]*model.PaymentMethodExpenseSummary, error) {
|
||||||
|
var summaries []*model.PaymentMethodExpenseSummary
|
||||||
|
query := `
|
||||||
|
SELECT COALESCE(pm.id, 'cash') as payment_method_id,
|
||||||
|
COALESCE(pm.name, 'Cash') as payment_method_name,
|
||||||
|
COALESCE(pm.type, 'cash') as payment_method_type,
|
||||||
|
SUM(CAST(e.amount AS DECIMAL)) as total_amount
|
||||||
|
FROM expenses e
|
||||||
|
LEFT JOIN payment_methods pm ON e.payment_method_id = pm.id
|
||||||
|
WHERE e.space_id = $1 AND e.type = 'expense' AND e.date >= $2 AND e.date <= $3
|
||||||
|
GROUP BY pm.id, pm.name, pm.type
|
||||||
|
ORDER BY total_amount DESC;
|
||||||
|
`
|
||||||
|
err := r.db.Select(&summaries, query, spaceID, from, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return summaries, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,11 @@ func (s *ReportService) GetSpendingReport(spaceID string, from, to time.Time) (*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byPaymentMethod, err := s.expenseRepo.GetExpensesByPaymentMethod(spaceID, from, to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
totalIncome, totalExpenses, err := s.expenseRepo.GetIncomeVsExpenseSummary(spaceID, from, to)
|
totalIncome, totalExpenses, err := s.expenseRepo.GetIncomeVsExpenseSummary(spaceID, from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -68,6 +73,7 @@ func (s *ReportService) GetSpendingReport(spaceID string, from, to time.Time) (*
|
||||||
|
|
||||||
return &model.SpendingReport{
|
return &model.SpendingReport{
|
||||||
ByTag: byTag,
|
ByTag: byTag,
|
||||||
|
ByPaymentMethod: byPaymentMethod,
|
||||||
DailySpending: daily,
|
DailySpending: daily,
|
||||||
MonthlySpending: monthly,
|
MonthlySpending: monthly,
|
||||||
TopExpenses: topWithTags,
|
TopExpenses: topWithTags,
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,41 @@ templ ReportCharts(spaceID string, report *model.SpendingReport, from, to time.T
|
||||||
<p class="text-sm text-muted-foreground">No tagged expenses in this period.</p>
|
<p class="text-sm text-muted-foreground">No tagged expenses in this period.</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
// Spending by Payment Method (Doughnut chart)
|
||||||
|
if len(report.ByPaymentMethod) > 0 {
|
||||||
|
<div class="border rounded-lg p-4 bg-card text-card-foreground min-w-0 overflow-hidden">
|
||||||
|
<h3 class="font-semibold mb-2">Spending by Payment Method</h3>
|
||||||
|
{{
|
||||||
|
pmLabels := make([]string, len(report.ByPaymentMethod))
|
||||||
|
pmData := make([]float64, len(report.ByPaymentMethod))
|
||||||
|
pmColors := make([]string, len(report.ByPaymentMethod))
|
||||||
|
for i, pm := range report.ByPaymentMethod {
|
||||||
|
pmLabels[i] = pm.PaymentMethodName
|
||||||
|
pmData[i] = pm.TotalAmount.InexactFloat64()
|
||||||
|
pmColors[i] = defaultChartColors[i%len(defaultChartColors)]
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@chart.Chart(chart.Props{
|
||||||
|
Variant: chart.VariantDoughnut,
|
||||||
|
ShowLegend: true,
|
||||||
|
Data: chart.Data{
|
||||||
|
Labels: pmLabels,
|
||||||
|
Datasets: []chart.Dataset{
|
||||||
|
{
|
||||||
|
Label: "Spending",
|
||||||
|
Data: pmData,
|
||||||
|
BackgroundColor: pmColors,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<div class="border rounded-lg p-4 bg-card text-card-foreground min-w-0">
|
||||||
|
<h3 class="font-semibold mb-2">Spending by Payment Method</h3>
|
||||||
|
<p class="text-sm text-muted-foreground">No payment method data in this period.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
// Spending Over Time (Bar chart)
|
// Spending Over Time (Bar chart)
|
||||||
if len(report.DailySpending) > 0 || len(report.MonthlySpending) > 0 {
|
if len(report.DailySpending) > 0 || len(report.MonthlySpending) > 0 {
|
||||||
<div class="border rounded-lg p-4 bg-card text-card-foreground min-w-0 overflow-hidden">
|
<div class="border rounded-lg p-4 bg-card text-card-foreground min-w-0 overflow-hidden">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue