Sistema de Gestão
Carregando...
🔴 Manutenções vencidas — clientes que não retornaram
🟡 Manutenções nos próximos 7 dias
🏆 Ranking VIP — maiores LTVs
📊 Sazonalidade mensal
🎯 Origem dos clientes
📦 Produtos mais vendidos
🎂 Aniversariantes do mês
😴 Clientes sumidos (+90 dias)
Cliente
Telefone
Cidade / Bairro
Origem
Status
Ações
Cliente
NPS
Classificação
Ação
🔗 Google Sheets
Um único Google Sheets com 6 abas (criadas automaticamente pelo script):

CRM:
clientes · compras · produtos · leads · config

Financeiro:
transacoes: id|tipo|valor|data|cliente|tipoVenda|tipoDespesa|categoria|forma|obs

Um único Apps Script serve tudo — cole o script na aba Extensions > Apps Script e publique como Web App.
🔐 Senha
Padrão: admin123
A senha é salva no Google Sheets (aba config) e sincroniza em todos os dispositivos automaticamente. Se o Sheets não estiver configurado, fica salva só neste navegador.
Exigir senha para entrar
Se desativado, o sistema abre direto, sem pedir senha.
🧹 Limpar Cache
Se o sistema travar ou não carregar, use este botão para limpar o cache do navegador e reiniciar.
💬 Mensagens do WhatsApp
Personalize as mensagens enviadas pelo sistema. Use {nome} para inserir o nome do cliente automaticamente.
// ═══════════════════════════════════════════════════════════════════
// APPS SCRIPT UNIFICADO — Sistema Completo (CRM + Financeiro)
// Cole este código em Extensions > Apps Script do seu Google Sheets
// Publique como: Deploy > New Deployment > Web App
//   Execute as: Me | Who has access: Anyone
// ═══════════════════════════════════════════════════════════════════

const SS = SpreadsheetApp.getActiveSpreadsheet();

// ── Utilitário: pega ou cria aba ─────────────────────────────────
function getSheet(name) {
  let sheet = SS.getSheetByName(name);
  if (!sheet) {
    sheet = SS.insertSheet(name);
    // Adiciona cabeçalhos padrão por aba
    const headers = {
      clientes:    ['id','nome','telefone','email','cidade','bairro','aniversario','origem','interesse','tags','status','nps','depoimento','ultimoContato','cadastroEm','obs'],
      compras:     ['id','clienteId','produto','data','valor','formaPagamento','vendedor','proximaManutencao','frequencia','obs'],
      produtos:    ['id','nome','categoria','descricao','preco','custo','margem','frequencia','status','tags','cadastroEm'],
      leads:       ['id','nome','telefone','origem','cidade','interesse','valor','coluna','proxAcao','proxData','motivo','obs','criadoEm','log'],
      config:      ['chave','valor'],
      transacoes:  ['id','tipo','valor','data','cliente','tipoVenda','tipoDespesa','categoria','forma','obs'],
    };
    if (headers[name]) sheet.appendRow(headers[name]);
  }
  return sheet;
}

// ── Lê aba como array de objetos ─────────────────────────────────
function readSheet(name) {
  const sheet = SS.getSheetByName(name);
  if (!sheet) return [];
  const rows = sheet.getDataRange().getValues();
  if (rows.length < 2) return [];
  const headers = rows[0];
  return rows.slice(1).map(r => {
    const o = {};
    headers.forEach((h, i) => o[h] = r[i]);
    return o;
  });
}

// ── Salvar linha ─────────────────────────────────────────────────
function saveRow(sheetName, row) {
  getSheet(sheetName).appendRow(row);
}

// ── Editar linha por ID ──────────────────────────────────────────
function editRow(sheetName, id, row) {
  const sheet = getSheet(sheetName);
  const rows = sheet.getDataRange().getValues();
  for (let i = 1; i < rows.length; i++) {
    if (String(rows[i][0]) === String(id)) {
      sheet.getRange(i + 1, 1, 1, row.length).setValues([row]);
      return;
    }
  }
}

// ── Deletar linha por ID ─────────────────────────────────────────
function deleteRow(sheetName, id) {
  const sheet = SS.getSheetByName(sheetName);
  if (!sheet) return;
  const rows = sheet.getDataRange().getValues();
  for (let i = 1; i < rows.length; i++) {
    if (String(rows[i][0]) === String(id)) {
      sheet.deleteRow(i + 1);
      return;
    }
  }
}

// ── Resposta JSON ────────────────────────────────────────────────
function jsonResp(data) {
  return ContentService
    .createTextOutput(JSON.stringify(data))
    .setMimeType(ContentService.MimeType.JSON);
}

