Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions docs/pt-br/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,73 @@ getNfeKeyInfo('35170458716523000119620010000000121000123450');
getNfeKeyInfo('invalid'); // null
```

## Chave de NFS-e

### isValidNfseKey

Verifica se a chave de acesso de uma NFS-e nacional, a Nota Fiscal de Serviço eletrônica do Sistema Nacional NFS-e, é válida.

- A chave é um bloco único de 50 dígitos, `Cód.Mun.(7) Amb.Ger.(1) Tipo de Inscrição Federal(1) Inscrição Federal(14) nNFSe(13) AAMM(4) Cód.Num.(9) DV(1)`.
- O literal `NFS` que o atributo `Id` de `infNFSe` coloca antes da chave é retirado, junto com os espaços nas extremidades.
- A chave não tem máscara impressa, já que o DANFSe a imprime em um único bloco de 50 dígitos, então, diferente do `isValidNfeKey`, um separador em qualquer ponto dela é rejeitado em vez de removido.
- O código do município precisa começar com um código IBGE de UF; ele não é consultado na tabela do IBGE.
- O `ambGer` precisa ser `1` (o sistema do município) ou `2` (o Sistema Nacional NFS-e), e o tipo de inscrição `1` (um CPF, preenchido com `000` à esquerda) ou `2` (um CNPJ), com um CPF ou CNPJ cujos próprios dígitos verificadores sejam válidos.
- O `nNFSe` não pode ser todo de zeros e o mês precisa estar entre 01 e 12.
- O dígito verificador é um módulo 11 sobre os 49 primeiros dígitos, pesos de 2 a 9 ciclando a partir da direita, em que resto 0 ou 1 dá 0.
- Chaves com CNPJ alfanumérico ainda não são aceitas: nenhum documento oficial diz como uma letra entra no dígito verificador da chave.
- Os modelos municipais de NFS-e que não são o padrão nacional estão fora do escopo.

```javascript
import { isValidNfseKey } from '@brazilian-utils/brazilian-utils';

isValidNfseKey('35503082258716523000119000000000001226011357924683'); // true (emitente com CNPJ, SP)
isValidNfseKey('NFS35503082258716523000119000000000001226011357924683'); // true (prefixo Id do XML)
isValidNfseKey('43149021100040364478829000000000105725120484407255'); // true (emitente com CPF, RS)
isValidNfseKey('35503082258716523000119000000000001226011357924684'); // false (dígito verificador)
isValidNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3'); // false (a chave não tem máscara)
```

### parseNfseKey

Remove tudo o que não é dígito da chave de acesso de uma NFS-e nacional, inclusive o prefixo `NFS` do atributo `Id` do XML, e limita o resultado a 50 dígitos.

- Essa é a forma em que o leiaute guarda a chave e a que o DANFSe imprime, um bloco único, e por isso não existe `formatNfseKey`.

```javascript
import { parseNfseKey } from '@brazilian-utils/brazilian-utils';

parseNfseKey('NFS35503082258716523000119000000000001226011357924683');
// '35503082258716523000119000000000001226011357924683'

parseNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3');
// '35503082258716523000119000000000001226011357924683'
```

### getNfseKeyInfo

Interpreta a chave de acesso de uma NFS-e nacional e retorna seus campos, como um `NfseKeyInfo`. Aceita as mesmas formas de entrada do `isValidNfseKey`.

- Retorna `municipalityCode`, `stateCode`, `generatorEnvironment`, `taxIdType`, `taxId`, `number`, `year`, `month`, `code` e `checkDigit`.
- O `generatorEnvironment` é um `NfseKeyGeneratorEnvironment`: `1` o sistema do município, `2` o Sistema Nacional NFS-e.
- O `taxIdType` é um `NfseKeyTaxIdType`, `'cpf'` ou `'cnpj'`, e o `taxId` é o CPF de 11 dígitos, sem o `000` que o preenche na chave, ou o CNPJ de 14 dígitos.
- Retorna `null` quando a chave não é válida.

```javascript
import { getNfseKeyInfo } from '@brazilian-utils/brazilian-utils';

getNfseKeyInfo('35503082258716523000119000000000001226011357924683');
// { municipalityCode: '3550308', stateCode: 'SP', generatorEnvironment: 2, taxIdType: 'cnpj',
// taxId: '58716523000119', number: 12, year: 2026, month: 1, code: '135792468', checkDigit: 3 }

