13 KiB
Story 1.2: Sincronização da Taxa de Acréscimo do ERP
Status: done
Story
Como representante de vendas, quero que a taxa de acréscimo de cada forma de pagamento seja sincronizada do ERP para o app durante a comunicação, para que os cálculos de acréscimo usem sempre os valores atualizados do servidor.
Acceptance Criteria
-
Dado que
gestao.formapagno PostgreSQL possui a colunaacresccom valores preenchidos Quando aComunicaActivityexecutar o sync de formas de pagamento Então o valor deacrescé gravado emformapag.tx_acrescimono SQLite para cada forma de pagamento -
Dado que
gestao.formapagno PostgreSQL não possui a colunaacresc(schema antigo) Quando o sync de formas de pagamento for executado Então a comunicação continua sem erro etx_acrescimopermanece com o valor anterior (ou0) -
Dado que o VO
FormaPagamentoé carregado após o sync QuandoFormaPagamentoDBpreencher o objeto Então o campotx_acrescimoestá acessível no VO, com valor0.0quandoNULLno banco
Tasks / Subtasks
- Task 1: Atualizar
FormaPagamentoPGSQL.selectAll()para leracrescdo PostgreSQL (AC: 1, 2, 3)- 1.1 - Refatorar
selectAll()em dois métodos privados para suportar fallback:- Criar
private List<FormaPagamento> executaSelectAll(Date ultAtualizacao, boolean comAcresc) throws Exception - O método usa
comAcrescpara decidir se incluicoalesce(acresc, 0.0) as acrescno SELECT (coluna 10) - Quando
comAcresc = true: adicionar, coalesce(acresc, 0.0) as acrescao final da lista de colunas e lerrs.getDouble(10)→setTxAcrescimo() - Quando
comAcresc = false: não adicionar a coluna; chamarforPag.setTxAcrescimo(0.0)explicitamente
- Criar
- 1.2 - Alterar
selectAll()público para chamarexecutaSelectAll(ultAtualizacao, true)envolvido em try/catch:public List<FormaPagamento> selectAll(Date ultAtualizacao) throws Exception { try { return executaSelectAll(ultAtualizacao, true); } catch (Exception e) { Log.w("FormaPagamentoPGSQL", "Coluna acresc ausente no servidor, usando 0.0: " + e.getMessage()); return executaSelectAll(ultAtualizacao, false); } } - 1.3 - Verificar que
FormaPagamentoDB.insert()eupdate()já persistemtx_acrescimo(implementados na Story 1.1 — confirmado: linhas 68, 79, 100)
- 1.1 - Refatorar
Review Findings (2026-04-16)
- [Review][Patch] Catch muito abrangente:
catch (Exception e)deveria sercatch (SQLException e)para não mascarar falhas reais de rede/autenticacao como "coluna ausente" [postgres/FormaPagamentoPGSQL.java:24] — corrigido - [Review][Defer] Resource leak:
PreparedStatement/ResultSetnao fechados em blocofinally[postgres/FormaPagamentoPGSQL.java:45-68] — deferred, pre-existing - [Review][Defer] SQL injection via concatenacao de strings no WHERE (pre-existente em toda a classe) [postgres/FormaPagamentoPGSQL.java] — deferred, pre-existing
- [Review][Defer]
coalesce(libera_credito)sem argumento padrao e no-op (nao protege contra NULL) [postgres/FormaPagamentoPGSQL.java:35] — deferred, pre-existing - [Review][Defer]
setDescontoPercle coluna decimal comogetInt, truncando frações [postgres/FormaPagamentoPGSQL.java:56] — deferred, pre-existing - [Review][Defer] Chave mal colocada em
AtualizaDados.atualizaFormaPag:inativaAllexecuta em toda chamada, nao so quandoultAtualizacao == null[comunicacao/AtualizaDados.java:377-379] — deferred, pre-existing
Dev Notes
Escopo desta história
Esta história cobre EXCLUSIVAMENTE a leitura de gestao.formapag.acresc no PostgreSQL e o mapeamento para FormaPagamento.txAcrescimo no VO durante o sync.
- Um único arquivo a modificar:
FormaPagamentoPGSQL.java - Toda a cadeia downstream (
AtualizaDados.atualizaFormaPag→FormaPagamentoDB.salvar→insert/update) já suportatxAcrescimocorretamente desde a Story 1.1 — não alterar - Cálculo de acréscimo na UI e persistência no pedido são escopo do Epic 2
Estado atual verificado de FormaPagamentoPGSQL.java
O método selectAll() atual (linha 22) retorna 9 colunas e não lê acresc:
// SELECT atual (linha 24-26):
sql.append("SELECT id_formapag, codigo,");
sql.append(" descr, ativa, numparc, desco, md5(descr || ativa || numparc || desco || vl_ped_minimo || libera_credito),");
sql.append(" coalesce(vl_ped_minimo,0.00) as vl_ped_minimo, coalesce(libera_credito) as libera_credito");
// Mapeamento atual (linhas 38-46): 9 colunas, sem leitura de acresc
// forPag.setTxAcrescimo() NUNCA é chamado — sempre fica 0.0 (padrão do double)
Estado atual verificado de AtualizaDados.java (linha 367-382)
private void atualizaFormaPag(Date ultAtualizacao) throws Exception {
FormaPagamentoPGSQL forPagPG = new FormaPagamentoPGSQL(conn);
FormaPagamentoDB forPagtoDB = new FormaPagamentoDB(comunica);
List<FormaPagamento> forPagtosPG = forPagPG.selectAll(ultAtualizacao); // ← apenas esta chamada
// ...
forPagtoDB.salvar(context, forPagtosPG);
}
Nenhuma alteração necessária em AtualizaDados.java.
Estado atual verificado de FormaPagamentoDB.java (já implementado em Story 1.1)
insert() (linha 68-79) e update() (linhas 88-105) já incluem tx_acrescimo. Confirmar sem alterar.
Implementação completa esperada de FormaPagamentoPGSQL.java
public List<FormaPagamento> selectAll(Date ultAtualizacao) throws Exception {
try {
return executaSelectAll(ultAtualizacao, true);
} catch (Exception e) {
Log.w("FormaPagamentoPGSQL", "Coluna acresc ausente no servidor, usando 0.0: " + e.getMessage());
return executaSelectAll(ultAtualizacao, false);
}
}
private List<FormaPagamento> executaSelectAll(Date ultAtualizacao, boolean comAcresc) throws Exception {
List<FormaPagamento> formaPagtos = new ArrayList<FormaPagamento>();
StringBuilder sql = new StringBuilder("SELECT id_formapag, codigo,");
sql.append(" descr, ativa, numparc, desco, md5(descr || ativa || numparc || desco || vl_ped_minimo || libera_credito),");
sql.append(" coalesce(vl_ped_minimo,0.00) as vl_ped_minimo, coalesce(libera_credito) as libera_credito");
if (comAcresc) {
sql.append(", coalesce(acresc, 0.0) as acresc");
}
sql.append(" FROM gestao.formapag");
sql.append(" WHERE id_empresa=" + Global.getEmpresa().getIdMatriz());
sql.append(" AND ativa = 1 AND integrar_sar=1");
if (ultAtualizacao != null)
sql.append(" AND dt_atual >='" + Util.formatDateDB(ultAtualizacao) + "'");
Log.i("SQL FORMA DE PAGAMENTO", sql.toString());
PreparedStatement st = conn.prepareStatement(sql.toString());
ResultSet rs = st.executeQuery();
while (rs.next()) {
FormaPagamento forPag = new FormaPagamento();
forPag.setId(0);
forPag.setIdErp(rs.getInt(1));
forPag.setCodigo(rs.getInt(2));
forPag.setDescricao(rs.getString(3));
forPag.setAtivo(rs.getInt(4) == 1);
forPag.setParcelas(rs.getInt(5));
forPag.setDescontoPerc(rs.getInt(6));
forPag.setMd5(Util.corrigeString(rs.getString(7)));
forPag.setVlPedMin(rs.getDouble(8));
forPag.setLiberaCredito(rs.getInt(9) == 1);
if (comAcresc) {
forPag.setTxAcrescimo(rs.getDouble(10));
} else {
forPag.setTxAcrescimo(0.0);
}
formaPagtos.add(forPag);
}
rs.close();
st.close();
return formaPagtos;
}
Regras críticas do projeto
- Sem testes automatizados — validação é manual via dispositivo/emulador. Não criar arquivos de teste.
- Sem Kotlin — somente Java puro
- Sem Gradle — projeto Eclipse ADT, não criar build.gradle
- JDBC sempre em thread background —
executaSelectAll()é chamado de dentro deAtualizaDadosque já roda em thread separada; não há alteração necessária - Sem JARs novos — implementação usa apenas
java.sql.*já disponível acrescpode ser NULL no PostgreSQL — usarcoalesce(acresc, 0.0)na query para tratar no lado do servidor quando a coluna existeacrescpode não existir no schema — tratar via try/catch (NFR5): nunca interromper a comunicação por coluna ausente- Conexão JDBC (
conn) já existe e é passada no construtor — não criar nova conexão - Strings com acentos em código Java são proibidas — os logs usam apenas ASCII
Por que try/catch em vez de coalesce
coalesce(acresc, 0.0) trata NULL mas não trata coluna ausente — se acresc não existir no schema PostgreSQL, o conn.prepareStatement() lança SQLException (column not found). O try/catch captura esse erro e reexecuta sem a coluna, garantindo NFR5 sem interromper a comunicação.
Sincronização incremental e MD5
O MD5 computado no PostgreSQL (md5(descr || ativa || numparc || desco || vl_ped_minimo || libera_credito)) não inclui acresc. Isso é intencional: a mudança de acresc no ERP normalmente atualiza dt_atual na tabela, fazendo o registro aparecer no sync incremental (dt_atual >= ultAtualizacao). Para syncs totais (ultAtualizacao = null), todos os registros são atualizados. Não alterar o MD5.
Arquivos a modificar
| Arquivo | Caminho | O que muda |
|---|---|---|
FormaPagamentoPGSQL.java |
src/br/com/jcsinformatica/sarandroid/postgres/ |
selectAll() refatorado + executaSelectAll() privado com flag comAcresc |
NÃO modificar nesta história:
AtualizaDados.java— sem mudança necessáriaComunicaActivity.java— sem mudança necessáriaFormaPagamentoDB.java— já atualizado na Story 1.1FormaPagamento.java(VO) — já atualizado na Story 1.1- Qualquer Activity ou layout — escopo do Epic 2
Verificação manual após implementação
- Com coluna
acresc: executar sync com servidor que possuigestao.formapag.acresc→ verificar em SQLite queformapag.tx_acrescimorecebe o valor correto - Sem coluna
acresc: executar sync com servidor que não possui a coluna → verificar no log"Coluna acresc ausente no servidor"e que a comunicação completa sem erro acresc = NULL: verificar que formas de pagamento comacresc = NULLrecebemtx_acrescimo = 0.0(viacoalesce)
Project Structure Notes
- Estrutura Eclipse ADT: código em
src/, pacotebr.com.jcsinformatica.sarandroid - DAOs PostgreSQL:
src/br/com/jcsinformatica/sarandroid/postgres/ - Padrão de sync:
*PGSQL.javalê do servidor → VO →*DB.javapersiste no SQLite
References
- [Source: _bmad-output/project-context.md#Padrão Dual-Banco] — ciclo JDBC, ConnectionManager, sync pattern
- [Source: _bmad-output/project-context.md#Regras Críticas] — proibições absolutas
- [Source: _bmad-output/planning-artifacts/epics.md#Story 1.2] — ACs e requisitos FR1, FR2, NFR5, NFR6
- [Source: _bmad-output/planning-artifacts/prd.md#Sincronização de Dados] — FR1, FR2
- [Source: _bmad-output/planning-artifacts/prd.md#NonFunctional Requirements] — NFR5, NFR6
- [Source: _bmad-output/implementation-artifacts/1-1-migracao-do-schema-sqlite-para-suporte-a-acrescimo.md] — Story 1.1 completa; FormaPagamentoDB.insert/update já persistem tx_acrescimo; mapeamento PostgreSQL→SQLite documentado
- [Source: src/br/com/jcsinformatica/sarandroid/postgres/FormaPagamentoPGSQL.java] — estado atual linha 22-53: 9 colunas, sem acresc
- [Source: src/br/com/jcsinformatica/sarandroid/comunicacao/AtualizaDados.java:367] — atualizaFormaPag() chama selectAll() e salvar()
- [Source: src/br/com/jcsinformatica/sarandroid/database/FormaPagamentoDB.java:63-105] — insert/update já incluem tx_acrescimo (Story 1.1)
Dev Agent Record
Agent Model Used
claude-sonnet-4-6 (dev-story workflow)
Debug Log References
Completion Notes List
- Story 1.2 implementada em 2026-04-16.
FormaPagamentoPGSQL.selectAll()refatorado: método público agora delega paraexecutaSelectAll(ultAtualizacao, true)com try/catch que faz fallback paraexecutaSelectAll(ultAtualizacao, false)em caso de colunaacrescausente no servidor.executaSelectAll(Date, boolean)privado: quandocomAcresc=trueadiciona, coalesce(acresc, 0.0) as acrescao SELECT e lêrs.getDouble(10)→setTxAcrescimo(); quandocomAcresc=falsechamasetTxAcrescimo(0.0)explicitamente.FormaPagamentoDB.insert()eupdate()confirmados: ambos ja persistemtx_acrescimodesde a Story 1.1 — nenhuma alteracao necessaria.AtualizaDados.atualizaFormaPag()confirmado: sem alteracao necessaria — fluxoselectAll()→salvar()e transparente a esta mudanca.- Sem testes automatizados (projeto nao possui infraestrutura de testes). Validacao e manual via dispositivo/emulador.
- NFR5 satisfeito: try/catch captura
Exceptionao preparar/executar o SELECT comacresc; comunicacao nao e interrompida em schemas antigos. - NFR6 satisfeito:
coalesce(acresc, 0.0)garante queNULLe tratado como0.0no servidor antes de chegar ao VO.
File List
src/br/com/jcsinformatica/sarandroid/postgres/FormaPagamentoPGSQL.java