// ═══════════════════════════════════════════════════════════════════
// doGet — CRM (payload base64) + Financeiro (GET simples por sheet)
// ═══════════════════════════════════════════════════════════════════
function doGet(e) {
  try {
    // ── CRM: payload base64 ──────────────────────────────────────
    if (e.parameter && e.parameter.payload) {
      const data = JSON.parse(
        Utilities.newBlob(
          Utilities.base64Decode(e.parameter.payload, Utilities.Charset.UTF_8)
        ).getDataAsString()
      );
      const sheetName = data.sheet || 'clientes';

      if (data.action === 'salvar') {
        saveRow(sheetName, data.row);
        return jsonResp({ ok: true });
      }
      if (data.action === 'editar') {
        editRow(sheetName, data.id, data.row);
        return jsonResp({ ok: true });
      }
      if (data.action === 'deletar') {
        deleteRow(sheetName, data.id);
        return jsonResp({ ok: true });
      }
      // salvar_todos (Kanban bulk save — não usado mais, mas por segurança)
      if (data.action === 'salvar_todos') {
        return jsonResp({ ok: true });
      }
    }

    // ── Leitura simples por ?sheet=nome ─────────────────────────
    const which = (e.parameter && e.parameter.sheet) || 'clientes';
    return jsonResp(readSheet(which));

  } catch (err) {
    return jsonResp({ ok: false, error: err.message });
  }
}

// ═══════════════════════════════════════════════════════════════════
// doPost — Financeiro (JSON no body: { action, id, tipo, valor, ... })
// ═══════════════════════════════════════════════════════════════════
function doPost(e) {
  try {
    const data = JSON.parse(e.postData.contents);
    const sheet = data.sheet || 'transacoes';

    // ── Salvar transação ─────────────────────────────────────────
    if (data.action === 'salvar') {
      const row = [
        data.id, data.tipo, data.valor, data.data,
        data.cliente || '', data.tipoVenda || '',
        data.tipoDespesa || '', data.categoria || '',
        data.forma || '', data.obs || ''
      ];
      saveRow(sheet, row);
      return jsonResp({ ok: true });
    }

    // ── Deletar transação ────────────────────────────────────────
    if (data.action === 'deletar') {
      deleteRow(sheet, data.id);
      return jsonResp({ ok: true });
    }

    // ── Editar transação ─────────────────────────────────────────
    if (data.action === 'editar') {
      const row = [
        data.id, data.tipo, data.valor, data.data,
        data.cliente || '', data.tipoVenda || '',
        data.tipoDespesa || '', data.categoria || '',
        data.forma || '', data.obs || ''
      ];
      editRow(sheet, data.id, row);
      return jsonResp({ ok: true });
    }

    return jsonResp({ ok: false, error: 'Ação não reconhecida: ' + data.action });

  } catch (err) {
    return jsonResp({ ok: false, error: err.message });
  }
}
Entradas
R$ 0
0 vendas
Saídas
R$ 0
0 despesas
Lucro
R$ 0
margem 0%
Ticket médio
R$ 0
por venda
Entradas por categoria
Saídas por categoria
Entradas por tipo de venda
Evolução financeira mensal
Total filtrado R$ 0
Data
Descrição
Categoria
Tipo / Despesa
Forma
Valor
Composição das despesas por mês
Mês
Saldo Ant.
Entradas
Saídas
Saldo
Evolução mensal detalhada
Configurar meta do mês
Progresso — Faturamento
Progresso — Lucro Líquido
O que fazer para bater a meta
Configure a meta acima.
Concentração de receita
Carregando...
Nova obrigação
Nenhuma obrigação cadastrada.
Como funciona: O caixa é calculado dos seus lançamentos. Contas a Pagar vem do módulo CAP. Os demais campos (imobilizado, estoque, empréstimos, capital) você preenche manualmente. Os dados ficam salvos no navegador.
ATIVO
Ativo Circulante
Caixa e equivalentes (auto) R$ 0,00
Contas a receber
Estoque
Outros circulantes
Total Ativo Circulante R$ 0,00
Ativo Não Circulante
Imobilizado (equipamentos)
Depreciação acumulada (-)
Outros não circulantes
Total Ativo Não Circulante R$ 0,00
TOTAL DO ATIVO R$ 0,00
PASSIVO + PATRIMÔNIO LÍQUIDO
Passivo Circulante
Contas a pagar (auto) R$ 0,00
Empréstimos CP
Impostos a recolher
Outros passivos CP
Total Passivo Circulante R$ 0,00
Passivo Não Circulante
Empréstimos LP
Outros passivos LP
Total Passivo Não Circulante R$ 0,00
Patrimônio Líquido
Capital social
Lucros acumulados (auto) R$ 0,00
Reservas
Total PL R$ 0,00
TOTAL PASSIVO + PL R$ 0,00