Add balance assertions

This commit is contained in:
RunasSudo 2022-12-24 17:39:18 +11:00
parent cf92c9b638
commit 4b9025185d
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
6 changed files with 204 additions and 4 deletions

View File

@ -45,3 +45,17 @@ class GeneralJournalPosting(Base, Posting):
def amount(self):
return Amount(self.quantity, self.commodity)
class BalanceAssertion(Base):
__tablename__ = 'balance_assertions'
id = Column(Integer, primary_key=True)
dt = Column(DateTime)
description = Column(String)
account = Column(String)
quantity = Column(Integer)
commodity = Column(String)
def balance(self):
return Amount(self.quantity, self.commodity)

View File

@ -14,11 +14,81 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from flask import render_template
from flask import abort, redirect, render_template, request
from ..webapp import app
from .models import GeneralJournalTransaction
from .. import AMOUNT_DPS
from ..database import db_session
from ..models import TrialBalancer
from ..webapp import all_transactions, app
from .models import BalanceAssertion, GeneralJournalTransaction
from datetime import datetime
@app.route('/general-journal')
def general_journal():
return render_template('general_journal.html', transactions=GeneralJournalTransaction.query.all())
return render_template('general_journal/general_journal.html', transactions=GeneralJournalTransaction.query.all())
@app.route('/balance-assertions')
def balance_assertions():
assertions = BalanceAssertion.query.all()
# Check assertion status
transactions = all_transactions()
assertion_status = {}
for assertion in assertions:
# FIXME: This is very inefficient
balancer = TrialBalancer()
balancer.apply_transactions([t for t in transactions if t.dt <= assertion.dt])
# TODO: Commodities
if balancer.accounts[assertion.account].quantity == assertion.quantity:
assertion_status[assertion] = True
else:
assertion_status[assertion] = False
return render_template('general_journal/balance_assertions.html', assertions=assertions, assertion_status=assertion_status)
@app.route('/balance-assertions/new', methods=['GET', 'POST'])
def balance_assertions_new():
if request.method == 'GET':
return render_template('general_journal/balance_assertions_edit.html', assertion=None)
quantity = round(float(request.form['amount']) * (10**AMOUNT_DPS))
if request.form['sign'] == 'cr':
quantity = -quantity
# New balance assertion
assertion = BalanceAssertion(
dt=datetime.strptime(request.form['dt'], '%Y-%m-%d'),
description=request.form['description'],
account=request.form['account'],
quantity=quantity,
commodity='$'
)
db_session.add(assertion)
db_session.commit()
return redirect('/balance-assertions')
@app.route('/balance-assertions/edit', methods=['GET', 'POST'])
def balance_assertions_edit():
assertion = db_session.get(BalanceAssertion, request.args['id'])
if not assertion:
abort(404)
if request.method == 'GET':
return render_template('general_journal/balance_assertions_edit.html', assertion=assertion)
quantity = round(float(request.form['amount']) * (10**AMOUNT_DPS))
if request.form['sign'] == 'cr':
quantity = -quantity
# Edit balance assertion
assertion.dt = datetime.strptime(request.form['dt'], '%Y-%m-%d')
assertion.description = request.form['description']
assertion.account = request.form['account']
assertion.quantity = quantity
db_session.commit()
return redirect('/balance-assertions')

View File

@ -0,0 +1,54 @@
{# DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022 Lee Yingtong Li (RunasSudo)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
#}
{% extends 'base.html' %}
{% block title %}Balance assertions{% endblock %}
{% block content %}
<h1 class="h2 my-4">Balance assertions</h1>
<div class="mb-2">
<a href="/balance-assertions/new" class="btn btn-primary"><i class="bi bi-plus-lg"></i> New assertion</a>
</div>
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Account</th>
<th class="text-end">Balance</th>
<th></th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{% for assertion in assertions %}
<tr>
<td>{{ assertion.dt.strftime('%Y-%m-%d') }}</td>
<td>{{ assertion.description }}</td>
<td>{{ assertion.account }}</td>
<td class="text-end">{{ (assertion.balance()|abs).format() }}</td>
<td>{{ 'Dr' if assertion.quantity >= 0 else 'Cr' }}</td>
<td>{% if assertion_status[assertion] %}<i class="bi bi-check-lg"></i>{% else %}<i class="bi bi-x-circle-fill text-danger"></i>{% endif %}</td>
<td><a href="/balance-assertions/edit?id={{ assertion.id }}"><i class="bi bi-pencil"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,61 @@
{# DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022 Lee Yingtong Li (RunasSudo)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
#}
{% extends 'base.html' %}
{% block title %}{{ 'Edit' if assertion else 'New' }} balance assertion{% endblock %}
{% block content %}
<h1 class="h2 my-4">{{ 'Edit' if assertion else 'New' }} balance assertion</h1>
<form method="POST">
<div class="row mb-2">
<label class="col-sm-2 col-form-label">Date</label>
<div class="col-sm-10">
<input type="date" class="form-control" name="dt" value="{{ assertion.dt.strftime('%Y-%m-%d') if assertion }}">
</div>
</div>
<div class="row mb-2">
<label class="col-sm-2 col-form-label">Description</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="description" value="{{ assertion.description if assertion }}">
</div>
</div>
<div class="row mb-2">
<label class="col-sm-2 col-form-label">Account</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="account" value="{{ assertion.account if assertion }}">
</div>
</div>
<div class="row mb-4">
<label class="col-sm-2 col-form-label">Balance</label>
<div class="col-sm-10">
<div class="input-group">
<div class="input-group-text">$</div>
<input type="number" class="form-control" name="amount" step="0.01" value="{{ assertion.balance().quantity_string() if assertion }}">
<select class="form-select" name="sign">
<option value="dr">Dr</option>
<option value="cr">Cr</option>
</select>
</div>
</div>
</div>
<div class="text-end">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
{% endblock %}

View File

@ -23,6 +23,7 @@
<ul>
<li><a href="/general-journal">General journal</a></li>
<li><a href="/statement-lines">Statement lines</a></li>
<li><a href="/balance-assertions">Balance assertions</a></li>
</ul>
<h1 class="h2 my-4">General reports</h1>