20 KiB
Story 3.1: Enviar Acréscimo ao PostgreSQL ao Salvar Pedido
Status: done
Story
Como sistema, quero enviar a taxa e o valor do acréscimo ao PostgreSQL quando um pedido for sincronizado para o ERP, para que o valor praticado localmente seja registrado no sistema central e fique disponível para relatórios do escritório.
Acceptance Criteria
-
Dado que
Global.sistema == SISTEMA_GERENTEe um pedido comvl_acrescimo = 87.50(calculado sobre taxa 2,5%) é enviado viaPedidoPGSQL.insertQuando o INSERT emgerente.pedidosfor executado Então a colunaacrepcontém2.5(taxa da forma de pagamento) eacrevcontém87.50(valor calculado) -
Dado que
Global.sistema == SISTEMA_SIGe o mesmo pedido é enviado Quando o INSERT emsig.pedidosfor executado Então a colunatx_acrescimocontém2.5(só a taxa; o valor é derivado server-side) -
Dado que um pedido sem acréscimo (forma de pagamento com
tx_acrescimo = 0) é enviado Quando o INSERT for executado em qualquer dos dois sistemas Então o campo correspondente recebe0.0sem erro -
Dado que a coluna
tx_acrescimoainda não existe emsig.pedidos(schema antigo / DDL server-side pendente) Quando o INSERT tentar gravar o pedido SIG Então o INSERT é construído sem o campotx_acrescimo(via checagem eminformation_schema.columnsantes do loop de envio), a comunicação conclui normalmente, e nenhuma exceção é lançada
Escopo desta história
SIM:
- Substituir
st.setInt(21, 0); // acrepest.setInt(22, 0); // acrevemPedidoPGSQL.insert()por valores reais do VO - Adicionar
tx_acrescimoao INSERT deinsertSig(), com checagem prévia de existência da coluna emsig.pedidos - Helper privado
hasColunaAcrescimoSig()consultandoinformation_schema.columns
NÃO cobre (deferred):
- Leitura inbound do acréscimo do PG (
selectAllPedConsulta) — Story 3.2 - MD5 de
pedido_consultaincluindovl_acrescimo— Story 3.3 - DDL server-side (responsabilidade do DBA; app tolera ausência)
Pré-condições verificadas
Inspeção confirma:
- Gerente — colunas já existem:
PedidoPGSQL.insert()já incluiacrepeacrevna lista de colunas do INSERT (PedidoPGSQL.java:134) — estavam apenas hardcoded como0. As colunas já existem emgerente.pedidos(estavam sendo gravadas como zero desde antes do Epic 3). - SIG — coluna nova:
insertSig()(PedidoPGSQL.java:342-351) não incluitx_acrescimo— DDL emsig.pedidospode ainda não ter sido aplicado. ped.getFormapag().getTxAcrescimo()disponível —FormaPagamento.txAcrescimoadicionado em Story 1.1-1.2 (formapag.tx_acrescimono SQLite).ped.getVlAcrescimo()disponível —Pedido.vlAcrescimoadicionado em Story 2.3.- Em
insert(),ped.getFormapag()nunca é null neste ponto — linha 160 já chamaped.getFormapag().getCodigo()sem null-check; se fosse null, falharia antes. - Em
insertSig(),ped.getFormapag()nunca é null — linha 378 já o usa diretamente. dbVersaopermanece 43 — esta história não altera schema SQLite.insertSig()usa 39 parâmetros (st.setInt(1)…st.setString(39)) —tx_acrescimoseria o parâmetro 40 (quando coluna existir).
Tasks / Subtasks
-
Task 1: Corrigir Gerente — gravar
acrepeacrevcom valores reais (AC: 1, 3)- 1.1 — Em PedidoPGSQL.java:181-182, substituir:
// ANTES: st.setInt(21, 0);// acrep st.setInt(22, 0);// acrev // DEPOIS: st.setDouble(21, ped.getFormapag().getTxAcrescimo());// acrep st.setDouble(22, ped.getVlAcrescimo());// acrevColuna
acrep= taxa %;acrev= valor calculado. Ambas já existem emgerente.pedidos(hardcoded a 0 anteriormente). Nenhuma alteração no SQL string ou na lista de colunas — apenas substituir os valores.
- 1.1 — Em PedidoPGSQL.java:181-182, substituir:
-
Task 2: Adicionar helper de checagem de coluna SIG (AC: 4)
- 2.1 — Adicionar método privado ao final de
PedidoPGSQL.java(antes do último}):private boolean hasColunaAcrescimoSig() { PreparedStatement st = null; ResultSet rs = null; try { st = conn.prepareStatement( "SELECT 1 FROM information_schema.columns " + "WHERE table_schema = 'sig' " + " AND table_name = 'pedidos' " + " AND column_name = 'tx_acrescimo'"); rs = st.executeQuery(); return rs.next(); } catch (Exception e) { return false; } finally { ConnectionManager.closeAll(st, rs); } }Retorna
truese a coluna já existe (DDL aplicado);falsecaso contrário. OfinallyusaConnectionManager.closeAll— padrão projeto-wide.
- 2.1 — Adicionar método privado ao final de
-
Task 3: Cache da checagem em
save()antes do loop (AC: 4)- 3.1 — Em PedidoPGSQL.java:56-110, adicionar checagem UMA VEZ antes do loop
for, logo apósconn.setAutoCommit(false):conn.setAutoCommit(false); db.beginTransaction(); final boolean temAcrescSig = !Global.sistema.equals(Global.SISTEMA_GERENTE) && hasColunaAcrescimoSig(); // int ultimoNumeroPedido = 0; for (int i = 0; i < pedidos.size(); i++) {Evita uma query
information_schemapor pedido — executa uma única vez por batch. - 3.2 — No mesmo método, alterar a chamada a
insertSig()para passar o boolean:// ANTES: idNumErp = insertSig(ped, db); // DEPOIS: idNumErp = insertSig(ped, db, temAcrescSig);
- 3.1 — Em PedidoPGSQL.java:56-110, adicionar checagem UMA VEZ antes do loop
-
Task 4: Modificar
insertSig()para aceitar e usar o boolean (AC: 2, 3, 4)-
4.1 — Alterar a assinatura do método:
// ANTES: private int[] insertSig(Pedido ped, SQLiteDatabase db) throws Exception { // DEPOIS: private int[] insertSig(Pedido ped, SQLiteDatabase db, boolean temAcrescimo) throws Exception { -
4.2 — Em PedidoPGSQL.java:350, após a lista de colunas (
vol_marca), adicionar condicionalmentetx_acrescimo:sql.append(" tx_desc_financ, cod_vend2, tx_com_vend2, tp_pg_com_vend2, vol_marca"); //35-39 if (temAcrescimo) { sql.append(", tx_acrescimo"); //40 } sql.append(") "); -
4.3 — Em PedidoPGSQL.java:358, na lista de VALUES (após o
"?, ?, ?, ?)"existente), adicionar condicionalmente o?:sql.append(" ?, ?, ?, ?"); //35-38: tx_desc_financ, cod_vend2, tx_com_vend2, tp_pg_com_vend2 if (temAcrescimo) { sql.append(", ?, ?"); //39: vol_marca, 40: tx_acrescimo } else { sql.append(", ?"); //39: vol_marca } sql.append(") RETURNING id_pedido, numero;");⚠️ Atenção: a linha original
sql.append(" ?, ?, ?, ?)")tem 4 params (índices 36-39) e NÃO inclui oRETURNING. Verificar o SQL final após a refatoração. Ovol_marcaatual é o param 39; ao adicionartx_acrescimo, ele passaria a ser o 40.⚠️ Contagem crítica: Antes de modificar, confirme que
st.setString(39, "*SAR*");corresponde avol_marca. ComtemAcrescimo = true,tx_acrescimofica no índice 40. -
4.4 — Após
st.setString(39, "*SAR*");(último setter atual), adicionar condicionalmente:if (temAcrescimo) { st.setDouble(40, ped.getFormapag().getTxAcrescimo()); // tx_acrescimo — só taxa; valor derivado server-side }
-
Dev Notes
Assimetria Gerente vs SIG — regra central desta história
| Sistema | Campo PG | O que gravar | Fonte no app |
|---|---|---|---|
Gerente (gerente.pedidos) |
acrep |
% de acréscimo (taxa) | ped.getFormapag().getTxAcrescimo() |
Gerente (gerente.pedidos) |
acrev |
Valor calculado do acréscimo | ped.getVlAcrescimo() |
SIG (sig.pedidos) |
tx_acrescimo |
% de acréscimo (só taxa; SIG calcula valor server-side) | ped.getFormapag().getTxAcrescimo() |
Esta assimetria é intencional e documentada nas Pré-condições do Epic 3. Gerente guarda taxa + valor (histórico congelado); SIG guarda só a taxa (valor recalculado server-side).
Por que Gerente NÃO precisa de tolerância à ausência de coluna
acrep e acrev já estavam na lista de colunas do INSERT Gerente (linha 134 de PedidoPGSQL.java) sendo gravados como 0. Isso prova que as colunas já existiam em gerente.pedidos antes do Epic 3. Nenhuma checagem de information_schema é necessária para Gerente.
Por que SIG PRECISA de tolerância
tx_acrescimo não existe na lista do INSERT SIG atual. O DBA precisa executar o DDL no servidor antes que o app possa gravar. A checagem em information_schema.columns permite deploy sem coordenação com o DBA:
- App deployed, DDL pendente → coluna não incluída → pedidos enviados sem
tx_acrescimo(campo ficará comDEFAULT 0quando DDL for aplicado) - DDL aplicado → próximo sync envia
tx_acrescimocom valor real
Contagem de parâmetros em insertSig() — tabela de referência
Param 1: id_empresa
Param 2: cod_vendedor
Param 3: tipo
Param 4: numero
Param 5: situa
Param 6: data
Param 7: clien
Param 8: e_ender
Param 9: e_bairr
Param 10: e_munic
Param 11: e_estad
Param 12: e_cep
Param 13: conta
Param 14: cod_formapag
Param 15: totpr
Param 16: ipi
Param 17: fconta
Param 18: frete
Param 19: total
Param 20: descp
Param 21: descv
Param 22: com_fat
Param 23: com_rec
Param 24: obs
Param 25: prz_con
Param 26: id_portador
Param 27: num_ped_sar
Param 28: num_ped_vendedor
Param 29: cod_pauta
Param 30: id_tes
Param 31: inf_usuario
Param 32: ped_flex
Param 33: obs_entrega
Param 34: abater_desc_fin
Param 35: tx_desc_financ
Param 36: cod_vend2
Param 37: tx_com_vend2
Param 38: tp_pg_com_vend2
Param 39: vol_marca ← "*SAR*" (st.setString(39, "*SAR*"))
Param 40: tx_acrescimo ← NOVO (condicional, quando temAcrescimo = true)
Null-safety
ped.getFormapag()nunca é null neste ponto —insertSig()já chamaped.getFormapag().getCodigo()em linha 378 sem null-check. Mesma garantia aplica-se agetTxAcrescimo().ped.getVlAcrescimo()retornadoubleprimitivo (0.0 default) — sem risco de NPE.
Arquivos a modificar
| Arquivo | Caminho | O que muda |
|---|---|---|
PedidoPGSQL.java |
src/br/com/jcsinformatica/sarandroid/postgres/ |
insert() params 21-22; save() cache temAcrescSig; insertSig() assinatura + SQL condicional; helper hasColunaAcrescimoSig() |
NÃO modificar nesta história:
DatabaseHelper.java— sem mudança de schema SQLitePedidoDB.java— persistência local já concluída (Story 2.3)Pedido.java/FormaPagamento.java— VOs completosselectAllPedConsultaemPedidoPGSQL— deferred (Story 3.2)PedidoConsultaDB.java— deferred (Story 3.2/3.3)
Regras críticas do projeto (aplicáveis)
- Sem Kotlin — somente Java puro
- Sem Gradle — projeto Eclipse ADT
- Sem JARs novos — nenhuma dependência adicional
- Sem testes automatizados — validação manual via dispositivo/emulador
- SQL por
StringBuilder— padrão projeto-wide mantido ConnectionManager.closeAll(st, rs)— sempre usado para fechar recursos JDBC
DDL server-side recomendado (a executar ANTES do deploy para resultado imediato)
-- Sistema Gerente (colunas já devem existir — confirmar antes):
-- ALTER TABLE gerente.pedidos ADD COLUMN acrep REAL DEFAULT 0;
-- ALTER TABLE gerente.pedidos ADD COLUMN acrev REAL DEFAULT 0;
-- Sistema SIG (coluna nova — executar no servidor):
ALTER TABLE sig.pedidos ADD COLUMN tx_acrescimo REAL DEFAULT 0;
⚠️ Para Gerente: como o app já grava
acrep/acrev = 0desde antes, as colunas certamente existem. Confirmar no banco antes de qualquer DDL.
Verificação manual após implementação
-
Gerente — pedido com acréscimo
- Criar pedido com forma de pagamento com
tx_acrescimo = 2.5; subtotal R$ 100,00 → acréscimo R$ 2,50 - Enviar ao ERP via ComunicaActivity
- Verificar em
gerente.pedidos:acrep = 2.5,acrev = 2.50para o pedido enviado
- Criar pedido com forma de pagamento com
-
Gerente — pedido sem acréscimo
- Criar pedido com forma de pagamento com
tx_acrescimo = 0 - Verificar em
gerente.pedidos:acrep = 0,acrev = 0— sem erro
- Criar pedido com forma de pagamento com
-
SIG com DDL aplicado — pedido com acréscimo
- Após executar DDL em
sig.pedidos - Criar pedido, enviar; verificar
sig.pedidos:tx_acrescimo = 2.5
- Após executar DDL em
-
SIG sem DDL (coluna ausente) — tolerância
- Com
tx_acrescimoausente emsig.pedidos(ou em ambiente de teste) - Enviar pedido: comunicação deve concluir sem exceção
- Verificar que o INSERT SIG não inclui
tx_acrescimono log (Log.d("String insert PEDIDO SIG", st.toString()))
- Com
-
Log de INSERT para debug
Log.d("String insert PEDIDO", st.toString())(Gerente) — verificar que contémacrepeacrevcom valores reaisLog.d("String insert PEDIDO SIG", st.toString())(SIG) — verificar presença/ausência detx_acrescimoconforme DDL
Inteligência de histórias anteriores
- Story 1.2: padrão de tolerância a coluna ausente no PG — inspiração para
hasColunaAcrescimoSig(). Story 1.2 usou try-catch emFormaPagamentoPGSQL; aqui optamos por checagem prévia pois o resultado condiciona a construção do SQL (não dá para fazer try-catch depois de preparar o statement). - Story 2.3:
Pedido.vlAcrescimopersistido no SQLite. Esta história fecha o ciclo enviando esse valor ao PG. - Story 2.4:
PedidoConsultaDBaceitavlAcrescimo = 0.0do sync PG atual. Após Story 3.1 + 3.2, pedidos novos terão valores reais. - Stories 1.1/2.3: padrão
if (oldVersion < N)emonUpgrade+ConnectionManager.closeAll(). Este último aplicado emhasColunaAcrescimoSig().
Git intelligence (commits recentes)
f2cf45dStory 2.4:pedido_consulta.vl_acrescimo+ guardstatus >= STATUS_ENVIADO3ff26a7Story 2.3:PedidoDB.insert/update/selectAllFullcomvl_acrescimo9b70ee3Story 2.2: cálculo emMainPedidoFragmenteTotalPedidoFragment168b9dbStory 1.2: syncgestao.formapag.acresc→formapag.tx_acrescimo
References
- Epics:
_bmad-output/planning-artifacts/epics.md#story-31-enviar-acrescimo-ao-postgresql-ao-salvar-pedido - FR14: Envio outbound do acréscimo ao PG (Gerente:
acrep+acrev; SIG:tx_acrescimo) - FR16: Sync tolera ausência das colunas de acréscimo no PostgreSQL
- Memory:
project_pg_sync_acrescimo_schema.md— assimetria Gerente vs SIG confirmada pelo Julio em 2026-04-16 PedidoPGSQL.java:129-255— métodoinsert()GerentePedidoPGSQL.java:317-457— métodoinsertSig()SIGPedidoPGSQL.java:56-110— métodosave()(loop de envio)
Dev Agent Record
Agent Model Used
claude-sonnet-4-6 (dev-story workflow)
Debug Log References
N/A — projeto sem infraestrutura de testes automatizados; validação manual via dispositivo/emulador conforme roteiro em "Verificação manual após implementação".
Completion Notes List
- Story 3.1 implementada em 2026-04-16.
- Task 1 — Gerente:
PedidoPGSQL.insert()linhas 181-182 —st.setInt(21, 0)est.setInt(22, 0)substituídos porst.setDouble(21, ped.getFormapag().getTxAcrescimo())est.setDouble(22, ped.getVlAcrescimo()). Colunasacrepeacrevjá existiam emgerente.pedidos(já estavam na lista de colunas do INSERT, apenas recebendo zero). AC 1 e AC 3 satisfeitos para Gerente. - Task 2 — Helper SIG: Método privado
hasColunaAcrescimoSig()adicionado ao final da classe. Consultainformation_schema.columnsparasig.pedidos.tx_acrescimo. Retornafalseem qualquer exceção. UsaConnectionManager.closeAll(st, rs)nofinally— padrão projeto-wide. AC 4 satisfeito. - Task 3 — Cache em
save():final boolean temAcrescSigcalculado UMA VEZ antes do loop de pedidos (apósconn.setAutoCommit(false)edb.beginTransaction()). Curto-circuita parafalseem sistema Gerente sem executar a query.insertSig()passatemAcrescSigcomo terceiro argumento. - Task 4 —
insertSig()condicional: Assinatura alterada parainsertSig(Pedido ped, SQLiteDatabase db, boolean temAcrescimo). Lista de colunas SQL e lista de?em VALUES construídas condicionalmente comif (temAcrescimo).st.setDouble(40, ped.getFormapag().getTxAcrescimo())adicionado condicionalmente após o setter 39 (vol_marca). QuandotemAcrescimo = false, INSERT permanece exatamente igual ao original (39 colunas/params). AC 2, AC 3 e AC 4 satisfeitos para SIG. - AC 1 satisfeito: Gerente grava
acrep = tx_acrescimoeacrev = vl_acrescimoreais. - AC 2 satisfeito: SIG grava
tx_acrescimo = tx_acrescimo(apenas taxa) quando coluna existe. - AC 3 satisfeito: pedido sem acréscimo →
getTxAcrescimo() = 0.0egetVlAcrescimo() = 0.0(primitivos double) → sem erro. - AC 4 satisfeito: coluna ausente →
hasColunaAcrescimoSig()retornafalse→ INSERT SIG construído semtx_acrescimo→ comunicação conclui normalmente. - Sem testes automatizados: projeto não possui infraestrutura (conforme CLAUDE.md). Validação manual necessária — roteiro completo em "Verificação manual após implementação".
- Nenhum import adicionado —
PreparedStatement,ResultSeteConnectionManagerjá importados. dbVersaopermanece 43 — sem alteração de schema SQLite.
File List
src/br/com/jcsinformatica/sarandroid/postgres/PedidoPGSQL.java
Review Findings
- [Review][Patch]
hasColunaAcrescimoSig()swallows all exceptions silently — exception em conexão/query retornafalsesem log, omitindotx_acrescimode toda a sessão sem rastreamento [PedidoPGSQL.java:1197] — FIXED: adicionadoLog.w()no catch - [Review][Defer]
ped.getFormapag()sem null-check antes degetTxAcrescimo()eminsert()einsertSig()[PedidoPGSQL.java:182,425] — deferred, pré-existente: a mesma ausência de null-check existe nas linhas 160 e 387 que vêm antes das novas chamadas; se fosse null, já teria crashado antes - [Review][Defer] Schema
'sig'hardcoded como string literal emhasColunaAcrescimoSig()[PedidoPGSQL.java:1192] — deferred, pré-existente: padrão projeto-wide ('sig'e'gerente'literais em toda a classe) - [Review][Defer] Valores monetários com
setDoubleparaacrep/acrev(precisão floating-point) [PedidoPGSQL.java:182-183] — deferred, pré-existente projeto-wide (setDoublepara todos os campos monetários) - [Review][Defer]
codVend2 = nullpode causar NullPointerException no auto-unboxing emst.setInt(36, codVend2)[PedidoPGSQL.java:420] — deferred, bug pré-existente não introduzido por este diff - [Review][Defer]
information_schema.columnssem filtrotable_catalog[PedidoPGSQL.java:1191] — deferred, risco muito baixo em conexões JDBC por-banco; padrão consistente comschemaTes() - [Review][Defer]
information_schema.columnsmais lento quepg_attribute[PedidoPGSQL.java:1191] — deferred, consistente com o padrão deschemaTes()já existente na classe - [Review][Defer]
keys.close()/st.close()eminsertSig()não executados se loop de itens lança exceção [PedidoPGSQL.java:~460] — deferred, padrão pré-existente em toda a classe - [Review][Defer] Comentário morto
//int ultimoNumeroPedido = 0;emsave()— deferred, pré-existente
Change Log
- 2026-04-16: Story 3.1 criada pelo create-story workflow.
- 2026-04-16: Implementação da Story 3.1.
PedidoPGSQL.insert()(Gerente) agora gravaacrep/acrevcom valores reais do VO.PedidoPGSQL.insertSig()(SIG) aceita booleantemAcrescimoe inclui condicionalmentetx_acrescimono INSERT.hasColunaAcrescimoSig()verifica existência da coluna emsig.pedidosviainformation_schema. Cache do resultado emsave()antes do loop de pedidos.