Files
sar-android/_bmad-output/implementation-artifacts/3-1-enviar-acrescimo-ao-postgresql-ao-salvar-pedido.md
Julio Schlickmann dc61705c91 add project files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 22:33:42 -03:00

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

  1. Dado que Global.sistema == SISTEMA_GERENTE e um pedido com vl_acrescimo = 87.50 (calculado sobre taxa 2,5%) é enviado via PedidoPGSQL.insert Quando o INSERT em gerente.pedidos for executado Então a coluna acrep contém 2.5 (taxa da forma de pagamento) e acrev contém 87.50 (valor calculado)

  2. Dado que Global.sistema == SISTEMA_SIG e o mesmo pedido é enviado Quando o INSERT em sig.pedidos for executado Então a coluna tx_acrescimo contém 2.5 (só a taxa; o valor é derivado server-side)

  3. 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 recebe 0.0 sem erro

  4. Dado que a coluna tx_acrescimo ainda não existe em sig.pedidos (schema antigo / DDL server-side pendente) Quando o INSERT tentar gravar o pedido SIG Então o INSERT é construído sem o campo tx_acrescimo (via checagem em information_schema.columns antes 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); // acrep e st.setInt(22, 0); // acrev em PedidoPGSQL.insert() por valores reais do VO
  • Adicionar tx_acrescimo ao INSERT de insertSig(), com checagem prévia de existência da coluna em sig.pedidos
  • Helper privado hasColunaAcrescimoSig() consultando information_schema.columns

NÃO cobre (deferred):

  • Leitura inbound do acréscimo do PG (selectAllPedConsulta) — Story 3.2
  • MD5 de pedido_consulta incluindo vl_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á inclui acrep e acrev na lista de colunas do INSERT (PedidoPGSQL.java:134) — estavam apenas hardcoded como 0. As colunas já existem em gerente.pedidos (estavam sendo gravadas como zero desde antes do Epic 3).
  • SIG — coluna nova: insertSig() (PedidoPGSQL.java:342-351) não inclui tx_acrescimo — DDL em sig.pedidos pode ainda não ter sido aplicado.
  • ped.getFormapag().getTxAcrescimo() disponível — FormaPagamento.txAcrescimo adicionado em Story 1.1-1.2 (formapag.tx_acrescimo no SQLite).
  • ped.getVlAcrescimo() disponível — Pedido.vlAcrescimo adicionado em Story 2.3.
  • Em insert(), ped.getFormapag() nunca é null neste ponto — linha 160 já chama ped.getFormapag().getCodigo() sem null-check; se fosse null, falharia antes.
  • Em insertSig(), ped.getFormapag() nunca é null — linha 378 já o usa diretamente.
  • dbVersao permanece 43 — esta história não altera schema SQLite.
  • insertSig() usa 39 parâmetros (st.setInt(1)st.setString(39)) — tx_acrescimo seria o parâmetro 40 (quando coluna existir).

Tasks / Subtasks

  • Task 1: Corrigir Gerente — gravar acrep e acrev com 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());// acrev
      

      Coluna acrep = taxa %; acrev = valor calculado. Ambas já existem em gerente.pedidos (hardcoded a 0 anteriormente). Nenhuma alteração no SQL string ou na lista de colunas — apenas substituir os valores.

  • 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 true se a coluna já existe (DDL aplicado); false caso contrário. O finally usa ConnectionManager.closeAll — padrão projeto-wide.

  • 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ós conn.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_schema por 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);
      
  • 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 condicionalmente tx_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 o RETURNING. Verificar o SQL final após a refatoração. O vol_marca atual é o param 39; ao adicionar tx_acrescimo, ele passaria a ser o 40.

      ⚠️ Contagem crítica: Antes de modificar, confirme que st.setString(39, "*SAR*"); corresponde a vol_marca. Com temAcrescimo = true, tx_acrescimo fica 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á com DEFAULT 0 quando DDL for aplicado)
  • DDL aplicado → próximo sync envia tx_acrescimo com 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á chama ped.getFormapag().getCodigo() em linha 378 sem null-check. Mesma garantia aplica-se a getTxAcrescimo().
  • ped.getVlAcrescimo() retorna double primitivo (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 SQLite
  • PedidoDB.java — persistência local já concluída (Story 2.3)
  • Pedido.java / FormaPagamento.java — VOs completos
  • selectAllPedConsulta em PedidoPGSQL — 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 = 0 desde antes, as colunas certamente existem. Confirmar no banco antes de qualquer DDL.

Verificação manual após implementação

  1. 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.50 para o pedido enviado
  2. 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
  3. 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
  4. SIG sem DDL (coluna ausente) — tolerância

    • Com tx_acrescimo ausente em sig.pedidos (ou em ambiente de teste)
    • Enviar pedido: comunicação deve concluir sem exceção
    • Verificar que o INSERT SIG não inclui tx_acrescimo no log (Log.d("String insert PEDIDO SIG", st.toString()))
  5. Log de INSERT para debug

    • Log.d("String insert PEDIDO", st.toString()) (Gerente) — verificar que contém acrep e acrev com valores reais
    • Log.d("String insert PEDIDO SIG", st.toString()) (SIG) — verificar presença/ausência de tx_acrescimo conforme 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 em FormaPagamentoPGSQL; 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.vlAcrescimo persistido no SQLite. Esta história fecha o ciclo enviando esse valor ao PG.
  • Story 2.4: PedidoConsultaDB aceita vlAcrescimo = 0.0 do 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) em onUpgrade + ConnectionManager.closeAll(). Este último aplicado em hasColunaAcrescimoSig().

