예제 #1
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)]
예제 #2
0
파일: charts.py 프로젝트: 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}
예제 #3
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)]
예제 #4
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)]
예제 #5
0
파일: charts.py 프로젝트: mhansen/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' 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)]
예제 #6
0
파일: __init__.py 프로젝트: yagebu/fava
    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)
예제 #7
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)
예제 #8
0
    def _journal(self, entries, include_types=None,
                 with_change_and_balance=False):
        if include_types:
            entries = [entry for entry in entries
                       if isinstance(entry, include_types)]

        if not with_change_and_balance:
            return [serialize_entry(entry) for entry in entries]
        else:
            return [serialize_entry_with(entry, change, balance)
                    for entry, _, change, balance in
                    realization.iterate_with_balance(entries)]
예제 #9
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)]
예제 #10
0
파일: __init__.py 프로젝트: cgrinds/fava
    def _journal(self, entries, include_types=None,
                 with_change_and_balance=False):
        if include_types:
            entries = [entry for entry in entries
                       if isinstance(entry, include_types)]

        if not with_change_and_balance:
            return [serialize_entry(entry) for entry in entries]
        else:
            return [serialize_entry_with(entry, change, balance)
                    for entry, _, change, balance in
                    realization.iterate_with_balance(entries)]
예제 #11
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)
        ]
예제 #12
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)]
예제 #13
0
파일: charts.py 프로젝트: yagebu/fava
    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)]
예제 #14
0
def size_and_render_amounts(postings, at_cost, render_balance):
    """Iterate through postings and compute sizers and render amounts.

    Args:
      postings: A list of Posting or directive instances.
      at_cost: A boolean, if true, render the cost value, not the actual.
      render_balance: A boolean, if true, renders a running balance column.
    """

    # Compute the maximum width required to render the change and balance
    # columns. In order to carry this out, we will pre-compute all the data to
    # render this and save it for later.
    change_sizer = AmountColumnSizer('change')
    balance_sizer = AmountColumnSizer('balance')

    entry_data = []
    for entry_line in realization.iterate_with_balance(postings):
        entry, leg_postings, change, balance = entry_line

        # Convert to cost if necessary. (Note that this agglutinates currencies,
        # so we'd rather do make the conversion at this level (inventory) than
        # convert the positions or amounts later.)
        if at_cost:
            change = change.reduce(convert.get_cost)
            if render_balance:
                balance = balance.reduce(convert.get_cost)

        # Compute the amounts and maximum widths for the change column.
        change_amounts = []
        for position in change.get_positions():
            units = position.units
            change_amounts.append(units)
            change_sizer.update(units.number, units.currency)

        # Compute the amounts and maximum widths for the balance column.
        balance_amounts = []
        if render_balance:
            for position in balance.get_positions():
                units = position.units
                balance_amounts.append(units)
                balance_sizer.update(units.number, units.currency)

        entry_data.append(
            (entry, leg_postings, change_amounts, balance_amounts))

    return (entry_data, change_sizer, balance_sizer)
예제 #15
0
파일: charts.py 프로젝트: adamgibbins/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' 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,
            }
예제 #16
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,
            }
예제 #17
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)
예제 #18
0
파일: __init__.py 프로젝트: TomJohnZ/fava
    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)]
