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:
parent
9e7df0b020
commit
a0ff9e6834
2 changed files with 33 additions and 19 deletions
|
@ -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
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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+',
|
||||
|
|
Loading…
Reference in a new issue