Exemplo n.º 1
0
 def test_contains(self):
     ra0 = RealAccount('')
     realization.get_or_create(ra0, 'Assets:US:Bank:Checking')
     realization.get_or_create(ra0, 'Assets:US:Bank:Savings')
     self.assertTrue(realization.contains(ra0, 'Assets:US:Bank:Checking'))
     self.assertTrue(realization.contains(ra0, 'Assets:US:Bank:Savings'))
     self.assertFalse(realization.contains(ra0, 'Assets:US:Cash'))
Exemplo n.º 2
0
    def test_compare_realizations(self):
        # Check that value comparison uses our balance comparison properly.
        map1 = {'Assets:US:Bank:Checking': inventory.Inventory()}
        map2 = {'Assets:US:Bank:Checking': inventory.Inventory()}
        map2['Assets:US:Bank:Checking'].add_amount(A('0.01 USD'))
        self.assertNotEqual(map1, map2)

        # Now check this with accounts.
        root1 = RealAccount('')
        ra1 = realization.get_or_create(root1, 'Assets:US:Bank:Checking')
        ra1.balance.add_amount(A('0.01 USD'))
        root2 = RealAccount('')
        ra2 = realization.get_or_create(root2, 'Assets:US:Bank:Checking')
        ra2.balance.add_amount(A('0.01 USD'))
        self.assertEqual(ra1, ra2)

        root3 = copy.deepcopy(root2)
        ra3 = realization.get(root3, 'Assets:US:Bank:Checking')
        ra3.account = 'Liabilities:US:CreditCard'
        self.assertNotEqual(root1, root3)

        root3 = copy.deepcopy(root2)
        ra3 = realization.get(root3, 'Assets:US:Bank:Checking')
        ra3.balance.add_amount(A('0.01 CAD'))
        self.assertNotEqual(root1, root3)

        root3 = copy.deepcopy(root2)
        ra3 = realization.get(root3, 'Assets:US:Bank:Checking')
        ra3.txn_postings.append('posting')
        self.assertNotEqual(root1, root3)

        root3 = copy.deepcopy(root2)
        ra3 = realization.get(root3, 'Assets:US:Bank:Checking')
        ra3['Sub'] = RealAccount('Assets:US:Bank:Checking:Sub')
        self.assertNotEqual(root1, root3)
Exemplo n.º 3
0
    def test_iter_children(self):
        ra0 = RealAccount('')
        for account_name in ['Assets:US:Bank:Checking',
                             'Assets:US:Bank:Savings',
                             'Assets:US:Cash',
                             'Assets:CA:Cash']:
            realization.get_or_create(ra0, account_name)

        # Test enumerating all accounts.
        self.assertEqual(['',
                          'Assets',
                          'Assets:CA',
                          'Assets:CA:Cash',
                          'Assets:US',
                          'Assets:US:Bank',
                          'Assets:US:Bank:Checking',
                          'Assets:US:Bank:Savings',
                          'Assets:US:Cash'],
                         [ra.account for ra in realization.iter_children(ra0)])

        # Test enumerating leaves only.
        self.assertEqual(['Assets:CA:Cash',
                          'Assets:US:Bank:Checking',
                          'Assets:US:Bank:Savings',
                          'Assets:US:Cash'],
                         [ra.account for ra in realization.iter_children(ra0, True)])
Exemplo n.º 4
0
    def test_get_or_create(self):
        ra0 = RealAccount('')
        ra0_checking = realization.get_or_create(ra0, 'Assets:US:Bank:Checking')
        realization.get_or_create(ra0, 'Assets:US:Bank:Savings')
        self.assertEqual('Assets:US:Bank:Checking', ra0_checking.account)
        self.assertEqual({'Assets'}, ra0.keys())
        self.assertEqual({'Checking', 'Savings'}, ra0['Assets']['US']['Bank'].keys())

        ra0_assets = ra0['Assets']
        ra0_assets2 = realization.get_or_create(ra0, 'Assets')
        self.assertTrue(ra0_assets2 is ra0_assets)
Exemplo n.º 5
0
def get_or_create(
    account: realization.RealAccount, account_name: str
) -> realization.RealAccount:
    """Get or create a child account."""
    if account.account == account_name:
        return account
    return realization.get_or_create(account, account_name)