예제 #19
0
파일: api.py 프로젝트: blais/beancount-web
    def _journal_for_postings(self, postings, include_types=None):
        journal = []

        for posting, leg_postings, change, entry_balance in realization.iterate_with_balance(postings):

            if include_types and not isinstance(posting, include_types):
                continue

            if  isinstance(posting, Transaction) or \
                isinstance(posting, Note) or \
                isinstance(posting, Balance) or \
                isinstance(posting, Open) or \
                isinstance(posting, Close) or \
                isinstance(posting, Pad) or \
                isinstance(posting, Document):   # TEMP

                # if isinstance(posting, TxnPosting):
                #     posting = posting.txn

                entry = {
                    'meta': {
                        'type': posting.__class__.__name__.lower(),
                        'filename': posting.meta['filename'],
                        'lineno': posting.meta['lineno']
                    },
                    'date': posting.date,
                    'hash': compare.hash_entry(posting)
                }

                if isinstance(posting, Open):
                    entry['account'] =       posting.account
                    entry['currencies'] =    posting.currencies
                    entry['booking'] =       posting.booking # TODO im html-template

                if isinstance(posting, Close):
                    entry['account'] =       posting.account

                if isinstance(posting, Note):
                    entry['comment'] =       posting.comment

                if isinstance(posting, Document):
                    entry['account'] =       posting.account
                    entry['filename'] =      posting.filename

                if isinstance(posting, Pad):
                    entry['account'] =       posting.account
                    entry['source_account'] =      posting.source_account

                if isinstance(posting, Balance):
                    # TODO failed balances
                    entry['account'] =       posting.account
                    entry['change'] =        { posting.amount.currency: posting.amount.number }
                    entry['balance'] =       { posting.amount.currency: posting.amount.number }
                    entry['tolerance'] =     posting.tolerance  # TODO currency? TODO in HTML-template
                    entry['diff_amount'] =   posting.diff_amount  # TODO currency? TODO in HTML-template

                if isinstance(posting, Transaction):
                    if posting.flag == 'P':
                        entry['meta']['type'] = 'padding'

                    entry['flag'] =         posting.flag
                    entry['payee'] =        posting.payee
                    entry['narration'] =    posting.narration
                    entry['tags'] =         posting.tags
                    entry['links'] =        posting.links
                    entry['change'] =       self._inventory_to_json(change)
                    entry['balance'] =      self._inventory_to_json(entry_balance)
                    entry['legs'] =         []


                    for posting_ in posting.postings:
                        leg = {
                            'account': posting_.account,
                            'flag': posting_.flag,
                            'hash': entry['hash']
                        }

                        if posting_.position:
                            leg['position'] = posting_.position.number
                            leg['position_currency'] = posting_.position.lot.currency

                        if posting_.price:
                            leg['price'] = posting_.price.number
                            leg['price_currency'] = posting_.price.currency

                        entry['legs'].append(leg)


                journal.append(entry)

        return journal
