package postgres import ( "context" "encoding/csv" "fmt" "io" "github.com/google/uuid" ) type YNABExport struct { queries *Queries budgetID uuid.UUID } func NewYNABExport(context context.Context, queries *Queries, budgetID uuid.UUID) (*YNABExport, error) { return &YNABExport{ queries: queries, budgetID: budgetID, }, nil } // ImportAssignments expects a TSV-file as exported by YNAB in the following format: // "Month" "Category Group/Category" "Category Group" "Category" "Budgeted" "Activity" "Available" // "Apr 2019" "Income: Next Month" "Income" "Next Month" 0,00€ 0,00€ 0,00€ // // Activity and Available are not imported, since they are determined by the transactions and historic assignments. func (ynab *YNABExport) ExportAssignments(context context.Context, w io.Writer) error { csv := csv.NewWriter(w) csv.Comma = '\t' assignments, err := ynab.queries.GetAllAssignments(context, ynab.budgetID) if err != nil { return fmt.Errorf("load assignments: %w", err) } count := 0 for _, assignment := range assignments { row := []string{ assignment.Date.Format("Jan 2006"), assignment.Group + ": " + assignment.Category, assignment.Group, assignment.Category, assignment.Amount.String() + "€", NewZeroNumeric().String() + "€", NewZeroNumeric().String() + "€", } err := csv.Write(row) if err != nil { return fmt.Errorf("write assignment: %w", err) } count++ } csv.Flush() fmt.Printf("Exported %d assignments\n", count) return nil } // ImportTransactions expects a TSV-file as exported by YNAB in the following format: // "Account" "Flag" "Date" "Payee" "Category Group/Category" "Category Group" "Category" "Memo" "Outflow" "Inflow" "Cleared" // "Cash" "" "11.12.2021" "Transfer : Checking" "" "" "" "Brought to bank" 500,00€ 0,00€ "Cleared". func (ynab *YNABExport) ExportTransactions(context context.Context, w io.Writer) error { csv := csv.NewWriter(w) csv.Comma = '\t' transactions, err := ynab.queries.GetAllTransactionsForBudget(context, ynab.budgetID) if err != nil { return fmt.Errorf("load transactions: %w", err) } header := []string{ "Account", "Flag", "Date", "Payee", "Category Group/Category", "Category Group", "Category", "Memo", "Outflow", "Inflow", "Cleared", } err = csv.Write(header) if err != nil { return fmt.Errorf("write transaction: %w", err) } count := 0 for _, transaction := range transactions { row := GetTransactionRow(transaction) err := csv.Write(row) if err != nil { return fmt.Errorf("write transaction: %w", err) } count++ } csv.Flush() fmt.Printf("Exported %d transactions\n", count) return nil } func GetTransactionRow(transaction GetAllTransactionsForBudgetRow) []string { row := []string{ transaction.Account, "", // Flag transaction.Date.Format("02.01.2006"), transaction.Payee, } if transaction.CategoryGroup != "" && transaction.Category != "" { row = append(row, transaction.CategoryGroup+" : "+transaction.Category, transaction.CategoryGroup, transaction.Category) } else { row = append(row, "", "", "") } row = append(row, transaction.Memo) if transaction.Amount.IsPositive() { row = append(row, NewZeroNumeric().String()+"€", transaction.Amount.String()+"€") } else { row = append(row, transaction.Amount.String()[1:]+"€", NewZeroNumeric().String()+"€") } return append(row, string(transaction.Status)) }