Continue on ynab import
This commit is contained in:
parent
7b6914e5f2
commit
1cd2eedcb8
39
http/http.go
39
http/http.go
@ -80,12 +80,51 @@ func (h *Handler) Serve() {
|
|||||||
{
|
{
|
||||||
transaction.Use(h.verifyLoginWithRedirect)
|
transaction.Use(h.verifyLoginWithRedirect)
|
||||||
transaction.POST("/new", h.newTransaction)
|
transaction.POST("/new", h.newTransaction)
|
||||||
|
transaction.POST("/import/ynab", h.importYNAB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.Run(":1323")
|
router.Run(":1323")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) importYNAB(c *gin.Context) {
|
||||||
|
transactionsFile, err := c.FormFile("transactions")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions, err := transactionsFile.Open()
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
budgetID, succ := c.GetPostForm("budget_id")
|
||||||
|
if !succ {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
budgetUUID, err := uuid.Parse(budgetID)
|
||||||
|
if !succ {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ynab, err := NewYNABImport(h.Service.DB, budgetUUID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ynab.Import(transactions)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) verifyLoginWithRedirect(c *gin.Context) {
|
func (h *Handler) verifyLoginWithRedirect(c *gin.Context) {
|
||||||
_, err := h.verifyLogin(c)
|
_, err := h.verifyLogin(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,11 +3,13 @@ package http
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.javil.eu/jacob1123/budgeteer/postgres"
|
"git.javil.eu/jacob1123/budgeteer/postgres"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YNABImport struct {
|
type YNABImport struct {
|
||||||
@ -15,9 +17,10 @@ type YNABImport struct {
|
|||||||
accounts []postgres.Account
|
accounts []postgres.Account
|
||||||
payees []postgres.Payee
|
payees []postgres.Payee
|
||||||
queries *postgres.Queries
|
queries *postgres.Queries
|
||||||
|
budgetID uuid.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYNABImport(q *postgres.Queries) (*YNABImport, error) {
|
func NewYNABImport(q *postgres.Queries, budgetID uuid.UUID) (*YNABImport, error) {
|
||||||
accounts, err := q.GetAccounts(context.Background(), uuid.UUID{})
|
accounts, err := q.GetAccounts(context.Background(), uuid.UUID{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -33,24 +36,26 @@ func NewYNABImport(q *postgres.Queries) (*YNABImport, error) {
|
|||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
payees: payees,
|
payees: payees,
|
||||||
queries: q,
|
queries: q,
|
||||||
|
budgetID: budgetID,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ynab *YNABImport) Import(q *postgres.Queries, r io.Reader) error {
|
func (ynab *YNABImport) Import(r io.Reader) error {
|
||||||
csv := csv.NewReader(r)
|
csv := csv.NewReader(r)
|
||||||
csv.Comma = '\t'
|
csv.Comma = '\t'
|
||||||
|
csv.LazyQuotes = true
|
||||||
|
|
||||||
csvData, err := csv.ReadAll()
|
csvData, err := csv.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not read from tsv: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, record := range csvData {
|
for _, record := range csvData {
|
||||||
accountName := record[0]
|
accountName := record[0]
|
||||||
account, err := ynab.GetAccount(accountName)
|
account, err := ynab.GetAccount(accountName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not get account %s: %w", accountName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//flag := record[1]
|
//flag := record[1]
|
||||||
@ -58,22 +63,45 @@ func (ynab *YNABImport) Import(q *postgres.Queries, r io.Reader) error {
|
|||||||
dateString := record[2]
|
dateString := record[2]
|
||||||
date, err := time.Parse("02.01.2006", dateString)
|
date, err := time.Parse("02.01.2006", dateString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not parse date %s: %w", dateString, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
payee := record[3]
|
payeeName := record[3]
|
||||||
category := record[4] //also in 5 + 6 split by group/category
|
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]
|
memo := record[7]
|
||||||
|
|
||||||
outflow := record[8]
|
outflow := record[8]
|
||||||
inflow := record[9]
|
inflow := record[9]
|
||||||
cleared := record[10]
|
amount := GetAmount(inflow, outflow)
|
||||||
|
|
||||||
transaction := postgres.Transaction{
|
//cleared := record[10]
|
||||||
|
|
||||||
|
transaction := postgres.CreateTransactionParams{
|
||||||
Date: date,
|
Date: date,
|
||||||
Memo: memo,
|
Memo: memo,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
|
PayeeID: payeeID,
|
||||||
|
Amount: amount,
|
||||||
}
|
}
|
||||||
|
ynab.queries.CreateTransaction(ynab.Context, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAmount(inflow string, outflow string) pgtype.Numeric {
|
||||||
|
// Remove currency
|
||||||
|
inflow = inflow[:len(inflow)-1]
|
||||||
|
outflow = outflow[:len(outflow)-1]
|
||||||
|
|
||||||
|
num := pgtype.Numeric{}
|
||||||
|
num.Set(inflow)
|
||||||
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ynab *YNABImport) GetAccount(name string) (*postgres.Account, error) {
|
func (ynab *YNABImport) GetAccount(name string) (*postgres.Account, error) {
|
||||||
@ -92,18 +120,22 @@ func (ynab *YNABImport) GetAccount(name string) (*postgres.Account, error) {
|
|||||||
return &account, nil
|
return &account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ynab *YNABImport) GetPayee(name string) (*postgres.Payee, error) {
|
func (ynab *YNABImport) GetPayee(name string) (uuid.NullUUID, error) {
|
||||||
|
if name == "" {
|
||||||
|
return uuid.NullUUID{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, pay := range ynab.payees {
|
for _, pay := range ynab.payees {
|
||||||
if pay.Name == name {
|
if pay.Name == name {
|
||||||
return &pay, nil
|
return uuid.NullUUID{UUID: pay.ID, Valid: true}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
payee, err := ynab.queries.CreatePayee(ynab.Context, name)
|
payee, err := ynab.queries.CreatePayee(ynab.Context, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return uuid.NullUUID{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ynab.payees = append(ynab.payees, payee)
|
ynab.payees = append(ynab.payees, payee)
|
||||||
return &payee, nil
|
return uuid.NullUUID{UUID: payee.ID, Valid: true}, nil
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,18 @@ import (
|
|||||||
|
|
||||||
const createAccount = `-- name: CreateAccount :one
|
const createAccount = `-- name: CreateAccount :one
|
||||||
INSERT INTO accounts
|
INSERT INTO accounts
|
||||||
(name)
|
(name, budget_id)
|
||||||
VALUES ($1)
|
VALUES ($1, $2)
|
||||||
RETURNING id, budget_id, name
|
RETURNING id, budget_id, name
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CreateAccount(ctx context.Context, name string) (Account, error) {
|
type CreateAccountParams struct {
|
||||||
row := q.db.QueryRow(ctx, createAccount, name)
|
Name string
|
||||||
|
BudgetID uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (Account, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createAccount, arg.Name, arg.BudgetID)
|
||||||
var i Account
|
var i Account
|
||||||
err := row.Scan(&i.ID, &i.BudgetID, &i.Name)
|
err := row.Scan(&i.ID, &i.BudgetID, &i.Name)
|
||||||
return i, err
|
return i, err
|
||||||
|
@ -11,13 +11,18 @@ import (
|
|||||||
|
|
||||||
const createPayee = `-- name: CreatePayee :one
|
const createPayee = `-- name: CreatePayee :one
|
||||||
INSERT INTO payees
|
INSERT INTO payees
|
||||||
(name)
|
(name, budget_id)
|
||||||
VALUES ($1)
|
VALUES ($1, $2)
|
||||||
RETURNING id, budget_id, name
|
RETURNING id, budget_id, name
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CreatePayee(ctx context.Context, name string) (Payee, error) {
|
type CreatePayeeParams struct {
|
||||||
row := q.db.QueryRow(ctx, createPayee, name)
|
Name string
|
||||||
|
BudgetID uuid.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreatePayee(ctx context.Context, arg CreatePayeeParams) (Payee, error) {
|
||||||
|
row := q.db.QueryRow(ctx, createPayee, arg.Name, arg.BudgetID)
|
||||||
var i Payee
|
var i Payee
|
||||||
err := row.Scan(&i.ID, &i.BudgetID, &i.Name)
|
err := row.Scan(&i.ID, &i.BudgetID, &i.Name)
|
||||||
return i, err
|
return i, err
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- name: CreateAccount :one
|
-- name: CreateAccount :one
|
||||||
INSERT INTO accounts
|
INSERT INTO accounts
|
||||||
(name)
|
(name, budget_id)
|
||||||
VALUES ($1)
|
VALUES ($1, $2)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: GetAccounts :many
|
-- name: GetAccounts :many
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- name: CreatePayee :one
|
-- name: CreatePayee :one
|
||||||
INSERT INTO payees
|
INSERT INTO payees
|
||||||
(name)
|
(name, budget_id)
|
||||||
VALUES ($1)
|
VALUES ($1, $2)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: GetPayees :many
|
-- name: GetPayees :many
|
||||||
|
Loading…
x
Reference in New Issue
Block a user