:root{
–bg:#f8fafc;
–card:#ffffff;
–muted:#475569;
–text:#1e293b;
–accent:#16a34a;
–danger:#dc2626;
–border:#cbd5e1;
}
*{box-sizing:border-box}
body{
margin:0;
background:var(–bg);
font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,”Helvetica Neue”,Arial;
color:var(–text);
line-height:1.45;
}
.wrap{max-width:1100px;margin:24px auto;padding:16px}
h1{font-size:clamp(22px,3.5vw,32px);margin:0 0 8px}
p.hint{color:var(–muted);margin:4px 0 16px}
.grid{display:grid;grid-template-columns:1fr;gap:14px}
@media (min-width:900px){.grid{grid-template-columns:1.1fr 1fr}}
.card{
background:var(–card);
border:1px solid var(–border);
border-radius:16px;
padding:16px;
box-shadow:0 4px 10px rgba(0,0,0,.05);
}
.card h2{font-size:18px;margin:0 0 10px}
.row{display:grid;grid-template-columns:1fr;gap:10px;margin-bottom:10px}
@media (min-width:640px){.row{grid-template-columns:repeat(2,minmax(0,1fr))}}
label{display:flex;flex-direction:column;font-size:13px;color:var(–muted);gap:6px}
input, select{
background:#f9fafb;
border:1px solid var(–border);
border-radius:10px;
padding:10px 12px;
color:var(–text);
font-size:14px;
outline:none;
}
input[type=”number”]::-webkit-outer-spin-button,
input[type=”number”]::-webkit-inner-spin-button{appearance:none;margin:0}
.inline{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.btn{
cursor:pointer;
border:1px solid var(–border);
background:#f1f5f9;
padding:10px 14px;
border-radius:12px;
font-weight:600;
color:var(–text);
transition:.15s;
text-decoration:none;
display:inline-block;
}
.btn:hover{transform:translateY(-1px);background:#e2e8f0}
.btn.primary{background:var(–accent);color:#fff;border-color:#15803d}
.btn.danger{background:var(–danger);color:#fff;border-color:#991b1b}
.kpi{
display:flex;
justify-content:space-between;
align-items:center;
padding:12px;
border:1px dashed var(–border);
border-radius:12px;
margin:10px 0;
background:#f8fafc;
}
.kpi b{font-size:18px}
.muted{color:var(–muted);font-size:13px}
table{width:100%;border-collapse:separate;border-spacing:0 8px}
th,td{text-align:left;padding:10px 12px}
th{font-size:12px;color:var(–muted)}
tr{background:#f9fafb;border-radius:10px}
tr td:first-child{border-top-left-radius:10px;border-bottom-left-radius:10px}
tr td:last-child{border-top-right-radius:10px;border-bottom-right-radius:10px;text-align:right}
.pos{color:#15803d}
.neg{color:#dc2626}
.tot{font-weight:800}
.footer{font-size:12px;color:var(–muted);margin-top:10px}
.badge{
font-size:12px;
padding:3px 8px;
border:1px solid var(–border);
border-radius:999px;
background:#f1f5f9;
margin-left:6px;
color:var(–muted);
}
(function(){
const el = id => document.getElementById(id);
const salarioEl = el(‘salario’);
const tipoEl = el(‘tipo’);
const admissaoEl = el(‘admissao’);
const demissaoEl = el(‘demissao’);
const avisoEl = el(‘aviso’);
const descontarAvisoEl = el(‘descontarAviso’);
const meses13El = el(‘meses13’);
const mesesFeriasEl = el(‘mesesFerias’);
const feriasVencidasDiasEl = el(‘feriasVencidasDias’);
const faltasDiasEl = el(‘faltasDias’);
const fgtsSaldoEl = el(‘fgtsSaldo’);
const adiantamentosEl = el(‘adiantamentos’);
const diasMesEl = el(‘diasMes’);
const tempoCasaEl = el(‘tempoCasa’);
const avisoDiasEl = el(‘avisoDias’);
const tabelaBody = document.querySelector(‘#tabela tbody’);
const totProvEl = el(‘totProventos’);
const totDescEl = el(‘totDescontos’);
const totLiqEl = el(‘totLiquido’);
const fmt = v => isFinite(v) ? v.toLocaleString(‘pt-BR’,{style:’currency’,currency:’BRL’}) : ‘—’;
function parseNum(elm){
const v = (elm.value || ”).toString().replace(‘,’, ‘.’);
return v === ” ? NaN : Number(v);
}
function diffYM(a, b){
const start = new Date(a.getFullYear(), a.getMonth(), a.getDate());
const end = new Date(b.getFullYear(), b.getMonth(), b.getDate());
let anos = end.getFullYear() – start.getFullYear();
let meses = end.getMonth() – start.getMonth();
let dias = end.getDate() – start.getDate();
if (dias < 0){
const prevMonth = new Date(end.getFullYear(), end.getMonth(), 0).getDate();
dias += prevMonth;
meses -= 1;
}
if (meses < 0){ meses += 12; anos -= 1; }
return {anos, meses, dias};
}
function countMonthsRule15(startDate, endDate){
if (!startDate || !endDate || endDate < startDate) return 0;
let count = 0;
let cur = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
const end = new Date(endDate.getFullYear(), endDate.getMonth(), 1);
while (cur = 15) count++;
cur = new Date(year, month+1, 1);
}
return count;
}
function avisoPrevioLegalDias(adm, dem){
if (!adm || !dem || dem dem) inicio = new Date(dem.getFullYear()-1, adm.getMonth(), adm.getDate());
return countMonthsRule15(inicio, dem);
}
function multaFgtsPercent(tipo){
switch (tipo){
case ‘sem_justa’: return 40;
case ‘acordo’: return 20;
default: return 0;
}
}
function renderLinha(nome, valor, classe=’pos’){
const tr = document.createElement(‘tr’);
const td1 = document.createElement(‘td’); td1.textContent = nome;
const td2 = document.createElement(‘td’); td2.className = classe; td2.textContent = fmt(valor);
tr.appendChild(td1); tr.appendChild(td2);
tabelaBody.appendChild(tr);
}
function calcular(){
tabelaBody.innerHTML = ”;
let proventos = 0, descontos = 0;
const salario = parseNum(salarioEl);
const tipo = tipoEl.value;
const adm = admissaoEl.value ? new Date(admissaoEl.value+’T12:00:00′) : null;
const dem = demissaoEl.value ? new Date(demissaoEl.value+’T12:00:00′) : null;
const aviso = avisoEl.value;
const descontarAviso = descontarAvisoEl.value === ‘sim’;
if (!salario || !dem){
renderLinha(‘Preencha salário e data de demissão’, 0, ‘neg’);
totProvEl.textContent = ‘—’; totDescEl.textContent = ‘—’; totLiqEl.textContent = ‘—’;
return;
}
const diasMes = diasTrabalhadosMes(dem);
diasMesEl.textContent = diasMes || ‘—’;
const tc = (adm && dem) ? diffYM(adm, dem) : {anos:0,meses:0};
tempoCasaEl.textContent = adm ? `${tc.anos}a ${tc.meses}m` : ‘—’;
const avisoLegal = avisoPrevioLegalDias(adm, dem);
avisoDiasEl.textContent = avisoLegal || ‘—’;
const saldoSalario = (salario/30) * diasMes;
if (saldoSalario>0){ proventos += saldoSalario; renderLinha(‘Saldo de salário’, saldoSalario); }
let m13 = meses13El.value === ” ? calcAutoMeses13(adm, dem) : Number(meses13El.value);
if (!isFinite(m13) || m1312) m13 = 12;
const decimo = salario * (m13/12);
if (decimo>0){ proventos += decimo; renderLinha(`13º proporcional (${m13} m)`, decimo); }
let mFer = mesesFeriasEl.value === ” ? calcAutoMesesFerias(adm, dem) : Number(mesesFeriasEl.value);
if (!isFinite(mFer) || mFer12) mFer = 12;
const feriasProp = salario * (mFer/12);
const tercoProp = feriasProp/3;
if (feriasProp>0){
proventos += feriasProp + tercoProp;
renderLinha(`Férias proporcionais (${mFer} m)`, feriasProp);
renderLinha(`1/3 de férias proporcionais`, tercoProp);
}
const ferVencDias = Number(feriasVencidasDiasEl.value || 0);
if (ferVencDias>0){
const ferVenc = (salario/30)*ferVencDias;
const tercoVenc = ferVenc/3;
proventos += ferVenc + tercoVenc;
renderLinha(`Férias vencidas (${ferVencDias} dias)`, ferVenc);
renderLinha(`1/3 de férias vencidas`, tercoVenc);
}
if (aviso === ‘indenizado’){
if (tipo === ‘sem_justa’ || tipo === ‘acordo’){
const baseDias = avisoLegal || 30;
let avisoIndenizado = salario/30 * baseDias;
if (tipo === ‘acordo’) avisoIndenizado *= 0.5;
if (avisoIndenizado>0){ proventos += avisoIndenizado; renderLinha(`Aviso prévio indenizado`, avisoIndenizado); }
}
}
if (tipo === ‘pedido’ && descontarAviso){
const descontoAviso = salario;
descontos += descontoAviso;
renderLinha(‘Desconto aviso não trabalhado (30 dias)’, -descontoAviso, ‘neg’);
}
const faltasDias = Number(faltasDiasEl.value || 0);
if (faltasDias>0){
const descFaltas = (salario/30)*faltasDias;
descontos += descFaltas;
renderLinha(`Desconto por faltas/atrasos (${faltasDias} dias)`, -descFaltas, ‘neg’);
}
const adiant = parseNum(adiantamentosEl) || 0;
if (adiant>0){
descontos += adiant;
renderLinha(‘Adiantamentos/Descontos diversos’, -adiant, ‘neg’);
}
const fgtsSaldo = parseNum(fgtsSaldoEl) || 0;
const fgtsPct = multaFgtsPercent(tipo);
if (fgtsSaldo>0 && fgtsPct>0){
const multa = fgtsSaldo * (fgtsPct/100);
proventos += multa;
renderLinha(`Multa FGTS (${fgtsPct}% sobre saldo informado)`, multa);
}
totProvEl.textContent = fmt(proventos);
totDescEl.textContent = fmt(descontos);
totLiqEl.textContent = fmt(proventos – descontos);
}
function recalcAuto(){
const adm = admissaoEl.value ? new Date(admissaoEl.value+’T12:00:00′) : null;
const dem = demissaoEl.value ? new Date(demissaoEl.value+’T12:00:00′) : null;
meses13El.value = calcAutoMeses13(adm, dem) || ”;
mesesFeriasEl.value = calcAutoMesesFerias(adm, dem) || ”;
avisoDiasEl.textContent = avisoPrevioLegalDias(adm, dem) || ‘—’;
diasMesEl.textContent = dem ? dem.getDate() : ‘—’;
if (adm && dem){
const tc = diffYM(adm, dem);
tempoCasaEl.textContent = `${tc.anos}a ${tc.meses}m`;
} else {
tempoCasaEl.textContent = ‘—’;
}
}
el(‘calc’).addEventListener(‘click’, e => { e.preventDefault(); calcular(); });
el(‘auto’).addEventListener(‘click’, e => { e.preventDefault(); recalcAuto(); calcular(); });
el(‘limpar’).addEventListener(‘click’, e => {
e.preventDefault();
document.querySelectorAll(‘input’).forEach(i=>i.value=”);
tipoEl.value=’sem_justa’;
avisoEl.value=’trabalhado’;
descontarAvisoEl.value=’nao’;
tabelaBody.innerHTML=”;
totProvEl.textContent=’—’; totDescEl.textContent=’—’; totLiqEl.textContent=’—’;
diasMesEl.textContent=’—’; tempoCasaEl.textContent=’—’; avisoDiasEl.textContent=’—’;
});
el(‘imprimir’).addEventListener(‘click’, e => { e.preventDefault(); window.print(); });
})();
Preencha os campos abaixo.
Dados do Contrato
Dias trabalhados no mês da demissão
—
Tempo de casa (anos/meses)
—
Aviso prévio legal (dias)
—
Proporcionais & Ajustes
Resumo
| Verba | Valor (R$) |
|---|---|
| Total de Proventos | — |
| Total de Descontos | — |
| Total Líquido | — |