🎮ArcadeLab

Controle de Cobranças

by PrismPanda27
326 lines14.6 KB
▶ Play
<!DOCTYPE html>
<html lang="pt‑BR">
<head>
<meta charset="UTF‑8">
<meta name="viewport" content="width=device‑width, initial‑scale=1">
<title>Controle de Clientes e Cobranças</title>
<style>
*{box‑sizing:border‑box;margin:0;padding:0;font-family:Arial,sans‑serif;}
body{max‑width:760px;margin:15px auto;padding:10px;background:#f0f2f5;}
h2{text‑align:center;margin‑bottom:15px;color:#224;}
.menu‑abas{display:flex;gap:4px;margin‑bottom:12px;flex‑wrap:wrap;}
.btn‑aba{background:#b8c8e0;border:none;padding:10px 12px;border‑radius:8px;cursor:pointer;font‑weight:bold;}
.btn‑aba.ativo{background:#2b74b8;color:white;}
.bloco{background:#fff;padding:14px;border‑radius:10px;margin‑bottom:12px;box‑shadow:0 1px 3px #ccc;}
h3{margin‑bottom:10px;color:#2b4060;}
input,select,textarea{width:100%;padding:9px;margin:6px 0;border:1px solid #ccc;border‑radius:6px;}
button{background:#2b74b8;color:white;border:none;padding:10px;border‑radius:6px;font‑weight:bold;cursor:pointer;margin:4px 2px;}
.btn‑vermelho{background:#d32f2f;}
.btn‑laranja{background:#f57c00;}
.item{padding:8px;border‑bottom:1px solid #ddd;}
.item‑vencido{background:#fff2f2;padding:8px;border‑bottom:1px solid #ffcccc;}
.item‑proximo{background:#fffbdc;padding:8px;border‑bottom:1px solid #ffe888;}
.resumo{background:#e8f4fc;padding:10px;border‑radius:8px;font‑weight:bold;margin:8px 0;}
.aviso{color:#d32f2f;font‑size:0.9em;}
.obs{font‑style:italic;color:#444;font‑size:0.9em;}
.conteudo‑aba{display:none;}
.conteudo‑aba.ativo{display:block;}
@media print{
  body *{visibility:hidden;}
  #areaImpressao, #areaImpressao *{visibility:visible;}
  #areaImpressao{position:absolute;top:10px;left:10px;width:95%;}
}
</style>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h2>📋 Controle de Clientes e Cobranças</h2>
<div class="menu‑abas">
  <button class="btn‑aba ativo" onclick="abrirAba('abaClientes')">👤 Clientes</button>
  <button class="btn‑aba" onclick="abrirAba('abaEmprestimo')">📝 Empréstimo</button>
  <button class="btn‑aba" onclick="abrirAba('abaParcelas')">📋 Parcelas & Avisos</button>
  <button class="btn‑aba" onclick="abrirAba('abaPagamentos')">💳 Pagamentos</button>
  <button class="btn‑aba" onclick="abrirAba('abaRelatorios')">📊 Relatórios</button>
</div>

<div id="abaClientes" class="conteudo‑aba ativo">
  <div class="bloco">
    <h3>Cadastrar / Editar Cliente</h3>
    Nome: <input type="text" id="nomeCliente" placeholder="Ex: Maria Silva">
    Telefone: <input type="text" id="foneCliente" placeholder="Ex: (44) 99999‑1234">
    Observação: <textarea id="obsCliente" placeholder="Ex: melhor horário para ligar..."></textarea>
    <input type="hidden" id="idClienteEditar">
    <button onclick="cadastrarCliente()">Cadastrar</button>
    <button class="btn‑laranja" onclick="prepararEditarCliente()">Editar Selecionado</button>
    <button class="btn‑vermelho" onclick="excluirCliente()">Excluir Selecionado</button>
    <select id="selListaClientes"></select>
    <div id="listaClientes"></div>
  </div>
  <div class="bloco">
    <h3>Ficha do Cliente</h3>
    Selecione: <select id="selFicha"></select>
    <button onclick="mostrarFicha()">Abrir ficha</button>
    <div id="fichaCliente"></div>
  </div>
</div>

<div id="abaEmprestimo" class="conteudo‑aba">
  <div class="bloco">
    <h3>💵 Saldo inicial de caixa</h3>
    Valor inicial (R$): <input type="number" id="saldoInicial" placeholder="Ex: 3000">
    <button onclick="iniciarCaixa()">Definir saldo</button>
    <p class="resumo" id="exibeSaldo">Saldo atual: R$ 0,00</p>
  </div>
  <div class="bloco">
    <h3>📝 Gerar empréstimo (cálculo automático de parcelas)</h3>
    Cliente: <select id="selCliente"></select>
    Valor emprestado (que você entrega): <input type="number" id="valorEmprestado" placeholder="Ex: 1000">
    Porcentagem total de juros (%): <input type="number" id="porcJurosTotal" placeholder="Ex: 20">
    Taxa de juros de atraso (% ao dia): <input type="number" id="taxaAtraso" placeholder="Ex: 0,3">
    Tipo de vencimento:
    <select id="tipoVenc">
      <option value="diario">Diário</option>
      <option value="semanal">Semanal</option>
      <option value="quinzenal">Quinzenal</option>
      <option value="mensal">Mensal</option>
    </select>
    Data 1º vencimento: <input type="date" id="dataVenc">
    Quantidade de parcelas: <input type="number" id="qtdParcelas" placeholder="Ex: 5">
    <button onclick="calcularEgerarParcelas()">Calcular e gerar parcelas</button>
    <p id="resumoCalculo"></p>
  </div>
</div>

<div id="abaParcelas" class="conteudo‑aba">
  <div class="bloco">
    <h3>⚠️ Parcelas vencidas</h3>
    <button onclick="atualizarAlertas()">Atualizar alertas e juros</button>
    <div id="listaVencidos"></div>
  </div>
  <div class="bloco">
    <h3>🔔 Próximos vencimentos (até 3 dias)</h3>
    <div id="listaProximos"></div>
  </div>
  <div class="bloco">
    <h3>📅 Lista geral de parcelas</h3>
    <div id="listaParcelas"></div>
  </div>
  <div class="bloco">
    <h3>✏️ Alterar / Excluir parcela</h3>
    Índice da parcela: <input type="number" id="indiceParcAlt" placeholder="Ex: 1">
    Novo valor total (R$): <input type="number" id="novoValorParc" placeholder="Apenas para alterar">
    <button class="btn‑laranja" onclick="alterarValorParcela()">Alterar valor</button>
    <button class="btn‑vermelho" onclick="excluirParcela()">Excluir parcela</button>
  </div>
  <div class="bloco">
    <h3>🔍 Filtrar parcelas</h3>
    Cliente: <select id="filtroCliente"><option value="">Todos</option></select>
    Data inicial: <input type="date" id="filtroDataIni">
    Data final: <input type="date" id="filtroDataFim">
    <button onclick="aplicarFiltro()">Aplicar filtro</button>
    <button onclick="limparFiltro()">Limpar filtro</button>
  </div>
</div>

<div id="abaPagamentos" class="conteudo‑aba">
  <div class="bloco">
    <h3>💳 Registrar pagamento</h3>
    Índice da parcela: <input type="number" id="indiceParc" placeholder="Ex: 1">
    Valor recebido (R$): <input type="number" id="valorRecebido" placeholder="Ex: 200">
    <button onclick="pagamentoParcial()">Registrar pagamento</button>
    <p id="resultadoParcial"></p>
  </div>
  <div class="bloco">
    <h3>🔄 Renovação de empréstimo</h3>
    Valor devedor atual (R$): <input type="number" id="valorDevedor">
    Nova taxa (%): <input type="number" id="novaTaxa">
    <button onclick="renovarJuros()">Calcular valor renovado</button>
    <p id="resultadoRenovacao"></p>
  </div>
</div>

<div id="abaRelatorios" class="conteudo‑aba">
  <div class="bloco">
    <h3>📊 Relatório de caixa e lucro</h3>
    <button onclick="gerarRelatorio()">Gerar relatório</button>
    <button onclick="atualizarGrafico()">Atualizar gráfico</button>
    <div id="relatorio"></div>
    <div id="areaGrafico"><canvas id="graficoReceitas"></canvas></div>
  </div>
  <div class="bloco">
    <h3>💾 Backup e salvar dados</h3>
    <button onclick="salvarDados()">Salvar no navegador</button>
    <button onclick="carregarDados()">Carregar dados</button>
    <button onclick="exportarBackup()">Baixar arquivo de backup</button>
    <input type="file" id="arquivoBackup" accept=".json" onchange="importarBackup(event)">
    <button onclick="imprimirRelatorio()">Imprimir relatório</button>
    <p class="aviso">💡 Faça backup regularmente para não perder os dados!</p>
    <div id="areaImpressao" style="display:none;"></div>
  </div>
</div>

<script>
let saldoCaixa = 0;
let totalInvestido = 0;
let totalRecebido = 0;
let clientes = [];
let parcelasGeradas = [];
let movimentos = [];
let graficoReceitas = null;
let filtroAtivo = {cliente:"", dataIni:"", dataFim:""};

function abrirAba(idAba){
  document.querySelectorAll('.conteudo‑aba').forEach(el=>el.classList.remove('ativo'));
  document.querySelectorAll('.btn‑aba').forEach(el=>el.classList.remove('ativo'));
  document.getElementById(idAba).classList.add('ativo');
  event.target.classList.add('ativo');
}

function cadastrarCliente(){
  let nome = document.getElementById('nomeCliente').value.trim();
  let fone = document.getElementById('foneCliente').value.trim();
  let obs = document.getElementById('obsCliente').value.trim();
  let idEditar = document.getElementById('idClienteEditar').value;
  if(!nome){alert("Digite o nome do cliente!");return;}
  if(idEditar){
    let cli = clientes.find(c=>c.id==idEditar);
    cli.nome = nome; cli.fone = fone; cli.obs = obs;
    document.getElementById('idClienteEditar').value = "";
  }else{
    clientes.push({nome, fone, obs, id:Date.now()});
  }
  document.getElementById('nomeCliente').value = "";
  document.getElementById('foneCliente').value = "";
  document.getElementById('obsCliente').value = "";
  atualizarListaClientes();
  atualizarSelectClientes();
  atualizarFiltroCliente();
}
function prepararEditarCliente(){
  let idSel = document.getElementById('selListaClientes').value;
  if(!idSel){alert("Selecione um cliente primeiro!");return;}
  let cli = clientes.find(c=>c.id==idSel);
  document.getElementById('nomeCliente').value = cli.nome;
  document.getElementById('foneCliente').value = cli.fone||"";
  document.getElementById('obsCliente').value = cli.obs||"";
  document.getElementById('idClienteEditar').value = cli.id;
}
function excluirCliente(){
  let idSel = document.getElementById('selListaClientes').value;
  if(!idSel){alert("Selecione um cliente primeiro!");return;}
  if(!confirm("Tem certeza? Todas as parcelas desse cliente serão apagadas!")) return;
  let cli = clientes.find(c=>c.id==idSel);
  parcelasGeradas = parcelasGeradas.filter(p=>p.cliente !== cli.nome);
  clientes = clientes.filter(c=>c.id != idSel);
  atualizarListaClientes();
  atualizarSelectClientes();
  atualizarFiltroCliente();
  atualizarLista();
  atualizarAlertas();
}
function atualizarListaClientes(){
  let html = "";
  clientes.forEach(c=>{
    html += `<div class="item">👤 ${c.nome} — 📞 ${c.fone||"sem telefone"}<br><span class="obs">Obs: ${c.obs||"nenhuma"}</span></div>`;
  });
  document.getElementById('listaClientes').innerHTML = html;
  let selLista = document.getElementById('selListaClientes');
  selLista.innerHTML = "";
  clientes.forEach(c=> selLista.innerHTML += `<option value="${c.id}">${c.nome}</option>`);
}
function atualizarSelectClientes(){
  let sel = document.getElementById('selCliente');
  let selF = document.getElementById('selFicha');
  sel.innerHTML = ""; selF.innerHTML = "";
  clientes.forEach(c=>{
    sel.innerHTML += `<option value="${c.id}">${c.nome}</option>`;
    selF.innerHTML += `<option value="${c.id}">${c.nome}</option>`;
  });
}
function atualizarFiltroCliente(){
  let selFiltro = document.getElementById('filtroCliente');
  selFiltro.innerHTML = `<option value="">Todos</option>`;
  clientes.forEach(c=> selFiltro.innerHTML += `<option value="${c.nome}">${c.nome}</option>`);
}
function mostrarFicha(){
  let idSel = document.getElementById('selFicha').value;
  let cli = clientes.find(c=>c.id==idSel);
  let parcelasCli = parcelasGeradas.filter(p=>p.cliente===cli.nome);
  let totalDevido = 0; let totalPago = 0;
  parcelasCli.forEach(p=>{
    totalDevido += Number(p.valorTotal);
    totalPago += Number(p.valorPago);
  });
  let restante = totalDevido - totalPago;
  let html = `<h4>Ficha: ${cli.nome}</h4>`;
  html += `<p>Telefone: ${cli.fone||"sem telefone"}</p>`;
  html += `<p>Observação: ${cli.obs||"nenhuma"}</p>`;
  html += `<p>Total contratado: R$ ${totalDevido.toFixed(2)}</p>`;
  html += `<p>Total já pago: R$ ${totalPago.toFixed(2)}</p>`;
  html += `<p>Valor restante: R$ ${restante.toFixed(2)}</p>`;
  html += `<h5>Parcelas:</h5>`;
  parcelasCli.forEach(p=>{
    let sit = p.situacao==="paga" ? "✅ Paga" : "⏳ Pendente";
    html += `<p>• ${p.dataVenc} | R$ ${p.valorTotal} — ${sit}</p>`;
  });
  document.getElementById('fichaCliente').innerHTML = html;
}
function iniciarCaixa(){
  saldoCaixa = Number(document.getElementById('saldoInicial').value) || 0;
  atualizarSaldo();
}
function atualizarSaldo(){
  document.getElementById('exibeSaldo').innerText = "Saldo atual: R$ " + saldoCaixa.toFixed(2);
}
function calcularEgerarParcelas(){
  let valorEmprestado = Number(document.getElementById('valorEmprestado').value);
  let porcJurosTotal = Number(document.getElementById('porcJurosTotal').value);
  let taxaAtrasoDia = Number(document.getElementById('taxaAtraso').value)||0;
  let qtd = Number(document.getElementById('qtdParcelas').value);
  let idCli = document.getElementById('selCliente').value;
  let data1 = document.getElementById('dataVenc').value;
  let cli = clientes.find(c=>c.id==idCli);

  if(!valorEmprestado || !porcJurosTotal || !qtd || !cli || !data1){
    alert("Preencha todos os campos e cadastre um cliente!");
    return;
  }

  let valorTotalReceber = valorEmprestado * (1 + porcJurosTotal / 100);
  let valorParcela = valorTotalReceber / qtd;
  totalInvestido += valorEmprestado;

  document.getElementById('resumoCalculo').innerText =
    `👉 Valor emprestado: R$ ${valorEmprestado.toFixed(2)} | Total a receber: R$ ${valorTotalReceber.toFixed(2)} | Cada parcela: R$ ${valorParcela.toFixed(2)}`;

  let dataAtual = new Date(data1);
  for(let i=1;i<=qtd;i++){
    let dataVencStr = dataAtual.toISOString().split('T')[0];
    parcelasGeradas.push({
      numero:i, cliente:cli.nome, foneCliente:cli.fone, obsCliente:cli.obs,
      valorTotal:valorParcela.toFixed(2), valorPago:0, jurosAtraso:0, taxaAtrasoDia:taxaAtrasoDia,
      dataVenc:dataVencStr, situacao:"pendente"
    });
    let tipo = document.getElementById('tipoVenc').value;
    if(tipo==="diario") dataAtual.setDate(dataAtual.getDate()+1);
    else if(tipo==="semanal") dataAtual.setDate(dataAtual.getDate()+7);
    else if(tipo==="quinzenal") dataAtual.setDate(dataAtual.getDate()+15);
    else if(tipo==="mensal") dataAtual.setMonth(dataAtual.getMonth()+1);
  }
  atualizarLista(); atualizarAlertas();
}
function atualizarAlertas(){
  let hoje = new Date(); hoje.setHours(0,0,0,0);
  let limiteProximo = new Date(hoje); limiteProximo.setDate(hoje.getDate()+3);
  let htmlVencidos = ""; let htmlProximos = "";
  let temVencidos = false; let temProximos = false;
  parcelasGeradas.forEach(p=>{
    if(p.situacao==="pendente"){
      let dtV = new Date(p.dataVenc); dtV.setHours(0,0,0,0);
      let diasAtraso = Math.floor((hoje - dtV)/(1000*60*60*24));
      if(diasAtraso > 0){
        p.jurosAtraso = (Number(p.valorTotal) * (p.taxaAtrasoDia/100) * diasAtraso);
        temVencidos = true;
        let totalDev = (Number(p.valorTotal) + p.jurosAtraso - Number(p.valorPago)).toFixed(2);
        htmlVencidos += `<div class="item‑vencido">🔴 ${p.cliente} (${p.foneCliente||"sem fone"})<br>Parcela ${p.numero} | Venc.: ${p.dataVenc} | Juros de atraso: R$ ${p.jurosAtraso.toFixed(2)} | Restante c/juros: R$ ${

Game Source: Controle de Cobranças

Creator: PrismPanda27

Libraries: none

Complexity: complex (326 lines, 14.6 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: controle-de-cobran-as-prismpanda27" to link back to the original. Then publish at arcadelab.ai/publish.