Changeset - 20d242c7c753
[Not reviewed]
0 2 0
Ben Sturmfels (bsturmfels) - 2 years ago 2022-03-02 07:26:10
ben@sturm.com.au
reconcile: Fix a mutation bug causing not all matches to be passed through.
2 files changed with 54 insertions and 20 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reconcile/statement_reconciler.py
Show inline comments
...
 
@@ -106,3 +106,3 @@ import re
 
import sys
 
from typing import Callable, Dict, List, Tuple, TextIO
 
from typing import Callable, Dict, List, Optional, Sequence, Tuple, TextIO
 

	
...
 
@@ -112,2 +112,5 @@ from colorama import Fore, Style  # type: ignore
 

	
 
from .. import cliutil
 
from .. import config as configmod
 

	
 
if not sys.warnoptions:
...
 
@@ -119,8 +122,8 @@ from thefuzz import fuzz  # type: ignore
 

	
 
logger = logging.getLogger()
 
logger.setLevel(logging.INFO)
 

	
 
# Console logging.
 
logger.addHandler(logging.StreamHandler())
 
PROGNAME = 'reconcile-statement'
 
logger = logging.getLogger(__name__)
 

	
 
# Get some interesting feedback on call to RT with this:
 
# logger.setLevel(logging.DEBUG)
 
# logger.addHandler(logging.StreamHandler())
 

	
...
 
@@ -459,4 +462,12 @@ def parse_repo_relative_path(path: str) -> str:
 

	
 
def parse_args(argv: List[str]) -> argparse.Namespace:
 
    parser = argparse.ArgumentParser(description='Reconciliation helper')
 
def parse_decimal_with_separator(number_text: str) -> decimal.Decimal:
 
    """decimal.Decimal can't parse numbers with thousands separator."""
 
    number_text = number_text.replace(',', '')
 
    return decimal.Decimal(number_text)
 

	
 

	
 
def parse_arguments(argv: List[str]) -> argparse.Namespace:
 
    parser = argparse.ArgumentParser(prog=PROGNAME, description='Reconciliation helper')
 
    cliutil.add_version_argument(parser)
 
    cliutil.add_loglevel_argument(parser)
 
    parser.add_argument('--beancount-file', required=True, type=parse_path)
...
 
@@ -468,5 +479,6 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
 
    parser.add_argument('--show-reconciled-matches', action='store_true')
 
    parser.add_argument('--statement-balance', type=decimal.Decimal, required=True, help="A.K.A \"cleared balance\" taken from the end of the period on the PDF statement. Required because CSV statements don't include final or running totals")
 
    parser.add_argument('--statement-balance', type=parse_decimal_with_separator, required=True, help="A.K.A \"cleared balance\" taken from the end of the period on the PDF statement. Required because CSV statements don't include final or running totals")
 
    parser.add_argument('--non-interactive', action='store_true', help="Don't prompt to write to the books")
 
    return parser.parse_args(args=argv[1:])
 
    args = parser.parse_args(args=argv)
 
    return args
 

	
...
 
@@ -514,5 +526,2 @@ def subset_match(statement_trans: List[dict], books_trans: List[dict]) -> Tuple[
 
                del statement_trans[best_match_index]
 
            for item in group_items:
 
                # TODO: Why?
 
                books_trans.remove(item)
 
        else:
...
 
@@ -533,3 +542,13 @@ def process_unmatched(statement_trans: List[dict], books_trans: List[dict]) -> L
 

	
 
def main(args: argparse.Namespace) -> None:
 
def main(arglist: Optional[Sequence[str]] = None,
 
         stdout: TextIO = sys.stdout,
 
         stderr: TextIO = sys.stderr,
 
         config: Optional[configmod.Config] = None,
 
         ) -> int:
 
    args = parse_arguments(arglist)
 
    cliutil.set_loglevel(logger, args.loglevel)
 
    if config is None:
 
        config = configmod.Config()
 
        config.load_file()
 

	
 
    # TODO: Should put in a sanity check to make sure the statement you're feeding
...
 
@@ -609,8 +628,5 @@ def main(args: argparse.Namespace) -> None:
 

	
 
if __name__ == '__main__':
 
    args = parse_args(sys.argv)
 
    main(args)
 
entry_point = cliutil.make_entry_point(__name__, PROGNAME)
 

	
 
def entry_point():
 
    args = parse_args(sys.argv)
 
    main(args)
 
if __name__ == '__main__':
 
    exit(entry_point())
tests/test_reconcile.py
Show inline comments
...
 
@@ -310 +310,19 @@ def test_subset_sum_match():
 
    )
 

	
 
def test_subset_passes_through_all_non_matches():
 
    """This was used to locate a bug where some of the non-matches had
 
    gone missing due to mutation of books_trans."""
 
    statement_trans = [
 
        S1,  # No match
 
        S4,  # Match
 
    ]
 
    books_trans = [
 
        B2,  # No match
 
        B4A, B4B, B4C, # Match
 
        B3_next_day, B3_next_week,  # No match
 
    ]
 
    assert subset_match(statement_trans, books_trans) == (
 
        [([S4], [B4A, B4B, B4C], [])],  # Matched
 
        [S1],  # No match: preserved intact
 
        [B2, B3_next_day, B3_next_week]  # No match: preserved intact
 
    )
0 comments (0 inline, 0 general)