예제 #20
0
    def _journal_for_postings(self,
                              postings,
                              include_types=None,
                              with_change_and_balance=False):
        journal = []

        for posting, leg_postings, change, entry_balance in realization.iterate_with_balance(
                postings):

            if include_types and not isinstance(posting, include_types):
                continue

            if  isinstance(posting, Transaction) or \
                isinstance(posting, Note) or \
                isinstance(posting, Balance) or \
                isinstance(posting, Open) or \
                isinstance(posting, Close) or \
                isinstance(posting, Pad) or \
                isinstance(posting, Event) or \
                isinstance(posting, Document):

                entry = {
                    'meta': {
                        'type': posting.__class__.__name__.lower(),
                        'filename': posting.meta['filename'],
                        'lineno': posting.meta['lineno']
                    },
                    'date': posting.date,
                    'hash': compare.hash_entry(posting),
                    'metadata': posting.meta.copy()
                }

                entry['metadata'].pop("__tolerances__", None)
                entry['metadata'].pop("filename", None)
                entry['metadata'].pop("lineno", None)

                if isinstance(posting, Open):
                    entry['account'] = posting.account
                    entry['currencies'] = posting.currencies

                if isinstance(posting, Close):
                    entry['account'] = posting.account

                if isinstance(posting, Event):
                    entry['type'] = posting.type
                    entry['description'] = posting.description

                if isinstance(posting, Note):
                    entry['comment'] = posting.comment

                if isinstance(posting, Document):
                    entry['account'] = posting.account
                    entry['filename'] = posting.filename

                if isinstance(posting, Pad):
                    entry['account'] = posting.account
                    entry['source_account'] = posting.source_account

                if isinstance(posting, Balance):
                    entry['account'] = posting.account
                    entry['change'] = {
                        posting.amount.currency: posting.amount.number
                    }
                    entry['amount'] = {
                        posting.amount.currency: posting.amount.number
                    }

                    if posting.diff_amount:
                        balance = entry_balance.get_units(
                            posting.amount.currency)
                        entry['diff_amount'] = {
                            posting.diff_amount.currency:
                            posting.diff_amount.number
                        }
                        entry['balance'] = {balance.currency: balance.number}

                if isinstance(posting, Transaction):
                    if posting.flag == 'P':
                        entry['meta'][
                            'type'] = 'padding'  # TODO handle Padding, Summarize and Transfer

                    entry['flag'] = posting.flag
                    entry['payee'] = posting.payee
                    entry['narration'] = posting.narration
                    entry['tags'] = posting.tags or []
                    entry['links'] = posting.links or []
                    entry['legs'] = []

                    for posting_ in posting.postings:
                        leg = {
                            'account': posting_.account,
                            'flag': posting_.flag,
                            'hash': entry['hash']
                        }

                        if posting_.position:
                            leg['position'] = posting_.position.number
                            leg['position_currency'] = posting_.position.lot.currency
                            cost = interpolate.get_posting_weight(posting_)
                            leg['cost'] = cost.number
                            leg['cost_currency'] = cost.currency

                        if posting_.price:
                            leg['price'] = posting_.price.number
                            leg['price_currency'] = posting_.price.currency

                        entry['legs'].append(leg)

                if with_change_and_balance:
                    if isinstance(posting, Balance):
                        entry['change'] = {
                            posting.amount.currency: posting.amount.number
                        }
                        entry['balance'] = self._inventory_to_json(
                            entry_balance
                        )  #, include_currencies=entry['change'].keys())

                    if isinstance(posting, Transaction):
                        entry['change'] = self._inventory_to_json(change)
                        entry['balance'] = self._inventory_to_json(
                            entry_balance,
                            include_currencies=entry['change'].keys())

                journal.append(entry)

        return journal
