Skip to content

Commit

Permalink
Merge pull request #106 from lucamenor/feat/nup
Browse files Browse the repository at this point in the history
Adiciona formatter e validator para NUP
  • Loading branch information
rubensdemelo authored Jan 15, 2025
2 parents 0ebf6a6 + c6252d6 commit e14f811
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 23 deletions.
49 changes: 26 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,30 @@ TextFormField(

### Formatters

| Padrão | Formatter | Formato
|:------------------|:-------------------------------|:----------------
| Altura | AlturaInputFormatter() | 2,22
| Cartão | CartaoBancarioInputFormatter() | 0000 1111 2222 3333 4444
| Centavos | CentavosInputFormatter() | 7,194
| CEP | CepInputFormatter() | 99.999-999
| CPF | CpfInputFormatter() | 999.999.99-99
| CNPJ | CnpjInputFormatter() | 99.999.999/9999-99
| CPF / CNPJ | CpfOuCnpjFormatter() | se adapta conforme os números são inseridos
| CEST | CESTInputFormatter() | 12.345.67
| CNS | CNSInputFormatter() | 111 2222 3333 4444
| Data | DataInputFormatter() | 01/01/1900
| Hora | HoraInputFormatter() | 23:59
| IOF | HoraInputFormatter() | 1,234567
| KM | KmInputFormatter() | 999.999
| Cert. nascimento | CertNascimentoInputFormatter() | 000000 11 22 3333 4 55555 666 7777777 88
| NCM | NCMInputFormatter() | 1234.56.78
| Peso | PesoInputFormatter() | 111,1
| Placa | PlacaVeiculoInputFormatter() | AAA-1234 (**não** utilizar `FilteringTextInputFormatter.digitsOnly`)
| Real | RealInputFormatter() | 20.550
| Telefone | TelefoneInputFormatter() | (99) 9999-9999
| Validade cartão | ValidadeCartaoInputFormatter() | 12/24 ou 12/2024
| Temperatura | TemperaturaInputFormatter() |27,1
| Padrão | Formatter | Formato |
|:-----------------|:-------------------------------|:---------------------------------------------------------------------|
| Altura | AlturaInputFormatter() | 2,22 |
| Cartão | CartaoBancarioInputFormatter() | 0000 1111 2222 3333 4444 |
| Centavos | CentavosInputFormatter() | 7,194 |
| CEP | CepInputFormatter() | 99.999-999 |
| CPF | CpfInputFormatter() | 999.999.99-99 |
| CNPJ | CnpjInputFormatter() | 99.999.999/9999-99 |
| CPF / CNPJ | CpfOuCnpjFormatter() | se adapta conforme os números são inseridos |
| CEST | CESTInputFormatter() | 12.345.67 |
| CNS | CNSInputFormatter() | 111 2222 3333 4444 |
| Data | DataInputFormatter() | 01/01/1900 |
| Hora | HoraInputFormatter() | 23:59 |
| IOF | HoraInputFormatter() | 1,234567 |
| KM | KmInputFormatter() | 999.999 |
| Cert. nascimento | CertNascimentoInputFormatter() | 000000 11 22 3333 4 55555 666 7777777 88 |
| NCM | NCMInputFormatter() | 1234.56.78 |
| NUP | NUPInputFormatter() | 1234567-89.0123.4.56.7890 |
| Peso | PesoInputFormatter() | 111,1 |
| Placa | PlacaVeiculoInputFormatter() | AAA-1234 (**não** utilizar `FilteringTextInputFormatter.digitsOnly`) |
| Real | RealInputFormatter() | 20.550 |
| Telefone | TelefoneInputFormatter() | (99) 9999-9999 |
| Validade cartão | ValidadeCartaoInputFormatter() | 12/24 ou 12/2024 |
| Temperatura | TemperaturaInputFormatter() | 27,1 |

### Modelos

Expand Down Expand Up @@ -97,6 +98,7 @@ Métodos que facilitam manipular valores:
- `UtilBrasilFields.obterCnpj('11222333444455')` (11.222.333/4444-55)
- `UtilBrasilFields.obterCep('11222333')` (11.222-333)
- `UtilBrasilFields.obterCep('11222333', ponto: false)` (11222-333)
- `UtilBrasilFields.obterNUP('06010642120226000000')` (0601064-21.2022.6.00.0000)
- `UtilBrasilFields.obterTelefone('00999998877')` ((00) 99999-8877)
- `UtilBrasilFields.obterTelefone('(00) 99999-8877', mascara: false)` (00999998877)
- `UtilBrasilFields.obterTelefone('999998877', ddd: false)` (99999-8877)
Expand All @@ -112,6 +114,7 @@ Métodos que facilitam manipular valores:
- `UtilBrasilFields.converterMoedaParaDouble` (remove o R$ e retorna um double)
- `UtilBrasilFields.isCPFValido` (retorna `true` se o CPF for válido, caso contrário, retorna `false`)
- `UtilBrasilFields.isCNPJValido` (retorna `true` se o CNPJ for válido, caso contrário, retorna `false`)
- `UtilBrasilFields.isNUPValido` (retorna `true` se o NUP for válido, caso contrário, retorna `false`)

Para inicializar um `TextEditingController` com o texto já formatado, basta escolher o método com o formato desejado e setar no atributo `text`:

Expand Down
4 changes: 4 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ class MyApp extends StatelessWidget {
label: 'NCM',
formatter: NCMInputFormatter(),
),
RowFormatters(
label: 'NUP',
formatter: NUPInputFormatter(),
),
RowFormatters(
label: 'CEST',
formatter: CESTInputFormatter(),
Expand Down
1 change: 1 addition & 0 deletions lib/brasil_fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export 'src/formatters/hora_input_formatter.dart';
export 'src/formatters/iof_input_formatter.dart';
export 'src/formatters/km_input_formatter.dart';
export 'src/formatters/ncm_input_formatter.dart';
export 'src/formatters/nup_input_formatter.dart';
export 'src/formatters/peso_input_formatter.dart';
export 'src/formatters/placa_veiculo_formatter.dart';
export 'src/formatters/real_input_formatter.dart';
Expand Down
46 changes: 46 additions & 0 deletions lib/src/formatters/nup_input_formatter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:flutter/services.dart';

/// Formata o valor do campo com a máscara de NUP (Numeração Única de Processos): `XXXXXXX-XX.XXXX.X.XX.XXXX`
/// Referência: [Documentação CNJ](https://www.cnj.jus.br/programas-e-acoes/numeracao-unica/)
class NUPInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
// verifica o tamanho máximo do campo
if (newValue.text.length > 20) return oldValue;

var posicaoCursor = newValue.selection.end;
var substrIndex = 0;
final valorFinal = StringBuffer();

if (newValue.text.length >= 8) {
valorFinal.write('${newValue.text.substring(0, substrIndex = 7)}-');
if (newValue.selection.end >= 7) posicaoCursor++;
}
if (newValue.text.length >= 10) {
valorFinal.write('${newValue.text.substring(7, substrIndex = 9)}.');
if (newValue.selection.end >= 9) posicaoCursor++;
}
if (newValue.text.length >= 14) {
valorFinal.write('${newValue.text.substring(9, substrIndex = 13)}.');
if (newValue.selection.end >= 13) posicaoCursor++;
}
if (newValue.text.length >= 15) {
valorFinal.write('${newValue.text.substring(13, substrIndex = 14)}.');
if (newValue.selection.end >= 14) posicaoCursor++;
}
if (newValue.text.length >= 17) {
valorFinal.write('${newValue.text.substring(14, substrIndex = 16)}.');
if (newValue.selection.end >= 16) posicaoCursor++;
}

if (newValue.text.length >= substrIndex) {
valorFinal.write(newValue.text.substring(substrIndex));
}

return TextEditingValue(
text: valorFinal.toString(),
selection: TextSelection.collapsed(offset: posicaoCursor),
);
}
}
10 changes: 10 additions & 0 deletions lib/src/util/util_brasil_fields.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '../formatters/adiciona_separador.dart';
import '../validators/nup_validator.dart';
import '../validators/validators.dart';

