Files
sar-android/_bmad-output/implementation-artifacts/2-2-calcular-acrescimo-ao-selecionar-forma-de-pagamento.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

14 KiB
Raw Blame History

Story 2.2: Calcular Acréscimo ao Selecionar Forma de Pagamento

Status: done

Story

Como representante de vendas, quero que o acréscimo seja calculado e exibido automaticamente ao selecionar a forma de pagamento, para que eu veja o valor real do pedido sem precisar calcular manualmente.

Acceptance Criteria

  1. Dado que estou editando um pedido com subtotal R$ 3.500,00 Quando selecionar "Boleto 30/60/90" com tx_acrescimo = 2.5 Então o campo "Acréscimo" exibe R$ 87,50 e o total exibe R$ 3.587,50

  2. Dado que já selecionei "Boleto 30/60/90" (2,5%) e troco para "Dinheiro" (tx_acrescimo = 0) Quando a forma de pagamento for alterada Então o campo "Acréscimo" retorna a R$ 0,00 e o total retorna a R$ 3.500,00

  3. Dado que a forma de pagamento selecionada tem tx_acrescimo = NULL (zero no VO após Story 1.2) Quando o cálculo for executado Então acréscimo = R$ 0,00, sem crash ou NullPointerException

  4. Dado que o cálculo é disparado Quando a troca de forma de pagamento ocorrer Então o recálculo conclui em menos de 100ms (aritmética em memória, sem I/O)

Escopo desta história

Esta história cobre exclusivamente o cálculo em memória e exibição do acréscimo ao selecionar forma de pagamento.

  • NÃO persiste o valor no banco — persistência é escopo da Story 2.3
  • NÃO adiciona campos novos na UI — campos tvAcrescimoPedido e tvAcrescimo foram criados na Story 2.1
  • NÃO lê do SQLite durante a seleção — tx_acrescimo já está no objeto FormaPagamento em memória

Tasks / Subtasks

  • Task 1: Atualizar cálculo em MainPedidoFragment.atualizarResumoPedido() (AC: 1, 2, 3, 4)

    • 1.1 — Na linha ~974 de MainPedidoFragment.java, substituir o bloco placeholder:
      double vlAcrescimo = 0.0; // Story 2.2 calculara: subtotal * (tx_acrescimo/100)
      tvAcrescimoPedido.setText(Util.formataValorMonetario(vlAcrescimo));
      tvTotalGeral.setText(Util.formataValorMonetario(totalGeral));
      
      Por:
      double txAcrescimo = 0.0;
      if (Global.pedido.getFormapag() != null) {
          txAcrescimo = Global.pedido.getFormapag().getTxAcrescimo();
      }
      double vlAcrescimo = totalGeral * (txAcrescimo / 100.0);
      tvAcrescimoPedido.setText(Util.formataValorMonetario(vlAcrescimo));
      tvTotalGeral.setText(Util.formataValorMonetario(totalGeral + vlAcrescimo));
      
  • Task 2: Atualizar cálculo em MainPedidoFragment.fillFields() (AC: 1, 2, 3)

    • 2.1 — Na linha ~439 de MainPedidoFragment.java, substituir o bloco placeholder:
      double vlAcrescimo = 0.0; // Story 2.2 calculara: subtotal * (tx_acrescimo/100)
      tvAcrescimoPedido.setText(Util.formataValorMonetario(vlAcrescimo));
      tvTotalGeral.setText(Util.formataValorMonetario(totalGeral));
      
      Pelo mesmo padrão da Task 1.1 (idêntico — fillFields() usa as mesmas variáveis locais totalGeral).
  • Task 3: Atualizar cálculo em TotalPedidoFragment.FillFields() (AC: 1, 2, 3)

    • 3.1 — Na linha ~97 de TotalPedidoFragment.java, substituir:
      tvAcrescimo.setText(Util.formataValorMonetario(0.0));
      tvTotalGeral.setText(Util.formataValorMonetario(vlTotalGeral));
      
      Por:
      double txAcrescimo = 0.0;
      if (Global.pedido.getFormapag() != null) {
          txAcrescimo = Global.pedido.getFormapag().getTxAcrescimo();
      }
      double vlAcrescimo = vlTotalGeral * (txAcrescimo / 100.0);
      tvAcrescimo.setText(Util.formataValorMonetario(vlAcrescimo));
      tvTotalGeral.setText(Util.formataValorMonetario(vlTotalGeral + vlAcrescimo));
      
      Atenção: a variável local em TotalPedidoFragment é vlTotalGeral (não totalGeral como em MainPedidoFragment).

