BF

Ingresa a tu cuenta

Boleta Fiel — calculadora del régimen laboral peruano.

¿No tienes cuenta? Regístrate aquí
BF

Boleta Fiel

Calculadora de remuneraciones · Régimen laboral de la actividad privada — Ley N° 728
/* ============ CONSTANTES LEGALES (referenciales, ago-2026) ============ ZONA DE CONFIGURACIÓN LEGAL — edita estos valores cuando cambien las normas. Fuente principal: sbs.gob.pe (AFP), sunat.gob.pe (UIT), essalud.gob.pe. */ const RMV = 1130; const ASIG_FAM = Math.round(RMV*0.10*100)/100; const ONP_TASA = 13.00; const AFP_APORTE = 10.00; const AFP_PRIMA_SEGURO = 1.37; // Ley 32123 (ene-2026), igual para las 4 AFP const AFP_TOPE_ASEGURABLE = 12598.91; // tope remuneración asegurable — SBS lo actualiza TRIMESTRALMENTE, verificar cada trimestre const ESSALUD_TASA = 9.00; const UIT = 5500; /* Comisión por flujo: solo la mantienen quienes se afiliaron antes de 2013 y eligieron no migrar. Comisión anual sobre saldo: aplica a todos los afiliados desde 2013 (la gran mayoría hoy). Nunca se cobran ambas a la vez. La de saldo se descuenta del FONDO acumulado, no del sueldo, por eso no resta del neto a pagar en la boleta mensual — solo se muestra como referencia. */ const AFP_FONDOS = { habitat:{nombre:'AFP Habitat', comision:1.47}, integra:{nombre:'AFP Integra', comision:1.55}, prima:{nombre:'AFP Prima', comision:1.60}, profuturo:{nombre:'AFP Profuturo',comision:1.69} }; const MESES = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']; const DIVISOR_RENTA = {1:12,2:12,3:12,4:9,5:8,6:7,7:6,8:5,9:4,10:3,11:2,12:1}; const fmt = n => 'S/ ' + (isFinite(n)?n:0).toLocaleString('es-PE',{minimumFractionDigits:2,maximumFractionDigits:2}); const num = id => { const el=document.getElementById(id); if(!el) return 0; const v=parseFloat(el.value); return isFinite(v)?v:0; }; const chk = id => document.getElementById(id).checked; const sel = id => document.getElementById(id).value; const getBasico = () => num('cfg-basico'); const getAsigFam = () => chk('cfg-hijos') ? ASIG_FAM : 0; const state = { mensual:null, grat:null, cts:null, vac:null, renta:null, liq:null }; function pensionSobre(monto, sistema, afpKey){ if(sistema==='afp'){ const fondo = AFP_FONDOS[afpKey]; const aporte = monto*AFP_APORTE/100; const baseSeguro = Math.min(monto, AFP_TOPE_ASEGURABLE); const prima = baseSeguro*AFP_PRIMA_SEGURO/100; const comision = monto*fondo.comision/100; return {total:aporte+prima+comision, detalle:[ ['Aporte obligatorio (10%)', aporte], ['Prima de seguro (1.37%)', prima], [`Comisión ${fondo.nombre} (${fondo.comision}%)`, comision] ]}; } const total = monto*ONP_TASA/100; return {total, detalle:[[`Aporte ONP (${ONP_TASA}%)`, total]]}; } function enforceMinRMV(id){ const el = document.getElementById(id); const v = parseFloat(el.value); const warnId = id+'-warn'; let warn = document.getElementById(warnId); if(isFinite(v) && v>0 && v
Remuneración básica fija (Configuración)
S/ 0.00
Editar en Configuración → `; } function refreshBasicoBanners(){ document.querySelectorAll('[id^=disp-basico]').forEach(el=>{ el.textContent = fmt(getBasico()); }); } /* ============ PESTAÑAS ============ */ const folios = [ {id:'config', num:'01', label:'Configuración'}, {id:'mensual', num:'02', label:'Remuneración mensual'}, {id:'grat', num:'03', label:'Gratificación'}, {id:'cts', num:'04', label:'CTS'}, {id:'vac', num:'05', label:'Vacaciones'}, {id:'liq', num:'06', label:'Liquidación de beneficios'}, {id:'resumen', num:'07', label:'Resumen anual'}, {id:'salud', num:'08', label:'Módulo de practicantes'} ]; const nav = document.getElementById('nav'); folios.forEach(f=>{ const b=document.createElement('button'); b.className='folio-btn'; b.dataset.target=f.id; b.innerHTML=`${f.num}${f.label}`; b.onclick=()=>showPanel(f.id); nav.appendChild(b); }); function showPanel(id){ document.querySelectorAll('.panel').forEach(p=>p.classList.remove('active')); document.querySelectorAll('.folio-btn').forEach(b=>b.classList.toggle('active', b.dataset.target===id)); document.getElementById('panel-'+id).classList.add('active'); if(id==='resumen') renderResumen(); } const panels = document.getElementById('panels'); /* ============ 01 · CONFIGURACIÓN ============ */ panels.innerHTML += `

