package dal import ( "database/sql" "errors" "fmt" "log" "strings" "git.rosemyrtle.work/personal-finance/server/internal/entity" ) type DalImpl struct { Db *sql.DB } func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error) { log.Printf("DAL::Transaction(%v)", transactionId) if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT t.id, t.date, t.description, t.amount, tc.name", "FROM pfbudget.transactions t", "LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id", "WHERE t.id = $1", } stmt := strings.Join(stmts, "\n") + "\n" rows, err := dal.Db.Query(stmt, transactionId) if err != nil { return nil, err } transactions := convert[entity.Transaction](rows) if len(transactions) == 0 { return nil, nil } return &transactions[0], nil } func (dal *DalImpl) Transactions(limit, offset int, category *string) (entity.Transactions, error) { log.Print("DAL::Transactions", "limit", limit, "offset", offset, "category", category) if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT t.id, t.date, t.description, t.amount, tc.name", "FROM pfbudget.transactions t", "LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id", } args := []any{limit, offset} if category != nil { stmts = append(stmts, "WHERE tc.name SIMILAR TO '%' || $3 || '%'") args = append(args, *category) } stmts = append(stmts, "ORDER BY t.date DESC", "LIMIT $1", "OFFSET $2", ) stmt := strings.Join(stmts, "\n") + "\n" log.Printf("DAL::Transactions::stmt: %s", stmt) rows, err := dal.Db.Query(stmt, args...) if err != nil { return entity.Transactions{}, err } return convert[entity.Transaction](rows), nil } func (dal *DalImpl) InsertTransaction(t entity.Transaction) (entity.Transaction, error) { log.Print("DAL::InsertTransaction") if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "INSERT INTO pfbudget.transactions (date, description, amount)", "VALUES ($1, $2, $3)", "RETURNING id", } stmt := strings.Join(stmts, "\n") + "\n" id := new(uint64) if err := dal.Db.QueryRow(stmt, t.Date, t.Description, t.Value).Scan(id); err != nil { return entity.Transaction{}, err } t.Id = *id return t, nil } func (dal *DalImpl) UpdateTransaction(id entity.TransactionId, category *entity.CategoryName) (bool, error) { log.Print("DAL::UpdateTransaction") if dal.Db == nil { log.Panic("database not available") } if category == nil { return false, errors.New("missing category") } stmts := []string{ "UPDATE pfbudget.transactions_categorized", "SET name = $2", "WHERE id = $1", } stmt := strings.Join(stmts, "\n") + "\n" result, err := dal.Db.Exec(stmt, id, *category) if err != nil { return false, err } nAffected, err := result.RowsAffected() if err != nil { return false, err } // TODO: find if this value can be different than 1, otherwise the func return can only be error if nAffected != 1 { return false, fmt.Errorf("%d rows affected", nAffected) } return true, nil } func (dal *DalImpl) TransactionExists(id uint64) (bool, error) { log.Print("DAL::TransactionExists") if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT EXISTS(", " SELECT 1", " FROM pfbudget.transactions", " WHERE id = $1", ")", } stmt := strings.Join(stmts, "\n") + "\n" exists := new(bool) err := dal.Db.QueryRow(stmt, id).Scan(&exists) if err != nil { return false, err } return *exists, nil } func (dal *DalImpl) Bank(bankId string) (*entity.Bank, error) { log.Printf("DAL::Bank(%v)", bankId) if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT b.name, b.name, n.requisition_id", "FROM pfbudget.banks b", "JOIN pfbudget.banks_nordigen n ON b.name = n.name", "WHERE b.name = $1", } stmt := strings.Join(stmts, "\n") + "\n" rows, err := dal.Db.Query(stmt, bankId) if err != nil { return nil, err } banks := convert[entity.Bank](rows) if len(banks) == 0 { return nil, nil } return &banks[0], nil } func (dal *DalImpl) Banks() (entity.Banks, error) { log.Print("DAL::Banks") if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT b.name, b.name, n.requisition_id", "FROM pfbudget.banks b", "JOIN pfbudget.banks_nordigen n ON b.name = n.name", } stmt := strings.Join(stmts, "\n") + "\n" rows, err := dal.Db.Query(stmt) if err != nil { return entity.Banks{}, err } return convert[entity.Bank](rows), nil } func (dal *DalImpl) Categories() (entity.Categories, error) { log.Print("DAL::Categories") if dal.Db == nil { log.Panic("database not available") } stmts := []string{ "SELECT c.name, c.group", "FROM pfbudget.categories c", } stmt := strings.Join(stmts, "\n") + "\n" rows, err := dal.Db.Query(stmt) if err != nil { return []entity.Category{}, err } return convert[entity.Category](rows), nil }