internal/cli/report.go
package cli
import (
"gt/internal/render"
"gt/internal/report"
"gt/internal/store"
"time"
"github.com/spf13/cobra"
)
func reportCmd(cli *cli) *cobra.Command {
var cmd = &cobra.Command{
Use: "report",
}
cmd.AddCommand(incomeExpenseReportCmd(cli))
return cmd
}
type AccountType string
const (
AccountTypeLiability AccountType = "LIABILITY"
AccountTypeExpense AccountType = "EXPENSE"
AccountTypeIncome AccountType = "INCOME"
)
func incomeExpenseReportCmd(cli *cli) *cobra.Command {
var flags struct {
sortBy string
startDate string
endDate string
}
var cmd = &cobra.Command{
Use: "income-expense [flags]",
Short: "Income Expense Report",
Example: `
* Print report between 2 dates:
$ gt report income-expense --start-date 2026-01-01 --end-date 2026-01-31
* Print report sorting by 'value':
$ gt report income-expense --sort-by value
`,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
s := store.NewStore(cli.db)
transactionQuery := store.NewTransactionQuery().
Where("transactions.post_date > ?", flags.startDate).
Where("transactions.post_date < ?", flags.endDate)
transactions, err := s.Transactions.All(cmd.Context(), transactionQuery)
if err != nil {
return err
}
p := report.IncomeExpenseReport{
Income: []*report.AccountItem{},
Expense: []*report.AccountItem{},
}
for _, t := range transactions {
for _, s := range t.Splits {
switch s.Account.AccountType {
case string(AccountTypeExpense):
exists := false
for _, i := range p.Expense {
if i.Account.GUID == s.Account.GUID {
i.ValueNum = i.ValueNum + s.ValueNum
exists = true
break
}
}
if !exists {
item := &report.AccountItem{
Account: *s.Account,
ValueNum: s.ValueNum,
ValueDenom: s.ValueDenom,
}
p.Expense = append(p.Expense, item)
}
case string(AccountTypeIncome):
exists := false
for _, i := range p.Income {
if i.Account.GUID == s.Account.GUID {
i.ValueNum = i.ValueNum + s.ValueNum
exists = true
break
}
}
if !exists {
item := &report.AccountItem{
Account: *s.Account,
ValueNum: s.ValueNum,
ValueDenom: s.ValueDenom,
}
p.Income = append(p.Income, item)
}
}
}
}
r, err := render.New("table")
if err != nil {
return err
}
renderOpts := []render.RendererOptsFunc{render.WithAccountShortName(true), render.WithSortBy(flags.sortBy)}
return r.Render(cmd.OutOrStdout(), p, renderOpts...)
},
}
cmd.Flags().StringVar(&flags.sortBy, "sort-by", "name", "Sort report items by name or value")
cmd.Flags().StringVar(&flags.startDate, "start-date", time.Now().AddDate(0, 0, -31).Format("2006-01-02 15:04:05"), "Start date of the report")
cmd.Flags().StringVar(&flags.endDate, "end-date", time.Now().Format("2006-01-02 15:04:05"), "End date of the report")
return cmd
}