Files
GTI_GERENCIADOR_FISCAL/app/templates/gerador_chave.html
T
GustavoandClaude Sonnet 5 a3a84abd37 fix: campo codigo do cliente so aceita numeros + botoes de tabela nao quebram linha
Gerador de chave: filtra caracteres nao numericos do campo codigo em
tempo real (input event), alem da validacao ja existente no submit.

Tabelas: a.botao/button ganham white-space: nowrap, corrigindo o
botao 'Abrir' (e outros) quebrando em duas linhas depois que as
celulas de dado passaram a permitir quebra de texto longo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 09:19:44 -03:00

151 lines
6.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block titulo %}Gerador de Chave{% endblock %}
{% block conteudo %}
<div class="cartao" style="max-width:420px;margin:0 auto;">
<h1 style="text-align:center;">Gerador de Chave</h1>
<label for="tipo">Tipo de chave</label>
<select id="tipo">
<option value="reativacao">Reativação (mês/ano)</option>
<option value="instalacao">Instalação (aluguel/compra)</option>
</select>
<label for="codigo">Código do cliente</label>
<input type="text" id="codigo" placeholder="Ex: 000123" maxlength="6" inputmode="numeric">
<div id="campos-reativacao">
<label for="referencia">Mês/ano de referência</label>
<input type="month" id="referencia" min="2000-01" max="2100-12">
</div>
<div id="campos-instalacao" style="display:none;">
<label for="modalidade">Modalidade</label>
<select id="modalidade">
<option value="ALUGUEL">Aluguel</option>
<option value="COMPRA">Compra</option>
</select>
</div>
<button id="gerar">Gerar chave</button>
<div id="erro" style="color:var(--cor-perigo);margin-top:14px;display:none;"></div>
<div id="resultado" style="margin-top:20px;padding:14px;border-radius:8px;background:var(--cor-superficie-alt);border:1px solid var(--cor-borda);border-left:3px solid var(--cor-acento);word-break:break-all;font-family:'Courier New',monospace;color:var(--cor-acento-texto);display:none;"></div>
<button id="copiar" style="display:none;background:transparent;border:1px solid var(--cor-borda);color:var(--cor-texto-secundario);width:auto;padding:6px 10px;">Copiar</button>
</div>
<script>
const CHAVE_INTERNA =
"YUQL23KL23DF90WI5E1JAS467NMCXXL6JAOAUWWMCL0AOMM4A4VZYW9KHJUI2347" +
"EJHJKDF3424SKL K3LAKDJSL9RTIKJ";
// ==========================
// REATIVAÇÃO — mês/cliente/ano cifrados por inteiro (22 caracteres)
// ==========================
function gerarReativacao(codigoCliente, mes, ano, byteInicial = null) {
const cliente = String(codigoCliente).padStart(6, '0');
const texto = String(mes).padStart(2, '0') + cliente + String(ano).slice(-2);
const aleatorio = byteInicial !== null ? byteInicial : Math.floor(Math.random() * 256);
let anterior = aleatorio;
const resultado = [aleatorio];
const bytes = [];
for (let i = 0; i < texto.length; i++) bytes.push(texto.charCodeAt(i) & 0xff);
for (let i = 0; i < bytes.length; i++) {
const valor = (bytes[i] + anterior) % 255;
const cifrado = valor ^ CHAVE_INTERNA.charCodeAt(i % CHAVE_INTERNA.length);
resultado.push(cifrado);
anterior = cifrado;
}
return resultado.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join('');
}
// ==========================
// INSTALAÇÃO — só a modalidade ("30"=ALUGUEL / "00"=COMPRA) é cifrada
// (mesmo algoritmo de cifra da reativação, texto diferente); o valor
// derivado do código do cliente (cliente×19+31) entra sem cifrar, no
// meio dos 6 caracteres cifrados (3 antes + 3 depois).
// ==========================
function gerarInstalacao(codigoCliente, modalidade, byteInicial = null) {
const clienteNum = parseInt(String(codigoCliente), 10) || 0;
const valorCliente = clienteNum * 19 + 31;
const textoModalidade = modalidade === 'ALUGUEL' ? '30' : '00';
const aleatorio = byteInicial !== null ? byteInicial : Math.floor(Math.random() * 256);
let anterior = aleatorio;
const bytes = [aleatorio];
for (let i = 0; i < textoModalidade.length; i++) {
const valor = (textoModalidade.charCodeAt(i) + anterior) % 255;
const cifrado = valor ^ CHAVE_INTERNA.charCodeAt(i % CHAVE_INTERNA.length);
bytes.push(cifrado);
anterior = cifrado;
}
const criptografado = bytes.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join('');
const parte1 = criptografado.substring(0, 3);
const parte2 = criptografado.substring(3, 6);
return `${parte1}${valorCliente}${parte2}`;
}
const $tipo = document.getElementById('tipo');
const $codigo = document.getElementById('codigo');
const $referencia = document.getElementById('referencia');
const $modalidade = document.getElementById('modalidade');
const $camposReativacao = document.getElementById('campos-reativacao');
const $camposInstalacao = document.getElementById('campos-instalacao');
const $erro = document.getElementById('erro');
const $resultado = document.getElementById('resultado');
const $copiar = document.getElementById('copiar');
$codigo.addEventListener('input', () => {
$codigo.value = $codigo.value.replace(/\D/g, '');
});
(function preencherMesAtual() {
const hoje = new Date();
$referencia.value = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`;
})();
$tipo.addEventListener('change', () => {
const ehReativacao = $tipo.value === 'reativacao';
$camposReativacao.style.display = ehReativacao ? 'block' : 'none';
$camposInstalacao.style.display = ehReativacao ? 'none' : 'block';
});
function mostrarErro(msg) {
$erro.textContent = msg;
$erro.style.display = 'block';
$resultado.style.display = 'none';
$copiar.style.display = 'none';
}
document.getElementById('gerar').addEventListener('click', () => {
$erro.style.display = 'none';
const codigoCliente = String($codigo.value || '').trim();
if (!/^[0-9]{1,6}$/.test(codigoCliente)) return mostrarErro('Código do cliente deve ter até 6 dígitos numéricos.');
const codigoFormatado = codigoCliente.padStart(6, '0');
let chave;
if ($tipo.value === 'reativacao') {
if (!/^\d{4}-\d{2}$/.test($referencia.value)) return mostrarErro('Selecione o mês/ano de referência.');
const [anoStr, mesStr] = $referencia.value.split('-');
const mesNum = Number(mesStr), anoNum = Number(anoStr);
if (!(mesNum >= 1 && mesNum <= 12)) return mostrarErro('Mês fora do intervalo 1-12.');
if (!(anoNum >= 2000 && anoNum <= 2100)) return mostrarErro('Ano inválido (use entre 2000 e 2100).');
chave = gerarReativacao(codigoFormatado, mesNum, anoNum);
} else {
chave = gerarInstalacao(codigoFormatado, $modalidade.value);
}
$resultado.textContent = chave;
$resultado.style.display = 'block';
$copiar.style.display = 'inline-block';
});
$copiar.addEventListener('click', () => {
navigator.clipboard.writeText($resultado.textContent).then(() => {
$copiar.textContent = 'Copiado!';
setTimeout(() => { $copiar.textContent = 'Copiar'; }, 1500);
});
});
</script>
{% endblock %}