From c144124fc14d1b470b39892c420dfa4b82d5def6 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 7 Jun 2020 22:43:11 +1000 Subject: [PATCH] Correctly account for unrealised gains across multiple years --- README.md | 2 ++ ledger_pyreport/accounting.py | 23 +++++++++++++---------- ledger_pyreport/model.py | 5 +++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index fcd8e5b..bd81c19 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ Ledger-pyreport by default observes the convention that positive amounts in Ledg A commodity which has *any* price data in Ledger (including those specified through `@` or `@@`, or explicitly through `P`) will be regarded as a commodity measured at fair market value, and automatically revalued accordingly. A commodity which has *no* price data in Ledger (i.e. lot prices through `{…}` or `{{…}}` only) will be regarded as a commodity measured at historical cost, and will not be subsequently revalued. +In accordance with accrual accounting, unrealised gains are charged to the Unrealized Gains income/expense account shown in *config.yml* in the period in which they occur. Subsequent realisations of those gains should be charged against the Unrealized Gains account within the Ledger journal. Alternatively, if charged to a different account (e.g. a realised Capital Gains income account), note that the Unrealized Gains account will contain a *contra* balance corresponding to previous years gains which are now realised. + ## Comparative statements ledger-pyreport can be used to produce comparative statements, for example ‘compare *n* years’ or ‘compare *n* months’. diff --git a/ledger_pyreport/accounting.py b/ledger_pyreport/accounting.py index 91464b6..dbe24d3 100644 --- a/ledger_pyreport/accounting.py +++ b/ledger_pyreport/accounting.py @@ -47,19 +47,22 @@ def trial_balance(ledger, date, pstart, commodity, label=None): for account in set(list(r_date.keys()) + list(r_pstart.keys())): if account in r_pstart: - # Charge previous unrealized gains to Accumulated OCI - #r_pstart[account].postings[1].account = ledger.get_account(config['accumulated_oci']) - accumulated = r_pstart[account].postings[0].amount + tb_date.balances[account.name] = tb_date.get_balance(account) + r_pstart[account].postings[0].amount + if r_pstart[account].postings[1].account.is_income or r_pstart[account].postings[1].account.is_expense: + tb_date.balances[config['retained_earnings']] = tb_date.get_balance(ledger.get_account(config['retained_earnings'])) - r_pstart[account].postings[0].amount + elif r_pstart[account].postings[1].account.is_oci: + tb_date.balances[config['accumulated_oci']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - r_pstart[account].postings[0].amount + else: + tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - r_pstart[account].postings[0].amount - tb_date.balances[account.name] = tb_date.get_balance(account) + accumulated - tb_date.balances[config['accumulated_oci']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - accumulated + # Reversing entry + trn_reversal = r_pstart[account].reverse(None, pstart, '') + ledger.transactions.insert(0, trn_reversal) + + tb_date.balances[account.name] = tb_date.get_balance(account) + trn_reversal.postings[0].amount + tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['unrealized_gains'])) - trn_reversal.postings[0].amount if account in r_date: - if account in r_pstart: - # Adjust for this year's unrealized gains only - r_date[account].postings[0].amount -= accumulated - r_date[account].postings[1].amount += accumulated - tb_date.balances[account.name] = tb_date.get_balance(account) + r_date[account].postings[0].amount tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['unrealized_gains'])) - r_date[account].postings[0].amount diff --git a/ledger_pyreport/model.py b/ledger_pyreport/model.py index ddb28ae..c6644e5 100644 --- a/ledger_pyreport/model.py +++ b/ledger_pyreport/model.py @@ -84,6 +84,11 @@ class Transaction: result.append(' {} {}'.format(posting.account.name, posting.amount.tostr(False))) return '\n'.join(result) + def reverse(self, id, date, description, code=None, uuid=None): + result = Transaction(self.ledger, id, date, description, code, uuid) + result.postings = [Posting(p.transaction, p.account, -p.amount, p.comment, p.state) for p in self.postings] + return result + @property def has_comment_detail(self): return any(p.comment for p in self.postings)