Browse Source

fix: parse partial Ourocard payment records

master
Codex 2 months ago
parent
commit
9352240b3f
  1. 4
      ingestion/parsers.py
  2. 15
      tests/test_import_parsers.py

4
ingestion/parsers.py

@ -90,7 +90,7 @@ def _txt_items(text, profile, skipped):
regular = [re.compile(rf'^({date_re})(.{{{body_size}}})([A-Z ]{{2}})\s+(\S+)\s+(\S+)\s*$')] regular = [re.compile(rf'^({date_re})(.{{{body_size}}})([A-Z ]{{2}})\s+(\S+)\s+(\S+)\s*$')]
if profile == 'OUROCARD_FULL_V1': if profile == 'OUROCARD_FULL_V1':
regular.append(re.compile(rf'^({date_re})(.{{33}})([A-Za-z ]{{7}})\s+(\S+)\s+(\S+)\s*$')) regular.append(re.compile(rf'^({date_re})(.{{33}})([A-Za-z ]{{7}})\s+(\S+)\s+(\S+)\s*$'))
payment = re.compile(rf'^({date_re})\s*(PGTO DEBITO CONTA.*?)200211\s+(\S+)\s+(\S+)\s*$') payment = re.compile(rf'^({date_re})\s*(PGTO DEBITO CONTA.*?)\s+(\S+)\s+(\S+)\s*$')
section, section_count = '1', 0 section, section_count = '1', 0
for number, line in enumerate(text.splitlines(), 1): for number, line in enumerate(text.splitlines(), 1):
locator = f'line:{number}' locator = f'line:{number}'
@ -117,6 +117,8 @@ def _txt_items(text, profile, skipped):
skipped['balance'] += 1 skipped['balance'] += 1
else: else:
yield ParsedItem(locator, section, match[1], match[2].rstrip(), match[4], match[5]) yield ParsedItem(locator, section, match[1], match[2].rstrip(), match[4], match[5])
elif partial and not re.match(r'^\s*(?:\d{1,2}[./]|DATA\s+DE\s+EMISS)', line, re.I):
skipped['metadata'] += 1
else: else:
code = 'INVALID_ROW' if re.match(r'^\s*\d{1,2}[./]', line) else 'UNRECOGNIZED_LINE' code = 'INVALID_ROW' if re.match(r'^\s*\d{1,2}[./]', line) else 'UNRECOGNIZED_LINE'
yield ParsedItem(locator, section, description=line, errors=(code,)) yield ParsedItem(locator, section, description=line, errors=(code,))

15
tests/test_import_parsers.py

@ -64,6 +64,20 @@ def test_full_card_sections_installments_credit_and_payment():
assert rows[3].description == 'unknown text' assert rows[3].description == 'unknown text'
def test_partial_card_skips_metadata_and_parses_payment_with_variable_identifiers():
parsed = parse_document((
'SYNTHETIC INVOICE HEADER\n'
'15/01 PGTO DEBITO CONTA 0001 123456789 123 123 -100,00 0,00\n'
).encode(), 'OUROCARD_PARTIAL_V1', {'start_date': '2025-02-01', 'end_date': '2026-01-31'})
rows = normalize(parsed)
assert parsed.skipped['metadata'] == 1
assert len(rows) == 1
assert rows[0].errors == ()
assert rows[0].kind == 'TRANSFER'
assert rows[0].amount == '100.00'
@pytest.mark.parametrize('country', ['ESTADOS', 'Brasil']) @pytest.mark.parametrize('country', ['ESTADOS', 'Brasil'])
def test_full_card_accepts_extended_country_layout(country): def test_full_card_accepts_extended_country_layout(country):
row = card(alternate_full_card_line(country=country))[0] row = card(alternate_full_card_line(country=country))[0]
@ -75,6 +89,7 @@ def test_full_card_accepts_extended_country_layout(country):
def test_card_ambiguity_bad_shapes_and_installments_block(): def test_card_ambiguity_bad_shapes_and_installments_block():
assert 'AMBIGUOUS_AMOUNT' in card(card_line(debit='2,00', credit='1,00'))[0].errors assert 'AMBIGUOUS_AMOUNT' in card(card_line(debit='2,00', credit='1,00'))[0].errors
assert 'INVALID_ROW' in card('15.02.2026 MALFORMED')[0].errors assert 'INVALID_ROW' in card('15.02.2026 MALFORMED')[0].errors
assert 'INVALID_ROW' in card('15/01 MALFORMED', True)[0].errors
assert 'INVALID_INSTALLMENT' in card(card_line(body='LOJA PARC 3/2'))[0].errors assert 'INVALID_INSTALLMENT' in card(card_line(body='LOJA PARC 3/2'))[0].errors
assert 'INVALID_AMOUNT' in card(card_line(debit='1.23,00'))[0].errors assert 'INVALID_AMOUNT' in card(card_line(debit='1.23,00'))[0].errors

Loading…
Cancel
Save