Dev Notes

Estado atual do código após Story 2.1

Ambos os métodos já possuem o placeholder exato que deve ser substituído:

MainPedidoFragment.javaatualizarResumoPedido() (linha ~974):

double vlAcrescimo = 0.0; // Story 2.2 calculara: subtotal * (tx_acrescimo/100)
tvAcrescimoPedido.setText(Util.formataValorMonetario(vlAcrescimo));
tvTotalGeral.setText(Util.formataValorMonetario(totalGeral));

MainPedidoFragment.javafillFields() (linha ~439):

double vlAcrescimo = 0.0; // Story 2.2 calculara: subtotal * (tx_acrescimo/100)
tvAcrescimoPedido.setText(Util.formataValorMonetario(vlAcrescimo));
tvTotalGeral.setText(Util.formataValorMonetario(totalGeral));

TotalPedidoFragment.javaFillFields() (linha ~97):

tvAcrescimo.setText(Util.formataValorMonetario(0.0));
tvTotalGeral.setText(Util.formataValorMonetario(vlTotalGeral));

Fórmula de cálculo

subtotal     = totalProduto - totalDesconto + totalIcmsST (+ totalIPI se !precoComIpi)
vlAcrescimo  = subtotal * (tx_acrescimo / 100.0)
totalFinal   = subtotal + vlAcrescimo

O subtotal já está calculado nas variáveis locais totalGeral (MainPedidoFragment) e vlTotalGeral (TotalPedidoFragment) — não recalcular.

Onde tx_acrescimo está disponível (sem I/O)

Global.pedido.getFormapag().getTxAcrescimo() retorna double diretamente — a FormaPagamento já está carregada em memória. Não há necessidade de acessar o SQLite durante o cálculo — NFR1 (< 100ms) é satisfeito trivialmente.

Null-safety obrigatória

Global.pedido.getFormapag() pode ser null (pedido sem forma de pagamento definida). getTxAcrescimo() retorna double (primitivo), então não há risco de NPE no getter, mas o objeto pai pode ser null. Padrão obrigatório:

double txAcrescimo = 0.0;
if (Global.pedido.getFormapag() != null) {
    txAcrescimo = Global.pedido.getFormapag().getTxAcrescimo();
}

FormaPagamentoDB (Story 1.2) já garante que tx_acrescimo = NULL no SQLite é lido como 0.0 no VO — sem risco adicional.

Fluxo de disparo do cálculo em MainPedidoFragment

O cálculo é disparado em dois cenários:

  1. Seleção de forma de pagamentoonItemSelected() → chama atualizarResumoPedido() (linha 789):

    } else if (spFormaPag != null && parent == spFormaPag) {
        if ((!isFormaPagBloq && ...) || Global.pedido.getFormapag() == null){
            Global.pedido.setFormapag(listForPagtos.get(pos));
            atualizarResumoPedido();  // ← aqui está o gatilho
        }
    }
    

    O Global.pedido.getFormapag() já está atualizado quando atualizarResumoPedido() é chamado.

  2. Carregamento inicial do fragmentoonResume()Handler.post()fillFields() → exibe o acréscimo da forma já selecionada.

Nomes de variáveis — diferença entre os dois fragmentos

Fragmento Variável "subtotal" TextView acréscimo TextView total
MainPedidoFragment totalGeral tvAcrescimoPedido tvTotalGeral
TotalPedidoFragment vlTotalGeral tvAcrescimo tvTotalGeral

Não confundir os nomes — são fragmentos separados com variáveis locais distintas.

TotalPedidoFragment não chama atualizarResumoPedido()

TotalPedidoFragment é independente de MainPedidoFragment. Ele recalcula tudo em FillFields(), chamado no onStart(), onResume() e setUserVisibleHint(). O campo tvAcrescimo neste fragmento mostra o acréscimo da forma de pagamento atual do Global.pedido — que já foi atualizado pelo MainPedidoFragment via Global.pedido.setFormapag(...) antes de qualquer troca de aba.

Arquivos a modificar

Arquivo Caminho O que muda
MainPedidoFragment.java src/br/com/jcsinformatica/sarandroid/pedido/ Substituir placeholder em atualizarResumoPedido() e fillFields()
TotalPedidoFragment.java src/br/com/jcsinformatica/sarandroid/pedido/ Substituir placeholder em FillFields()

NÃO modificar nesta história:

  • FormaPagamento.javagetTxAcrescimo() já existe (Story 1.2)
  • FormaPagamentoDB.java — leitura de tx_acrescimo já implementada (Story 1.2)
  • Pedido.java — campo vlAcrescimo no VO não é necessário nesta história (Story 2.3 adiciona persistência)
  • PedidoDB.java — sem mudança de banco nesta história
  • DatabaseHelper.java — sem migração de schema nesta história
  • Layouts XML — sem mudança (campos já existem da Story 2.1)

Regras críticas do projeto (aplicáveis a esta história)

  • Sem Kotlin — somente Java puro
  • Sem Gradle — projeto Eclipse ADT
  • Sem JARs novos — cálculo aritmético, sem dependências
  • Sem testes automatizados — validação manual via emulador/dispositivo
  • Strings com acentos — NÃO usar em código Java; esta história não adiciona strings novas
  • SQLite em thread background — esta história NÃO lê o banco durante o cálculo, não se aplica

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

  1. Abrir pedido com itens — aba Dados → selecionar forma de pagamento com tx_acrescimo > 0 → verificar campo "Acréscimo" e "Total Geral" atualizados corretamente
  2. Trocar para forma de pagamento com tx_acrescimo = 0 → verificar retorno a R$ 0,00 no acréscimo e total sem acréscimo
  3. Navegar para aba Total → verificar que tvAcrescimo e tvTotalGeral refletem o mesmo valor calculado
  4. Abrir pedido sem forma de pagamento definida → sem crash; acréscimo = R$ 0,00
  5. Conferir: 3500 × 2,5% = 87,50 e 3500 + 87,50 = 3587,50 nos campos visuais

Inteligência de histórias anteriores

  • Story 2.1 preparou: tvAcrescimoPedido em MainPedidoFragment (vinculado em onCreateView()) e tvAcrescimo em TotalPedidoFragment — ambos prontos para receber valores calculados
  • Story 1.2 garantiu: FormaPagamento.getTxAcrescimo() retorna double (nunca null; NULL no SQLite → 0.0)
  • Story 2.1 documentou: padrão vlAcrescimo = 0.0 como ponto de expansão explícito nos dois locais de MainPedidoFragment e como 0.0 hardcoded em TotalPedidoFragment
  • Padrão de separação UI/background do MainPedidoFragment.onStart() (Thread + runOnUiThread) não é necessário aqui — cálculo é puramente em memória, sem I/O

