Example #1
0
    def monthly_income_expenses_totals(self):
        month_tuples = self._month_tuples(self.entries)
        monthly_totals = []
        for begin_date, end_date in month_tuples:
            entries, index = summarize.clamp_opt(self.entries, begin_date, end_date + timedelta(days=1),
                                                          self.options_map)

            income_totals = self._table_totals(realization.get(realization.realize(entries, self.account_types), self.options_map['name_income']))
            expenses_totals = self._table_totals(realization.get(realization.realize(entries, self.account_types), self.options_map['name_expenses']))

            # FIXME find better way to only include relevant totals (lots of ZERO-ones at the beginning)
            sum_ = ZERO
            for currency, number in income_totals.items():
                sum_ += number
            for currency, number in expenses_totals.items():
                sum_ += number

            if sum_ != ZERO:
                monthly_totals.append({
                    'begin_date': begin_date,
                    'end_date': end_date,
                    'income_totals': income_totals,
                    'expenses_totals': expenses_totals
                })

        return monthly_totals
Example #2
0
    def _initialize(self, options_map):
        """Compute the list of filtered entries and realization trees."""

        # Get the filtered list of entries.
        self.entries, self.begin_index, self.price_date = self.apply_filter(
            self.all_entries, options_map)

        # Compute the list of entries for the opening balances sheet.
        self.opening_entries = (self.entries[:self.begin_index]
                                if self.begin_index is not None
                                else [])

        # Compute the list of entries that includes transfer entries of the
        # income/expenses amounts to the balance sheet's equity (as "net
        # income"). This is used to render the end-period balance sheet, with
        # the current period's net income, closing the period.
        self.closing_entries = summarize.cap_opt(self.entries, options_map)

        # Realize the three sets of entries.
        account_types = options.get_account_types(options_map)
        with misc_utils.log_time('realize_opening', logging.info):
            self.opening_real_accounts = realization.realize(self.opening_entries,
                                                             account_types)

        with misc_utils.log_time('realize', logging.info):
            self.real_accounts = realization.realize(self.entries,
                                                     account_types)

        with misc_utils.log_time('realize_closing', logging.info):
            self.closing_real_accounts = realization.realize(self.closing_entries,
                                                             account_types)

        assert self.real_accounts is not None
        assert self.closing_real_accounts is not None
Example #3
0
    def apply_filters(self):
        self.entries = self.all_entries

        if self.filters['time']:
            try:
                begin_date, end_date = parse_date(self.filters['time'])
                self.entries, _ = summarize.clamp_opt(self.entries, begin_date, end_date, self.options)
            except TypeError:
                raise FilterException('Failed to parse date string: {}'.format(self.filters['time']))

        if self.filters['tag']:
            self.entries = [entry
                            for entry in self.entries
                            if isinstance(entry, Transaction) and entry.tags and (entry.tags & set(self.filters['tag']))]

        if self.filters['payee']:
            self.entries = [entry
                            for entry in self.entries
                            if (isinstance(entry, Transaction) and entry.payee and (entry.payee in self.filters['payee']))
                            or (isinstance(entry, Transaction) and not entry.payee and ('' in self.filters['payee']))]

        if self.filters['account']:
            self.entries = [entry
                            for entry in self.entries
                            if isinstance(entry, Transaction) and
                                any(has_component(posting.account, self.filters['account'])
                                    for posting in entry.postings)]

        self.root_account = realization.realize(self.entries, self.account_types)
        self.all_accounts = self._account_components()
        self.all_accounts_leaf_only = self._account_components(leaf_only=True)

        self.closing_entries = summarize.cap_opt(self.entries, self.options)
        self.closing_real_accounts = realization.realize(self.closing_entries, self.account_types)
Example #4
0
    def interval_balances(self, interval, account_name, accumulate=False):
        """Balances by interval.

        Arguments:
            interval: An interval.
            account_name: An account name.
            accumulate: A boolean, ``True`` if the balances for an interval
                should include all entries up to the end of the interval.

        Returns:
            A list of RealAccount instances for all the intervals.
        """
        min_accounts = [
            account for account in self.accounts.keys()
            if account.startswith(account_name)
        ]

        interval_tuples = list(
            reversed(list(pairwise(self.interval_ends(interval)))))

        interval_balances = [
            realization.realize(
                list(
                    iter_entry_dates(
                        self.entries,
                        datetime.date.min if accumulate else begin_date,
                        end_date,
                    )),
                min_accounts,
            ) for begin_date, end_date in interval_tuples
        ]

        return interval_balances, interval_tuples
Example #5
0
 def test_dump(self, entries, _, __):
     """
     2012-01-01 open Assets:Bank1:Checking
     2012-01-01 open Assets:Bank1:Savings
     2012-01-01 open Assets:Bank2:Checking
     2012-01-01 open Expenses:Restaurant
     2012-01-01 open Expenses:Movie
     2012-01-01 open Liabilities:CreditCard
     2012-01-01 open Equity:Opening-Balances
     """
     real_account = realization.realize(entries)
     lines = realization.dump(real_account)
     self.assertEqual([
         ('|-- Assets              ', '|   |                   '),
         ('|   |-- Bank1           ', '|   |   |               '),
         ('|   |   |-- Checking    ', '|   |   |               '),
         ('|   |   `-- Savings     ', '|   |                   '),
         ('|   `-- Bank2           ', '|       |               '),
         ('|       `-- Checking    ', '|                       '),
         ('|-- Equity              ', '|   |                   '),
         ('|   `-- Opening-Balances', '|                       '),
         ('|-- Expenses            ', '|   |                   '),
         ('|   |-- Movie           ', '|   |                   '),
         ('|   `-- Restaurant      ', '|                       '),
         ('`-- Liabilities         ', '    |                   '),
         ('    `-- CreditCard      ', '                        '),
     ], [(first_line, cont_line) for first_line, cont_line, _1 in lines])
Example #6
0
    def test_dump_balances(self, entries, _, options_map):
        """
        2012-01-01 open Expenses:Restaurant
        2012-01-01 open Liabilities:US:CreditCard
        2012-01-01 open Liabilities:CA:CreditCard

        2014-05-30 *
          Liabilities:CA:CreditCard   123.45 CAD
          Expenses:Restaurant

        2014-05-31 *
          Liabilities:US:CreditCard   123.45 USD
          Expenses:Restaurant

        """
        real_account = realization.realize(entries)
        dformat = options_map['dcontext'].build(
            alignment=display_context.Align.DOT, reserved=2)
        self.assertLines(
            """
            |-- Expenses
            |   `-- Restaurant          -123.45 CAD
            |                           -123.45 USD
            `-- Liabilities
                |-- CA
                |   `-- CreditCard       123.45 CAD
                `-- US
                    `-- CreditCard       123.45 USD
        """, realization.dump_balances(real_account, dformat))
Example #7
0
def get_final_holdings(entries, included_account_types=None, price_map=None,
                       date=None):
    """Get a list of holdings by account (as Postings)."""

    simple_entries = [entry for entry in entries
                      if (not isinstance(entry, Transaction) or
                          entry.flag != flags.FLAG_UNREALIZED)]

    root_account = realization.realize(simple_entries)

    holdings = []

    for real_account in sorted(list(realization.iter_children(root_account)),
                               key=lambda ra: ra.account):
        account_type = account_types.get_account_type(real_account.account)
        if (included_account_types and
                account_type not in included_account_types):
            continue
        for pos in real_account.balance:
            price = None
            if pos.cost and price_map:
                base_quote = (pos.units.currency, pos.cost.currency)
                _, price = prices.get_price(price_map, base_quote, date)
            holdings.append(Posting(real_account.account,
                                    pos.units,
                                    pos.cost,
                                    price, None, None))

    return holdings
Example #8
0
    def monthly_income_expenses_totals(self):
        month_tuples = self._month_tuples(self.entries)
        monthly_totals = []
        for begin_date, end_date in month_tuples:
            entries = self._entries_in_inclusive_range(begin_date, end_date)
            realized = realization.realize(entries, self.account_types)
            income_totals = self._table_totals(realization.get(realized, self.account_types.income))
            expenses_totals = self._table_totals(realization.get(realized, self.account_types.expenses))

            # FIXME find better way to only include relevant totals (lots of ZERO-ones at the beginning)
            sum_ = ZERO
            for currency, number in income_totals.items():
                sum_ += number
            for currency, number in expenses_totals.items():
                sum_ += number

            if sum_ != ZERO:
                monthly_totals.append({
                    'begin_date': begin_date,
                    'end_date': end_date,
                    'income_totals': income_totals,
                    'expenses_totals': expenses_totals
                })

        return monthly_totals
Example #9
0
    def load_file(self):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        with open(self.beancount_file_path, encoding='utf8') as f:
            self.source = f.read()

        self.entries, self._errors, self.options = loader.load_file(self.beancount_file_path)
        self.all_entries = self.entries
        self.price_map = prices.build_price_map(self.all_entries)

        self.title = self.options['title']

        self.errors = []
        for error in self._errors:
            self.errors.append({
                'file': error.source['filename'],
                'line': error.source['lineno'],
                'error': error.message,
                'entry': error.entry  # TODO render entry
            })

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))

        self.account_types = options.get_account_types(self.options)
        self.real_accounts = realization.realize(self.entries, self.account_types)
        self.all_accounts = self._account_components()
Example #10
0
    def load_file(self, beancount_file_path=None):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        if beancount_file_path:
            self.beancount_file_path = beancount_file_path

        self.all_entries, self.errors, self.options = \
            loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = options.get_account_types(self.options)

        self.title = self.options['title']
        if self.options['render_commas']:
            self.format_string = '{:,f}'
            self.default_format_string = '{:,.2f}'
        else:
            self.format_string = '{:f}'
            self.default_format_string = '{:.2f}'
        self.dcontext = self.options['dcontext']

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))
        self.active_payees = list(getters.get_all_payees(self.all_entries))

        self.queries = self._entries_filter_type(self.all_entries, Query)

        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)
        self.all_accounts = self._all_accounts()
        self.all_accounts_leaf_only = self._all_accounts(leaf_only=True)

        self._apply_filters()
Example #11
0
def do_linked(filename, args):
    """Print out a list of transactions linked to the one at the given line.

    Args:
      filename: A string, which consists in the filename.
      args: A tuple of the rest of arguments. We're expecting the first argument
        to be an integer as a string.
    """
    from beancount.parser import options
    from beancount.parser import printer
    from beancount.core import account_types
    from beancount.core import inventory
    from beancount.core import data
    from beancount.core import realization
    from beancount import loader

    # Parse the arguments, get the line number.
    if len(args) != 1:
        raise SystemExit("Missing line number argument.")
    lineno = int(args[0])

    # Load the input file.
    entries, errors, options_map = loader.load_file(filename)

    # Find the closest entry.
    closest_entry = data.find_closest(entries, options_map['filename'], lineno)

    # Find its links.
    links = closest_entry.links
    if closest_entry is None:
        raise SystemExit("No entry could be found before {}:{}".format(
            filename, lineno))
    if not links:
        return

    # Find all linked entries.
    linked_entries = [
        entry for entry in entries
        if (isinstance(entry, data.Transaction) and entry.links and entry.links
            & links)
    ]

    # Render linked entries (in date order) as errors (for Emacs).
    errors = [RenderError(entry.meta, '', entry) for entry in linked_entries]
    printer.print_errors(errors)

    # Print out balances.
    real_root = realization.realize(linked_entries)
    realization.dump_balances(real_root, file=sys.stdout)

    # Print out net income change.
    acctypes = options.get_account_types(options_map)
    net_income = inventory.Inventory()
    for real_node in realization.iter_children(real_root):
        if account_types.is_income_statement_account(real_node.account,
                                                     acctypes):
            net_income.add_inventory(real_node.balance)

    print()
    print('Net Income: {}'.format(-net_income))
Example #12
0
    def test_dump_balances(self, entries, _, __):
        """
        2012-01-01 open Expenses:Restaurant
        2012-01-01 open Liabilities:US:CreditCard
        2012-01-01 open Liabilities:CA:CreditCard

        2014-05-30 *
          Liabilities:CA:CreditCard   123.45 CAD
          Expenses:Restaurant

        2014-05-31 *
          Liabilities:US:CreditCard   123.45 USD
          Expenses:Restaurant

        """
        real_account = realization.realize(entries)
        self.assertLines("""
            |-- Expenses
            |   `-- Restaurant          -123.45 CAD
            |                           -123.45 USD
            `-- Liabilities
                |-- CA
                |   `-- CreditCard       123.45 CAD
                `-- US
                    `-- CreditCard       123.45 USD
        """, realization.dump_balances(real_account))
Example #13
0
    def load_file(self):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        self.all_entries, self.errors, self.options = \
            loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = options.get_account_types(self.options)

        self.title = self.options['title']
        if self.options['render_commas']:
            self.format_string = '{:,f}'
            self.default_format_string = '{:,.2f}'
        else:
            self.format_string = '{:f}'
            self.default_format_string = '{:.2f}'

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))
        self.active_payees = list(getters.get_all_payees(self.all_entries))

        self.queries = _filter_entries_by_type(self.all_entries, Query)

        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)
        self.all_accounts = _list_accounts(self.all_root_account)
        self.all_accounts_leaf_only = _list_accounts(self.all_root_account,
                                                     leaf_only=True)

        self.sidebar_links = _sidebar_links(self.all_entries)

        self._apply_filters()

        self.budgets = Budgets(self.entries)
        self.errors.extend(self.budgets.errors)
Example #14
0
    def load_file(self):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        self.all_entries, self.errors, self.options = \
            loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = options.get_account_types(self.options)

        self.title = self.options['title']
        if self.options['render_commas']:
            self.format_string = '{:,f}'
            self.default_format_string = '{:,.2f}'
        else:
            self.format_string = '{:f}'
            self.default_format_string = '{:.2f}'

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))
        self.active_payees = list(getters.get_all_payees(self.all_entries))

        self.queries = _filter_entries_by_type(self.all_entries, Query)

        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)
        self.all_accounts = _list_accounts(self.all_root_account)
        self.all_accounts_leaf_only = _list_accounts(
            self.all_root_account, leaf_only=True)

        self.sidebar_links = _sidebar_links(self.all_entries)

        self._apply_filters()

        self.budgets = Budgets(self.entries)
        self.errors.extend(self.budgets.errors)
Example #15
0
    def load_file(self) -> None:
        """Load the main file and all included files and set attributes."""
        # use the internal function to disable cache
        if not self._is_encrypted:
            # pylint: disable=protected-access
            self.all_entries, self.errors, self.options = _load(
                [(self.beancount_file_path, True)], None, None, None)
        else:
            self.all_entries, self.errors, self.options = load_file(
                self.beancount_file_path)

        self.get_filtered.cache_clear()

        self.account_types = get_account_types(self.options)
        self.price_map = build_price_map(self.all_entries)
        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)

        self.all_entries_by_type = group_entries_by_type(self.all_entries)

        self.accounts = AccountDict()
        for open_entry in self.all_entries_by_type.Open:
            self.accounts.setdefault(open_entry.account).meta = open_entry.meta
        for close in self.all_entries_by_type.Close:
            self.accounts.setdefault(close.account).close_date = close.date

        self.fava_options, errors = parse_options(
            self.all_entries_by_type.Custom)
        self.errors.extend(errors)

        if not self._is_encrypted:
            self._watcher.update(*self.paths_to_watch())

        for mod in MODULES:
            getattr(self, mod).load_file()
Example #16
0
def render_mini_balances(entries, options_map, conversion=None, price_map=None):
    """Render a treeified list of the balances for the given transactions.

    Args:
      entries: A list of selected transactions to render.
      options_map: The parsed options.
      conversion: Conversion method string, None, 'value' or 'cost'.
      price_map: A price map from the original entries. If this isn't provided,
        the inventories are rendered directly. If it is, their contents are
        converted to market value.
    """
    # Render linked entries (in date order) as errors (for Emacs).
    errors = [RenderError(entry.meta, '', entry)
              for entry in entries]
    printer.print_errors(errors)

    # Print out balances.
    real_root = realization.realize(entries)
    dformat = options_map['dcontext'].build(alignment=Align.DOT, reserved=2)

    # TODO(blais): I always want to be able to convert at cost. We need
    # arguments capability.
    #
    # TODO(blais): Ideally this conversion inserts a new transactions to
    # 'Unrealized' to account for the difference between cost and market value.
    # Insert one and update the realization. Add an update() method to the
    # realization, given a transaction.
    acctypes = options.get_account_types(options_map)
    if conversion == 'value':
        assert price_map is not None

        # Warning: Mutate the inventories in-place, converting them to market
        # value.
        balance_diff = inventory.Inventory()
        for real_account in realization.iter_children(real_root):
            balance_cost = real_account.balance.reduce(convert.get_cost)
            balance_value = real_account.balance.reduce(convert.get_value, price_map)
            real_account.balance = balance_value
            balance_diff.add_inventory(balance_cost)
            balance_diff.add_inventory(-balance_value)
        if not balance_diff.is_empty():
            account_unrealized = account.join(acctypes.income,
                                              options_map["account_unrealized_gains"])
            unrealized = realization.get_or_create(real_root, account_unrealized)
            unrealized.balance.add_inventory(balance_diff)

    elif conversion == 'cost':
        for real_account in realization.iter_children(real_root):
            real_account.balance = real_account.balance.reduce(convert.get_cost)

    realization.dump_balances(real_root, dformat, file=sys.stdout)

    # Print out net income change.
    net_income = inventory.Inventory()
    for real_node in realization.iter_children(real_root):
        if account_types.is_income_statement_account(real_node.account, acctypes):
            net_income.add_inventory(real_node.balance)

    print()
    print('Net Income: {}'.format(-net_income))
Example #17
0
    def filter(
        self,
        force: bool = False,
        account: str | None = None,
        filter: str | None = None,  # pylint: disable=redefined-builtin
        time: str | None = None,
    ) -> None:
        """Set and apply (if necessary) filters."""
        changed = self.filters.set(account=account, filter=filter, time=time)

        if not (changed or force):
            return

        self.entries = self.filters.apply(self.all_entries)

        self.root_account = realization.realize(self.entries,
                                                self.account_types)
        self.root_tree = Tree(self.entries)

        self._date_first, self._date_last = get_min_max_dates(
            self.entries, (Transaction, Price))
        if self._date_last:
            self._date_last = self._date_last + datetime.timedelta(1)

        if self.filters.time:
            self._date_first = self.filters.time.begin_date
            self._date_last = self.filters.time.end_date
Example #18
0
def validate_leaf_only(entries, unused_options_map):
    """Check for non-leaf accounts that have postings on them.

    Args:
      entries: A list of directives.
      unused_options_map: An options map.
    Returns:
      A list of new errors, if any were found.
    """
    real_root = realization.realize(entries, compute_balance=False)

    default_meta = data.new_metadata('<leafonly>', 0)
    open_close_map = None  # Lazily computed.
    errors = []
    for real_account in realization.iter_children(real_root):
        if len(real_account) > 0 and real_account.txn_postings:

            if open_close_map is None:
                open_close_map = getters.get_account_open_close(entries)

            open_entry = open_close_map[real_account.account][0]
            errors.append(
                LeafOnlyError(
                    open_entry.meta if open_entry else default_meta,
                    "Non-leaf account '{}' has postings on it".format(
                        real_account.account), open_entry))

    return entries, errors
Example #19
0
def get_final_holdings(entries,
                       included_account_types=None,
                       price_map=None,
                       date=None):
    """Get a list of holdings by account (as Postings)."""

    simple_entries = [
        entry for entry in entries if (not isinstance(entry, Transaction)
                                       or entry.flag != flags.FLAG_UNREALIZED)
    ]

    root_account = realization.realize(simple_entries)

    holdings = []

    for real_account in sorted(list(realization.iter_children(root_account)),
                               key=lambda ra: ra.account):
        account_type = account_types.get_account_type(real_account.account)
        if (included_account_types
                and account_type not in included_account_types):
            continue
        for pos in real_account.balance:
            price = None
            if pos.cost and price_map:
                base_quote = (pos.units.currency, pos.cost.currency)
                _, price = prices.get_price(price_map, base_quote, date)
            holdings.append(
                Posting(real_account.account, pos.units, pos.cost, price, None,
                        None))

    return holdings
def build_interesting_realacc(entries, accounts):
    def is_included_account(realacc):
        for pattern in accounts:
            if re.match(pattern, realacc.account):
                if realacc.balance == inventory.Inventory():
                    return False  # Remove empty accounts to "clean up" the tree
                return True
        return False

    realroot = realization.realize(entries)

    # first, filter out accounts that are not specified:
    realacc = realization.filter(realroot, is_included_account)

    if not realacc:
        sys.stderr.write(
            "No included accounts found. (Your --accounts <regex> failed to match any account)\n"
        )
        sys.exit(1)

    # However, realacc includes all ancestor accounts of specified accounts, and their balances. For example,
    # if we specified 'Accounts:Investments:Brokerage', balances due to transactions on 'Accounts:Investments'
    # will also be included. We need to filter these out:
    for acc in realization.iter_children(realacc):
        if not is_included_account(acc):
            acc.balance = inventory.Inventory()
    return realacc
Example #21
0
    def load_file(self):
        """Load the main file and all included files and set attributes."""
        # use the internal function to disable cache
        if not self._is_encrypted:
            # pylint: disable=protected-access
            self.all_entries, self.errors, self.options = \
                loader._load([(self.beancount_file_path, True)],
                             None, None, None)
            include_path = os.path.dirname(self.beancount_file_path)
            self._watcher.update(self.options['include'], [
                os.path.join(include_path, path)
                for path in self.options['documents']
            ])
        else:
            self.all_entries, self.errors, self.options = \
                loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = get_account_types(self.options)
        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)
        if self.options['render_commas']:
            self._format_string = '{:,f}'
            self._default_format_string = '{:,.2f}'
        else:
            self._format_string = '{:f}'
            self._default_format_string = '{:.2f}'

        self.fava_options, errors = parse_options(
            filter_type(self.all_entries, Custom))
        self.errors.extend(errors)

        for mod in MODULES:
            getattr(self, mod).load_file()

        self.filter(True)
Example #22
0
    def interval_balances(self, interval, account_name, accumulate=False):
        """Balances by interval.

        Arguments:
            interval: An interval.
            account_name: An account name.
            accumulate: A boolean, ``True`` if the balances for an interval
                should include all entries up to the end of the interval.

        Returns:
            A list of RealAccount instances for all the intervals.
        """
        min_accounts = [
            account for account in
            self.accounts.keys()
            if account.startswith(account_name)]

        interval_tuples = list(
            reversed(list(pairwise(self.interval_ends(interval))))
        )

        interval_balances = [
            realization.realize(list(iter_entry_dates(
                self.entries,
                datetime.date.min if accumulate else begin_date,
                end_date)), min_accounts)
            for begin_date, end_date in interval_tuples]

        return interval_balances, interval_tuples
Example #23
0
    def filter(self, force=False, **kwargs):
        """Set and apply (if necessary) filters."""
        changed = False
        for filter_name, value in kwargs.items():
            if self._filters[filter_name].set(value):
                changed = True

        if not (changed or force):
            return

        self.entries = self.all_entries

        for filter_class in self._filters.values():
            self.entries = filter_class.apply(self.entries)

        self.root_account = realization.realize(self.entries,
                                                self.account_types)
        self.root_tree = Tree(self.entries)

        self._date_first, self._date_last = \
            getters.get_min_max_dates(self.entries, (Transaction))
        if self._date_last:
            self._date_last = self._date_last + datetime.timedelta(1)

        if self._filters['time']:
            self._date_first = self._filters['time'].begin_date
            self._date_last = self._filters['time'].end_date
Example #24
0
    def filter(self, force=False, **kwargs):
        """Set and apply (if necessary) filters."""
        changed = False
        for filter_name, value in kwargs.items():
            if self._filters[filter_name].set(value):
                changed = True

        if not (changed or force):
            return

        self.entries = self.all_entries

        for filter_class in self._filters.values():
            self.entries = filter_class.apply(self.entries)

        self.root_account = realization.realize(self.entries,
                                                self.account_types)
        self.root_tree = Tree(self.entries)

        self._date_first, self._date_last = getters.get_min_max_dates(
            self.entries, (Transaction))
        if self._date_last:
            self._date_last = self._date_last + datetime.timedelta(1)

        if self._filters["time"]:
            self._date_first = self._filters["time"].begin_date
            self._date_last = self._filters["time"].end_date
Example #25
0
    def _apply_filters(self):
        self.entries = self.all_entries

        for filter in self.filters.values():
            self.entries = filter.apply(self.entries, self.options)

        self.root_account = realization.realize(self.entries,
                                                self.account_types)
Example #26
0
 def test_realize_min_accoumts(self):
     real_account = realization.realize([],
                                        account_types.DEFAULT_ACCOUNT_TYPES)
     self.assertTrue(isinstance(real_account, realization.RealAccount))
     self.assertEqual(real_account.account, '')
     self.assertEqual(len(real_account), 5)
     self.assertEqual(set(account_types.DEFAULT_ACCOUNT_TYPES),
                      real_account.keys())
Example #27
0
    def load_file(self):
        with open(self.beancount_file_path, encoding='utf8') as f:
            self._source = f.read()

        self.entries, self._errors, self.options_map = loader.load_file(self.beancount_file_path)
        self.all_entries = self.entries

        self.account_types = options.get_account_types(self.options_map)
        self.real_accounts = realization.realize(self.entries, self.account_types)
Example #28
0
 def hierarchy(self, account_name, begin=None, end=None):
     """An account tree."""
     if begin:
         entries = iter_entry_dates(self.ledger.entries, begin, end)
         root_account = realization.realize(entries)
     else:
         root_account = self.ledger.root_account
     return _serialize_real_account(
         realization.get_or_create(root_account, account_name), end)
Example #29
0
 def forward_method(self,
                    entries,
                    errors,
                    options_map,
                    file,
                    fwdfunc=value):
     account_types = options.get_account_types(options_map)
     real_root = realization.realize(entries, account_types)
     return fwdfunc(self, real_root, options_map, file)
Example #30
0
    def apply_filters(self):
        self.entries = self.all_entries

        if self.filters['time']:
            try:
                begin_date, end_date = parse_date(self.filters['time'])
                self.entries, _ = summarize.clamp_opt(self.entries, begin_date,
                                                      end_date, self.options)
            except TypeError:
                raise FilterException('Failed to parse date string: {}'.format(
                    self.filters['time']))

        if self.filters['tag']:
            self.entries = [
                entry for entry in self.entries
                if isinstance(entry, Transaction) and entry.tags and (
                    entry.tags & set(self.filters['tag']))
            ]

        if self.filters['payee']:
            self.entries = [
                entry for entry in self.entries
                if (isinstance(entry, Transaction) and entry.payee and
                    (entry.payee in self.filters['payee'])) or (
                        isinstance(entry, Transaction) and not entry.payee and
                        ('' in self.filters['payee']))
            ]

        if self.filters['account']:
            self.entries = [
                entry for entry in self.entries
                if isinstance(entry, Transaction) and any(
                    has_component(posting.account, self.filters['account'])
                    for posting in entry.postings)
            ]

        self.root_account = realization.realize(self.entries,
                                                self.account_types)
        self.all_accounts = self._account_components()
        self.all_accounts_leaf_only = self._account_components(leaf_only=True)

        self.closing_entries = summarize.cap_opt(self.entries, self.options)
        self.closing_real_accounts = realization.realize(
            self.closing_entries, self.account_types)
Example #31
0
def create_report(entries, options_map):
    real_root = realization.realize(entries)

    # Find the institutions from the data.
    groups, ignored_accounts = find_institutions(entries, options_map)

    # List all the asset accounts which aren't included in the report.
    oc_map = getters.get_account_open_close(entries)
    open_map = {acc: open_entry for acc, (open_entry, _) in oc_map.items()}
    for acc in sorted(ignored_accounts):
        logging.info("Ignored account: %s", acc)

    # Gather missing fields and create a report object.
    institutions = []
    for name, accounts in sorted(groups.items()):
        # Get the institution fields, which is the union of the fields for all
        # the accounts with the institution fields.
        institution_accounts = [
            acc for acc in accounts if 'institution' in open_map[acc].meta
        ]

        institution_fields = {}
        for acc in institution_accounts:
            for key, value in open_map[acc].meta.items():
                institution_fields.setdefault(key, value)
        institution_fields.pop('filename', None)
        institution_fields.pop('lineno', None)

        # Create infos for each account in this institution.
        account_reports = []
        for acc in accounts:
            account_fields = {}
            for subacc in account.parents(acc):
                open_entry = open_map[subacc]
                if 'institution' in open_entry.meta:
                    break
                account_fields.update(open_entry.meta)
            account_fields.pop('filename', None)
            account_fields.pop('lineno', None)
            for field in institution_fields:
                account_fields.pop(field, None)

            real_node = realization.get(real_root, acc)
            account_reports.append(
                AccountReport(
                    acc, open_entry.date, real_node.balance,
                    sum(1 for posting in real_node.txn_postings
                        if isinstance(posting, data.TxnPosting)),
                    account_fields))

        # Create the institution report.
        institution = InstitutionReport(name, institution_fields,
                                        account_reports)
        institutions.append(institution)

    return Report(options_map['title'], institutions)
Example #32
0
def test_tree_from_entries(example_ledger):
    tree = Tree(example_ledger.entries)
    real_account = realization.realize(example_ledger.entries)

    for account in realization.iter_children(real_account):
        name = account.account
        node = tree[name]
        _compare_inv_and_counter(account.balance, node.balance)
        _compare_inv_and_counter(realization.compute_balance(account),
                                 node.balance_children)
Example #33
0
    def filter(self, year=None, tag=None):
        if year:
            yv = YearView(self.all_entries, self.options_map, str(year), year)
            self.entries = yv.entries

        if tag:
            tv = TagView(self.all_entries, self.options_map, tag, set([tag]))
            self.entries = tv.entries

        self.real_accounts = realization.realize(self.entries, self.account_types)
Example #34
0
 def reload(self):
     if self.transaction is not None:
         self.log.warn("Discard transactions due to reload")
         self.log.warn(format_entry(self.transaction))
     entries, errors, options_map = loader.load_file(self.filename)
     assert(len(errors) == 0)
     self.entries = entries
     self.options_map = options_map
     self.transaction = None
     # Commonly used transformations of the entries
     self.price_entries = prices.get_last_price_entries(entries, datetime.date.today())
     self.accounts = realization.realize(self.entries)
Example #35
0
 def forward_method(self,
                    entries,
                    errors,
                    options_map,
                    file,
                    fwdfunc=value):
     account_types = options.get_account_types(options_map)
     real_root = realization.realize(entries, account_types)
     price_map = prices.build_price_map(entries)
     # Note: When we forward, use the latest date (None).
     return fwdfunc(self, real_root, price_map, None, options_map,
                    file)
Example #36
0
    def load_file(self):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        # use the internal function to disable cache
        if not self.is_encrypted:
            self.all_entries, self.errors, self.options = \
                loader._load([(self.beancount_file_path, True)],
                             None, None, None)
            include_path = os.path.dirname(self.beancount_file_path)
            self.watcher.update(self.options['include'], [
                os.path.join(include_path, path)
                for path in self.options['documents']])
        else:
            self.all_entries, self.errors, self.options = \
                loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = options.get_account_types(self.options)

        self.title = self.options['title']
        if self.options['render_commas']:
            self._format_string = '{:,f}'
            self._default_format_string = '{:,.2f}'
        else:
            self._format_string = '{:f}'
            self._default_format_string = '{:.2f}'

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))
        self.active_payees = list(getters.get_all_payees(self.all_entries))

        self.queries = _filter_entries_by_type(self.all_entries, Query)
        self.custom_entries = _filter_entries_by_type(self.all_entries, Custom)

        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)
        self.all_accounts = _list_accounts(self.all_root_account)
        self.all_accounts_active = _list_accounts(
            self.all_root_account, active_only=True)

        self.fava_options, errors = parse_options(self.custom_entries)
        self.errors.extend(errors)

        self.sidebar_links = _sidebar_links(self.custom_entries)

        self.upcoming_events = _upcoming_events(
            self.all_entries, self.fava_options['upcoming-events'])

        self.budgets, errors = parse_budgets(self.custom_entries)
        self.errors.extend(errors)

        self._apply_filters()
Example #37
0
    def _real_accounts(self, account_name, entries, begin_date=None, end_date=None):
        """
        Returns the realization.RealAccount instances for account_name, and
        their entries clamped by the optional begin_date and end_date.

        Warning: For efficiency, the returned result does not include any added
        postings to account for balances at 'begin_date'.

        :return: realization.RealAccount instances
        """
        entries_in_range = self._entries_in_inclusive_range(entries, begin_date=begin_date, end_date=end_date)
        real_accounts = realization.get(realization.realize(entries_in_range, [account_name]), account_name)

        return real_accounts
Example #38
0
    def load_file(self) -> None:
        """Load the main file and all included files and set attributes."""
        # use the internal function to disable cache
        if not self._is_encrypted:
            # pylint: disable=protected-access
            self.all_entries, self.errors, self.options = loader._load(
                [(self.beancount_file_path, True)], None, None, None
            )
        else:
            self.all_entries, self.errors, self.options = loader.load_file(
                self.beancount_file_path
            )

        self.account_types = get_account_types(self.options)
        self.price_map = build_price_map(self.all_entries)
        self.all_root_account = realization.realize(
            self.all_entries, self.account_types
        )

        entries_by_type: DefaultDict[
            Type[Directive], Entries
        ] = collections.defaultdict(list)
        for entry in self.all_entries:
            entries_by_type[type(entry)].append(entry)
        self.all_entries_by_type = entries_by_type

        self.accounts = AccountDict()
        for entry in entries_by_type[Open]:
            self.accounts.setdefault(
                cast(Open, entry).account
            ).meta = entry.meta
        for entry in entries_by_type[Close]:
            self.accounts.setdefault(
                cast(Close, entry).account
            ).close_date = entry.date

        self.fava_options, errors = parse_options(
            cast(List[Custom], entries_by_type[Custom])
        )
        self.errors.extend(errors)

        if not self._is_encrypted:
            self._watcher.update(*self.paths_to_watch())

        for mod in MODULES:
            getattr(self, mod).load_file()

        self.filters = Filters(self.options, self.fava_options)

        self.filter(True)
Example #39
0
    def _apply_filters(self):
        self.entries = self.all_entries

        for filter_class in self.filters.values():
            self.entries = filter_class.apply(self.entries, self.options)

        self.root_account = realization.realize(self.entries, self.account_types)

        self.date_first, self.date_last = getters.get_min_max_dates(self.entries, (Transaction))
        if self.date_last:
            self.date_last = self.date_last + datetime.timedelta(1)

        if self.filters["time"]:
            self.date_first = self.filters["time"].begin_date
            self.date_last = self.filters["time"].end_date
Example #40
0
    def test_simple_realize(self, entries, errors, options_map):
        """
          2013-05-01 open Assets:US:Checking:Sub   USD
          2013-05-01 open Expenses:Stuff
          2013-05-02 txn "Testing!"
            Assets:US:Checking:Sub            100 USD
            Expenses:Stuff           -100 USD
        """
        real_root = realization.realize(entries)
        for real_account in realization.iter_children(real_root):
            assert isinstance(real_account, realization.RealAccount)

        for account_name in ['Assets:US:Checking:Sub', 'Expenses:Stuff']:
            real_account = realization.get(real_root, account_name)
            self.assertEqual(account_name, real_account.account)
Example #41
0
    def _apply_filters(self):
        self.entries = self.all_entries

        for filter in self.filters.values():
            self.entries = filter.apply(self.entries, self.options)

        self.root_account = realization.realize(self.entries,
                                                self.account_types)

        self.date_first, self.date_last = \
            getters.get_min_max_dates(self.entries, (Transaction))

        if self.filters['time']:
            self.date_first = self.filters['time'].begin_date
            self.date_last = self.filters['time'].end_date
Example #42
0
    def interval_balances(self, interval, account_name, accumulate=False):
        """accumulate is False for /changes and True for /balances"""
        min_accounts = [account for account in self.all_accounts if account.startswith(account_name)]

        interval_tuples = list(reversed(self._interval_tuples(interval)))

        interval_balances = [
            realization.realize(
                list(iter_entry_dates(self.entries, self.date_first if accumulate else begin_date, end_date)),
                min_accounts,
            )
            for begin_date, end_date in interval_tuples
        ]

        return interval_balances, interval_tuples
Example #43
0
def test_tree_cap(example_ledger):
    closing_entries = summarize.cap_opt(example_ledger.entries,
                                        example_ledger.options)
    real_account = realization.realize(closing_entries)

    tree = Tree(example_ledger.entries)
    tree.cap(example_ledger.options, 'Unrealized')

    for account in realization.iter_children(real_account):
        name = account.account
        node = tree[name]
        if not name:
            continue
        if name.startswith('Expenses') or name.startswith('Income'):
            continue
        _compare_inv_and_counter(account.balance, node.balance)
Example #44
0
def _real_account(account_name, entries, begin_date=None, end_date=None, min_accounts=None):
    """
    Returns the realization.RealAccount instances for account_name, and
    their entries clamped by the optional begin_date and end_date.

    Warning: For efficiency, the returned result does not include any added
    postings to account for balances at 'begin_date'.

    :return: realization.RealAccount instances
    """
    if begin_date:
        entries = list(iter_entry_dates(entries, begin_date, end_date))
    if not min_accounts:
        min_accounts = [account_name]

    return realization.get(realization.realize(entries, min_accounts), account_name)
Example #45
0
    def interval_balances(self, interval, account_name, accumulate=False):
        """accumulate is False for /changes and True for /balances"""
        min_accounts = [account
                        for account in self.all_accounts
                        if account.startswith(account_name)]

        interval_tuples = list(reversed(self._interval_tuples(interval)))

        interval_balances = [
            realization.realize(list(iter_entry_dates(
                self.entries,
                self.date_first if accumulate else begin_date,
                end_date)), min_accounts)
            for begin_date, end_date in interval_tuples]

        return interval_balances, interval_tuples
Example #46
0
    def test_is_account_active(self, entries, _, __):
        """
        2014-01-01 open Assets:Inactive
        2014-01-01 open Assets:Active
        2014-01-01 open Equity:Other

        2014-07-04 *
          Assets:Active   1 USD
          Equity:Other

        """
        real_root = realization.realize(entries)
        self.assertFalse(tree_table.is_account_active(
            realization.get(real_root, 'Assets:Inactive')))
        self.assertTrue(tree_table.is_account_active(
            realization.get(real_root, 'Assets:Active')))
Example #47
0
    def load_file(self):
        """Load self.beancount_file_path and compute things that are independent
        of how the entries might be filtered later"""
        # use the internal function to disable cache
        if not self.is_encrypted:
            self.all_entries, self.errors, self.options = loader._load(
                [(self.beancount_file_path, True)], None, None, None
            )
            include_path = os.path.dirname(self.beancount_file_path)
            self.watcher.update(
                self.options["include"], [os.path.join(include_path, path) for path in self.options["documents"]]
            )
        else:
            self.all_entries, self.errors, self.options = loader.load_file(self.beancount_file_path)
        self.price_map = prices.build_price_map(self.all_entries)
        self.account_types = options.get_account_types(self.options)

        self.title = self.options["title"]
        if self.options["render_commas"]:
            self._format_string = "{:,f}"
            self._default_format_string = "{:,.2f}"
        else:
            self._format_string = "{:f}"
            self._default_format_string = "{:.2f}"

        self.active_years = list(getters.get_active_years(self.all_entries))
        self.active_tags = list(getters.get_all_tags(self.all_entries))
        self.active_payees = list(getters.get_all_payees(self.all_entries))

        self.queries = _filter_entries_by_type(self.all_entries, Query)
        self.custom_entries = _filter_entries_by_type(self.all_entries, Custom)

        self.all_root_account = realization.realize(self.all_entries, self.account_types)
        self.all_accounts = _list_accounts(self.all_root_account)
        self.all_accounts_active = _list_accounts(self.all_root_account, active_only=True)

        self.fava_options, errors = parse_options(self.custom_entries)
        self.errors.extend(errors)

        self.sidebar_links = _sidebar_links(self.custom_entries)

        self.upcoming_events = _upcoming_events(self.all_entries, self.fava_options["upcoming-events"])

        self.budgets, errors = parse_budgets(self.custom_entries)
        self.errors.extend(errors)

        self._apply_filters()
Example #48
0
    def monthly_income_expenses_totals(self):
        month_tuples = self._interval_tuples('month', self.entries)
        monthly_totals = []
        for begin_date, end_date in month_tuples:
            entries = self._entries_in_inclusive_range(self.entries, begin_date, end_date)
            realized = realization.realize(entries, self.account_types)
            income_totals = self._table_totals(realization.get(realized, self.account_types.income))
            expenses_totals = self._table_totals(realization.get(realized, self.account_types.expenses))

            monthly_totals.append({
                'begin_date': begin_date,
                'end_date': end_date,
                'income_totals': income_totals,
                'expenses_totals': expenses_totals
            })

        return monthly_totals
Example #49
0
    def load_file(self):
        """Load the main file and all included files and set attributes."""
        # use the internal function to disable cache
        if not self._is_encrypted:
            # pylint: disable=protected-access
            self.all_entries, self.errors, self.options = \
                loader._load([(self.beancount_file_path, True)],
                             None, None, None)
            self.account_types = get_account_types(self.options)
            self._watcher.update(*self.paths_to_watch())
        else:
            self.all_entries, self.errors, self.options = \
                loader.load_file(self.beancount_file_path)
            self.account_types = get_account_types(self.options)
        self.price_map = prices.build_price_map(self.all_entries)
        self.all_root_account = realization.realize(self.all_entries,
                                                    self.account_types)

        entries_by_type = collections.defaultdict(list)
        for entry in self.all_entries:
            entries_by_type[type(entry)].append(entry)
        self.all_entries_by_type = entries_by_type

        self.accounts = _AccountDict()
        for entry in entries_by_type[Open]:
            self.accounts.setdefault(entry.account).meta = entry.meta
        for entry in entries_by_type[Close]:
            self.accounts.setdefault(entry.account).close_date = entry.date

        self.fava_options, errors = parse_options(entries_by_type[Custom])
        self.errors.extend(errors)

        for mod in MODULES:
            getattr(self, mod).load_file()

        self._filters = {
            'account': AccountFilter(self.options, self.fava_options),
            'filter': AdvancedFilter(self.options, self.fava_options),
            'time': TimeFilter(self.options, self.fava_options),
        }

        self.filter(True)
Example #50
0
    def _real_accounts(self, account_name, begin_date=None, end_date=None):
        """
        Returns the realization.RealAccount instances for account_name, and their entries
        clamped by the optional begin_date and end_date.

        Returns:
            realization.RealAccount instances
        """
        begin_date_, end_date_ = getters.get_min_max_dates(self.entries, (Transaction))
        if begin_date:
            begin_date_ = begin_date
        if end_date:
            end_date_ = end_date

        entries, index = summarize.clamp_opt(self.entries, begin_date_, end_date_ + timedelta(days=1),
                                                     self.options_map)

        real_accounts = realization.get(realization.realize(entries, self.account_types), account_name)

        return real_accounts
Example #51
0
 def closing_balances(self, account_name):
     closing_entries = summarize.cap_opt(self.entries, self.options)
     return realization.get_or_create(realization.realize(closing_entries), account_name)
Example #52
0
def _real_account(account_name, entries, begin_date, end_date):
    if begin_date:
        entries = list(iter_entry_dates(entries, begin_date, end_date))

    return realization.get_or_create(realization.realize(entries),
                                     account_name)