Skip to content

Support alphanumeric CNPJ identifiers in CSV ingestion - #44

Merged
vkruoso merged 1 commit into
masterfrom
copilot/add-compatibilidade-cnpj-alfanumerico
Sep 3, 2026
Merged

Support alphanumeric CNPJ identifiers in CSV ingestion#44
vkruoso merged 1 commit into
masterfrom
copilot/add-compatibilidade-cnpj-alfanumerico

Conversation

Copilot AI commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The Receita API now accepts alphanumeric CNPJ values, but the project was still stripping non-digit characters and rejecting anything that was not a 14-digit numeric identifier. This prevented valid newer CNPJ values from being fetched through the CLI pipeline.

  • What changed

    • Normalized CSV values by removing formatting punctuation while preserving alphanumeric characters.
    • Kept the legacy checksum validation for numeric CNPJ values already in use.
    • Allowed 14-character alphanumeric CNPJ identifiers to pass validation as valid API inputs.
    • Added a regression test covering both legacy numeric and alphanumeric formats.
  • Example

getter = Get("sample.csv", "/tmp", None)

assert getter.format("03.420.926/0049-79") == "03420926004979"
assert getter.valid("ABCD1234567890") is True

Copilot AI linked an issue Sep 3, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add support for alphanumeric CNPJ compatibility Support alphanumeric CNPJ identifiers in CSV ingestion Sep 3, 2026
Copilot AI requested a review from vkruoso September 3, 2026 20:07
@vkruoso
vkruoso marked this pull request as ready for review September 3, 2026 20:21
Copilot AI lite review requested due to automatic review settings September 3, 2026 20:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Get.valid() still contains a large unreachable legacy checksum block after an early return, which must be removed to avoid future maintenance mistakes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the CSV ingestion pipeline to support the new alphanumeric CNPJ format accepted by ReceitaWS, ensuring formatted CSV values can be normalized and validated before being queried.

Changes:

  • Updated Get.format() to strip punctuation while preserving alphanumeric characters and normalizing to uppercase.
  • Extended Get.valid() to accept alphanumeric CNPJs (12 alphanumeric + 2 numeric check digits) and validate check digits using a shared _check_digit() routine.
  • Added regression tests and updated README docs (PT/EN) to describe the supported formats and validation behavior.
File summaries
File Description
receita/tools/get.py Implements alphanumeric-aware normalization and validation, introducing _check_digit() and updated format()/valid() logic.
tests/test_get.py Adds regression coverage for formatting, validation, and CSV reading with both numeric and alphanumeric CNPJs.
README.rst Documents acceptance of numeric/alphanumeric CNPJ formats and pre-checking of check digits (PT).
README.en.rst Documents acceptance of numeric/alphanumeric CNPJ formats and pre-checking of check digits (EN).
Review details

Suppressed comments (1)

receita/tools/get.py:141

  • There is unreachable legacy checksum code left after the early return, so lines 139-167 can never execute. This makes the function confusing to maintain and risks future edits happening in the dead path instead of the active one; please delete the leftover block so valid() has a single source of truth.
        if self._check_digit(base) != int(digits[0]):
            return False
        return self._check_digit(base + digits[0]) == int(digits[1])

        tam = 12
        nums = cnpj[:tam]
        digs = cnpj[tam:]
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread receita/tools/get.py
Copilot AI review requested due to automatic review settings September 3, 2026 20:23
@vkruoso

vkruoso commented Sep 3, 2026

Copy link
Copy Markdown
Member

Took this over and pushed a fix on top, as the checksum was being skipped for the new format.

What was happening

valid() returned True for any fourteen character alphanumeric value:

if not cnpj.isdigit():
    return True

read() uses valid() to decide what gets requested, so the check digits are what keeps an invalid identifier from consuming a request. Skipping them meant:

  • values with wrong check digits were accepted (ABCD1234567899);
  • values that cannot be a CNPJ were accepted, since the last two characters must be numeric check digits (ABCDEFGHIJKLMN);
  • ABCD1234567890, used as the example here and asserted in the test, is itself invalid — the correct check digits for ABCD12345678 are 80;
  • because format() now keeps letters, a fourteen character text column became valid, so Empresa ABC Ltda normalised to EMPRESAABCLTDA and would be queried.

What changed

The premise that the alphanumeric format "cannot be validated with the numeric checksum algorithm" is not the case. The numeric format is a subset of the alphanumeric one: the same modulus 11 rules apply when each character is converted with ord(char) - 48, which keeps the value of the digits. So the two formats now share one code path rather than branching:

if not re.fullmatch(r"[0-9A-Z]{12}[0-9]{2}", cnpj):
    return False

The shape check also enforces numeric check digits, which the previous {14} match did not. Input is upper cased before the checksum, since ord("a") and ord("A") differ.

Verification

  • Behaviour for numeric CNPJs is unchanged: compared against the previous algorithm over 200000 random fourteen digit strings and 20000 constructed valid CNPJs, with no disagreement.
  • The published sample for the alphanumeric format, 00000000E08G12, validates, and the computed check digits are 12 as expected.
  • End to end, a CSV holding one alphanumeric CNPJ, one numeric CNPJ, a company name and a value with wrong check digits results in exactly two requests, for the two valid entries.

Tests were rewritten, as the previous ones asserted the invalid example as valid. They now cover both formats, wrong check digits, non numeric check digits, length and punctuation, the published sample, and that read() filters before anything is requested. The accepted formats are documented in both READMEs and the feature is listed in the changelog.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Get.valid() still contains unreachable legacy validation code after the new early return, and it should be removed before merging to avoid maintainability issues.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

tests/test_get.py:9

  • The _cnpj() test helper currently calls getter._check_digit(), which is part of the implementation under test; this makes the alphanumeric test cases self-fulfilling (they can pass even if _check_digit() is wrong). Prefer computing the digits independently in the test helper (or using fixed known-good samples) so the tests can catch checksum regressions.
    receita/tools/get.py:140
  • After the new checksum checks, valid() returns on line 137, but the old numeric-only validation logic (starting at line 139) is still present below and is now unreachable dead code. Please delete the legacy block to keep the function maintainable and avoid future divergence between two checksum implementations.
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

The CSV input stripped every character that was not a digit and required
fourteen digits, so identifiers in the new alphanumeric format were
discarded before reaching the webservice.

Keep letters when normalizing the input and validate both formats with
the same rules: twelve alphanumeric characters followed by two numeric
check digits. The numeric format is a subset of the alphanumeric one, so
the existing modulus 11 checksum applies to both once each character is
converted through the ASCII table, which preserves the value of the
digits. Verifying the check digits still gates the request, so an invalid
identifier does not consume a query.

Behaviour for numeric CNPJs is unchanged, and the published sample for
the alphanumeric format is covered by the tests.

Closes #38

Co-Authored-By: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vkruoso
vkruoso force-pushed the copilot/add-compatibilidade-cnpj-alfanumerico branch from 7ccf2e7 to 36b8b09 Compare September 3, 2026 20:29
@vkruoso
vkruoso merged commit 71cef44 into master Sep 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compatibilidade com CNPJ alfanumérico

3 participants