Exemplo n.º 6
0
    def account_uptodate_status(self, account_name):
        """Status of the last balance.

        Args:
            account_name: An account name.

        Returns:
            A status string for the last balance of the account,
            as well as the date of the last balance.

            - 'green':  A balance check that passed.
            - 'red':    A balance check that failed.
        """

        real_account = realization.get_or_create(self.ledger.all_root_account,
                                                 account_name)

        status = None
        date = None
        for txn_posting in reversed(real_account.txn_postings):
            if isinstance(txn_posting, Balance):
                date = txn_posting.date
                if txn_posting.diff_amount:
                    status = "red"
                    break
                # XXX check date
                status = "green"
                break

        return status, date
Exemplo n.º 7
0
Arquivo: charts.py Projeto: SSITB/fava
    def linechart(self, account_name):
        """The balance of an account.

        Args:
            account_name: A string.

        Returns:
            A list of dicts for all dates on which the balance of the given
            account has changed containing the balance (in units) of the
            account at that date.
        """
        real_account = realization.get_or_create(self.ledger.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        # When the balance for a commodity just went to zero, it will be
        # missing from the 'balance' so keep track of currencies that last had
        # a balance.
        last_currencies = None

        for entry, _, change, balance in journal:
            if change.is_empty():
                continue

            balance = inv_to_dict(cost_or_value(balance, entry.date))

            currencies = set(balance.keys())
            if last_currencies:
                for currency in last_currencies - currencies:
                    balance[currency] = 0
            last_currencies = currencies

            yield {"date": entry.date, "balance": balance}
Exemplo n.º 8
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))
Exemplo n.º 9
0
    def account_uptodate_status(self, account_name):
        """Status of the last balance or transaction.

        Args:
            account_name: An account name.

        Returns:
            A status string for the last balance or transaction of the account.

            - 'green':  A balance check that passed.
            - 'red':    A balance check that failed.
            - 'yellow': Not a balance check.
        """

        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        for txn_posting in reversed(real_account.txn_postings):
            if isinstance(txn_posting, Balance):
                if txn_posting.diff_amount:
                    return 'red'
                return 'green'
            if isinstance(txn_posting, TxnPosting) and \
                    txn_posting.txn.flag != FLAG_UNREALIZED:
                return 'yellow'
        return None
Exemplo n.º 10
0
    def account_journal(self, account_name, with_journal_children=False):
        """Journal for an account.

        Args:
            account_name: An account name.
            with_journal_children: Whether to include postings of subaccounts
                of the given account.

        Returns:
            A list of tuples ``(entry, postings, change, balance)``.
            change and balance have already been reduced to units.
        """
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [(entry, postings_, copy.copy(change), copy.copy(balance)) for (
            entry,
            postings_,
            change,
            balance,
        ) in realization.iterate_with_balance(postings)]
Exemplo n.º 11
0
    def account_journal(self, account_name, with_journal_children=False):
        """Journal for an account.

        Args:
            account_name: An account name.
            with_journal_children: Whether to include postings of subaccounts
                of the given account.

        Returns:
            A list of tuples ``(entry, postings, change, balance)``.
            change and balance have already been reduced to units.
        """
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            # pylint: disable=unused-variable
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [(entry, postings_,
                 copy.copy(change),
                 copy.copy(balance)) for
                (entry, postings_, change, balance) in
                realization.iterate_with_balance(postings)]
Exemplo n.º 12
0
    def account_uptodate_status(self, account_name):
        """Status of the last balance or transaction.

        Args:
            account_name: An account name.

        Returns:
            A status string for the last balance or transaction of the account.

            - 'green':  A balance check that passed.
            - 'red':    A balance check that failed.
            - 'yellow': Not a balance check.
        """

        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        for txn_posting in reversed(real_account.txn_postings):
            if isinstance(txn_posting, Balance):
                if txn_posting.diff_amount:
                    return "red"
                return "green"
            if (isinstance(txn_posting, TxnPosting)
                    and txn_posting.txn.flag != FLAG_UNREALIZED):
                return "yellow"
        return None