예제 #21
0
파일: api.py 프로젝트: ghmajx/beancount-web
    def _journal_for_postings(self, postings, include_types=None, with_change_and_balance=False):
        journal = []

        for posting, leg_postings, change, entry_balance in realization.iterate_with_balance(postings):

            if include_types and not isinstance(posting, include_types):
                continue

            if  isinstance(posting, Transaction) or \
                isinstance(posting, Note) or \
                isinstance(posting, Balance) or \
                isinstance(posting, Open) or \
                isinstance(posting, Close) or \
                isinstance(posting, Pad) or \
                isinstance(posting, Event) or \
                isinstance(posting, Document):

                entry = {
                    'meta': {
                        'type': posting.__class__.__name__.lower(),
                        'filename': posting.meta['filename'],
                        'lineno': posting.meta['lineno']
                    },
                    'date': posting.date,
                    'hash': compare.hash_entry(posting),
                    'metadata': posting.meta.copy()
                }

                entry['metadata'].pop("__tolerances__", None)
                entry['metadata'].pop("filename", None)
                entry['metadata'].pop("lineno", None)

                if isinstance(posting, Open):
                    entry['account']        = posting.account
                    entry['currencies']     = posting.currencies

                if isinstance(posting, Close):
                    entry['account']        = posting.account

                if isinstance(posting, Event):
                    entry['type']           = posting.type
                    entry['description']    = posting.description

                if isinstance(posting, Note):
                    entry['comment']        = posting.comment

                if isinstance(posting, Document):
                    entry['account']        = posting.account
                    entry['filename']       = posting.filename

                if isinstance(posting, Pad):
                    entry['account']        = posting.account
                    entry['source_account'] = posting.source_account

                if isinstance(posting, Balance):
                    entry['account']        = posting.account
                    entry['change']         = { posting.amount.currency: posting.amount.number }
                    entry['amount']         = { posting.amount.currency: posting.amount.number }

                    if posting.diff_amount:
                        balance              = entry_balance.get_units(posting.amount.currency)
                        entry['diff_amount'] = { posting.diff_amount.currency: posting.diff_amount.number }
                        entry['balance']     = { balance.currency: balance.number }

                if isinstance(posting, Transaction):
                    if posting.flag == 'P':
                        entry['meta']['type'] = 'padding'  # TODO handle Padding, Summarize and Transfer

                    entry['flag']       = posting.flag
                    entry['payee']      = posting.payee
                    entry['narration']  = posting.narration
                    entry['tags']       = posting.tags or []
                    entry['links']      = posting.links or []
                    entry['legs']       = []

                    for posting_ in posting.postings:
                        leg = {
                            'account': posting_.account,
                            'flag': posting_.flag,
                            'hash': entry['hash']
                        }

                        if posting_.position:
                            leg['position']          = posting_.position.number
                            leg['position_currency'] = posting_.position.lot.currency
                            cost                     = interpolate.get_posting_weight(posting_)
                            leg['cost']              = cost.number
                            leg['cost_currency']     = cost.currency

                        if posting_.price:
                            leg['price']             = posting_.price.number
                            leg['price_currency']    = posting_.price.currency

                        entry['legs'].append(leg)

                if with_change_and_balance:
                    if isinstance(posting, Balance):
                        entry['change']     = { posting.amount.currency: posting.amount.number }
                        entry['balance']    = self._inventory_to_json(entry_balance)  #, include_currencies=entry['change'].keys())

                    if isinstance(posting, Transaction):
                        entry['change']     = self._inventory_to_json(change)
                        entry['balance']    = self._inventory_to_json(entry_balance, include_currencies=entry['change'].keys())

                journal.append(entry)

        return journal
예제 #22
0
    def test_iterate_with_balance(self, entries, _, __):
        """
        2012-01-01 open Assets:Bank:Checking
        2012-01-01 open Expenses:Restaurant
        2012-01-01 open Equity:Opening-Balances

        2012-01-15 pad Assets:Bank:Checking Equity:Opening-Balances

        2012-01-20 balance Assets:Bank:Checking  20.00 USD

        2012-03-01 * "With a single entry"
          Expenses:Restaurant     11.11 CAD
          Assets:Bank:Checking

        2012-03-02 * "With two entries"
          Expenses:Restaurant     20.01 CAD
          Expenses:Restaurant     20.02 CAD
          Assets:Bank:Checking

        2012-03-02 note Expenses:Restaurant  "This was good"

        2012-04-01 balance Expenses:Restaurant  51.14 CAD
        """
        root_account = realization.realize(entries)
        real_account = realization.get(root_account, 'Expenses:Restaurant')

        def simplify_rtuple(rtuple):
            return [(type(entry), len(postings), str(change), str(balance))
                    for entry, postings, change, balance in rtuple]

        # Surprinsingly enough, this covers all the legal cases that occur in
        # practice (checked for full coverage manually if you like).
        # pylint: disable=bad-whitespace
        rtuple = realization.iterate_with_balance(
            real_account.txn_postings[:-2])
        self.assertEqual([
            (data.Open, 0, '()', '()'),
            (data.Transaction, 1, '(11.11 CAD)', '(11.11 CAD)'),
            (data.Transaction, 2, '(40.03 CAD)', '(51.14 CAD)'),
        ], simplify_rtuple(rtuple))

        # Test it again with the final balance entry.
        rtuple = realization.iterate_with_balance(real_account.txn_postings)
        self.assertEqual([
            (data.Open, 0, '()', '()'),
            (data.Transaction, 1, '(11.11 CAD)', '(11.11 CAD)'),
            (data.Transaction, 2, '(40.03 CAD)', '(51.14 CAD)'),
            (data.Note, 0, '()', '(51.14 CAD)'),
            (data.Balance, 0, '()', '(51.14 CAD)'),
        ], simplify_rtuple(rtuple))

        # Test it out with valid input but with entries for the same transaction
        # separated by another entry. Swap the balance entry with the last
        # posting entry to test this.
        postings = list(real_account.txn_postings)
        postings[-3], postings[-2] = postings[-2], postings[-3]
        rtuple = realization.iterate_with_balance(postings)
        self.assertEqual([
            (data.Open, 0, '()', '()'),
            (data.Transaction, 1, '(11.11 CAD)', '(11.11 CAD)'),
            (data.Transaction, 2, '(40.03 CAD)', '(51.14 CAD)'),
            (data.Note, 0, '()', '(51.14 CAD)'),
            (data.Balance, 0, '()', '(51.14 CAD)'),
        ], simplify_rtuple(rtuple))

        # Go one step further and test it out with invalid date ordering.
        postings = list(real_account.txn_postings)
        postings[-1], postings[-2] = postings[-2], postings[-1]
        with self.assertRaises(AssertionError):
            list(realization.iterate_with_balance(postings))
