// Relatório mensal completo (todas as sessões + insights + sugestões) — imprimível
function RelatorioMensalPage({ state, refKey, onVoltar }) {
const fmt = window.fmtBRLString;
const [ys, ms] = (refKey || "2026-06").split("-");
const year = Number(ys), month = Number(ms) - 1;
const refLabel = `${window.MESES_FULL[month]}/${year}`;
const txs = state.transactions || [];
const monthTxs = window.txByMonth(txs, year, month);
const plano = window.computePlano(txs, state.aggregates, year, month);
const income = plano.income;
const expense = window.sumExpense(monthTxs) || window.aggExpenseForMonth(state.aggregates, year, month);
const classes = window.byClasse(monthTxs);
const cats = window.byCategory(monthTxs.filter(t => t.type === "Despesa")).slice(0, 6);
const p = state.patrimonio || {};
const tot = window.patrimonioTotais(p, state.dividas);
const custoFixo = window.custoFixoEssencialMensal(txs);
const reservaSaldo = (p.saldos || []).filter(s => s.reserva).reduce((s, a) => s + Number(a.valor || 0), 0);
const reservaAlvo = custoFixo * ((state.reserva || {}).mesesAlvo || 6);
const apo = window.calcAposentadoria(state.aposentadoria || { rendaMensalDesejada: 0 }, tot.financeiro);
const apoGap = Math.max(0, (apo.alvo || 0) - (apo.projecao || 0));
const objetivos = state.objetivos || [];
const objPess = state.objetivosPessoais || {};
const objPessSel = (window.OBJ_PESSOAIS_CATALOGO || []).filter(i => objPess[i.id] && objPess[i.id].selecionado);
const seguros = state.seguros || [];
const dividas = state.dividas || [];
const dividaTotal = dividas.reduce((s, d) => s + Number(d.saldo || d.valor || 0), 0);
const entrega = ((state.fechamento || {}).entregas || {})[refKey] || {};
// ---- Lei de Jim Rohn (70/10/10/10) ----
const bmap = {}; plano.buckets.forEach(b => { bmap[b.key] = b; });
const rohn = window.PLANO_ORDER.map(k => {
const b = bmap[k];
const def = window.PLANO[k];
const target = income * def.pct;
const used = b ? b.used : 0;
const pct = target > 0 ? used / target : 0;
const avaliado = target > 0;
const ok = avaliado && (k === "gastos" ? used <= target * 1.02 : used >= target * 0.9);
return { key: k, label: def.label, alvoPct: def.pct * 100, target, used, pct, ok, avaliado };
});
const semDados = income <= 0;
const avaliados = rohn.filter(r => r.avaliado);
const rohnOk = avaliados.filter(r => r.ok).length;
const nota = avaliados.length ? Math.round((rohnOk / avaliados.length) * 100) : null;
const saldoMes = income - expense;
const taxaPoupanca = income > 0 ? (income - (bmap.gastos ? bmap.gastos.used : expense)) / income : 0;
const veredito = semDados
? { t: "Sem dados suficientes", c: "warn", d: `Não há receitas lançadas em ${refLabel}. Registre as receitas e despesas do mês para que o diagnóstico do 70/10/10/10 possa ser calculado.` }
: nota >= 100 ? { t: "No caminho", c: "ok", d: "Todos os quatro pilares do 70/10/10/10 foram respeitados neste mês." }
: nota >= 50 ? { t: "Parcialmente no caminho", c: "warn", d: "Parte dos pilares foi cumprida — os desvios abaixo precisam de correção no próximo ciclo." }
: { t: "Fora do caminho", c: "bad", d: "A maioria dos pilares ficou fora do alvo. É prioridade reorganizar o mês seguinte." };
// ---- Sugestões assertivas ----
const sug = [];
if (semDados) sug.push(`Lance as receitas e despesas de ${refLabel} no Orçamento — sem isso o fechamento do mês não pode ser avaliado.`);
const g = bmap.gastos;
if (!semDados && g && g.used > income * 0.70) {
const excesso = g.used - income * 0.70;
sug.push(`Reduza ${fmt(excesso)} de gastos até o próximo fechamento: comece pelas despesas variáveis e pelos gastos marcados como redutíveis.`);
}
["caridade", "ativo", "passivo"].forEach(k => {
const b = bmap[k], def = window.PLANO[k];
if (!semDados && b && b.used < income * def.pct * 0.9) {
sug.push(`Aporte ${fmt(income * def.pct - b.used)} em ${def.label} para fechar os 10% do mês — programe débito automático no dia do recebimento.`);
}
});
if (reservaSaldo < reservaAlvo) {
const falta = reservaAlvo - reservaSaldo;
const aporte = income * 0.1;
if (aporte >= 100) {
const meses = Math.max(1, Math.ceil(falta / aporte));
sug.push(`Reserva de emergência ${fmt(falta)} abaixo do alvo (${fmt(reservaAlvo)}). Mantendo 10% da renda, ela é completada em ~${meses} mês(es).`);
} else {
sug.push(`Reserva de emergência ${fmt(falta)} abaixo do alvo (${fmt(reservaAlvo)}) — defina um aporte mensal fixo para fechar essa diferença.`);
}
}
if (dividaTotal > 0) sug.push(`Priorize a quitação de ${fmt(dividaTotal)} em dívidas — amortize primeiro a de maior taxa e evite novas parcelas neste ciclo.`);
if (seguros.length === 0) sug.push("Nenhuma proteção cadastrada: contrate/registre seguro de vida e de renda antes de ampliar investimentos.");
if (apoGap > 0) sug.push(`Aposentadoria: a projeção atual fica ${fmt(apoGap)} abaixo do capital necessário. Aporte mensal recomendado: ${fmt(apo.aporteNecessario || 0)}.`);
if (objPessSel.length === 0) sug.push("Cadastre seus objetivos pessoais na primeira etapa — sem objetivo definido não há como priorizar o orçamento.");
objetivos.filter(o => o.alvo > 0 && o.acumulado < o.alvo).slice(0, 2).forEach(o => {
sug.push(`Objetivo "${o.nome}": faltam ${fmt(o.alvo - o.acumulado)} até ${o.prazo ? o.prazo.split("-").reverse().join("/") : "o prazo"} — reserve uma parcela fixa mensal para ele.`);
});
if (taxaPoupanca >= 0.3) sug.push("Taxa de poupança acima de 30%: avalie diversificar novos aportes entre renda fixa e ativos de longo prazo.");
if (sug.length === 0) sug.push("Mês equilibrado: mantenha o mesmo padrão de aportes e revise as metas de longo prazo no próximo trimestre.");
function Sec({ title, scope, children }) {
return (
{title}
{scope && {scope}}
Relatório mensal consolidado — {refLabel}
{veredito.d}
Sem lançamentos classificados neste mês.
}Sem despesas detalhadas.
}{d.motivo}
}Nenhum objetivo pessoal selecionado.
}Nenhum objetivo cadastrado.
}Nenhuma proteção cadastrada.
}Como foi o mês: {entrega.nota}
{entrega.pontos &&Pontos positivos: {entrega.pontos}
} {entrega.ajustes &&Ajustes: {entrega.ajustes}
}Relato ainda não registrado para {refLabel}.
}