Controle do limite de contratação direta por valor (art. 75, I e II, da Lei 14.133/2021) — por ramo de atividade, unidade gestora e exercício, com a declaração que protege quem assina.
A Lei 14.133 permite contratar sem licitação até um teto anual por valor. O § 1º do art. 75 diz como esse teto se mede: somando tudo o que a unidade gestora gastou no exercício com objetos de mesma natureza. É a regra anti-fracionamento — e é a que quase nenhum município consegue aplicar, porque exige saber, a cada nova compra, quanto daquele ramo já foi comprometido e quanto já foi pago.
Na prática o controle vive numa planilha por setor, alimentada à mão, que ninguém consegue somar. Quem assina a declaração de que o limite não foi ultrapassado assina sem ter como saber.
Este repositório é o sistema que faz essa conta — e é público porque essa dor é a de todo município que compra por dispensa.
- Por que isso importa (e por que não é só nosso)
- Como funciona
- Estado real do projeto
- Onde entra a Inteligência Artificial
- Reproduza no seu município
- Roadmap
- Procuram-se parceiros
- Contexto institucional
- Licença
| A dor | O que acontece na prática |
|---|---|
| O teto é anual, mas a compra é pontual | Cada dispensa parece pequena. O estouro só aparece quando alguém soma o ano inteiro — geralmente o controle externo. |
| "Mesma natureza" não tem régua | Sem um critério fixo para agrupar objetos, cada setor decide sozinho o que soma com o quê. |
| Comprometido não é o mesmo que pago | A requisição de compra bloqueia dotação antes do empenho. Um controle que só olha empenhos vê o estouro tarde demais. |
| A declaração é assinada às cegas | O § 3º do art. 75 exige divulgar; o processo pede uma declaração de que o limite foi respeitado. Sem a soma, ela é um ato de fé. |
- Lei 14.133/2021, art. 75, I e II — dispensa por valor para obras/engenharia (I) e demais compras e serviços (II); tetos atualizados anualmente por decreto federal (art. 182). Valores de 2026: R$ 130.984,20 e R$ 65.492,11 (Decreto 12.807/2025).
- Art. 75, § 1º — os limites consideram o somatório do exercício, por unidade gestora, de objetos de mesma natureza.
- Art. 75, § 7º — manutenção de veículos até um sublimite não consome o teto do inciso I (R$ 10.478,74 em 2026).
- O que é "mesma natureza" fica para cada ente. No município de origem, o Decreto 19.637/2024, art. 7º, fixou ramo de atividade = subclasse CNAE, por unidade gestora. A União e o Estado de São Paulo caminham para catálogo de materiais. O sistema nasceu com CNAE, mas o critério é uma camada trocável (ver o que você vai precisar trocar).
Fracionamento é o apontamento mais comum de tribunais de contas em compras diretas. A consequência não é só a glosa: é a responsabilização pessoal de quem autorizou e de quem assinou a declaração. Um controle que mostra o saldo do ramo antes da contratação transforma esse risco individual em processo.
exports CSV do sistema de compras base CNAE do IBGE (1.332 subclasses)
(catálogo · SD · RC · AF · empenho) │
│ │
▼ ▼
importação com prévia ──► bancada de classificação item → subclasse CNAE
│ │
▼ ▼
motor de saldo: UG × exercício × subclasse CNAE × inciso
realizado (empenhos) + comprometido (RCs abertas com SD) vs. teto
│
├──► painel da UG: faixas ≥ 80 % atenção · ≥ 90 % crítico · ≥ 100 % estourado
├──► painel central: todas as UGs, ranking dos ramos, montante não classificado
└──► Declaração de Limite de Dispêndio (PDF, numerada, imutável) — recusada se a compra estoura o ramo
- Importar. O administrador sobe os CSVs exportados do sistema de compras. Antes de gravar, o sistema mostra o que é novo, o que atualiza e o que rejeita (com motivo). Reimportar o mesmo arquivo não duplica nada.
- Classificar. Itens de catálogo sem ramo entram numa fila ordenada pelo valor envolvido. O curador escolhe a subclasse CNAE com busca na base do IBGE; o inciso (I ou II) é derivado da subclasse e pode ser sobrescrito com registro.
- Calcular. Para cada UG, exercício, subclasse e inciso: realizado (empenhos, líquidos de anulação) + comprometido (requisições abertas com solicitação de demanda vinculada, abatidas do que já foi empenhado). O que não tem classificação não some: aparece como montante não classificado, sempre visível.
- Alertar. Painéis por UG e consolidado, com faixas de alerta e detecção de requisições "mistas" (itens de ramos diferentes).
- Declarar. O coordenador da UG escolhe uma requisição e emite a declaração em PDF: ramo, teto, realizado, comprometido, valor da compra, saldo antes e depois, data/hora e emissor. É uma fotografia do saldo naquele instante, numerada e imutável. Se a compra estoura o ramo, a declaração não sai — o sistema não gera atestado falso; registra a tentativa.
- Conferir o fornecedor. Na importação de autorizações de fornecimento, o CNPJ é consultado na BrasilAPI e o sistema avisa se o ramo da compra não está entre as atividades do contratado. Consultivo: nunca bloqueia.
Três papéis, sem senha própria: Administrador/Curador (importa, classifica, gerencia papéis e tetos), Coordenador de UG (vê a sua UG e emite declarações) e Visão Central (lê todas as UGs). Quem diz quem é você é o SSO do órgão — o sistema só diz o que você pode.
Esta seção existe porque vitrine sem honestidade não serve para ninguém. Se você está avaliando adotar este projeto, precisa saber exatamente onde ele está — inclusive o que ainda não existe.
| Componente | Estado | Observação |
|---|---|---|
| Importação dos cinco tipos de CSV, com prévia, idempotência e log | ✅ Funciona | Layouts em dispendio/data/layouts_admc.json, ajustáveis sem código; o da RC foi fixado com export real |
| Base CNAE do IBGE (1.332 subclasses, hierarquia, notas) | ✅ Funciona | Versionada no repositório; scripts/baixar_cnae.py atualiza |
| Bancada de classificação item → CNAE, com histórico | ✅ Funciona | Busca textual nas descrições e notas explicativas |
| Motor de saldo (realizado + comprometido, faixas, § 7º, não classificado, RC mista) | ✅ Funciona | Camada pura, sem web nem banco, coberta por testes |
| Painéis da UG e central | ✅ Funciona | < 2 s com um exercício completo carregado (testado com massa de 50 mil linhas) |
| Declaração de Limite de Dispêndio em PDF, numerada e imutável | ✅ Funciona | Recusa quando a compra estoura o ramo; registra a tentativa |
| Checagem consultiva de CNPJ (BrasilAPI, cache de 30 dias) | ✅ Funciona | Indisponibilidade da API não trava nada; checagens ficam pendentes e reprocessáveis |
| Papéis e segregação por UG impostos no backend | ✅ Funciona | Nenhum cabeçalho HTTP vale como identidade |
| Uso em produção | ✅ Em uso | Em uma secretaria do município de origem, com exports reais |
| Identidade (SSO) | 🟡 Só simulada nesta versão | O provedor de produção depende do portal interno do município e não acompanha o repositório; aqui a identidade é escolhida em tela e o servidor só atende em 127.0.0.1. O contrato para plugar o seu SSO está em dispendio/web/identidade.py |
| Modelo oficial da declaração | 🟡 Layout provisório | O PDF tem todos os dados; o desenho conforme o modelo oficial do órgão é o próximo incremento |
| Adaptador para outro sistema de compras | ❌ Não existe | Só o layout do ERP de origem. Outro ERP = outro JSON de layout — nunca foi feito |
| Critério de ramo por catálogo (em vez de CNAE) | ❌ Não existe | Direção registrada; a camada de classificação é a única que mudaria |
| Sugestão automática de CNAE (IA) | ❌ Não existe | Especificada em Onde entra a IA |
O sistema depende de exports manuais do sistema de compras. Funciona — mas cada rodada de importação é um servidor exportando CSVs. Integração direta (API ou leitura de banco) depende do fornecedor do ERP de cada órgão, e é o que separa "controle semanal" de "controle no momento da compra".
Sejamos precisos: hoje este projeto não tem IA nenhuma. É importação, classificação humana e aritmética.
Ele importa para uma agenda de IA no setor público por outro motivo: não existe base de dados de dispêndio por ramo em município nenhum — existem planilhas. Este sistema cria essa base (documento × item × subclasse CNAE × valor × exercício, com a decisão de classificação registrada e datada), e é ela que torna possíveis as aplicações abaixo. A fila de classificação é, literalmente, um conjunto de treinamento sendo rotulado por um curador.
| Aplicação | Problema real | Entrada → Saída | Métrica |
|---|---|---|---|
| Sugestão de subclasse CNAE | A classificação é o trabalho humano mais caro do sistema: ler a descrição do item e achar a subclasse certa entre 1.332. Hoje é busca textual + julgamento | descrição do item + histórico de classificações → 3 subclasses candidatas com confiança | % de sugestões aceitas sem edição pelo curador |
| Detecção de fracionamento por similaridade | Objetos "de mesma natureza" com descrições diferentes (TONER HP 85A e CARTUCHO DE TONER PARA LASERJET) escapam de qualquer regra por código |
itens comprometidos no exercício → grupos de objetos equivalentes que somados se aproximam do teto | fracionamentos apontados pelo sistema × apontados por auditoria |
| Referência de preço | O sistema sabe quanto se gasta por ramo, não se o preço é bom. O Colibri (MGI) tem 27 mi de itens de NF-e federais com NCM | item + valor unitário → mediana federal do NCM correspondente, via tradutor CATMAT→NCM | desvio do preço pago em relação à mediana |
| Conferência espelho | O que o município publica no PNCP deveria bater com a base interna de modalidade 8 | base interna × PNCP → divergências documentadas | apontamentos evitados |
Nenhuma delas está implementada. Estão aqui como roadmap e convite. A primeira é a mais barata e a que mais economiza tempo humano; a segunda é a que mais interessa a controle interno.
git clone https://github.com/maia-andre/dispendio.git && cd dispendio
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python scripts/gerar_demo.py demo.sqlite3 # base sintética: 6 UGs, cenários de atenção/crítico/estouro, fila, fornecedores
python -m dispendio --banco demo.sqlite3 # http://127.0.0.1:8001/Abra http://127.0.0.1:8001/simulacao e escolha quem você é: ana.admin (administrador), vera.central (visão central) ou um coordenador (carlos.sec, dora.sms, joao.smc…). A base de demonstração é inteiramente sintética — os CNPJs têm dígito verificador inválido de propósito.
Para começar do zero com os seus dados:
python -m dispendio --banco dispendio.sqlite3 criar-admin seu.login --nome "Seu Nome" # primeiro administrador
python -m dispendio --banco dispendio.sqlite3 # cadastre as UGs e importe os CSVs pela tela de Administração
python -m dispendio --banco dispendio.sqlite3 saldos --ug SEC --exercicio 2026 # saldos de uma UG na linha de comando
pytest # suíte completa (~650 testes, < 30 s)Todos os dados ficam num único arquivo SQLite; backup = copiar o arquivo. Os CSVs em exemplos/ mostram o layout esperado de cada tipo.
| O que | Como está aqui | O que fazer no seu órgão |
|---|---|---|
| Identidade | simulada, escolhida em tela, só 127.0.0.1 |
Implemente ProvedorIdentidade.identificar() em dispendio/web/identidade.py lendo a sessão do seu SSO (cookie do portal, JWT, cabeçalho de proxy autenticador) e devolvendo uma Identidade. Papéis e segregação por UG não mudam |
| Layout dos CSVs | os do ERP de origem, em dispendio/data/layouts_admc.json |
Outro sistema de compras = outro JSON (nomes de colunas e apelidos aceitos), sem tocar em código: DISPENDIO_LAYOUTS=/caminho/seu.json |
| Tetos | 2026 pré-carregados (Decreto 12.807/2025) | Tela Administração → Tetos: por exercício e inciso, mais o sublimite do § 7º |
| Critério de ramo | subclasse CNAE (Decreto municipal 19.637/2024) | Se o seu ente usa catálogo de materiais, a camada a trocar é dispendio/core/classificacao.py + a base de referência; o motor de saldo não sabe o que é um ramo, só agrega por chave |
| Brasão e nome do órgão | placeholder em dispendio/web/static/brasao.svg e cabeçalho em templates/base.html |
Substitua o arquivo e o texto |
Python 3.11+, Flask 3, SQLite, waitress como servidor. Sem nuvem, sem banco externo, sem serviço pago. Únicos serviços externos: API do IBGE (só para atualizar a base CNAE, opcional) e BrasilAPI (consulta pública de CNPJ; se cair, nada trava). Nenhum dado interno do órgão sai da máquina além do CNPJ consultado. Roda numa estação Windows ou Linux comum.
- Desenho do PDF da declaração conforme o modelo oficial do órgão
- Provedor de identidade genérico (JWT/OIDC) para servir de exemplo de integração com SSO
- Sugestão automática de subclasse CNAE na bancada de classificação
- Segundo adaptador de importação (outro ERP municipal) — precisa de parceiro
- Módulo de inteligência de preços sobre o Colibri (mediana por NCM, tradutor CATMAT→NCM)
- Conferência espelho com o PNCP
- Um município com outro sistema de compras. Para provar que o adaptador de importação é mesmo um JSON, e não uma promessa. Se você exporta SDs/RCs/AFs/empenhos em CSV de qualquer ERP, dá para testar em uma tarde.
- Quem usa catálogo de materiais como critério de "mesma natureza". Para desenhar a segunda camada de classificação com um caso real.
- Controle interno ou tribunal de contas. Para validar se a régua do § 1º está certa e se a declaração tem o que uma auditoria pede.
- Quem já plugou um sistema Flask num SSO institucional. O provedor de identidade genérico está aberto.
Veja CONTRIBUTING.md — não precisa escrever código.
Desenvolvido no Departamento de Planejamento e Gestão de Recursos (DPGR) da Prefeitura de São José dos Campos - SP, a partir da necessidade concreta de substituir o controle manual do limite de dispensa por valor, que não resistia à regra anti-fracionamento do § 1º do art. 75.
O projeto nasceu de iniciativa própria do departamento, com recurso mínimo, e é publicado no espírito do BBSIA — Banco Brasileiro de Soluções de IA para a Gestão Pública (ENAP/LIIA): compartilhar soluções organizadas pelo problema que resolvem, para que órgãos públicos parem de reconstruir isoladamente o que já existe.
Faz parte de uma família de soluções do mesmo departamento sobre o ciclo da compra pública: Patrimônio Inteligente (inventário patrimonial com RFID). Outras — notas fiscais na SEFAZ, expedição de almoxarifado, fiscalização de contratos, termos de referência — estão a caminho.
Este repositório é uma versão pública derivada do sistema em uso; o desenvolvimento do dia a dia acontece no repositório interno do município, e as mudanças são trazidas para cá em blocos.
Código: Apache License 2.0 — uso, modificação e redistribuição livres, inclusive para fins comerciais, com concessão expressa de patente e exigência de atribuição.
Documentação: CC BY 4.0.
Escolhemos uma licença permissiva deliberadamente: o objetivo é remover atrito de adoção. Um jurídico municipal aprova Apache 2.0 sem discussão, e um fornecedor consegue integrar este código ao sistema de compras já contratado pela sua prefeitura sem conflito de licença. Copyleft protegeria melhor contra apropriação, mas ao custo de reduzir exatamente o alcance que buscamos.
⚖️ Titularidade: nos termos do art. 4º da Lei nº 9.609/1998, os direitos sobre software desenvolvido por servidor no exercício da função pertencem, em regra, ao ente empregador. A formalização da autorização institucional de publicação está em curso — veja o arquivo NOTICE para os detalhes.
Se o seu município também controla o teto de dispensa numa planilha que ninguém consegue somar, este repositório é seu.
A conta do § 1º é simples. O difícil é ter os dados para fazê-la — e é isso que este sistema constrói.