diff --git a/postgres/queries/transactions.sql b/postgres/queries/transactions.sql index e39e3e2..622b8cb 100644 --- a/postgres/queries/transactions.sql +++ b/postgres/queries/transactions.sql @@ -61,4 +61,13 @@ LEFT JOIn accounts AS otherGroupAccount WHERE transactions.category_id IS NULL AND accounts.on_budget AND (otherGroupAccount.id IS NULL OR NOT otherGroupAccount.on_budget) -AND accounts.budget_id = $1; \ No newline at end of file +AND accounts.budget_id = $1; + +-- name: GetFilteredTransactions :many +SELECT transactions.* +FROM display_transactions AS transactions +WHERE (NOT @filter_category::boolean OR transactions.category_id = @category_id) +AND (NOT @filter_account::boolean OR transactions.account_id = @account_id) +AND (NOT @filter_payee::boolean OR transactions.payee_id = @payee_id) +AND transactions.date BETWEEN @from_date AND @to_date +AND transactions.budget_id = @budget_id; \ No newline at end of file diff --git a/postgres/transactions.sql.go b/postgres/transactions.sql.go index f110a70..2986f69 100644 --- a/postgres/transactions.sql.go +++ b/postgres/transactions.sql.go @@ -117,6 +117,77 @@ func (q *Queries) GetAllTransactionsForBudget(ctx context.Context, budgetID uuid return items, nil } +const getFilteredTransactions = `-- name: GetFilteredTransactions :many +SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, transactions.account, transactions.payee_id, transactions.category_id, transactions.payee, transactions.category_group, transactions.category, transactions.transfer_account, transactions.budget_id, transactions.account_id +FROM display_transactions AS transactions +WHERE (NOT $1::boolean OR transactions.category_id = $2) +AND (NOT $3::boolean OR transactions.account_id = $4) +AND (NOT $5::boolean OR transactions.payee_id = $6) +AND transactions.date BETWEEN $7 AND $8 +AND transactions.budget_id = $9 +` + +type GetFilteredTransactionsParams struct { + FilterCategory bool + CategoryID uuid.NullUUID + FilterAccount bool + AccountID uuid.UUID + FilterPayee bool + PayeeID uuid.NullUUID + FromDate time.Time + ToDate time.Time + BudgetID uuid.UUID +} + +func (q *Queries) GetFilteredTransactions(ctx context.Context, arg GetFilteredTransactionsParams) ([]DisplayTransaction, error) { + rows, err := q.db.QueryContext(ctx, getFilteredTransactions, + arg.FilterCategory, + arg.CategoryID, + arg.FilterAccount, + arg.AccountID, + arg.FilterPayee, + arg.PayeeID, + arg.FromDate, + arg.ToDate, + arg.BudgetID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []DisplayTransaction + for rows.Next() { + var i DisplayTransaction + if err := rows.Scan( + &i.ID, + &i.Date, + &i.Memo, + &i.Amount, + &i.GroupID, + &i.Status, + &i.Account, + &i.PayeeID, + &i.CategoryID, + &i.Payee, + &i.CategoryGroup, + &i.Category, + &i.TransferAccount, + &i.BudgetID, + &i.AccountID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getProblematicTransactions = `-- name: GetProblematicTransactions :many SELECT transactions.id, transactions.date, transactions.memo, transactions.amount, transactions.group_id, transactions.status, transactions.account, transactions.payee_id, transactions.category_id, transactions.payee, transactions.category_group, transactions.category, transactions.transfer_account, transactions.budget_id, transactions.account_id FROM display_transactions AS transactions diff --git a/server/account.go b/server/account.go index 758ada8..d1e9eae 100644 --- a/server/account.go +++ b/server/account.go @@ -2,12 +2,68 @@ package server import ( "net/http" + "time" "git.javil.eu/jacob1123/budgeteer/postgres" "github.com/gin-gonic/gin" "github.com/google/uuid" ) +type FilterTransactionsRequest struct { + CategoryID string `json:"category_id"` + PayeeID string `json:"payee_id"` + AccountID string `json:"account_id"` + FromDate time.Time `json:"from_date"` + ToDate time.Time `json:"to_date"` +} + +func (h *Handler) filteredTransactions(c *gin.Context) { + budgetID := c.Param("budgetid") + budgetUUID, err := uuid.Parse(budgetID) + if err != nil { + c.AbortWithError(http.StatusBadRequest, err) + return + } + + var request FilterTransactionsRequest + err = c.BindJSON(&request) + if err != nil { + c.AbortWithError(http.StatusBadRequest, err) + return + } + + params := postgres.GetFilteredTransactionsParams{ + BudgetID: budgetUUID, + FromDate: request.FromDate, + ToDate: request.ToDate, + } + params.CategoryID, params.FilterCategory = parseEmptyUUID(request.CategoryID) + accountID, filterAccount := parseEmptyUUID(request.AccountID) + params.AccountID, params.FilterAccount = accountID.UUID, filterAccount + params.PayeeID, params.FilterPayee = parseEmptyUUID(request.PayeeID) + + transactions, err := h.Service.GetFilteredTransactions(c.Request.Context(), params) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, TransactionsResponse{nil, transactions}) +} + +func parseEmptyUUID(value string) (uuid.NullUUID, bool) { + if value == "" { + return uuid.NullUUID{}, false + } + + val, err := uuid.Parse(value) + if err != nil { + return uuid.NullUUID{}, false + } + + return uuid.NullUUID{val, true}, true +} + func (h *Handler) problematicTransactions(c *gin.Context) { budgetID := c.Param("budgetid") budgetUUID, err := uuid.Parse(budgetID) diff --git a/server/http.go b/server/http.go index de5f7c8..34bcf0a 100644 --- a/server/http.go +++ b/server/http.go @@ -68,6 +68,7 @@ func (h *Handler) LoadRoutes(router *gin.Engine) { budget.GET("/:budgetid/autocomplete/accounts", h.autocompleteAccounts) budget.GET("/:budgetid/autocomplete/categories", h.autocompleteCategories) budget.GET("/:budgetid/problematic-transactions", h.problematicTransactions) + budget.POST("/:budgetid/filtered-transactions", h.filteredTransactions) budget.DELETE("/:budgetid", h.deleteBudget) budget.POST("/:budgetid/import/ynab", h.importYNAB) budget.POST("/:budgetid/export/ynab/transactions", h.exportYNABTransactions) diff --git a/web/src/components/Input.vue b/web/src/components/Input.vue index e810ba1..41b1a73 100644 --- a/web/src/components/Input.vue +++ b/web/src/components/Input.vue @@ -15,7 +15,6 @@ function valueChanged(e: Event) { emits('update:modelValue', target.valueAsNumber); break; default: - console.log("STR-INPUT", props.type) emits('update:modelValue', target.value) break; } diff --git a/web/src/pages/AllAccounts.vue b/web/src/pages/AllAccounts.vue index 2b35684..f1c9396 100644 --- a/web/src/pages/AllAccounts.vue +++ b/web/src/pages/AllAccounts.vue @@ -1,5 +1,5 @@