diff --git a/postgres/conn.go b/postgres/conn.go index 046e127..6a5c814 100644 --- a/postgres/conn.go +++ b/postgres/conn.go @@ -5,7 +5,7 @@ import ( "embed" "fmt" - _ "github.com/jackc/pgx/v4/stdlib" + _ "github.com/jackc/pgx/v4/stdlib" // needed for pg connection "github.com/pressly/goose/v3" ) diff --git a/postgres/ynab-import.go b/postgres/ynab-import.go index 1438659..0ca9ba7 100644 --- a/postgres/ynab-import.go +++ b/postgres/ynab-import.go @@ -54,8 +54,8 @@ func NewYNABImport(context context.Context, q *Queries, budgetID uuid.UUID) (*YN } // 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€ +// "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 *YNABImport) ImportAssignments(context context.Context, r io.Reader) error { @@ -70,14 +70,13 @@ func (ynab *YNABImport) ImportAssignments(context context.Context, r io.Reader) count := 0 for _, record := range csvData[1:] { - dateString := record[0] date, err := time.Parse("Jan 2006", dateString) if err != nil { return fmt.Errorf("parse date %s: %w", dateString, err) } - categoryGroup, categoryName := record[2], record[3] //also in 1 joined by : + categoryGroup, categoryName := record[2], record[3] // also in 1 joined by : category, err := ynab.GetCategory(context, categoryGroup, categoryName) if err != nil { return fmt.Errorf("get category %s/%s: %w", categoryGroup, categoryName, err) @@ -140,7 +139,7 @@ func (ynab *YNABImport) ImportTransactions(context context.Context, r io.Reader) return fmt.Errorf("get account %s: %w", accountName, err) } - //flag := record[1] + // flag := record[1] dateString := record[2] date, err := time.Parse("02.01.2006", dateString) @@ -183,85 +182,96 @@ func (ynab *YNABImport) ImportTransactions(context context.Context, r io.Reader) } payeeName := record[3] - if strings.HasPrefix(payeeName, "Transfer : ") { - // Transaction is a transfer to - transferToAccountName := payeeName[11:] - transferToAccount, err := ynab.GetAccount(context, transferToAccountName) - if err != nil { - return fmt.Errorf("get transfer account %s: %w", transferToAccountName, err) - } - - transfer := Transfer{ - transaction, - transferToAccount, - accountName, - transferToAccountName, - } - - found := false - for i, openTransfer := range openTransfers { - if openTransfer.TransferToAccount.ID != transfer.AccountID { - continue - } - if openTransfer.AccountID != transfer.TransferToAccount.ID { - continue - } - if openTransfer.Amount.GetFloat64() != -1*transfer.Amount.GetFloat64() { - continue - } - - fmt.Printf("Matched transfers from %s to %s over %f\n", account.Name, transferToAccount.Name, amount.GetFloat64()) - openTransfers[i] = openTransfers[len(openTransfers)-1] - openTransfers = openTransfers[:len(openTransfers)-1] - found = true - - groupID := uuid.New() - transfer.GroupID = uuid.NullUUID{UUID: groupID, Valid: true} - openTransfer.GroupID = uuid.NullUUID{UUID: groupID, Valid: true} - - _, err = ynab.queries.CreateTransaction(context, transfer.CreateTransactionParams) - if err != nil { - return fmt.Errorf("save transaction %v: %w", transfer.CreateTransactionParams, err) - } - _, err = ynab.queries.CreateTransaction(context, openTransfer.CreateTransactionParams) - if err != nil { - return fmt.Errorf("save transaction %v: %w", openTransfer.CreateTransactionParams, err) - } - break - } - - if !found { - openTransfers = append(openTransfers, transfer) - } - } else { - payeeID, err := ynab.GetPayee(context, payeeName) - if err != nil { - return fmt.Errorf("get payee %s: %w", payeeName, err) - } - transaction.PayeeID = payeeID - - _, err = ynab.queries.CreateTransaction(context, transaction) - if err != nil { - return fmt.Errorf("save transaction %v: %w", transaction, err) - } + // Transaction is a transfer to + var shouldReturn bool + var returnValue error + openTransfers, shouldReturn, returnValue = ynab.ImportTransaction(payeeName, context, transaction, accountName, openTransfers, account, amount) + if shouldReturn { + return returnValue } count++ } for _, openTransfer := range openTransfers { - fmt.Printf("Saving unmatched transfer from %s to %s on %s over %f as regular transaction\n", openTransfer.FromAccount, openTransfer.ToAccount, openTransfer.Date, openTransfer.Amount.GetFloat64()) + fmt.Printf("Saving unmatched transfer from %s to %s on %s over %f as regular transaction\n", + openTransfer.FromAccount, openTransfer.ToAccount, openTransfer.Date, openTransfer.Amount.GetFloat64()) _, err = ynab.queries.CreateTransaction(context, openTransfer.CreateTransactionParams) if err != nil { return fmt.Errorf("save transaction %v: %w", openTransfer.CreateTransactionParams, err) } - } + fmt.Printf("Imported %d transactions\n", count) return nil } +func (ynab *YNABImport) ImportTransaction(payeeName string, context context.Context, transaction CreateTransactionParams, accountName string, openTransfers []Transfer, account *Account, amount Numeric) ([]Transfer, bool, error) { + if strings.HasPrefix(payeeName, "Transfer : ") { + transferToAccountName := payeeName[11:] + transferToAccount, err := ynab.GetAccount(context, transferToAccountName) + if err != nil { + return nil, true, fmt.Errorf("get transfer account %s: %w", transferToAccountName, err) + } + + transfer := Transfer{ + transaction, + transferToAccount, + accountName, + transferToAccountName, + } + + found := false + for i, openTransfer := range openTransfers { + if openTransfer.TransferToAccount.ID != transfer.AccountID { + continue + } + if openTransfer.AccountID != transfer.TransferToAccount.ID { + continue + } + if openTransfer.Amount.GetFloat64() != -1*transfer.Amount.GetFloat64() { + continue + } + + fmt.Printf("Matched transfers from %s to %s over %f\n", account.Name, transferToAccount.Name, amount.GetFloat64()) + openTransfers[i] = openTransfers[len(openTransfers)-1] + openTransfers = openTransfers[:len(openTransfers)-1] + found = true + + groupID := uuid.New() + transfer.GroupID = uuid.NullUUID{UUID: groupID, Valid: true} + openTransfer.GroupID = uuid.NullUUID{UUID: groupID, Valid: true} + + _, err = ynab.queries.CreateTransaction(context, transfer.CreateTransactionParams) + if err != nil { + return nil, true, fmt.Errorf("save transaction %v: %w", transfer.CreateTransactionParams, err) + } + _, err = ynab.queries.CreateTransaction(context, openTransfer.CreateTransactionParams) + if err != nil { + return nil, true, fmt.Errorf("save transaction %v: %w", openTransfer.CreateTransactionParams, err) + } + break + } + + if !found { + openTransfers = append(openTransfers, transfer) + } + } else { + payeeID, err := ynab.GetPayee(context, payeeName) + if err != nil { + return nil, true, fmt.Errorf("get payee %s: %w", payeeName, err) + } + transaction.PayeeID = payeeID + + _, err = ynab.queries.CreateTransaction(context, transaction) + if err != nil { + return nil, true, fmt.Errorf("save transaction %v: %w", transaction, err) + } + } + return openTransfers, false, nil +} + func trimLastChar(s string) string { r, size := utf8.DecodeLastRuneInString(s) if r == utf8.RuneError && (size == 0 || size == 1) {