Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
Cadastro de clientes, filiais e serviços vendidos.
Translation pending
FinanceTypeScript · PLpgSQLGitHub @GoesGuilhermeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Sistema de cadastro e cobrança para várias empresas assinantes. Cada empresa vê só os próprios clientes, serviços e faturas.
O cliente é quem a empresa atende e pode ter várias unidades (filiais). A empresa monta o próprio catálogo, vende por cliente e cobra pelo Asaas: fatura avulsa, mensalidade recorrente e webhook. Nada de texto, vencimento ou encargos fica preso a uma empresa específica. A Cora não entra neste código.
A interface, as mensagens de validação e este texto estão em português do Brasil.
Cada empresa assinante vê só os próprios clientes e filiais. O CPF/CNPJ do cliente é único dentro da empresa, não no sistema inteiro.
/cadastro. A primeira pessoa vira dona da conta.POST /api/webhooks/asaas autentica o cabeçalho asaas-access-token de cada empresa e atualiza a situação da cobrança sem processar o mesmo evento duas vezes.É preciso Node.js 22 e um PostgreSQL 16. O caminho mais simples é o Docker.
cp .env.example .env
docker compose up -d
npm install
npx prisma migrate deploy
npm run db:seed
npm run dev
Abra http://localhost:3000.
O docker compose sobe o Postgres na porta 5432, cria o banco gestaocobranca e, na primeira inicialização do volume, também o banco gestaocobranca_test usado pelos testes.
Se você já tem um PostgreSQL, crie o usuário e os bancos e aponte o .env:
CREATE USER gestao WITH PASSWORD 'gestao';
CREATE DATABASE gestaocobranca OWNER gestao;
CREATE DATABASE gestaocobranca_test OWNER gestao;
O seed cria um acesso fictício, só para desenvolvimento:
[email protected]exemplo-1234Os clientes gravados pelo seed têm Exemplo no nome. CPF, CNPJ, telefones e e-mails são inventados. Não são empresas reais.
Quem esquece a senha pede um link em Esqueci minha senha. O e-mail sai pela Resend da plataforma (AUTH_RESEND_API_KEY), de Taguaro <[email protected]> (AUTH_EMAIL_FROM). Não usa a chave que cada empresa grava na régua. Sem a chave, o pedido falha com um aviso genérico e o link não aparece no log.
O boleto e a régua saem do endereço gravado em Régua, no campo E-mail do remetente. Se a empresa ainda não preencheu, o código usa [email protected]. O dono pode trocar esse endereço na mesma tela. A chave da Resend continua sendo a da empresa.
O seed recusa rodar quando NODE_ENV é production. Em produção o primeiro acesso da Collab já nasce na migration multiempresa. npm run criar-admin ainda serve para criar outro superadministrador nessa empresa, se o e-mail ainda não existir.
A migration 20261021235900_multiempresa cria a empresa Collab Software e liga nela os usuários e clientes que já existirem. O CNPJ gravado é 00000000000000. Esse número é um placeholder, não é um CNPJ válido, e precisa ser trocado na tela Empresa pelo dono. O e-mail [email protected] fica como dono dessa empresa e superadministrador da plataforma. Outros usuários que já existirem entram na mesma empresa como donos, sem o flag da plataforma.
npm test
O comando cria o banco gestaocobranca_test se ele ainda não existir (usando o usuário do .env), aplica as migrations nele e roda:
Os testes apagam empresas, usuários, clientes, filiais, serviços, vínculos, faturas, a configuração do Asaas, a régua, os lembretes, os eventos de webhook e o limite de cadastro desse banco de teste.
| Variável | Para que serve |
|---|---|
DATABASE_URL | Conexão da aplicação. Na Vercel, o pooler de transação do Supabase (porta 6543) com ?pgbouncer=true. |
DIRECT_URL | Conexão do Prisma Migrate. Direta ou pooler em modo sessão, porta 5432. No computador local, igual à DATABASE_URL. |
AUTH_SECRET | Segredo do cookie de sessão. Use uma string longa e aleatória (openssl rand -base64 32). |
SEED_OWNER_NOME | Nome do acesso fictício do seed local. |
SEED_OWNER_EMAIL | E-mail desse acesso. |
SEED_OWNER_SENHA | Senha do seed local. Só é gravada se o e-mail ainda não existir. |
SEED_EXEMPLOS | true grava os clientes fictícios no seed local. false grava só o acesso de exemplo. |
ADMIN_NOME | Nome do primeiro acesso real. Só para npm run criar-admin. |
ADMIN_EMAIL | E-mail desse acesso. |
ADMIN_SENHA | Senha em texto. O script grava o hash e não altera a senha se o e-mail já existir. |
ASAAS_ENCRYPTION_KEY | 32 bytes em base64 (openssl rand -base64 32). Cifra, no banco, a chave da API do Asaas, o token do webhook, a chave da Resend e o auth token da subconta Twilio de cada empresa. Não é a chave da API e não pode ir para o repositório. Sem ela, a tela Asaas e a régua não gravam nem leem esses segredos. Não troque essa chave se já houver segredo gravado. |
TWILIO_ACCOUNT_SID | Account SID da conta pai da Twilio (começa com AC). Não é digitado na tela. |
TWILIO_AUTH_TOKEN | Auth token da conta pai. Só no servidor. A subconta de cada empresa é criada pela API e o token dela fica cifrado. |
TWILIO_WEBHOOK_URL | URL pública exata de /api/webhooks/twilio. Em produção, https. A Twilio assina esse endereço. |
TWILIO_WHATSAPP_TESTE | Número que recebe o WhatsApp enquanto o modo teste da régua está ligado. Sem ele, o modo teste não envia. |
O banco de produção já está no Supabase, região sa-east-1, projeto ziqjcworupopkcnfddjr. As tabelas iniciais e o RLS foram aplicados à mão, com o SQL de prisma/migrations/20261020225413_init e o de 20261021180000_enable_rls. A aplicação sobe na Vercel.
A migration 20261021235900_multiempresa não é aplicada por este repositório no banco de produção. Depois do merge, na sua máquina, com DIRECT_URL apontando para esse projeto:
npx prisma migrate deploy
Esse comando aplica só o que ainda falta. Se _prisma_migrations ainda não registrar as duas primeiras, marque-as antes, senão o deploy tenta criar de novo tipos que já existem:
npx prisma migrate resolve --applied 20261020225413_init
npx prisma migrate resolve --applied 20261021180000_enable_rls
npx prisma migrate deploy
O deploy da multiempresa cria a Collab Software, liga o usuário existente e liga o RLS em empresas e limites_cadastro. Em seguida, entre como Guilherme e troque o CNPJ placeholder na tela Empresa. Sessões antigas deixam de valer: o cookie passou a carregar a empresa e o papel, então é preciso entrar de novo.
O Prisma 7 não aceita url nem directUrl dentro de schema.prisma. O papel do antigo directUrl está em prisma.config.ts: as migrations leem DIRECT_URL. O servidor Next lê DATABASE_URL.
No projeto, abra Connect (ou Project Settings → Database). Escolha o formato URI e copie duas URLs.
pooler.supabase.com e a região sa-east-1. Essa é a DATABASE_URL. Confira se a URL termina com ?pgbouncer=true. Se o painel não incluir esse parâmetro, acrescente. O adapter não usa prepared statements nomeados, que é o que o pooler de transação exige; o driver pg ignora o parâmetro pgbouncer em si.db.<ref>.supabase.co, porta 5432). Essa é a DIRECT_URL. Não use a porta 6543 aqui: o prisma migrate não atravessa o pooler de transação.Se a senha do banco tiver @, # ou espaço, troque esses caracteres pelo código percentual na URL, ou gere outra senha no painel.
O RLS fica ligado nas tabelas, sem política. O papel postgres, usado por essas duas URLs, ignora o RLS. A API REST pública do Supabase continua sem acesso. A migration multiempresa liga o RLS também em empresas e limites_cadastro.
Em Project Settings → Environment Variables, no ambiente Production (e no Preview, se ele apontar para um banco):
| Variável | Valor |
|---|---|
DATABASE_URL | Pooler de transação, porta 6543, com ?pgbouncer=true |
DIRECT_URL | Conexão direta ou pooler em modo sessão, porta 5432 |
AUTH_SECRET | Saída de openssl rand -base64 32 |
ASAAS_ENCRYPTION_KEY | Outra saída de openssl rand -base64 32, diferente do AUTH_SECRET e diferente da chave da API do Asaas. A mesma chave cifra a Resend e o token da subconta Twilio de cada empresa. Não rotacione se já houver segredo gravado. |
TWILIO_ACCOUNT_SID | Account SID da conta pai. |
TWILIO_AUTH_TOKEN | Auth token da conta pai. |
TWILIO_WEBHOOK_URL | https://<seu-dominio>/api/webhooks/twilio |
TWILIO_WHATSAPP_TESTE | Número de teste do dono, em formato nacional ou E.164. |
CRON_SECRET | Saída de openssl rand -base64 32. A Vercel Cron envia esse valor em Authorization: Bearer para /api/cron/regua. |
Não defina ASAAS_MOCK nem TWILIO_MOCK na Vercel. Em produção a integração chama https://api.asaas.com/v3 ou https://api-sandbox.asaas.com/v3, conforme o ambiente salvo pelo dono, e envia a chave no cabeçalho access_token. A chave real ainda não entra neste repositório.
Essas variáveis precisam existir no build e em runtime. O postinstall e o script build rodam prisma generate, e o arquivo prisma.config.ts lê DIRECT_URL ao carregar. A aplicação lê DATABASE_URL e AUTH_SECRET quando atende um pedido. O cookie de sessão sai com secure quando NODE_ENV é production.
Não defina ADMIN_SENHA na Vercel e não rode npm run db:seed contra o banco de produção. O seed de exemplo está bloqueado quando NODE_ENV é production.
O build da Vercel não aplica migration.
Num banco vazio, no futuro, o caminho normal continua sendo npx prisma migrate deploy, que aplica as migrations em ordem. A pasta database/ é uma cópia legível desses SQLs; a fonte da verdade continua em prisma/migrations.
Com DATABASE_URL (ou, se só ela existir, DIRECT_URL) apontando para o mesmo banco:
ADMIN_NOME="Seu Nome" \
ADMIN_EMAIL="[email protected]" \
ADMIN_SENHA="uma-senha-longa" \
npm run criar-admin
O script grava o hash da senha do mesmo jeito que o login confere, na empresa Collab Software, como dono e superadministrador. Se o e-mail já existir, ele para e não troca a senha. Não use este comando para o Guilherme depois da migration multiempresa: esse acesso já fica marcado.
src/lib — CPF/CNPJ, máscaras, rótulos e os esquemas de validação usados tanto na tela quanto no servidor.src/server/services — regras de empresa, usuário, cliente, filial, serviço, resultado, Asaas e fatura. Cliente, fatura e configuração sempre recebem a empresa da sessão, nunca um id vindo do navegador. O arquivo src/server/services/index.ts é o ponto de entrada estável.src/app/api — rotas HTTP finas em cima desses serviços. As telas chamam essas rotas para criar, editar e excluir. POST /api/webhooks/asaas não usa o cookie: autentica o token da empresa.prisma/schema.prisma — Empresa 1-N Usuario, Cliente e Servico. Cliente 1-N Filial, ClienteServico e Fatura. ClienteServico guarda o valor combinado daquele cliente. ConfiguracaoAsaas e PreferenciaCobranca são uma por empresa. O campo Empresa.plano continua sendo a assinatura da empresa na plataforma, separado desse catálogo e separado da cobrança no Asaas. As migrations em prisma/migrations são a fonte da verdade do banco.database/ — os mesmos SQLs, com comentário curto, para quem não usa o Prisma.Um servidor MCP futuro (buscar cliente, listar filiais, e as ações de escrita) deve importar os serviços, não o Prisma. Os próximos módulos entram como tabela, serviço e rota próprios:
Dono e financeiro ativam a cobrança recorrente nas vendas MENSAL ativas. Forma, dia de vencimento, texto da descrição, multa, juros, desconto e a opção de atualizar cobranças em aberto começam pelas preferências da empresa, gravadas pelo dono em Asaas. Cada venda e o lote podem trocar esses valores. Sem percentual informado, a cobrança não envia multa, juros nem desconto, e o Asaas conserva o padrão da conta. O prazo de gerar a cobrança antes do vencimento não vai na assinatura: o Asaas usa 7, 14 ou 40 dias na configuração da conta, e o número escolhido fica só na preferência. A assinatura usa ciclo mensal. Dá para editar, pausar ou cancelar. O lote mostra a prévia antes de criar. As cobranças da assinatura entram em Faturas pelo webhook (o pagamento traz o campo subscription) e pelo botão Importar faturas, sem duplicar. Suporte não vê faturas nem recorrência. A chave da API continua só com o dono.
A régua de cobrança fica em Régua, só para o dono alterar e para o financeiro consultar. A empresa liga a régua, monta várias réguas nomeadas (uma é a padrão) e define, em cada uma, os passos, o horário e se o envio sai no fim de semana. O cliente usa a padrão, outra régua, nenhuma, ou fica pausado. O passo pode ser na emissão ou em dias antes, no vencimento ou em atraso, com assunto e texto usando {cliente}, {servico}, {valor}, {vencimento}, {link}, {dias_atraso} e {empresa}. O e-mail sai pela Resend da própria empresa; a chave fica cifrada com ASAAS_ENCRYPTION_KEY e a tela só diz se ela está configurada. O mesmo passo pode marcar WhatsApp. Aí a mensagem usa um template UTILITY da Twilio (boleto gerado, vence em X dias, venceu ou pago), com nome, valor, vencimento e link do Asaas. A empresa tem uma subconta e um Sender. O webhook POST /api/webhooks/twilio valida X-Twilio-Signature. Régua pausada, opt-out e o modo teste seguram o envio: no modo teste o WhatsApp só vai para TWILIO_WHATSAPP_TESTE. A rota /api/cron/regua roda todo dia, exige CRON_SECRET e também esvazia a fila. Fora de produção o envio não chama a Resend nem a Twilio.
A forma também pode ser cartão de crédito. A assinatura nasce no Asaas com billingType CREDIT_CARD e sem dados de cartão: o cliente informa o cartão na página da primeira cobrança, e o Asaas passa a cobrar sozinho. A venda mostra “Aguardando cadastro do cartão” até o webhook confirmar o pagamento, “Cartão cadastrado” (só bandeira e quatro últimos dígitos, quando o Asaas os devolve) ou “Cartão recusado”. O número completo e o CVV não entram no banco nem no log. Qualquer cobrança com link pode ser copiada, aberta no WhatsApp de quem está usando o sistema ou no e-mail, com o texto gravado nas preferências da empresa ({cliente}, {servico}, {valor}, {vencimento}, {link}). Nada disso envia a mensagem sozinho.
A Cora não é alterada por estes módulos.
O cliente aceita CPF ou CNPJ. A filial aceita CNPJ. A validação usa o cálculo dos dígitos verificadores. No CNPJ, a raiz pode ter letras (formato alfanumérico): o valor de cada caractere é o código ASCII menos 48, e os dois dígitos verificadores continuam numéricos. O documento é guardado sem pontuação e mostrado com máscara.
TWILIO_MOCK | Só fora de produção. true em produção é recusado. Fora de produção o envio já fica no mock e não chama a Twilio. |
CRON_SECRET | Segredo da rota diária /api/cron/regua. A Vercel Cron envia Authorization: Bearer. Sem ele a rota responde 500 e não envia. Não é a chave da Resend. |
ASAAS_MOCK | Só no computador de desenvolvimento. true usa um Asaas em memória, sem rede e sem chave real, para a tela funcionar antes de existir a chave. Se NODE_ENV for production, a aplicação recusa esse modo. Não defina na Vercel. |