diff --git a/app/templates/gerador_chave.html b/app/templates/gerador_chave.html
index f6d9042..f4f9cf3 100644
--- a/app/templates/gerador_chave.html
+++ b/app/templates/gerador_chave.html
@@ -1,15 +1,31 @@
{% extends "base.html" %}
-{% block titulo %}Gerador de Chave de Reativação{% endblock %}
+{% block titulo %}Gerador de Chave{% endblock %}
{% block conteudo %}

-
Gerador de Chave de Reativação
+
Gerador de Chave
+
+
+
-
-
+
+
+
+
+
+
+
+
+
@@ -23,6 +39,9 @@ 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);
@@ -40,8 +59,39 @@ function gerarReativacao(codigoCliente, mes, ano, byteInicial = null) {
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');
@@ -51,6 +101,12 @@ const $copiar = document.getElementById('copiar');
$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';
@@ -63,13 +119,19 @@ document.getElementById('gerar').addEventListener('click', () => {
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');
- 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).');
- const chave = gerarReativacao(codigoFormatado, mesNum, anoNum);
+ 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';
diff --git a/tests/test_router_chave_manual.py b/tests/test_router_chave_manual.py
index d4a786a..a690dd3 100644
--- a/tests/test_router_chave_manual.py
+++ b/tests/test_router_chave_manual.py
@@ -14,7 +14,8 @@ def test_pagina_gerador_carrega():
resposta = client.get("/gerador-chave")
assert resposta.status_code == 200
assert "gerarReativacao" in resposta.text
- assert "Gerador de Chave de Reativação" in resposta.text
+ assert "gerarInstalacao" in resposta.text
+ assert "Gerador de Chave" in resposta.text
def test_pagina_gerador_nao_grava_nada():