balance_sheet: Add cash flows report.
With this, the balance sheet report has all the reports from the audit.
This commit is contained in:
parent
eaaf8fe98c
commit
07757e7717
2 changed files with 112 additions and 5 deletions
|
@ -181,9 +181,11 @@ class Balances:
|
||||||
|
|
||||||
|
|
||||||
class Report(core.BaseODS[Sequence[None], None]):
|
class Report(core.BaseODS[Sequence[None], None]):
|
||||||
|
C_CASH = 'Cash'
|
||||||
C_SATISFIED = 'Satisfaction of program restrictions'
|
C_SATISFIED = 'Satisfaction of program restrictions'
|
||||||
EQUITY_ACCOUNTS = ['Equity', 'Income', 'Expenses']
|
EQUITY_ACCOUNTS = frozenset(['Equity', 'Income', 'Expenses'])
|
||||||
NO_BALANCE = core.Balance()
|
NO_BALANCE = core.Balance()
|
||||||
|
SPACE = ' ' * 4
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
balances: Balances,
|
balances: Balances,
|
||||||
|
@ -219,6 +221,7 @@ class Report(core.BaseODS[Sequence[None], None]):
|
||||||
self.write_financial_position()
|
self.write_financial_position()
|
||||||
self.write_activities()
|
self.write_activities()
|
||||||
self.write_functional_expenses()
|
self.write_functional_expenses()
|
||||||
|
self.write_cash_flows()
|
||||||
|
|
||||||
def walk_classifications(self, cseq: Iterable[data.Account]) \
|
def walk_classifications(self, cseq: Iterable[data.Account]) \
|
||||||
-> Iterator[Tuple[str, Optional[data.Account]]]:
|
-> Iterator[Tuple[str, Optional[data.Account]]]:
|
||||||
|
@ -226,11 +229,11 @@ class Report(core.BaseODS[Sequence[None], None]):
|
||||||
for classification in cseq:
|
for classification in cseq:
|
||||||
parts = classification.split(':')
|
parts = classification.split(':')
|
||||||
tail = parts.pop()
|
tail = parts.pop()
|
||||||
tabs = ' ' * 4 * len(parts)
|
space = self.SPACE * len(parts)
|
||||||
if parts != last_prefix:
|
if parts != last_prefix:
|
||||||
yield f'{tabs[4:]}{parts[-1]}', None
|
yield f'{space[len(self.SPACE):]}{parts[-1]}', None
|
||||||
last_prefix = parts
|
last_prefix = parts
|
||||||
yield f'{tabs}{tail}', classification
|
yield f'{space}{tail}', classification
|
||||||
|
|
||||||
def walk_classifications_by_account(
|
def walk_classifications_by_account(
|
||||||
self,
|
self,
|
||||||
|
@ -549,6 +552,7 @@ class Report(core.BaseODS[Sequence[None], None]):
|
||||||
for text, classification in self.walk_classifications_by_account('Expenses'):
|
for text, classification in self.walk_classifications_by_account('Expenses'):
|
||||||
text_cell = self.string_cell(text)
|
text_cell = self.string_cell(text)
|
||||||
if classification is None:
|
if classification is None:
|
||||||
|
self.add_row(text_cell)
|
||||||
if not text[0].isspace():
|
if not text[0].isspace():
|
||||||
self.add_row()
|
self.add_row()
|
||||||
self.add_row(text_cell)
|
self.add_row(text_cell)
|
||||||
|
@ -576,6 +580,109 @@ class Report(core.BaseODS[Sequence[None], None]):
|
||||||
for tot_bal in totals),
|
for tot_bal in totals),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def write_cash_flows(self) -> None:
|
||||||
|
self.use_sheet("Cash Flows")
|
||||||
|
bal_kwargs: Sequence[Dict[str, Any]] = [
|
||||||
|
{'period': Period.PERIOD},
|
||||||
|
{'period': Period.PRIOR},
|
||||||
|
]
|
||||||
|
col_count = len(bal_kwargs) + 1
|
||||||
|
for index in range(col_count):
|
||||||
|
col_style = self.column_style(1.5 if index else 3)
|
||||||
|
self.sheet.addElement(odf.table.TableColumn(stylename=col_style))
|
||||||
|
self.add_row(
|
||||||
|
self.multiline_cell([
|
||||||
|
"DRAFT Statement of Cash Flows",
|
||||||
|
self.period_name,
|
||||||
|
], numbercolumnsspanned=col_count, stylename=self.style_header)
|
||||||
|
)
|
||||||
|
self.add_row()
|
||||||
|
self.add_row(
|
||||||
|
odf.table.TableCell(),
|
||||||
|
self.string_cell(self.period_name, stylename=self.style_huline),
|
||||||
|
self.string_cell(self.opening_name, stylename=self.style_huline),
|
||||||
|
)
|
||||||
|
self.add_row()
|
||||||
|
self.add_row(self.string_cell(
|
||||||
|
"Cash Flows from Operating Activities",
|
||||||
|
stylename=self.style_bold,
|
||||||
|
))
|
||||||
|
self.add_row()
|
||||||
|
|
||||||
|
totals = [
|
||||||
|
-sum((self.balances.total(account=account, **kwargs)
|
||||||
|
for account in self.EQUITY_ACCOUNTS), core.MutableBalance())
|
||||||
|
for kwargs in bal_kwargs
|
||||||
|
]
|
||||||
|
self.add_row(
|
||||||
|
self.string_cell("Change in Net Assets"),
|
||||||
|
*(self.balance_cell(bal) for bal in totals),
|
||||||
|
)
|
||||||
|
self.add_row(self.string_cell(
|
||||||
|
"(Increase) decrease in operating assets:",
|
||||||
|
))
|
||||||
|
for text, classification in self.walk_classifications_by_account('Assets'):
|
||||||
|
text_cell = self.string_cell(self.SPACE + text)
|
||||||
|
if classification is None:
|
||||||
|
self.add_row(text_cell)
|
||||||
|
elif classification == self.C_CASH:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
balances = [
|
||||||
|
-self.balances.total(classification=classification, **kwargs)
|
||||||
|
for kwargs in bal_kwargs
|
||||||
|
]
|
||||||
|
self.add_row(
|
||||||
|
text_cell,
|
||||||
|
*(self.balance_cell(bal) for bal in balances),
|
||||||
|
)
|
||||||
|
for total, bal in zip(totals, balances):
|
||||||
|
total += bal
|
||||||
|
self.add_row(self.string_cell(
|
||||||
|
"Increase (decrease) in operating liabilities:",
|
||||||
|
))
|
||||||
|
for text, classification in self.walk_classifications_by_account('Liabilities'):
|
||||||
|
text_cell = self.string_cell(self.SPACE + text)
|
||||||
|
if classification is None:
|
||||||
|
self.add_row(text_cell)
|
||||||
|
else:
|
||||||
|
balances = [
|
||||||
|
-self.balances.total(classification=classification, **kwargs)
|
||||||
|
for kwargs in bal_kwargs
|
||||||
|
]
|
||||||
|
self.add_row(
|
||||||
|
text_cell,
|
||||||
|
*(self.balance_cell(bal) for bal in balances),
|
||||||
|
)
|
||||||
|
for total, bal in zip(totals, balances):
|
||||||
|
total += bal
|
||||||
|
self.add_row(
|
||||||
|
self.string_cell("Net cash provided by operating activites"),
|
||||||
|
*(self.balance_cell(tot_bal, stylename=self.style_totline)
|
||||||
|
for tot_bal in totals),
|
||||||
|
)
|
||||||
|
self.add_row()
|
||||||
|
|
||||||
|
self.add_row(
|
||||||
|
self.string_cell("Net Increase in Cash"),
|
||||||
|
*(self.balance_cell(tot_bal) for tot_bal in totals),
|
||||||
|
)
|
||||||
|
self.add_row()
|
||||||
|
balances = [
|
||||||
|
self.balances.total(classification=self.C_CASH, period=period)
|
||||||
|
for period in [Period.BEFORE_PERIOD, Period.OPENING]
|
||||||
|
]
|
||||||
|
self.add_row(
|
||||||
|
self.string_cell("Beginning Cash"),
|
||||||
|
*(self.balance_cell(bal) for bal in balances),
|
||||||
|
)
|
||||||
|
self.add_row()
|
||||||
|
self.add_row(
|
||||||
|
self.string_cell("Ending Cash"),
|
||||||
|
*(self.balance_cell(tot + bal, stylename=self.style_bottomline)
|
||||||
|
for tot, bal in zip(totals, balances)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments(arglist: Optional[Sequence[str]]=None) -> argparse.Namespace:
|
def parse_arguments(arglist: Optional[Sequence[str]]=None) -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(prog=PROGNAME)
|
parser = argparse.ArgumentParser(prog=PROGNAME)
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -5,7 +5,7 @@ from setuptools import setup
|
||||||
setup(
|
setup(
|
||||||
name='conservancy_beancount',
|
name='conservancy_beancount',
|
||||||
description="Plugin, library, and reports for reading Conservancy's books",
|
description="Plugin, library, and reports for reading Conservancy's books",
|
||||||
version='1.7.4',
|
version='1.8.0',
|
||||||
author='Software Freedom Conservancy',
|
author='Software Freedom Conservancy',
|
||||||
author_email='info@sfconservancy.org',
|
author_email='info@sfconservancy.org',
|
||||||
license='GNU AGPLv3+',
|
license='GNU AGPLv3+',
|
||||||
|
|
Loading…
Reference in a new issue