Synaplan
Open-source AI platform for companies: self-hosted AI agents, GDPR-compliant chatbot widget, RAG knowledge base, WhatsApp and Outlook AI, MCP, multi-model routing. Docker, Kubernetes or hosted in Germ
Gerenciar estoque, ressuprimento e processos de uma distribuidora
Translation pending
AI summary · website and README
BusinessJavaScript · HTMLGitHub @NicolasLeme-sudoOnlineOwner 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.
Report Distribuidora — Balanço e Detalhamento de Estoque Dashboard executivo de estoque da distribuidora, com a hierarquia Armazém › Marca › Família em listagem que abre e fecha, no modelo do dash financeiro usado como referência.Read the full README ↗
Dashboard executivo de estoque da distribuidora, com a hierarquia Armazém › Marca › Família em listagem que abre e fecha, no modelo do dash financeiro usado como referência.
A base técnica e a identidade visual são herdadas do Report E-commerce
(CD Vulcabras — Extrema/MG), mas este é um projeto independente: repositório
próprio, banco Supabase próprio e deploy próprio. As demais páginas — romaneio,
faturamento, entrega por transportadora — entram depois nesta mesma base, sem
mudança de estrutura: cada uma vira uma nova linha em dashboard_snapshots.
Três camadas, com separação rígida de responsabilidade:
Extração TXT do sistema (Posição de Stock)
│ upload manual, feito pelo operador no navegador
▼
┌───────────────┐
│ ingest.js │ Lê o arquivo, cruza com os gabaritos, calcula
│ (processa e │ os indicadores e grava tudo já pronto.
│ só escreve) │ NUNCA desenha nada na tela.
└───────┬────────┘
│ grava
▼
┌───────────────┐
│ Supabase │ Postgres (dados + snapshot) +
│ │ Storage (backup gzip do TXT original)
└───────┬────────┘
│ lê o snapshot já pronto
▼
┌───────────────┐
│ index.html │ Desenha cards, gráficos e a árvore.
│ (renderiza e │ NUNCA recalcula regra de negócio.
│ só lê) │
└───────────────┘
▼
Vercel
Essa separação não é opcional. Número errado na tela se investiga no
ingest.js (como foi calculado); aparência se ajusta no index.html (como é
exibido). Foi o que economizou tempo de debug no projeto do e-commerce e é o
que mantém a tela rápida com base grande.
| Arquivo | O que é |
|---|---|
index.html | O dashboard. Single-file, sem build step. |
ingest.js | Parse, agregação e gravação (página Estoque). |
distr-fluxo.js | Renderizador + editor da página Fluxo de Processos (ver seção 10). |
esquema.sql | Todo o banco: tabelas, gabaritos, RLS. Já aplicado. |
embarque.js | Setor Embarque: leitura da base histórica, forecast mensal ÷ dias úteis e o gráfico Expedição × Forecast / Análise Prevista de Backlog (mesmo traço do Report E-commerce). |
migracao_embarque.sql | Tabelas embarque_diario (histórico diário) e embarque_forecast_mensal. Já aplicada. |
testes-embarque.js | Testes do Embarque (node testes-embarque.js). |
seed_distr_fluxo.sql | Semente da primeira versão do Fluxo de Processos (ver seção 10). |
vercel.json | Diz ao Vercel que é estático puro, sem build. |
favicon.png | Ícone da aba e marca no menu. |
| Projeto | report-distribuidora |
| Organização | Vulca |
| Região | sa-east-1 (São Paulo) |
| URL | https://qaehjplsasumuhszjcnu.supabase.co |
| Custo | R$ 0/mês |
O schema e os gabaritos já estão aplicados (7 armazéns, 37 famílias com
marca e segmento). A publishable key já está preenchida no index.html —
ela é pública por design: quem protege o dado é a RLS no Postgres, não o fato
de a chave estar escondida no front.
backups, Public: OFF.
É onde fica a cópia comprimida de cada TXT enviado, para auditoria.insert into perfis_acesso (user_id, role, nome)
values ('COLE-O-UUID-AQUI', 'admin', 'Seu Nome');
Sem esse passo o login recusa: a tela exige perfil cadastrado.Crie um repositório novo (ex. report-distribuidora) e suba estes arquivos na
raiz dele. Nada além do que está aqui é necessário.
vercel.json já cuida disso.O
index.htmltambém funciona aberto direto do disco (duplo clique): ele fala com o Supabase pela internet, não depende do servidor de hospedagem. Serve de plano B se o deploy travar na véspera de uma apresentação.
O ingest aceita os dois formatos e detecta qual é pelo conteúdo do arquivo, não pelo nome:
| Layout | Extração | Observação |
|---|---|---|
Delimitado por | | EX000796 | Preferido. Traz St e Em Linha como colunas separadas, e artigo/descrição/cor/tamanho cada um na sua coluna. |
| Largura fixa | EX000914 | Mantido por compatibilidade. É o único que imprime o ** TOTAL GERAL do sistema, útil para conferência. |
Não abra o arquivo no Excel e salve por cima. No layout de largura fixa as colunas são lidas por posição de caractere, e qualquer reformatação quebra a leitura.
Quando a extração é a de largura fixa, o ingest compara o que leu com o
** TOTAL GERAL impresso pelo próprio sistema:
Quando a extração é a delimitada, o arquivo não imprime totais. A tela diz exatamente isso — "não há conferência externa" — em vez de fingir uma validação que não houve.
Valor total, quantidade, disponível para venda (armazéns DISPONIVEL —
hoje só o AC190), bloqueado / em análise, SKUs com estoque e custo médio
unitário.
A leitura imediata: dos R$ 127,2 mi de estoque, R$ 120,5 mi (94,7%) disponíveis e R$ 6,7 mi (5,3%) travados em análise, qualidade ou devolução.
O relatório traz dois status independentes, que respondem a perguntas diferentes — e por isso são dois filtros separados, não um só:
| Filtro | Valores | O que significa |
|---|---|---|
| Válido | Sim (V) / Não (I) | O artigo é válido? |
| Em linha | Sim (S) / Não (N) | O artigo está em linha? |
Clicar no filtro já ativo desliga — para ninguém ficar preso num recorte sem perceber no meio de uma apresentação. O filtro atinge tudo ao mesmo tempo: KPIs, gráficos e a árvore inteira, e o recorte ativo fica escrito no rodapé da tabela.
Isso funciona porque cada nó da árvore já vem do ingest com o total quebrado nas quatro combinações. O navegador só soma baldes já calculados — não reprocessa regra de negócio.
No layout antigo os dois status vinham colados num código só (
VS/VN/IS/IN). O ingest desmembra na leitura, então os dois layouts produzem exatamente o mesmo dado.
Fechada, mostra os 7 armazéns e o TOTAL GERAL. Cada + abre um nível:
AC190 [DISPONÍVEL] Armazém Físico segregado em PULMÃO e PICKING
└ MIZUNO
├ 102 TÊNIS MIZUNO | TÊNIS MIZUNO
├ 103 VESTUÁRIO MIZUNO | TÊXTIL/ACESSÓRIOS MIZUNO
└ ...
Colunas: Qtd · % do nível · SKUs · R$ · % do nível.
O % é sempre em relação ao nível pai, não ao total geral — dentro de um armazém as marcas somam 100%; dentro de uma marca, as famílias somam 100%. É o que responde à pergunta que a gestão faz de verdade: "quanto desse armazém é Mizuno?"
Os nomes de família e o segmento ao lado vêm do gabarito da operação
(dim_familias), não do relatório — o sistema escreve "VESTUARIO
OLYMP.TERCEIROS" onde a operação lê "VESTUÁRIO OLY".
A coluna KPI traz a bandeira nos armazéns cujo estoque não está disponível para venda, mesma função da bandeira do dash financeiro de referência.
Botões Expandir tudo / Recolher tudo e CSV (exporta a árvore inteira, não só o que está aberto, respeitando os filtros ativos).
| Linhas com estoque | 24.973 |
| Linhas de SKU zerado | 8.480 — fora do total |
| Artigos distintos | 3.009 |
| Quantidade | 2.931.823 un |
| Valor | R$ 127.199.303,46 |
| Famílias | 21 com estoque, todas mapeadas |
Por armazém:
| Armazém | Qtd | Valor | % valor |
|---|---|---|---|
| AC190 (disponível) | 2.834.432 | R$ 120.514.650,87 | 94,7% |
| ARMRP (bloqueado) | 77.662 | R$ 4.997.283,39 | 3,9% |
| ARAMO (análise) | 13.957 | R$ 1.369.869,23 | 1,1% |
| DEVFT (bloqueado) | 3.848 | R$ 269.372,02 | 0,2% |
| ARMFT (análise) | 1.737 | R$ 41.268,88 | 0,0% |
| ARMC1 (análise) | 42 | R$ 2.349,60 | 0,0% |
| SEM_ARMAZEM | 145 | R$ 4.509,47 | 0,0% |
Por marca: Mizuno R$ 68,9 mi (54,1%) · Under Armour R$ 32,3 mi (25,4%) · Olympikus R$ 26,1 mi (20,5%).
Por status:
| Válido | Em linha | Qtd | Valor |
|---|---|---|---|
| Sim | Sim | 2.240.615 | R$ 81.667.203,02 |
| Sim | Não | 690.053 | R$ 45.494.599,30 |
| Não | Não | 805 | R$ 30.027,08 |
| Não | Sim | 350 | R$ 7.474,05 |
Stock Zero: N), por isso ele não tem essas linhas.ARMC1 (materiais de consumo, 42 un) está somado no total geral. Se
materiais de consumo não devem contar como estoque de produto, o certo é
filtrar no ingest.js — não esconder na tela — e isso muda o total
apresentado.St | Em Linha | Fam | Artigo | Descricao | Cor | Tamanho | Um |
Armazem | Local | Stock_Minimo | Qtd_Stock | Preco_Medio | Valor_Stock |
O cabeçalho é lido pelo nome das colunas, não por posição: se o sistema
passar a exportar uma coluna a mais ou em outra ordem, o parser continua
achando cada campo. O armazém vem como EXTRE-AC190; linhas sem armazém
(EXTRE sozinho) viram SEM_ARMAZEM e aparecem no relatório — estoque
escondido é pior do que estoque estranho.
Relatório de terminal paginado, com cabeçalho repetido a cada página. Não é
CSV: separar por espaço quebra, porque a descrição do artigo tem espaços. As
posições vieram da própria linha de régua do relatório e estão em
COLUNAS_LINHA / COLUNAS_ARTIGO, no topo do ingest.js — se o layout
mudar, é só ali que se mexe.
Dois detalhes que não são óbvios nesse layout:
Familia ...: no
cabeçalho de cada página e vale para as linhas seguintes. O parser a carrega
como estado enquanto varre.UA casa dentro de
outras palavras — a relação família → marca é dado, em dim_familias.
Família nova que apareça e não esteja cadastrada cai em NÃO MAPEADA e o
dashboard avisa, em vez de somar silenciosamente no lugar errado.Implementados desde o primeiro commit, para não repetir bugs já resolvidos no report do e-commerce:
Intl.DateTimeFormat com timezone fixo. Nunca somar ou subtrair horas na
mão: quebra em horário de verão.:last-child. Estilizar "a
última linha" por posição fez a 10ª linha de um Top 10 parecer total geral.Segue a skill vulcabras-visual-identity: Segoe UI, cards neutros sem sombra
pesada, kicker em caixa alta com linha fina dourada, tema escuro como padrão e
claro como alternativo (o botão Tema troca e a escolha fica salva).
Cor tem significado, nunca é decoração: verde = disponível/positivo, âmbar = atenção, vermelho = bloqueado/negativo, e as cores de marca só identificam marca.
Antes de liberar para mais gente:
select em
estoque_posicoes usando só a publishable key, sem login. Tem que voltar
vazio. Se voltar dado, a RLS não está valendo — pare e corrija.admin / gestor / operador do CD, que pode não ser a hierarquia certa
aqui.Perguntas de negócio em aberto:
ARMC1 (materiais de consumo) deve entrar no total de estoque?Evoluções naturais desta base:
estoque_posicoes; é só carregar sob demanda ao abrir a família.estoque_extracoes já guarda o histórico.dim_familias e no snapshot, falta só o seletor de hierarquia na tela.dashboard_snapshots, sem mudança de schema.Fluxograma do processo operacional do CD (Recebimento → Armazenagem → Expedição, mais o fluxo à parte de Reversa), no menu Processos → Fluxo de Processos - DISTR, visível a todos os perfis — é material de treinamento, não só de gestão. Quem tem perfil admin também edita, ao vivo, no navegador.
Segue a mesma separação do restante do app — só que aqui não existe
ingest.js porque não há arquivo externo pra processar: o próprio Admin é
quem gera o dado, editando na tela.
distr-fluxo.js Lê e desenha o fluxograma (retângulo=etapa,
(renderiza e, losango=decisão, pílula=início/fim). Para admin,
p/ admin, edita) também liga o editor por cima: clicar numa seta
│ insere etapa/quebra/B.O.; clicar numa etapa ou
│ lê/grava losango abre o painel de edição.
▼
dashboard_snapshots pagina='distr_fluxo' — mesma tabela e mesmo
(Postgres) padrão "uma versão = uma linha nova" do resto
do app (seção 1). "Salvar" nunca dá UPDATE, então
toda versão anterior continua no banco — é o
histórico/undo de graça.
RLS: nenhuma policy nova foi criada. A leitura (ler_snapshots) já libera
qualquer pagina pra todo autenticado, e a escrita (gravar_snapshots) já é
só-admin — exatamente a regra que essa página precisa. O botão "Editar fluxo"
na tela é conveniência de interface; a proteção de verdade é essa RLS.
payload jsonb){
"versao_schema": 1,
"setores": ["Comercial", "Coleta", ...], // autocomplete do campo Responsável
"lanes": [
{ "id": "principal", "titulo": "...", "sub": "...",
"fases": [
{ "id": "receb", "titulo": "Recebimento",
"blocos": [ ["step","1"], ["step","2"],
["guard","A quantidade bateu na conferência?", ["4.1","4.2"],
{"exc":"Não","ok":"Sim","rejoin":true}], ... ] },
...
] },
{ "id": "reversa", "titulo": "Reversa", "fases": [ ... ] }
],
"nos": { "receb:1": { "nome":"...", "resumo":"...", "original":"...",
"resp":"...", "orig":"—", "dest":"AC190", ... } }
}
blocos é a topologia de cada fase — step/gate (retângulo), guard
(losango com um ramo lateral de desvio) ou fork (losango com duas colunas,
ambas legítimas). nos guarda o detalhe de cada etapa, indexado por
"fase:número". Ícones (o conjunto de 37 SVGs de linha) vivem só no
JavaScript, não no payload — é gabarito fixo, não dado editável.
Botão ✏️ Editar fluxo liga o modo edição (começa desligado, mesmo pra admin — ninguém edita sem querer só por ter a permissão):
dashboard_snapshots. Enquanto não salva, nada é permanente — Descartar
volta pra última versão publicada.Numeração (1, 6.1, 6.1.2, 5A...) é recalculada sozinha a cada
inserção/exclusão, com raio de ação limitado ao que a edição realmente
afeta — nunca renumera nada fora dali. O número sugerido sempre aparece
num campo editável, então dá pra ajustar à mão quando o padrão automático não
for exatamente o que se quer.
Fora do escopo desta versão (registrado, não esquecido): criar uma lane inteiramente nova (só fase nova dentro de lane existente); reordenar fases/etapas do tronco (inserir sim, reordenar não); criar um losango do zero a partir de uma seta (editar um já existente, sim); uma tela de "versões anteriores" (cada save já vira linha nova no banco, recuperável por SQL).
A tabela nasce vazia — sem uma primeira linha, a tela mostra "Nenhuma versão
do fluxo publicada ainda". Rode seed_distr_fluxo.sql inteiro no SQL
Editor do Supabase (mesmo projeto de esquema.sql) uma vez: ele insere a
versão validada com a operação (66 etapas, 4 fases + Reversa) como a primeira
linha de dashboard_snapshots para pagina='distr_fluxo'. Depois disso, é
editor na tela — não precisa rodar esse arquivo de novo (e se rodar, só cria
mais uma versão igual, não quebra nada).