Skip to content

feat(suframa): add isValidSuframa, formatSuframa, parseSuframa and generateSuframa - #559

Open
hyanmandian wants to merge 2 commits into
claude/get-cpf-infofrom
claude/suframa
Open

hyanmandian wants to merge 2 commits into
claude/get-cpf-infofrom
claude/suframa

Conversation

@hyanmandian

@hyanmandian hyanmandian commented Sep 19, 2026

Copy link
Copy Markdown
Member

Stacked on #558. This PR sits on top of #558 (getCpfInfo) and merges after it, which in turn sits on #588. Its base branch is claude/get-cpf-info, so the diff shown here is the SUFRAMA change alone. Part of stack #591.

What

Adds the Inscrição SUFRAMA family: the registration number the Superintendência da Zona Franca de Manaus gives to companies with tax incentives, carried by the ISUF field (E18) of the NF-e recipient. A wrong one is rejected by the SEFAZ with rejection 235.

New utilities, modelled on the PIS family: isValidSuframa, formatSuframa, parseSuframa, generateSuframa. No new internal: the check digit is the existing mod11 with the "arrecadacao" rule (weights 2 to 9 from the right, 0 for a remainder of 0 or 1). One shared constant, src/_internals/constants/suframa.ts (SUFRAMA_LENGTH).

API

isValidSuframa(suframa: string): boolean
formatSuframa(value: string | number, options?: FormatSuframaOptions): string // { pad?: boolean }
parseSuframa(value: string | number): string
generateSuframa(): string
isValidSuframa("123456789"); // true (the example of the manual)
isValidSuframa("12.3456.789"); // true
isValidSuframa("10001018"); // true, 8 digits, same as "010001018"
isValidSuframa("123456780"); // false, wrong check digit
isValidSuframa("001234560"); // false, sector 00

formatSuframa("123456789"); // "12.3456.789"
formatSuframa("10001018"); // "10.0010.18" (8 digits, the mask groups one position early)
formatSuframa("10001018", { pad: true }); // "01.0001.018"
parseSuframa("12.3456.789"); // "123456789"
generateSuframa(); // "205678106"

Rules implemented, all from the official source below:

  • Layout SS.NNNN.LLD: sector of activity, sequential number, locality of the SUFRAMA unit, check digit. Note that this differs from the order in the task description (sequence first): the manual puts the sector first.
  • Numeric field with 8 or 9 positions. SS may start with 0 (so the number loses it and has 8 digits) but can never be 00. An 8 digit value is validated with the zero back in place, so an 8 digit value starting with 0 is rejected as sector 00.
  • Check digit: módulo 11, weights 2 to 9 from right to left, 11 - remainder, 0 when the remainder is 0 or 1.
  • generateSuframa always returns the 9 digit form, never with sector 00.

Sources

  • Official: NF-e Manual de Orientação do Contribuinte 7.0, Visão Geral (CONFAZ, the current national manual), read from the PDF: section 8.4 "Identificador: Inscrição SUFRAMA" (pages 132-133) gives the composition SS.NNNN.LLD, "Campo: Numérico, com 8 ou 9 posições / Considerar que "SS" pode começar por "0", mas não pode ser "00"", "D: Dígito Verificador, Módulo 11, Pesos de 2 a 9 / considerar DV=0 se o resto da divisão for "0" ou "1"", and the worked example 12345678 with sum 156, remainder 2, DV 9. That example is a test vector (123456789).
  • Official: MOC 7.0, Anexo I - Leiaute e Regras de Validação, read from the PDF: field 79 / E18 ISUF is numeric, occurrence 0-1, size 8-9; validation rule E18-20 "Inscrição com dígito verificador inválido", rejection 235. The SEFAZ itself checks only the check digit.
  • The same content is in Anexo XII.01 of the superseded MOC 6.0, which the first revision of this PR cited through a copy hosted by SEFAZ-SP. The citation now points at the current manual published by CONFAZ, the one this repository already cites in is-valid-nfe-key and get-nfe-key-info.
  • No third-party implementation was used. The other test vectors were computed by hand from the algorithm of the manual.

Verification

  • npm run check: pass
  • npm run test -- --run / npm run test:coverage: 188 files, 6224 tests pass, 100% statements, branches, functions and lines
  • npm run test:bun: 6224 pass, 0 fail. npm run test:deno: 6224 passed, 0 failed
  • npm run build: pass (attw and publint clean)
  • npm run check:api:update: report updated and committed
  • npm run check:unused: pass
  • npm run check:duplication: 0 clones
  • npm run check:commits: pass
  • npm run test:mutation on the four new source files: 100% (51 killed, 2 timeouts, 0 survived)
  • npm run build:docs and npm run build:jsr: run, output committed (jsr.json gains ./is-valid-suframa, ./format-suframa, ./parse-suframa and ./generate-suframa)
  • Not run: browser tests, the full Stryker run, and check:tree-shaking (its CI job was broken on every branch rebased past feat: Standard Schema wrapper, JSR, pkg.pr.new, docs previews and a playground #556; fix(build): the document field templates stop breaking the package build #588, now at the bottom of this stack, fixes it, and the job is green here)

Re-verified after the rebase onto claude/get-cpf-info: check, test:coverage (192 files, 6309 tests, 100% statements, branches, functions and lines), build, check:unused, check:duplication and check:commits all pass on the rebased branch.

Rebase onto #558

Rebased onto claude/get-cpf-info after #556 landed on main. Conflicts resolved:

  • docs/llms.txt and docs/llms-full.txt are no longer tracked (they are generated now), so both were git rm-ed.
  • The SUFRAMA section of docs/utilities.md and docs/pt-br/utilities.md was ported into the new per-utility format: a ### heading per function, a short paragraph, a bullet list for the options and the edge cases, a javascript block, and one shared Source: line at the end of the family. The family intro paragraph was folded into isValidSuframa, and the MOC 7.0 and Anexo I citations moved to that Source: line.
  • src/index.ts and src/index.test.ts kept strictly alphabetical.
  • jsr.json (new on main) regenerated with npm run build:jsr.

Open points

  • Sector (SS) and locality (LL) codes are not validated against a table. The manual lists them only "conforme exemplos abaixo" (01 and 02 Cooperativa, 10 and 11 Comércio, 20 Indústria com Projeto Pleno, 60 Serviços; 01 Manaus, 10 Boa Vista, 30 Porto Velho), and I found no official exhaustive list from SUFRAMA. For the same reason there is no getSuframaInfo and generateSuframa uses random sector and locality codes.
  • The SINTEGRA page that used to describe the number (sintegra.gov.br/insc_suframa.html) returns 404. MOC 7.0 was downloaded from CONFAZ and read: section 8.4 and the Anexo I entries for ISUF say exactly what MOC 6.0 said, so the rules did not change with the new manual.
  • Rule E18-30 (the recipient must be in AC, AM, RO, RR, or Macapá/Santana in AP) depends on the recipient address, not on the number, so it is out of scope.
  • formatSuframa masks progressively like formatPis, so an 8 digit value needs { pad: true } to get the leading zero back; without it the separators fall one position early. Both docs and the @example block now show that unpadded output (formatSuframa("10001018"); // "10.0010.18") next to the padded one, since 8 positions is a valid ISUF length.

Summary by CodeRabbit

  • New Features

    • Added SUFRAMA registration utilities for validation, formatting, parsing, and random value generation.
    • Validation supports formatted or unformatted 8- and 9-digit registrations, including check-digit and sector rules.
    • Formatting supports the 00.0000.000 mask and optional zero-padding.
    • Parsing removes non-digit characters and limits results to nine digits.
  • Documentation

    • Added SUFRAMA utility documentation in English and Brazilian Portuguese.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 2bae99a1-5b67-4df0-ad49-43036bc1d46b

📥 Commits

Reviewing files that changed from the base of the PR and between c46a785 and 91ede6a.

📒 Files selected for processing (6)
  • docs/pt-br/utilities.md
  • docs/utilities.md
  • jsr.json
  • reports/api/brazilian-utils.api.md
  • src/index.test.ts
  • src/index.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Adds SUFRAMA parsing, validation, formatting, and generation utilities. The APIs are exported from the package, mapped in JSR, tested, documented in English and Portuguese, and included in the public API contract.

Changes

SUFRAMA utilities

Layer / File(s) Summary
Parsing and validation rules
src/_internals/constants/suframa.ts, src/parse-suframa/*, src/is-valid-suframa/*
Adds the 9-digit length constant, digit parsing, accepted 8- or 9-digit validation, sector checks, módulo 11 validation, and related tests.
Formatting and generation
src/format-suframa/*, src/generate-suframa/*
Adds masked formatting with optional padding, random valid registration generation, and related tests.
Public exports and references
src/index.ts, src/index.test.ts, jsr.json, reports/api/brazilian-utils.api.md, docs/utilities.md, docs/pt-br/utilities.md
Exports the SUFRAMA APIs and type, updates package and API mappings, verifies public contracts, and adds documentation.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 91ede

The SUFRAMA changes have no remaining actionable merge-blocking issue.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the SUFRAMA feature and names all four added public utilities. It is concise and directly matches the main changes.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Tree-shaking report

No size regression. 4 new out of 161 exports.

Base Head Δ
Pre-existing exports, all imported 649.5 KB 649.5 KB (gzip 166.5 KB) +16 B (+0.0%)
Full import 649.5 KB 649.9 KB (gzip 166.6 KB) +453 B (+0.1%)
Exports 157 161 +4

What changed (4)

Export Base Head Δ gzip
🆕 isValidSuframa 1.4 KB new 884 B
🆕 generateSuframa 1.3 KB new 809 B
🆕 formatSuframa 1.3 KB new 779 B
🆕 parseSuframa 1002 B new 620 B
All exports (161)
Export Base Head Δ gzip
GetAddressInfoByCepError 966 B 966 B 0 B 600 B
GetAddressInfoByCepNotFoundError 1.0 KB 1.0 KB 0 B 618 B
GetAddressInfoByCepServiceError 1.0 KB 1.0 KB 0 B 617 B
GetAddressInfoByCepValidationError 1.0 KB 1.0 KB 0 B 620 B
GetCepInfoByAddressError 966 B 966 B 0 B 600 B
GetCepInfoByAddressNotFoundError 1.0 KB 1.0 KB 0 B 618 B
GetCepInfoByAddressValidationError 1.0 KB 1.0 KB 0 B 620 B
addBusinessDays 6.8 KB 6.8 KB 0 B 2.8 KB
capitalize 2.5 KB 2.5 KB 0 B 1.3 KB
convertCurrencyToWords 2.8 KB 2.8 KB 0 B 1.5 KB
convertDateToWords 3.2 KB 3.2 KB 0 B 1.7 KB
convertLicensePlateToMercosul 1.3 KB 1.3 KB 0 B 807 B
convertNumberToWords 2.4 KB 2.4 KB 0 B 1.3 KB
differenceInBusinessDays 6.9 KB 6.9 KB 0 B 2.9 KB
formatBoleto 1.4 KB 1.4 KB 0 B 837 B
formatCEP 1.2 KB 1.2 KB 0 B 778 B
formatCNPJ 1.4 KB 1.4 KB 0 B 855 B
formatCPF 1.3 KB 1.3 KB 0 B 807 B
formatCaepf 1.3 KB 1.3 KB 0 B 787 B
formatCei 1.3 KB 1.3 KB 0 B 785 B
formatCep 1.2 KB 1.2 KB 0 B 778 B
formatCertidao 1.3 KB 1.3 KB 0 B 789 B
formatCnae 1.2 KB 1.2 KB 0 B 782 B
formatCnh 1.3 KB 1.3 KB 0 B 780 B
formatCno 1.3 KB 1.3 KB 0 B 786 B
formatCnpj 1.4 KB 1.4 KB 0 B 855 B
formatCns 1.3 KB 1.3 KB 0 B 780 B
formatCpf 1.3 KB 1.3 KB 0 B 807 B
formatCurrency 1.8 KB 1.8 KB 0 B 1.0 KB
formatIban 1.1 KB 1.1 KB 0 B 696 B
formatLegalNature 1.2 KB 1.2 KB 0 B 777 B
formatLicensePlate 1.2 KB 1.2 KB 0 B 738 B
formatNcm 1.2 KB 1.2 KB 0 B 780 B
formatNfeKey 1.3 KB 1.3 KB 0 B 783 B
formatPassport 1.0 KB 1.0 KB 0 B 643 B
formatPhone 2.8 KB 2.8 KB 0 B 1.5 KB
formatPis 1.3 KB 1.3 KB 0 B 781 B
formatProcessoJuridico 1.3 KB 1.3 KB 0 B 785 B
🆕 formatSuframa 1.3 KB new 779 B
formatVoterId 1.3 KB 1.3 KB 0 B 821 B
generateBoleto 2.1 KB 2.1 KB 0 B 1.2 KB
generateCNPJ 1.6 KB 1.6 KB 0 B 965 B
generateCPF 1.4 KB 1.4 KB 0 B 878 B
generateCep 984 B 984 B 0 B 610 B
generateCnh 1.4 KB 1.4 KB 0 B 829 B
generateCnpj 1.6 KB 1.6 KB 0 B 965 B
generateCpf 1.4 KB 1.4 KB 0 B 878 B
generateLegalNature 5.9 KB 5.9 KB 0 B 2.1 KB
generateLicensePlate 1.1 KB 1.1 KB 0 B 692 B
generatePassport 1.1 KB 1.1 KB 0 B 656 B
generatePhone 1.5 KB 1.5 KB 0 B 900 B
generatePis 1.2 KB 1.2 KB 0 B 744 B
generatePixPayload 6.3 KB 6.3 KB 0 B 2.8 KB
generateProcessoJuridico 1.4 KB 1.4 KB 0 B 870 B
generateRenavam 1.2 KB 1.2 KB 0 B 760 B
🆕 generateSuframa 1.3 KB new 809 B
generateVoterId 1.7 KB 1.7 KB 0 B 1021 B
getAddressInfoByCep 4.1 KB 4.1 KB 0 B 1.9 KB
getAreaCodeInfo 3.9 KB 3.9 KB 0 B 1.4 KB
getAreaCodesByState 1.6 KB 1.6 KB 0 B 917 B
getBankByCode 38.6 KB 38.6 KB 0 B 9.8 KB
getBankByIspb 38.6 KB 38.6 KB 0 B 9.8 KB
getBanks 38.4 KB 38.4 KB 0 B 9.6 KB
getBoletoInfo 3.1 KB 3.1 KB 0 B 1.6 KB
getCbo 119.1 KB 119.1 KB 0 B 30.7 KB
getCepInfoByAddress 2.7 KB 2.7 KB 0 B 1.4 KB
getCertidaoInfo 1.8 KB 1.8 KB 0 B 1.0 KB
getCfop 68.9 KB 68.9 KB 0 B 6.9 KB
getCities 154.3 KB 154.3 KB 0 B 49.9 KB
getCnae 93.9 KB 93.9 KB 0 B 21.2 KB
getCpfInfo 1.7 KB 1.7 KB 0 B 999 B
getFormatLicensePlate 1.1 KB 1.1 KB 0 B 692 B
getHolidays 6.1 KB 6.1 KB 0 B 2.6 KB
getIbanInfo 1.6 KB 1.6 KB 0 B 955 B
getLegalNature 6.3 KB 6.3 KB 0 B 2.3 KB
getLegalNatures 5.9 KB 5.9 KB 0 B 2.1 KB
getLegalNaturesByCategory 6.5 KB 6.5 KB 0 B 2.4 KB
getMunicipalities 156.4 KB 156.4 KB 0 B 50.3 KB
getMunicipality 154.9 KB 154.9 KB 0 B 50.3 KB
getMunicipalityByCode 156.5 KB 156.5 KB 0 B 50.4 KB
getNfeKeyInfo 2.7 KB 2.7 KB 0 B 1.5 KB
getPixKeyInfo 4.5 KB 4.5 KB 0 B 2.0 KB
getPixPayloadInfo 2.9 KB 2.9 KB 0 B 1.4 KB
getStateByIbgeCode 3.2 KB 3.2 KB 0 B 1.1 KB
getStateCodeByName 3.2 KB 3.2 KB 0 B 1.1 KB
getStateNameByCode 3.1 KB 3.1 KB 0 B 1.0 KB
getStates 3.0 KB 3.0 KB 0 B 1017 B
getTimezoneByState 1.6 KB 1.6 KB 0 B 809 B
isBusinessDay 6.5 KB 6.5 KB 0 B 2.7 KB
isHoliday 6.4 KB 6.4 KB 0 B 2.7 KB
isValidBankAccount 7.4 KB 7.4 KB 0 B 2.8 KB
isValidBoleto 2.4 KB 2.4 KB 0 B 1.3 KB
isValidCEP 984 B 984 B 0 B 610 B
isValidCNPJ 1.6 KB 1.6 KB 0 B 914 B
isValidCPF 1.3 KB 1.3 KB 0 B 805 B
isValidCaepf 1.5 KB 1.5 KB 0 B 913 B
isValidCbo 119.2 KB 119.2 KB 0 B 30.7 KB
isValidCei 1.5 KB 1.5 KB 0 B 898 B
isValidCep 984 B 984 B 0 B 610 B
isValidCertidao 1.6 KB 1.6 KB 0 B 938 B
isValidCfop 68.9 KB 68.9 KB 0 B 6.9 KB
isValidCnae 94.0 KB 94.0 KB 0 B 21.2 KB
isValidCnh 1.4 KB 1.4 KB 0 B 856 B
isValidCno 1.5 KB 1.5 KB 0 B 901 B
isValidCnpj 1.6 KB 1.6 KB 0 B 914 B
isValidCns 1.5 KB 1.5 KB 0 B 925 B
isValidCpf 1.3 KB 1.3 KB 0 B 805 B
isValidCreditCard 1.4 KB 1.4 KB 0 B 868 B
isValidCsosn 1.2 KB 1.2 KB 0 B 737 B
isValidCst 1.8 KB 1.8 KB 0 B 1.0 KB
isValidEmail 1.0 KB 1.0 KB 0 B 622 B
isValidIE 5.7 KB 5.7 KB 0 B 2.1 KB
isValidIban 1.3 KB 1.3 KB 0 B 836 B
isValidIe 5.7 KB 5.7 KB 0 B 2.1 KB
isValidLandlinePhone 1.5 KB 1.5 KB 0 B 932 B
isValidLegalNature 5.8 KB 5.8 KB 0 B 2.1 KB
isValidLicensePlate 1.1 KB 1.1 KB 0 B 702 B
isValidMobilePhone 1.6 KB 1.6 KB 0 B 971 B
isValidNcm 114.2 KB 114.2 KB 0 B 24.6 KB
isValidNfeKey 2.7 KB 2.7 KB 0 B 1.5 KB
isValidPIS 1.2 KB 1.2 KB 0 B 784 B
isValidPassport 1.0 KB 1.0 KB 0 B 654 B
isValidPhone 2.6 KB 2.6 KB 0 B 1.3 KB
isValidPis 1.2 KB 1.2 KB 0 B 784 B
isValidPixKey 4.6 KB 4.6 KB 0 B 2.1 KB
isValidPixPayload 2.9 KB 2.9 KB 0 B 1.5 KB
isValidProcessoJuridico 1.3 KB 1.3 KB 0 B 787 B
isValidRegistroProfissional 1.6 KB 1.6 KB 0 B 964 B
isValidRenavam 1.3 KB 1.3 KB 0 B 814 B
isValidServicePhone 1.5 KB 1.5 KB 0 B 846 B
🆕 isValidSuframa 1.4 KB new 884 B
isValidVin 1.6 KB 1.6 KB 0 B 995 B
isValidVoterId 1.6 KB 1.6 KB 0 B 900 B
parseBoleto 1020 B 1020 B 0 B 634 B
parseCaepf 1003 B 1003 B 0 B 621 B
parseCbo 1002 B 1002 B 0 B 620 B
parseCei 1003 B 1003 B 0 B 619 B
parseCep 1002 B 1002 B 0 B 620 B
parseCertidao 1003 B 1003 B 0 B 620 B
parseCfop 1002 B 1002 B 0 B 620 B
parseCnae 1002 B 1002 B 0 B 620 B
parseCnh 1003 B 1003 B 0 B 621 B
parseCno 1003 B 1003 B 0 B 619 B
parseCnpj 1.1 KB 1.1 KB 0 B 669 B
parseCns 1003 B 1003 B 0 B 621 B
parseCpf 1003 B 1003 B 0 B 621 B
parseCurrency 1.4 KB 1.4 KB 0 B 881 B
parseIban 1.0 KB 1.0 KB 0 B 638 B
parseLegalNature 1002 B 1002 B 0 B 620 B
parseLicensePlate 1.0 KB 1.0 KB 0 B 638 B
parseNcm 1002 B 1002 B 0 B 620 B
parseNfeKey 1.0 KB 1.0 KB 0 B 659 B
parsePassport 1.0 KB 1.0 KB 0 B 637 B
parsePhone 1.1 KB 1.1 KB 0 B 707 B
parsePis 1003 B 1003 B 0 B 621 B
parseProcessoJuridico 1003 B 1003 B 0 B 621 B
🆕 parseSuframa 1002 B new 620 B
parseVoterId 1.0 KB 1.0 KB 0 B 649 B
removeAccents 953 B 953 B 0 B 593 B
subBusinessDays 6.9 KB 6.9 KB 0 B 2.9 KB
toStandardSchema 1.1 KB 1.1 KB 0 B 713 B
How this is measured

Every export is imported alone into an esbuild consumer bundle (minified, tree-shaken) built from the head and from the base of this pull request; the sizes are the resulting bundles, gzip is their gzipped size. 🔴 marks a regression: a pre-existing export that grew more than 20% and more than 256 B, or the bundle importing every pre-existing export growing more than 5%. 🟡 is growth under the threshold, 🟢 a decrease, ⚪ no change, 🆕 an export that does not exist on the base (never a regression), 🗑️ an export that was removed. An intentional increase is accepted with the tree-shaking: accepted label.

@codecov

codecov Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e92e93a) to head (5b2b199).

Additional details and impacted files
@@                  Coverage Diff                  @@
##           claude/get-cpf-info      #559   +/-   ##
=====================================================
  Coverage               100.00%   100.00%           
=====================================================
  Files                      187       191    +4     
  Lines                     2077      2094   +17     
  Branches                   614       618    +4     
=====================================================
+ Hits                      2077      2094   +17     
Flag Coverage Δ
node 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hyanmandian
hyanmandian changed the base branch from main to claude/get-cpf-info September 22, 2026 04:36
@vercel

vercel Bot commented Sep 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
brazilian-utils Ready Ready Preview Sep 22, 2026 4:58am UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@brazilian-utils/brazilian-utils@559

commit: 5b2b199

@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hyanmandian
hyanmandian removed this pull request from stack #589 September 22, 2026 04:57
…nerateSuframa

The Inscrição SUFRAMA goes in the ISUF field of the NF-e recipient and a wrong
one is rejected by the SEFAZ (rejection 235), so issuers need to check it before
sending the document.

The rules come from Anexo XII.01 of the NF-e Manual de Orientação do
Contribuinte 6.0: the number is SS.NNNN.LLD (sector, sequence, locality, check
digit), the field holds 8 or 9 digits because a sector code such as 01 loses its
leading zero, the sector code is never 00, and the check digit is módulo 11 with
weights 2 to 9 and 0 for a remainder of 0 or 1. That is the existing mod11
"arrecadacao" rule, so no new internal is needed.

The manual lists the sector and locality codes only as examples, so they are
not validated against a table.
The four utilities cited a SEFAZ-SP copy of the superseded MOC 6.0. The
current national manual, MOC 7.0, is published by CONFAZ and says the
same in the Visão Geral, section 8.4: the composition SS.NNNN.LLD, the
numeric field with 8 or 9 positions whose SS may start with 0 but never
be 00, and the módulo 11 check digit with weights 2 to 9. Its Anexo I
still types field 79 (E18, ISUF) as numeric with 8 to 9 positions and
still rejects an invalid check digit through rule E18-20 (rejection
235), so nothing in the code changes, only the citation. That also
answers the open point about MOC 7.0 changing the annex: it does not.

The docs never showed what formatSuframa does to an 8 digit value
without pad, although 8 positions is a valid ISUF length, so a reader
could not see that the mask is progressive and groups one position
early. Both docs and the example block now show it next to the padded
form.

The test that guarded against a 10 digit value was also named after the
first 9 digits being valid while its second vector, 1234567090, is the
opposite case: 123456709 has check digit 3, not 9, and it is the last 9
digits, 234567090, that are valid. Split into the two named cases.
@hyanmandian

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
⚠️ Action not completed

No files to review.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

This branch was successfully deployed

1 active deployment
Preview 5b2b1997 Deployed Sep 22, 2026 by vercel[bot]
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.

1 participant