Changeset - a0ff9e6834aa
[Not reviewed]
0 2 0
Brett Smith - 4 years ago 2020-08-18 19:15:02
brettcsmith@brettcsmith.org
balance_sheet: Support arbitrary date ranges.

This lets you do year-over-year comparisons of smaller ranges of time, like
a quarter.
2 files changed with 33 insertions and 19 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reports/balance_sheet.py
Show inline comments
...
 
@@ -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
setup.py
Show inline comments
...
 
@@ -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+',
0 comments (0 inline, 0 general)