diff --git a/tests/test_reconcile.py b/tests/test_reconcile.py new file mode 100644 index 0000000000000000000000000000000000000000..5f75d3aae4c6a48df2a62ddfdb56b4e5ecef5eb8 --- /dev/null +++ b/tests/test_reconcile.py @@ -0,0 +1,175 @@ +import datetime +import decimal + +from conservancy_beancount.reconcile.prototype_amex_reconciler import match_statement_and_books2 as match_statement_and_books, remove_payee_junk, date_proximity + +S1 = { + 'date': datetime.date(2022, 1, 1), + 'amount': decimal.Decimal('10.00'), + 'payee': 'Patreon / Patreon / 123456/ ST-A1B2C3D4G5H6 /', + 'line': 222, +} +S2 = { + 'date': datetime.date(2022, 1, 2), + 'amount': decimal.Decimal('20.00'), + 'payee': 'BT*LINODE PHILADELPHIA P', + 'line': 333, +} +S3 = { + 'date': datetime.date(2022, 1, 3), + 'amount': decimal.Decimal('30.00'), + 'payee': 'USPS PO 4067540039 0PORTLAND OR', + 'line': 444, +} + +B1 = { + 'date': datetime.date(2022, 1, 1), + 'amount': decimal.Decimal('10.00'), + 'payee': 'Patreon', + 'entity': '', + 'check_id': None, + 'filename': '2022/imports.beancount', + 'line': 777, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B2 = { + 'date': datetime.date(2022, 1, 2), + 'amount': decimal.Decimal('20.00'), + 'payee': 'Linode', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 888, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B3_next_day = { + 'date': datetime.date(2022, 1, 4), + 'amount': decimal.Decimal('30.00'), + 'payee': 'USPS', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 999, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B3_next_week = { + 'date': datetime.date(2022, 1, 10), + 'amount': decimal.Decimal('30.00'), + 'payee': 'USPS', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 999, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B3_mismatch_amount = { + 'date': datetime.date(2022, 1, 3), + 'amount': decimal.Decimal('31.00'), + 'payee': 'USPS', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 999, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B3_payee_mismatch_1 = { + 'date': datetime.date(2022, 1, 3), + 'amount': decimal.Decimal('30.00'), + 'payee': 'Credit X', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 999, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} +B3_payee_mismatch_2 = { + 'date': datetime.date(2022, 1, 3), + 'amount': decimal.Decimal('30.00'), + 'payee': 'Credit Y', + 'entity': '', + 'check_id': None, + 'filename': '2022/main.beancount', + 'line': 999, + 'bank_statement': "Financial/Bank-Statements/AMEX/2022-01-12_AMEX_statement.pdf" +} + + +def test_one_exact_match(): + statement = [S1] + books = [B1] + assert match_statement_and_books(statement, books) == [ + ([S1], [B1], []), + ] + +def test_multiple_exact_matches(): + statement = [S1, S2] + books = [B1, 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']), + ] + +def test_multiple_mismatches(): + statement = [S1] + books = [B2] + assert match_statement_and_books(statement, books) == [ + ([S1], [], ['no match']), + ([], [B2], ['no match']), + ] + +def test_next_day_matches(): + statement = [S3] + books = [B3_next_day] + 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']), + ] + +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']), + ] + +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']), + ] + +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']), + ] + +# def test_subset_sum_with_same_date_and_payee(): + +def test_remove_payee_junk(): + assert remove_payee_junk('WIDGETSRUS INC PAYMENT 1') == 'WIDGETSRUS' + assert remove_payee_junk('0000010017') == '10017' + +def test_date_proximity(): + assert date_proximity(datetime.date(2021, 8, 23), datetime.date(2021, 8, 23)) == 1.0 + assert date_proximity(datetime.date(2021, 8, 23), datetime.date(2021, 8, 23) - datetime.timedelta(days=30)) == 0.5 + assert date_proximity(datetime.date(2021, 8, 23), datetime.date(2021, 8, 23) - datetime.timedelta(days=60)) == 0.0