getNfseKeyInfo('43149021100040364478829000000000105725120484407255');
// { municipalityCode: '4314902', stateCode: 'RS', generatorEnvironment: 1, taxIdType: 'cpf',
// taxId: '40364478829', number: 1057, year: 2025, month: 12, code: '048440725', checkDigit: 5 }

getNfseKeyInfo('invalid'); // null
```

Fonte: a [documentação técnica do Sistema Nacional NFS-e](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual), cujos tipos de esquema `TSIdNFSe` e `TSChaveNFSe` e o campo `NFSe/infNFSe/id` do ANEXO I definem o leiaute e as regras E1280 e E1284, o [manual da emissão por decisão administrativa ou judicial](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual/manual-contribuintes-emissor-publico-api-emissao-decisao-administrativa-e-judicial.pdf), que nomeia o dígito verificador de módulo 11, e a [Nota Técnica SE/CGNFS-e 008](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/rtc/nt-008-se-cgnfse-danfse-20260714-v1-02.pdf) (item 2.1.1), que imprime a chave em bloco único.

## SUFRAMA

### isValidSuframa
Expand Down
67 changes: 67 additions & 0 deletions docs/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,73 @@ getNfeKeyInfo('35170458716523000119620010000000121000123450');
getNfeKeyInfo('invalid'); // null
```

## NFS-e key

### isValidNfseKey

Check if the access key (chave de acesso) of a national NFS-e, the Nota Fiscal de Serviço eletrônica of the Sistema Nacional NFS-e, is valid.

- The key is one block of 50 digits, `Cód.Mun.(7) Amb.Ger.(1) Tipo de Inscrição Federal(1) Inscrição Federal(14) nNFSe(13) AAMM(4) Cód.Num.(9) DV(1)`.
- The `NFS` literal the `Id` attribute of `infNFSe` puts in front of the key is stripped, with surrounding whitespace.
- The key has no printed mask, since the DANFSe prints it as a single block of 50 digits, so, unlike `isValidNfeKey`, a separator anywhere in it is rejected instead of being stripped.
- The municipality code must start with an IBGE UF code; it is not looked up in the IBGE table.
- `ambGer` must be `1` (the system of the municipality) or `2` (the Sistema Nacional NFS-e), and the registration type `1` (a CPF, left padded with `000`) or `2` (a CNPJ), with a CPF or CNPJ whose own check digits are valid.
- `nNFSe` must not be all zeros and the month must be 01 to 12.
- The check digit is a modulus 11 over the first 49 digits, weights 2 to 9 cycling from the right, where a remainder of 0 or 1 gives 0.
- Keys carrying an alphanumeric CNPJ are not accepted yet: no official document states how a letter enters the check digit of the key.
- The municipal NFS-e models that are not the national standard are out of scope.

```javascript
import { isValidNfseKey } from '@brazilian-utils/brazilian-utils';

isValidNfseKey('35503082258716523000119000000000001226011357924683'); // true (CNPJ issuer, SP)
isValidNfseKey('NFS35503082258716523000119000000000001226011357924683'); // true (XML Id prefix)
isValidNfseKey('43149021100040364478829000000000105725120484407255'); // true (CPF issuer, RS)
isValidNfseKey('35503082258716523000119000000000001226011357924684'); // false (check digit)
isValidNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3'); // false (the key has no mask)
```

### parseNfseKey

Remove everything but the digits from the access key of a national NFS-e, the `NFS` prefix of the XML `Id` attribute included, and cap the result to 50 digits.

- That is the form the leiaute stores the key in and the one the DANFSe prints, a single block, which is why there is no `formatNfseKey`.

```javascript
import { parseNfseKey } from '@brazilian-utils/brazilian-utils';

parseNfseKey('NFS35503082258716523000119000000000001226011357924683');
// '35503082258716523000119000000000001226011357924683'

parseNfseKey('3550308 2 2 58716523000119 0000000000012 2601 135792468 3');
// '35503082258716523000119000000000001226011357924683'
```

### getNfseKeyInfo

Parse the access key of a national NFS-e into its fields, as an `NfseKeyInfo`. Accepts the same input forms as `isValidNfseKey`.

