Reimplement balance sheet and P&L
This commit is contained in:
parent
2bc6bb0e22
commit
fda9fbaa6f
@ -69,17 +69,15 @@ def trial():
|
|||||||
trial_balances = [accounting.add_unrealized_gains(accounting.trial_balance(l, d, p), report_currency) for d, p in zip(dates, pstarts)]
|
trial_balances = [accounting.add_unrealized_gains(accounting.trial_balance(l, d, p), report_currency) for d, p in zip(dates, pstarts)]
|
||||||
|
|
||||||
# Delete accounts with always zero balances
|
# Delete accounts with always zero balances
|
||||||
accounts = list(trial_balances[0].ledger.accounts.values())
|
accounts = sorted(l.accounts.values(), key=lambda a: a.name)
|
||||||
for account in accounts[:]:
|
for account in accounts[:]:
|
||||||
if all(t.get_balance(account) == 0 for t in trial_balances):
|
if all(t.get_balance(account) == 0 for t in trial_balances):
|
||||||
accounts.remove(account)
|
accounts.remove(account)
|
||||||
|
|
||||||
return flask.render_template('trial_multiple.html', trial_balances=trial_balances, accounts=sorted(accounts, key=lambda a: a.name), report_currency=report_currency)
|
return flask.render_template('trial_multiple.html', trial_balances=trial_balances, accounts=accounts, report_currency=report_currency)
|
||||||
|
|
||||||
@app.route('/balance')
|
@app.route('/balance')
|
||||||
def balance():
|
def balance():
|
||||||
raise Exception('NYI')
|
|
||||||
|
|
||||||
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d')
|
||||||
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d')
|
||||||
compare = int(flask.request.args['compare'])
|
compare = int(flask.request.args['compare'])
|
||||||
@ -88,20 +86,20 @@ def balance():
|
|||||||
dates = [date.replace(year=date.year - i) for i in range(0, compare + 1)]
|
dates = [date.replace(year=date.year - i) for i in range(0, compare + 1)]
|
||||||
pstarts = [pstart.replace(year=pstart.year - i) for i in range(0, compare + 1)]
|
pstarts = [pstart.replace(year=pstart.year - i) for i in range(0, compare + 1)]
|
||||||
|
|
||||||
balance_sheets = [accounting.balance_sheet(d, p) for d, p in zip(dates, pstarts)]
|
report_currency = Currency(*config['report_currency'])
|
||||||
|
l = ledger.raw_transactions_at_date(date)
|
||||||
|
balance_sheets = [accounting.balance_sheet(accounting.add_unrealized_gains(accounting.trial_balance(l, d, p), report_currency)) for d, p in zip(dates, pstarts)]
|
||||||
|
|
||||||
# Delete accounts with always zero balances
|
# Delete accounts with always zero balances
|
||||||
accounts = list(balance_sheets[0].accounts.values())
|
accounts = sorted(l.accounts.values(), key=lambda a: a.name)
|
||||||
for account in accounts[:]:
|
for account in accounts[:]:
|
||||||
if all(b.get_balance(account.name) == 0 and b.get_total(account.name) == 0 for b in balance_sheets):
|
if all(b.get_balance(account) == 0 and b.get_total(account) == 0 for b in balance_sheets):
|
||||||
accounts.remove(account)
|
accounts.remove(account)
|
||||||
|
|
||||||
return flask.render_template('balance.html', balance_sheets=balance_sheets, accounts=accounts, config=ledger.config)
|
return flask.render_template('balance.html', ledger=l, balance_sheets=balance_sheets, accounts=accounts, config=config, report_currency=report_currency)
|
||||||
|
|
||||||
@app.route('/pandl')
|
@app.route('/pandl')
|
||||||
def pandl():
|
def pandl():
|
||||||
raise Exception('NYI')
|
|
||||||
|
|
||||||
date_beg = datetime.strptime(flask.request.args['date_beg'], '%Y-%m-%d')
|
date_beg = datetime.strptime(flask.request.args['date_beg'], '%Y-%m-%d')
|
||||||
date_end = datetime.strptime(flask.request.args['date_end'], '%Y-%m-%d')
|
date_end = datetime.strptime(flask.request.args['date_end'], '%Y-%m-%d')
|
||||||
compare = int(flask.request.args['compare'])
|
compare = int(flask.request.args['compare'])
|
||||||
@ -110,12 +108,14 @@ def pandl():
|
|||||||
dates_beg = [date_beg.replace(year=date_beg.year - i) for i in range(0, compare + 1)]
|
dates_beg = [date_beg.replace(year=date_beg.year - i) for i in range(0, compare + 1)]
|
||||||
dates_end = [date_end.replace(year=date_end.year - i) for i in range(0, compare + 1)]
|
dates_end = [date_end.replace(year=date_end.year - i) for i in range(0, compare + 1)]
|
||||||
|
|
||||||
pandls = [ledger.trial_balance(de, db) for de, db in zip(dates_end, dates_beg)]
|
report_currency = Currency(*config['report_currency'])
|
||||||
|
l = ledger.raw_transactions_at_date(date_end)
|
||||||
|
pandls = [accounting.trial_balance(l, de, db) for de, db in zip(dates_end, dates_beg)]
|
||||||
|
|
||||||
# Delete accounts with always zero balances
|
# Delete accounts with always zero balances
|
||||||
accounts = list(pandls[0].accounts.values())
|
accounts = sorted(l.accounts.values(), key=lambda a: a.name)
|
||||||
for account in accounts[:]:
|
for account in accounts[:]:
|
||||||
if all(p.get_balance(account.name) == 0 and p.get_total(account.name) == 0 for p in pandls):
|
if all(p.get_balance(account) == 0 and p.get_total(account) == 0 for p in pandls):
|
||||||
accounts.remove(account)
|
accounts.remove(account)
|
||||||
|
|
||||||
if date_end == (date_beg.replace(year=date_beg.year + 1) - timedelta(days=1)):
|
if date_end == (date_beg.replace(year=date_beg.year + 1) - timedelta(days=1)):
|
||||||
@ -125,7 +125,7 @@ def pandl():
|
|||||||
else:
|
else:
|
||||||
period = 'period from {} to {}'.format(date_beg.strftime('%d %B %Y'), date_end.strftime('%d %B %Y'))
|
period = 'period from {} to {}'.format(date_beg.strftime('%d %B %Y'), date_end.strftime('%d %B %Y'))
|
||||||
|
|
||||||
return flask.render_template('pandl.html', period=period, pandls=pandls, accounts=accounts, config=ledger.config)
|
return flask.render_template('pandl.html', period=period, ledger=l, pandls=pandls, accounts=accounts, config=config, report_currency=report_currency)
|
||||||
|
|
||||||
@app.route('/transactions')
|
@app.route('/transactions')
|
||||||
def transactions():
|
def transactions():
|
||||||
|
@ -19,6 +19,21 @@ from decimal import Decimal
|
|||||||
|
|
||||||
from .model import *
|
from .model import *
|
||||||
|
|
||||||
|
def trial_balance(ledger, date, pstart):
|
||||||
|
tb = TrialBalance(ledger, date, pstart)
|
||||||
|
|
||||||
|
for transaction in ledger.transactions:
|
||||||
|
if transaction.date > date:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for posting in transaction.postings:
|
||||||
|
if (posting.account.is_income or posting.account.is_expense) and transaction.date < pstart:
|
||||||
|
tb.balances[config['retained_earnings']] = tb.get_balance(ledger.get_account(config['retained_earnings'])) + posting.amount
|
||||||
|
else:
|
||||||
|
tb.balances[posting.account.name] = tb.get_balance(posting.account) + posting.amount
|
||||||
|
|
||||||
|
return tb
|
||||||
|
|
||||||
def add_unrealized_gains(tb, currency):
|
def add_unrealized_gains(tb, currency):
|
||||||
for account in list(tb.ledger.accounts.values()):
|
for account in list(tb.ledger.accounts.values()):
|
||||||
if not account.is_market:
|
if not account.is_market:
|
||||||
@ -36,17 +51,11 @@ def add_unrealized_gains(tb, currency):
|
|||||||
|
|
||||||
return trial_balance(tb.ledger, tb.date, tb.pstart)
|
return trial_balance(tb.ledger, tb.date, tb.pstart)
|
||||||
|
|
||||||
def trial_balance(ledger, date, pstart):
|
def balance_sheet(tb):
|
||||||
tb = TrialBalance(ledger, date, pstart)
|
# Calculate Profit/Loss
|
||||||
|
total_pandl = tb.get_total(tb.ledger.get_account(config['income_account'])) + tb.get_total(tb.ledger.get_account(config['expenses_account']))
|
||||||
|
|
||||||
for transaction in ledger.transactions:
|
# Add Current Year Earnings account
|
||||||
if transaction.date > date:
|
tb.balances[config['current_year_earnings']] = tb.get_balance(tb.ledger.get_account(config['current_year_earnings'])) + total_pandl
|
||||||
continue
|
|
||||||
|
|
||||||
for posting in transaction.postings:
|
|
||||||
if (posting.account.is_income or posting.account.is_expense) and transaction.date < pstart:
|
|
||||||
tb.balances[config['retained_earnings']] = tb.get_balance(ledger.get_account(config['retained_earnings'])) + posting.amount
|
|
||||||
else:
|
|
||||||
tb.balances[posting.account.name] = tb.get_balance(posting.account) + posting.amount
|
|
||||||
|
|
||||||
return tb
|
return tb
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% for balance_sheet in balance_sheets %}
|
{% for balance_sheet in balance_sheets %}
|
||||||
{% set amount = balance_sheet.get_balance(account.name) * (-1 if invert else 1) %}
|
{% set amount = (-balance_sheet.get_balance(account) if invert else balance_sheet.get_balance(account)).exchange(report_currency, True) %}
|
||||||
<td>{% if amount != 0 %}<a href="/transactions?{{ {'date': balance_sheet.date.strftime('%Y-%m-%d'), 'pstart': balance_sheet.pstart.strftime('%Y-%m-%d'), 'account': account.name}|urlencode }}">{{ amount|a }}</a>{% endif %}</td>
|
<td>{% if amount != 0 %}<a href="/transactions?{{ {'date': balance_sheet.date.strftime('%Y-%m-%d'), 'pstart': balance_sheet.pstart.strftime('%Y-%m-%d'), 'account': account.name}|urlencode }}">{{ amount|a }}</a>{% endif %}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro do_accounts(root, label, invert, year_headers) %}
|
{% macro do_accounts(root, label, invert, year_headers) %}
|
||||||
{% for account_class in balance_sheets[0].get_account(root).children if account_class in accounts %}
|
{% for account_class in root.children if account_class in accounts %}
|
||||||
<tr>
|
<tr>
|
||||||
{% if loop.first and year_headers %}
|
{% if loop.first and year_headers %}
|
||||||
<th class="h2">{{ account_class.bits[-1] }} {{ label }}</th>
|
<th class="h2">{{ account_class.bits[-1] }} {{ label }}</th>
|
||||||
@ -53,14 +53,14 @@
|
|||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Total {{ account_class.bits[-1] }} {{ label }}</td>
|
<td>Total {{ account_class.bits[-1] }} {{ label }}</td>
|
||||||
{% for balance_sheet in balance_sheets %}<td>{{ (balance_sheet.get_total(account_class.name) * (-1 if invert else 1))|a }}</td>{% endfor %}
|
{% for balance_sheet in balance_sheets %}<td>{{ (-balance_sheet.get_total(account_class) if invert else balance_sheet.get_total(account_class)).exchange(report_currency, True)|a }}</td>{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Total {{ label }}</td>
|
<td>Total {{ label }}</td>
|
||||||
{% for balance_sheet in balance_sheets %}<td>{{ (balance_sheet.get_total(root) * (-1 if invert else 1))|a }}</td>{% endfor %}
|
{% for balance_sheet in balance_sheets %}<td>{{ (-balance_sheet.get_total(root) if invert else balance_sheet.get_total(root)).exchange(report_currency, True)|a }}</td>{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
@ -79,24 +79,24 @@
|
|||||||
<table class="ledger onedesc">
|
<table class="ledger onedesc">
|
||||||
{# Assets #}
|
{# Assets #}
|
||||||
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Assets</th></tr>
|
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Assets</th></tr>
|
||||||
{{ do_accounts(config['assets_account'], 'Assets', False, True) }}
|
{{ do_accounts(ledger.get_account(config['assets_account']), 'Assets', False, True) }}
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
{# Liabilities #}
|
{# Liabilities #}
|
||||||
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Liabilities</th></tr>
|
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Liabilities</th></tr>
|
||||||
{{ do_accounts(config['liabilities_account'], 'Liabilities', True, False) }}
|
{{ do_accounts(ledger.get_account(config['liabilities_account']), 'Liabilities', True, False) }}
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
{# Equity #}
|
{# Equity #}
|
||||||
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Equity</th></tr>
|
<tr><th class="h1" colspan="{{ balance_sheets|length + 1 }}">Equity</th></tr>
|
||||||
|
|
||||||
{% for account in balance_sheets[0].get_account(config['equity_account']).children if account in accounts %}
|
{% for account in ledger.get_account(config['equity_account']).children if account in accounts %}
|
||||||
{{ print_rows(account, invert=True) }}
|
{{ print_rows(account, invert=True) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Total Equity</td>
|
<td>Total Equity</td>
|
||||||
{% for balance_sheet in balance_sheets %}<td>{{ -balance_sheet.get_total(config['equity_account'])|a }}</td>{% endfor %}
|
{% for balance_sheet in balance_sheets %}<td>{{ -balance_sheet.get_total(ledger.get_account(config['equity_account'])).exchange(report_currency, True)|a }}</td>{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% for pandl in pandls %}
|
{% for pandl in pandls %}
|
||||||
{% set amount = pandl.get_balance(account.name) * (-1 if invert else 1) %}
|
{% set amount = (-pandl.get_balance(account) if invert else pandl.get_balance(account)).exchange(report_currency, True) %}
|
||||||
<td>{% if amount != 0 %}<a href="/transactions?{{ {'date': pandl.date.strftime('%Y-%m-%d'), 'pstart': pandl.pstart.strftime('%Y-%m-%d'), 'account': account.name}|urlencode }}">{{ amount|a }}</a>{% endif %}</td>
|
<td>{% if amount != 0 %}<a href="/transactions?{{ {'date': pandl.date.strftime('%Y-%m-%d'), 'pstart': pandl.pstart.strftime('%Y-%m-%d'), 'account': account.name}|urlencode }}">{{ amount|a }}</a>{% endif %}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -47,13 +47,13 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% for account in pandls[0].get_account(root).children if account in accounts %}
|
{% for account in root.children if account in accounts %}
|
||||||
{{ print_rows(account, invert=invert) }}
|
{{ print_rows(account, invert=invert) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Total {{ label }}</td>
|
<td>Total {{ label }}</td>
|
||||||
{% for pandl in pandls %}<td>{{ (pandl.get_total(root) * (-1 if invert else 1))|a }}</td>{% endfor %}
|
{% for pandl in pandls %}<td>{{ (-pandl.get_total(root) if invert else pandl.get_total(root)).exchange(report_currency, True)|a }}</td>{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
@ -70,15 +70,15 @@
|
|||||||
<h2>For the {{ period }}</h2>
|
<h2>For the {{ period }}</h2>
|
||||||
|
|
||||||
<table class="ledger onedesc">
|
<table class="ledger onedesc">
|
||||||
{{ do_accounts(config['income_account'], 'Income', True, True) }}
|
{{ do_accounts(ledger.get_account(config['income_account']), 'Income', True, True) }}
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
{{ do_accounts(config['expenses_account'], 'Expenses', False, False) }}
|
{{ do_accounts(ledger.get_account(config['expenses_account']), 'Expenses', False, False) }}
|
||||||
<tr><td colspan="2"> </td></tr>
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
<tr class="total">
|
<tr class="total">
|
||||||
<td>Net Surplus (Loss)</td>
|
<td>Net Surplus (Loss)</td>
|
||||||
{% for pandl in pandls %}<td>{{ -(pandl.get_total(config['income_account']) + pandl.get_total(config['expenses_account']))|a }}</td>{% endfor %}
|
{% for pandl in pandls %}<td>{{ -(pandl.get_total(ledger.get_account(config['income_account'])) + pandl.get_total(ledger.get_account(config['expenses_account']))).exchange(report_currency, True)|a }}</td>{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
|
@ -245,7 +245,7 @@ class Balance:
|
|||||||
elif isinstance(other, Amount):
|
elif isinstance(other, Amount):
|
||||||
raise Exception('NYI')
|
raise Exception('NYI')
|
||||||
elif other == 0:
|
elif other == 0:
|
||||||
return len(self.amounts) == 0
|
return all(a == 0 for a in self.amounts)
|
||||||
else:
|
else:
|
||||||
raise TypeError('Cannot compare Balance with non-zero number')
|
raise TypeError('Cannot compare Balance with non-zero number')
|
||||||
|
|
||||||
@ -265,6 +265,8 @@ class Balance:
|
|||||||
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
|
||||||
|
elif other == 0:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception('NYI')
|
raise Exception('NYI')
|
||||||
|
|
||||||
@ -296,4 +298,4 @@ class TrialBalance:
|
|||||||
return self.balances.get(account.name, Balance())
|
return self.balances.get(account.name, Balance())
|
||||||
|
|
||||||
def get_total(self, account):
|
def get_total(self, account):
|
||||||
return self.get_balance(account) + sum(self.get_total(a) for a in account.children)
|
return self.get_balance(account) + sum((self.get_total(a) for a in account.children), Balance())
|
||||||
|
Reference in New Issue
Block a user