Merge pull request 'Implement editing of transactions' (#23) from edit-transaction into master
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			Reviewed-on: #23
This commit was merged in pull request #23.
	This commit is contained in:
		@@ -130,7 +130,7 @@ func (q *Queries) GetAccountsWithBalance(ctx context.Context, budgetID uuid.UUID
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const searchAccounts = `-- name: SearchAccounts :many
 | 
					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
 | 
					WHERE accounts.budget_id = $1
 | 
				
			||||||
AND accounts.name LIKE $2
 | 
					AND accounts.name LIKE $2
 | 
				
			||||||
ORDER BY accounts.name
 | 
					ORDER BY accounts.name
 | 
				
			||||||
@@ -142,10 +142,10 @@ type SearchAccountsParams struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SearchAccountsRow struct {
 | 
					type SearchAccountsRow struct {
 | 
				
			||||||
	ID        uuid.UUID
 | 
						ID       uuid.UUID
 | 
				
			||||||
	BudgetID  uuid.UUID
 | 
						BudgetID uuid.UUID
 | 
				
			||||||
	Name      string
 | 
						Name     string
 | 
				
			||||||
	IsAccount bool
 | 
						Type     interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) SearchAccounts(ctx context.Context, arg SearchAccountsParams) ([]SearchAccountsRow, error) {
 | 
					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.ID,
 | 
				
			||||||
			&i.BudgetID,
 | 
								&i.BudgetID,
 | 
				
			||||||
			&i.Name,
 | 
								&i.Name,
 | 
				
			||||||
			&i.IsAccount,
 | 
								&i.Type,
 | 
				
			||||||
		); err != nil {
 | 
							); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -118,7 +118,8 @@ func (q *Queries) GetCategoryGroups(ctx context.Context, budgetID uuid.UUID) ([]
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const searchCategories = `-- name: SearchCategories :many
 | 
					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
 | 
					INNER JOIN category_groups ON categories.category_group_id = category_groups.id
 | 
				
			||||||
WHERE category_groups.budget_id = $1
 | 
					WHERE category_groups.budget_id = $1
 | 
				
			||||||
AND categories.name LIKE $2
 | 
					AND categories.name LIKE $2
 | 
				
			||||||
@@ -133,6 +134,7 @@ type SearchCategoriesParams struct {
 | 
				
			|||||||
type SearchCategoriesRow struct {
 | 
					type SearchCategoriesRow struct {
 | 
				
			||||||
	Name interface{}
 | 
						Name interface{}
 | 
				
			||||||
	ID   uuid.UUID
 | 
						ID   uuid.UUID
 | 
				
			||||||
 | 
						Type interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (q *Queries) SearchCategories(ctx context.Context, arg SearchCategoriesParams) ([]SearchCategoriesRow, error) {
 | 
					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
 | 
						var items []SearchCategoriesRow
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		var i SearchCategoriesRow
 | 
							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
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		items = append(items, i)
 | 
							items = append(items, i)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@ func (q *Queries) GetPayees(ctx context.Context, budgetID uuid.UUID) ([]Payee, e
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const searchPayees = `-- name: SearchPayees :many
 | 
					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
 | 
					WHERE payees.budget_id = $1
 | 
				
			||||||
AND payees.name LIKE $2
 | 
					AND payees.name LIKE $2
 | 
				
			||||||
ORDER BY payees.name
 | 
					ORDER BY payees.name
 | 
				
			||||||
@@ -69,16 +69,28 @@ type SearchPayeesParams struct {
 | 
				
			|||||||
	Search   string
 | 
						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)
 | 
						rows, err := q.db.QueryContext(ctx, searchPayees, arg.BudgetID, arg.Search)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer rows.Close()
 | 
						defer rows.Close()
 | 
				
			||||||
	var items []Payee
 | 
						var items []SearchPayeesRow
 | 
				
			||||||
	for rows.Next() {
 | 
						for rows.Next() {
 | 
				
			||||||
		var i Payee
 | 
							var i SearchPayeesRow
 | 
				
			||||||
		if err := rows.Scan(&i.ID, &i.BudgetID, &i.Name); err != nil {
 | 
							if err := rows.Scan(
 | 
				
			||||||
 | 
								&i.ID,
 | 
				
			||||||
 | 
								&i.BudgetID,
 | 
				
			||||||
 | 
								&i.Name,
 | 
				
			||||||
 | 
								&i.Type,
 | 
				
			||||||
 | 
							); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		items = append(items, i)
 | 
							items = append(items, i)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,7 @@ GROUP BY accounts.id, accounts.name
 | 
				
			|||||||
ORDER BY accounts.name;
 | 
					ORDER BY accounts.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: SearchAccounts :many
 | 
					-- 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
 | 
					WHERE accounts.budget_id = @budget_id
 | 
				
			||||||
AND accounts.name LIKE @search
 | 
					AND accounts.name LIKE @search
 | 
				
			||||||
ORDER BY accounts.name;
 | 
					ORDER BY accounts.name;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,8 @@ WHERE category_groups.budget_id = $1
 | 
				
			|||||||
ORDER BY category_groups.name, categories.name;
 | 
					ORDER BY category_groups.name, categories.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: SearchCategories :many
 | 
					-- 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
 | 
					INNER JOIN category_groups ON categories.category_group_id = category_groups.id
 | 
				
			||||||
WHERE category_groups.budget_id = @budget_id
 | 
					WHERE category_groups.budget_id = @budget_id
 | 
				
			||||||
AND categories.name LIKE @search
 | 
					AND categories.name LIKE @search
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ WHERE payees.budget_id = $1
 | 
				
			|||||||
ORDER BY name;
 | 
					ORDER BY name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: SearchPayees :many
 | 
					-- name: SearchPayees :many
 | 
				
			||||||
SELECT payees.* FROM payees 
 | 
					SELECT payees.*, 'payee' as type FROM payees 
 | 
				
			||||||
WHERE payees.budget_id = @budget_id
 | 
					WHERE payees.budget_id = @budget_id
 | 
				
			||||||
AND payees.name LIKE @search
 | 
					AND payees.name LIKE @search
 | 
				
			||||||
ORDER BY payees.name;
 | 
					ORDER BY payees.name;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,9 @@ UPDATE transactions
 | 
				
			|||||||
SET date = $1,
 | 
					SET date = $1,
 | 
				
			||||||
    memo = $2,
 | 
					    memo = $2,
 | 
				
			||||||
    amount = $3,
 | 
					    amount = $3,
 | 
				
			||||||
    account_id = $4,
 | 
					    payee_id = $4,
 | 
				
			||||||
    payee_id = $5,
 | 
					    category_id = $5
 | 
				
			||||||
    category_id = $6
 | 
					WHERE id = $6;
 | 
				
			||||||
WHERE id = $7;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- name: DeleteTransaction :exec
 | 
					-- name: DeleteTransaction :exec
 | 
				
			||||||
DELETE FROM transactions
 | 
					DELETE FROM transactions
 | 
				
			||||||
@@ -25,7 +24,7 @@ WHERE id = $1;
 | 
				
			|||||||
-- name: GetAllTransactionsForBudget :many
 | 
					-- name: GetAllTransactionsForBudget :many
 | 
				
			||||||
SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
					SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
				
			||||||
        transactions.amount, transactions.group_id, transactions.status,
 | 
					        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(payees.name, '') as payee, 
 | 
				
			||||||
        COALESCE(category_groups.name, '') as category_group, 
 | 
					        COALESCE(category_groups.name, '') as category_group, 
 | 
				
			||||||
        COALESCE(categories.name, '') as category,
 | 
					        COALESCE(categories.name, '') as category,
 | 
				
			||||||
@@ -47,7 +46,7 @@ ORDER BY transactions.date DESC;
 | 
				
			|||||||
-- name: GetTransactionsForAccount :many
 | 
					-- name: GetTransactionsForAccount :many
 | 
				
			||||||
SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
					SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
				
			||||||
        transactions.amount, transactions.group_id, transactions.status,
 | 
					        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(payees.name, '') as payee, 
 | 
				
			||||||
        COALESCE(category_groups.name, '') as category_group, 
 | 
					        COALESCE(category_groups.name, '') as category_group, 
 | 
				
			||||||
        COALESCE(categories.name, '') as category,
 | 
					        COALESCE(categories.name, '') as category,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ func (q *Queries) DeleteTransaction(ctx context.Context, id uuid.UUID) error {
 | 
				
			|||||||
const getAllTransactionsForBudget = `-- name: GetAllTransactionsForBudget :many
 | 
					const getAllTransactionsForBudget = `-- name: GetAllTransactionsForBudget :many
 | 
				
			||||||
SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
					SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
				
			||||||
        transactions.amount, transactions.group_id, transactions.status,
 | 
					        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(payees.name, '') as payee, 
 | 
				
			||||||
        COALESCE(category_groups.name, '') as category_group, 
 | 
					        COALESCE(category_groups.name, '') as category_group, 
 | 
				
			||||||
        COALESCE(categories.name, '') as category,
 | 
					        COALESCE(categories.name, '') as category,
 | 
				
			||||||
@@ -111,6 +111,8 @@ type GetAllTransactionsForBudgetRow struct {
 | 
				
			|||||||
	GroupID         uuid.NullUUID
 | 
						GroupID         uuid.NullUUID
 | 
				
			||||||
	Status          TransactionStatus
 | 
						Status          TransactionStatus
 | 
				
			||||||
	Account         string
 | 
						Account         string
 | 
				
			||||||
 | 
						PayeeID         uuid.NullUUID
 | 
				
			||||||
 | 
						CategoryID      uuid.NullUUID
 | 
				
			||||||
	Payee           string
 | 
						Payee           string
 | 
				
			||||||
	CategoryGroup   string
 | 
						CategoryGroup   string
 | 
				
			||||||
	Category        string
 | 
						Category        string
 | 
				
			||||||
@@ -134,6 +136,8 @@ func (q *Queries) GetAllTransactionsForBudget(ctx context.Context, budgetID uuid
 | 
				
			|||||||
			&i.GroupID,
 | 
								&i.GroupID,
 | 
				
			||||||
			&i.Status,
 | 
								&i.Status,
 | 
				
			||||||
			&i.Account,
 | 
								&i.Account,
 | 
				
			||||||
 | 
								&i.PayeeID,
 | 
				
			||||||
 | 
								&i.CategoryID,
 | 
				
			||||||
			&i.Payee,
 | 
								&i.Payee,
 | 
				
			||||||
			&i.CategoryGroup,
 | 
								&i.CategoryGroup,
 | 
				
			||||||
			&i.Category,
 | 
								&i.Category,
 | 
				
			||||||
@@ -211,7 +215,7 @@ func (q *Queries) GetTransactionsByMonthAndCategory(ctx context.Context, budgetI
 | 
				
			|||||||
const getTransactionsForAccount = `-- name: GetTransactionsForAccount :many
 | 
					const getTransactionsForAccount = `-- name: GetTransactionsForAccount :many
 | 
				
			||||||
SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
					SELECT  transactions.id, transactions.date, transactions.memo, 
 | 
				
			||||||
        transactions.amount, transactions.group_id, transactions.status,
 | 
					        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(payees.name, '') as payee, 
 | 
				
			||||||
        COALESCE(category_groups.name, '') as category_group, 
 | 
					        COALESCE(category_groups.name, '') as category_group, 
 | 
				
			||||||
        COALESCE(categories.name, '') as category,
 | 
					        COALESCE(categories.name, '') as category,
 | 
				
			||||||
@@ -240,6 +244,8 @@ type GetTransactionsForAccountRow struct {
 | 
				
			|||||||
	GroupID         uuid.NullUUID
 | 
						GroupID         uuid.NullUUID
 | 
				
			||||||
	Status          TransactionStatus
 | 
						Status          TransactionStatus
 | 
				
			||||||
	Account         string
 | 
						Account         string
 | 
				
			||||||
 | 
						PayeeID         uuid.NullUUID
 | 
				
			||||||
 | 
						CategoryID      uuid.NullUUID
 | 
				
			||||||
	Payee           string
 | 
						Payee           string
 | 
				
			||||||
	CategoryGroup   string
 | 
						CategoryGroup   string
 | 
				
			||||||
	Category        string
 | 
						Category        string
 | 
				
			||||||
@@ -263,6 +269,8 @@ func (q *Queries) GetTransactionsForAccount(ctx context.Context, accountID uuid.
 | 
				
			|||||||
			&i.GroupID,
 | 
								&i.GroupID,
 | 
				
			||||||
			&i.Status,
 | 
								&i.Status,
 | 
				
			||||||
			&i.Account,
 | 
								&i.Account,
 | 
				
			||||||
 | 
								&i.PayeeID,
 | 
				
			||||||
 | 
								&i.CategoryID,
 | 
				
			||||||
			&i.Payee,
 | 
								&i.Payee,
 | 
				
			||||||
			&i.CategoryGroup,
 | 
								&i.CategoryGroup,
 | 
				
			||||||
			&i.Category,
 | 
								&i.Category,
 | 
				
			||||||
@@ -286,17 +294,15 @@ UPDATE transactions
 | 
				
			|||||||
SET date = $1,
 | 
					SET date = $1,
 | 
				
			||||||
    memo = $2,
 | 
					    memo = $2,
 | 
				
			||||||
    amount = $3,
 | 
					    amount = $3,
 | 
				
			||||||
    account_id = $4,
 | 
					    payee_id = $4,
 | 
				
			||||||
    payee_id = $5,
 | 
					    category_id = $5
 | 
				
			||||||
    category_id = $6
 | 
					WHERE id = $6
 | 
				
			||||||
WHERE id = $7
 | 
					 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTransactionParams struct {
 | 
					type UpdateTransactionParams struct {
 | 
				
			||||||
	Date       time.Time
 | 
						Date       time.Time
 | 
				
			||||||
	Memo       string
 | 
						Memo       string
 | 
				
			||||||
	Amount     numeric.Numeric
 | 
						Amount     numeric.Numeric
 | 
				
			||||||
	AccountID  uuid.UUID
 | 
					 | 
				
			||||||
	PayeeID    uuid.NullUUID
 | 
						PayeeID    uuid.NullUUID
 | 
				
			||||||
	CategoryID uuid.NullUUID
 | 
						CategoryID uuid.NullUUID
 | 
				
			||||||
	ID         uuid.UUID
 | 
						ID         uuid.UUID
 | 
				
			||||||
@@ -307,7 +313,6 @@ func (q *Queries) UpdateTransaction(ctx context.Context, arg UpdateTransactionPa
 | 
				
			|||||||
		arg.Date,
 | 
							arg.Date,
 | 
				
			||||||
		arg.Memo,
 | 
							arg.Memo,
 | 
				
			||||||
		arg.Amount,
 | 
							arg.Amount,
 | 
				
			||||||
		arg.AccountID,
 | 
					 | 
				
			||||||
		arg.PayeeID,
 | 
							arg.PayeeID,
 | 
				
			||||||
		arg.CategoryID,
 | 
							arg.CategoryID,
 | 
				
			||||||
		arg.ID,
 | 
							arg.ID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,19 +15,16 @@ import (
 | 
				
			|||||||
type NewTransactionPayload struct {
 | 
					type NewTransactionPayload struct {
 | 
				
			||||||
	Date  JSONDate `json:"date"`
 | 
						Date  JSONDate `json:"date"`
 | 
				
			||||||
	Payee struct {
 | 
						Payee struct {
 | 
				
			||||||
		ID        uuid.NullUUID
 | 
					 | 
				
			||||||
		Name      string
 | 
					 | 
				
			||||||
		IsAccount bool
 | 
					 | 
				
			||||||
	} `json:"payee"`
 | 
					 | 
				
			||||||
	Category struct {
 | 
					 | 
				
			||||||
		ID   uuid.NullUUID
 | 
							ID   uuid.NullUUID
 | 
				
			||||||
		Name string
 | 
							Name string
 | 
				
			||||||
	} `json:"category"`
 | 
							Type string
 | 
				
			||||||
	Memo      string    `json:"memo"`
 | 
						} `json:"payee"`
 | 
				
			||||||
	Amount    string    `json:"amount"`
 | 
						CategoryID uuid.NullUUID `json:"categoryId"`
 | 
				
			||||||
	BudgetID  uuid.UUID `json:"budgetId"`
 | 
						Memo       string        `json:"memo"`
 | 
				
			||||||
	AccountID uuid.UUID `json:"accountId"`
 | 
						Amount     string        `json:"amount"`
 | 
				
			||||||
	State     string    `json:"state"`
 | 
						BudgetID   uuid.UUID     `json:"budgetId"`
 | 
				
			||||||
 | 
						AccountID  uuid.UUID     `json:"accountId"`
 | 
				
			||||||
 | 
						State      string        `json:"state"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (h *Handler) newTransaction(c *gin.Context) {
 | 
					func (h *Handler) newTransaction(c *gin.Context) {
 | 
				
			||||||
@@ -44,26 +41,27 @@ func (h *Handler) newTransaction(c *gin.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newTransaction := postgres.CreateTransactionParams{
 | 
						transactionID := c.Param("transactionid")
 | 
				
			||||||
		Memo:   payload.Memo,
 | 
						if transactionID != "" {
 | 
				
			||||||
		Date:   time.Time(payload.Date),
 | 
							h.UpdateTransaction(payload, amount, transactionID, c)
 | 
				
			||||||
		Amount: amount,
 | 
							return
 | 
				
			||||||
		Status: postgres.TransactionStatus(payload.State),
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if payload.Payee.IsAccount {
 | 
						newTransaction := postgres.CreateTransactionParams{
 | 
				
			||||||
		newTransaction.GroupID = uuid.NullUUID{UUID: uuid.New(), Valid: true}
 | 
							Memo:       payload.Memo,
 | 
				
			||||||
		newTransaction.Amount = amount.Neg()
 | 
							Date:       time.Time(payload.Date),
 | 
				
			||||||
		newTransaction.AccountID = payload.Payee.ID.UUID
 | 
							Amount:     amount,
 | 
				
			||||||
		newTransaction.CategoryID = uuid.NullUUID{}
 | 
							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 {
 | 
							if err != nil {
 | 
				
			||||||
			c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transfer transaction: %w", err))
 | 
								c.AbortWithError(http.StatusInternalServerError, err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		newTransaction.Amount = amount
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		payeeID, err := GetPayeeID(c.Request.Context(), payload, h)
 | 
							payeeID, err := GetPayeeID(c.Request.Context(), payload, h)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -72,17 +70,54 @@ func (h *Handler) newTransaction(c *gin.Context) {
 | 
				
			|||||||
		newTransaction.PayeeID = payeeID
 | 
							newTransaction.PayeeID = payeeID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newTransaction.CategoryID = payload.Category.ID
 | 
					 | 
				
			||||||
	newTransaction.AccountID = payload.AccountID
 | 
					 | 
				
			||||||
	transaction, err := h.Service.CreateTransaction(c.Request.Context(), newTransaction)
 | 
						transaction, err := h.Service.CreateTransaction(c.Request.Context(), newTransaction)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transaction: %w", err))
 | 
							c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("create transaction: %w", err))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	c.JSON(http.StatusOK, transaction)
 | 
						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) {
 | 
					func GetPayeeID(context context.Context, payload NewTransactionPayload, h *Handler) (uuid.NullUUID, error) {
 | 
				
			||||||
	payeeID := payload.Payee.ID
 | 
						payeeID := payload.Payee.ID
 | 
				
			||||||
	if payeeID.Valid {
 | 
						if payeeID.Valid {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,43 +1,38 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { defineComponent, PropType, ref, watch } from "vue"
 | 
					import { ref, watch } from "vue"
 | 
				
			||||||
import { GET } from "../api";
 | 
					import { GET } from "../api";
 | 
				
			||||||
import { useBudgetsStore } from "../stores/budget";
 | 
					import { useBudgetsStore } from "../stores/budget";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Suggestion {
 | 
					export interface Suggestion {
 | 
				
			||||||
    ID: string
 | 
					    ID: string
 | 
				
			||||||
    Name: string
 | 
					    Name: string
 | 
				
			||||||
}
 | 
					    Type: string
 | 
				
			||||||
 | 
					 | 
				
			||||||
interface Data {
 | 
					 | 
				
			||||||
    Selected: Suggestion | undefined
 | 
					 | 
				
			||||||
    SearchQuery: String
 | 
					 | 
				
			||||||
    Suggestions: Suggestion[]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
    modelValue: Suggestion | undefined,
 | 
					    text: String,
 | 
				
			||||||
    type: String
 | 
					    id: String | undefined,
 | 
				
			||||||
 | 
					    model: String,
 | 
				
			||||||
 | 
					    type?: string | undefined,
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Selected = ref<Suggestion | undefined>(props.modelValue || undefined);
 | 
					const SearchQuery = ref(props.text || "");
 | 
				
			||||||
const SearchQuery = ref(props.modelValue?.Name || "");
 | 
					 | 
				
			||||||
const Suggestions = ref<Array<Suggestion>>([]);
 | 
					const Suggestions = ref<Array<Suggestion>>([]);
 | 
				
			||||||
const emit = defineEmits(["update:modelValue"]);
 | 
					const emit = defineEmits(["update:id", "update:text", "update:type"]);
 | 
				
			||||||
watch(SearchQuery, () => {
 | 
					watch(SearchQuery, () => {
 | 
				
			||||||
    load(SearchQuery.value);
 | 
					    load(SearchQuery.value);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
function saveTransaction(e: MouseEvent) {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
function load(text: String) {
 | 
					function load(text: String) {
 | 
				
			||||||
    emit('update:modelValue', { ID: null, Name: text });
 | 
					    emit('update:id', null);
 | 
				
			||||||
 | 
					    emit('update:text', text);
 | 
				
			||||||
 | 
					    emit('update:type', undefined);
 | 
				
			||||||
    if (text == "") {
 | 
					    if (text == "") {
 | 
				
			||||||
        Suggestions.value = [];
 | 
					        Suggestions.value = [];
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const budgetStore = useBudgetsStore();
 | 
					    const budgetStore = useBudgetsStore();
 | 
				
			||||||
    GET("/budget/" + budgetStore.CurrentBudgetID + "/autocomplete/" + props.type + "?s=" + text)
 | 
					    GET("/budget/" + budgetStore.CurrentBudgetID + "/autocomplete/" + props.model + "?s=" + text)
 | 
				
			||||||
        .then(x => x.json())
 | 
					        .then(x => x.json())
 | 
				
			||||||
        .then(x => {
 | 
					        .then(x => {
 | 
				
			||||||
            let suggestions = x || [];
 | 
					            let suggestions = x || [];
 | 
				
			||||||
@@ -56,13 +51,13 @@ function keypress(e: KeyboardEvent) {
 | 
				
			|||||||
        const currentIndex = inputElements.indexOf(el);
 | 
					        const currentIndex = inputElements.indexOf(el);
 | 
				
			||||||
        const nextElement = inputElements[currentIndex < inputElements.length - 1 ? currentIndex + 1 : 0];
 | 
					        const nextElement = inputElements[currentIndex < inputElements.length - 1 ? currentIndex + 1 : 0];
 | 
				
			||||||
        (<HTMLInputElement>nextElement).focus();
 | 
					        (<HTMLInputElement>nextElement).focus();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
function selectElement(element: Suggestion) {
 | 
					function selectElement(element: Suggestion) {
 | 
				
			||||||
    Selected.value = element;
 | 
					    emit('update:id', element.ID);
 | 
				
			||||||
 | 
					    emit('update:text', element.Name);
 | 
				
			||||||
 | 
					    emit('update:type', element.Type);
 | 
				
			||||||
    Suggestions.value = [];
 | 
					    Suggestions.value = [];
 | 
				
			||||||
    emit('update:modelValue', element);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
function select(e: MouseEvent) {
 | 
					function select(e: MouseEvent) {
 | 
				
			||||||
    const target = (<HTMLInputElement>e.target);
 | 
					    const target = (<HTMLInputElement>e.target);
 | 
				
			||||||
@@ -74,8 +69,9 @@ function select(e: MouseEvent) {
 | 
				
			|||||||
    selectElement(selected);
 | 
					    selectElement(selected);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
function clear() {
 | 
					function clear() {
 | 
				
			||||||
    Selected.value = undefined;
 | 
					    emit('update:id', null);
 | 
				
			||||||
    emit('update:modelValue', { ID: null, Name: SearchQuery.value });
 | 
					    emit('update:text', SearchQuery.value);
 | 
				
			||||||
 | 
					    emit('update:type', undefined);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,10 +80,10 @@ function clear() {
 | 
				
			|||||||
        <input
 | 
					        <input
 | 
				
			||||||
            class="border-b-2 border-black"
 | 
					            class="border-b-2 border-black"
 | 
				
			||||||
            @keypress="keypress"
 | 
					            @keypress="keypress"
 | 
				
			||||||
            v-if="Selected == undefined"
 | 
					            v-if="id == undefined"
 | 
				
			||||||
            v-model="SearchQuery"
 | 
					            v-model="SearchQuery"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <span @click="clear" v-if="Selected != undefined" class="bg-gray-300">{{ Selected.Name }}</span>
 | 
					        <span @click="clear" v-if="id != undefined" class="bg-gray-300">{{ text }}</span>
 | 
				
			||||||
        <div v-if="Suggestions.length > 0" class="absolute bg-gray-400 w-64 p-2">
 | 
					        <div v-if="Suggestions.length > 0" class="absolute bg-gray-400 w-64 p-2">
 | 
				
			||||||
            <span
 | 
					            <span
 | 
				
			||||||
                v-for="suggestion in Suggestions"
 | 
					                v-for="suggestion in Suggestions"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								web/src/components/DateInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								web/src/components/DateInput.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					const props = defineProps(["modelValue"]);
 | 
				
			||||||
 | 
					const emit = defineEmits(['update:modelValue']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function dateToYYYYMMDD(d: Date) : string {
 | 
				
			||||||
 | 
					  // alternative implementations in https://stackoverflow.com/q/23593052/1850609
 | 
				
			||||||
 | 
					  //return new Date(d.getTime() - (d.getTimezoneOffset() * 60 * 1000)).toISOString().split('T')[0];
 | 
				
			||||||
 | 
					  return d.toISOString().split('T')[0];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function updateValue(event: Event) {
 | 
				
			||||||
 | 
					  const target = event.target as HTMLInputElement;
 | 
				
			||||||
 | 
					  emit('update:modelValue', target.valueAsDate);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function selectAll(event: FocusEvent) {
 | 
				
			||||||
 | 
					  // Workaround for Safari bug
 | 
				
			||||||
 | 
					  // http://stackoverflow.com/questions/1269722/selecting-text-on-focus-using-jquery-not-working-in-safari-and-chrome
 | 
				
			||||||
 | 
					  setTimeout(function () {
 | 
				
			||||||
 | 
					    const target = event.target as HTMLInputElement;
 | 
				
			||||||
 | 
					    target.select()
 | 
				
			||||||
 | 
					  }, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <input
 | 
				
			||||||
 | 
					    type="date"
 | 
				
			||||||
 | 
					    ref="input"
 | 
				
			||||||
 | 
					    v-bind:value="dateToYYYYMMDD(modelValue)"
 | 
				
			||||||
 | 
					    @input="updateValue"
 | 
				
			||||||
 | 
					    @focus="selectAll"
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										60
									
								
								web/src/components/TransactionEditRow.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								web/src/components/TransactionEditRow.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { computed, ref } from "vue";
 | 
				
			||||||
 | 
					import Autocomplete from './Autocomplete.vue'
 | 
				
			||||||
 | 
					import { useAccountStore } from '../stores/budget-account'
 | 
				
			||||||
 | 
					import DateInput from "./DateInput.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps<{
 | 
				
			||||||
 | 
					    transactionid: string
 | 
				
			||||||
 | 
					}>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const accountStore = useAccountStore();
 | 
				
			||||||
 | 
					const TX = accountStore.Transactions.get(props.transactionid)!;
 | 
				
			||||||
 | 
					const payeeType = ref<string|undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const payload = computed(() => JSON.stringify({
 | 
				
			||||||
 | 
					    date: TX.Date.toISOString().split("T")[0],
 | 
				
			||||||
 | 
					    payee: {
 | 
				
			||||||
 | 
					        Name: TX.Payee,
 | 
				
			||||||
 | 
					        ID: TX.PayeeID,
 | 
				
			||||||
 | 
					        Type: payeeType.value,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    categoryId: TX.CategoryID,
 | 
				
			||||||
 | 
					    memo: TX.Memo,
 | 
				
			||||||
 | 
					    amount: TX.Amount.toString(),
 | 
				
			||||||
 | 
					    state: "Uncleared"
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function saveTransaction(e: MouseEvent) {
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					    accountStore.editTransaction(TX.ID, payload.value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <tr>
 | 
				
			||||||
 | 
					        <td style="width: 90px;" class="text-sm">
 | 
				
			||||||
 | 
					            <DateInput class="border-b-2 border-black" v-model="TX.Date" />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td style="max-width: 150px;">
 | 
				
			||||||
 | 
					            <Autocomplete v-model:text="TX.Payee" v-model:id="TX.PayeeID" v-model:type="payeeType" model="payees" />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td style="max-width: 200px;">
 | 
				
			||||||
 | 
					            <Autocomplete v-model:text="TX.Category" v-model:id="TX.CategoryID" model="categories" />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td>
 | 
				
			||||||
 | 
					            <input class="block w-full border-b-2 border-black" type="text" v-model="TX.Memo" />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td style="width: 80px;" class="text-right">
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					                class="text-right block w-full border-b-2 border-black"
 | 
				
			||||||
 | 
					                type="currency"
 | 
				
			||||||
 | 
					                v-model="TX.Amount"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td style="width: 20px;">
 | 
				
			||||||
 | 
					            <input type="submit" @click="saveTransaction" value="Save" />
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					        <td style="width: 20px;"></td>
 | 
				
			||||||
 | 
					    </tr>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
@@ -1,27 +1,43 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { computed, ref } from "vue";
 | 
					import { computed, ref } from "vue";
 | 
				
			||||||
import Autocomplete, { Suggestion } from '../components/Autocomplete.vue'
 | 
					import Autocomplete, { Suggestion } from '../components/Autocomplete.vue'
 | 
				
			||||||
import { useAccountStore } from '../stores/budget-account'
 | 
					import { Transaction, useAccountStore } from '../stores/budget-account'
 | 
				
			||||||
 | 
					import DateInput from "./DateInput.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
    budgetid: string
 | 
					    budgetid: string
 | 
				
			||||||
    accountid: string
 | 
					    accountid: string
 | 
				
			||||||
}>()
 | 
					}>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TransactionDate = ref(new Date().toISOString().substring(0, 10));
 | 
					const TX = ref<Transaction>({
 | 
				
			||||||
const Payee = ref<Suggestion | undefined>(undefined);
 | 
					    Date: new Date(),
 | 
				
			||||||
const Category = ref<Suggestion | undefined>(undefined);
 | 
					    Memo: "",
 | 
				
			||||||
const Memo = ref("");
 | 
					    Amount: 0,
 | 
				
			||||||
const Amount = ref("0");
 | 
					    Payee: "",
 | 
				
			||||||
 | 
					    PayeeID: undefined,
 | 
				
			||||||
 | 
					    Category: "",
 | 
				
			||||||
 | 
					    CategoryID: undefined,
 | 
				
			||||||
 | 
					    CategoryGroup: "",
 | 
				
			||||||
 | 
					    GroupID: "",
 | 
				
			||||||
 | 
					    ID: "",
 | 
				
			||||||
 | 
					    Status: "Uncleared",
 | 
				
			||||||
 | 
					    TransferAccount: "",
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const payeeType = ref<string|undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const payload = computed(() => JSON.stringify({
 | 
					const payload = computed(() => JSON.stringify({
 | 
				
			||||||
    budgetId: props.budgetid,
 | 
					    budgetId: props.budgetid,
 | 
				
			||||||
    accountId: props.accountid,
 | 
					    accountId: props.accountid,
 | 
				
			||||||
    date: TransactionDate.value,
 | 
					    date: TX.value.Date.toISOString().split("T")[0],
 | 
				
			||||||
    payee: Payee.value,
 | 
					    payee: {
 | 
				
			||||||
    category: Category.value,
 | 
					        Name: TX.value.Payee,
 | 
				
			||||||
    memo: Memo.value,
 | 
					        ID: TX.value.PayeeID,
 | 
				
			||||||
    amount: Amount.value,
 | 
					        Type: payeeType.value,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    categoryId: TX.value.CategoryID,
 | 
				
			||||||
 | 
					    memo: TX.value.Memo,
 | 
				
			||||||
 | 
					    amount: TX.value.Amount.toString(),
 | 
				
			||||||
    state: "Uncleared"
 | 
					    state: "Uncleared"
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,22 +51,22 @@ function saveTransaction(e: MouseEvent) {
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <tr>
 | 
					    <tr>
 | 
				
			||||||
        <td style="width: 90px;" class="text-sm">
 | 
					        <td style="width: 90px;" class="text-sm">
 | 
				
			||||||
            <input class="border-b-2 border-black" type="date" v-model="TransactionDate" />
 | 
					            <DateInput class="border-b-2 border-black" v-model="TX.Date" />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="max-width: 150px;">
 | 
					        <td style="max-width: 150px;">
 | 
				
			||||||
            <Autocomplete v-model="Payee" type="payees" />
 | 
					            <Autocomplete v-model:text="TX.Payee" v-model:id="TX.PayeeID" v-model:type="payeeType" model="payees" />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="max-width: 200px;">
 | 
					        <td style="max-width: 200px;">
 | 
				
			||||||
            <Autocomplete v-model="Category" type="categories" />
 | 
					            <Autocomplete v-model:text="TX.Category" v-model:id="TX.CategoryID" model="categories" />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td>
 | 
					        <td>
 | 
				
			||||||
            <input class="block w-full border-b-2 border-black" type="text" v-model="Memo" />
 | 
					            <input class="block w-full border-b-2 border-black" type="text" v-model="TX.Memo" />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="width: 80px;" class="text-right">
 | 
					        <td style="width: 80px;" class="text-right">
 | 
				
			||||||
            <input
 | 
					            <input
 | 
				
			||||||
                class="text-right block w-full border-b-2 border-black"
 | 
					                class="text-right block w-full border-b-2 border-black"
 | 
				
			||||||
                type="currency"
 | 
					                type="currency"
 | 
				
			||||||
                v-model="Amount"
 | 
					                v-model="TX.Amount"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="width: 20px;">
 | 
					        <td style="width: 20px;">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,28 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { computed } from "vue";
 | 
					import { computed, ref } from "vue";
 | 
				
			||||||
import { useBudgetsStore } from "../stores/budget";
 | 
					import { useBudgetsStore } from "../stores/budget";
 | 
				
			||||||
import { Transaction } from "../stores/budget-account";
 | 
					import { Transaction } from "../stores/budget-account";
 | 
				
			||||||
import Currency from "./Currency.vue";
 | 
					import Currency from "./Currency.vue";
 | 
				
			||||||
 | 
					import TransactionEditRow from "./TransactionEditRow.vue";
 | 
				
			||||||
 | 
					import { formatDate } from "../date";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
    transaction: Transaction,
 | 
					    transaction: Transaction,
 | 
				
			||||||
    index: number,
 | 
					    index: number,
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
 | 
					const edit = ref(false);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
const CurrentBudgetID = computed(()=> useBudgetsStore().CurrentBudgetID);
 | 
					const CurrentBudgetID = computed(()=> useBudgetsStore().CurrentBudgetID);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <tr class="{{transaction.Date.After now ? 'future' : ''}}"
 | 
					    <tr v-if="!edit" class="{{new Date(transaction.Date) > new Date() ? 'future' : ''}}"
 | 
				
			||||||
        :class="[index % 6 < 3 ? 'bg-gray-300' : 'bg-gray-100']">
 | 
					        :class="[index % 6 < 3 ? 'bg-gray-300' : 'bg-gray-100']">
 | 
				
			||||||
        <!--:class="[index % 6 < 3 ? index % 6 === 1 ? 'bg-gray-400' : 'bg-gray-300' : index % 6 !== 4 ? 'bg-gray-100' : '']">-->
 | 
					        <!--:class="[index % 6 < 3 ? index % 6 === 1 ? 'bg-gray-400' : 'bg-gray-300' : index % 6 !== 4 ? 'bg-gray-100' : '']">-->
 | 
				
			||||||
        <td style="width: 90px;">{{ transaction.Date.substring(0, 10) }}</td>
 | 
					        <td>{{ formatDate(transaction.Date) }}</td>
 | 
				
			||||||
        <td style="max-width: 150px;">{{ transaction.TransferAccount ? "Transfer : " + transaction.TransferAccount : transaction.Payee }}</td>
 | 
					        <td>{{ transaction.TransferAccount ? "Transfer : " + transaction.TransferAccount : transaction.Payee }}</td>
 | 
				
			||||||
        <td style="max-width: 200px;">
 | 
					        <td>
 | 
				
			||||||
            {{ transaction.CategoryGroup ? transaction.CategoryGroup + " : " + transaction.Category : "" }}
 | 
					            {{ transaction.CategoryGroup ? transaction.CategoryGroup + " : " + transaction.Category : "" }}
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td>
 | 
					        <td>
 | 
				
			||||||
@@ -29,11 +33,12 @@ const CurrentBudgetID = computed(()=> useBudgetsStore().CurrentBudgetID);
 | 
				
			|||||||
        <td>
 | 
					        <td>
 | 
				
			||||||
            <Currency class="block" :value="transaction.Amount" />
 | 
					            <Currency class="block" :value="transaction.Amount" />
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="width: 20px;">
 | 
					        <td>
 | 
				
			||||||
            {{ transaction.Status == "Reconciled" ? "✔" : (transaction.Status == "Uncleared" ? "" : "*") }}
 | 
					            {{ transaction.Status == "Reconciled" ? "✔" : (transaction.Status == "Uncleared" ? "" : "*") }}
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td style="width: 20px;">{{ transaction.GroupID ? "☀" : "" }}</td>
 | 
					        <td class="text-right">{{ transaction.GroupID ? "☀" : "" }}<a @click="edit = true;">✎</a></td>
 | 
				
			||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
 | 
					    <TransactionEditRow v-if="edit" :transactionid="transaction.ID" />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								web/src/date.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/src/date.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					export function formatDate(date: Date): string {
 | 
				
			||||||
 | 
					    return date.toLocaleDateString(undefined, { // you can use undefined as first argument
 | 
				
			||||||
 | 
					        year: "numeric",
 | 
				
			||||||
 | 
					        month: "2-digit",
 | 
				
			||||||
 | 
					        day: "2-digit",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								web/src/dialogs/EditAccount.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								web/src/dialogs/EditAccount.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { computed, ref } from 'vue';
 | 
				
			||||||
 | 
					import Modal from '../components/Modal.vue';
 | 
				
			||||||
 | 
					import { useAccountStore } from '../stores/budget-account';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const accountStore = useAccountStore();
 | 
				
			||||||
 | 
					const CurrentAccount = computed(() => accountStore.CurrentAccount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const accountName = ref("");
 | 
				
			||||||
 | 
					const accountOnBudget = ref(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function editAccount(e : any) {
 | 
				
			||||||
 | 
					    accountStore.EditAccount(CurrentAccount.value?.ID ?? "", accountName.value, accountOnBudget.value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function openEditAccount(e : any) {
 | 
				
			||||||
 | 
					    accountName.value = CurrentAccount.value?.Name ?? "";
 | 
				
			||||||
 | 
					    accountOnBudget.value = CurrentAccount.value?.OnBudget ?? true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <Modal button-text="Edit Account" @open="openEditAccount" @submit="editAccount">
 | 
				
			||||||
 | 
					        <template v-slot:placeholder>✎</template>
 | 
				
			||||||
 | 
					        <div class="mt-2 px-7 py-3">
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					                class="border-2"
 | 
				
			||||||
 | 
					                type="text"
 | 
				
			||||||
 | 
					                v-model="accountName"
 | 
				
			||||||
 | 
					                placeholder="Account name"
 | 
				
			||||||
 | 
					                required
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="mt-2 px-7 py-3">
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					                class="border-2"
 | 
				
			||||||
 | 
					                type="checkbox"
 | 
				
			||||||
 | 
					                v-model="accountOnBudget"
 | 
				
			||||||
 | 
					                required
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <label>On Budget</label>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
@@ -4,7 +4,7 @@ import Currency from "../components/Currency.vue";
 | 
				
			|||||||
import TransactionRow from "../components/TransactionRow.vue";
 | 
					import TransactionRow from "../components/TransactionRow.vue";
 | 
				
			||||||
import TransactionInputRow from "../components/TransactionInputRow.vue";
 | 
					import TransactionInputRow from "../components/TransactionInputRow.vue";
 | 
				
			||||||
import { useAccountStore } from "../stores/budget-account";
 | 
					import { useAccountStore } from "../stores/budget-account";
 | 
				
			||||||
import Modal from "../components/Modal.vue";
 | 
					import EditAccount from "../dialogs/EditAccount.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
    budgetid: string
 | 
					    budgetid: string
 | 
				
			||||||
@@ -15,42 +15,11 @@ const accountStore = useAccountStore();
 | 
				
			|||||||
const CurrentAccount = computed(() => accountStore.CurrentAccount);
 | 
					const CurrentAccount = computed(() => accountStore.CurrentAccount);
 | 
				
			||||||
const TransactionsList = computed(() => accountStore.TransactionsList);
 | 
					const TransactionsList = computed(() => accountStore.TransactionsList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const accountName = ref("");
 | 
					 | 
				
			||||||
const accountOnBudget = ref(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function editAccount(e : any) {
 | 
					 | 
				
			||||||
    accountStore.EditAccount(CurrentAccount.value?.ID ?? "", accountName.value, accountOnBudget.value);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function openEditAccount(e : any) {
 | 
					 | 
				
			||||||
    accountName.value = CurrentAccount.value?.Name ?? "";
 | 
					 | 
				
			||||||
    accountOnBudget.value = CurrentAccount.value?.OnBudget ?? true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <h1 class="inline">{{ CurrentAccount?.Name }}</h1>
 | 
					    <h1 class="inline">{{ CurrentAccount?.Name }}</h1>
 | 
				
			||||||
    <Modal button-text="Edit Account" @open="openEditAccount" @submit="editAccount">
 | 
					    <EditAccount />
 | 
				
			||||||
        <template v-slot:placeholder>✎</template>
 | 
					 | 
				
			||||||
        <div class="mt-2 px-7 py-3">
 | 
					 | 
				
			||||||
            <input
 | 
					 | 
				
			||||||
                class="border-2"
 | 
					 | 
				
			||||||
                type="text"
 | 
					 | 
				
			||||||
                v-model="accountName"
 | 
					 | 
				
			||||||
                placeholder="Account name"
 | 
					 | 
				
			||||||
                required
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="mt-2 px-7 py-3">
 | 
					 | 
				
			||||||
            <input
 | 
					 | 
				
			||||||
                class="border-2"
 | 
					 | 
				
			||||||
                type="checkbox"
 | 
					 | 
				
			||||||
                v-model="accountOnBudget"
 | 
					 | 
				
			||||||
                required
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <label>On Budget</label>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </Modal>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
        Current Balance:
 | 
					        Current Balance:
 | 
				
			||||||
@@ -64,7 +33,7 @@ function openEditAccount(e : any) {
 | 
				
			|||||||
            <td>Memo</td>
 | 
					            <td>Memo</td>
 | 
				
			||||||
            <td class="text-right">Amount</td>
 | 
					            <td class="text-right">Amount</td>
 | 
				
			||||||
            <td style="width: 20px;"></td>
 | 
					            <td style="width: 20px;"></td>
 | 
				
			||||||
            <td style="width: 20px;"></td>
 | 
					            <td style="width: 40px;"></td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
        <TransactionInputRow :budgetid="budgetid" :accountid="accountid" />
 | 
					        <TransactionInputRow :budgetid="budgetid" :accountid="accountid" />
 | 
				
			||||||
        <TransactionRow
 | 
					        <TransactionRow
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,20 +8,22 @@ interface State {
 | 
				
			|||||||
    CurrentAccountID: string | null,
 | 
					    CurrentAccountID: string | null,
 | 
				
			||||||
    Categories: Map<string, Category>,
 | 
					    Categories: Map<string, Category>,
 | 
				
			||||||
    Months: Map<number, Map<number, Map<string, Category>>>,
 | 
					    Months: Map<number, Map<number, Map<string, Category>>>,
 | 
				
			||||||
    Transactions: any[],
 | 
					    Transactions: Map<string, Transaction>,
 | 
				
			||||||
    Assignments: []
 | 
					    Assignments: []
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Transaction {
 | 
					export interface Transaction {
 | 
				
			||||||
    ID: string,
 | 
					    ID: string,
 | 
				
			||||||
    Date: string,
 | 
					    Date: Date,
 | 
				
			||||||
    TransferAccount: string,
 | 
					    TransferAccount: string,
 | 
				
			||||||
    CategoryGroup: string,
 | 
					    CategoryGroup: string,
 | 
				
			||||||
    Category: string,
 | 
					    Category: string,
 | 
				
			||||||
 | 
					    CategoryID: string | undefined,
 | 
				
			||||||
    Memo: string,
 | 
					    Memo: string,
 | 
				
			||||||
    Status: string,
 | 
					    Status: string,
 | 
				
			||||||
    GroupID: string,
 | 
					    GroupID: string,
 | 
				
			||||||
    Payee: string,
 | 
					    Payee: string,
 | 
				
			||||||
 | 
					    PayeeID: string | undefined,
 | 
				
			||||||
    Amount: number,
 | 
					    Amount: number,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,6 +32,7 @@ export interface Account {
 | 
				
			|||||||
    Name: string
 | 
					    Name: string
 | 
				
			||||||
    OnBudget: boolean
 | 
					    OnBudget: boolean
 | 
				
			||||||
    Balance: number
 | 
					    Balance: number
 | 
				
			||||||
 | 
					    Transactions: string[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Category {
 | 
					export interface Category {
 | 
				
			||||||
@@ -48,7 +51,7 @@ export const useAccountStore = defineStore("budget/account", {
 | 
				
			|||||||
        CurrentAccountID: null,
 | 
					        CurrentAccountID: null,
 | 
				
			||||||
        Months: new Map<number, Map<number, Map<string, Category>>>(),
 | 
					        Months: new Map<number, Map<number, Map<string, Category>>>(),
 | 
				
			||||||
        Categories: new Map<string, Category>(),
 | 
					        Categories: new Map<string, Category>(),
 | 
				
			||||||
        Transactions: [],
 | 
					        Transactions: new Map<string, Transaction>(),
 | 
				
			||||||
        Assignments: []
 | 
					        Assignments: []
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
    getters: {
 | 
					    getters: {
 | 
				
			||||||
@@ -100,8 +103,10 @@ export const useAccountStore = defineStore("budget/account", {
 | 
				
			|||||||
        OffBudgetAccountsBalance(state): number {
 | 
					        OffBudgetAccountsBalance(state): number {
 | 
				
			||||||
            return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
 | 
					            return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        TransactionsList(state) {
 | 
					        TransactionsList(state) : Transaction[] {
 | 
				
			||||||
            return (state.Transactions || []);
 | 
					            return this.CurrentAccount!.Transactions.map(x => {
 | 
				
			||||||
 | 
					                return this.Transactions.get(x)!
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					    actions: {
 | 
				
			||||||
@@ -110,16 +115,22 @@ export const useAccountStore = defineStore("budget/account", {
 | 
				
			|||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.CurrentAccountID = accountid;
 | 
					            this.CurrentAccountID = accountid;
 | 
				
			||||||
            if (this.CurrentAccount == undefined)
 | 
					            const account = this.CurrentAccount;
 | 
				
			||||||
 | 
					            if (account == undefined)
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            useSessionStore().setTitle(this.CurrentAccount.Name);
 | 
					            useSessionStore().setTitle(account.Name);
 | 
				
			||||||
            await this.FetchAccount(accountid);
 | 
					            await this.FetchAccount(account);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        async FetchAccount(accountid: string) {
 | 
					        async FetchAccount(account: Account) {
 | 
				
			||||||
            const result = await GET("/account/" + accountid + "/transactions");
 | 
					            const result = await GET("/account/" + account.ID + "/transactions");
 | 
				
			||||||
            const response = await result.json();
 | 
					            const response = await result.json();
 | 
				
			||||||
            this.Transactions = response.Transactions;
 | 
					            account.Transactions = [];
 | 
				
			||||||
 | 
					            for (const transaction of response.Transactions) {
 | 
				
			||||||
 | 
					                transaction.Date = new Date(transaction.Date);
 | 
				
			||||||
 | 
					                this.Transactions.set(transaction.ID, transaction);
 | 
				
			||||||
 | 
					                account.Transactions.push(transaction.ID);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        async FetchMonthBudget(budgetid: string, year: number, month: number) {
 | 
					        async FetchMonthBudget(budgetid: string, year: number, month: number) {
 | 
				
			||||||
            const result = await GET("/budget/" + budgetid + "/" + year + "/" + month);
 | 
					            const result = await GET("/budget/" + budgetid + "/" + year + "/" + month);
 | 
				
			||||||
@@ -151,7 +162,12 @@ export const useAccountStore = defineStore("budget/account", {
 | 
				
			|||||||
        async saveTransaction(payload: string) {
 | 
					        async saveTransaction(payload: string) {
 | 
				
			||||||
            const result = await POST("/transaction/new", payload);
 | 
					            const result = await POST("/transaction/new", payload);
 | 
				
			||||||
            const response = await result.json();
 | 
					            const response = await result.json();
 | 
				
			||||||
            this.Transactions.unshift(response);
 | 
					            this.CurrentAccount?.Transactions.unshift(response);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        async editTransaction(transactionid : string, payload: string) {
 | 
				
			||||||
 | 
					            const result = await POST("/transaction/" + transactionid, payload);
 | 
				
			||||||
 | 
					            const response = await result.json();
 | 
				
			||||||
 | 
					            this.CurrentAccount?.Transactions.unshift(response);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user