class UtilBrasilFields {
Expand Down Expand Up @@ -76,6 +77,9 @@ class UtilBrasilFields {
///Faz a validação do CNPJ retornando `[true]` ou `[false]`
static bool isCNPJValido(String? cnpj) => CNPJValidator.isValid(cnpj);

///Faz a validação do NUP retornando `[true]` ou `[false]`
static bool isNUPValido(String? nup) => NUPValidator.isValid(nup);

/// Gera um CPF aleatório
///
/// Formatado ou não formatado, baseado no parâmetro `useFormat`:
Expand Down Expand Up @@ -139,6 +143,12 @@ class UtilBrasilFields {
return CNPJValidator.strip(cnpj).substring(12);
}

/// Retorna o NUP informado, utilizando a máscara: `NNNNNNN-DD.AAAA.J.TR.OOOO`
static String obterNUP(String nup) {
assert(isNUPValido(nup), 'Número de Processo inválido!');
return NUPValidator.format(nup);
}

/// Retorna o número real informado, utilizando a máscara: `R$ 50.000,00` ou `50.000,00`
static String obterReal(double value, {bool moeda = true, int decimal = 2}) {
bool isNegative = false;
Expand Down
52 changes: 52 additions & 0 deletions lib/src/validators/nup_validator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class NUPValidator {
static const stripRegex = r'[^\d]';

static bool isValid(String? nup, {stripBeforeValidation = true}) {
if (stripBeforeValidation) {
nup = strip(nup);
}

if (nup == null || nup.isEmpty) {
return false;
}

if (nup.length != 20) {
return false;
}

final checkDigit = _checkDigit(nup);
return nup.substring(7, 9) == checkDigit.toString();
}

static String strip(String? nup) {
var regex = RegExp(stripRegex);
nup = nup ?? '';

return nup.replaceAll(regex, '');
}

// Compute the Check Digit (or 'Dígito Verificador (DV)' in PT-BR).
// You can learn more about the algorithm on [CNJ (pt-br)](https://atos.cnj.jus.br/files/compilado23285720221017634de539229ab.pdf)
static String _checkDigit(String nup) {
final sequential = nup.substring(0, 7);
final year = nup.substring(9, 13);
final segment = nup[13];
final court = nup.substring(14, 16);
final origin = nup.substring(16);

final r1 = int.parse(sequential) % 97;
final r2 = int.parse('$r1$year$segment$court') % 97;
final r3 = int.parse('$r2${origin}00') % 97;

final checkDigit = 98 - r3;
return checkDigit.toString().padLeft(2, '0');
}

static String format(String nup) {
var regExp = RegExp(r'^(\d{7})(\d{2})(\d{4})(\d{1})(\d{2})(\d{4})$');

return strip(nup).replaceAllMapped(
regExp, (Match m) => '${m[1]}-${m[2]}.${m[3]}.${m[4]}.${m[5]}.${m[6]}');
}

}
9 changes: 9 additions & 0 deletions test/brasil_fields_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:brasil_fields/brasil_fields.dart';
import 'package:brasil_fields/src/formatters/compound_formatters/compound_formatter.dart';
import 'package:brasil_fields/src/formatters/nup_input_formatter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -333,6 +334,14 @@ void main() {
expect(textController.text, '0309.90.00');
});

testWidgets('NUPInputFormatter', (WidgetTester tester) async {
final textController = TextEditingController();

await tester.pumpWidget(boilerplate(NUPInputFormatter(), textController));
await tester.enterText(find.byType(TextField), '12345678901234567890');
expect(textController.text, '1234567-89.0123.4.56.7890');
});

testWidgets('CESTInputFormatter', (WidgetTester tester) async {
final textController = TextEditingController();

Expand Down
28 changes: 28 additions & 0 deletions test/nup_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:brasil_fields/src/validators/nup_validator.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('Test NUP validator', () {
expect(NUPValidator.isValid('0601064-21.2022.6.00.0000'), true);
expect(NUPValidator.isValid('0601064-22.2022.6.00.0000'), false);
expect(NUPValidator.isValid('00601064-22.2022.6.00.0000'), false);
expect(NUPValidator.isValid('06010642120226000000'), true);
expect(NUPValidator.isValid('06010642220226000000'), false);
expect(NUPValidator.isValid('006010642120226000000'), false);
expect(
NUPValidator.isValid('03346teste1671002@mail',
stripBeforeValidation: false),
false);
expect(
NUPValidator.isValid('57abc803.6586-52', stripBeforeValidation: false),
false);
});

test('Test NUP formatter', () {
expect(NUPValidator.format('06010642120226000000'), '0601064-21.2022.6.00.0000');
});

test('Test NUP strip', () {
expect(NUPValidator.strip('0601064-21.2022.6.00.0000'), '06010642120226000000');
});
}
63 changes: 63 additions & 0 deletions test/src/formatters/nup_input_formatter_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'package:brasil_fields/src/formatters/nup_input_formatter.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
evaluate(String oldValue, String newValue) {
return NUPInputFormatter()
.formatEditUpdate(
TextEditingValue(text: oldValue),
TextEditingValue(text: newValue),
)
.text;
}

group('NUPInputFormatter', () {
test('padrao', () => expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890'));
test('limite 20 digitos', () => expect(evaluate('', '123456789012345678901'), ''));
test('backspace', () {
expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890');
expect(evaluate('', '1234567890123456789'), '1234567-89.0123.4.56.789');
expect(evaluate('', '123456789012345678'), '1234567-89.0123.4.56.78');
expect(evaluate('', '12345678901234567'), '1234567-89.0123.4.56.7');
expect(evaluate('', '1234567890123456'), '1234567-89.0123.4.56');
expect(evaluate('', '123456789012345'), '1234567-89.0123.4.5');
expect(evaluate('', '12345678901234'), '1234567-89.0123.4');
expect(evaluate('', '1234567890123'), '1234567-89.0123');
expect(evaluate('', '123456789012'), '1234567-89.012');
expect(evaluate('', '12345678901'), '1234567-89.01');
expect(evaluate('', '1234567890'), '1234567-89.0');
expect(evaluate('', '123456789'), '1234567-89');
expect(evaluate('', '12345678'), '1234567-8');
expect(evaluate('', '1234567'), '1234567');
expect(evaluate('', '123456'), '123456');
expect(evaluate('', '12345'), '12345');
expect(evaluate('', '1234'), '1234');
expect(evaluate('', '123'), '123');
expect(evaluate('', '12'), '12');
expect(evaluate('', '1'), '1');
});

test('digitacao', () {
expect(evaluate('', '1'), '1');
expect(evaluate('', '12'), '12');
expect(evaluate('', '123'), '123');
expect(evaluate('', '1234'), '1234');
expect(evaluate('', '12345'), '12345');
expect(evaluate('', '123456'), '123456');
expect(evaluate('', '1234567'), '1234567');
expect(evaluate('', '12345678'), '1234567-8');
expect(evaluate('', '123456789'), '1234567-89');
expect(evaluate('', '1234567890'), '1234567-89.0');
expect(evaluate('', '12345678901'), '1234567-89.01');
expect(evaluate('', '123456789012'), '1234567-89.012');
expect(evaluate('', '1234567890123'), '1234567-89.0123');
expect(evaluate('', '12345678901234'), '1234567-89.0123.4');
expect(evaluate('', '123456789012345'), '1234567-89.0123.4.5');
expect(evaluate('', '1234567890123456'), '1234567-89.0123.4.56');
expect(evaluate('', '12345678901234567'), '1234567-89.0123.4.56.7');
expect(evaluate('', '123456789012345678'), '1234567-89.0123.4.56.78');
expect(evaluate('', '1234567890123456789'), '1234567-89.0123.4.56.789');
expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890');
});
});
}
11 changes: 11 additions & 0 deletions test/util_brasil_fields_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ void main() {
expect(UtilBrasilFields.removeCaracteres(cep), '11222333');
});

test('NUP', () {
const nup = '0601064-21.2022.6.00.0000';
expect(UtilBrasilFields.removeCaracteres(nup), '06010642120226000000');
});

test('Real', () {
const real = '11.222';
expect(UtilBrasilFields.removeCaracteres(real), '11222');
Expand Down Expand Up @@ -263,6 +268,12 @@ void main() {
expect(UtilBrasilFields.obterCnpjDiv(cpnjComMascara), '90');
});

test('Obter NUP', () {
const nupSemMascara = '06010642120226000000';
const nupComMascara = '0601064-21.2022.6.00.0000';
expect(UtilBrasilFields.obterNUP(nupSemMascara), nupComMascara);
});

group('Obter Real', () {
test('com moeda (R\$)', () {
const real = 85437107.04;
Expand Down

0 comments on commit e14f811

Please sign in to comment.