From fda9fbaa6f0c69cbee62dd102448c7adc74674e5 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Fri, 20 Mar 2020 22:14:05 +1100 Subject: [PATCH] Reimplement balance sheet and P&L --- ledger_pyreport/__init__.py | 28 +++++++++++++------------- ledger_pyreport/accounting.py | 31 +++++++++++++++++++---------- ledger_pyreport/jinja2/balance.html | 16 +++++++-------- ledger_pyreport/jinja2/pandl.html | 12 +++++------ ledger_pyreport/model.py | 6 ++++-- 5 files changed, 52 insertions(+), 41 deletions(-) diff --git a/ledger_pyreport/__init__.py b/ledger_pyreport/__init__.py index 97f7ee4..967c455 100644 --- a/ledger_pyreport/__init__.py +++ b/ledger_pyreport/__init__.py @@ -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)] # 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[:]: if all(t.get_balance(account) == 0 for t in trial_balances): 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') def balance(): - raise Exception('NYI') - date = datetime.strptime(flask.request.args['date'], '%Y-%m-%d') pstart = datetime.strptime(flask.request.args['pstart'], '%Y-%m-%d') 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)] 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 - accounts = list(balance_sheets[0].accounts.values()) + accounts = sorted(l.accounts.values(), key=lambda a: a.name) 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) - 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') def pandl(): - raise Exception('NYI') - date_beg = datetime.strptime(flask.request.args['date_beg'], '%Y-%m-%d') date_end = datetime.strptime(flask.request.args['date_end'], '%Y-%m-%d') 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_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 - accounts = list(pandls[0].accounts.values()) + accounts = sorted(l.accounts.values(), key=lambda a: a.name) 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) if date_end == (date_beg.replace(year=date_beg.year + 1) - timedelta(days=1)): @@ -125,7 +125,7 @@ def pandl(): else: 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') def transactions(): diff --git a/ledger_pyreport/accounting.py b/ledger_pyreport/accounting.py index 468f124..fe7ad9e 100644 --- a/ledger_pyreport/accounting.py +++ b/ledger_pyreport/accounting.py @@ -19,6 +19,21 @@ from decimal import Decimal 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): for account in list(tb.ledger.accounts.values()): if not account.is_market: @@ -36,17 +51,11 @@ def add_unrealized_gains(tb, currency): return trial_balance(tb.ledger, tb.date, tb.pstart) -def trial_balance(ledger, date, pstart): - tb = TrialBalance(ledger, date, pstart) +def balance_sheet(tb): + # 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: - 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 + # Add Current Year Earnings account + tb.balances[config['current_year_earnings']] = tb.get_balance(tb.ledger.get_account(config['current_year_earnings'])) + total_pandl return tb diff --git a/ledger_pyreport/jinja2/balance.html b/ledger_pyreport/jinja2/balance.html index 7e1ef07..f6a65dc 100644 --- a/ledger_pyreport/jinja2/balance.html +++ b/ledger_pyreport/jinja2/balance.html @@ -26,7 +26,7 @@ {% endif %} {% 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) %} {% if amount != 0 %}{{ amount|a }}{% endif %} {% endfor %} @@ -37,7 +37,7 @@ {% endmacro %} {% 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 %} {% if loop.first and year_headers %} {{ account_class.bits[-1] }} {{ label }} @@ -53,14 +53,14 @@ Total {{ account_class.bits[-1] }} {{ label }} - {% for balance_sheet in balance_sheets %}{{ (balance_sheet.get_total(account_class.name) * (-1 if invert else 1))|a }}{% endfor %} + {% for balance_sheet in balance_sheets %}{{ (-balance_sheet.get_total(account_class) if invert else balance_sheet.get_total(account_class)).exchange(report_currency, True)|a }}{% endfor %}   {% endfor %} Total {{ label }} - {% for balance_sheet in balance_sheets %}{{ (balance_sheet.get_total(root) * (-1 if invert else 1))|a }}{% endfor %} + {% for balance_sheet in balance_sheets %}{{ (-balance_sheet.get_total(root) if invert else balance_sheet.get_total(root)).exchange(report_currency, True)|a }}{% endfor %} {% endmacro %} @@ -79,24 +79,24 @@ {# Assets #} - {{ do_accounts(config['assets_account'], 'Assets', False, True) }} + {{ do_accounts(ledger.get_account(config['assets_account']), 'Assets', False, True) }} {# Liabilities #} - {{ do_accounts(config['liabilities_account'], 'Liabilities', True, False) }} + {{ do_accounts(ledger.get_account(config['liabilities_account']), 'Liabilities', True, False) }} {# Equity #} - {% 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) }} {% endfor %} - {% for balance_sheet in balance_sheets %}{% endfor %} + {% for balance_sheet in balance_sheets %}{% endfor %}
Assets
 
Liabilities
 
Equity
Total Equity{{ -balance_sheet.get_total(config['equity_account'])|a }}{{ -balance_sheet.get_total(ledger.get_account(config['equity_account'])).exchange(report_currency, True)|a }}
diff --git a/ledger_pyreport/jinja2/pandl.html b/ledger_pyreport/jinja2/pandl.html index d967ad6..45fa1bc 100644 --- a/ledger_pyreport/jinja2/pandl.html +++ b/ledger_pyreport/jinja2/pandl.html @@ -26,7 +26,7 @@ {% endif %} {% 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) %} {% if amount != 0 %}{{ amount|a }}{% endif %} {% endfor %} @@ -47,13 +47,13 @@ {% endif %} - {% 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) }} {% endfor %} Total {{ label }} - {% for pandl in pandls %}{{ (pandl.get_total(root) * (-1 if invert else 1))|a }}{% endfor %} + {% for pandl in pandls %}{{ (-pandl.get_total(root) if invert else pandl.get_total(root)).exchange(report_currency, True)|a }}{% endfor %} {% endmacro %} @@ -70,15 +70,15 @@

For the {{ period }}

- {{ do_accounts(config['income_account'], 'Income', True, True) }} + {{ do_accounts(ledger.get_account(config['income_account']), 'Income', True, True) }} - {{ do_accounts(config['expenses_account'], 'Expenses', False, False) }} + {{ do_accounts(ledger.get_account(config['expenses_account']), 'Expenses', False, False) }} - {% for pandl in pandls %}{% endfor %} + {% for pandl in pandls %}{% endfor %}
 
 
Net Surplus (Loss){{ -(pandl.get_total(config['income_account']) + pandl.get_total(config['expenses_account']))|a }}{{ -(pandl.get_total(ledger.get_account(config['income_account'])) + pandl.get_total(ledger.get_account(config['expenses_account']))).exchange(report_currency, True)|a }}
diff --git a/ledger_pyreport/model.py b/ledger_pyreport/model.py index 0a6d0fc..fa2d9b2 100644 --- a/ledger_pyreport/model.py +++ b/ledger_pyreport/model.py @@ -245,7 +245,7 @@ class Balance: elif isinstance(other, Amount): raise Exception('NYI') elif other == 0: - return len(self.amounts) == 0 + return all(a == 0 for a in self.amounts) else: raise TypeError('Cannot compare Balance with non-zero number') @@ -265,6 +265,8 @@ class Balance: new_amount = Amount(0, other.currency) new_amounts.append(new_amount) new_amount.amount += other.amount + elif other == 0: + pass else: raise Exception('NYI') @@ -296,4 +298,4 @@ class TrialBalance: return self.balances.get(account.name, Balance()) 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())