diff --git a/postgres/accounts.sql.go b/postgres/accounts.sql.go
index ab5de4e..666e620 100644
--- a/postgres/accounts.sql.go
+++ b/postgres/accounts.sql.go
@@ -5,7 +5,7 @@ package postgres
import (
"context"
- "time"
+ "database/sql"
"git.javil.eu/jacob1123/budgeteer/postgres/numeric"
"github.com/google/uuid"
@@ -15,7 +15,7 @@ const createAccount = `-- name: CreateAccount :one
INSERT INTO accounts
(name, budget_id)
VALUES ($1, $2)
-RETURNING id, budget_id, name, on_budget, is_open
+RETURNING id, budget_id, name, on_budget, is_open, last_reconciled
`
type CreateAccountParams struct {
@@ -32,12 +32,13 @@ func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (A
&i.Name,
&i.OnBudget,
&i.IsOpen,
+ &i.LastReconciled,
)
return i, err
}
const getAccount = `-- name: GetAccount :one
-SELECT accounts.id, accounts.budget_id, accounts.name, accounts.on_budget, accounts.is_open FROM accounts
+SELECT accounts.id, accounts.budget_id, accounts.name, accounts.on_budget, accounts.is_open, accounts.last_reconciled FROM accounts
WHERE accounts.id = $1
`
@@ -50,12 +51,13 @@ func (q *Queries) GetAccount(ctx context.Context, id uuid.UUID) (Account, error)
&i.Name,
&i.OnBudget,
&i.IsOpen,
+ &i.LastReconciled,
)
return i, err
}
const getAccounts = `-- name: GetAccounts :many
-SELECT accounts.id, accounts.budget_id, accounts.name, accounts.on_budget, accounts.is_open FROM accounts
+SELECT accounts.id, accounts.budget_id, accounts.name, accounts.on_budget, accounts.is_open, accounts.last_reconciled FROM accounts
WHERE accounts.budget_id = $1
AND accounts.is_open = TRUE
ORDER BY accounts.name
@@ -76,6 +78,7 @@ func (q *Queries) GetAccounts(ctx context.Context, budgetID uuid.UUID) ([]Accoun
&i.Name,
&i.OnBudget,
&i.IsOpen,
+ &i.LastReconciled,
); err != nil {
return nil, err
}
@@ -91,8 +94,7 @@ func (q *Queries) GetAccounts(ctx context.Context, budgetID uuid.UUID) ([]Accoun
}
const getAccountsWithBalance = `-- name: GetAccountsWithBalance :many
-SELECT accounts.id, accounts.name, accounts.on_budget, accounts.is_open,
- (SELECT MAX(transactions.date) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.status = 'Reconciled')::date as last_reconciled,
+SELECT accounts.id, accounts.name, accounts.on_budget, accounts.is_open, accounts.last_reconciled,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW())::decimal(12,2) as working_balance,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW() AND transactions.status IN ('Cleared', 'Reconciled'))::decimal(12,2) as cleared_balance,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW() AND transactions.status = 'Reconciled')::decimal(12,2) as reconciled_balance
@@ -107,7 +109,7 @@ type GetAccountsWithBalanceRow struct {
Name string
OnBudget bool
IsOpen bool
- LastReconciled time.Time
+ LastReconciled sql.NullTime
WorkingBalance numeric.Numeric
ClearedBalance numeric.Numeric
ReconciledBalance numeric.Numeric
@@ -193,13 +195,24 @@ func (q *Queries) SearchAccounts(ctx context.Context, arg SearchAccountsParams)
return items, nil
}
+const setLastReconciled = `-- name: SetLastReconciled :exec
+UPDATE accounts
+SET last_reconciled = NOW()
+WHERE accounts.id = $1
+`
+
+func (q *Queries) SetLastReconciled(ctx context.Context, id uuid.UUID) error {
+ _, err := q.db.ExecContext(ctx, setLastReconciled, id)
+ return err
+}
+
const updateAccount = `-- name: UpdateAccount :one
UPDATE accounts
SET name = $1,
on_budget = $2,
is_open = $3
WHERE accounts.id = $4
-RETURNING id, budget_id, name, on_budget, is_open
+RETURNING id, budget_id, name, on_budget, is_open, last_reconciled
`
type UpdateAccountParams struct {
@@ -223,6 +236,7 @@ func (q *Queries) UpdateAccount(ctx context.Context, arg UpdateAccountParams) (A
&i.Name,
&i.OnBudget,
&i.IsOpen,
+ &i.LastReconciled,
)
return i, err
}
diff --git a/postgres/models.go b/postgres/models.go
index 7ff33e8..8e01747 100644
--- a/postgres/models.go
+++ b/postgres/models.go
@@ -32,11 +32,12 @@ func (e *TransactionStatus) Scan(src interface{}) error {
}
type Account struct {
- ID uuid.UUID
- BudgetID uuid.UUID
- Name string
- OnBudget bool
- IsOpen bool
+ ID uuid.UUID
+ BudgetID uuid.UUID
+ Name string
+ OnBudget bool
+ IsOpen bool
+ LastReconciled sql.NullTime
}
type Assignment struct {
diff --git a/postgres/queries/accounts.sql b/postgres/queries/accounts.sql
index 0e62900..6390403 100644
--- a/postgres/queries/accounts.sql
+++ b/postgres/queries/accounts.sql
@@ -15,8 +15,7 @@ AND accounts.is_open = TRUE
ORDER BY accounts.name;
-- name: GetAccountsWithBalance :many
-SELECT accounts.id, accounts.name, accounts.on_budget, accounts.is_open,
- (SELECT MAX(transactions.date) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.status = 'Reconciled')::date as last_reconciled,
+SELECT accounts.id, accounts.name, accounts.on_budget, accounts.is_open, accounts.last_reconciled,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW())::decimal(12,2) as working_balance,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW() AND transactions.status IN ('Cleared', 'Reconciled'))::decimal(12,2) as cleared_balance,
(SELECT SUM(transactions.amount) FROM transactions WHERE transactions.account_id = accounts.id AND transactions.date < NOW() AND transactions.status = 'Reconciled')::decimal(12,2) as reconciled_balance
@@ -38,4 +37,9 @@ SET name = $1,
on_budget = $2,
is_open = $3
WHERE accounts.id = $4
-RETURNING *;
\ No newline at end of file
+RETURNING *;
+
+-- name: SetLastReconciled :exec
+UPDATE accounts
+SET last_reconciled = NOW()
+WHERE accounts.id = $1;
\ No newline at end of file
diff --git a/postgres/schema/0018_reconciled-on.sql b/postgres/schema/0018_reconciled-on.sql
new file mode 100644
index 0000000..42a16ed
--- /dev/null
+++ b/postgres/schema/0018_reconciled-on.sql
@@ -0,0 +1,12 @@
+-- +goose Up
+ALTER TABLE accounts ADD COLUMN last_reconciled date NULL;
+UPDATE accounts
+SET last_reconciled = (
+ SELECT MAX(transactions.date)
+ FROM transactions
+ WHERE transactions.account_id = accounts.id
+ AND transactions.status = 'Reconciled'
+);
+
+-- +goose Down
+ALTER TABLE accounts DROP COLUMN last_reconciled;
\ No newline at end of file
diff --git a/server/reconcile.go b/server/reconcile.go
index 7b9cd5a..2ac619b 100644
--- a/server/reconcile.go
+++ b/server/reconcile.go
@@ -65,6 +65,12 @@ func (h *Handler) reconcileTransactions(c *gin.Context) {
return
}
+ err = h.Service.SetLastReconciled(c.Request.Context(), accountUUID)
+ if err != nil {
+ c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("set last reconciled: %w", err))
+ return
+ }
+
err = tx.Commit()
if err != nil {
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("commit: %w", err))
diff --git a/web/src/components/AccountWithReconciled.vue b/web/src/components/AccountWithReconciled.vue
new file mode 100644
index 0000000..044c754
--- /dev/null
+++ b/web/src/components/AccountWithReconciled.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+ {{account.Name}}
+
+
+ {{daysSinceLastReconciled()}}
+
+
+
\ No newline at end of file
diff --git a/web/src/pages/BudgetSidebar.vue b/web/src/pages/BudgetSidebar.vue
index e3e2045..0407e76 100644
--- a/web/src/pages/BudgetSidebar.vue
+++ b/web/src/pages/BudgetSidebar.vue
@@ -4,6 +4,7 @@ import Currency from "../components/Currency.vue"
import { useBudgetsStore } from "../stores/budget"
import { Account, useAccountStore } from "../stores/budget-account"
import { useSettingsStore } from "../stores/settings"
+import AccountWithReconciled from "../components/AccountWithReconciled.vue";
const settings = useSettingsStore();
const ExpandMenu = computed(() => settings.Menu.Expand);
@@ -18,17 +19,6 @@ const OnBudgetAccounts = computed(() => accountStore.OnBudgetAccounts);
const OffBudgetAccounts = computed(() => accountStore.OffBudgetAccounts);
const OnBudgetAccountsBalance = computed(() => accountStore.OnBudgetAccountsBalance);
const OffBudgetAccountsBalance = computed(() => accountStore.OffBudgetAccountsBalance);
-
-function isRecentlyReconciled(account: Account) {
- const now = new Date().getTime();
- const recently = 7 * 24 * 60 * 60 * 1000;
- return new Date(now - recently).getTime() < account.LastReconciled.getTime();
-}
-
-function getAccountName(account: Account) {
- const reconciledMarker = isRecentlyReconciled(account) ? "" : " *";
- return account.Name + reconciledMarker;
-}
@@ -56,9 +46,7 @@ function getAccountName(account: Account) {
-
{{ getAccountName(account) }}
+
@@ -68,9 +56,7 @@ function getAccountName(account: Account) {
-
{{ getAccountName(account) }}
+
diff --git a/web/src/stores/budget-account.ts b/web/src/stores/budget-account.ts
index bf64694..e582248 100644
--- a/web/src/stores/budget-account.ts
+++ b/web/src/stores/budget-account.ts
@@ -21,7 +21,12 @@ export interface Account {
WorkingBalance: number
ReconciledBalance: number
Transactions: string[]
- LastReconciled: Date
+ LastReconciled: NullDate
+}
+
+interface NullDate {
+ Valid: boolean
+ Time: Date
}
export interface Category {
diff --git a/web/src/stores/budget.ts b/web/src/stores/budget.ts
index 57200c7..49b8310 100644
--- a/web/src/stores/budget.ts
+++ b/web/src/stores/budget.ts
@@ -58,7 +58,8 @@ export const useBudgetsStore = defineStore('budget', {
for (const account of response.Accounts || []) {
const existingAccount = accounts.Accounts.get(account.ID);
account.Transactions = existingAccount?.Transactions ?? [];
- account.LastReconciled = new Date(account.LastReconciled);
+ if(account.LastReconciled.Valid)
+ account.LastReconciled.Time = new Date(account.LastReconciled.Time);
accounts.Accounts.set(account.ID, account);
}
for (const category of response.Categories || []) {