Add account reconcilation
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
ded3444391
commit
dae017f99e
5 changed files with 63 additions and 0 deletions
|
@ -72,6 +72,14 @@
|
||||||
Add Transfer
|
Add Transfer
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-success"
|
||||||
|
@click="markAccountReconciled"
|
||||||
|
>
|
||||||
|
<i class="fas fa-fw fa-square-check mr-1" />
|
||||||
|
Mark Reconciled
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
:disabled="selectedTx.length !== 1"
|
:disabled="selectedTx.length !== 1"
|
||||||
|
@ -80,6 +88,7 @@
|
||||||
<i class="fas fa-fw fa-pencil mr-1" />
|
<i class="fas fa-fw fa-pencil mr-1" />
|
||||||
Edit Transaction
|
Edit Transaction
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
|
class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
|
||||||
|
@ -178,7 +187,14 @@
|
||||||
{{ formatNumber(tx.amount) }} €
|
{{ formatNumber(tx.amount) }} €
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<span
|
||||||
|
v-if="tx.reconciled"
|
||||||
|
class="text-success"
|
||||||
|
>
|
||||||
|
<i class="fas fa-lock" />
|
||||||
|
</span>
|
||||||
<a
|
<a
|
||||||
|
v-else
|
||||||
href="#"
|
href="#"
|
||||||
:class="{'text-decoration-none':true, 'text-muted': !tx.cleared, 'text-success': tx.cleared}"
|
:class="{'text-decoration-none':true, 'text-muted': !tx.cleared, 'text-success': tx.cleared}"
|
||||||
@click.prevent="markCleared(tx.id, !tx.cleared)"
|
@click.prevent="markCleared(tx.id, !tx.cleared)"
|
||||||
|
@ -482,6 +498,13 @@ export default {
|
||||||
|
|
||||||
formatNumber,
|
formatNumber,
|
||||||
|
|
||||||
|
markAccountReconciled() {
|
||||||
|
return fetch(`/api/accounts/${this.accountId}/reconcile`, {
|
||||||
|
method: 'PUT',
|
||||||
|
})
|
||||||
|
.then(() => this.fetchTransactions())
|
||||||
|
},
|
||||||
|
|
||||||
markCleared(txId, cleared) {
|
markCleared(txId, cleared) {
|
||||||
return fetch(`/api/transactions/${txId}?cleared=${cleared}`, {
|
return fetch(`/api/transactions/${txId}?cleared=${cleared}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|
|
@ -102,6 +102,25 @@ func (a apiServer) handleListAccounts(w http.ResponseWriter, r *http.Request) {
|
||||||
a.jsonResponse(w, http.StatusOK, payload)
|
a.jsonResponse(w, http.StatusOK, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a apiServer) handleAccountReconcile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var (
|
||||||
|
acctID uuid.UUID
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if acctID, err = uuid.Parse(mux.Vars(r)["id"]); err != nil {
|
||||||
|
a.errorResponse(w, err, "parsing id", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = a.dbc.MarkAccountReconciled(acctID); err != nil {
|
||||||
|
a.errorResponse(w, err, "marking reconciled", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
func (a apiServer) handleTransferMoney(w http.ResponseWriter, r *http.Request) {
|
func (a apiServer) handleTransferMoney(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
amount float64
|
amount float64
|
||||||
|
|
|
@ -38,6 +38,9 @@ func RegisterHandler(apiRouter *mux.Router, dbc *database.Client, logger *logrus
|
||||||
apiRouter.
|
apiRouter.
|
||||||
HandleFunc("/accounts/{id}", as.handleUpdateAccount).
|
HandleFunc("/accounts/{id}", as.handleUpdateAccount).
|
||||||
Methods(http.MethodPatch)
|
Methods(http.MethodPatch)
|
||||||
|
apiRouter.
|
||||||
|
HandleFunc("/accounts/{id}/reconcile", as.handleAccountReconcile).
|
||||||
|
Methods(http.MethodPut)
|
||||||
apiRouter.
|
apiRouter.
|
||||||
HandleFunc("/accounts/{id}/transactions", as.handleListTransactionsByAccount).
|
HandleFunc("/accounts/{id}/transactions", as.handleListTransactionsByAccount).
|
||||||
Methods(http.MethodGet)
|
Methods(http.MethodGet)
|
||||||
|
|
|
@ -267,6 +267,23 @@ func (c *Client) ListTransactionsByAccount(acc uuid.UUID, since, until time.Time
|
||||||
return txs, nil
|
return txs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkAccountReconciled marks all cleared transactions as reconciled.
|
||||||
|
// The account balance is NOT checked in this method.
|
||||||
|
func (c *Client) MarkAccountReconciled(acc uuid.UUID) (err error) {
|
||||||
|
if err = c.retryTx(func(db *gorm.DB) error {
|
||||||
|
return db.
|
||||||
|
Model(&Transaction{}).
|
||||||
|
Where("account = ?", acc).
|
||||||
|
Where("cleared = ?", true).
|
||||||
|
Update("reconciled", true).
|
||||||
|
Error
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("updating transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TransferMoney creates new Transactions for the given account
|
// TransferMoney creates new Transactions for the given account
|
||||||
// transfer. The account type of the from and to account must match
|
// transfer. The account type of the from and to account must match
|
||||||
// for this to work.
|
// for this to work.
|
||||||
|
|
|
@ -39,6 +39,7 @@ type (
|
||||||
Account uuid.NullUUID `gorm:"type:uuid" json:"account"`
|
Account uuid.NullUUID `gorm:"type:uuid" json:"account"`
|
||||||
Category uuid.NullUUID `gorm:"type:uuid" json:"category"`
|
Category uuid.NullUUID `gorm:"type:uuid" json:"category"`
|
||||||
Cleared bool `json:"cleared"`
|
Cleared bool `json:"cleared"`
|
||||||
|
Reconciled bool `json:"reconciled"`
|
||||||
|
|
||||||
PairKey uuid.NullUUID `gorm:"type:uuid" json:"-"`
|
PairKey uuid.NullUUID `gorm:"type:uuid" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue