feat: validação de CNPJ e máscaras de telefone/UF/CEP no formulário de cliente

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-14 12:21:15 -03:00
co-authored by Claude Sonnet 5
parent 59f15d9ac0
commit eec7ce21b5
+108 -1
View File
@@ -51,7 +51,7 @@
{% endfor %} {% endfor %}
</div> </div>
<p><small>* campos obrigatórios. Os demais podem ficar em branco — implantação/mensalidade/juros ficam 0,00 e as datas de contrato ficam com a data de hoje até serem ajustadas.</small></p> <p><small>* campos obrigatórios. Os demais podem ficar em branco — implantação/mensalidade/juros ficam 0,00 e as datas de contrato ficam com a data de hoje até serem ajustadas.</small></p>
<button type="submit">Salvar</button> <button type="submit" id="botao-salvar">Salvar</button>
</form> </form>
</div> </div>
@@ -65,6 +65,113 @@
var $status = document.getElementById('cnpj-status'); var $status = document.getElementById('cnpj-status');
if (!$cnpj) return; if (!$cnpj) return;
var $botaoSalvar = document.getElementById('botao-salvar');
// Mesma fórmula de dígito verificador de app/normalize.py
// (_digito_verificador + validar_dv_cnpj) — módulo 11 sobre valor ASCII
// − 48 de cada caractere, suporta CNPJ numérico e o alfanumérico
// vigente desde 31/07/2026.
var PESOS_CNPJ_DV1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
var PESOS_CNPJ_DV2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
function digitoVerificador(valores, pesos) {
var soma = 0;
for (var i = 0; i < valores.length; i++) soma += valores[i] * pesos[i];
var resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
function valorCaractere(c) {
return c.charCodeAt(0) - 48;
}
function validarDvCnpj(cnpj14) {
var base = [];
for (var i = 0; i < 12; i++) base.push(valorCaractere(cnpj14[i]));
var dvInformado = [parseInt(cnpj14[12], 10), parseInt(cnpj14[13], 10)];
var dv1 = digitoVerificador(base, PESOS_CNPJ_DV1);
var dv2 = digitoVerificador(base.concat([dv1]), PESOS_CNPJ_DV2);
return dvInformado[0] === dv1 && dvInformado[1] === dv2;
}
function detectarEValidarDocumento() {
var limpo = normalizarCnpj($cnpj.value);
if ($botaoSalvar) $botaoSalvar.disabled = false;
if (/^[0-9]{11}$/.test(limpo)) {
if ($status) {
$status.textContent = 'CPF detectado.';
$status.style.color = 'var(--cor-texto-secundario)';
}
return;
}
if (/^[0-9A-Z]{12}[0-9]{2}$/.test(limpo)) {
if (validarDvCnpj(limpo)) {
if ($status) {
$status.textContent = 'CNPJ detectado.';
$status.style.color = 'var(--cor-texto-secundario)';
}
} else {
if ($status) {
$status.textContent = 'CNPJ inválido (dígito verificador não confere).';
$status.style.color = 'var(--cor-perigo)';
}
if ($botaoSalvar) $botaoSalvar.disabled = true;
}
return;
}
// Tamanho ainda incompleto/indeterminado: não bloqueia, não rotula.
if ($status) $status.textContent = '';
}
$cnpj.addEventListener('input', detectarEValidarDocumento);
function aplicarMascaraTelefone(elemento, digitosAposDdd) {
elemento.addEventListener('input', function () {
var digitos = elemento.value.replace(/\D/g, '');
// Se veio com DDI 55 colado (12-13 dígitos totais), usa só o resto.
var totalComDdi = 2 + digitosAposDdd; // DDI(2) + DDD(2) já contado em digitosAposDdd
if (digitos.length >= totalComDdi && digitos.slice(0, 2) === '55') {
digitos = digitos.slice(2);
}
digitos = digitos.slice(0, digitosAposDdd);
var ddd = digitos.slice(0, 2);
var resto = digitos.slice(2);
var meio = resto.slice(0, digitosAposDdd - 6);
var fim = resto.slice(digitosAposDdd - 6);
var formatado = '';
if (ddd) formatado += '(' + ddd;
if (ddd.length === 2) formatado += ') ';
formatado += meio;
if (fim) formatado += '-' + fim;
elemento.value = formatado;
});
}
var $telefone = document.getElementById('TELEFONE');
var $celular = document.getElementById('CELULAR');
// TELEFONE: DDD(2) + 8 dígitos = 10 dígitos após o DDI.
if ($telefone) aplicarMascaraTelefone($telefone, 10);
// CELULAR: DDD(2) + 9 dígitos (com o 9 na frente) = 11 dígitos após o DDI.
if ($celular) aplicarMascaraTelefone($celular, 11);
var $uf = document.getElementById('UF');
if ($uf) {
$uf.addEventListener('input', function () {
$uf.value = $uf.value.toUpperCase().replace(/[^A-Z]/g, '').slice(0, 2);
});
}
var $cep = document.getElementById('CEP');
if ($cep) {
$cep.addEventListener('input', function () {
var digitos = $cep.value.replace(/\D/g, '').slice(0, 8);
$cep.value = digitos.length > 5 ? digitos.slice(0, 5) + '-' + digitos.slice(5) : digitos;
});
}
var CAMPOS_PARA_PREENCHER = ['RAZAO', 'FANTASIA', 'ENDERECO', 'CIDADE', 'BAIRRO', 'UF', 'CEP', 'TELEFONE', 'EMAIL']; var CAMPOS_PARA_PREENCHER = ['RAZAO', 'FANTASIA', 'ENDERECO', 'CIDADE', 'BAIRRO', 'UF', 'CEP', 'TELEFONE', 'EMAIL'];
function normalizarCnpj(valor) { function normalizarCnpj(valor) {