Datos del trabajador y remuneración base

La remuneración básica que ingreses aquí se usará automáticamente en Remuneración mensual, Gratificación, CTS, Vacaciones y Liquidación. El módulo de practicantes tiene su propia subvención, independiente de este dato.

Mínimo: RMV = S/ ${RMV.toFixed(2)}

Asignación Familiar: 10% de la RMV = ${fmt(ASIG_FAM)} mensuales, monto fijo. Se aplica automáticamente en todas las pestañas si marcas la casilla.
RMV: S/ ${RMV.toFixed(2)} UIT 2026: S/ ${UIT.toLocaleString('es-PE')} ONP: ${ONP_TASA}% flat AFP: 10% + 1.37% + comisión
`; function toggleAFP(){ document.getElementById('cfg-afp-wrap').style.display = sel('cfg-sistema')==='afp' ? 'flex':'none'; } /* ============ 02 · REMUNERACIÓN MENSUAL ============ */ panels.innerHTML += `
${basicoBanner()}

Ingresos del mes

La remuneración básica y la asignación familiar se toman de Configuración. Completa aquí los conceptos variables del mes.


Valor hora ordinaria = (Remuneración básica / 30) / 8 = S/ 0.00.

Detalle del cálculo — neto a pagar

Neto a pagar0%
Aportes a cargo del empleador (no se descuentan al trabajador): EsSalud ${ESSALUD_TASA}% sobre la remuneración bruta = S/ 0.00.
`; function calcMensual(){ const basico = getBasico(); const asigFam = getAsigFam(); const comisiones = num('m-comisiones'), bonos = num('m-bonos'), otros = num('m-otros'); const valorHora = basico/30/8; document.getElementById('m-valorhora').textContent = fmt(valorHora); const he25 = num('m-he25')*valorHora*1.25; const he35 = num('m-he35')*valorHora*1.35; const he100 = num('m-he100')*valorHora*2.00; const bruto = basico+asigFam+comisiones+bonos+otros+he25+he35+he100; const sistema = sel('cfg-sistema'), afpKey = sel('cfg-afp'); const pension = pensionSobre(bruto, sistema, afpKey); const adelanto = num('m-adelanto'), otrosdesc = num('m-otrosdesc'); /* Renta de 5ª categoría — cálculo automático, oculto, solo aparece como concepto */ const mesActual = new Date().getMonth()+1; const gratProy = 2*(basico+asigFam); const RBA = bruto*12 + gratProy; const RN = Math.max(0, RBA-7*UIT); const t1=Math.min(RN,5*UIT)*0.08, t2=Math.max(0,Math.min(RN,20*UIT)-5*UIT)*0.14, t3=Math.max(0,Math.min(RN,35*UIT)-20*UIT)*0.17, t4=Math.max(0,Math.min(RN,45*UIT)-35*UIT)*0.20, t5=Math.max(0,RN-45*UIT)*0.30; const impuestoAnual = t1+t2+t3+t4+t5; const retencionMes = impuestoAnual/DIVISOR_RENTA[mesActual]; state.renta = {impuestoAnual, retencionMes}; const totalDesc = pension.total+adelanto+otrosdesc+retencionMes; const neto = bruto-totalDesc; const essalud = bruto*ESSALUD_TASA/100; let rows=''; rows += `INGRESOS`; rows += `Remuneración básica${fmt(basico)}`; if(asigFam) rows += `Asignación familiar${fmt(asigFam)}`; if(comisiones) rows += `Comisiones${fmt(comisiones)}`; if(bonos) rows += `Bonos${fmt(bonos)}`; if(otros) rows += `Otros ingresos${fmt(otros)}`; if(he25) rows += `Horas extra 25%${fmt(he25)}`; if(he35) rows += `Horas extra 35%${fmt(he35)}`; if(he100) rows += `Horas al 100%${fmt(he100)}`; rows += `Total ingreso bruto${fmt(bruto)}`; rows += `DESCUENTOS`; pension.detalle.forEach(([l,v])=> rows += `${l}-${fmt(v)}`); if(adelanto) rows += `Adelanto de sueldo-${fmt(adelanto)}`; if(otrosdesc) rows += `Otros descuentos-${fmt(otrosdesc)}`; if(retencionMes>0) rows += `Renta de quinta-${fmt(retencionMes)}`; rows += `Neto a pagar${fmt(neto)}`; document.getElementById('m-ledger').innerHTML = rows; document.getElementById('m-essalud').textContent = fmt(essalud); state.mensual = {basico, asigFam, bruto, neto, sistema}; const pct = bruto>0 ? Math.max(0,Math.min(100,(neto/bruto)*100)) : 0; document.getElementById('m-bar-fill').style.width = pct.toFixed(1)+'%'; document.getElementById('m-bar-pct').textContent = pct.toFixed(0)+'% del bruto'; refreshBasicoBanners(); } /* ============ 03 · GRATIFICACIÓN ============ */ panels.innerHTML += `
${basicoBanner()}