Git intelligence (commits recentes)

  • f2cf45d Story 2.4: pedido_consulta.vl_acrescimo + guard status >= STATUS_ENVIADO
  • 3ff26a7 Story 2.3: PedidoDB.insert/update/selectAllFull com vl_acrescimo
  • 9b70ee3 Story 2.2: cálculo em MainPedidoFragment e TotalPedidoFragment
  • 168b9db Story 1.2: sync gestao.formapag.acrescformapag.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étodo insert() Gerente
  • PedidoPGSQL.java:317-457 — método insertSig() SIG
  • PedidoPGSQL.java:56-110 — método save() (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) e st.setInt(22, 0) substituídos por st.setDouble(21, ped.getFormapag().getTxAcrescimo()) e st.setDouble(22, ped.getVlAcrescimo()). Colunas acrep e acrev já existiam em gerente.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. Consulta information_schema.columns para sig.pedidos.tx_acrescimo. Retorna false em qualquer exceção. Usa ConnectionManager.closeAll(st, rs) no finally — padrão projeto-wide. AC 4 satisfeito.
  • Task 3 — Cache em save(): final boolean temAcrescSig calculado UMA VEZ antes do loop de pedidos (após conn.setAutoCommit(false) e db.beginTransaction()). Curto-circuita para false em sistema Gerente sem executar a query. insertSig() passa temAcrescSig como terceiro argumento.
  • Task 4 — insertSig() condicional: Assinatura alterada para insertSig(Pedido ped, SQLiteDatabase db, boolean temAcrescimo). Lista de colunas SQL e lista de ? em VALUES construídas condicionalmente com if (temAcrescimo). st.setDouble(40, ped.getFormapag().getTxAcrescimo()) adicionado condicionalmente após o setter 39 (vol_marca). Quando temAcrescimo = 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_acrescimo e acrev = vl_acrescimo reais.
  • AC 2 satisfeito: SIG grava tx_acrescimo = tx_acrescimo (apenas taxa) quando coluna existe.
  • AC 3 satisfeito: pedido sem acréscimo → getTxAcrescimo() = 0.0 e getVlAcrescimo() = 0.0 (primitivos double) → sem erro.
  • AC 4 satisfeito: coluna ausente → hasColunaAcrescimoSig() retorna false → INSERT SIG construído sem tx_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, ResultSet e ConnectionManager já importados.
  • dbVersao permanece 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 retorna false sem log, omitindo tx_acrescimo de toda a sessão sem rastreamento [PedidoPGSQL.java:1197] — FIXED: adicionado Log.w() no catch
  • [Review][Defer] ped.getFormapag() sem null-check antes de getTxAcrescimo() em insert() e insertSig() [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 em hasColunaAcrescimoSig() [PedidoPGSQL.java:1192] — deferred, pré-existente: padrão projeto-wide ('sig' e 'gerente' literais em toda a classe)
  • [Review][Defer] Valores monetários com setDouble para acrep/acrev (precisão floating-point) [PedidoPGSQL.java:182-183] — deferred, pré-existente projeto-wide (setDouble para todos os campos monetários)
  • [Review][Defer] codVend2 = null pode causar NullPointerException no auto-unboxing em st.setInt(36, codVend2) [PedidoPGSQL.java:420] — deferred, bug pré-existente não introduzido por este diff
  • [Review][Defer] information_schema.columns sem filtro table_catalog [PedidoPGSQL.java:1191] — deferred, risco muito baixo em conexões JDBC por-banco; padrão consistente com schemaTes()
  • [Review][Defer] information_schema.columns mais lento que pg_attribute [PedidoPGSQL.java:1191] — deferred, consistente com o padrão de schemaTes() já existente na classe
  • [Review][Defer] keys.close()/st.close() em insertSig() 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; em save() — 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 grava acrep/acrev com valores reais do VO. PedidoPGSQL.insertSig() (SIG) aceita boolean temAcrescimo e inclui condicionalmente tx_acrescimo no INSERT. hasColunaAcrescimoSig() verifica existência da coluna em sig.pedidos via information_schema. Cache do resultado em save() antes do loop de pedidos.