Changelog¶
Todas as mudanças notáveis neste projeto são documentadas neste arquivo.
O formato é baseado em Keep a Changelog, e este projeto adere ao Versionamento Semântico.
Unreleased¶
2.1.0 - 2026-10-02¶
Adicionado¶
- FluxoPagarReceber (títulos em aberto a pagar e a receber): tipo novo
FLUXO_PAGAR_RECEBER, reconhecido por qualquer aba "Fluxo Pagar-Receber". A aba SemQuebras virabi_tituloebi_centro_custo(migration V22), com histórico por posição (a data do relatório) eatualsó na mais recente — a única que o PowerSync vai sincronizar. Reimportar substitui a posição; arquivo de posição antiga entra no histórico sem virar atual; importações simultâneas são serializadas. Decisãodocs/decisoes/0031-titulos-por-posicao-com-atual.md. - Recusa total do FluxoPagarReceber: sem a aba SemQuebras, sem data de posição ou com qualquer
linha inválida, nada é gravado e o motivo lista as linhas (
linha 312: vencimento '31/02/26' inválido). O total do receber diferente do "Total" da aba Grupo grava e avisa. - A página
/xls/importaraceita o FluxoPagarReceber e espera o processamento (até 30 s): mostra a posição e o que aconteceu com ela, os totais a pagar e a receber com o vencido, e os avisos — ou a recusa, a falha técnica ou "ainda processando".
Alterado¶
- Reenviar um arquivo que terminou em ERRO ou IGNORADO reprocessa o mesmo registro (
202), em todos os tipos e também emPOST /api/xls/processar. Antes devolvia409"Checksum duplicado (concorrência)" e o arquivo recusado ficava preso mesmo depois de uma correção. - Constituição X-Adm 2.2.2 · kit 2.0.11: pipeline com o contrato de release dentro do
gate, odocsbarrando o deploy na tag, o smoke no job de deploy e timeouts em todocurl; skills atualizadas; aPOWERSYNC_HEALTH_URLdocumentada como URL pública (decisão central 0039). xadm-comum-web0.10.2: 404 de rota não casada comAuthorization: Basic(scanner) deixa de abrirERRORno GlitchTip.
Corrigido¶
- Upload MARGEM_DIA não apaga mais o CNPJ, a classe, o município e a UF do cliente: o upsert de
bi_clientepreserva o valor gravado quando a planilha não traz o campo. - Doc de mapeamento: RELATORIO18 e MARGEM_CONSOLIDADA só garantem o código da filial (município e UF
são cadastro manual) e
PRAZOvazio gravaNULL, não0.
Migração¶
- Nada a fazer no Coolify (nenhuma env nova nem removida). A tabela nova só chega aos aparelhos depois
da regra do PowerSync (repo
onpetro/powersync), que sobe por último: xls → app → regra.
2.0.0 - 2026-09-17¶
Adicionado¶
- Smoke com uma rota de cada classe de credencial: o
smoke.routesdodocs/app.jsonpassa a declararGET /api/comercial/processamentos(m2m, Bearer) eGET /processamentos(session, a tela pela sessão do seamPOST /smoke/session), além do/health. Os dois são leituras sem efeito colateral; sem a credencial, o smoke confere que respondem401/302.
Segurança¶
/testePOST /test/glitchtipexigem autenticação (sessão da view ou Bearer). Estavam@Secured(IS_ANONYMOUS)com a premissa de que a regra de view decidia antes; oSecuredAnnotationRuledo micronaut-security roda primeiro e liberava o anônimo em produção.GET /api/comercial/processamentos,/{id}e/{id}/logsexigem autenticação: Bearer do/apiou a sessão da view (o link de log da tela de detalhe segue funcionando). Antes eram anônimos.- Segredo comparado em tempo constante (
MessageDigest.isEqual) no?k=do/xls/importare noX-Confirm-Nukedo nuke REST. /xls/**e/compras/**saem dointercept-url-map, e o@Secured(IS_ANONYMOUS)do import passa da classe para os dois métodos da página: anônimo na classe abriria qualquer rota nova do controller.
Alterado¶
- Constituição X-Adm 2.0.0 · kit 2.0.0. Kit re-derivado (pipeline com as guardas novas, skills,
hook, Dockerfiles, kit de UI, mkdocs);
docs/app.jsoncomperfil, semnativeedeploy; CLAUDE.md como roteador;docs/dev/como-rodar.mdeREADME.mdnovos; decisões 0028–0030. - Libs da casa na corrente (decisão 0029):
xadm-seguranca0.8.0,xadm-comum-web0.10.0,xadm-comum-storage0.3.0 e, nos testes,xadm-comum-teste0.4.0 — o Postgres e o Garage de teste da lib (Postgres em tmpfs) substituem as cópias locais, a@IntegracaoDockere aDockerDisponivelConditionvêm da lib (saem as duas cópias locais), e as travas de arquitetura vêm daRegrasArquitetura, com allowlist para o SQL cru do bulk. - Modo
S3_FILE_STORAGE=PSQLdispensa as envs do Garage. Com axadm-comum-storage0.3.0 o cliente S3 nasce sem credencial nesse modo e só uma chamada ao Garage a cobra; oArquivoXlsStoragesegue injetado direto noArmazenamentoArquivoServicee noStorageBackfillService. Nos modosPSQL_GARAGEeGARAGEa credencial segue obrigatória no boot. mavenLocal()no build, depois do registro e filtrado parabr.com.xadm: a versão da casa ainda não publicada sai do~/.m2sem init script.- Travas de teste: timeout de 15 min na task
test, 2 min por teste e Hikari de teste comconnection-timeoutde 5 s. @Validno corpo do nuke REST (motivoaté 500 caracteres);-H:IncludeLocales=pt-BRno build native; o jar base deixa de se chamarapp.jar(só oshadowJar); imagens do Dockerfile JVM pinadas por digest.- Configuração e auditoria do nuke via micronaut-data:
BiConfiguracaoRepositoryeNukeReplicationRepositoryviram@JdbcRepository, sem SQL cru pela conexão; 404/403/409 da tela de configurações e o 409 do nuke são decididos no serviço. - DTOs renomeados:
LoteRecebimentoBody→LoteRecebimentoResponseeProcessamentoResumoDto→ProcessamentoResumoResponse. O JSON não muda; muda o nome do schema no OpenAPI. - Listagens com
Pageable, devolvendo oPagedo micronaut-data:GET /api/comercial/processamentose/processamentos(tela) compagebase 0,sizedefault 20 e máximo 100 (zero ou negativo cai no default) e ordenação fixa porrecebido_emdecrescente (osortdo cliente é ignorado). O corpo do/apié oPagecomo o micronaut-data o serializa —content,pageable(number,size,sort,mode) etotalSize—, sem DTO de paginação próprio: saem o{items, page, size, total}e oProcessamentoListResponse. - Mudança de contrato do
GET /api/comercial/admin/nuke-replication:limit/offset→page/size(mesmos limites da listagem de processamentos, ordenação fixa poriniciado_emdecrescente); o corpo passa a ser o mesmoPage(content,pageable,totalSize) no lugar de{items, total, limit, offset}, e oNukeListResponsesai. - Package-by-feature (decisão 0030): fatias
processamento,processamento.planilha,dados,importacao,powersync,configuracao,diagnosticoesegurancasobre a basecomum, travadas pelo ArchUnit. O Telegram vira transporte (TelegramService) mais um notificador por feature, o recovery de boot se parte em processamento e nuke, e oComprasImportControllerpassa aImportacaoXlsController. As telas de admin recebem view-model, não a entidade. Rotas, JSON, schema e envs não mudam. - Log por processamento em
data/execucoes(app.logs.dir): a trilha que a UI exibe é dado e mora no volume/app/data, não em/app/logs(Operação no Coolify). A migration V21 regrava olog_arquivodos processamentos antigos para o caminho novo. - Deploy só native. A perna jar sai do
build.targetse do mapadeploydodocs/app.json: o canário 50/50 não estava efetivo (medido em 2026-09-11, 20 requisições no domínio principal = 100% native) e cada release religava uma perna jar sem tráfego. O recurso*-jar-pulldo Coolify fica parado; rollback = redeploy da tag imutável<sha>-native(constituição §9, decisão central 0022).
Removido¶
- Task
badgeCoberturadobuild.gradle.kts(e odependsOndocheck): gravava um SVG embuild/que nenhum job publicava. O badge de cobertura é doetc/tests.
Corrigido¶
- Nuke que falha em
DROP_MONGOtenta oRESUME, como a spec 002 previa: o slot já é novo, e o PowerSync volta a subir (comstrategy=coolify) em vez de ficar parado. Falha nos demais steps segue sem cleanup automático.
Migração¶
- Antes do deploy, no recurso do Coolify:
- renomeie
GARAGE_ACCESS_KEY/GARAGE_SECRET_KEYparaGARAGE_ACCESS_KEY_ID/GARAGE_SECRET_ACCESS_KEY(os nomes antigos seguem lidos, comWARNno boot; oapplication.ymlnão os declara mais); - confira que existe
SENTRY_DSN(oGLITCHTIP_DSNnão é mais lido — sem oSENTRY_DSN, o app sobe sem mandar erro ao GlitchTip); - remonte o volume da trilha de processamento de
/app/logsem/app/data(o mesmo volume, só muda o ponto de montagem). Sem isso a trilha dos processamentos antigos some da UI e os novos gravam no disco efêmero do container; - cadastre
SMOKE_TOKEN(liga o seamPOST /smoke/session), com o mesmo valor do secret do repo. - Antes do deploy, nos secrets do repo no GitHub:
SMOKE_TOKEN(o mesmo do recurso) eSMOKE_M2M_TOKEN(o valor doBI_COMERCIAL_XLS_API_TOKEN). Sem eles as rotasm2mesessiondo smoke viram só asserção negativa (sem credencial,401/302) e o smoke deixa de provar a tela e o Bearer. - Consumidor de
/api/comercial/processamentos*passa a mandar o Bearer (BI_COMERCIAL_XLS_API_TOKEN); sem ele recebe401. - Consumidor das duas listagens (
GET /api/comercial/processamentoseGET /api/comercial/admin/nuke-replication) lê o corpo novo: itens emcontent, total emtotalSize, página e tamanho empageable.number/pageable.size; o total de páginas não vem no corpo (ceil(totalSize / pageable.size)). No nuke, a query troca tambémlimit/offsetporpage/size;limit/offsetpassam a ser ignorados. - As libs novas só existem no
~/.m2até o release doxadm-commons: a CI fica vermelha até lá.
1.8.1 - 2026-09-11¶
Corrigido¶
- Determinismo do
resetSlotComPluginInvalidono CI — o assert travava a mensagem literal e reprovou 2x no runner com a suite verde local; agora trava que a mensagem nomeia o slot e tenta de novo uma vez quando a falha chega com o slot ainda existindo (transiente pré-drop sob carga). Falha persistente continua vermelha.
1.8.0 - 2026-09-11¶
Adicionado¶
- Smoke de produção pós-deploy (constituição §9, ADR central 0031; RD local 0024). Depois do
deploy o pipeline prova produção: identidade (o
commitdo/healthé o sha desta entrega), as rotas críticas dodocs/app.json, e nenhuma exceção nova no GlitchTip desde a entrega. Reprovou, reverte para a tag imutável<imagem>:<sha anterior>-<target>e confirma a reversão.
Alterado¶
XADM_COMMITcarimbado na imagem (build-arg no pipeline,ARG/ENVtarde no Dockerfile) — vira ocommitdo/healthe oreleaseno GlitchTip. Um identificador só atravessa pipeline, imagem,/healthe tag de rollback.- Bump
xadm-comum-webpara 0.9.0 — é quem expõe ocommitno/healthe resolve oreleasea partir deXADM_COMMIT. - Migração da constituição 1.2.2 → 1.4.8 (RDs locais 0024+0025):
- Login servida pela lib — bump
xadm-seguranca0.6.1 → 0.7.2,login.jtelocal apagado (o controller renderizaxadm/logindo JAR), identidade porxadm.views.login.*; de quebra, fail-fast de token vazio (BearerTokenGuard) e o seamPOST /smoke/sessiondestravado. - Bootstrap do kit 5.3.3 → 5.3.8 nos paths do kit (
/css/**,/js/**; vendor local removido) — a view da lib linka esses paths, e o/js/**passa a anônimo nointercept-url-map+ViewWhitelist(sem ele, 401 na tela de login). - Guardas novas no gate: estáticos que o layout linka,
@Scheduledsem eleição (aviso) e a alternância anti-vácuo do ArchUnit aceitandoassertTrue; as guardas de placeholder, micronaut-data e DDL destrutivo re-derivadas com o filtro de diretório gerado (1.3.1). - Kit de UI verbatim do central (
kit/layout.jte) eEstaticosDoKitAnonimosTest:GETanônimo em cada estático do layout e do/loginda lib (java-micronaut §UI) — sem/js/**nointercept-url-map, reprova. - Env do Bearer inbound renomeada para
BI_COMERCIAL_XLS_API_TOKEN(seguranca §Segredos: nome pelo app, não pelo papel). Resolvida em código (BearerTokenEnv, nomain) — o Micronaut não aninha placeholder;app.api-tokensaiu doapplication.yml. - Respostas JSON do
/api/**passam a emitir toda chave (@JsonInclude(ALWAYS)nos DTOs de resposta, java-micronaut §Armadilhas): campo sem valor sainulle lista vazia sai[], em vez de a chave sumir (defaultNON_EMPTYdo serde). Mudança aditiva no fio — cliente que já tratava ausente como vazio não muda nada. - A CI passa a rodar a integração (
./gradlew check -PdockerTestsno step Gate, timeout 25 min): sem o flag, os testes@Tag("docker")nunca rodavam na CI — cobertura órfã (ci-testes, regra estrita; a guarda "Integration no gate" reprovava). - Migração da constituição 1.4.8 → 1.4.10: guardas do
pipeline.ymlre-derivadas (a do reflect-config da JTE roda no Windows; a anti-vácuo do ArchUnit aceitaassertFalse(isEmpty()); entra a de destino de proxy cravado, que aqui passa); skill/xadm-docsatualizada; hint native doSentryAppendercompleta (SentryOptions+Level.valueOfnoreflect-config.json), para tag dologback.xmlnão ser ignorada em silêncio no native. - Migração da constituição 1.4.10 → 1.5.0:
pipeline.ymlre-derivado do template (a anti-vácuo do ArchUnit reconheceRegrasArquitetura.importNaoVazio(); odecidebusca o histórico inteiro para ler o trailerDeploy:da tag anotada; sai o passo morto que importava fatos do integrador — nenhuma página os transcluía; o deploy segue um step por alvo, gateado pelo sucesso do próprio build); skills/xadm-docse/x-documentarre-derivadas e/xadm-meta-audit-workinstalada; o healthcheck do Postgres dodocker-compose.dev.ymlpassa apg_isready -h 127.0.0.1(sem-hresponde "pronto" no servidor temporário do initdb); a RD 0024 marca a rota do central-backend que cita (checa-rotas). - A integração roda por default no
check(@IntegracaoDocker=@Tag("docker")+DockerDisponivelCondition, o idioma do bi-transporte-xls): sem Docker, cada classe é pulada comERRORno log, nunca em silêncio. Antes ochecklocal e o pré-flight da/xadm-releasepulavam a integração calados. - Bump
xadm-comum-web0.9.0 → 0.9.1 — a lib embarca a hint native doSentryAppender(três entradas, com os métodos) e o 404 de negócio volta aDEBUG(só o 404 de rota não casada em requisição autenticada vai ao GlitchTip). - Cobertura mede só código autoral: o
jacocoTestReportexclui o gerado pelo Micronaut ($Introspection,$Definition,$IntrospectionRef,$Intercepted), o serializer do serde 3.x (Serde*) e as templates JTE precompiladas. - Datasource sem
DB_USER/DB_PASSWORD: produção sempre usouDATASOURCES_DEFAULT_*(binding do Micronaut); os placeholdersDB_*doapplication.ymle as linhas doHOWTO-DEPLOY.mdsaíram.
Removido¶
-PdockerTests— a integração deixou de ser opt-in (ver Alterado).reflect-config.jsonlocal (META-INF/native-image/br.com.onpetro/bi-comercial-xls/) — axadm-comum-web0.9.1 traz as mesmas três entradas (§Migração da lib: segunda fonte drifta).API_BEARER_TOKEN— o nome antigo não é mais lido: o Bearer vem só deBI_COMERCIAL_XLS_API_TOKEN. Os dois recursos Coolify (jar e native) já têm o nome novo, com o mesmo valor, desde 2026-09-10; sem ele (ou vazio), oBearerTokenGuardrecusa o boot.
Corrigido¶
- Passo
RESET_SLOTdo nuke podia falhar comreplication slot "…" is active for PID N— opg_terminate_backend(pid)só envia o sinal e retorna na hora; o drop logo em seguida perdia a corrida com o walsender ainda soltando o slot. Agora usa a forma com timeout (pg_terminate_backend(pid, 5000), PG ≥ 14), que espera o processo sair antes do drop. Era também o flake doPostgresReplicationAdminIntegrationTest. - Flake no cleanup do
ComprasImportControllerTest— o processamento assíncrono de um teste inseriabi_compraentre osDELETEs do seguinte (FK violada); vira umTRUNCATEatômico. - Seed de filiais fora do portal —
docs/seed-filiais.sql(massa de dados) era publicado pelo mkdocs; foi paradocs/privado/(excluído do site, constituição §5) com o rastro atualizado.
1.7.3 - 2026-08-31¶
Alterado¶
- Deploy via control-plane (ADR central 0026): o CI passa a disparar
POST /api/ci/deployno central-backend (resolução ancorada na imagem:registry_slug+target→ recurso Coolify) em vez da ponte SSH forced-command, que ficava órfã no allowlist a cada cutover blue-green. Build off-host (GitHub Actions), Coolify só puxa a imagem pronta.
1.7.2 - 2026-08-28¶
Corrigido¶
- Build native: o
Dockerfile.nativenão pré-criava/app/logs(destino doapp.logs.dir, log por-processamento exibido na UI) com o donoapp, ao contrário do Dockerfile JVM. Sob o binário native (userapp,/appdo root), a criação do diretório em runtime falhava comAccessDeniedExceptione o processamento morria. Espelhado omkdir+chowndo JVM antes doUSER app.
1.7.1 - 2026-08-27¶
Corrigido¶
- Build native e documentação: título OpenAPI normalizado para ASCII e referências
legadas de rotas/changelog alinhadas ao contrato atual (decisão 0021). A correção
evita falha do
micronaut-openapino build limpo do container. Config de build GraalVM native-image (Fase B, spec 012 tarefa T6): perfilgraalvmNativenobuild.gradle.kts(-PnativeQuick→-Ob/-Os+--gc=serial) ereflect-config.jsonlocal registrando oSentryAppenderdo logback (instanciado por reflexão no boot).nativeCompilecompila verde. O recurso Coolify native usa a imagem pré-compiladafonte.xadm.biz/xadm/onpetro-xls:native-amd64, no domíniohttps://excel-native.onpetro.xadm.biz; o recurso JVM permanece emhttps://excel.onpetro.xadm.bizdurante a validação.
1.7.0 - 2026-08-27¶
Adicionado¶
- Deploy native em produção — recurso Coolify Docker Image
excel-native.onpetro, com a imagem GraalVMfonte.xadm.biz/xadm/onpetro-xls:native-amd64e domíniohttps://excel-native.onpetro.xadm.biz. O recurso JVM original permanece disponível para rollback durante a validação do cutover.
Corrigido¶
- Detecção da Cota Petrobrás passou a aceitar o produto Diesel R5.
1.6.0 - 2026-08-18¶
Adicionado¶
- Importação da Cota Petrobrás — novo tipo de planilha reconhecido automaticamente (uma aba por
ano + cabeçalho POLO/PRODUTO/meses): a matriz de cotas passa a alimentar a tabela
bi_cota_petrobras, sincronizada via PowerSync. Cada envio faz substituição total (a planilha é a matriz inteira do ano); um envio vazio não apaga nada, como salvaguarda. A página de importação manual (/xls/importar, antes/compras/importar— a rota antiga continua viva) passa a aceitar Compras e Cota Petrobrás, no máximo uma planilha de cota por lote. Spec.ia/011.
Alterado¶
- Infra compartilhada consolidada nas libs
xadm-comum-*— a infra web (RFC-7807ProblemDetail+ processor que@Replaceso Hateoas default,/health,/info,SentryInitializere as exceções HTTP genéricas), o object storage (seam + S3 + Garage + modos de armazenamento) e os utilitários (checksum SHA-256 + conversão de data) deixaram de ser cópias locais e passam a vir debr.com.xadm:xadm-comum-web:0.4.0,xadm-comum-storage:0.1.0exadm-comum-util:0.1.0; o motor de auth sobe paraxadm-seguranca:0.3.0. Comportamento idêntico ao anterior (paridade provada por teste de integração). Locais ficam só a whitelist de rota do app, a exceção de domínioStorageIndisponivelException(503, XLS) e o coordenador de storage. Decisões emdocs/decisoes/0018-adota-xadm-seguranca.mde ADR 0019 (xadm-commons), spec 009.
Corrigido¶
- Metas podiam sumir em silêncio no envio de lote — num
.zipos arquivos são processados em paralelo e um arquivo dependente (ex. META, com FK em vendedor) podia commitar antes do relatório que cria o vendedor; a chave estrangeira abortava o lote daquele arquivo e a meta desaparecia, mesmo com o envio marcado como recebido. Agora, ao fechar o lote (quando o relatório mestre já commitou), os itens que falharam por FK transiente são reprocessados uma vez; a primeira falha é silenciosa e só a segunda (vendedor genuinamente ausente) dispara alarme. Migration V18. Arquivo solto (fora de lote) segue alarmando na hora.
1.5.2 - 2026-08-04¶
Corrigido¶
- Importação de TRR voltou a falhar com "Tipo de arquivo não reconhecido" depois que a ANP renomeou a coluna "Tipo de Instalação" para "Qualificação da Empresa" no export (e removeu as colunas finais) — o detector reconhecia o TRR só pelo nome antigo da coluna. Agora aceita os dois cabeçalhos (com/sem acento), preservando compatibilidade com planilhas antigas. Sem colisão com POSTOS, que é detectado por coluna própria e tem prioridade maior.
1.5.1 - 2026-08-04¶
Alterado¶
- Motor de autenticação extraído para a lib compartilhada
xadm-seguranca— o código de auth das views (login Firebase, cookie de sessão, gate de rota, CSRF) deixou de ser cópia local e passa a vir debr.com.xadm:xadm-seguranca, a mesma usada pelos apps irmãos. Comportamento idêntico ao anterior (paridade provada por teste ponta-a-ponta); nenhuma mudança visível ao usuário. Fica local só a whitelist de rota específica do app (inclui/compras/). Nova variável opcionalAUTH_ISSUER(defaultbi-comercial-xls) — não alterar em produção, pois muda a validação do cookie e invalida sessões em voo. Decisão emdocs/decisoes/0018-adota-xadm-seguranca.md.
1.5.0 - 2026-07-29¶
Adicionado¶
- Processador de Compras — novo tipo de arquivo reconhecido automaticamente pela aba
"Compras": a Relação de Compras do ERP passa a virar as tabelas
bi_fornecedorebi_compra, sincronizadas via PowerSync para o painel de Compras do app móvel. Só combustíveis entram (filtro ONU); quantidade e valor são guardados crus (custo médio é derivado no cliente). Reenvio do mesmo arquivo é idempotente por checksum; correção de um período substitui aquele período. Decisão emdocs/decisoes/0017-compras-sem-chave-natural-replace-periodo.md. - Página de importação manual de Compras (
/compras/importar) — destino do botão "Importar XLS" do app bi-comercial (abre em nova aba). Aceita 1 ou mais.xlsxda Relação de Compras, empacota no servidor e registra quem enviou (id e nome do usuário) e a data/hora, visíveis no detalhe do processamento. É uma página aberta (sem login Google) protegida por um token compartilhado (BI_COMERCIAL_XLS_IMPORT_TOKEN): sem o token, sem a identidade do usuário, ou se o arquivo não for a Relação de Compras, o envio é recusado.
1.4.6 - 2026-07-29¶
Adicionado¶
- Filtro por tipo de relatório na tela de processamentos: ao lado do filtro
de Status, um segundo seletor mostra só os envios de um tipo (RELATORIO18,
METAS, POSTOS, TRR, margens…). Os dois filtros se combinam e a lista de tipos
acompanha o que o sistema reconhece. O mesmo filtro está disponível na API REST
(
GET /api/comercial/processamentos?tipo=…, parâmetro opcional que combina comstatus). A ordenação segue por data de recebimento, do mais novo para o mais antigo.
1.4.5 - 2026-07-27¶
Corrigido¶
- Envio de lote podia falhar com "deadlock detected" quando dois arquivos do
mesmo
.ziperam processados em paralelo: ambos gravavam os mesmos clientes (e demais dimensões) em ordens diferentes, e o PostgreSQL abortava um dos processamentos (erro 40P01), derrubando o arquivo inteiro do lote. Agora cada gravação em lote ordena as linhas pela chave natural da tabela, o que garante ordem de bloqueio consistente entre processamentos concorrentes e elimina o deadlock. Reenviar o.zipque falhou (ou usar o botão Reprocessar) resolve os envios afetados.
1.4.4 - 2026-07-17¶
Adicionado¶
- Botão Reprocessar nas telas internas para envios com status ERRO, que refaz
o processamento a partir do arquivo já guardado — sem reenviar a planilha. Vem
em três escopos: o envio, os erros do lote (
.zip) dele e todos os erros do banco (na lista filtrada por ERRO). Antes o botão existia só em ambiente de desenvolvimento. Só ERRO é aceito: envio em SUCESSO, PENDENTE ou PROCESSANDO responde409, o que torna o duplo-clique inofensivo. - Campo
conjuntoIdno detalhe do processamento (GET /api/comercial/…/{id}) — identifica o lote a que o arquivo pertence. Adição de chave; nada foi removido nem renomeado.
Corrigido¶
- Todo processamento falhava em produção com
AccessDeniedExceptionao criar/app/logs: o container roda como usuário não-root, mas/apppertencia ao root, e o app não conseguia criar o diretório de log ali. A imagem agora cria/app/logscom o dono certo. Também derrubava o log em arquivo do logback. Se o diretório for um bind mount no host, o dono precisa ser1001:1001(volume nomeado herda sozinho).
1.4.3 - 2026-07-16¶
Alterado¶
- Telas internas migradas para o layout padrão X-Adm: nova paleta (azul royal no lugar do navy/burgundy), cabeçalho com marca e chip de sessão. Mudança visual — o operador vai notar; nenhum fluxo mudou.
/healthpassa a responder{status, versao}, com a versão do release lida do próprio artefato. O health check do container usa este endpoint.- Imagem de runtime do container passa de Alpine para Jammy (glibc), com processo não-root e limites de heap da JVM. Sem mudança de comportamento do app.
- Gate de CI passa a imprimir a cobertura de testes.
Corrigido¶
- Documentação do envio de planilhas: descrevia
POST /api/comercial/processarcom um.xlsxpor envio e camposconjunto_id/conjunto_total. O endpoint real éPOST /api/xls/processar, que recebe um.zipcom as planilhas do ciclo, desde a v1.4.0 — a API não mudou agora; a doc é que estava errada. Quem integra deve conferir o manual e a referência da API. - Tela de detalhe exibia "Inalteradas" negativa (ex.:
-98) e "Efetivas: 231 / 133 enviadas": a conta misturava o total de linhas de todas as tabelas com o de registros só da entidade principal. Nova colunalinhas_enviadas(migration V15) fecha o cálculo no mesmo escopo. Uploads anteriores à V15 exibem "—" (o dado nunca foi persistido). - Observabilidade lê
SENTRY_DSN, comGLITCHTIP_DSNcomo fallback.
1.4.2 - 2026-07-07¶
Fixed¶
- Geração da referência de API (Javadoc) no CI de docs: corrigidas as referências
@linkdePowerSyncProcessControlCoolify(doclint) que faziam odocs.ymlfalhar. Sem mudança de comportamento do app.
1.4.1 - 2026-07-07¶
Changed¶
- Release de manutenção: reexecuta os workflows de CI/docs no Forgejo. Sem mudança de código nem de comportamento do app.
1.4.0 - 2026-07-07¶
Added¶
- Observabilidade de erros via GlitchTip (protocolo Sentry,
bug.xadm.biz): logsERRORe exceções não-tratadas são reportados automaticamente. Nova rota de diagnóstico autenticada/test/glitchtippara smoke-test pós-deploy. - Documentação do projeto publicada como site MkDocs Material em
docs.xadm.biz(arquitetura, etapas do projeto, decisões e as regras de negócio de processamento célula-a-célula por tipo de arquivo).
Changed¶
- Contrato de erro da API REST agora é RFC 7807 (
application/problem+json). O corpo de erro de/api/**mudou de{message, _links}paraproblem+json(status HTTP preservados) — consumidores da API devem atualizar o parsing. - [operação] A variável do DSN de error tracking mudou de
SENTRY_DSNparaGLITCHTIP_DSN— o valor já foi injetado no Coolify pela Central de Apps. - Padronização do pipeline de processamento de planilhas XLS.
- Filtro de autenticação das views migrado para
@ServerFilter(ordem determinística no fat jar).
Fixed¶
- Default do
APP_BASE_URLcorrigido parahttps://excel.onpetro.xadm.biz(apontava para um domínio sem certificado válido; só funcionava com o env sobrescrito em produção).
1.3.1 - 2026-05-28¶
Fixed¶
- Layout das views server-rendered padronizado. No detalhe do processamento
(
/processamentos/{id}) os blocos de informação deixam de usar listas de definição (<dl>) e passam a um grid responsivo de duas colunas; novo fragmentpageHeadercentraliza o título da tela; painel de logs ganha mais altura e quebra de linha (white-space: pre-wrap); o filtro do histórico (/processamentos) foi realinhado. Apenas apresentação — sem mudança de comportamento.
1.3.0 - 2026-05-28¶
Added¶
- Restaurado o sinal de nuke
bi_configuracao.ultimo_nuke(removido na v1.2.4). Empiricamente o sinal é necessário: sem ele o cliente Dart reusa o SQLite local após o nuke do Mongo e fica com dados stale — linhas DELETE-adas no PG entre o último sync do cliente e o nuke viram zumbis no cache (PowerSync sincroniza só deltas e não força re-download sozinho). ONukeReplicationServicevolta a fazer UPSERT na chave noSTEP_FINALIZAR(viaBiConfiguracaoRepository.marcarUltimoNuke(), construtor de volta a 6 args); clientes Dart comparam o valor pra disparardisconnectAndCleare re-sincronizar do zero. Spec 007 reativada.
1.2.4 - 2026-05-27¶
Removed¶
- Sinal de nuke
bi_configuracao.ultimo_nuke(mecanismo dedisconnectAndClearno cliente). PowerSync já detecta mudança de checksum no servidor e força re-download automaticamente — o sinal manual era boilerplate desnecessário. Removidos:NukeReplicationService.marcarUltimoNuke()chamada noSTEP_FINALIZAR,BiConfiguracaoRepository.marcarUltimoNuke()(método), injeção doBiConfiguracaoRepositorynoNukeReplicationService(construtor volta a 5 args), 2 testes do método emBiConfiguracaoRepositoryIntegrationTest. Cliente Flutterbi-comercialremoveu oDatasetVersionGuardequivalente. A rowultimo_nukepermanece no seed da V12 como artefato legado. Spec 007 marcada como SUPERSEDED parcial; o resto (bi_configuracaoKV,ConfigService,/admin/configuracoes,slow_query_timeout_ms) permanece ativo.
1.2.3 - 2026-05-26¶
Fixed¶
- Recovery automático de nukes órfãos no startup: se a JVM/container morre
no meio do pipeline (deploy, OOM, crash), a row em
xls_nuke_replicationficava eternamente emEM_ANDAMENTOe o partial unique index bloqueava novos nukes (recovery exigiaUPDATEmanual via psql). Agora oStartupRecoveryServicemarca essas rows comoERROautomaticamente no startup, gravando "Interrompido após reinício da aplicação" noerro_mensageme preservando ostep_atualoriginal pra debug. Novo métodoNukeReplicationRepository.marcarOrfaosComoErro()faz o UPDATE em massa (seguro por construção — partial unique index garante 0-1 row afetada).
1.2.2 - 2026-05-26¶
Fixed¶
- V13 consolida o fix manual de permissões:
GRANT SELECT ON bi_*retroativo para as 10 tabelasbi_*que sincronizam +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO powersync_rolepara tabelas futuras criadas pelouser_onpetro. Migração idempotente; pula em dev/test ondepowersync_rolenão existe. CLAUDE.md ganha seção "Permissões propowersync_role" com sintoma (PG error 42501), causa e padrão de cinto de segurança (GRANTexplícito logo apósCREATE TABLEbi_* nova).
Changed¶
- Nomenclatura de DB/user em dev alinhada com prod:
POSTGRES_DBpassa deonpetro_comercial→db_onpetro,POSTGRES_USER→user_onpetro. Reflete emdocker-compose.dev.yml,run-dev.bate defaults doapplication.yml. Dev local agora bate com o padrãodb_*/user_*documentado em../powersync/doc/postgresq-setup.md.
1.2.1 - 2026-05-26¶
Added¶
- Tabela
bi_configuracao(V12) — KV genérico sincronizado via PowerSync (streamrecent_data, priority 0). Seed inicial:ultimo_nuke(sistema=TRUE) eslow_query_timeout_ms(sistema=FALSE). Spec.ia/007-bi-configuracao-sinal-nuke.spec.md. - Marcador
ultimo_nukeatualizado automaticamente peloNukeReplicationServicenoSTEP_FINALIZAR— clientes Dart detectam a mudança e disparamdisconnectAndClearno próximo sync, evitando SQLite zumbi pós-nuke. ConfigService(@Singleton) — cache in-memory TTL 30 s com APIgetString/getInt/getBool/getDurationpra consumo de config no backend.- Tela
/admin/configuracoes— CRUD de chaves KV com validação por tipo, ordem de precedência 404 > 403 > 400; rowssistema=TRUEaparecem read-only. - Exception
ForbiddenException(HTTP 403) para chaves de sistema. - Card "Configurações globais" no painel
/adminlinkando para a nova tela. - Entrada
- SELECT * FROM bi_configuracaoem../powersync/powersync.yaml(streamsrecent_dataedev). Requer redeploy do recurso PowerSync no Coolify.
Changed¶
NukeReplicationServiceagora recebeBiConfiguracaoRepositoryno construtor (6 args). Sem mudança no error path: falha do UPSERT cai no catch existente → nukeERRO.views/admin/powersync.htmlreorganizado: histórico de nukes agora é card Bootstrap (estilo consistente com os outros cards), título simplificado para "Administração".docs/nuke-replication.md+HOWTO-DEPLOY.mddocumentam o atributoREPLICATIONobrigatório na role do app PG e a URL interna do Coolify (http://coolify:8080) para o restart automático funcionar atrás do Traefik com cert wildcard (evitaPKIX path building failed).
Fixed¶
- Assets do Bootstrap (CSS + JS bundle 5.3.3) agora são selfhosted em
/public/vendor/bootstrap-5.3.3/— elimina dependência do CDN jsdelivr e problemas de integrity/CSP em ambientes restritos. - Favicon servido via
/public/favicon.pngem vez de/favicon.pngpath-exato, que o Micronaut ignorava silenciosamente como mapping de static-resources (gerava 404). Limpa também o entry redundante de whitelist noAuthFilter.
1.2.0 - 2026-05-26¶
Fixed¶
- Falhas intermitentes de build no Coolify quando o download do Gradle
distribution (
gradle-8.10.2-bin.zip) lançavaUnknownHostExceptionpararelease-assets.githubusercontent.com(CDN do GitHub Releases ao qual oservices.gradle.orgredireciona). ODockerfileagora usa BuildKit cache mount em/root/.gradle, persistindo o wrapper distribution e as dependências Maven entre deploys — a janela de risco do DNS lookup some na maioria dos builds e o tempo total cai significativamente.
1.1.2 - 2026-05-25¶
Added¶
- Botão "Baixar XLS" no detalhe do processamento (
/processamentos/{id}) e endpointGET /processamentos/{id}/arquivo, fechando a lacuna deixada pelo armazenamento no Garage (v1.1.0): operadores recuperam pela tela a planilha original processada (lê do Garage ou doarquivo_byteslegado conforme o modo).
Fixed¶
- Rodapé de paginação do histórico de processamentos exibia os literais
page1etotalPagesno lugar dos números calculados (expressão Thymeleaf com variáveis fora de${...}). - Página 2+ do histórico de processamentos retornava "Nenhum processamento
encontrado" mesmo com registros existentes: o link "Próxima" emitia
status=vazio e o SQL filtrava para status literalmente igual à string vazia. Empty/blank agora é normalizado para "sem filtro".
1.1.1 - 2026-05-25¶
Changed¶
- Padronização estrutural do código entre os projetos de BI (vantroba/onpetro): convenções de teste, naming e organização de arquivos alinhados entre os repositórios irmãos.
1.1.0 - 2026-05-25¶
Added¶
- Armazenamento do binário XLS recebido em object storage S3-compatível
(Garage) via AWS SDK v2. Modo configurável
app.storage.modo/S3_FILE_STORAGE:PSQL(status quo),PSQL_GARAGE(default — grava nos dois e lê do Garage, rede de segurança) ouGARAGE(só Garage, BYTEA ficaNULL). Chave content-addressed{aaaa}/{mm}/{checksum}.xlsx. StorageBackfillServiceopt-in (app.storage.backfill.enabled=true) que migra os blobs legados (arquivo_bytes) pro Garage no startup, antes do recovery. Idempotente; falha por linha não aborta as demais.- HTTP
503(StorageIndisponivelException) quando o Garage está indisponível durante o upload — falha transitória, cliente reenvia. - Migration
V11__xls_processamento_objeto_storage.sql: colunasobjeto_bucket,objeto_chave,objeto_tamanho_bytesemxls_processamento.
1.0.1 - 2026-05-22¶
Fixed¶
- Login Google: corrigido o erro
400 "Token CSRF inválido"noPOST /login/callback— o token CSRF da página de login era sobrescrito pelo model global das views, deixando o<meta>sem o token.
1.0.0 - 2026-05-22¶
Added¶
- Processamento de planilhas XLSX comerciais com detecção automática de 7 tipos (POSTOS, TRR, RELATORIO18, MARGEM_CONSOLIDADA, MARGEM_DIA, META, META_FERNANDO), parsing por streaming (SAX) e persistência em PostgreSQL.
- API REST
POST /api/comercial/processarprotegida por token Bearer, com endpoints de consulta de processamentos e logs de execução. - Views server-rendered em
/processamentospara acompanhar uploads, status e logs. - Conceito de lote (
xls_conjunto_lote) para rastrear conjuntos de uploads relacionados. - Schema
bi_*compatível com PowerSync (PKid UUID+ chave naturalUNIQUE), sincronizável com clientes mobile. - Upsert diff-aware: grava apenas as linhas que de fato mudaram, com as métricas
linhas_efetivas/linhas_removidasde churn evitado. - Nuke da replicação PowerSync — tela
/admine endpoint REST que zeram o estado replicado (slot Postgres + database Mongo) e forçam full re-sync dos clientes; restart automático do PowerSync via API REST do Coolify. - Login Google (Firebase) nas views server-rendered, restrito a emails
@xadm.com.br, com proteção CSRF nos formulários. - Error tracking via Bugsink (protocolo Sentry).
- Notificações de processamento via Telegram.