diff --git a/postgres/accounts.sql.go b/postgres/accounts.sql.go index fa5ee92..31f9aa8 100644 --- a/postgres/accounts.sql.go +++ b/postgres/accounts.sql.go @@ -130,7 +130,7 @@ func (q *Queries) GetAccountsWithBalance(ctx context.Context, budgetID uuid.UUID } const searchAccounts = `-- name: SearchAccounts :many -SELECT accounts.id, accounts.budget_id, accounts.name, true as is_account FROM accounts +SELECT accounts.id, accounts.budget_id, accounts.name, 'account' as type FROM accounts WHERE accounts.budget_id = $1 AND accounts.name LIKE $2 ORDER BY accounts.name @@ -142,10 +142,10 @@ type SearchAccountsParams struct { } type SearchAccountsRow struct { - ID uuid.UUID - BudgetID uuid.UUID - Name string - IsAccount bool + ID uuid.UUID + BudgetID uuid.UUID + Name string + Type interface{} } func (q *Queries) SearchAccounts(ctx context.Context, arg SearchAccountsParams) ([]SearchAccountsRow, error) { @@ -161,7 +161,7 @@ func (q *Queries) SearchAccounts(ctx context.Context, arg SearchAccountsParams) &i.ID, &i.BudgetID, &i.Name, - &i.IsAccount, + &i.Type, ); err != nil { return nil, err } diff --git a/postgres/categories.sql.go b/postgres/categories.sql.go index 7ddb062..fbf295e 100644 --- a/postgres/categories.sql.go +++ b/postgres/categories.sql.go @@ -118,7 +118,8 @@ func (q *Queries) GetCategoryGroups(ctx context.Context, budgetID uuid.UUID) ([] } const searchCategories = `-- name: SearchCategories :many -SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id FROM categories +SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id, 'category' as type +FROM categories INNER JOIN category_groups ON categories.category_group_id = category_groups.id WHERE category_groups.budget_id = $1 AND categories.name LIKE $2 @@ -133,6 +134,7 @@ type SearchCategoriesParams struct { type SearchCategoriesRow struct { Name interface{} ID uuid.UUID + Type interface{} } func (q *Queries) SearchCategories(ctx context.Context, arg SearchCategoriesParams) ([]SearchCategoriesRow, error) { @@ -144,7 +146,7 @@ func (q *Queries) SearchCategories(ctx context.Context, arg SearchCategoriesPara var items []SearchCategoriesRow for rows.Next() { var i SearchCategoriesRow - if err := rows.Scan(&i.Name, &i.ID); err != nil { + if err := rows.Scan(&i.Name, &i.ID, &i.Type); err != nil { return nil, err } items = append(items, i) diff --git a/postgres/payees.sql.go b/postgres/payees.sql.go index 8e39497..446097f 100644 --- a/postgres/payees.sql.go +++ b/postgres/payees.sql.go @@ -58,7 +58,7 @@ func (q *Queries) GetPayees(ctx context.Context, budgetID uuid.UUID) ([]Payee, e } const searchPayees = `-- name: SearchPayees :many -SELECT payees.id, payees.budget_id, payees.name FROM payees +SELECT payees.id, payees.budget_id, payees.name, 'payee' as type FROM payees WHERE payees.budget_id = $1 AND payees.name LIKE $2 ORDER BY payees.name @@ -69,16 +69,28 @@ type SearchPayeesParams struct { Search string } -func (q *Queries) SearchPayees(ctx context.Context, arg SearchPayeesParams) ([]Payee, error) { +type SearchPayeesRow struct { + ID uuid.UUID + BudgetID uuid.UUID + Name string + Type interface{} +} + +func (q *Queries) SearchPayees(ctx context.Context, arg SearchPayeesParams) ([]SearchPayeesRow, error) { rows, err := q.db.QueryContext(ctx, searchPayees, arg.BudgetID, arg.Search) if err != nil { return nil, err } defer rows.Close() - var items []Payee + var items []SearchPayeesRow for rows.Next() { - var i Payee - if err := rows.Scan(&i.ID, &i.BudgetID, &i.Name); err != nil { + var i SearchPayeesRow + if err := rows.Scan( + &i.ID, + &i.BudgetID, + &i.Name, + &i.Type, + ); err != nil { return nil, err } items = append(items, i) diff --git a/postgres/queries/accounts.sql b/postgres/queries/accounts.sql index 97e581b..ac291d5 100644 --- a/postgres/queries/accounts.sql +++ b/postgres/queries/accounts.sql @@ -22,7 +22,7 @@ GROUP BY accounts.id, accounts.name ORDER BY accounts.name; -- name: SearchAccounts :many -SELECT accounts.id, accounts.budget_id, accounts.name, true as is_account FROM accounts +SELECT accounts.id, accounts.budget_id, accounts.name, 'account' as type FROM accounts WHERE accounts.budget_id = @budget_id AND accounts.name LIKE @search ORDER BY accounts.name; diff --git a/postgres/queries/categories.sql b/postgres/queries/categories.sql index 2ff245b..fb507af 100644 --- a/postgres/queries/categories.sql +++ b/postgres/queries/categories.sql @@ -21,7 +21,8 @@ WHERE category_groups.budget_id = $1 ORDER BY category_groups.name, categories.name; -- name: SearchCategories :many -SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id FROM categories +SELECT CONCAT(category_groups.name, ' : ', categories.name) as name, categories.id, 'category' as type +FROM categories INNER JOIN category_groups ON categories.category_group_id = category_groups.id WHERE category_groups.budget_id = @budget_id AND categories.name LIKE @search diff --git a/postgres/queries/payees.sql b/postgres/queries/payees.sql index b82e9b1..ed38a5f 100644 --- a/postgres/queries/payees.sql +++ b/postgres/queries/payees.sql @@ -10,7 +10,7 @@ WHERE payees.budget_id = $1 ORDER BY name; -- name: SearchPayees :many -SELECT payees.* FROM payees +SELECT payees.*, 'payee' as type FROM payees WHERE payees.budget_id = @budget_id AND payees.name LIKE @search ORDER BY payees.name; diff --git a/postgres/queries/transactions.sql b/postgres/queries/transactions.sql index 8ad3504..df751e7 100644 --- a/postgres/queries/transactions.sql +++ b/postgres/queries/transactions.sql @@ -13,10 +13,9 @@ UPDATE transactions SET date = $1, memo = $2, amount = $3, - account_id = $4, - payee_id = $5, - category_id = $6 -WHERE id = $7; + payee_id = $4, + category_id = $5 +WHERE id = $6; -- name: DeleteTransaction :exec DELETE FROM transactions @@ -25,7 +24,7 @@ WHERE id = $1; -- name: GetAllTransactionsForBudget :many SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, - accounts.name as account, + accounts.name as account, transactions.payee_id, transactions.category_id, COALESCE(payees.name, '') as payee, COALESCE(category_groups.name, '') as category_group, COALESCE(categories.name, '') as category, @@ -47,7 +46,7 @@ ORDER BY transactions.date DESC; -- name: GetTransactionsForAccount :many SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, - accounts.name as account, + accounts.name as account, transactions.payee_id, transactions.category_id, COALESCE(payees.name, '') as payee, COALESCE(category_groups.name, '') as category_group, COALESCE(categories.name, '') as category, diff --git a/postgres/transactions.sql.go b/postgres/transactions.sql.go index e9bf8f8..1f02819 100644 --- a/postgres/transactions.sql.go +++ b/postgres/transactions.sql.go @@ -83,7 +83,7 @@ func (q *Queries) DeleteTransaction(ctx context.Context, id uuid.UUID) error { const getAllTransactionsForBudget = `-- name: GetAllTransactionsForBudget :many SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, - accounts.name as account, + accounts.name as account, transactions.payee_id, transactions.category_id, COALESCE(payees.name, '') as payee, COALESCE(category_groups.name, '') as category_group, COALESCE(categories.name, '') as category, @@ -111,6 +111,8 @@ type GetAllTransactionsForBudgetRow struct { GroupID uuid.NullUUID Status TransactionStatus Account string + PayeeID uuid.NullUUID + CategoryID uuid.NullUUID Payee string CategoryGroup string Category string @@ -134,6 +136,8 @@ func (q *Queries) GetAllTransactionsForBudget(ctx context.Context, budgetID uuid &i.GroupID, &i.Status, &i.Account, + &i.PayeeID, + &i.CategoryID, &i.Payee, &i.CategoryGroup, &i.Category, @@ -211,7 +215,7 @@ func (q *Queries) GetTransactionsByMonthAndCategory(ctx context.Context, budgetI const getTransactionsForAccount = `-- name: GetTransactionsForAccount :many SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, - accounts.name as account, + accounts.name as account, transactions.payee_id, transactions.category_id, COALESCE(payees.name, '') as payee, COALESCE(category_groups.name, '') as category_group, COALESCE(categories.name, '') as category, @@ -240,6 +244,8 @@ type GetTransactionsForAccountRow struct { GroupID uuid.NullUUID Status TransactionStatus Account string + PayeeID uuid.NullUUID + CategoryID uuid.NullUUID Payee string CategoryGroup string Category string @@ -263,6 +269,8 @@ func (q *Queries) GetTransactionsForAccount(ctx context.Context, accountID uuid. &i.GroupID, &i.Status, &i.Account, + &i.PayeeID, + &i.CategoryID, &i.Payee, &i.CategoryGroup, &i.Category, @@ -286,17 +294,15 @@ UPDATE transactions SET date = $1, memo = $2, amount = $3, - account_id = $4, - payee_id = $5, - category_id = $6 -WHERE id = $7 + payee_id = $4, + category_id = $5 +WHERE id = $6 ` type UpdateTransactionParams struct { Date time.Time Memo string Amount numeric.Numeric - AccountID uuid.UUID PayeeID uuid.NullUUID CategoryID uuid.NullUUID ID uuid.UUID @@ -307,7 +313,6 @@ func (q *Queries) UpdateTransaction(ctx context.Context, arg UpdateTransactionPa arg.Date, arg.Memo, arg.Amount, - arg.AccountID, arg.PayeeID, arg.CategoryID, arg.ID, diff --git a/server/transaction.go b/server/transaction.go index 979614b..08917d7 100644 --- a/server/transaction.go +++ b/server/transaction.go @@ -15,19 +15,16 @@ import ( type NewTransactionPayload struct { Date JSONDate `json:"date"` Payee struct { - ID uuid.NullUUID - Name string - IsAccount bool - } `json:"payee"` - Category struct { ID uuid.NullUUID Name string - } `json:"category"` - Memo string `json:"memo"` - Amount string `json:"amount"` - BudgetID uuid.UUID `json:"budgetId"` - AccountID uuid.UUID `json:"accountId"` - State string `json:"state"` + Type string + } `json:"payee"` + CategoryID uuid.NullUUID `json:"categoryId"` + Memo string `json:"memo"` + Amount string `json:"amount"` + BudgetID uuid.UUID `json:"budgetId"` + AccountID uuid.UUID `json:"accountId"` + State string `json:"state"` } func (h *Handler) newTransaction(c *gin.Context) { @@ -44,26 +41,27 @@ func (h *Handler) newTransaction(c *gin.Context) { return } - newTransaction := postgres.CreateTransactionParams{ - Memo: payload.Memo, - Date: time.Time(payload.Date), - Amount: amount, - Status: postgres.TransactionStatus(payload.State), + transactionID := c.Param("transactionid") + if transactionID != "" { + h.UpdateTransaction(payload, amount, transactionID, c) + return } - if payload.Payee.IsAccount { - newTransaction.GroupID = uuid.NullUUID{UUID: uuid.New(), Valid: true} - newTransaction.Amount = amount.Neg() - newTransaction.AccountID = payload.Payee.ID.UUID - newTransaction.CategoryID = uuid.NullUUID{} + newTransaction := postgres.CreateTransactionParams{ + Memo: payload.Memo, + Date: time.Time(payload.Date), + Amount: amount, + Status: postgres.TransactionStatus(payload.State), + CategoryID: payload.CategoryID, + AccountID: payload.AccountID, + } - _, err = h.Service.CreateTransaction(c.Request.Context(), newTransaction) + if payload.Payee.Type == "account" { + err := h.CreateTransferForOtherAccount(newTransaction, amount, payload, c) if err != nil { - c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transfer transaction: %w", err)) + c.AbortWithError(http.StatusInternalServerError, err) return } - - newTransaction.Amount = amount } else { payeeID, err := GetPayeeID(c.Request.Context(), payload, h) if err != nil { @@ -72,17 +70,54 @@ func (h *Handler) newTransaction(c *gin.Context) { newTransaction.PayeeID = payeeID } - newTransaction.CategoryID = payload.Category.ID - newTransaction.AccountID = payload.AccountID transaction, err := h.Service.CreateTransaction(c.Request.Context(), newTransaction) if err != nil { c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transaction: %w", err)) return } - c.JSON(http.StatusOK, transaction) } +func (h *Handler) UpdateTransaction(payload NewTransactionPayload, amount numeric.Numeric, transactionID string, c *gin.Context) { + transactionUUID := uuid.MustParse(transactionID) + if amount.IsZero() { + err := h.Service.DeleteTransaction(c.Request.Context(), transactionUUID) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("delete transaction: %w", err)) + } + return + } + + editTransaction := postgres.UpdateTransactionParams{ + Memo: payload.Memo, + Date: time.Time(payload.Date), + Amount: amount, + PayeeID: payload.Payee.ID, + CategoryID: payload.CategoryID, + ID: transactionUUID, + } + + err := h.Service.UpdateTransaction(c.Request.Context(), editTransaction) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("edit transaction: %w", err)) + } +} + +func (h *Handler) CreateTransferForOtherAccount(newTransaction postgres.CreateTransactionParams, amount numeric.Numeric, payload NewTransactionPayload, c *gin.Context) error { + newTransaction.GroupID = uuid.NullUUID{UUID: uuid.New(), Valid: true} + newTransaction.Amount = amount.Neg() + newTransaction.AccountID = payload.Payee.ID.UUID + + // transfer does not need category. Either it's account is off-budget or no category was supplied. + newTransaction.CategoryID = uuid.NullUUID{} + + _, err := h.Service.CreateTransaction(c.Request.Context(), newTransaction) + if err != nil { + return fmt.Errorf("create transfer transaction: %w", err) + } + return nil +} + func GetPayeeID(context context.Context, payload NewTransactionPayload, h *Handler) (uuid.NullUUID, error) { payeeID := payload.Payee.ID if payeeID.Valid { diff --git a/web/src/components/Autocomplete.vue b/web/src/components/Autocomplete.vue index e305b67..5568a60 100644 --- a/web/src/components/Autocomplete.vue +++ b/web/src/components/Autocomplete.vue @@ -1,43 +1,38 @@ @@ -84,10 +80,10 @@ function clear() { - {{ Selected.Name }} + {{ text }}