Changeset - dc2e2d200d70
[Not reviewed]
0 1 0
Brett Smith - 4 years ago 2020-08-30 15:14:25
brettcsmith@brettcsmith.org
balance_sheet: Fix logger name.
1 file changed with 1 insertions and 1 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reports/balance_sheet.py
Show inline comments
 
"""balance_sheet.py - Balance sheet report"""
 
# Copyright © 2020  Brett Smith
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU Affero General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU Affero General Public License for more details.
 
#
 
# You should have received a copy of the GNU Affero General Public License
 
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 

	
 
import argparse
 
import collections
 
import datetime
 
import enum
 
import logging
 
import operator
 
import os
 
import sys
 

	
 
from decimal import Decimal
 
from pathlib import Path
 

	
 
from typing import (
 
    Any,
 
    Callable,
 
    Collection,
 
    Dict,
 
    Hashable,
 
    Iterable,
 
    Iterator,
 
    List,
 
    Mapping,
 
    NamedTuple,
 
    Optional,
 
    Sequence,
 
    TextIO,
 
    Tuple,
 
    Union,
 
)
 

	
 
import odf.style  # type:ignore[import]
 
import odf.table  # type:ignore[import]
 

	
 
from beancount.parser import printer as bc_printer
 

	
 
from . import core
 
from . import rewrite
 
from .. import books
 
from .. import cliutil
 
from .. import config as configmod
 
from .. import data
 
from .. import ranges
 

	
 
EQUITY_ACCOUNTS = frozenset(['Equity', 'Income', 'Expenses'])
 
PROGNAME = 'balance-sheet-report'
 
logger = logging.getLogger('conservancy_beancount.tools.balance_sheet')
 
logger = logging.getLogger('conservancy_beancount.reports.balance_sheet')
 

	
 
KWArgs = Mapping[str, Any]
 

	
 
class Fund(enum.IntFlag):
 
    RESTRICTED = enum.auto()
 
    UNRESTRICTED = enum.auto()
 
    ANY = RESTRICTED | UNRESTRICTED
 

	
 

	
 
class Period(enum.IntFlag):
 
    OPENING = enum.auto()
 
    PRIOR = enum.auto()
 
    MIDDLE = enum.auto()
 
    PERIOD = enum.auto()
 
    THRU_PRIOR = OPENING | PRIOR
 
    THRU_MIDDLE = THRU_PRIOR | MIDDLE
 
    ANY = THRU_MIDDLE | PERIOD
 

	
 

	
 
class BalanceKey(NamedTuple):
 
    account: data.Account
 
    classification: data.Account
 
    period: Period
 
    fund: Fund
 
    post_type: Optional[str]
 

	
 

	
 
class Balances:
 
    def __init__(self,
 
                 postings: Iterable[data.Posting],
 
                 start_date: datetime.date,
 
                 stop_date: datetime.date,
 
                 fund_key: str='project',
 
                 unrestricted_fund_value: str='Conservancy',
 
    ) -> None:
 
        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 >= 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
 
            else:
 
                period = Period.OPENING
 
            if post.meta.get(fund_key) == unrestricted_fund_value:
 
                fund = Fund.UNRESTRICTED
 
            else:
 
                fund = Fund.RESTRICTED
 
            try:
 
                classification_s = post.account.meta['classification']
 
                if isinstance(classification_s, str):
 
                    classification = data.Account(classification_s)
 
                else:
 
                    raise TypeError()
 
            except (KeyError, TypeError):
 
                classification = post.account
 
            if post.account.root_part() == 'Expenses':
 
                post_type = post.meta.get('expense-type')
 
            else:
 
                post_type = None
 
            key = BalanceKey(post.account, classification, period, fund, post_type)
 
            self.balances[key] += post.at_cost()
 

	
 
    def total(self,
 
              account: Union[None, str, Collection[str]]=None,
 
              classification: Optional[str]=None,
 
              period: int=Period.ANY,
 
              fund: int=Fund.ANY,
 
              post_type: Optional[str]=None,
 
    ) -> core.Balance:
 
        if isinstance(account, str):
 
            account = (account,)
 
        retval = core.MutableBalance()
 
        for key, balance in self.balances.items():
 
            if not (account is None or key.account.is_under(*account)):
 
                pass
0 comments (0 inline, 0 general)