Esempio n. 1
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()
Esempio n. 2
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)
    def __init__(self, entries, options_map, currency):

        self.entries = entries
        self.options_map = options_map
        self.currency = currency
        if self.currency:
            self.etype = "envelope" + self.currency
        else:
            self.etype = "envelope"

        self.start_date, self.budget_accounts, self.mappings, self.income_accounts = self._find_envelop_settings(
        )

        if not self.currency:
            self.currency = self._find_currency(options_map)

        decimal_precison = '0.00'
        self.Q = Decimal(decimal_precison)

        # Compute start of period
        # TODO get start date from journal
        today = datetime.date.today()
        self.date_start = datetime.datetime.strptime(self.start_date,
                                                     '%Y-%m').date()

        # TODO should be able to assert errors

        # Compute end of period
        self.date_end = datetime.date(today.year, today.month, today.day)

        self.price_map = prices.build_price_map(entries)
        self.acctypes = options.get_account_types(options_map)
Esempio n. 4
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)
Esempio n. 5
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))
Esempio n. 6
0
def divert_expenses(entries, options_map, config_str):
    """Divert expenses.

    Explicit price entries are simply maintained in the output list. Prices from
    postings with costs or with prices from Transaction entries are synthesized
    as new Price entries in the list of entries output.

    Args:
      entries: A list of directives. We're interested only in the Transaction instances.
      options_map: A parser options dict.
      config_str: A configuration string, which is intended to be a list of two strings,
        a tag, and an account to replace expenses with.
    Returns:
      A modified list of entries.
    """
    # pylint: disable=eval-used
    config_obj = eval(config_str, {}, {})
    if not isinstance(config_obj, dict):
        raise RuntimeError(
            "Invalid plugin configuration: should be a single dict.")
    tag = config_obj['tag']
    replacement_account = config_obj['account']

    acctypes = options.get_account_types(options_map)

    new_entries = []
    errors = []
    for entry in entries:
        if isinstance(entry, Transaction) and tag in entry.tags:
            entry = replace_diverted_accounts(entry, replacement_account,
                                              acctypes)
        new_entries.append(entry)

    return new_entries, errors
Esempio n. 7
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))
Esempio n. 8
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()
Esempio n. 9
0
    def render_real_htmldiv(self, real_root, options_map, file):
        account_types = options.get_account_types(options_map)
        for root in (account_types.assets, account_types.liabilities):
            oss = io.StringIO()
            table = tree_table.tree_table(oss,
                                          realization.get(real_root, root),
                                          None, ['Account', 'Last Entry'])
            num_cells = 0
            for real_account, cells, row_classes in table:
                if not isinstance(real_account, realization.RealAccount):
                    continue
                last_posting = realization.find_last_active_posting(
                    real_account.txn_postings)

                # Don't render updates to accounts that have been closed.
                # Note: this is O(N), maybe we can store this at realization.
                if last_posting is None or isinstance(last_posting,
                                                      data.Close):
                    continue

                last_date = data.get_entry(last_posting).date

                # Skip this posting if a cutoff date has been specified and the
                # account has been updated to at least the cutoff date.
                if self.args.cutoff and self.args.cutoff <= last_date:
                    continue

                # Okay, we need to render this. Append.
                cells.append(
                    data.get_entry(last_posting).date if real_account.
                    txn_postings else '-')
                num_cells += 1

            if num_cells:
                file.write(oss.getvalue())
Esempio n. 10
0
def holdings_at_dates(entries, dates, price_map, options_map):
    """Computes aggregate holdings at mulitple dates.

    Yields for each date the list of Holding objects.  The holdings are
    aggregated across accounts; the Holding objects will have the account field
    set to None.

    :param entries: The list of entries.
    :param dates: The list of dates.
    :param price_map: A dict of prices, as built by prices.build_price_map().
    :param options_map: The account options.
    """
    transactions = [entry for entry in entries
                    if (isinstance(entry, Transaction) and
                        entry.flag != flags.FLAG_UNREALIZED)]

    types = options.get_account_types(options_map)

    def posting_predicate(posting):
        account_type = account_types.get_account_type(posting.account)
        if account_type in (types.assets, types.liabilities):
            return True

    for date, inventory in zip(dates,
                               inventory_at_dates(transactions, dates,
                                                  posting_predicate)):
        yield [get_holding_from_position(position, price_map, date)
               for position in inventory]
