Skip to content
Merged
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
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ but the few rules below hold everywhere:
they call.
- `src/_internals/` holds the helpers shared between utilities (check-digit arithmetic, `mod10`/
`mod11`, TLV parsing for Pix, sanitizers). `src/_internals/constants/` holds the datasets
(banks, municipalities, CNAE, CBO, CFOP, NCM, legal natures), generated by the `scripts/`
from their official sources and refreshed by the `Update datasets` workflow through
a pull request, never edited by hand.
(banks, municipalities, CNAE, CBO, CFOP, NCM, NBS, the LC 116/2003 service list, legal natures),
generated by the `scripts/` from their official sources and refreshed by the `Update datasets`
workflow through a pull request, never edited by hand.
- Utilities are synchronous, stateless and side-effect free: input in, value out, no globals, no
environment access, no dynamic code. The two exceptions are `getAddressInfoByCep` and
`getCepInfoByAddress`, the only utilities that do I/O: they query public CEP APIs (BrasilAPI,
Expand Down
2 changes: 1 addition & 1 deletion context7.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"rules": [
"The package has zero runtime dependencies and ships as ESM plus a UMD build; nothing else needs to be installed to use it.",
"Import from the root: import { isValidCpf } from '@brazilian-utils/brazilian-utils'. Every util is also a kebab-case subpath, e.g. '@brazilian-utils/brazilian-utils/is-valid-cpf'.",
"Use the subpaths to lazy-load the dataset-backed utils (getMunicipalities, getMunicipalityByCode, getCnae, getCbo, getCfop, isValidNcm, getBanks): each one embeds a large official table.",
"Use the subpaths to lazy-load the dataset-backed utils (getMunicipalities, getMunicipalityByCode, getCnae, getCbo, getCfop, isValidNcm, getBanks, getNbs, isValidNbs, getServiceItem, isValidServiceItem): each one embeds a large official table.",
"Never import the same util from both the root and its subpath in one app: a bundler treats them as two unrelated modules and bundles the dataset twice.",
"Public functions never throw on bad input (null, undefined, wrong type): isValid* return false, format* and parse* return '', single-item getters return null, list getters return [].",
"The only utils that reject are the async getAddressInfoByCep (GetAddressInfoByCepError: NotFound, Validation, Service) and getCepInfoByAddress (GetCepInfoByAddressError: NotFound, Validation).",
Expand Down
2 changes: 2 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ A few utils embed an official dataset and weigh far more than everything else co
| `isValidNcm` | NCM (Nomenclatura Comum do Mercosul) codes | 114.2 KB | 24.6 KB |
| `isValidCbo` · `getCbo` | CBO 2002 occupation titles | 119.1 KB | 30.6 KB |
| `isValidCnae` · `getCnae` | CNAE-Subclasses 2.3 | 93.9 KB | 21.2 KB |
| `isValidNbs` · `getNbs` | NBS 2.0 (Nomenclatura Brasileira de Serviços) descriptions | 81.8 KB | 13.8 KB |
| `isValidCfop` · `getCfop` | CFOP operation descriptions | 68.9 KB | 6.9 KB |
| `getBanks` · `getBankByCode` · `getBankByIspb` | Banco Central STR participants (COMPE + ISPB) | 38.3 - 38.6 KB | 9.5 - 9.7 KB |
| `isValidServiceItem` · `getServiceItem` | Service list of the Lei Complementar 116/2003 | 27.1 - 27.2 KB | 8.8 - 8.9 KB |

The root of the package is a single ESM module, so a bundler cannot split one of these datasets out of it: importing a heavy util from the root puts its whole dataset in your main bundle, and a dynamic `import()` of the root does not help. To lazy-load one, import it from its own subpath:

Expand Down
2 changes: 2 additions & 0 deletions docs/pt-br/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ Alguns utilitários embutem uma base de dados oficial e pesam muito mais que tod
| `isValidNcm` | códigos NCM (Nomenclatura Comum do Mercosul) | 114,2 KB | 24,6 KB |
| `isValidCbo` · `getCbo` | títulos das ocupações da CBO 2002 | 119,1 KB | 30,6 KB |
| `isValidCnae` · `getCnae` | CNAE-Subclasses 2.3 | 93,9 KB | 21,2 KB |
| `isValidNbs` · `getNbs` | descrições da NBS 2.0 (Nomenclatura Brasileira de Serviços) | 81,8 KB | 13,8 KB |
| `isValidCfop` · `getCfop` | descrições das operações do CFOP | 68,9 KB | 6,9 KB |
| `getBanks` · `getBankByCode` · `getBankByIspb` | participantes do STR do Banco Central (COMPE + ISPB) | 38,3 - 38,6 KB | 9,5 - 9,7 KB |
| `isValidServiceItem` · `getServiceItem` | lista de serviços da Lei Complementar 116/2003 | 27,1 - 27,2 KB | 8,8 - 8,9 KB |

A raiz do pacote é um único módulo ESM, então o bundler não consegue separar uma dessas bases de dados dele: importar um utilitário pesado da raiz coloca a base inteira no seu bundle principal, e um `import()` dinâmico da raiz não ajuda. Para carregar sob demanda, importe do subpath próprio:

Expand Down
95 changes: 95 additions & 0 deletions docs/pt-br/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,101 @@ parseNcm('8471.30.12'); // '84713012'
parseNcm('8471'); // '8471' (um código parcial é mantido como está)
```

### isValidNbs

Valida um código NBS (Nomenclatura Brasileira de Serviços, Intangíveis e Outras Operações que Produzam Variações no Patrimônio) contra a tabela oficial da NBS 2.0, o código que a NFS-e nacional leva em `cNBS`.

- O código tem 9 dígitos, impressos como `N.NNNN.NN.NN`: o algarismo 1, o capítulo, a posição, os dois níveis de subposição, o item e o subitem.
- Aceita uma string com os 9 dígitos ou com a máscara, com um único separador entre os grupos e espaços opcionais nas extremidades, ou um inteiro seguro não negativo. Qualquer outra string é rejeitada em vez de ter os dígitos pinçados.
- Só códigos completos são válidos: os títulos de capítulo (`1.01`), posição (`1.0101`) e subposição (`1.0101.1`) não classificam nada por si sós.
- O ANEXO B do Sistema Nacional NFS-e lista os mesmos 920 códigos menos três (`1.0402.29.00`, `1.0403.29.00` e `1.0904.40.00`), então um código válido aqui ainda pode ser recusado pela NFS-e.

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

isValidNbs('1.0101.11.00'); // true
isValidNbs('101011100'); // true
isValidNbs(101011100); // true
isValidNbs('1.0101'); // false (título de posição, não um código completo)
isValidNbs('1.9999.99.99'); // false
isValidNbs('1.0101abc11.00'); // false (não é uma forma documentada)
```

### formatNbs

Formata um código NBS (Nomenclatura Brasileira de Serviços) na máscara `N.NNNN.NN.NN` em que a nomenclatura o imprime. Só a estrutura muda; use `isValidNbs` para conferir um código com a tabela.

- Todo código NBS começa com 1, então, diferente do `formatNcm`, não há opção `pad`.
- No resto, mesmas regras de `formatCnae`: a máscara é aplicada até onde o valor vai, os caracteres fora dela são descartados e um número só é lido como a string dos seus dígitos quando é um inteiro seguro não negativo; qualquer outro número retorna `''`.

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

formatNbs('101011100'); // 1.0101.11.00
formatNbs(101011100); // 1.0101.11.00
formatNbs('10101'); // 1.0101 (mascarado até onde vai)
formatNbs('abc101011100'); // 1.0101.11.00 (só os dígitos são lidos)
formatNbs(-101011100); // '' (não é um inteiro seguro não negativo)
```

### getNbs

Consulta um código NBS (Nomenclatura Brasileira de Serviços) e retorna a sua descrição oficial. O resultado é um registro `Nbs`: `{ code, description }`.

- Mesmas regras de `isValidNbs`. `code` são os 9 dígitos, sem a máscara. Retorna `null` quando o código é desconhecido ou o valor não está em uma forma documentada.

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

getNbs('1.0101.11.00');
// { code: '101011100', description: 'Serviços de construção de edificações residenciais de um e dois pavimentos' }

getNbs(126050000); // { code: '126050000', description: 'Serviços domésticos' }
getNbs('1.0101'); // null (título de posição, não um código completo)
getNbs('1.9999.99.99'); // null
```

Fonte: [tabela da NBS 2.0 publicada pelo MDIC](https://www.gov.br/mdic/pt-br/assuntos/sdic/comercio-e-servicos/nbs-nomenclatura-brasileira-de-servicos), aprovada pela Portaria Conjunta RFB/SCS 1.429/2018 e alterada pela Portaria Conjunta RFB/SCS 2.000/2018.

### isValidServiceItem

Verifica se um valor é um subitem em vigor da lista de serviços anexa à Lei Complementar 116/2003, a lista dos serviços sobre os quais incide o ISS.

- A lei numera o subitem como o item, um ponto e dois dígitos, de `1.01` a `40.01`.
- Aceita essa forma, o item preenchido com zero (`'01.01'`) ou os dígitos puros (`'0101'`, `'101'` ou o inteiro `101`), que são os quatro primeiros dígitos do código `cTribNac` da NFS-e nacional, com espaços opcionais nas extremidades.
- O ponto é o único separador que a lei imprime entre o item e o subitem, então, ao contrário dos códigos com máscara de agrupamento impressa (`isValidCfop`, `isValidNbs`), nada mais é aceito no lugar dele e `'1-01'` é rejeitado.
- Um número só é lido quando é um inteiro seguro não negativo, então o decimal `1.01` é rejeitado: escreva a forma com ponto como string.
- Os subitens vetados (`3.01`, `7.14`, `7.15`, `13.01` e `17.07`), os títulos de item, os códigos nacionais de 6 dígitos em que um subitem se desdobra e o item 99 da lista nacional, que não faz parte da lei, não são válidos. Códigos municipais de serviço estão fora do escopo.

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

isValidServiceItem('1.01'); // true
isValidServiceItem('01.01'); // true
isValidServiceItem('0101'); // true
isValidServiceItem(101); // true
isValidServiceItem('3.01'); // false (vetado)
isValidServiceItem('99.01'); // false (só da lista nacional, não da lei)
isValidServiceItem(1.01); // false (não é um inteiro seguro não negativo)
```

### getServiceItem

Consulta um subitem da lista de serviços anexa à Lei Complementar 116/2003 e retorna a sua descrição oficial. O resultado é um registro `ServiceItem`: `{ code, description }`.

- Mesmas regras de `isValidServiceItem`. `code` é a forma em que a lei o imprime (`'1.01'`). Retorna `null` quando o subitem é desconhecido ou o valor não está em uma forma documentada.

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

getServiceItem('1.01'); // { code: '1.01', description: 'Análise e desenvolvimento de sistemas.' }
getServiceItem('0101'); // { code: '1.01', description: 'Análise e desenvolvimento de sistemas.' }
getServiceItem('40.01'); // { code: '40.01', description: 'Obras de arte sob encomenda.' }
getServiceItem('3.01'); // null (vetado)
```

Fonte: [Lei Complementar 116/2003](https://www.planalto.gov.br/ccivil_03/leis/lcp/lcp116.htm) e a planilha `LISTA.SERV.NAC.` do [ANEXO B do Sistema Nacional NFS-e](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual), a lista em vigor em formato legível por máquina.

### isValidCfop

Valida um código CFOP (Código Fiscal de Operações e Prestações) contra a tabela oficial, o Anexo II consolidado do Convênio SINIEF s/nº 1970 em vigor.
Expand Down
95 changes: 95 additions & 0 deletions docs/utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,101 @@ parseNcm('8471.30.12'); // '84713012'
parseNcm('8471'); // '8471' (a partial code is kept as written)
```

### isValidNbs

Check if an NBS (Nomenclatura Brasileira de Serviços, Intangíveis e Outras Operações que Produzam Variações no Patrimônio) code exists in the official NBS 2.0 table, the code the national NFS-e carries in `cNBS`.

- A code has 9 digits, printed as `N.NNNN.NN.NN`: the digit 1, the chapter, the position, the two subposition levels, the item and the subitem.
- Accepts a string with the 9 digits or with the mask, with a single separator between the groups and optional surrounding whitespace, or a non-negative safe integer. Any other string is rejected instead of having its digits picked out.
- Only complete codes are valid: the chapter (`1.01`), position (`1.0101`) and subposition (`1.0101.1`) headings classify nothing by themselves.
- The ANEXO B of the Sistema Nacional NFS-e lists the same 920 codes except three (`1.0402.29.00`, `1.0403.29.00` and `1.0904.40.00`), so a code valid here can still be refused by the NFS-e.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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

isValidNbs('1.0101.11.00'); // true
isValidNbs('101011100'); // true
isValidNbs(101011100); // true
isValidNbs('1.0101'); // false (a position heading, not a complete code)
isValidNbs('1.9999.99.99'); // false
isValidNbs('1.0101abc11.00'); // false (not a documented form)
```

### formatNbs

Format an NBS (Nomenclatura Brasileira de Serviços) code into the `N.NNNN.NN.NN` mask the nomenclature prints. Only the structure changes; use `isValidNbs` to check a code against the table.

- Every NBS code starts with 1, so, unlike `formatNcm`, there is no `pad` option.
- Same rules as `formatCnae` otherwise: the mask is applied as far as the value goes, characters outside it are dropped, and a number is read as the string of its digits only when it is a non-negative safe integer; any other number returns `''`.

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

formatNbs('101011100'); // 1.0101.11.00
formatNbs(101011100); // 1.0101.11.00
formatNbs('10101'); // 1.0101 (masked as far as it goes)
formatNbs('abc101011100'); // 1.0101.11.00 (only the digits are read)
formatNbs(-101011100); // '' (not a non-negative safe integer)
```

### getNbs

Look an NBS (Nomenclatura Brasileira de Serviços) code up and get its official description. The result is an `Nbs` record: `{ code, description }`.

- Same rules as `isValidNbs`. `code` is the 9 digits, without the mask. Returns `null` when the code is unknown or the value is not in a documented form.

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

getNbs('1.0101.11.00');
// { code: '101011100', description: 'Serviços de construção de edificações residenciais de um e dois pavimentos' }

getNbs(126050000); // { code: '126050000', description: 'Serviços domésticos' }
getNbs('1.0101'); // null (a position heading, not a complete code)
getNbs('1.9999.99.99'); // null
```

Source: [NBS 2.0 table published by the MDIC](https://www.gov.br/mdic/pt-br/assuntos/sdic/comercio-e-servicos/nbs-nomenclatura-brasileira-de-servicos), approved by the Portaria Conjunta RFB/SCS 1.429/2018 and amended by the Portaria Conjunta RFB/SCS 2.000/2018.

### isValidServiceItem

Check if a value is a subitem in force of the service list annexed to the Lei Complementar 116/2003, the list of the services the ISS is levied on.

- The law numbers a subitem as the item, a dot and two digits, `1.01` to `40.01`.
- Accepts that form, a zero padded item (`'01.01'`) or the bare digits (`'0101'`, `'101'` or the integer `101`), which are the first four digits of the `cTribNac` code of the national NFS-e, with optional surrounding whitespace.
- The dot is the only separator the law ever prints between the item and the subitem, so, unlike the codes with a printed grouping mask (`isValidCfop`, `isValidNbs`), nothing else is accepted in its place and `'1-01'` is rejected.
- A number is only read when it is a non-negative safe integer, so the float `1.01` is rejected: write the dotted form as a string.
- The vetoed subitems (`3.01`, `7.14`, `7.15`, `13.01` and `17.07`), the item headings, the 6 digit national codes a subitem is split into and item 99 of the national list, which is not part of the law, are not valid. Municipal service codes are out of scope.

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

isValidServiceItem('1.01'); // true
isValidServiceItem('01.01'); // true
isValidServiceItem('0101'); // true
isValidServiceItem(101); // true
isValidServiceItem('3.01'); // false (vetoed)
isValidServiceItem('99.01'); // false (national list only, not the law)
isValidServiceItem(1.01); // false (not a non-negative safe integer)
```

### getServiceItem

Look a subitem of the service list annexed to the Lei Complementar 116/2003 up and get its official description. The result is a `ServiceItem` record: `{ code, description }`.

- Same rules as `isValidServiceItem`. `code` is the form the law prints (`'1.01'`). Returns `null` when the subitem is unknown or the value is not in a documented form.

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

getServiceItem('1.01'); // { code: '1.01', description: 'Análise e desenvolvimento de sistemas.' }
getServiceItem('0101'); // { code: '1.01', description: 'Análise e desenvolvimento de sistemas.' }
getServiceItem('40.01'); // { code: '40.01', description: 'Obras de arte sob encomenda.' }
getServiceItem('3.01'); // null (vetoed)
```

Source: [Lei Complementar 116/2003](https://www.planalto.gov.br/ccivil_03/leis/lcp/lcp116.htm) and the sheet `LISTA.SERV.NAC.` of the [ANEXO B of the Sistema Nacional NFS-e](https://www.gov.br/nfse/pt-br/biblioteca/documentacao-tecnica/documentacao-atual), the list in force in machine readable form.

### isValidCfop

Check if a CFOP (Código Fiscal de Operações e Prestações) code exists in the official table, the consolidated Anexo II of Convênio SINIEF s/nº 1970 in force.
Expand Down
5 changes: 5 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"./format-iban": "./src/format-iban/format-iban.ts",
"./format-legal-nature": "./src/format-legal-nature/format-legal-nature.ts",
"./format-license-plate": "./src/format-license-plate/format-license-plate.ts",
"./format-nbs": "./src/format-nbs/format-nbs.ts",
"./format-ncm": "./src/format-ncm/format-ncm.ts",
"./format-nfe-key": "./src/format-nfe-key/format-nfe-key.ts",
"./format-passport": "./src/format-passport/format-passport.ts",
Expand Down Expand Up @@ -74,10 +75,12 @@
"./get-municipalities": "./src/get-municipalities/get-municipalities.ts",
"./get-municipality": "./src/get-municipality/get-municipality.ts",
"./get-municipality-by-code": "./src/get-municipality-by-code/get-municipality-by-code.ts",
"./get-nbs": "./src/get-nbs/get-nbs.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-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",
"./get-service-item": "./src/get-service-item/get-service-item.ts",
"./get-state-by-cep": "./src/get-state-by-cep/get-state-by-cep.ts",
"./get-state-by-ibge-code": "./src/get-state-by-ibge-code/get-state-by-ibge-code.ts",
"./get-state-code-by-name": "./src/get-state-code-by-name/get-state-code-by-name.ts",
Expand Down Expand Up @@ -111,6 +114,7 @@
"./is-valid-legal-nature": "./src/is-valid-legal-nature/is-valid-legal-nature.ts",
"./is-valid-license-plate": "./src/is-valid-license-plate/is-valid-license-plate.ts",
"./is-valid-mobile-phone": "./src/is-valid-mobile-phone/is-valid-mobile-phone.ts",
"./is-valid-nbs": "./src/is-valid-nbs/is-valid-nbs.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",
Expand All @@ -122,6 +126,7 @@
"./is-valid-processo-juridico": "./src/is-valid-processo-juridico/is-valid-processo-juridico.ts",
"./is-valid-registro-profissional": "./src/is-valid-registro-profissional/is-valid-registro-profissional.ts",
"./is-valid-renavam": "./src/is-valid-renavam/is-valid-renavam.ts",
"./is-valid-service-item": "./src/is-valid-service-item/is-valid-service-item.ts",
"./is-valid-service-phone": "./src/is-valid-service-phone/is-valid-service-phone.ts",
"./is-valid-suframa": "./src/is-valid-suframa/is-valid-suframa.ts",
"./is-valid-vin": "./src/is-valid-vin/is-valid-vin.ts",
Expand Down
Loading
Loading