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.
Análise de transferências em rede: uma base com todos os CDs, sequência de origens e destinos, carteira aprovada e baixa por faturamento.
Translation pending
FinanceTypeScript · CSSGitHub @nandorodriques37OnlineOwner 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
Loading the full README. The saved excerpt is shown below.
Transferências entre CDs — análise de rede Aplicativo web que planeja transferências de estoque entre todos os CDs da rede: cada CD pode ser origem (pelo que sobra) e destino (pelo que falta), na sequência que o planejador escolher — mostrando o benefício e o impacto fiscal (ICMS) por rota ao lado. Mudança de conceito (v2). Antes eram três bases e uma origem fixa (CD10 → demais CDs). Agora são duas bases e a rede inteira em uma análise só.Read the full README ↗
Aplicativo web que planeja transferências de estoque entre todos os CDs da rede: cada CD pode ser origem (pelo que sobra) e destino (pelo que falta), na sequência que o planejador escolher — mostrando o benefício e o impacto fiscal (ICMS) por rota ao lado.
Mudança de conceito (v2). Antes eram três bases e uma origem fixa (CD10 → demais CDs). Agora são duas bases e a rede inteira em uma análise só.
BASE ÚNICA DE CDs BASE DE PEDIDOS (opcional)
(todos os CDs empilhados) ano_mês · CD · produto
↓ o que sobra ↓ o que falta ↓
EXCESSO NECESSIDADE ←—— modo "saldo ideal" ou "pedidos"
Sequência de ORIGENS → Ordem de DESTINOS
CD10 → CD9 → CD8 CD1 → CD2 → CD7 → CD8
│ │
└── cada origem, na sua vez, olha TODOS os destinos na ordem
disponível + pendente − venda média 3m − estoque objetivoestoque objetivo − disponível − pendenteUm CD pode estar nas duas listas. A única regra absoluta: nenhum CD transfere para si mesmo.
lib/engine/)Puro, sem estado e testado (35 casos). Nenhum número de negócio é fixado em
código — tudo vem de ParametrosRede.
| # | Regra | Como o motor calcula |
|---|---|---|
| 1 | Preço | custo de reposição; se 0, preço de lista |
| 2 | Excesso (origem) | MAX(disp + pendente − venda média 3m − objetivo, 0). Com excesso físico ligado, o pendente sai da conta: só o que já está no CD é oferecido |
| 3 | Necessidade (destino) | modo saldo ideal: MAX(objetivo − disp − pendente, 0); modo pedidos: pedido projetado por (mês × CD) |
| 3b | Teto e piso de cobertura | necessidade limitada a venda_dia × dias − (disp + pendente + trânsito). Teto corta demanda inflada; piso garante o mínimo antirruptura. SKU sem giro no destino ignora os dois |
| 4 | Repartição | prioridade estrita (cascata gulosa) ou nivelar dias de cobertura (water-filling: enche primeiro quem está mais descoberto) |
| 5 | Ordem dos baldes | modo pedidos: mês → destino (mês 1 de todos os destinos antes do mês 2); modo saldo ideal: destino a destino |
| 6 | Embarque | opção de caixa fechada (múltiplos da embalagem, ROUNDDOWN), mínimo por linha (un e R$) e carga mínima por rota |
| 7 | Transferência imediata | MIN(transf, disp − venda média × fator), rateada entre os destinos na ordem, em caixa fechada |
| 8 | Cobertura | (disp + pendente) × 30 / venda média, na visão do CD de origem |
| 9 | Impacto fiscal | valor transferido × alíquota da ROTA (origem → destino) |
| 10 | Capacidade operacional | cada alocação consome três orçamentos ao mesmo tempo — expedição da origem, recebimento do destino e transporte da rota. O menor é o gargalo |
| 11 | Materialidade | só entram no plano rotas × SKU com transferência > 0 |
Invariantes verificados a cada análise (reconciliacao):
Desempenho: rede de 6 CDs × 20 mil produtos (120 mil linhas de base) em
menos de 1 s — teste de performance em lib/engine/calc.test.ts.
Todas essas regras vêm desligadas por padrão (o resultado sai igual à regra crua da base) e ficam na tela Nova análise. Cada grupo tem uma chave geral na barra do topo — Teto/piso de cobertura, Caixa fechada e mínimos e Capacidade operacional — mais um botão Desligar todas as restrições. Desligar não apaga nada: os valores continuam guardados e voltam a valer quando a chave é religada, o que torna barato comparar o plano com e sem restrição.
| Regra | Para que serve | Sugestão |
|---|---|---|
| Teto de cobertura (dias) | Um estoque objetivo inflado — ou 3 meses de pedido — puxa volume demais para um CD e trava capital do outro lado. O teto converte a demanda em dias de venda. | 60–90 dias |
| Piso de cobertura (dias) | Objetivo defasado ou zerado esconde uma ruptura real: sem piso, o CD em falta não aparece como destino. | 15–30 dias |
| Excesso físico | O pendente ainda não chegou; sugerir a transferência dele gera ordem que o WMS não consegue executar. | ligar quando o plano for executado no curto prazo |
| Nivelar dias de cobertura | Na prioridade estrita, o excesso escasso vai todo para o primeiro CD e o último fica em ruptura. O nivelamento reparte pelo giro. | usar quando os destinos têm importância parecida |
| Caixa fechada | Transferência quebrada não embarca no WMS. | ligar quando a operação exige caixa fechada |
| Mínimo por linha / carga mínima por rota | Cauda longa de 3 unidades e rotas de R$ 200 custam mais em frete e conferência do que o benefício. | mínimo por linha em R$ e carga mínima por rota |
O dashboard mostra a necessidade bruta → considerada por destino sempre que o teto ou o piso mudou o número, e o plano exporta a coluna não enviado por caixa fechada.
Um plano que sugere mais do que a rede consegue mover não é um plano, é uma lista de desejos. A análise aceita três limites, válidos ao mesmo tempo:
| Limite | O que representa |
|---|---|
| Expedição por origem | separação, embalagem e embarque que o CD consegue produzir na janela |
| Recebimento por destino | docas, conferência e endereços livres no CD que recebe |
| Transporte por rota | frota/viagens disponíveis entre aquele par de CDs |
Cada alocação consome os três — o menor é o gargalo. A capacidade é medida na unidade que faz sentido para a operação, e o motor converte cada unidade transferida usando dados do próprio SKU:
| Métrica | Fator por unidade | Coluna da base |
|---|---|---|
| Unidades | 1 | — |
| Caixas | 1 / embalagem de compra | Qt_Emb_Compra |
| Paletes | 1 / unidades por palete | unidades_por_palete |
| Peso (kg) | peso unitário | peso_unitario |
| Volume (m³) | cubagem unitária | cubagem_unitaria |
| Valor (R$) | preço unitário | custo/preço |
As três últimas colunas são opcionais — a validação da importação lista quais métricas a sua base sustenta. SKU sem o dado da métrica escolhida não consome capacidade, e o resultado diz quantos ficaram nessa situação.
Dois detalhes que fecham o ciclo:
O dashboard traz um painel com limite, comprometido, usado, % de utilização e quanto ficou barrado em cada ponto — e nomeia o gargalo da rede, que é onde ampliar capacidade libera mais transferência do que remexer prioridades.
npm test
A base anexada não precisa de nenhuma coluna de fórmula: o app lê só as
colunas cruas e recalcula tudo (mapa em CAMPOS_CALCULADOS, lib/data/schema.ts).
Os cabeçalhos aceitam variações e formato pt-BR (1.234,56); CSV em UTF-8 ou
latin1 é detectado automaticamente.
Base grande? Mande CSV. O CSV é lido em um passe direto (300 mil linhas em ~1,6 s, pico de ~270 MB). XLSX e XLSB passam pelo SheetJS, que carrega a planilha inteira: as mesmas 300 mil linhas levam ~18 s e passam de 1 GB de memória — perto do limite de uma função serverless.
Na importação, informe a data da posição de estoque — é ela que evita contar duas vezes (ou deixar de contar) uma transferência já faturada.
| Coluna | Campo | Obrigatória |
|---|---|---|
| CD / Depósito | cd | ✅ |
Código do produto (CodsemDv) | codigoProduto | ✅ |
| Estoque disponível | estoqueDisponivel | ✅ |
| Estoque objetivo | estoqueObjetivo | ✅ |
| Venda média 3 meses (média mensal) | vendaMedia3m | ✅ |
| Quantidade pendente | quantidadePendente | — |
Custo de reposição (custo, custo_reposicao…) / Preço de lista | custoReposicao / precoLista | — |
Embalagem de compra (embalagem, emb_compra…) | embCompra | — |
Descrição do produto (produto, descricao, descricao_produto) | produto | ✅ |
Categoria N3 (categoria_n3, CAT_NÍVEL_3, categoria) | categoriaN3 | ✅ — é a categoria exibida e filtrada no plano |
| Fornecedor · comprador · analista · categorias N1, N2, N4 | — | — |
| Unidades por palete | unidadesPorPalete | — (só para capacidade em paletes) |
| Peso unitário (kg) | pesoUnitario | — (só para capacidade em peso) |
| Cubagem unitária (m³) | cubagemUnitaria | — (só para capacidade em volume) |
Uma linha por (CD, produto). Chave repetida bloqueia a importação.
ano_mês (aceita 2026_07, 2026-07, 07/2026) · CD destino ·
código do produto · pedido.
CD origem · CD destino · código do produto · quantidade faturada ·
(opcional) documento/NF e data. O casamento é por rota + produto, na ordem
de aprovação, com baixa parcial quando a quantidade faturada é menor que a
aprovada. Linhas sem sugestão correspondente são listadas, e não alteram nada.
A data aceita dd/mm/aaaa (também com - ou .), aaaa-mm-dd, aaaammdd e
a célula de data do Excel (que chega como número, ex.: 46280). Data que
não é reconhecida usa a data do processamento e aparece como aviso na prévia.
A baixa grava tudo numa transação: ou entram todas as baixas e o registro da
importação, ou nada entra.
Você sobe a base a cada rodada. Entre uma rodada e outra, transferências são aprovadas, faturadas (ou não), e a base muda. Duas invariantes sustentam isso:
1. A data da posição de estoque decide o que já está refletido.
Ao importar, você informa quando a base foi extraída (não quando está subindo). O app compara essa data com a data de cada faturamento:
| Situação da sugestão | A base reflete? | O app… |
|---|---|---|
| Aprovada, não faturada | não — o estoque não saiu | desconta da origem, trata como trânsito no destino |
| Faturada antes da data da base | sim — a base já mostra a saída | não desconta (contar de novo seria descontar duas vezes) |
| Faturada depois da data da base | não — a base é anterior | continua descontando até a próxima base chegar |
É esse terceiro caso que evita o pior erro possível: importar uma base extraída antes do faturamento e o app sugerir de novo o que já saiu do CD.
2. Aprovar duas vezes a mesma rota + produto não soma volume inexistente.
Nenhum dos dois casos exige disciplina do operador: a trava é do app.
Outras proteções do ciclo:
análise → aprova linha → SUGESTÃO EM ABERTO ──(desconta origem e destino)──┐
│ │
│ importa faturamento │ próxima análise
▼ │
FATURADA (bases atualizadas já refletem) ───────┘
A caixa "Considerar sugestões já aprovadas" permite rodar uma análise do cenário cheio, ignorando a carteira, sem apagar nada.
Dashboard executivo — KPIs (excesso disponível, transferências, cobertura da necessidade, impacto fiscal), matriz origem × destino (heatmap na sequência escolhida), aproveitamento por origem, cobertura por destino, painel de capacidade com o gargalo da rede, maiores rotas e detalhe por rota (com quebra mensal no modo pedidos).
Nova análise — upload das duas bases com validação, escolha do modo de demanda, sequência de origens, ordem de destinos (cada CD mostra quanto tem de excesso/falta e o que já está comprometido), alíquotas por rota, regras de necessidade/materialidade e capacidade operacional. Um clique roda a análise.
Plano de transferência — uma linha por rota × SKU, filtros (origem, destino, categoria, comprador, cobertura, só imediata), busca, ordenação, exportação CSV/Excel e aprovação (selecionadas ou todas do filtro).
Carteira — sugestões em aberto e faturadas, cancelamento, modal de importação do faturamento (com prévia) e geração da ordem de transferência para ERP/WMS com o saldo em aberto. No dashboard, o histórico de análises permite excluir uma ou várias (seleção em lote). A confirmação lista o que sai, avisa se é a análise aberta e mostra quantas sugestões em aberto cada uma ainda tem na carteira — com a opção de cancelá-las junto (desligada por padrão). Excluir apaga o resultado e o plano guardado; faturamento já registrado nunca é apagado.
Acompanhamento — o que foi aprovado virou NF, e em quanto tempo:
aprovado = faturado + em aberto + cancelado, e o
atendimento é faturado ÷ (aprovado − cancelado);O período (30, 90, 180 dias ou tudo) filtra pela data de aprovação.
A decisão de arquitetura é simples: o app guarda resultado, não base.
A base pesada entra, é analisada e sai. O que fica é o resultado.
| Camada | O que é | Onde vive | Sobrevive a deploy/instância? |
|---|---|---|---|
| Carteira | sugestões aprovadas e baixas por faturamento | Postgres (Neon) | ✅ |
| Resultado das análises | parâmetros, KPIs, resumos por rota/origem/destino, o que foi aprovado | Postgres, ou o armazenamento quando não há banco | ✅ |
| Plano | as linhas origem × destino × SKU, só com o que o app usa | Armazenamento, em TSV com dicionário | ✅ |
| Insumo (base + pedidos) | a planilha normalizada | Armazenamento, em TSV comprimido | ✅ |
| Base crua de 900 mil linhas | o arquivo que você sobe | — | descartada após a importação |
O ponto: o plano é pequeno. A base tem 900 mil linhas com dezenas de colunas; o plano tem as linhas que interessam, com os campos que o app de fato mostra — e os campos deriváveis (valor, caixas, impacto fiscal, status de cobertura) nem são gravados, são recalculados na leitura.
Medido na escala real — 11 CDs × 80 mil produtos:
| Etapa | Tempo | Tamanho |
|---|---|---|
| Importar a base (880 mil linhas, CSV de 156 MB) | 18 s | insumo guardado: 7,6 MB |
| Rodar a análise (1 origem → 10 destinos) | 2 s (motor: 0,5 s) | 238.800 linhas de plano |
| Guardar o plano | — | 2,2 MB |
| Instância fria: abrir o dashboard | 16 ms | — |
| Instância fria: abrir o plano completo | 948 ms | 325 MB de RSS |
| Filtrar de novo (já em cache) | 54 ms | — |
Visualizar, filtrar, exportar e aprovar não dependem da base: só de rodar uma análise nova. Enquanto a base estava carregada, o processo usava 1,9 GB; depois que ela sai de cena, o app trabalha com 325 MB.
O corpo de uma função serverless no Vercel não passa de 4,5 MB, e a base real tem dezenas de MB. Com um Blob store conectado ao projeto, a tela envia o arquivo direto do navegador para o Vercel Blob e a importação recebe só a URL — o arquivo nunca atravessa a função. Sem o token, o app cai no upload tradicional (que funciona local e para arquivos pequenos) e a tela avisa qual caminho está ativo.
Vale para as três planilhas — base, pedidos e faturamento. Detalhes:
upload/.Números medidos com 300 mil linhas (CSV de 54 MB): importação em ~4 s, e o insumo normalizado ocupa 2,1 MB no armazenamento.
npm install
npm run dev # http://localhost:3000
Importe o repositório no Vercel (framework Next.js detectado
automaticamente; vercel.json ajusta o timeout das rotas pesadas).
Banco (Vercel Neon / Postgres): crie um banco Neon no painel do Vercel e
conecte ao projeto — POSTGRES_URL (ou DATABASE_URL) é injetada
automaticamente. Opcionalmente aplique o esquema completo:
npm run seed:pg
Guarda a carteira (sugestao_transferencia, faturamento_evento) e o
resultado das análises (analise_resultado) — as tabelas são criadas sob
demanda.
Armazenamento de arquivos (Vercel Blob): crie um Blob store com acesso
Private e conecte ao projeto. Lojas novas autenticam por OIDC: o
Vercel injeta só BLOB_STORE_ID, e o upload direto usa URL pré-assinada
emitida pelo servidor. Lojas antigas, com BLOB_READ_WRITE_TOKEN, seguem
funcionando pelo token de cliente. É o que habilita o upload direto (arquivos acima de
4,5 MB) e o que faz o insumo sobreviver à troca de instância. Sem ele, o app
grava em .data/ no disco local — bom para desenvolvimento, efêmero no
serverless.
Por que privado. A base traz estoque e custo de todos os CDs. Numa loja pública, qualquer pessoa com a URL de um arquivo lê o conteúdo. Na privada, toda leitura exige o token, que só o servidor tem. O acesso é escolhido na criação da loja e não muda depois; o app usa privado por padrão. Uma loja pública já existente só funciona com
BLOB_ACESSO=public— prefira criar outra, privada.
Sem banco e sem Blob, tudo roda em memória: útil para avaliar, mas a tela sinaliza o estado e nada sobrevive a um novo deploy.
Variáveis de ambiente
| Variável | Para quê |
|---|---|
POSTGRES_URL / DATABASE_URL | carteira e resultados das análises |
BLOB_STORE_ID | loja do Blob conectada por OIDC (lojas novas) |
BLOB_READ_WRITE_TOKEN | loja do Blob com token fixo (lojas antigas) |
BLOB_ACESSO | public só para uma loja pública antiga (padrão: privada) |
main.main dispara o deploy de produção; cada PR ganha um Preview.main: PR obrigatório + aprovação + status check
CI / test-build (.github/workflows/ci.yml, roda npm test e
npm run build).Autenticação (SSO):
lib/auth.tsé um stub pronto para o SSO corporativo (lêx-user-email). Em produção, plugue o provedor (ex.: Azure AD via NextAuth) mantendo a trilha de auditoria já existente.
app/ Telas + rotas de API
analise/ Nova análise (bases, sequências, parâmetros)
plano/ Plano de transferência + aprovação
carteira/ Sugestões aprovadas + faturamento + ordem ERP
acompanhamento/ Aprovado × faturado e prazo até a NF
api/ status · cds · params · import · importlog · analise ·
dashboard · plano[/export] · carteira[/ordem] · faturamento ·
acompanhamento
lib/engine/ Motor de rede (puro) + tipos + testes
lib/data/ Esquemas, parsing, validação, leitura de planilha, seed
lib/query/ Filtro/paginação/agregação server-side
lib/store/ Estado da instância, carteira, resultados das análises,
plano persistido, repositório do insumo e armazenamento
lib/export.ts CSV/Excel do plano e ordem de transferência
components/ Nav, UI e o seletor ordenado de CDs
Explícitos de propósito — o motor é guloso e determinístico, não um otimizador:
venda_media_3m é lida como média
mensal dos 3 meses. Se a base trouxer a soma do trimestre, o excesso e a
cobertura saem distorcidos — divida por 3 antes de importar.Frete por rota e consolidação de carga, validade (shelf life) e lead time no horizonte, capacidade por janela diária, solver de otimização global como modo avançado (a cascata gulosa segue como padrão) e integração direta com o ERP para dispensar a importação manual do faturamento.
DADOS_DIRpasta local do armazenamento em disco (padrão .data) |
DEMO_DATA | 1 força a base de exemplo; 0 desliga em qualquer ambiente |