reconciler: Handle comma separators in some FR statements
This commit is contained in:
		
							parent
							
								
									82ec68962a
								
							
						
					
					
						commit
						0968f7f051
					
				
					 2 changed files with 54 additions and 8 deletions
				
			
		|  | @ -194,6 +194,11 @@ def read_transactions_from_csv(f: TextIO, standardize_statement_record: Callable | |||
|     return sort_records([standardize_statement_record(row, i) for i, row in enumerate(reader, 2)]) | ||||
| 
 | ||||
| 
 | ||||
| def parse_amount(amount: str) -> decimal.Decimal: | ||||
|     """Parse amounts and handle comma separators as seen in some FR statements.""" | ||||
|     return decimal.Decimal(amount.replace(',', '')) | ||||
| 
 | ||||
| 
 | ||||
| def validate_amex_csv(sample: str, account: str) -> None: | ||||
|     required_cols = {'Date', 'Amount', 'Description', 'Card Member'} | ||||
|     reader = csv.DictReader(io.StringIO(sample)) | ||||
|  | @ -206,7 +211,7 @@ def standardize_amex_record(row: Dict, line: int) -> Dict: | |||
|     # NOTE: Statement doesn't seem to give us a running balance or a final total. | ||||
|     return { | ||||
|         'date': datetime.datetime.strptime(row['Date'], '%m/%d/%Y').date(), | ||||
|         'amount': -1 * decimal.Decimal(row['Amount']), | ||||
|         'amount': -1 * parse_amount(row['Amount']), | ||||
|         # Descriptions have too much noise, so taking just the start | ||||
|         # significantly assists the fuzzy matching. | ||||
|         'payee': remove_payee_junk(row['Description'] or '')[:20], | ||||
|  | @ -225,7 +230,7 @@ def validate_fr_csv(sample: str, account: str) -> None: | |||
| def standardize_fr_record(row: Dict, line: int) -> Dict: | ||||
|     return { | ||||
|         'date': datetime.datetime.strptime(row['Date'], '%m/%d/%Y').date(), | ||||
|         'amount': decimal.Decimal(row['Amount']), | ||||
|         'amount': parse_amount(row['Amount']), | ||||
|         'payee': remove_payee_junk(row['Detail'] or '')[:20], | ||||
|         'check_id': row['Serial Num'].lstrip('0'), | ||||
|         'line': line, | ||||
|  |  | |||
|  | @ -5,15 +5,17 @@ import tempfile | |||
| import textwrap | ||||
| 
 | ||||
| from conservancy_beancount.reconcile.statement_reconciler import ( | ||||
|     match_statement_and_books, | ||||
|     remove_payee_junk, | ||||
|     date_proximity, | ||||
|     remove_duplicate_words, | ||||
|     payee_match, | ||||
|     match_statement_and_books, | ||||
|     metadata_for_match, | ||||
|     write_metadata_to_books, | ||||
|     totals, | ||||
|     payee_match, | ||||
|     remove_duplicate_words, | ||||
|     remove_payee_junk, | ||||
|     standardize_amex_record, | ||||
|     standardize_fr_record, | ||||
|     subset_match, | ||||
|     totals, | ||||
|     write_metadata_to_books, | ||||
| ) | ||||
| 
 | ||||
| # These data structures represent individual transactions as taken from the | ||||
|  | @ -341,3 +343,42 @@ def test_subset_passes_through_all_non_matches(): | |||
|         [S1],  # No match: preserved intact | ||||
|         [B2, B3_next_day, B3_next_week]  # No match: preserved intact | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def test_handles_fr_record_with_comma_separators(): | ||||
|     # CSV would look something like: | ||||
|     # | ||||
|     # "Date","ABA Num","Currency","Account Num","Account Name","Description","BAI Code","Amount","Serial Num","Ref Num","Detail" | ||||
|     # "02/07/2022",,,,,,,"10,000.00",,,"XXXX" | ||||
|     input_row = { | ||||
|         'Date': '02/07/2022', | ||||
|         'Amount': '10,000.00', | ||||
|         'Detail': 'XXXX', | ||||
|         'Serial Num': '', | ||||
|     } | ||||
|     expected = { | ||||
|         'date': datetime.date(2022, 2, 7), | ||||
|         'amount': decimal.Decimal('10000'), | ||||
|         'payee': 'XXXX', | ||||
|         'check_id': '', | ||||
|         'line': 1, | ||||
|     } | ||||
|     assert standardize_fr_record(input_row, line=1) == expected | ||||
| 
 | ||||
| 
 | ||||
| def test_handles_amex_record_with_comma_separators(): | ||||
|     # This insn't typically a problem with AMEX, but adding for completeness. | ||||
|     input_row = { | ||||
|         'Date': '02/07/2022', | ||||
|         'Amount': '-10,000.00',  # Amounts are from Bank's perspective/negated. | ||||
|         'Description': 'XXXX', | ||||
|         'Serial Num': '', | ||||
|     } | ||||
|     expected = { | ||||
|         'date': datetime.date(2022, 2, 7), | ||||
|         'amount': decimal.Decimal('10000'), | ||||
|         'payee': 'XXXX', | ||||
|         'check_id': '', | ||||
|         'line': 1, | ||||
|     } | ||||
|     assert standardize_amex_record(input_row, line=1) == expected | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue