reports: All reports support rewrite rules.

I realized that if ledger-report supported rewrite rules, then it would
include all the information necessary to reproduce the numbers on the
statement of functional expenses.

With that, it was easy enough to add support to the rest of the reports for
consistency's sake.
This commit is contained in:
Brett Smith 2020-08-31 14:19:00 -04:00
parent 8c81eceb2c
commit 35804db617
7 changed files with 51 additions and 20 deletions

View file

@ -119,6 +119,7 @@ class ExitCode(enum.IntEnum):
NoConfig = NoConfiguration
NoDataFiltered = os.EX_DATAERR
NoDataLoaded = os.EX_NOINPUT
RewriteRulesError = os.EX_DATAERR
# Our own exit codes, working down from that range
BeancountErrors = 63
@ -253,6 +254,17 @@ def add_loglevel_argument(parser: argparse.ArgumentParser,
f" Default {default.name.lower()}.",
)
def add_rewrite_rules_argument(parser: argparse.ArgumentParser) -> argparse.Action:
return parser.add_argument(
'--rewrite-rules', '--rewrites', '-r',
action='append',
default=[],
metavar='PATH',
type=Path,
help="""Use rewrite rules from the given YAML file. You can specify
this option multiple times to load multiple sets of rewrite rules in order.
""")
def add_version_argument(parser: argparse.ArgumentParser) -> argparse.Action:
progname = parser.prog or sys.argv[0]
return parser.add_argument(

View file

@ -115,6 +115,7 @@ import rt
from beancount.parser import printer as bc_printer
from . import core
from . import rewrite
from .. import books
from .. import cliutil
from .. import config as configmod
@ -647,6 +648,7 @@ def filter_search(postings: Iterable[data.Posting],
def parse_arguments(arglist: Optional[Sequence[str]]=None) -> argparse.Namespace:
parser = argparse.ArgumentParser(prog=PROGNAME)
cliutil.add_version_argument(parser)
cliutil.add_rewrite_rules_argument(parser)
parser.add_argument(
'--report-type', '-t',
metavar='NAME',
@ -721,9 +723,16 @@ def main(arglist: Optional[Sequence[str]]=None,
for error in load_errors:
bc_printer.print_error(error, file=stderr)
postings = list(filter_search(
data.Posting.from_entries(entries), args.search_terms,
))
postings_src = data.Posting.from_entries(entries)
for rewrite_path in args.rewrite_rules:
try:
ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path)
except ValueError as error:
logger.critical("failed loading rewrite rules from %s: %s",
rewrite_path, error.args[0])
return cliutil.ExitCode.RewriteRulesError
postings_src = ruleset.rewrite(postings_src)
postings = list(filter_search(postings_src, args.search_terms))
if not postings:
logger.warning("no matching entries found to report")
returncode = returncode or cliutil.ExitCode.NoDataFiltered

View file

@ -20,7 +20,6 @@ import datetime
import enum
import logging
import operator
import os
import sys
from decimal import Decimal
@ -633,15 +632,7 @@ The default is one year ago.
help="""Date to stop reporting entries, exclusive, in YYYY-MM-DD format.
The default is a year after the start date.
""")
parser.add_argument(
'--rewrite-rules', '--rewrites', '-r',
action='append',
default=[],
metavar='PATH',
type=Path,
help="""Use rewrite rules from the given YAML file. You can specify
this option multiple times to load multiple sets of rewrite rules in order.
""")
cliutil.add_rewrite_rules_argument(parser)
parser.add_argument(
'--fund-metadata-key', '-m',
metavar='KEY',
@ -705,7 +696,7 @@ def main(arglist: Optional[Sequence[str]]=None,
except ValueError as error:
logger.critical("failed loading rewrite rules from %s: %s",
rewrite_path, error.args[0])
return os.EX_DATAERR
return cliutil.ExitCode.RewriteRulesError
postings = ruleset.rewrite(postings)
balances = Balances(

View file

@ -74,6 +74,7 @@ import odf.table # type:ignore[import]
from beancount.parser import printer as bc_printer
from . import core
from . import rewrite
from .. import books
from .. import cliutil
from .. import config as configmod
@ -308,6 +309,7 @@ The default is one year ago.
help="""Date to stop reporting entries, exclusive, in YYYY-MM-DD format.
The default is a year after the start date.
""")
cliutil.add_rewrite_rules_argument(parser)
parser.add_argument(
'--report-type', '-t',
metavar='TYPE',
@ -378,11 +380,19 @@ def main(arglist: Optional[Sequence[str]]=None,
for error in load_errors:
bc_printer.print_error(error, file=stderr)
postings = (
postings = iter(
post
for post in data.Posting.from_entries(entries)
if post.meta.date < args.stop_date
)
for rewrite_path in args.rewrite_rules:
try:
ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path)
except ValueError as error:
logger.critical("failed loading rewrite rules from %s: %s",
rewrite_path, error.args[0])
return cliutil.ExitCode.RewriteRulesError
postings = ruleset.rewrite(postings)
for search_term in args.search_terms:
postings = search_term.filter_postings(postings)
fund_postings = {

View file

@ -78,6 +78,7 @@ from beancount.core import data as bc_data
from beancount.parser import printer as bc_printer
from . import core
from . import rewrite
from .. import books
from .. import cliutil
from .. import config as configmod
@ -701,6 +702,7 @@ multiple times. You can specify a part of the account hierarchy, or an account
classification from metadata. If not specified, the default set adapts to your
search criteria.
""")
cliutil.add_rewrite_rules_argument(parser)
parser.add_argument(
'--show-totals', '-S',
metavar='ACCOUNT',
@ -797,6 +799,14 @@ def main(arglist: Optional[Sequence[str]]=None,
data.Account.load_from_books(entries, options)
postings = data.Posting.from_entries(entries)
for rewrite_path in args.rewrite_rules:
try:
ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path)
except ValueError as error:
logger.critical("failed loading rewrite rules from %s: %s",
rewrite_path, error.args[0])
return cliutil.ExitCode.RewriteRulesError
postings = ruleset.rewrite(postings)
for search_term in args.search_terms:
postings = search_term.filter_postings(postings)

View file

@ -196,6 +196,8 @@ def main(arglist: Optional[Sequence[str]]=None,
raise ValueError(f"unknown year {year!r}")
out_path = args.output_directory / out_name
output_reports.append(out_path)
for path in args.rewrite_rules:
yield f'--rewrite-rules={path}'
yield f'--output-file={out_path}'
yield from arglist
reports: List[Tuple[ReportFunc, ArgList]] = [
@ -208,10 +210,7 @@ def main(arglist: Optional[Sequence[str]]=None,
(ledger.main, list(common_args('Disbursements', next_year, '--disbursements'))),
(ledger.main, list(common_args('Receipts', next_year, '--receipts'))),
(accrual.main, list(common_args('AgingReport.ods'))),
(balance_sheet.main, list(common_args(
'Summary', args.audit_year,
*(f'--rewrite-rules={path}' for path in args.rewrite_rules),
))),
(balance_sheet.main, list(common_args('Summary', args.audit_year))),
(fund.main, list(common_args('FundReport', args.audit_year))),
(fund.main, list(common_args('FundReport', next_year))),
]

View file

@ -5,7 +5,7 @@ from setuptools import setup
setup(
name='conservancy_beancount',
description="Plugin, library, and reports for reading Conservancy's books",
version='1.9.0',
version='1.9.1',
author='Software Freedom Conservancy',
author_email='info@sfconservancy.org',
license='GNU AGPLv3+',