~trufas/ledgeroni

4a3df2e838a604481dd8b5472cdaba35ec9a4fb6 — Rafael Castillo 1 year, 8 months ago 2b5f589 accounts-command
don't keep filter in internal journal state
M ledgeroni/commands/balance.py => ledgeroni/commands/balance.py +1 -1
@@ 20,7 20,7 @@ def print_balance(ctx, filter_strs):
    filter_query = MATCH_ALL
    if filter_strs:
        filter_query = expression.build_expression(' '.join(filter_strs))
    journal = Journal(query=filter_query)
    journal = Journal()

    aggregate = AccountAggregate(query=filter_query)


M ledgeroni/commands/print.py => ledgeroni/commands/print.py +5 -4
@@ 5,6 5,7 @@ import sys
import click
from ledgeroni.journal import Journal
from ledgeroni import expression
from ledgeroni.query import MATCH_ALL


@click.command()


@@ 12,12 13,12 @@ from ledgeroni import expression
@click.pass_context
def print_transactions(ctx, filter_strs):
    "`ledgeroni print` subcommand"
    filter_query = None
    filter_query = MATCH_ALL
    if filter_strs:
        filter_query = expression.build_expression(' '.join(filter_strs))

    sorter = ctx.obj.get('SORTER', None)
    journal = Journal(query=filter_query)
    journal = Journal()
    price_db = ctx.obj.get('PRICE_DB', None)
    if price_db:
        journal.add_from_file(price_db)


@@ 35,5 36,5 @@ def print_transactions(ctx, filter_strs):
    if sorter:
        sorter.sort_journal(journal)

    click.echo('\n\n'.join(t.as_journal_format()
                           for t in journal.transactions))
    click.echo('\n\n'.join(t.as_journal_format() for t in
                           journal.transactions_matching(filter_query)))

M ledgeroni/commands/register.py => ledgeroni/commands/register.py +4 -3
@@ 9,6 9,7 @@ from colorama import Fore, Style
from ledgeroni.journal import Journal
from ledgeroni.util import format_amount
from ledgeroni import expression
from ledgeroni.query import MATCH_ALL


def build_table(transaction, postings):


@@ 40,12 41,12 @@ def build_table(transaction, postings):
@click.pass_context
def print_register(ctx, filter_strs):
    "The `ledgeroni print` subcommand"
    filter_query = None
    filter_query = MATCH_ALL
    if filter_strs:
        filter_query = expression.build_expression(' '.join(filter_strs))
    sorter = ctx.obj.get('SORTER', None)

    journal = Journal(query=filter_query)
    journal = Journal()

    price_db = ctx.obj.get('PRICE_DB', None)
    if price_db:


@@ 64,7 65,7 @@ def print_register(ctx, filter_strs):
    if sorter:
        sorter.sort_journal(journal)

    for transaction, postings in journal.generate_running_total_report():
    for transaction, postings in journal.generate_running_total_report(filter_query):
        for trans, post, change, total in build_table(transaction, postings):
            click.echo('{:<64} {:<50} {} {}'.format(
                trans, post, change, total))

M ledgeroni/journal.py => ledgeroni/journal.py +12 -8
@@ 21,15 21,13 @@ class Journal:
    prices: List[Price] = field(default_factory=list)
    default_commodity: Commodity = None
    ignored_symbols: List[str] = field(default_factory=list)
    query: Query = None

    def add_transaction(self, transaction: Transaction):
        "Adds and indexes a transaction."
        if not self.query or self.query.execute(transaction):
            self.transactions.append(transaction)
            self.accounts.update(p.account for p in transaction.postings)
            self.commodities.update(c for p in transaction.postings
                                    if p.amounts for c in p.amounts)
        self.transactions.append(transaction)
        self.accounts.update(p.account for p in transaction.postings)
        self.commodities.update(c for p in transaction.postings
                                if p.amounts for c in p.amounts)

    def add_from_file(self, filename: str):
        "Loads all objects from a journal file"


@@ 43,14 41,16 @@ class Journal:
            elif isinstance(result, Price):
                self.prices.append(result)

    def generate_running_total_report(self) -> Iterator[Tuple[Transaction,
                                                              Dict]]:
    def generate_running_total_report(
            self, query: Query) -> Iterator[Tuple[Transaction, Dict]]:
        """
        Generates a running total from the transactions stored in the journal.
        """
        totals = defaultdict(Fraction)

        for transaction in self.transactions:
            if not query.execute(transaction):
                continue
            transaction = transaction.calc_totals()
            trans_total = {}
            for posting in transaction.postings:


@@ 61,6 61,10 @@ class Journal:
                trans_total[posting.account_name] = total
            yield transaction, trans_total

    def transactions_matching(self, query: Query) -> Iterator[Transaction]:
        "Returns this journals transactions filtered by a query"
        return (t for t in self.transactions if query.execute(t))

    def verify_transaction_balances(self) -> List[Transaction]:
        errors = []
        for transaction in self.transactions:

A tests/test_journal.py => tests/test_journal.py +80 -0
@@ 0,0 1,80 @@
import re
from datetime import datetime
import arrow
from ledgeroni.journal import Journal
from ledgeroni.query import And, Or, Not, RegexQuery, PayeeQuery
from ledgeroni.types import Transaction, Posting, Commodity

def test_add_transaction():
    journal = Journal()

    trans = Transaction(
        date=arrow.get(datetime(2013, 2, 20)),
        description='Purchased reddit gold for the year')
    trans.add_posting(Posting(
        account=('Asset', 'Bitcoin Wallet'),
        amounts={None: -10.0}))
    trans.add_posting(Posting(
        account=('Expense', 'Web Services', 'Reddit'),
        amounts=None))

    journal.add_transaction(trans)

    assert trans in journal.transactions


def test_add_transaction():
    journal = Journal()

    trans = Transaction(
        date=arrow.get(datetime(2013, 2, 20)),
        description='Purchased reddit gold for the year')
    trans.add_posting(Posting(
        account=('Asset', 'Bitcoin Wallet'),
        amounts={None: -10.0}))
    trans.add_posting(Posting(
        account=('Expense', 'Web Services', 'Reddit'),
        amounts=None))

    journal.add_transaction(trans)

    assert trans in journal.transactions


class TestTransactionsMatching:
    def test_filter_pass(self):
        journal = Journal()

        trans = Transaction(
            date=arrow.get(datetime(2013, 2, 20)),
            description='Purchased reddit gold for the year')
        trans.add_posting(Posting(
            account=('Asset', 'Bitcoin Wallet'),
            amounts={None: -10.0}))
        trans.add_posting(Posting(
            account=('Expense', 'Web Services', 'Reddit'),
            amounts=None))

        journal.add_transaction(trans)

        assert trans in journal.transactions_matching(
                query=RegexQuery(re.compile('Reddit')))


    def test_filter_pass(self):
        journal = Journal()

        trans = Transaction(
            date=arrow.get(datetime(2013, 2, 20)),
            description='Purchased reddit gold for the year')
        trans.add_posting(Posting(
            account=('Asset', 'Bitcoin Wallet'),
            amounts={None: -10.0}))
        trans.add_posting(Posting(
            account=('Expense', 'Web Services', 'Digg'),
            amounts=None))

        journal.add_transaction(trans)

        assert trans not in journal.transactions_matching(
                query=RegexQuery(re.compile('Reddit')))