It is error-prone joining together string literals, since the separator between statements isn't clear. Instead, store statements on a string array, and combine them at the end.
234 lines
4.9 KiB
Go
234 lines
4.9 KiB
Go
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
|
|
}
|