diff --git a/tests/test_reconcile.py b/tests/test_reconcile.py index 7703921ad83e70aabc8c161644521c3b1e11fd71..fc00ef5c76a39769241dd22716379f7bf66093dd 100644 --- a/tests/test_reconcile.py +++ b/tests/test_reconcile.py @@ -13,6 +13,7 @@ from conservancy_beancount.reconcile.statement_reconciler import ( metadata_for_match, write_metadata_to_books, totals, + subset_match, ) # These data structures represent individual transactions as taken from the @@ -40,6 +41,13 @@ S3 = { 'check_id': '', 'line': 444, } +S4 = { + 'date': datetime.date(2022, 8, 11), + 'amount': decimal.Decimal('-2260.00'), + 'payee': 'Trust 0000000362 210', + 'check_id': '', + 'line': 555, +} # Books transaction examples. B1 = { @@ -114,81 +122,116 @@ B3_unmatched_check_id = { 'line': 999, 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" } +B4A = { + 'date': datetime.date(2022, 8, 11), + 'amount': decimal.Decimal('-250.00'), + 'payee': 'TRUST 0000000362 ACH Retirement Plan', + 'check_id': '', + 'line': 1000, +} +B4B = { + 'date': datetime.date(2022, 8, 11), + 'amount': decimal.Decimal('-250.00'), + 'payee': 'TRUST 0000000362 ACH Retirement Plan', + 'check_id': '', + 'line': 1000, +} +B4C = { + 'date': datetime.date(2022, 8, 11), + 'amount': decimal.Decimal('-1760.00'), + 'payee': 'TRUST 0000000362 ACH Retirement Plan', + 'check_id': '', + 'line': 1000, +} + def test_one_exact_match(): statement = [S1] books = [B1] - assert match_statement_and_books(statement, books) == [ + assert match_statement_and_books(statement, books) == ( # Match, match, notes. # # The matches are a list so we can implement subset-sum matching where # multiple books transactions may match to a single statement # transaction. - ([S1], [B1], []), - ] + [([S1], [B1], [])], + [], + [], + ) def test_multiple_exact_matches(): statement = [S1, S2] books = [B1, B2] - assert match_statement_and_books(statement, books) == [ - ([S1], [B1], []), - ([S2], [B2], []), - ] + assert match_statement_and_books(statement, books) == ( + [([S1], [B1], []), ([S2], [B2], [])], + [], + [], + ) def test_one_mismatch(): statement = [S1] books = [] - assert match_statement_and_books(statement, books) == [ - ([S1], [], ['no match']), - ] + assert match_statement_and_books(statement, books) == ( + [], + [S1], + [], + ) def test_multiple_mismatches(): statement = [S1] books = [B2] - assert match_statement_and_books(statement, books) == [ - ([S1], [], ['no match']), - ([], [B2], ['no match']), - ] + assert match_statement_and_books(statement, books) == ( + [], + [S1], + [B2], + ) def test_next_day_matches(): statement = [S3] books = [B3_next_day] - assert match_statement_and_books(statement, books) == [ - ([S3], [B3_next_day], ['+/- 1 days']), - ] + assert match_statement_and_books(statement, books) == ( + [([S3], [B3_next_day], ['+/- 1 days'])], + [], + [], + ) def test_next_week_matches(): statement = [S3] books = [B3_next_week] - assert match_statement_and_books(statement, books) == [ - ([S3], [B3_next_week], ['+/- 7 days']), - ] + assert match_statement_and_books(statement, books) == ( + [([S3], [B3_next_week], ['+/- 7 days'])], + [], + [], + ) def test_incorrect_amount_does_not_match(): statement = [S3] books = [B3_mismatch_amount] - assert match_statement_and_books(statement, books) == [ - ([S3], [], ['no match']), - ([], [B3_mismatch_amount], ['no match']), - ] + assert match_statement_and_books(statement, books) == ( + [], + [S3], + [B3_mismatch_amount], + ) def test_payee_mismatch_ok_when_only_one_that_amount_and_date(): statement = [S3] books = [B3_payee_mismatch_1] - assert match_statement_and_books(statement, books) == [ - ([S3], [B3_payee_mismatch_1], ['payee mismatch', 'only one decent match']), - ] + assert match_statement_and_books(statement, books) == ( + [([S3], [B3_payee_mismatch_1], ['payee mismatch', 'only one decent match'])], + [], + [], + ) def test_payee_mismatch_not_ok_when_multiple_that_amount_and_date(): statement = [S3] books = [B3_payee_mismatch_1, B3_payee_mismatch_2] match = match_statement_and_books(statement, books) - assert match == [ - ([S3], [], ['no match']), - ([], [B3_payee_mismatch_1], ['no match']), - ([], [B3_payee_mismatch_2], ['no match']), - ] + assert match == ( + [], + [S3], + [B3_payee_mismatch_1, B3_payee_mismatch_2], + ) def test_remove_payee_junk(): assert remove_payee_junk('WIDGETSRUS INC PAYMENT 1') == 'WIDGETSRUS' @@ -251,7 +294,17 @@ def test_payee_not_considered_if_check_id_present(): # These records match aside from check-id. statement = [S3] books = [B3_unmatched_check_id] - assert match_statement_and_books(statement, books) == [ - ([S3], [], ['no match']), - ([], [B3_unmatched_check_id], ['no match']), - ] + assert match_statement_and_books(statement, books) == ( + [], + [S3], + [B3_unmatched_check_id], + ) + +def test_subset_sum_match(): + statement = [S4] + books = [B4A, B4B, B4C] + assert subset_match(statement, books) == ( + [([S4], [B4A, B4B, B4C], [])], + [], # No remaining statement trans. + [], # No remaining books trans. + )