portal_publico/static/js/custo-contratacao.js

321 lines
13 KiB
JavaScript

document.addEventListener("DOMContentLoaded", async () => {
const formEl = document.getElementById("cc-form");
if (!formEl) return;
const me = await pidGetMe();
if (!me) return;
const geradocPerms = (me.permissoes_efetivas && me.permissoes_efetivas["geradoc"]) || {};
const canUse = !!(geradocPerms.apps && geradocPerms.apps["simulacao-custo-contratacao"]);
const noAccessEl = document.getElementById("cc-no-access");
const contentEl = document.getElementById("cc-content");
if (noAccessEl) noAccessEl.hidden = canUse;
if (contentEl) contentEl.hidden = !canUse;
if (!canUse) return;
const errorEl = document.getElementById("cc-form-error");
const submitBtn = document.getElementById("cc-form-submit-btn");
const limparBtn = document.getElementById("cc-form-limpar-btn");
const campoValor = (id) => document.getElementById(id).value.trim();
function parseValorBr(texto) {
if (!texto) return 0;
const numero = parseFloat(String(texto).trim().replace(/\./g, "").replace(",", "."));
return isNaN(numero) ? 0 : numero;
}
function formataValorBr(numero) {
return numero.toFixed(2).replace(".", ",");
}
/**
* Converte um texto que pode ser um valor em reais (ex.: "3.400,00") ou um
* percentual do salário contratual (ex.: "40%") pro valor em reais final —
* usado nos campos que aceitam as duas formas (Adicional Art. 62. e os
* campos da seção Descontos). Se o texto terminar em "%", calcula o valor
* a partir do salário antes de enviar pra API, que sempre espera um valor
* monetário nesses campos.
*/
function converteValorOuPercentual(bruto) {
if (!bruto) return "0";
if (bruto.endsWith("%")) {
const percentual = parseValorBr(bruto.slice(0, -1));
const salario = parseValorBr(campoValor("cc-salario"));
return formataValorBr((percentual / 100) * salario);
}
return bruto;
}
function valorOuPercentualDoSalario(id) {
return converteValorOuPercentual(campoValor(id));
}
function criarLinhaItem(nome, valor) {
const tr = document.createElement("tr");
tr.innerHTML =
'<td><input type="text" class="cc-tabela-fiscal__input" data-campo="nome" autocomplete="off" /></td>' +
'<td><input type="text" class="cc-tabela-fiscal__input" data-campo="valor" autocomplete="off" /></td>' +
'<td><button type="button" class="icon-btn cc-tabela-fiscal__remove" aria-label="Remover" title="Remover">✕</button></td>';
tr.querySelector('[data-campo="nome"]').value = nome || "";
tr.querySelector('[data-campo="valor"]').value = valor || "";
tr.querySelector(".cc-tabela-fiscal__remove").addEventListener("click", () => tr.remove());
return tr;
}
function lerItensDoTbody(tbodyEl, { comPercentual } = {}) {
return Array.from(tbodyEl.children)
.map((tr) => {
const nome = tr.querySelector('[data-campo="nome"]').value.trim();
const valorBruto = tr.querySelector('[data-campo="valor"]').value.trim();
return { nome, valor: comPercentual ? converteValorOuPercentual(valorBruto) : valorBruto || "0" };
})
.filter((item) => item.nome);
}
const remuneracaoOutrosBody = document.getElementById("cc-remuneracao-outros-body");
const beneficiosOutrosBody = document.getElementById("cc-beneficios-outros-body");
const descontosOutrosBody = document.getElementById("cc-descontos-outros-body");
const remuneracaoAddBtn = document.getElementById("cc-remuneracao-outros-add-btn");
if (remuneracaoAddBtn) {
remuneracaoAddBtn.addEventListener("click", () => {
remuneracaoOutrosBody.appendChild(criarLinhaItem());
});
}
const beneficiosAddBtn = document.getElementById("cc-beneficios-outros-add-btn");
if (beneficiosAddBtn) {
beneficiosAddBtn.addEventListener("click", () => {
beneficiosOutrosBody.appendChild(criarLinhaItem());
});
}
const descontosAddBtn = document.getElementById("cc-descontos-outros-add-btn");
if (descontosAddBtn) {
descontosAddBtn.addEventListener("click", () => {
descontosOutrosBody.appendChild(criarLinhaItem());
});
}
function montaPayload() {
return {
nome_referencia: campoValor("cc-nome-referencia"),
empresa: campoValor("cc-empresa"),
cargo: campoValor("cc-cargo"),
salario_contratual: campoValor("cc-salario"),
adicional_art62: valorOuPercentualDoSalario("cc-adicional"),
comissao: campoValor("cc-comissao") || "0",
dsr: campoValor("cc-dsr") || "0",
outros_itens_remuneracao: lerItensDoTbody(remuneracaoOutrosBody),
percentual_inss_empregador: campoValor("cc-perc-inss"),
percentual_fgts: campoValor("cc-perc-fgts"),
dependentes: Number(campoValor("cc-dependentes")) || 0,
vale_transporte: campoValor("cc-vale-transporte") || "0",
seguro_vida: campoValor("cc-seguro-vida") || "0",
vale_alimentacao: campoValor("cc-vale-alimentacao") || "0",
assiduidade: campoValor("cc-assiduidade") || "0",
fundo_formacao: campoValor("cc-fundo-formacao") || "0",
outros_beneficios: lerItensDoTbody(beneficiosOutrosBody),
desconto_vale_transporte: valorOuPercentualDoSalario("cc-desconto-vale-transporte"),
desconto_vale_alimentacao: valorOuPercentualDoSalario("cc-desconto-vale-alimentacao"),
desconto_sindicato: valorOuPercentualDoSalario("cc-desconto-sindicato"),
outros_descontos: lerItensDoTbody(descontosOutrosBody, { comPercentual: true }),
};
}
/**
* A resposta é o PDF binário, não JSON — por isso não passa por
* pidApiRequest. Reaproveita pidEnsureCsrfCookie/pidGetCookie/pidErrorMessageFrom
* de api.js (mesmo padrão de pidGerarArquivoPlanoSaude em
* importacao-plano-saude.js), mas abre o resultado numa aba nova em vez de
* forçar download — a ideia é já deixar o PDF pronto pra visualizar/imprimir/enviar.
*/
async function geraPdfSimulacao(payload) {
await pidEnsureCsrfCookie();
const response = await fetch("/api/simulacao-custo-contratacao/gerar/", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": pidGetCookie("csrftoken") || "",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
let mensagem = "Não foi possível gerar o PDF.";
try {
mensagem = pidErrorMessageFrom(await response.json());
} catch (e) {
// resposta sem corpo JSON — mantém a mensagem genérica
}
throw new Error(mensagem);
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
}
formEl.addEventListener("submit", async (event) => {
event.preventDefault();
errorEl.textContent = "";
const payload = montaPayload();
if (!payload.salario_contratual) {
errorEl.textContent = "Informe o salário contratual.";
return;
}
if (!payload.percentual_inss_empregador || !payload.percentual_fgts) {
errorEl.textContent = "Informe o % de INSS Empregador e o % de FGTS.";
return;
}
submitBtn.disabled = true;
submitBtn.textContent = "Gerando...";
try {
await geraPdfSimulacao(payload);
} catch (e) {
errorEl.textContent = e.message;
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Gerar PDF";
}
});
if (limparBtn) {
limparBtn.addEventListener("click", () => {
formEl.reset();
remuneracaoOutrosBody.innerHTML = "";
beneficiosOutrosBody.innerHTML = "";
descontosOutrosBody.innerHTML = "";
errorEl.textContent = "";
});
}
// ---- Painel "Ver/editar tabelas de INSS e IRRF" ----
const painelToggle = document.getElementById("cc-painel-fiscal-toggle");
const painelBody = document.getElementById("cc-painel-fiscal-body");
const painelError = document.getElementById("cc-painel-fiscal-error");
const painelStatus = document.getElementById("cc-painel-fiscal-status");
const painelSalvarBtn = document.getElementById("cc-painel-fiscal-salvar-btn");
const inssBody = document.getElementById("cc-tabela-inss-body");
const irrfBody = document.getElementById("cc-tabela-irrf-body");
if (painelToggle) {
painelToggle.addEventListener("click", () => {
const expandido = painelToggle.getAttribute("aria-expanded") === "true";
painelToggle.setAttribute("aria-expanded", String(!expandido));
painelBody.hidden = expandido;
});
}
function criarLinhaFaixa(faixa) {
const tr = document.createElement("tr");
const limite = formataValorBr(faixa.limite_superior);
const aliquota = formataValorBr(faixa.aliquota * 100);
const deduzir = formataValorBr(faixa.deduzir);
tr.innerHTML =
'<td><input type="text" class="cc-tabela-fiscal__input" data-campo="limite_superior" autocomplete="off" /></td>' +
'<td><input type="text" class="cc-tabela-fiscal__input" data-campo="aliquota" autocomplete="off" /></td>' +
'<td><input type="text" class="cc-tabela-fiscal__input" data-campo="deduzir" autocomplete="off" /></td>' +
'<td><button type="button" class="icon-btn cc-tabela-fiscal__remove" aria-label="Remover faixa" title="Remover faixa">✕</button></td>';
tr.querySelector('[data-campo="limite_superior"]').value = limite;
tr.querySelector('[data-campo="aliquota"]').value = aliquota;
tr.querySelector('[data-campo="deduzir"]').value = deduzir;
tr.querySelector(".cc-tabela-fiscal__remove").addEventListener("click", () => tr.remove());
return tr;
}
function renderFaixas(tbodyEl, faixas) {
tbodyEl.innerHTML = "";
faixas.forEach((faixa) => tbodyEl.appendChild(criarLinhaFaixa(faixa)));
}
function lerFaixasDoTbody(tbodyEl) {
return Array.from(tbodyEl.children).map((tr) => {
const valores = {};
tr.querySelectorAll("input[data-campo]").forEach((input) => {
valores[input.dataset.campo] = parseValorBr(input.value);
});
return {
limite_superior: valores.limite_superior || 0,
aliquota: (valores.aliquota || 0) / 100,
deduzir: valores.deduzir || 0,
};
});
}
async function carregarParametrosFiscais() {
try {
const parametros = await pidApiRequest("/parametros-fiscais-custo-contratacao/");
renderFaixas(inssBody, parametros.faixas_inss);
renderFaixas(irrfBody, parametros.faixas_irrf);
document.getElementById("cc-inss-teto").value = formataValorBr(parametros.teto_desconto_inss);
document.getElementById("cc-irrf-topo-aliquota").value = formataValorBr(parametros.aliquota_irrf_topo * 100);
document.getElementById("cc-irrf-topo-deduzir").value = formataValorBr(parametros.deduzir_irrf_topo);
document.getElementById("cc-desconto-simplificado").value = formataValorBr(parametros.desconto_simplificado_irrf);
document.getElementById("cc-deducao-dependente").value = formataValorBr(parametros.deducao_por_dependente);
document.getElementById("cc-lei15270-a").value = formataValorBr(parametros.reducao_lei_15270_coeficiente_a);
document.getElementById("cc-lei15270-b").value = String(parametros.reducao_lei_15270_coeficiente_b);
document.getElementById("cc-lei15270-limite").value = formataValorBr(parametros.reducao_lei_15270_limite);
} catch (e) {
painelError.textContent = "Não foi possível carregar as tabelas fiscais.";
}
}
const inssAddBtn = document.getElementById("cc-inss-add-btn");
if (inssAddBtn) {
inssAddBtn.addEventListener("click", () => {
inssBody.appendChild(criarLinhaFaixa({ limite_superior: 0, aliquota: 0, deduzir: 0 }));
});
}
const irrfAddBtn = document.getElementById("cc-irrf-add-btn");
if (irrfAddBtn) {
irrfAddBtn.addEventListener("click", () => {
irrfBody.appendChild(criarLinhaFaixa({ limite_superior: 0, aliquota: 0, deduzir: 0 }));
});
}
if (painelSalvarBtn) {
painelSalvarBtn.addEventListener("click", async () => {
painelError.textContent = "";
painelStatus.hidden = true;
const payload = {
faixas_inss: lerFaixasDoTbody(inssBody),
teto_desconto_inss: parseValorBr(campoValor("cc-inss-teto")),
faixas_irrf: lerFaixasDoTbody(irrfBody),
aliquota_irrf_topo: parseValorBr(campoValor("cc-irrf-topo-aliquota")) / 100,
deduzir_irrf_topo: parseValorBr(campoValor("cc-irrf-topo-deduzir")),
desconto_simplificado_irrf: parseValorBr(campoValor("cc-desconto-simplificado")),
deducao_por_dependente: parseValorBr(campoValor("cc-deducao-dependente")),
reducao_lei_15270_coeficiente_a: parseValorBr(campoValor("cc-lei15270-a")),
reducao_lei_15270_coeficiente_b: parseFloat(campoValor("cc-lei15270-b").replace(",", ".")) || 0,
reducao_lei_15270_limite: parseValorBr(campoValor("cc-lei15270-limite")),
};
painelSalvarBtn.disabled = true;
painelSalvarBtn.textContent = "Salvando...";
try {
const parametros = await pidApiRequest("/parametros-fiscais-custo-contratacao/", {
method: "PATCH",
body: payload,
});
renderFaixas(inssBody, parametros.faixas_inss);
renderFaixas(irrfBody, parametros.faixas_irrf);
painelStatus.textContent = "Tabelas salvas com sucesso.";
painelStatus.hidden = false;
} catch (e) {
painelError.textContent = e.message;
} finally {
painelSalvarBtn.disabled = false;
painelSalvarBtn.textContent = "Salvar tabelas";
}
});
}
carregarParametrosFiscais();
});