balance_sheet: Support arbitrary date ranges.

This lets you do year-over-year comparisons of smaller ranges of time, like
a quarter.
This commit is contained in:
Brett Smith 2020-08-18 15:15:02 -04:00
parent 9e7df0b020
commit a0ff9e6834
2 changed files with 33 additions and 19 deletions

View file

@ -71,9 +71,11 @@ class Fund(enum.IntFlag):
class Period(enum.IntFlag):
OPENING = enum.auto()
PRIOR = enum.auto()
MIDDLE = enum.auto()
PERIOD = enum.auto()
BEFORE_PERIOD = OPENING | PRIOR
ANY = OPENING | PRIOR | PERIOD
THRU_PRIOR = OPENING | PRIOR
THRU_MIDDLE = THRU_PRIOR | MIDDLE
ANY = THRU_MIDDLE | PERIOD
class BalanceKey(NamedTuple):
@ -92,24 +94,35 @@ class Balances:
fund_key: str='project',
unrestricted_fund_value: str='Conservancy',
) -> None:
self.prior_range = ranges.DateRange(
cliutil.diff_year(start_date, -1),
cliutil.diff_year(stop_date, -1),
)
assert self.prior_range.stop <= start_date
year_diff = (stop_date - start_date).days // 365
if year_diff == 0:
self.prior_range = ranges.DateRange(
cliutil.diff_year(start_date, -1),
cliutil.diff_year(stop_date, -1),
)
self.period_desc = "Period"
else:
self.prior_range = ranges.DateRange(
cliutil.diff_year(start_date, -year_diff),
start_date,
)
self.period_desc = f"Year{'s' if year_diff > 1 else ''}"
self.middle_range = ranges.DateRange(self.prior_range.stop, start_date)
self.period_range = ranges.DateRange(start_date, stop_date)
self.balances: Mapping[BalanceKey, core.MutableBalance] \
= collections.defaultdict(core.MutableBalance)
for post in postings:
post_date = post.meta.date
if post_date in self.period_range:
if post_date >= stop_date:
continue
elif post_date in self.period_range:
period = Period.PERIOD
elif post_date in self.middle_range:
period = Period.MIDDLE
elif post_date in self.prior_range:
period = Period.PRIOR
elif post_date < self.prior_range.start:
period = Period.OPENING
else:
continue
period = Period.OPENING
if post.account == 'Expenses:CurrencyConversion':
account = data.Account('Income:CurrencyConversion')
else:
@ -214,6 +227,7 @@ class Report(core.BaseODS[Sequence[None], None]):
) -> None:
super().__init__()
self.balances = balances
self.period_desc = balances.period_desc
self.date_fmt = date_fmt
one_day = datetime.timedelta(days=1)
date = balances.period_range.stop - one_day
@ -359,7 +373,7 @@ class Report(core.BaseODS[Sequence[None], None]):
self.start_sheet("Financial Position")
balance_kwargs: Sequence[KWArgs] = [
{'period': Period.ANY},
{'period': Period.BEFORE_PERIOD},
{'period': Period.THRU_PRIOR},
]
asset_totals = self.write_classifications_by_account('Assets', balance_kwargs)
@ -400,7 +414,7 @@ class Report(core.BaseODS[Sequence[None], None]):
"Activities",
["Without Donor", "Restrictions"],
["With Donor", "Restrictions"],
totals_prefix=["Total Year Ended"],
totals_prefix=[f"Total {self.period_desc} Ended"],
)
bal_kwargs: Sequence[Dict[str, Any]] = [
{'period': Period.PERIOD, 'fund': Fund.UNRESTRICTED},
@ -482,7 +496,7 @@ class Report(core.BaseODS[Sequence[None], None]):
for kwargs in bal_kwargs:
if kwargs['period'] is Period.PERIOD:
kwargs['period'] = Period.BEFORE_PERIOD
kwargs['period'] = Period.THRU_MIDDLE
else:
kwargs['period'] = Period.OPENING
equity_totals = [
@ -502,7 +516,7 @@ class Report(core.BaseODS[Sequence[None], None]):
["Program", "Services"],
["Management and", "Administrative"],
["Fundraising"],
totals_prefix=["Total Year Ended"],
totals_prefix=[f"Total {self.period_desc} Ended"],
)
totals = self.write_classifications_by_account('Expenses', [
{'period': Period.PERIOD, 'post_type': 'program'},
@ -558,7 +572,7 @@ class Report(core.BaseODS[Sequence[None], None]):
self.write_totals_row("Net Increase in Cash", period_totals)
begin_totals = [
self.balances.total(classification=self.C_CASH, period=period)
for period in [Period.BEFORE_PERIOD, Period.OPENING]
for period in [Period.THRU_MIDDLE, Period.OPENING]
]
self.write_totals_row("Beginning Cash", begin_totals)
self.write_totals_row(
@ -572,8 +586,8 @@ class Report(core.BaseODS[Sequence[None], None]):
"Trial Balances",
["Account Name"],
["Classification"],
["Balance Ending", self.opening_name],
totals_prefix=["Change During", "Year Ending"],
["Balance Beginning", self.balances.period_range.start.strftime(self.date_fmt)],
totals_prefix=["Change During", f"{self.period_desc} Ending"],
title_fmt="Chart of Accounts with DRAFT {sheet_name}",
)
# Widen text columns

View file

@ -5,7 +5,7 @@ from setuptools import setup
setup(
name='conservancy_beancount',
description="Plugin, library, and reports for reading Conservancy's books",
version='1.8.3',
version='1.8.4',
author='Software Freedom Conservancy',
author_email='info@sfconservancy.org',
license='GNU AGPLv3+',