Exemplo n.º 13
0
    def linechart(self, account_name):
        """The balance of an account.

        Args:
            account_name: A string.

        Returns:
            A list of dicts for all dates on which the balance of the given
            account has changed containing the balance (in units) of the
            account at that date.
        """
        real_account = realization.get_or_create(self.ledger.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        # When the balance for a commodity just went to zero, it will be
        # missing from the 'balance' field but appear in the 'change' field.
        # Use 0 for those commodities.
        return [{
            'date':
            entry.date,
            'balance':
            dict({curr: 0
                  for curr in list(change.currencies())},
                 **_inventory_units(balance)),
        } for entry, _, change, balance in journal if len(change)]
Exemplo n.º 14
0
    def account_journal(
        self,
        filtered: FilteredLedger,
        account_name: str,
        with_journal_children: bool = False,
    ) -> list[tuple[Directive, list[Posting], Inventory, Inventory]]:
        """Journal for an account.

        Args:
            filtered: The currently filtered ledger.
            account_name: An account name.
            with_journal_children: Whether to include postings of subaccounts
                of the given account.

        Returns:
            A list of tuples ``(entry, postings, change, balance)``.
            change and balance have already been reduced to units.
        """
        real_account = realization.get_or_create(filtered.root_account,
                                                 account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [(entry, postings_, copy.copy(change), copy.copy(balance)) for (
            entry,
            postings_,
            change,
            balance,
        ) in realization.iterate_with_balance(postings)]
Exemplo n.º 15
0
 def account_open_metadata(self, account_name):
     real_account = realization.get_or_create(self.root_account,
                                              account_name)
     postings = realization.get_postings(real_account)
     for posting in postings:
         if isinstance(posting, Open):
             return posting.meta
     return {}
Exemplo n.º 16
0
 def account_open_metadata(self, account_name):
     real_account = realization.get_or_create(self.root_account,
                                              account_name)
     postings = realization.get_postings(real_account)
     for posting in postings:
         if isinstance(posting, Open):
             return posting.meta
     return {}
Exemplo n.º 17
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)
Exemplo n.º 18
0
    def account_journal(self, account_name, with_journal_children=False):
        real_account = realization.get_or_create(self.root_account, account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return realization.iterate_with_balance(postings)
Exemplo n.º 19
0
    def _last_balance_or_transaction(self, account_name):
        real_account = realization.get_or_create(self.all_root_account, account_name)

        for txn_posting in reversed(real_account.txn_postings):
            if not isinstance(txn_posting, (TxnPosting, Balance)):
                continue

            if isinstance(txn_posting, TxnPosting) and txn_posting.txn.flag == flags.FLAG_UNREALIZED:
                continue
            return txn_posting
Exemplo n.º 20
0
    def account_metadata(self, account_name):
        """Metadata of the account.

        This is read from the Open entry of the account.
        """
        real_account = realization.get_or_create(self.root_account, account_name)
        for posting in real_account.txn_postings:
            if isinstance(posting, Open):
                return posting.meta
        return {}
Exemplo n.º 21
0
    def account_journal(self, account_name, with_journal_children=False):
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return realization.iterate_with_balance(postings)
Exemplo n.º 22
0
    def account_metadata(self, account_name):
        """Metadata of the account.

        This is read from the Open entry of the account.
        """
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)
        for posting in real_account.txn_postings:
            if isinstance(posting, Open):
                return posting.meta
        return {}
Exemplo n.º 23
0
    def last_entry(self, account_name):
        """The last entry of the account if it is not a Close entry.
        """
        account = realization.get_or_create(self.all_root_account, account_name)

        last = realization.find_last_active_posting(account.txn_postings)

        if last is None or isinstance(last, Close):
            return

        return get_entry(last)
Exemplo n.º 24
0
    def last_entry(self, account_name):
        """The last entry of the account if it is not a Close entry.
        """
        account = realization.get_or_create(self.all_root_account,
                                            account_name)

        last = realization.find_last_active_posting(account.txn_postings)

        if last is None or isinstance(last, Close):
            return

        return get_entry(last)
Exemplo n.º 25
0
    def account_journal(self, account_name, with_journal_children=False):
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [serialize_entry_with(entry, change, balance)
                for entry, _, change, balance in
                realization.iterate_with_balance(postings)]
Exemplo n.º 26
0
    def _last_balance_or_transaction(self, account_name):
        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        for txn_posting in reversed(real_account.txn_postings):
            if not isinstance(txn_posting, (TxnPosting, Balance)):
                continue

            if isinstance(txn_posting, TxnPosting) and \
               txn_posting.txn.flag == flags.FLAG_UNREALIZED:
                continue
            return txn_posting
Exemplo n.º 27
0
    def account_journal(self, account_name, with_journal_children=False):
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [
            serialize_entry_with(entry, change, balance) for entry, _, change,
            balance in realization.iterate_with_balance(postings)
        ]
Exemplo n.º 28
0
    def last_account_activity_in_days(self, account_name):
        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        last_posting = realization.find_last_active_posting(
            real_account.txn_postings)

        if last_posting is None or isinstance(last_posting, Close):
            return 0

        entry = get_entry(last_posting)

        return (datetime.date.today() - entry.date).days
Exemplo n.º 29
0
    def last_account_activity_in_days(self, account_name):
        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        last_posting = realization.find_last_active_posting(
            real_account.txn_postings)

        if last_posting is None or isinstance(last_posting, Close):
            return 0

        entry = get_entry(last_posting)

        return (datetime.date.today() - entry.date).days
Exemplo n.º 30
0
    def linechart(self, account_name):
        real_account = realization.get_or_create(self.api.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        return [{
            'date': entry.date,
            # when there's no holding for a commodity, it will be missing from
            # 'balance' field but appear in 'change' field. Use 0 for those
            # commodities.
            'balance': dict({curr: 0 for curr in list(change.currencies())},
                            **_serialize_inventory(balance)),
        } for entry, _, change, balance in journal if len(change)]
Exemplo n.º 31
0
    def linechart(self, account_name):
        real_account = realization.get_or_create(self.ledger.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        return [{
            'date': entry.date,
            # when there's no holding for a commodity, it will be missing from
            # 'balance' field but appear in 'change' field. Use 0 for those
            # commodities.
            'balance': dict({curr: 0 for curr in list(change.currencies())},
                            **_serialize_inventory(balance)),
        } for entry, _, change, balance in journal if len(change)]
Exemplo n.º 32
0
    def account_metadata(self, account_name):
        """Metadata of the account.

        Args:
            account_name: An account name.

        Returns:
            Metadata of the Open entry of the account.
        """
        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)
        for posting in real_account.txn_postings:
            if isinstance(posting, Open):
                return posting.meta
        return {}
Exemplo n.º 33
0
    def journal(self, account_name=None, with_change_and_balance=False,
                with_journal_children=True):
        if account_name:
            real_account = realization.get_or_create(self.root_account,
                                                     account_name)

            if with_journal_children:
                postings = realization.get_postings(real_account)
            else:
                postings = real_account.txn_postings

            return self._journal(postings, with_change_and_balance=True)
        else:
            return self._journal(
                self.entries, with_change_and_balance=with_change_and_balance)
Exemplo n.º 34
0
    def journal(self, account_name=None, with_change_and_balance=False,
                with_journal_children=True):
        if account_name:
            real_account = realization.get_or_create(self.root_account,
                                                     account_name)

            if with_journal_children:
                postings = realization.get_postings(real_account)
            else:
                postings = real_account.txn_postings

            return self._journal(postings, with_change_and_balance=True)
        else:
            return self._journal(
                self.entries, with_change_and_balance=with_change_and_balance)
Exemplo n.º 35
0
    def last_entry(self, account_name: str) -> Directive | None:
        """Get last entry of an account.

        Args:
            account_name: An account name.

        Returns:
            The last entry of the account if it is not a Close entry.
        """
        account = realization.get_or_create(self.all_root_account,
                                            account_name)

        last = realization.find_last_active_posting(account.txn_postings)

        if last is None or isinstance(last, Close):
            return None

        return get_entry(last)
Exemplo n.º 36
0
    def _last_posting_for_account(self, account_name):
        """
        Returns the last posting for an account (ignores Close)
        """
        real_account = realization.get_or_create(self.all_root_account,
                                                 account_name)

        last_posting = realization.find_last_active_posting(
            real_account.txn_postings)

        if not isinstance(last_posting, Close):
            return last_posting

        postings = realization.get_postings(real_account)
        if len(postings) >= 2:
            return postings[-2]

        return None
Exemplo n.º 37
0
    def linechart(self, account_name):
        """The balance of an account.

        Args:
            account_name: A string.

        Returns:
            A list of dicts for all dates on which the balance of the given
            account has changed containing the balance (in units) of the
            account at that date.
        """
        real_account = realization.get_or_create(self.ledger.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        # When the balance for a commodity just went to zero, it will be
        # missing from the 'balance' field but appear in the 'change' field.
        # Use 0 for those commodities.
        for entry, _, change, balance in journal:
            if change.is_empty():
                continue

            if g.conversion == 'units':
                bal = {curr: 0 for curr in list(change.currencies())}
                bal.update({
                    p.units.currency: p.units.number
                    for p in balance.reduce(convert.get_units)
                })
            else:
                bal = {
                    p.units.currency: p.units.number
                    for p in cost_or_value(balance, entry.date)
                }

            yield {
                'date': entry.date,
                'balance': bal,
            }
Exemplo n.º 38
0
    def linechart(self, filtered: FilteredLedger, account_name: str,
                  conversion: str) -> Generator[DateAndBalance, None, None]:
        """The balance of an account.

        Args:
            account_name: A string.
            conversion: The conversion to use.

        Returns:
            A list of dicts for all dates on which the balance of the given
            account has changed containing the balance (in units) of the
            account at that date.
        """
        real_account = realization.get_or_create(filtered.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        # When the balance for a commodity just went to zero, it will be
        # missing from the 'balance' so keep track of currencies that last had
        # a balance.
        last_currencies = None

        price_map = self.ledger.price_map
        for entry, _, change, balance_inventory in journal:
            if change.is_empty():
                continue

            balance = inv_to_dict(
                cost_or_value(balance_inventory, conversion, price_map,
                              entry.date))

            currencies = set(balance.keys())
            if last_currencies:
                for currency in last_currencies - currencies:
                    balance[currency] = 0
            last_currencies = currencies

            yield DateAndBalance(entry.date, balance)
Exemplo n.º 39
0
    def linechart(self, account_name):
        """The balance of an account.

        Args:
            account_name: A string.

        Returns:
            A list of dicts for all dates on which the balance of the given
            account has changed containing the balance (in units) of the
            account at that date.
        """
        real_account = realization.get_or_create(self.ledger.root_account,
                                                 account_name)
        postings = realization.get_postings(real_account)
        journal = realization.iterate_with_balance(postings)

        # When the balance for a commodity just went to zero, it will be
        # missing from the 'balance' field but appear in the 'change' field.
        # Use 0 for those commodities.
        for entry, _, change, balance in journal:
            if change.is_empty():
                continue

            if g.conversion == 'units':
                bal = {curr: 0 for curr in list(change.currencies())}
                bal.update({
                    p.units.currency: p.units.number
                    for p in balance.reduce(convert.get_units)
                })
            else:
                bal = {
                    p.units.currency: p.units.number
                    for p in cost_or_value(balance, entry.date)
                }

            yield {
                'date': entry.date,
                'balance': bal,
            }
Exemplo n.º 40
0
    def account_journal(self, account_name, with_journal_children=False):
        """Journal for an account.

        Args:
            account_name: An account name.
            with_journal_children: Whether to include postings of subaccounts
                of the given account.

        Returns:
            A list of tuples ``(entry, postings, change, balance)``.

        """
        real_account = realization.get_or_create(self.root_account,
                                                 account_name)

        if with_journal_children:
            # pylint: disable=unused-variable
            postings = realization.get_postings(real_account)
        else:
            postings = real_account.txn_postings

        return [(entry, postings, change, copy.copy(balance))
                for (entry, postings, change,
                     balance) in realization.iterate_with_balance(postings)]
Exemplo n.º 41
0
def check(entries, options_map):
    """Process the balance assertion directives.

    For each Balance directive, check that their expected balance corresponds to
    the actual balance computed at that time and replace failing ones by new
    ones with a flag that indicates failure.

    Args:
      entries: A list of directives.
      options_map: A dict of options, parsed from the input file.
    Returns:
      A pair of a list of directives and a list of balance check errors.
    """
    new_entries = []
    check_errors = []

    # This is similar to realization, but performed in a different order, and
    # where we only accumulate inventories for accounts that have balance
    # assertions in them (this saves on time). Here we process the entries one
    # by one along with the balance checks. We use a temporary realization in
    # order to hold the incremental tree of balances, so that we can easily get
    # the amounts of an account's subaccounts for making checks on parent
    # accounts.
    real_root = realization.RealAccount('')

    # Figure out the set of accounts for which we need to compute a running
    # inventory balance.
    asserted_accounts = {
        entry.account
        for entry in entries if isinstance(entry, Balance)
    }

    # Add all children accounts of an asserted account to be calculated as well,
    # and pre-create these accounts, and only those (we're just being tight to
    # make sure).
    asserted_match_list = [
        account.parent_matcher(account_) for account_ in asserted_accounts
    ]
    for account_ in getters.get_accounts(entries):
        if (account_ in asserted_accounts
                or any(match(account_) for match in asserted_match_list)):
            realization.get_or_create(real_root, account_)

    # Get the Open directives for each account.
    open_close_map = getters.get_account_open_close(entries)

    for entry in entries:
        if isinstance(entry, Transaction):
            # For each of the postings' accounts, update the balance inventory.
            for posting in entry.postings:
                real_account = realization.get(real_root, posting.account)

                # The account will have been created only if we're meant to track it.
                if real_account is not None:
                    # Note: Always allow negative lots for the purpose of balancing.
                    # This error should show up somewhere else than here.
                    real_account.balance.add_position(posting)

        elif isinstance(entry, Balance):
            # Check that the currency of the balance check is one of the allowed
            # currencies for that account.
            expected_amount = entry.amount
            try:
                open, _ = open_close_map[entry.account]
            except KeyError:
                check_errors.append(
                    BalanceError(
                        entry.meta,
                        "Account '{}' does not exist: ".format(entry.account),
                        entry))
                continue

            if (expected_amount is not None and open and open.currencies
                    and expected_amount.currency not in open.currencies):
                check_errors.append(
                    BalanceError(
                        entry.meta,
                        "Invalid currency '{}' for Balance directive: ".format(
                            expected_amount.currency), entry))

            # Check the balance against the check entry.
            real_account = realization.get(real_root, entry.account)
            assert real_account is not None, "Missing {}".format(entry.account)

            # Sum up the current balances for this account and its
            # sub-accounts. We want to support checks for parent accounts
            # for the total sum of their subaccounts.
            subtree_balance = inventory.Inventory()
            for real_child in realization.iter_children(real_account, False):
                subtree_balance += real_child.balance

            # Get only the amount in the desired currency.
            balance_amount = subtree_balance.get_currency_units(
                expected_amount.currency)

            # Check if the amount is within bounds of the expected amount.
            diff_amount = amount.sub(balance_amount, expected_amount)

            # Use the specified tolerance or automatically infer it.
            tolerance = get_balance_tolerance(entry, options_map)

            if abs(diff_amount.number) > tolerance:
                check_errors.append(
                    BalanceError(
                        entry.meta,
                        ("Balance failed for '{}': "
                         "expected {} != accumulated {} ({} {})").format(
                             entry.account, expected_amount, balance_amount,
                             abs(diff_amount.number),
                             ('too much' if diff_amount.number > 0 else
                              'too little')), entry))

                # Substitute the entry by a failing entry, with the diff_amount
                # field set on it. I'm not entirely sure that this is the best
                # of ideas, maybe leaving the original check intact and insert a
                # new error entry might be more functional or easier to
                # understand.
                entry = entry._replace(meta=entry.meta.copy(),
                                       diff_amount=diff_amount)

        new_entries.append(entry)

    return new_entries, check_errors
Exemplo n.º 42
0
def get_or_create(account, account_name):
    """Get or create a child account."""
    if account.account == account_name:
        return account
    return realization.get_or_create(account, account_name)
Exemplo n.º 43
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)
Exemplo n.º 44
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)
Exemplo n.º 45
0
 def balances(self, account_name):
     return realization.get_or_create(self.root_account, account_name)
Exemplo n.º 46
0
def create_real(account_value_pairs):
    real_root = RealAccount('')
    for account_name, value in account_value_pairs:
        real_account = realization.get_or_create(real_root, account_name)
        real_account.balance += inventory.from_string(value)
    return real_root
Exemplo n.º 47
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)