feat: recurring expenses and reports

This commit is contained in:
juancwu 2026-02-14 17:00:15 +00:00
commit 9e6ff67a87
23 changed files with 2943 additions and 56 deletions

42
internal/model/budget.go Normal file
View file

@ -0,0 +1,42 @@
package model
import "time"
type BudgetPeriod string
const (
BudgetPeriodWeekly BudgetPeriod = "weekly"
BudgetPeriodMonthly BudgetPeriod = "monthly"
BudgetPeriodYearly BudgetPeriod = "yearly"
)
type BudgetStatus string
const (
BudgetStatusOnTrack BudgetStatus = "on_track"
BudgetStatusWarning BudgetStatus = "warning"
BudgetStatusOver BudgetStatus = "over"
)
type Budget struct {
ID string `db:"id"`
SpaceID string `db:"space_id"`
TagID string `db:"tag_id"`
AmountCents int `db:"amount_cents"`
Period BudgetPeriod `db:"period"`
StartDate time.Time `db:"start_date"`
EndDate *time.Time `db:"end_date"`
IsActive bool `db:"is_active"`
CreatedBy string `db:"created_by"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type BudgetWithSpent struct {
Budget
TagName string `db:"tag_name"`
TagColor *string `db:"tag_color"`
SpentCents int
Percentage float64
Status BudgetStatus
}

View file

@ -10,16 +10,17 @@ const (
)
type Expense struct {
ID string `db:"id"`
SpaceID string `db:"space_id"`
CreatedBy string `db:"created_by"`
Description string `db:"description"`
AmountCents int `db:"amount_cents"`
Type ExpenseType `db:"type"`
Date time.Time `db:"date"`
PaymentMethodID *string `db:"payment_method_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID string `db:"id"`
SpaceID string `db:"space_id"`
CreatedBy string `db:"created_by"`
Description string `db:"description"`
AmountCents int `db:"amount_cents"`
Type ExpenseType `db:"type"`
Date time.Time `db:"date"`
PaymentMethodID *string `db:"payment_method_id"`
RecurringExpenseID *string `db:"recurring_expense_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type ExpenseWithTags struct {

View file

@ -0,0 +1,41 @@
package model
import "time"
type Frequency string
const (
FrequencyDaily Frequency = "daily"
FrequencyWeekly Frequency = "weekly"
FrequencyBiweekly Frequency = "biweekly"
FrequencyMonthly Frequency = "monthly"
FrequencyYearly Frequency = "yearly"
)
type RecurringExpense struct {
ID string `db:"id"`
SpaceID string `db:"space_id"`
CreatedBy string `db:"created_by"`
Description string `db:"description"`
AmountCents int `db:"amount_cents"`
Type ExpenseType `db:"type"`
PaymentMethodID *string `db:"payment_method_id"`
Frequency Frequency `db:"frequency"`
StartDate time.Time `db:"start_date"`
EndDate *time.Time `db:"end_date"`
NextOccurrence time.Time `db:"next_occurrence"`
IsActive bool `db:"is_active"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type RecurringExpenseWithTags struct {
RecurringExpense
Tags []*Tag
}
type RecurringExpenseWithTagsAndMethod struct {
RecurringExpense
Tags []*Tag
PaymentMethod *PaymentMethod
}

23
internal/model/report.go Normal file
View file

@ -0,0 +1,23 @@
package model
import "time"
type DailySpending struct {
Date time.Time `db:"date"`
TotalCents int `db:"total_cents"`
}
type MonthlySpending struct {
Month string `db:"month"`
TotalCents int `db:"total_cents"`
}
type SpendingReport struct {
ByTag []*TagExpenseSummary
DailySpending []*DailySpending
MonthlySpending []*MonthlySpending
TopExpenses []*ExpenseWithTagsAndMethod
TotalIncome int
TotalExpenses int
NetBalance int
}