savings_pct = (receita - despesas) / (receita - impostos). Toggle IMPOSTO no CRUD de categorias; badge dourado na lista. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
136 lines
3.7 KiB
Go
136 lines
3.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"financeiro-carvalho/internal/model"
|
|
)
|
|
|
|
type DashboardRepository struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewDashboardRepository(pool *pgxpool.Pool) *DashboardRepository {
|
|
return &DashboardRepository{pool: pool}
|
|
}
|
|
|
|
func (r *DashboardRepository) MonthlySummary(ctx context.Context, month string) (income, expenses float64, err error) {
|
|
row := r.pool.QueryRow(ctx, `
|
|
SELECT
|
|
COALESCE(SUM(CASE WHEN type = 'income' THEN amount ELSE 0 END), 0),
|
|
COALESCE(SUM(CASE WHEN type = 'expense' THEN amount ELSE 0 END), 0)
|
|
FROM transactions
|
|
WHERE to_char(date, 'YYYY-MM') = $1
|
|
`, month)
|
|
err = row.Scan(&income, &expenses)
|
|
return
|
|
}
|
|
|
|
func (r *DashboardRepository) ByCategory(ctx context.Context, month string) ([]model.CategoryTotal, error) {
|
|
rows, err := r.pool.Query(ctx, `
|
|
SELECT
|
|
t.category_id,
|
|
COALESCE(c.name, 'Sem categoria') AS category_name,
|
|
COALESCE(c.color, '#6B7280') AS color,
|
|
SUM(t.amount) AS total
|
|
FROM transactions t
|
|
LEFT JOIN categories c ON c.id = t.category_id
|
|
WHERE to_char(t.date, 'YYYY-MM') = $1
|
|
AND t.type = 'expense'
|
|
GROUP BY t.category_id, c.name, c.color
|
|
ORDER BY total DESC
|
|
`, month)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []model.CategoryTotal
|
|
for rows.Next() {
|
|
var ct model.CategoryTotal
|
|
if err := rows.Scan(&ct.CategoryID, &ct.CategoryName, &ct.Color, &ct.Total); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, ct)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (r *DashboardRepository) MonthlyEvolution(ctx context.Context, month string) ([]model.MonthEvolution, error) {
|
|
rows, err := r.pool.Query(ctx, `
|
|
SELECT
|
|
to_char(m.ms, 'YYYY-MM') AS month,
|
|
COALESCE(SUM(CASE WHEN t.type = 'income' THEN t.amount ELSE 0 END), 0) AS income,
|
|
COALESCE(SUM(CASE WHEN t.type = 'expense' THEN t.amount ELSE 0 END), 0) AS expenses
|
|
FROM generate_series(
|
|
date_trunc('month', ($1 || '-01')::date) - INTERVAL '5 months',
|
|
date_trunc('month', ($1 || '-01')::date),
|
|
'1 month'::interval
|
|
) AS m(ms)
|
|
LEFT JOIN transactions t ON date_trunc('month', t.date) = m.ms
|
|
GROUP BY m.ms
|
|
ORDER BY m.ms
|
|
`, month)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []model.MonthEvolution
|
|
for rows.Next() {
|
|
var me model.MonthEvolution
|
|
if err := rows.Scan(&me.Month, &me.Income, &me.Expenses); err != nil {
|
|
return nil, err
|
|
}
|
|
me.Saved = me.Income - me.Expenses
|
|
out = append(out, me)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (r *DashboardRepository) TotalTaxes(ctx context.Context, month string) (float64, error) {
|
|
var total float64
|
|
err := r.pool.QueryRow(ctx, `
|
|
SELECT COALESCE(SUM(t.amount), 0)
|
|
FROM transactions t
|
|
JOIN categories c ON c.id = t.category_id
|
|
WHERE to_char(t.date, 'YYYY-MM') = $1
|
|
AND t.type = 'expense'
|
|
AND c.is_tax = TRUE
|
|
`, month).Scan(&total)
|
|
return total, err
|
|
}
|
|
|
|
func (r *DashboardRepository) RecentTransactions(ctx context.Context, month string) ([]model.RecentTransaction, error) {
|
|
rows, err := r.pool.Query(ctx, `
|
|
SELECT
|
|
t.id,
|
|
t.date::text,
|
|
t.description,
|
|
COALESCE(c.name, 'Sem categoria') AS category_name,
|
|
t.amount,
|
|
t.type
|
|
FROM transactions t
|
|
LEFT JOIN categories c ON c.id = t.category_id
|
|
WHERE to_char(t.date, 'YYYY-MM') = $1
|
|
ORDER BY t.date DESC, t.id DESC
|
|
LIMIT 10
|
|
`, month)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []model.RecentTransaction
|
|
for rows.Next() {
|
|
var rt model.RecentTransaction
|
|
if err := rows.Scan(&rt.ID, &rt.Date, &rt.Description, &rt.CategoryName, &rt.Amount, &rt.Type); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, rt)
|
|
}
|
|
return out, rows.Err()
|
|
}
|