From 58954aab235a7ede8dbb4fab9af78a96ad9c9310 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sat, 22 Aug 2020 09:49:19 -0400 Subject: [PATCH] fund: Text output readability improvements. Make it look more like the spreadsheets: * Don't normalize Expenses negative. * Consistent account order: Income, then Expenses, then Equity. * Include a bottom line divider for each fund. --- conservancy_beancount/reports/fund.py | 22 ++++++++++++++-------- tests/test_reports_fund.py | 10 +++++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/conservancy_beancount/reports/fund.py b/conservancy_beancount/reports/fund.py index 3468bbb..b26a7a9 100644 --- a/conservancy_beancount/reports/fund.py +++ b/conservancy_beancount/reports/fund.py @@ -82,7 +82,7 @@ from .. import data AccountsMap = Mapping[data.Account, core.PeriodPostings] FundPosts = Tuple[MetaValue, AccountsMap] -EQUITY_ACCOUNTS = ['Equity', 'Income', 'Expenses'] +EQUITY_ACCOUNTS = ['Income', 'Expenses', 'Equity'] INFO_ACCOUNTS = [ 'Assets:Receivable', 'Assets:Prepaid', @@ -106,7 +106,7 @@ class ODSReport(core.BaseODS[FundPosts, None]): headers = [["Fund"], ["Balance as of", self.start_date.isoformat()]] if expanded: sheet_name = "With Breakdowns" - headers += [["Income"], ["Expenses"], ["Equity"]] + headers.extend([acct] for acct in EQUITY_ACCOUNTS) else: sheet_name = "Fund Report" headers += [["Additions"], ["Releases from", "Restrictions"]] @@ -176,10 +176,9 @@ class ODSReport(core.BaseODS[FundPosts, None]): self.sheet = start_sheet def _row_balances(self, accounts_map: AccountsMap) -> Iterator[core.Balance]: - acct_order = ['Income', 'Expenses', 'Equity'] - key_order = [core.OPENING_BALANCE_NAME, *acct_order, core.ENDING_BALANCE_NAME] + key_order = [core.OPENING_BALANCE_NAME, *EQUITY_ACCOUNTS, core.ENDING_BALANCE_NAME] balances: Dict[str, core.Balance] = {key: core.MutableBalance() for key in key_order} - for acct_s, balance in core.account_balances(accounts_map, acct_order): + for acct_s, balance in core.account_balances(accounts_map, EQUITY_ACCOUNTS): if acct_s in balances: balances[acct_s] = balance else: @@ -242,7 +241,9 @@ class TextReport: acct_s = total_fmt.format(self.start_date.isoformat()) elif acct_s is core.ENDING_BALANCE_NAME: acct_s = total_fmt.format(self.stop_date.isoformat()) - yield acct_s, (-balance).format(None, sep='\0').split('\0') + if not acct_s.startswith('Expenses:'): + balance = -balance + yield acct_s, balance.format(None, sep='\0').split('\0') for _, account in core.sort_and_filter_accounts(account_map, INFO_ACCOUNTS): balance = account_map[account].stop_bal if not balance.is_zero(): @@ -262,14 +263,19 @@ class TextReport: print(line_fmt.replace('{:>', '{:^').format("ACCOUNT", "BALANCE"), file=self.out_file) fund_start = f' balance as of {self.start_date.isoformat()}' + fund_end = f' balance as of {self.stop_date.isoformat()}' for acct_s, bal_seq in output: - if acct_s.endswith(fund_start): - print(line_fmt.format('―' * acct_width, '―' * bal_width), + is_end_bal = acct_s.endswith(fund_end) + if is_end_bal or acct_s.endswith(fund_start): + print(line_fmt.format('─' * acct_width, '─' * bal_width), file=self.out_file) bal_iter = iter(bal_seq) print(line_fmt.format(acct_s, next(bal_iter)), file=self.out_file) for bal_s in bal_iter: print(line_fmt.format('', bal_s), file=self.out_file) + if is_end_bal: + print(line_fmt.format('═' * acct_width, '═' * bal_width), + file=self.out_file) class ReportType(enum.Enum): diff --git a/tests/test_reports_fund.py b/tests/test_reports_fund.py index 42c6ed8..a413301 100644 --- a/tests/test_reports_fund.py +++ b/tests/test_reports_fund.py @@ -111,11 +111,13 @@ def check_text_balances(actual, expected, *expect_accounts): balance = Decimal() for expect_account in expect_accounts: expect_amount = expected[expect_account] + balance += expect_amount + if expect_account.startswith('Expenses:'): + expect_amount *= -1 if expect_amount: actual_account, actual_amount = next(actual) assert actual_account == expect_account assert actual_amount == format_amount(expect_amount) - balance += expect_amount return balance def check_text_report(output, project, start_date, stop_date): @@ -142,17 +144,19 @@ def check_text_report(output, project, start_date, stop_date): assert open_amt == format_amount(balance_amount) balance_amount += check_text_balances( actual, expected, + 'Income:Other', + 'Expenses:Other', 'Equity:Funds:Restricted', 'Equity:Funds:Unrestricted', 'Equity:Realized:CurrencyConversion', - 'Income:Other', - 'Expenses:Other', ) + next(actual) end_acct, end_amt = next(actual) assert end_acct == "{} balance as of {}".format( project, stop_date.isoformat(), ) assert end_amt == format_amount(balance_amount) + next(actual) balance_amount += check_text_balances( actual, expected, 'Assets:Receivable:Accounts',