Skip to content

feat(balcao): estorno, confirmacao com Desfazer, pontos pelo valor e clientes recentes (tasks 4 a 7) - #28

Merged
sousa7tz merged 6 commits into
mainfrom
feat/balcao-estorno-regra-recentes
Oct 6, 2026
Merged

sousa7tz merged 6 commits into
mainfrom
feat/balcao-estorno-regra-recentes

Conversation

@sousa7tz

@sousa7tz sousa7tz commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Tasks 4, 5, 6 e 7 do backlog (melhorias do balcão), um commit por task.

Task 4: estorno de lançamento (d62fda9)

  • Estorno pelo extrato: o lojista estorna pelo extrato do cliente um lançamento das últimas 24 h ("Estornar", com confirmação).
  • Nada é apagado: o estorno vira uma linha nova reversal no points_log, com reverses_id apontando para o lançamento. Saldo e total acumulado voltam ao que eram.
  • Saldo nunca negativo: se o cliente já gastou parte dos pontos, o estorno é recusado.
  • Um estorno por lançamento: o UNIQUE em reverses_id garante isso, inclusive com dois cliques simultâneos.
  • Relatórios: "Pontos emitidos" desconta os estornos, e extrato e relatórios mostram o selo "Estorno".
  • Exclusão LGPD: a exclusão do cliente também limpa a descrição dos estornos.
  • Migration: 004_estorno.sql.

Task 5: confirmação com novo saldo e Desfazer (c3c17d3)

  • Confirmação: depois de lançar, a tela do cliente mostra "+30 pontos lançados para Bia. Saldo agora: 57 pontos. Faltam 43 para Bolo."
  • Desfazer: o botão fica 30 s na tela, com contagem regressiva, e usa o estorno da task 4.
  • Só logo depois de lançar: a confirmação aparece só para o lançamento recém-feito (até 2 min) e não estornado. Recarregar depois não oferece Desfazer de novo.

Task 7: clientes atendidos recentemente (51f7ab0)

  • Lista na home: abaixo do campo de telefone aparecem os 5 últimos clientes com movimentação, e um toque abre a tela do cliente.
  • O que entra: cadastro sem movimentação não conta como atendimento. Clientes excluídos e de outras lojas não aparecem.

Task 6: pontos pelo valor da compra (e325bdd)

  • Regra da loja: a loja define "a cada R$ X em compras, 1 ponto" (de R$ 0,01 a R$ 1.000.000,00) em merchant/points-rule. A tela tem link a partir de Prêmios, da tela do cliente e do guia de primeiros passos, onde a regra é um passo opcional.
  • Prévia no balcão: o lojista digita o valor da compra e vê a prévia dos pontos. O servidor recalcula e sempre arredonda para baixo: R$ 12,90 com R$ 1 = 1 ponto dá 12 pontos.
  • Valores aceitos: "12,90", "12.90", "1.234,56" e "R$ 12,90".
  • Sem float: dinheiro é tratado em centavos inteiros (App\Support\Money).
  • Lançar direto continua valendo: digitar os pontos (ou usar +1/+5/+10) limpa o valor da compra, para não lançar uma coisa achando que é outra.
  • Migration: 005_regra_pontos.sql.
  • Correção junto: o extrato rolava 188 px para o lado a 360 px. O rótulo escondido da nova coluna de ações escapava do scroll da tabela.

Testes

  • Suíte: 205 testes / 898 asserções (eram 164).
  • Mutação: remover a checagem de saldo do estorno faz a suíte falhar.
  • JavaScript em navegador de verdade (Edge): prévia de R$ 12,90 = 12 pontos, R$ 0,50 abaixo de 1 ponto, o +5 limpa o valor, lançamento, contagem do Desfazer e Desfazer, sem erros de JS.
  • Migrations 004 e 005: aplicadas sobre o schema da main, ficam iguais, coluna a coluna, a uma instalação nova.
  • Layout: auditoria de 360 a 1440 px sem problemas, incluindo a tela nova da regra.