Esempio n. 11
0
def get_assets_holdings(entries, options_map, currency=None):
    """Return holdings for all assets and liabilities.

    Args:
      entries: A list of directives.
      options_map: A dict of parsed options.
      currency: If specified, a string, the target currency to convert all
        holding values to.
    Returns:
      A list of Holding instances and a price-map.
    """
    # Compute a price map, to perform conversions.
    price_map = prices.build_price_map(entries)

    # Get the list of holdings.
    account_types = options.get_account_types(options_map)
    holdings_list = holdings.get_final_holdings(
        entries, (account_types.assets, account_types.liabilities), price_map)

    # Convert holdings to a unified currency.
    if currency:
        holdings_list = holdings.convert_to_currency(price_map, currency,
                                                     holdings_list)

    return holdings_list, price_map
Esempio n. 12
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
Esempio n. 13
0
def open_opt(entries, date, options_map):
    """Convenience function to open() using an options map.
    """
    account_types = options.get_account_types(options_map)
    previous_accounts = options.get_previous_accounts(options_map)
    conversion_currency = options_map['conversion_currency']
    return open(entries, date, account_types, conversion_currency, *previous_accounts)
Esempio n. 14
0
def get_matching_entries(entries, options_map, query):
    query_text = 'SELECT * ' + query
    parser = query_parser.Parser()
    parsed_query = parser.parse(query_text)
    c_from = None
    if parsed_query.from_clause:
        c_from = query_compile.compile_from(parsed_query.from_clause, query_env.FilterEntriesEnvironment())
    c_where = None
    if parsed_query.where_clause:
        c_where = query_compile.compile_expression(parsed_query.where_clause, query_env.FilterPostingsEnvironment())

    # Figure out if we need to compute balance.
    balance = None
    if c_where and query_execute.uses_balance_column(c_where):
        balance = inventory.Inventory()

    context = query_execute.RowContext()
    context.balance = balance
    

    # Initialize some global properties for use by some of the accessors.
    context.options_map = options_map
    context.account_types = options.get_account_types(options_map)
    context.open_close_map = getters.get_account_open_close(entries)
    #context.commodity_map = getters.get_commodity_map(entries)
    context.price_map = prices.build_price_map(entries) 

    if c_from is not None:
        filtered_entries = query_execute.filter_entries(c_from, entries, options_map)
    else:
        filtered_entries = entries
    return filtered_entries
Esempio n. 15
0
def holdings_at_dates(entries, dates, price_map, options_map):
    """Computes aggregate holdings at mulitple dates.

    Yields for each date the list of Holding objects.  The holdings are
    aggregated across accounts; the Holding objects will have the account field
    set to None.

    :param entries: The list of entries.
    :param dates: The list of dates.
    :param price_map: A dict of prices, as built by prices.build_price_map().
    :param options_map: The account options.
    """
    account_types = options.get_account_types(options_map)
    FLAG_UNREALIZED = flags.FLAG_UNREALIZED
    transaction_predicate = lambda e: e.flag != FLAG_UNREALIZED
    account_re = re.compile(account_descendants_re_pattern(
        account_types.assets,
        account_types.liabilities))
    posting_predicate = lambda e, p: account_re.match(p.account)
    for date, inventory in zip(dates,
                               inventory_at_dates(
                                   entries, dates,
                                   transaction_predicate = transaction_predicate,
                                   posting_predicate = posting_predicate)):
        yield [get_holding_from_position(lot, number, price_map=price_map, date=date)
               for lot, number in inventory.items()]
Esempio n. 16
0
def compute_returns_with_regexp(entries,
                                options_map,
                                transfer_account,
                                assets_regexp,
                                intflows_regexp,
                                internalize_regexp=None,
                                date_begin=None,
                                date_end=None):
    """Compute the returns of a portfolio of accounts defined by a regular expression.

    Args:
      entries: A list of directives.
      options_map: An options dict as produced by the loader.
      transfer_account: A string, the name of an account to use for internalizing entries
        which need to be split between internal and external flows.
      assets_regexp: A regular expression string that matches names of asset accounts to
        value for the portfolio.
      intflows_regexp: A regular expression string that matches names of accounts considered
        internal flows to the portfolio (typically income and expenses accounts).
      internalize_regexp: A regular expression string that matches names of accounts
        to force internalization of. See internalize() for details.
      date_begin: A datetime.date instance, the beginning date of the period to compute
        returns over.
      date_end: A datetime.date instance, the end date of the period to compute returns
        over.
    Returns:
      See compute_returns().
    """
    acc_types = options.get_account_types(options_map)
    price_map = prices.build_price_map(entries)

    # Fetch the matching entries and figure out account name groups.
    matching_entries, (accounts_value, accounts_intflows, accounts_extflows,
                       accounts_internalize) = find_matching(
                           entries, acc_types, assets_regexp, intflows_regexp,
                           internalize_regexp)

    logging.info('Asset accounts:')
    for account in sorted(accounts_value):
        logging.info('  %s', account)

    logging.info('Internal flows:')
    for account in sorted(accounts_intflows):
        logging.info('  %s', account)

    logging.info('External flows:')
    for account in sorted(accounts_extflows):
        logging.info('  %s', account)
    logging.info('')

    if accounts_internalize:
        logging.info('Explicitly internalized accounts:')
        for account in sorted(accounts_internalize):
            logging.info('  %s', account)
        logging.info('')

    return compute_returns(entries, transfer_account, accounts_value,
                           accounts_intflows, accounts_internalize, price_map,
                           date_begin, date_end)
Esempio n. 17
0
def process_account_entries(entries: data.Entries, options_map: data.Options,
                            account: Account) -> AccountData:
    """Process a single account."""
    logging.info("Processing account: %s", account)

    # Extract the relevant transactions.
    transactions = transactions_for_account(entries, account)
    if not transactions:
        logging.warning("No transactions for %s; skipping.", account)
        return transactions, None, None

    # Categorize the set of accounts encountered in the filtered transactions.
    seen_accounts = {
        posting.account
        for entry in transactions for posting in entry.postings
    }
    atypes = options.get_account_types(options_map)
    catmap = categorize_accounts(account, seen_accounts, atypes)

    # Process each of the transactions, adding derived values as metadata.
    cash_flows = []
    balance = Inventory()
    decorated_transactions = []
    for entry in transactions:

        # Update the total position in the asset we're interested in.
        positions = []
        for posting in entry.postings:
            category = catmap[posting.account]
            if category is Cat.ASSET:
                balance.add_position(posting)
                positions.append(posting)

        # Compute the signature of the transaction.
        entry = copy_and_normalize(entry)
        signature = compute_transaction_signature(catmap, entry)
        entry.meta["signature"] = signature
        entry.meta["description"] = KNOWN_SIGNATURES[signature]

        # Compute the cash flows associated with the transaction.
        flows = produce_cash_flows(entry)
        entry.meta['cash_flows'] = flows

        cash_flows.extend(
            flow._replace(balance=copy.deepcopy(balance)) for flow in flows)
        decorated_transactions.append(entry)

    currency = accountlib.leaf(account)

    cost_currencies = set(cf.amount.currency for cf in cash_flows)
    assert len(cost_currencies) == 1, str(cost_currencies)
    cost_currency = cost_currencies.pop()

    commodity_map = getters.get_commodity_directives(entries)
    comm = commodity_map[currency]

    return AccountData(account, currency, cost_currency, comm, cash_flows,
                       decorated_transactions, catmap)
 def test_get_account_types(self):
     options_ = options.OPTIONS_DEFAULTS.copy()
     result = options.get_account_types(options_)
     expected = account_types.AccountTypes(assets='Assets',
                                           liabilities='Liabilities',
                                           equity='Equity',
                                           income='Income',
                                           expenses='Expenses')
     self.assertEqual(expected, result)
Esempio n. 19
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)
Esempio n. 20
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)
Esempio n. 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)
            self.account_types = get_account_types(self.options)
            # TODO: implement watcher for S3
            #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)
Esempio n. 22
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)
Esempio n. 23
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)
Esempio n. 24
0
def create_row_context(entries, options_map):
    """Create the context container which we will use to evaluate rows."""
    context = RowContext()
    context.balance = inventory.Inventory()

    # Initialize some global properties for use by some of the accessors.
    context.options_map = options_map
    context.account_types = options.get_account_types(options_map)
    context.open_close_map = getters.get_account_open_close(entries)
    context.commodity_map = getters.get_commodity_directives(entries)
    context.price_map = prices.build_price_map(entries)

    return context
Esempio n. 25
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()
Esempio n. 26
0
def find_accounts(entries: data.Entries, options_map: data.Options,
                  start_date: Optional[Date]) -> List[Account]:
    """Return a list of account names from the balance sheet which either aren't
    closed or are closed now but were still open at the given start date.
    """
    commodities = getters.get_commodity_directives(entries)
    open_close_map = getters.get_account_open_close(entries)
    atypes = options.get_account_types(options_map)
    return sorted(
        account for account, (_open, _close) in open_close_map.items()
        if (accountlib.leaf(account) in commodities
            and acctypes.is_balance_sheet_account(account, atypes)
            and not acctypes.is_equity_account(account, atypes) and (
                _close is None or (start_date and _close.date > start_date))))
Esempio n. 27
0
def get_postings_table(entries: data.Entries, options_map: Dict,
                       accounts_map: Dict[str, data.Open],
                       threshold: Decimal = D('0.01')) -> Table:
    """Enumerate all the postings."""
    header = ['account',
              'account_abbrev',
              'number',
              'currency',
              'cost_number',
              'cost_currency',
              'cost_date']
    balances, _ = summarize.balance_by_account(entries)
    acctypes = options.get_account_types(options_map)
    rows = []
    for acc, balance in sorted(balances.items()):
        # Keep only the balance sheet accounts.
        acctype = account_types.get_account_type(acc)
        if not acctype in (acctypes.assets, acctypes.liabilities):
            continue

        # If the account has "NONE" booking method, merge all its postings
        # together in order to obtain an accurate cost basis and balance of
        # units.
        #
        # (This is a complex issue.) If you accrued positions without having them
        # booked properly against existing cost bases, you have not properly accounted
        # for the profit/loss to other postings. This means that the resulting
        # profit/loss is merged in the cost basis of the positive and negative
        # postings.
        dopen = accounts_map.get(acc, None)
        if dopen is not None and dopen.booking is data.Booking.NONE:
            average_balance = balance.average()
            balance = inventory.Inventory(pos
                                          for pos in average_balance
                                          if pos.units.number >= threshold)

        # Create a posting for each of the positions.
        for pos in balance:
            acc_abbrev = abbreviate_account(acc, accounts_map)
            row = [acc,
                   acc_abbrev,
                   pos.units.number,
                   pos.units.currency,
                   pos.cost.number if pos.cost else ONE,
                   pos.cost.currency if pos.cost else pos.units.currency,
                   pos.cost.date if pos.cost else None]
            rows.append(row)

    return Table(header, rows)
Esempio n. 28
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()
Esempio n. 29
0
def net_worth_at_dates(entries, dates, price_map, options_map):
    transactions = [entry for entry in entries
                    if (isinstance(entry, Transaction) and
                        entry.flag != flags.FLAG_UNREALIZED)]

    types = options.get_account_types(options_map)

    def _posting_predicate(posting):
        account_type = account_types.get_account_type(posting.account)
        if account_type in (types.assets, types.liabilities):
            return True

    inventories = inventory_at_dates(transactions, dates, _posting_predicate)

    return [{
        'date': date,
        'balance': {
            currency: convert_inventory(price_map, currency, inv, date)
            for currency in options_map['operating_currency']
        }
    } for date, inv in zip(dates, inventories)]
Esempio n. 30
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.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.account_types = options.get_account_types(self.options)

        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
            })

        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.apply_filters()
def add_unrealized_gains(entries, options_map, subaccount=None):
    """Insert entries for unrealized capital gains.

    This function inserts entries that represent unrealized gains, at the end of
    the available history. It returns a new list of entries, with the new gains
    inserted. It replaces the account type with an entry in an income account.
    Optionally, it can book the gain in a subaccount of the original and income
    accounts.

    Args:
      entries: A list of data directives.
      options_map: A dict of options, that confirms to beancount.parser.options.
      subaccount: A string, and optional the name of a subaccount to create
        under an account to book the unrealized gain. If this is left to its
        default value, the gain is booked directly in the same account.
    Returns:
      A list of entries, which includes the new unrealized capital gains entries
      at the end, and a list of errors. The new list of entries is still sorted.
    """
    errors = []
    meta = data.new_metadata('<unrealized_gains>', 0)

    account_types = options.get_account_types(options_map)

    # Assert the subaccount name is in valid format.
    if subaccount:
        validation_account = account.join(account_types.assets, subaccount)
        if not account.is_valid(validation_account):
            errors.append(
                UnrealizedError(meta,
                                "Invalid subaccount name: '{}'".format(subaccount),
                                None))
            return entries, errors

    if not entries:
        return (entries, errors)

    # Group positions by (account, cost, cost_currency).
    price_map = prices.build_price_map(entries)

    new_entries = []

    # Start at the first month after our first transaction
    date = date_utils.next_month(entries[0].date)
    last_month = date_utils.next_month(entries[-1].date)
    last_holdings_with_currencies = None
    while date <= last_month:
        date_entries, holdings_with_currencies, date_errors = add_unrealized_gains_at_date(
            entries, new_entries, account_types.income, price_map, date, meta,
            subaccount)
        new_entries.extend(date_entries)
        errors.extend(date_errors)

        if last_holdings_with_currencies:
            for account_, cost_currency, currency in last_holdings_with_currencies - holdings_with_currencies:
                # Create a negation transaction specifically to mark that all gains have been realized
                if subaccount:
                    account_ = account.join(account_, subaccount)

                latest_unrealized_entry = find_previous_unrealized_transaction(new_entries, account_, cost_currency, currency)
                if not latest_unrealized_entry:
                    continue
                entry = data.Transaction(data.new_metadata(meta["filename"], lineno=999,
                                         kvlist={'prev_currency': currency}), date,
                                         flags.FLAG_UNREALIZED, None, 'Clear unrealized gains/losses of {}'.format(currency), set(), set(), [])

                # Negate the previous transaction because of unrealized gains are now 0
                for posting in latest_unrealized_entry.postings[:2]:
                    entry.postings.append(
                        data.Posting(
                            posting.account,
                            -posting.units,
                            None,
                            None,
                            None,
                            None))
                new_entries.append(entry)


        last_holdings_with_currencies = holdings_with_currencies
        date = date_utils.next_month(date)

    # Ensure that the accounts we're going to use to book the postings exist, by
    # creating open entries for those that we generated that weren't already
    # existing accounts.
    new_accounts = {posting.account
                    for entry in new_entries
                    for posting in entry.postings}
    open_entries = getters.get_account_open_close(entries)
    new_open_entries = []
    for index, account_ in enumerate(sorted(new_accounts)):
        if account_ not in open_entries:
            meta = data.new_metadata(meta["filename"], index)
            open_entry = data.Open(meta, new_entries[0].date, account_, None, None)
            new_open_entries.append(open_entry)

    return (entries + new_open_entries + new_entries, errors)