Gratificación (Ley N° 27735)

Enero–junio (pago julio) y julio–diciembre (pago diciembre). Exonerada de AFP/ONP y EsSalud/EPS (Ley N° 30334).

Ingresa la fecha de ingreso en Configuración para calcular automáticamente los meses/días según el periodo.

`; function calcPeriodoGrat(){ const ingreso = parseDate(sel('cfg-fecha')); const periodo = sel('g-periodo'); const anio = num('g-anio')||new Date().getFullYear(); const inicio = periodo==='h1'? new Date(anio,0,1): new Date(anio,6,1); const fin = periodo==='h1'? new Date(anio,5,30): new Date(anio,11,31); const note = document.getElementById('g-periodo-note'); if(!ingreso){ note.className='note-box'; note.textContent='Ingresa la fecha de ingreso en Configuración para calcular automáticamente los meses/días.'; calcGrat(); return; } if(ingreso>fin){ document.getElementById('g-meses').value=0; document.getElementById('g-dias').value=0; note.className='note-box danger'; note.textContent=`El trabajador ingresó el ${sel('cfg-fecha')}, después de este periodo: no corresponde gratificación.`; calcGrat(); return; } const efectivo = ingreso>inicio?ingreso:inicio; let {meses,dias} = diffMesesDias30(efectivo, fin); dias+=1; if(dias>=30){dias-=30; meses+=1;} if(meses>=6){meses=6;dias=0;} document.getElementById('g-meses').value=meses; document.getElementById('g-dias').value=dias; note.className = meses<6?'note-box warn':'note-box'; note.textContent = meses<6 ? `Ingresó el ${sel('cfg-fecha')} → proporcional: ${meses} meses y ${dias} días.` : 'Ingresó antes del inicio del periodo → periodo completo (6 meses).'; calcGrat(); } function calcGrat(){ const basico=getBasico(), asigFam=getAsigFam(), variable=num('g-variable'); const rc = basico+asigFam+variable; const meses=Math.min(num('g-meses'),6), dias=num('g-dias'); const grat = rc*(meses/6)+rc*(dias/180); const bonifPct = parseFloat(sel('g-salud')); const bonif = grat*bonifPct/100; const neto = grat+bonif; let rows=''; rows += `Remuneración computable${fmt(rc)}`; rows += `× (${meses} meses / 6 + ${dias} días / 180)`; rows += `Gratificación bruta${fmt(grat)}`; rows += `Descuento AFP/ONPNo aplica — exonerado (Ley 30334)`; rows += `Bonificación extraordinaria (${bonifPct}%)+${fmt(bonif)}`; rows += `Neto a pagar${fmt(neto)}`; document.getElementById('g-ledger').innerHTML = rows; state.grat = {rc, grat, bonif, neto}; refreshBasicoBanners(); } /* ============ 04 · CTS ============ */ panels.innerHTML += `
${basicoBanner()}

CTS (D.S. 001-97-TR)

Nov–Abr → depósito Mayo. May–Oct → depósito Noviembre. No sujeta a AFP/ONP/EsSalud.

Ingresa la fecha de ingreso en Configuración para calcular automáticamente los meses/días.

`; function copiarSextoGrat(){ const g = state.grat? state.grat.grat/6:0; document.getElementById('c-sextogrado').value=g.toFixed(2); calcCTS(); } function calcPeriodoCTS(){ const ingreso = parseDate(sel('cfg-fecha')); const periodo = sel('c-periodo'); const anioDeposito = num('c-anio')||new Date().getFullYear(); const inicio = periodo==='nov-abr'? new Date(anioDeposito-1,10,1): new Date(anioDeposito,4,1); const fin = periodo==='nov-abr'? new Date(anioDeposito,3,30): new Date(anioDeposito,9,31); const note = document.getElementById('c-periodo-note'); if(!ingreso){ note.className='note-box'; note.textContent='Ingresa la fecha de ingreso en Configuración para calcular automáticamente los meses/días.'; calcCTS(); return; } if(ingreso>fin){ document.getElementById('c-meses').value=0; document.getElementById('c-dias').value=0; note.className='note-box danger'; note.textContent=`El trabajador ingresó el ${sel('cfg-fecha')}, después de este periodo: no corresponde depósito en esta fecha.`; calcCTS(); return; } const efectivo = ingreso>inicio?ingreso:inicio; let {meses,dias} = diffMesesDias30(efectivo, fin); dias+=1; if(dias>=30){dias-=30; meses+=1;} if(meses>=6){meses=6;dias=0;} document.getElementById('c-meses').value=meses; document.getElementById('c-dias').value=dias; note.className = meses<6?'note-box warn':'note-box'; note.textContent = meses<6 ? `Ingresó el ${sel('cfg-fecha')} → proporcional: ${meses} meses y ${dias} días.` : 'Ingresó antes del inicio del periodo → semestre completo (6 meses).'; calcCTS(); } function calcCTS(){ const basico=getBasico(), asigFam=getAsigFam(), variable=num('c-variable'), sexto=num('c-sextogrado'); const rc = basico+asigFam+variable+sexto; const meses=Math.min(num('c-meses'),6), dias=num('c-dias'); const cts = rc*(meses/12)+rc*(dias/360); let rows=''; rows += `Remuneración computable CTS${fmt(rc)}`; rows += `× (${meses} meses / 12 + ${dias} días / 360)`; rows += `Depósito CTS del semestre${fmt(cts)}`; document.getElementById('c-ledger').innerHTML = rows; state.cts = {rc, cts}; refreshBasicoBanners(); } /* ============ 05 · VACACIONES (solo gozadas) ============ */ panels.innerHTML += `
${basicoBanner()}

Vacaciones gozadas (D.Leg. N° 713)

30 días calendario por año completo de servicios. Afecta a AFP/ONP.