Review Findings

  • [Review][Defer] txAcrescimo negativo não guardado — banco de dados do ERP possui checagem para valores negativos, tornando o guard defensivo desnecessário — deferred, ERP validates at source
  • [Review][Defer] Global.pedido sem null-check em atualizarResumoPedido() — padrão pré-existente: método já chama Global.pedido.getQtdTotalProduto() etc. sem guard [MainPedidoFragment.java:958] — deferred, pre-existing
  • [Review][Defer] Global.pedido sem null-check em TotalPedidoFragment.FillFields() — NPE silenciosa dentro do catch(Exception e){} vazio; pré-existente [TotalPedidoFragment.java:74] — deferred, pre-existing
  • [Review][Defer] Lógica de cálculo do acréscimo duplicada em fillFields() e atualizarResumoPedido() sem método auxiliar — padrão DRY violado, pré-existente no projeto [MainPedidoFragment.java:436,975] — deferred, pre-existing
  • [Review][Defer] catch (Exception e) {} vazio em TotalPedidoFragment.FillFields() engole qualquer exceção do novo bloco — pré-existente [TotalPedidoFragment.java:99] — deferred, pre-existing
  • [Review][Defer] Aritmética de ponto flutuante (double) para valores monetários — padrão projeto-wide pré-existente; Util.formataValorMonetario() já trata formatação [todos os sites] — deferred, pre-existing
  • [Review][Defer] codigoLiberacao2() computa o hash de autorização sobre totalGeral sem acréscimo — supervisor autoriza valor menor que o real quando há acréscimo; escopo da Story 2.3 [MainPedidoFragment.java:652] — deferred, pre-existing
  • [Review][Defer] validaCampos(): verificação de limite de crédito usa getTotalProduto() excluindo acréscimo — pré-existente, fora do escopo da Story 2.2 [MainPedidoFragment.java:606] — deferred, pre-existing
  • [Review][Defer] validaCampos(): check de vlPedMin usa getTotalProduto() excluindo acréscimo — pré-existente [MainPedidoFragment.java:617] — deferred, pre-existing
  • [Review][Defer] Race condition: listForPagtos preenchido em thread background vs atualização da UI no Handler.post — pré-existente, não introduzido por este diff [MainPedidoFragment.java:193] — deferred, pre-existing
  • [Review][Defer] totalGeral calculado com threshold diferente em fillFields() (≤ STATUS_LIBERADO) vs atualizarResumoPedido() (< STATUS_ENVIADO) para descontoV — inconsistência pré-existente entre os dois métodos — deferred, pre-existing
  • [Review][Defer] FormaPagamento em Global.pedido pode estar stale após sync do servidor — txAcrescimo em memória reflete taxa pré-sync até o usuário navegar da tela — deferred, pre-existing

Dev Agent Record

Agent Model Used

claude-sonnet-4-6 (dev-story workflow)

Debug Log References

Completion Notes List

  • Story 2.2 implementada em 2026-04-16.
  • MainPedidoFragment.javaatualizarResumoPedido(): substituído placeholder vlAcrescimo = 0.0 por cálculo real totalGeral * (txAcrescimo / 100.0) com null-guard em getFormapag(). tvTotalGeral agora exibe totalGeral + vlAcrescimo.
  • MainPedidoFragment.javafillFields(): mesma substituição aplicada, garantindo que o campo de acréscimo e o total já mostram o valor correto ao carregar o pedido.
  • TotalPedidoFragment.javaFillFields(): substituído Util.formataValorMonetario(0.0) hardcoded por cálculo equivalente usando variável local vlTotalGeral. tvTotalGeral atualizado com vlTotalGeral + vlAcrescimo.
  • Null-guard if (Global.pedido.getFormapag() != null) aplicado nos 3 pontos — tx_acrescimo default 0.0 quando sem forma de pagamento (AC3 satisfeito).
  • Cálculo puramente aritmético em memória, sem I/O — NFR1 < 100ms satisfeito trivialmente (AC4 satisfeito).
  • Sem testes automatizados (projeto não possui infraestrutura de testes). Validação é manual via dispositivo/emulador.
  • AC1 satisfeito: subtotal × (tx_acrescimo/100) exibido ao selecionar forma com acréscimo.
  • AC2 satisfeito: ao trocar para forma sem acréscimo, campo retorna a R$ 0,00 e total retorna ao subtotal.

File List

  • src/br/com/jcsinformatica/sarandroid/pedido/MainPedidoFragment.java
  • src/br/com/jcsinformatica/sarandroid/pedido/TotalPedidoFragment.java