- Returns `municipalityCode`, `stateCode`, `generatorEnvironment`, `taxIdType`, `taxId`, `number`, `year`, `month`, `code` and `checkDigit`.
- `generatorEnvironment` is an `NfseKeyGeneratorEnvironment`: `1` the system of the municipality, `2` the Sistema Nacional NFS-e.
- `taxIdType` is an `NfseKeyTaxIdType`, `'cpf'` or `'cnpj'`, and `taxId` is the 11 digit CPF, without the `000` that pads it in the key, or the 14 digit CNPJ.
- Returns `null` when the key is not valid.

```javascript
import { getNfseKeyInfo } from '@brazilian-utils/brazilian-utils';

getNfseKeyInfo('35503082258716523000119000000000001226011357924683');
// { municipalityCode: '3550308', stateCode: 'SP', generatorEnvironment: 2, taxIdType: 'cnpj',
// taxId: '58716523000119', number: 12, year: 2026, month: 1, code: '135792468', checkDigit: 3 }

getNfseKeyInfo('43149021100040364478829000000000105725120484407255');
// { municipalityCode: '4314902', stateCode: 'RS', generatorEnvironment: 1, taxIdType: 'cpf',
// taxId: '40364478829', number: 1057, year: 2025, month: 12, code: '048440725', checkDigit: 5 }

getNfseKeyInfo('invalid'); // null
```

Source: the [technical documentation of the Sistema Nacional NFS-e](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual), whose schema types `TSIdNFSe` and `TSChaveNFSe` and ANEXO I field `NFSe/infNFSe/id` define the layout and the rules E1280 and E1284, the [manual da emissão por decisão administrativa ou judicial](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual/manual-contribuintes-emissor-publico-api-emissao-decisao-administrativa-e-judicial.pdf), which names the modulus 11 check digit, and [Nota Técnica SE/CGNFS-e 008](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/rtc/nt-008-se-cgnfse-danfse-20260714-v1-02.pdf) (item 2.1.1), which prints the key as a single block.

## SUFRAMA

### isValidSuframa
Expand Down
3 changes: 3 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"./get-municipality-by-code": "./src/get-municipality-by-code/get-municipality-by-code.ts",
"./get-next-business-day": "./src/get-next-business-day/get-next-business-day.ts",
"./get-nfe-key-info": "./src/get-nfe-key-info/get-nfe-key-info.ts",
"./get-nfse-key-info": "./src/get-nfse-key-info/get-nfse-key-info.ts",
"./get-nth-business-day": "./src/get-nth-business-day/get-nth-business-day.ts",
"./get-pix-key-info": "./src/get-pix-key-info/get-pix-key-info.ts",
"./get-pix-payload-info": "./src/get-pix-payload-info/get-pix-payload-info.ts",
Expand Down Expand Up @@ -115,6 +116,7 @@
"./is-valid-mobile-phone": "./src/is-valid-mobile-phone/is-valid-mobile-phone.ts",
"./is-valid-ncm": "./src/is-valid-ncm/is-valid-ncm.ts",
"./is-valid-nfe-key": "./src/is-valid-nfe-key/is-valid-nfe-key.ts",
"./is-valid-nfse-key": "./src/is-valid-nfse-key/is-valid-nfse-key.ts",
"./is-valid-passport": "./src/is-valid-passport/is-valid-passport.ts",
"./is-valid-phone": "./src/is-valid-phone/is-valid-phone.ts",
"./is-valid-pis": "./src/is-valid-pis/is-valid-pis.ts",
Expand Down Expand Up @@ -148,6 +150,7 @@
"./parse-license-plate": "./src/parse-license-plate/parse-license-plate.ts",
"./parse-ncm": "./src/parse-ncm/parse-ncm.ts",
"./parse-nfe-key": "./src/parse-nfe-key/parse-nfe-key.ts",
"./parse-nfse-key": "./src/parse-nfse-key/parse-nfse-key.ts",
"./parse-passport": "./src/parse-passport/parse-passport.ts",
"./parse-phone": "./src/parse-phone/parse-phone.ts",
"./parse-pis": "./src/parse-pis/parse-pis.ts",
Expand Down
29 changes: 29 additions & 0 deletions reports/api/brazilian-utils.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,9 @@ export const getNextBusinessDay: (date: Date, options?: BusinessDayOptions) => D
// @public
export const getNfeKeyInfo: (value: string) => NfeKeyInfo | null;

// @public
export const getNfseKeyInfo: (value: string) => NfseKeyInfo | null;

// @public
export const getNthBusinessDay: (date: Date, n: number, options?: BusinessDayOptions) => Date | null;

Expand Down Expand Up @@ -840,6 +843,9 @@ export const isValidNcm: (value: string | number) => boolean;
// @public
export const isValidNfeKey: (value: string) => boolean;

// @public
export const isValidNfseKey: (value: string) => boolean;

// @public
export const isValidPassport: (passport: string | number) => boolean;

Expand Down Expand Up @@ -943,6 +949,26 @@ export type NfeKeyInfo = {
// @public
export type NfeKeyModel = "55" | "57" | "58" | "62" | "63" | "64" | "65" | "66" | "67";

// @public
export type NfseKeyGeneratorEnvironment = 1 | 2;

// @public
export type NfseKeyInfo = {
municipalityCode: string;
stateCode: StateCode;
generatorEnvironment: NfseKeyGeneratorEnvironment;
taxIdType: NfseKeyTaxIdType;
taxId: string;
number: number;
year: number;
month: number;
code: string;
checkDigit: number;
};

// @public
export type NfseKeyTaxIdType = "cpf" | "cnpj";

// @public
export type NumberToWordsGender = "masculine" | "feminine";

Expand Down Expand Up @@ -1017,6 +1043,9 @@ export const parseNcm: (value: string | number) => string;
// @public
export const parseNfeKey: (value: string | number) => string;

// @public
export const parseNfseKey: (value: string | number) => string;

// @public
export const parsePassport: (passport: string) => string;

Expand Down
53 changes: 53 additions & 0 deletions src/get-nfse-key-info/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Shape of the key: one block of 50 digits, the pattern of `TSChaveNFSe` in
* `tiposSimples_v1.01.xsd`, optionally behind the `NFS` literal the `Id` attribute of `infNFSe`
* puts in front of it (`TSIdNFSe`). The DANFSe prints the key the same way ("em único bloco
* contendo 50 dígitos", Nota Técnica SE/CGNFS-e 008, item 2.1.1), so there is no mask to accept.
* The digits are the first capture group.
*/
export const FORMAT_REGEX = /^(?:nfs)?(\d{50})$/i;

/**
* The `ambGer` (ambiente gerador) codes of `TSAmbGeradorNFSe`: 1 for the system of the
* municipality (Prefeitura), 2 for the Sistema Nacional NFS-e (Sefin Nacional).
*/
export const GENERATOR_ENVIRONMENTS = [1, 2] as const;

/**
* The "Tipo de Inscrição Federal" codes of the key, as rule E1263 of the ANEXO I states them:
* 1 for a CPF and 2 for a CNPJ.
*/
export const TAX_ID_TYPES: Readonly<Record<string, "cpf" | "cnpj">> = { "1": "cpf", "2": "cnpj" };

/** The zeros that pad an 11 digit CPF to the 14 positions of the "Inscrição Federal" field. */
export const CPF_PADDING = "000";

/**
* A number of all zeros is not a valid `nNFSe`: the leiaute types it `TSNNFSe`, whose pattern is
* `[1-9]{1}[0-9]{0,12}`.
*/
export const ABSENT_NUMBER = "0000000000000";

/** Position of `ambGer` inside the 50 digit key. */
export const GENERATOR_ENVIRONMENT_INDEX = 7;

/** Position of the "Tipo de Inscrição Federal" inside the 50 digit key. */
export const TAX_ID_TYPE_INDEX = 8;

/** Start of the "Inscrição Federal" inside the 50 digit key. */
export const TAX_ID_START = 9;

/** Start of the NFS-e number (`nNFSe`), which is also the end of the "Inscrição Federal". */
export const NUMBER_START = 23;

/** Start of the issue year and month (AAMM), which is also the end of the NFS-e number. */
export const YEAR_START = 36;

/** Start of the issue month, which is also the end of the issue year. */
export const MONTH_START = 38;

/** Start of the numeric code (Cód.Num.), which is also the end of the issue month. */
export const CODE_START = 40;

/** Position of the check digit (DV), which is also the end of the numeric code. */
export const CHECK_DIGIT_INDEX = 49;
Loading
Loading