Días de descanso0 / 30 días
`; function calcVac(){ const basico=getBasico(), asigFam=getAsigFam(), variable=num('v-variable'); const rc = basico+asigFam+variable; const dias = Math.min(30, num('v-dias')); const bruto = (rc/30)*dias; const pension = pensionSobre(bruto, sel('cfg-sistema'), sel('cfg-afp')); const neto = bruto-pension.total; let rows=''; rows += `Remuneración diaria (RC/30)${fmt(rc/30)}`; rows += `× ${dias} días de descanso${fmt(bruto)}`; pension.detalle.forEach(([l,v])=> rows += `${l}-${fmt(v)}`); rows += `Neto a pagar${fmt(neto)}`; document.getElementById('v-ledger').innerHTML = rows; document.getElementById('v-bar-fill').style.width = ((dias/30)*100).toFixed(1)+'%'; document.getElementById('v-bar-label').textContent = `${dias} / 30 días`; state.vac = {rc, neto}; refreshBasicoBanners(); } /* ============ 06 · LIQUIDACIÓN DE BENEFICIOS SOCIALES ============ */ panels.innerHTML += `
${basicoBanner()}

Simulación de Liquidación de Beneficios Sociales

Estima el pago por cese: remuneración pendiente, gratificación trunca, CTS trunca y vacaciones truncas, menos los descuentos que correspondan.

Ingresa la fecha de ingreso en Configuración y la fecha de cese para calcular los periodos truncos.

`; function calcLiquidacion(){ const note = document.getElementById('l-note'); const ingreso = parseDate(sel('cfg-fecha')); const cese = parseDate(sel('l-fecha')); const basico=getBasico(), asigFam=getAsigFam(), variable=num('l-variable'); const rc = basico+asigFam+variable; const adelanto=num('l-adelanto'), otrosdesc=num('l-otrosdesc'), rentaQuinta=num('l-renta'); const sistema=sel('cfg-sistema'), afpKey=sel('cfg-afp'); if(!ingreso || !cese){ note.className='note-box warn'; note.textContent='Completa la fecha de ingreso (Configuración) y la fecha de cese para calcular la liquidación.'; document.getElementById('l-ledger').innerHTML=''; return; } if(ceseinicioSemGrat ? ingreso : inicioSemGrat; let gratTrunca=0, bonifGrat=0, mesesGrat=0, diasGrat=0; if(efectivoGrat<=cese){ let {meses,dias} = diffMesesDias30(efectivoGrat, cese); dias+=1; if(dias>=30){dias-=30; meses+=1;} if(meses>6){meses=6;dias=0;} mesesGrat=meses; diasGrat=dias; gratTrunca = rc*(meses/6)+rc*(dias/180); bonifGrat = gratTrunca*parseFloat(sel('l-salud'))/100; } // 3) CTS trunca (semestre CTS que contiene la fecha de cese) let inicioSemCTS; if(mesCese>=11) inicioSemCTS = new Date(anioCese,10,1); else if(mesCese<=4) inicioSemCTS = new Date(anioCese-1,10,1); else inicioSemCTS = new Date(anioCese,4,1); const efectivoCTS = ingreso>inicioSemCTS ? ingreso : inicioSemCTS; let ctsTrunca=0, mesesCTS=0, diasCTS=0; if(efectivoCTS<=cese){ let {meses,dias} = diffMesesDias30(efectivoCTS, cese); dias+=1; if(dias>=30){dias-=30; meses+=1;} if(meses>6){meses=6;dias=0;} mesesCTS=meses; diasCTS=dias; const rcCTS = rc + gratTrunca/6; ctsTrunca = rcCTS*(meses/12)+rcCTS*(dias/360); } // 4) Vacaciones truncas (récord vacacional en curso desde el último aniversario) const totalServicio = diffMesesDias30(ingreso, cese); const aniosCompletos = Math.floor(totalServicio.meses/12); const inicioRecord = addYears(ingreso, aniosCompletos); let diasVacTruncas=0, vacTruncaBruto=0; if(inicioRecord<=cese){ let {meses,dias} = diffMesesDias30(inicioRecord, cese); dias+=1; if(dias>=30){dias-=30; meses+=1;} diasVacTruncas = Math.min(30, meses*2.5 + dias*(2.5/30)); vacTruncaBruto = (rc/30)*diasVacTruncas; } // Descuentos: AFP/ONP solo sobre conceptos remunerativos (rem. trunca del mes + vacaciones truncas) const pension = pensionSobre(remTruncaMes+vacTruncaBruto, sistema, afpKey); const totalIngresos = remTruncaMes+gratTrunca+bonifGrat+ctsTrunca+vacTruncaBruto; const totalDescuentos = pension.total+adelanto+otrosdesc+rentaQuinta; const netoLiquidacion = totalIngresos-totalDescuentos; let rows=''; rows += `INGRESOS`; rows += `Remuneración trunca del mes de cese (${diaCese} días)${fmt(remTruncaMes)}`; rows += `Gratificación trunca (${mesesGrat}m ${diasGrat}d)${fmt(gratTrunca)}`; if(bonifGrat) rows += `Bonificación extraordinaria de la grat. trunca${fmt(bonifGrat)}`; rows += `CTS trunca (${mesesCTS}m ${diasCTS}d)${fmt(ctsTrunca)}`; rows += `Vacaciones truncas (${diasVacTruncas.toFixed(2)} días)${fmt(vacTruncaBruto)}`; rows += `Total ingresos${fmt(totalIngresos)}`; rows += `DESCUENTOS`; pension.detalle.forEach(([l,v])=> rows += `${l} (sobre rem. trunca + vacaciones)-${fmt(v)}`); if(adelanto) rows += `Adelantos pendientes-${fmt(adelanto)}`; if(otrosdesc) rows += `Otros descuentos-${fmt(otrosdesc)}`; if(rentaQuinta) rows += `Renta de quinta-${fmt(rentaQuinta)}`; rows += `Neto de la liquidación${fmt(netoLiquidacion)}`; document.getElementById('l-ledger').innerHTML = rows; state.liq = {netoLiquidacion}; refreshBasicoBanners(); } /* ============ 07 · RESUMEN ANUAL ============ */ panels.innerHTML += `

Resumen anual estimado

Proyección referencial basada en los últimos valores calculados en cada pestaña.


`; function renderResumen(){ const m=state.mensual,g=state.grat,c=state.cts,v=state.vac,r=state.renta; const netoMensualAnual = m? m.neto*12:0; const gratAnual = g? g.neto*2:0; const ctsAnual = c? c.cts*2:0; const vacAnual = v? v.neto:0; const impuestoRenta = r? r.impuestoAnual:0; const totalAnual = netoMensualAnual+gratAnual+ctsAnual+vacAnual; document.getElementById('resumen-grid').innerHTML = `
12 sueldos netos
${fmt(netoMensualAnual)}
${m?fmt(m.neto)+' × 12':'sin datos'}
Gratificaciones (×2)
${fmt(gratAnual)}
jul + dic
CTS (×2 depósitos)
${fmt(ctsAnual)}
may + nov
Vacaciones
${fmt(vacAnual)}
último cálculo
`; let rows=''; rows += `12 remuneraciones netas mensuales${fmt(netoMensualAnual)}`; rows += `Gratificaciones (julio + diciembre)${fmt(gratAnual)}`; rows += `CTS (mayo + noviembre)${fmt(ctsAnual)}`; rows += `Vacaciones${fmt(vacAnual)}`; rows += `Total ingreso anual estimado${fmt(totalAnual)}`; rows += `Referencia: Impuesto a la Renta 5ª categoría (anual proyectado)${fmt(impuestoRenta)}`; document.getElementById('resumen-ledger').innerHTML = rows; } /* ============ 08 · MÓDULO DE PRACTICANTES ============ */ panels.innerHTML += `

Módulo de practicantes

Modalidades Formativas Laborales — Ley N° 28518 y su Reglamento (D.S. N° 007-2005-TR). El practicante no percibe remuneración: recibe una subvención económica mensual, que no puede ser menor a una Remuneración Mínima Vital (RMV) cuando cumple la jornada máxima de su modalidad. No firma un contrato de trabajo, sino un convenio de prácticas: no genera vínculo laboral, por lo que no corresponden CTS, gratificación ni vacaciones plenas — sí corresponden subvención, seguro y descanso.

Jornada máxima: 30 h/semana (práctica preprofesional)
Varía según lo que pacte cada empresa — no puede ser menor al mínimo legal

Progreso hacia el descanso de 15 días0 / 12 meses
Seguro: la empresa debe cubrir los riesgos de enfermedad y accidentes del practicante a través de EsSalud, o de un seguro privado con cobertura equivalente a 14 subvenciones mensuales (enfermedad) y 30 subvenciones mensuales (accidente).

Subvención adicional: equivalente al 50% de una subvención mensual, otorgada cada 6 meses continuos de duración del convenio (independiente del descanso de 15 días).
`; const JORNADA_MAX_FORMATIVA = { preprofesional:30, profesional:48, capacitacion:48, aprendizaje:48, pasantia:48 }; const MODALIDAD_LABEL = { preprofesional:'práctica preprofesional', profesional:'práctica profesional', capacitacion:'capacitación laboral juvenil', aprendizaje:'aprendizaje', pasantia:'pasantía en la empresa' }; function onModalidadChange(){ const jm = JORNADA_MAX_FORMATIVA[sel('f-modalidad')]; document.getElementById('f-horas').placeholder = jm; document.getElementById('f-horas-hint').textContent = `Jornada máxima: ${jm} h/semana (${MODALIDAD_LABEL[sel('f-modalidad')]})`; calcFormativa(); } function calcFormativa(){ const jornadaMax = JORNADA_MAX_FORMATIVA[sel('f-modalidad')]; const horas = num('f-horas')||jornadaMax; const proporcion = Math.min(1, horas/jornadaMax); const minimoLegal = RMV*proporcion; document.getElementById('f-minimo-legal').value = fmt(minimoLegal); const subvencion = num('f-subvencion'); const bajoMinimo = subvencion>0 && subvencion < minimoLegal; let rows=''; rows += `Mínimo legal (RMV × ${horas}h / ${jornadaMax}h)${fmt(minimoLegal)}`; rows += `Subvención mensual pactada${fmt(subvencion)}`; if(bajoMinimo) rows += `⚠ Está por debajo del mínimo legal para esas horas de práctica.`; document.getElementById('f-ledger').innerHTML = rows; // Descanso de 15 días — se otorga una sola vez, al superar los 12 meses de convenio const fechaInicio = parseDate(sel('f-fecha-inicio')); const noteEl = document.getElementById('f-descanso-note'); const barFill = document.getElementById('f-bar-fill'); const barLabel = document.getElementById('f-bar-label'); if(!fechaInicio || !subvencion){ noteEl.textContent = 'Completa la fecha de inicio y la subvención mensual para calcular tu descanso de 15 días.'; barFill.style.width = '0%'; barLabel.textContent = '0 / 12 meses'; return; } const hoy = new Date(); const {meses, dias} = diffMesesDias30(fechaInicio, hoy); const pagoDescanso = (subvencion/30)*15; const mesesCap = Math.min(12, meses); barFill.style.width = ((mesesCap/12)*100).toFixed(1)+'%'; barLabel.textContent = `${mesesCap} / 12 meses`; if(meses >= 12){ noteEl.innerHTML = `✓ Corresponde el descanso de 15 días — tu convenio superó los 12 meses (llevas ${meses} meses ${dias} días). Pago del descanso (subvención/30 × 15): ${fmt(pagoDescanso)}.`; } else { const faltanMeses = 12-meses; noteEl.textContent = `Llevas ${meses} meses y ${dias} días de convenio. Te faltan aproximadamente ${faltanMeses} meses para tener derecho al descanso de 15 días (se otorga una sola vez, al superar los 12 meses).`; } } /* ============ VOLVER A CALCULAR / INIT ============ */ function recalcularTodo(){ calcMensual(); calcPeriodoGrat(); calcPeriodoCTS(); calcVac(); calcLiquidacion(); calcFormativa(); refreshBasicoBanners(); } showPanel('config'); toggleAFP(); recalcularTodo();