166 lines
3.7 KiB
Go
166 lines
3.7 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"encoding/csv"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"git.javil.eu/jacob1123/budgeteer/postgres"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgtype"
|
|
)
|
|
|
|
type YNABImport struct {
|
|
Context context.Context
|
|
accounts []postgres.Account
|
|
payees []postgres.Payee
|
|
queries *postgres.Queries
|
|
budgetID uuid.UUID
|
|
}
|
|
|
|
func NewYNABImport(q *postgres.Queries, budgetID uuid.UUID) (*YNABImport, error) {
|
|
accounts, err := q.GetAccounts(context.Background(), uuid.UUID{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
payees, err := q.GetPayees(context.Background(), uuid.UUID{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &YNABImport{
|
|
Context: context.Background(),
|
|
accounts: accounts,
|
|
payees: payees,
|
|
queries: q,
|
|
budgetID: budgetID,
|
|
}, nil
|
|
|
|
}
|
|
|
|
func (ynab *YNABImport) Import(r io.Reader) error {
|
|
csv := csv.NewReader(r)
|
|
csv.Comma = '\t'
|
|
csv.LazyQuotes = true
|
|
|
|
csvData, err := csv.ReadAll()
|
|
if err != nil {
|
|
return fmt.Errorf("could not read from tsv: %w", err)
|
|
}
|
|
|
|
count := 0
|
|
for _, record := range csvData[1:] {
|
|
accountName := record[0]
|
|
account, err := ynab.GetAccount(accountName)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get account %s: %w", accountName, err)
|
|
}
|
|
|
|
//flag := record[1]
|
|
|
|
dateString := record[2]
|
|
date, err := time.Parse("02.01.2006", dateString)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse date %s: %w", dateString, err)
|
|
}
|
|
|
|
payeeName := record[3]
|
|
payeeID, err := ynab.GetPayee(payeeName)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get payee %s: %w", payeeName, err)
|
|
}
|
|
|
|
//category := record[4] //also in 5 + 6 split by group/category
|
|
memo := record[7]
|
|
|
|
outflow := record[8]
|
|
inflow := record[9]
|
|
amount, err := GetAmount(inflow, outflow)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse amount from (%s/%s): %w", inflow, outflow, err)
|
|
}
|
|
|
|
//cleared := record[10]
|
|
|
|
transaction := postgres.CreateTransactionParams{
|
|
Date: date,
|
|
Memo: memo,
|
|
AccountID: account.ID,
|
|
PayeeID: payeeID,
|
|
Amount: amount,
|
|
}
|
|
_, err = ynab.queries.CreateTransaction(ynab.Context, transaction)
|
|
if err != nil {
|
|
return fmt.Errorf("could not save transaction %v: %w", transaction, err)
|
|
}
|
|
|
|
count++
|
|
}
|
|
|
|
fmt.Printf("Imported %d transactions\n", count)
|
|
|
|
return nil
|
|
}
|
|
|
|
func trimLastChar(s string) string {
|
|
r, size := utf8.DecodeLastRuneInString(s)
|
|
if r == utf8.RuneError && (size == 0 || size == 1) {
|
|
size = 0
|
|
}
|
|
return s[:len(s)-size]
|
|
}
|
|
|
|
func GetAmount(inflow string, outflow string) (pgtype.Numeric, error) {
|
|
// Remove currency
|
|
inflow = strings.Replace(trimLastChar(inflow), ",", ".", 1)
|
|
outflow = strings.Replace(trimLastChar(outflow), ",", ".", 1)
|
|
|
|
num := pgtype.Numeric{}
|
|
err := num.Set(inflow)
|
|
if err != nil {
|
|
return num, fmt.Errorf("Could not inflow %s: %w", inflow, err)
|
|
}
|
|
return num, nil
|
|
}
|
|
|
|
func (ynab *YNABImport) GetAccount(name string) (*postgres.Account, error) {
|
|
for _, acc := range ynab.accounts {
|
|
if acc.Name == name {
|
|
return &acc, nil
|
|
}
|
|
}
|
|
|
|
account, err := ynab.queries.CreateAccount(ynab.Context, postgres.CreateAccountParams{Name: name, BudgetID: ynab.budgetID})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ynab.accounts = append(ynab.accounts, account)
|
|
return &account, nil
|
|
}
|
|
|
|
func (ynab *YNABImport) GetPayee(name string) (uuid.NullUUID, error) {
|
|
if name == "" {
|
|
return uuid.NullUUID{}, nil
|
|
}
|
|
|
|
for _, pay := range ynab.payees {
|
|
if pay.Name == name {
|
|
return uuid.NullUUID{UUID: pay.ID, Valid: true}, nil
|
|
}
|
|
}
|
|
|
|
payee, err := ynab.queries.CreatePayee(ynab.Context, postgres.CreatePayeeParams{Name: name, BudgetID: ynab.budgetID})
|
|
if err != nil {
|
|
return uuid.NullUUID{}, err
|
|
}
|
|
|
|
ynab.payees = append(ynab.payees, payee)
|
|
return uuid.NullUUID{UUID: payee.ID, Valid: true}, nil
|
|
}
|