예제 #23
0
def iterate_html_postings(txn_postings, formatter):
    """Iterate through the list of transactions with rendered HTML strings for each cell.

    This pre-renders all the data for each row to HTML. This is reused by the entries
    table rendering routines.

    Args:
      txn_postings: A list of TxnPosting or directive instances.
      formatter: An instance of HTMLFormatter, to be render accounts,
        inventories, links and docs.
    Yields:
      Instances of Row tuples. See above.
    """
    for entry_line in realization.iterate_with_balance(txn_postings):
        entry, leg_postings, change, entry_balance = entry_line

        # Prepare the data to be rendered for this row.
        balance_str = formatter.render_inventory(entry_balance)

        rowtype = entry.__class__.__name__
        flag = ''
        extra_class = ''
        links = None

        if isinstance(entry, data.Transaction):
            rowtype = FLAG_ROWTYPES.get(entry.flag, 'Transaction')
            extra_class = 'warning' if entry.flag == flags.FLAG_WARNING else ''
            flag = entry.flag
            description = '<span class="narration">{}</span>'.format(entry.narration)
            if entry.payee:
                description = ('<span class="payee">{}</span>'
                               '<span class="pnsep">|</span>'
                               '{}').format(entry.payee, description)
            amount_str = formatter.render_inventory(change)

            if entry.links and formatter:
                links = [formatter.render_link(link) for link in entry.links]

        elif isinstance(entry, data.Balance):
            # Check the balance here and possibly change the rowtype
            if entry.diff_amount is None:
                description = 'Balance {} has {}'.format(
                    formatter.render_account(entry.account),
                    entry.amount)
            else:
                description = ('Balance in {} fails; '
                               'expected = {}, balance = {}, difference = {}').format(
                                   formatter.render_account(entry.account),
                                   entry.amount,
                                   entry_balance.get_currency_units(entry.amount.currency),
                                   entry.diff_amount)
                extra_class = 'fail'

            amount_str = formatter.render_amount(entry.amount)

        elif isinstance(entry, (data.Open, data.Close)):
            description = '{} {}'.format(entry.__class__.__name__,
                                         formatter.render_account(entry.account))
            amount_str = ''

        elif isinstance(entry, data.Note):
            description = '{} {}'.format(entry.__class__.__name__, entry.comment)
            amount_str = ''
            balance_str = ''

        elif isinstance(entry, data.Document):
            assert path.isabs(entry.filename)
            description = 'Document for {}: {}'.format(
                formatter.render_account(entry.account),
                formatter.render_doc(entry.filename))
            amount_str = ''
            balance_str = ''

        else:
            description = entry.__class__.__name__
            amount_str = ''
            balance_str = ''

        yield Row(entry, leg_postings,
                  rowtype, extra_class,
                  flag, description, links, amount_str, balance_str)