Changeset - 8b08997fda07
[Not reviewed]
0 2 0
Ben Sturmfels (bsturmfels) - 16 months ago 2023-02-11 05:00:21
ben@sturm.com.au
reconciler: Add --full-months option to round statement dates to month boundaries
2 files changed with 38 insertions and 0 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reconcile/statement_reconciler.py
Show inline comments
...
 
@@ -295,6 +295,20 @@ def format_multirecord(r1s: List[dict], r2s: List[dict], note: str) -> List[list
 
    return match_output
 

	
 

	
 
def _start_of_month(time, offset_months=0):
 
    if offset_months > 0:
 
        return _start_of_month(time.replace(day=28) + datetime.timedelta(days=4), offset_months - 1)
 
    else:
 
        return time.replace(day=1)
 

	
 

	
 
def round_to_month(begin_date, end_date):
 
    """Round a beginning and end date to beginning and end of months respectively."""
 
    return (
 
        _start_of_month(begin_date),
 
        _start_of_month(end_date, offset_months=1) - datetime.timedelta(days=1))
 

	
 

	
 
def sort_records(records: List) -> List:
 
    return sorted(records, key=lambda x: (x['date'], x['amount']))
 

	
...
 
@@ -596,6 +610,7 @@ def parse_arguments(argv: List[str]) -> argparse.Namespace:
 
    # parser.add_argument('--report-group-regex')
 
    parser.add_argument('--show-reconciled-matches', action='store_true')
 
    parser.add_argument('--non-interactive', action='store_true', help="Don't prompt to write to the books")    # 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('--full-months', action='store_true', help='Match payments over the full month, rather that just between the beginning and end dates of the CSV statement')
 
    args = parser.parse_args(args=argv)
 
    return args
 

	
...
 
@@ -676,6 +691,9 @@ def main(arglist: Optional[Sequence[str]] = None,
 
    begin_date = statement_trans[0]['date']
 
    end_date = statement_trans[-1]['date']
 

	
 
    if args.full_months:
 
        begin_date, end_date = round_to_month(begin_date, end_date)
 

	
 
    # Query for the Beancount books data for this above period.
 
    #
 
    # There are pros and cons for using Beancount's in-memory entries
tests/test_reconcile.py
Show inline comments
...
 
@@ -5,6 +5,8 @@ import os
 
import tempfile
 
import textwrap
 

	
 
import pytest
 

	
 
from conservancy_beancount.reconcile.statement_reconciler import (
 
    date_proximity,
 
    format_output,
...
 
@@ -15,6 +17,7 @@ from conservancy_beancount.reconcile.statement_reconciler import (
 
    read_fr_csv,
 
    remove_duplicate_words,
 
    remove_payee_junk,
 
    round_to_month,
 
    subset_match,
 
    totals,
 
    write_metadata_to_books,
...
 
@@ -388,3 +391,20 @@ def test_format_output():
 
    matches, _, _ = match_statement_and_books(statement, books)
 
    output = format_output(matches, datetime.date(2022, 1, 1), datetime.date(2022, 2, 1), 'test.csv', True)
 
    assert '2022-01-01:       10.00 Patreon         / Patreon   / 12345  →  2022-01-01:       10.00 Patreon                              ✓ Matched' in output
 

	
 

	
 
month_test_data = [
 
    ((datetime.date(2022, 1, 2), datetime.date(2022, 1, 30)),
 
     (datetime.date(2022, 1, 1), datetime.date(2022, 1, 31))),
 
    ((datetime.date(2022, 4, 2), datetime.date(2022, 4, 29)),
 
     (datetime.date(2022, 4, 1), datetime.date(2022, 4, 30))),
 
    ((datetime.date(2022, 2, 2), datetime.date(2022, 2, 27)),
 
     (datetime.date(2022, 2, 1), datetime.date(2022, 2, 28))),
 
    ((datetime.date(2024, 2, 2), datetime.date(2024, 2, 27)),
 
     (datetime.date(2024, 2, 1), datetime.date(2024, 2, 29))),
 
]
 

	
 

	
 
@pytest.mark.parametrize('input_dates,rounded_dates', month_test_data)
 
def test_rounds_to_full_month(input_dates, rounded_dates):
 
    assert round_to_month(*input_dates) == rounded_dates
0 comments (0 inline, 0 general)