Implement report showing transactions by commodity
This commit is contained in:
parent
91343abd3b
commit
c379a28fbd
@ -169,6 +169,34 @@ def transactions():
|
|||||||
|
|
||||||
return flask.render_template('transactions.html', date=date, pstart=pstart, period=describe_period(date, pstart), account=account, ledger=l, transactions=transactions, opening_balance=opening_balance, closing_balance=closing_balance, report_currency=report_currency, cash=cash, timedelta=timedelta)
|
return flask.render_template('transactions.html', date=date, pstart=pstart, period=describe_period(date, pstart), account=account, ledger=l, transactions=transactions, opening_balance=opening_balance, closing_balance=closing_balance, report_currency=report_currency, cash=cash, timedelta=timedelta)
|
||||||
|
|
||||||
|
@app.route('/transactions_commodity')
|
||||||
|
def transactions_commodity():
|
||||||
|
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
||||||
|
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
||||||
|
account = flask.request.args.get('account', None)
|
||||||
|
cash = flask.request.args.get('cash', False)
|
||||||
|
|
||||||
|
report_currency = Currency(*config['report_currency'])
|
||||||
|
|
||||||
|
# General ledger
|
||||||
|
l = ledger.raw_transactions_at_date(date)
|
||||||
|
if cash:
|
||||||
|
l = accounting.ledger_to_cash(l, report_currency)
|
||||||
|
|
||||||
|
# Unrealized gains
|
||||||
|
l = accounting.add_unrealized_gains(accounting.trial_balance(l, date, pstart), report_currency).ledger
|
||||||
|
|
||||||
|
account = l.get_account(account)
|
||||||
|
transactions = [t for t in l.transactions if t.date <= date and t.date >= pstart and any(p.account == account for p in t.postings)]
|
||||||
|
|
||||||
|
opening_balance = accounting.trial_balance(l, pstart, pstart).get_balance(account)
|
||||||
|
closing_balance = accounting.trial_balance(l, date, pstart).get_balance(account)
|
||||||
|
|
||||||
|
def matching_posting(transaction, amount):
|
||||||
|
return next((p for p in transaction.postings if p.account == account and p.amount.currency == amount.currency), None)
|
||||||
|
|
||||||
|
return flask.render_template('transactions_commodity.html', date=date, pstart=pstart, period=describe_period(date, pstart), account=account, ledger=l, transactions=transactions, opening_balance=opening_balance, closing_balance=closing_balance, report_currency=report_currency, cash=cash, timedelta=timedelta, matching_posting=matching_posting)
|
||||||
|
|
||||||
# Template filters
|
# Template filters
|
||||||
|
|
||||||
@app.template_filter('a')
|
@app.template_filter('a')
|
||||||
@ -185,9 +213,12 @@ def filter_amount_positive(amt):
|
|||||||
return flask.Markup('{:,.2f}'.format(amt.amount).replace(',', ' '))
|
return flask.Markup('{:,.2f}'.format(amt.amount).replace(',', ' '))
|
||||||
#return flask.Markup('{:,.2f} {}'.format(amt.amount, amt.currency.name).replace(',', ' '))
|
#return flask.Markup('{:,.2f} {}'.format(amt.amount, amt.currency.name).replace(',', ' '))
|
||||||
|
|
||||||
@app.template_filter('bb')
|
@app.template_filter('bc')
|
||||||
def filter_balance_positive(balance):
|
def filter_currency_positive(amt):
|
||||||
return flask.Markup('<br>'.join(filter_amount_positive(a) for a in balance.amounts))
|
if amt.currency.is_prefix:
|
||||||
|
return flask.Markup('{}{:,.2f}'.format(amt.currency.name, amt.amount).replace(',', ' '))
|
||||||
|
else:
|
||||||
|
return flask.Markup('{:,.2f} {}'.format(amt.amount, amt.currency.name).replace(',', ' '))
|
||||||
|
|
||||||
# Debug views
|
# Debug views
|
||||||
|
|
||||||
|
@ -19,7 +19,11 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<a class="homelink" href="/">Home</a>
|
<div class="nav-header">
|
||||||
|
{% block links %}
|
||||||
|
<a href="/">Home</a>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block report %}
|
{% block report %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
{% block title %}{% if account %}Account Transactions{% else %}General Ledger{% endif %} as at {{ date.strftime('%d %B %Y') }}{% endblock %}
|
{% block title %}{% if account %}Account Transactions{% else %}General Ledger{% endif %} as at {{ date.strftime('%d %B %Y') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block links %}
|
||||||
|
{{ super() }}
|
||||||
|
<a href="/transactions_commodity?{{ {'date': date.strftime('%Y-%m-%d'), 'pstart': pstart.strftime('%Y-%m-%d'), 'account': account.name, 'cash': 'on' if cash else ''}|urlencode }}">Show commodity detail</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block report %}
|
{% block report %}
|
||||||
{% if account %}
|
{% if account %}
|
||||||
<h1>Account Transactions</h1>
|
<h1>Account Transactions</h1>
|
||||||
|
93
ledger_pyreport/jinja2/transactions_commodity.html
Normal file
93
ledger_pyreport/jinja2/transactions_commodity.html
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
{#
|
||||||
|
ledger-pyreport
|
||||||
|
Copyright © 2020 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_report.html' %}
|
||||||
|
|
||||||
|
{% block title %}Account Transactions as at {{ date.strftime('%d %B %Y') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block report %}
|
||||||
|
<h1>Account Transactions</h1>
|
||||||
|
<h2 style="margin-bottom: 0;">For {{ account.name }}</h2>
|
||||||
|
<h2>For the {{ period }}</h2>
|
||||||
|
|
||||||
|
<table class="ledger">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 5em;">Date</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="h1" style="width: 1em;"></th>
|
||||||
|
<th class="h1" style="text-align: right; width: 5em;">Amount</th>
|
||||||
|
<th class="h1" style="width: 4em;"></th>
|
||||||
|
<th class="h1" style="text-align: right; width: 5em;">Balance</th>
|
||||||
|
<th class="h1" style="width: 4em;"></th>
|
||||||
|
<th class="h1" style="width: 1em;"></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% set ns = namespace(balance=None) %}
|
||||||
|
{% set prevlink = '/transactions_commodity?' + {'date': (pstart - timedelta(days=1)).strftime('%Y-%m-%d'), 'pstart': pstart.replace(year=pstart.year-1).strftime('%Y-%m-%d'), 'account': account.name, 'cash': 'on' if cash else ''}|urlencode %}
|
||||||
|
{% for amount in opening_balance.amounts %}
|
||||||
|
<tr class="total">
|
||||||
|
<td>{% if loop.first %}<a href="{{ prevlink }}">{{ pstart.strftime('%Y-%m-%d') }}</a>{% endif %}</td>
|
||||||
|
<td>{% if loop.first %}<a href="{{ prevlink }}">Opening Balance</a>{% endif %}</td>
|
||||||
|
<td style="text-align: right;"></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td style="text-align: right;"><a href="{{ prevlink }}">{{ amount|abs|bc }}</a></td>
|
||||||
|
<td><a href="{{ prevlink }}">{% if amount.currency.price %}{{ '{' + amount.currency.price|bc + '}' }}{% endif %}</a></td>
|
||||||
|
<td>{% if amount >= 0 %}Dr{% else %}Cr{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% set ns.balance = opening_balance %}
|
||||||
|
|
||||||
|
{% for transaction in transactions %}
|
||||||
|
{% for posting in transaction.postings if posting.account == account %}
|
||||||
|
{% set ns.balance = ns.balance + posting.amount %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for amount in ns.balance.amounts %}
|
||||||
|
{% set posting = matching_posting(transaction, amount) %}
|
||||||
|
<tr{% if loop.first %} style="border-top: 1px solid black;"{% endif %}>
|
||||||
|
<td>{% if loop.first %}{{ transaction.date.strftime('%Y-%m-%d') }}{% endif %}</td>
|
||||||
|
<td>{% if loop.first %}{{ transaction.description }}{% endif %}</td>
|
||||||
|
{% if posting %}
|
||||||
|
<td>{% if posting.amount >= 0 %}Dr{% else %}Cr{% endif %}</td>
|
||||||
|
<td style="text-align: right;">{{ posting.amount|abs|bc }}</td>
|
||||||
|
<td>{% if posting.amount.currency.price %}{{ '{' + posting.amount.currency.price|bc + '}' }}{% endif %}</td>
|
||||||
|
{% else %}
|
||||||
|
<td colspan="3"></td>
|
||||||
|
{% endif %}
|
||||||
|
<td style="text-align: right;">{{ amount|abs|bc }}</td>
|
||||||
|
<td>{% if amount.currency.price %}{{ '{' + amount.currency.price|bc + '}' }}{% endif %}</td>
|
||||||
|
<td>{% if amount >= 0 %}Dr{% else %}Cr{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% set ns.balance = ns.balance.clean() %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for amount in closing_balance.amounts %}
|
||||||
|
<tr class="total explicit-rules" style="{% if loop.first %}border-top: 1pt solid black;{% endif %}{% if loop.last %}border-bottom: 1pt solid black;{% endif %}">
|
||||||
|
<td>{% if loop.first %}{{ date.strftime('%Y-%m-%d') }}{% endif %}</td>
|
||||||
|
<td>{% if loop.first %}Closing Balance{% endif %}</td>
|
||||||
|
<td style="text-align: right;"></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td style="text-align: right;">{{ amount|abs|bc }}</td>
|
||||||
|
<td>{% if amount.currency.price %}{{ '{' + amount.currency.price|bc + '}' }}{% endif %}</td>
|
||||||
|
<td>{% if amount >= 0 %}Dr{% else %}Cr{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
@ -176,10 +176,21 @@ class Amount:
|
|||||||
|
|
||||||
def __neg__(self):
|
def __neg__(self):
|
||||||
return Amount(-self.amount, self.currency)
|
return Amount(-self.amount, self.currency)
|
||||||
|
def __abs__(self):
|
||||||
|
return Amount(abs(self.amount), self.currency)
|
||||||
|
|
||||||
@compatible_currency
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.amount == other
|
if isinstance(other, Amount):
|
||||||
|
if self.amount == 0 and other.amount == 0:
|
||||||
|
return True
|
||||||
|
if other.currency != self.currency:
|
||||||
|
return False
|
||||||
|
return self.amount == other.amount
|
||||||
|
|
||||||
|
if other == 0:
|
||||||
|
return self.amount == 0
|
||||||
|
|
||||||
|
raise TypeError('Cannot compare Amount with non-zero number')
|
||||||
@compatible_currency
|
@compatible_currency
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return self.amount != other
|
return self.amount != other
|
||||||
@ -237,6 +248,9 @@ class Balance:
|
|||||||
new_amount = next((a for a in new_amounts if a.currency == amount.currency), None)
|
new_amount = next((a for a in new_amounts if a.currency == amount.currency), None)
|
||||||
return Balance(new_amounts)
|
return Balance(new_amounts)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
return Balance([a for a in self.amounts if a != 0])
|
||||||
|
|
||||||
def exchange(self, currency, is_cost, date=None, ledger=None):
|
def exchange(self, currency, is_cost, date=None, ledger=None):
|
||||||
result = Amount(0, currency)
|
result = Amount(0, currency)
|
||||||
for amount in self.amounts:
|
for amount in self.amounts:
|
||||||
@ -269,12 +283,18 @@ class Balance:
|
|||||||
new_amount = Amount(0, amount.currency)
|
new_amount = Amount(0, amount.currency)
|
||||||
new_amounts.append(new_amount)
|
new_amounts.append(new_amount)
|
||||||
new_amount.amount += amount.amount
|
new_amount.amount += amount.amount
|
||||||
|
|
||||||
|
#if new_amount == 0:
|
||||||
|
# new_amounts.remove(new_amount)
|
||||||
elif isinstance(other, Amount):
|
elif isinstance(other, Amount):
|
||||||
new_amount = next((a for a in new_amounts if a.currency == other.currency), None)
|
new_amount = next((a for a in new_amounts if a.currency == other.currency), None)
|
||||||
if new_amount is None:
|
if new_amount is None:
|
||||||
new_amount = Amount(0, other.currency)
|
new_amount = Amount(0, other.currency)
|
||||||
new_amounts.append(new_amount)
|
new_amounts.append(new_amount)
|
||||||
new_amount.amount += other.amount
|
new_amount.amount += other.amount
|
||||||
|
|
||||||
|
#if new_amount == 0:
|
||||||
|
# new_amounts.remove(new_amount)
|
||||||
elif other == 0:
|
elif other == 0:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -282,6 +302,9 @@ class Balance:
|
|||||||
|
|
||||||
return Balance(new_amounts)
|
return Balance(new_amounts)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return self + (-other)
|
||||||
|
|
||||||
class Currency:
|
class Currency:
|
||||||
def __init__(self, name, is_prefix, price=None):
|
def __init__(self, name, is_prefix, price=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -52,7 +52,8 @@ table.ledger th.h1 {
|
|||||||
|
|
||||||
table.ledger tr.total td {
|
table.ledger tr.total td {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
table.ledger tr.total:not(.explicit-rules) td {
|
||||||
border-top: 1pt solid black;
|
border-top: 1pt solid black;
|
||||||
border-bottom: 1pt solid black;
|
border-bottom: 1pt solid black;
|
||||||
}
|
}
|
||||||
@ -72,12 +73,18 @@ table.ledger a:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.homelink {
|
.nav-header {
|
||||||
color: #888;
|
color: #888;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
.nav-header a {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.nav-header a:hover {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen {
|
@media screen {
|
||||||
body {
|
body {
|
||||||
|
Reference in New Issue
Block a user