Banco local: rodar 004_estorno.sql e 005_regra_pontos.sql. O banco de dev do autor já está migrado.

🤖 Generated with Claude Code

sousa7tz and others added 6 commits October 4, 2026 00:24
Task 4 do backlog: o lojista estorna pelo extrato um lancamento das
ultimas 24 h. O estorno vira um registro novo (reversal) apontando para o
lancamento, nada e apagado, e o saldo nunca fica negativo. Migration 004.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Task 5 do backlog: depois de lancar, a tela do cliente mostra os pontos
lancados, o saldo novo e quanto falta para o proximo premio, com um botao
Desfazer por 30 s (usa o estorno da task 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Task 7 do backlog: abaixo do campo de telefone aparecem os 5 ultimos
clientes atendidos (com movimentacao), e um toque abre a tela do cliente.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Task 6 do backlog: a loja define a regra (a cada R$ X, 1 ponto, de R$ 0,01
em diante) e no balcao digita o valor da compra; a tela mostra a previa e o
servidor recalcula, sempre arredondando para baixo. Lancar pontos direto
continua valendo. Migration 005.

Tambem corrige a rolagem horizontal do extrato a 360 px: o rotulo
escondido da coluna de acoes escapava do scroll da tabela.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
traz o controle de migrations, a configuracao por ambiente e o ip por proxy confiavel (#29).
- 004_estorno e 005_regra_pontos registradas no schema_migrations do schema.sql
- cabecalho das migrations 004 e 005 aponta para o php bin/migrate.php
- LoyaltyCardModel::log grava reverses_id e o ip resolvido pelo ClientIp
- docs com as instrucoes de migration da main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eat/balcao-estorno-regra-recentes

- MerchantModel::findById devolve points_rule_cents e password_hash (regra de pontos e assinatura da senha)
- authGuard confere a assinatura da senha antes de guardar a loja da requisicao
- constantes, rotas do README e codigos de flash das duas branches mantidos

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@sousa7tz sousa7tz left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisão (feita com o Claude Code; o GitHub não deixa o autor aprovar a própria PR, então fica como comentário).

Conferido e ok

  • reverse(): elegibilidade (desta loja, earn, ainda não estornado, até 24 h, cartão não anonimizado) + FOR UPDATE no cartão antes de checar o saldo; dois estornos simultâneos esbarram no UNIQUE de reverses_id e viram already. Estorno concorrente com anonimização cai em insufficient (saldo zerado), sem saldo negativo.
  • O log_id do POST é conferido contra o cartão do cliente (logBelongsToCard) e o model confere a loja de novo.
  • Os pontos pela regra são calculados no servidor, com intdiv (sempre pra baixo) e em centavos inteiros, sem float; o limite de MAX_POINTS_PER_ENTRY também vale nesse caminho.
  • A anonimização agora limpa também a descrição dos estornos (Estorno: <texto livre>).
  • points_issued nos relatórios desconta os estornos.
  • Migrations 004/005 registradas no schema.sql; toda saída nova das views passa por e() ou (int).
  • CI verde (PHP 8.1, 8.3 e masks.js); 283 testes passando localmente.

Melhorias menores, não bloqueiam

  1. Money::toCents("-10") devolve 1000: o sinal é descartado junto com o resto. Melhor recusar com valor_invalido.
  2. Compra que passa de 10.000 pontos pela regra devolve pontos_invalidos, mensagem genérica para quem digitou um valor em R$.
  3. logBelongsToCard faz SQL direto no controller; o padrão do projeto é deixar isso no PointsLogModel.
  4. O Desfazer some em 30 s só no navegador; no servidor o estorno vale por 24 h. É coerente com o extrato, mas vale deixar claro no texto do botão.

Sem bloqueios: pode mergear.

@sousa7tz
sousa7tz merged commit f3c54c1 into main Oct 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant