Skip to main content

Review de Alterações - US 194760 Task 10

Produtos Recomendados no Check Sell

Data: 08/06/2026
Branch: feat/194760_upsell
Status: Alterações não commitadas


Arquivos Modificados

1. lib/controllers/cart/cart_controller.dart

Linhas modificadas: 610-640 (32 linhas adicionadas)

Novos métodos criados:

changeWithSkipTotals(ChangeCartFunction change)

  • Propósito: Aplica mutação no carrinho SEM recalcular totais
  • Uso exclusivo: Operações em recommendedItems
  • Implementação:
    • Cria cópia do cart via copyWith()
    • Aplica função de mutação
    • Dispara _openSalesController.activeCartDTO.trigger(newCart)
    • NÃO chama updateCRMBonusValues() nem recalcula totais

Justificativa: Evita loops infinitos de recálculo ao editar descontos em itens recomendados.

removeRecommendedItem(int index)

  • Remove item do array recommendedItems pelo índice
  • Se array ficar vazio, usa const Nil()
  • Caso contrário, usa n.Value(items)

applyRecommendedItemDiscount(CartItemDTO updatedItem)

  • Encontra item pelo cartItemId
  • Substitui item atualizado no array
  • Usa changeWithSkipTotals para persistir sem recálculo

2. lib/screens/sell/new_sell/check_sell/check_sell_screen.dart

Linhas modificadas: 13, 121-125 (6 linhas adicionadas)

Alterações:

  • Import do novo widget: import 'check_sell_recommended.dart'
  • Inserção do CheckSellRecommended na árvore de widgets (linha 121)
  • Posicionamento: Entre CheckSellProducts e CheckSellAddress

Parâmetros passados:

CheckSellRecommended(
cartController,
checkSellController,
employeeSalesController,
)

3. lib/screens/sell/new_sell/check_sell/widgets/check_sell_product_discount.dart

Linhas modificadas: 37, 47, 251-290 (68 linhas modificadas)

Novidades:

Parâmetro opcional onConfirmDiscount

  • Tipo: Future<void> Function(CartItemDTO)?
  • Propósito: Callback customizado para confirmar desconto
  • Uso principal: Itens recomendados

Fluxo alternativo no _onConfirm (linhas 266-290):

if (onConfirmDiscount != null) {
// Não chama applyCartItemUpdateValuePrice
// Não verifica CRM bonus
// Executa callback customizado
await onConfirmDiscount!(cartItemDTO);
checkSellController.clearBottomSheetEditPrice();
Get.close(1);
ZzToast.showSuccess('Seu desconto foi atualizado!');
}

Justificativa: Itens recomendados não participam do cálculo do carrinho principal, então não devem recalcular totais nem verificar CRM.


Arquivo Novo

4. lib/screens/sell/new_sell/check_sell/widgets/check_sell_recommended.dart

Linhas: 359 (arquivo completo)

Estrutura

Widget Principal: CheckSellRecommended

  • Responsável por renderizar a seção de produtos recomendados
  • Usa AppGetBuilder com cartController para reatividade
  • Retorna SizedBox.shrink() se recommendedItems for null ou vazio

Componentes Internos:

_RecommendedProductCard

Exibe cada item com:

  • Imagem: ZzUrlImage (75x75, borderRadius 4)
  • Nome: ZZTextNew com maxLines: 2
  • SKU: Com ícone barcode (se disponível)
  • Tamanho: Label "Tamanho:" + valor (se disponível)
  • Preço: MaskUtils.formatNumberBr(cartItemDTO.unitPrice)
  • Desconto: Percentual ou valor, com cor de destaque
  • Preço Final: Com destaque em verde se houver desconto

Ação: Botão de remover com confirmação via ConfirmBottomSheet

_RecommendedButtonAddDiscount
  • Visibilidade condicional: checkSellController.hasMaxDiscountPercent
  • Botão "EDITAR" (ZzTextButton)
  • Ao clicar: abre CheckSellProductDiscount com callback customizado
_RecommendedLabelValue

Widget helper para exibir pares label/valor:

  • Suporta desconto (com cor e prefixo)
  • Suporta percentual (com %)
  • Alinhamento: spaceBetween

UX: Card de aviso amarelo (attentionLightest) informando:

"Os produtos recomendados ficarão disponíveis para a cliente por até 1 hora."


Análise de Qualidade

✅ Pontos Fortes

  1. Separação de responsabilidades: Widget dedicado para itens recomendados
  2. Reuso de componentes: CheckSellProductDiscount adaptado com callback
  3. Evita side effects: changeWithSkipTotals previne recálculos desnecessários
  4. UX informativa: Mensagem clara sobre disponibilidade (1 hora)
  5. Validação de desconto: Aplica as mesmas regras do carrinho principal

⚠️ Pontos de Atenção

  1. Hardcoded de 1 hora: A mensagem de "1 hora" está hardcoded na UI

    • Recomendação: Vir de configuração ou constantes
  2. Nil vs Value: Uso de const Nil() vs n.Value(items) para array vazio

    • Verificar se isso é consistente com outras partes do código
  3. Sem testes: Não há menção a testes unitários ou widget tests

    • Recomendação: Adicionar testes para os novos métodos de CartController
  4. Tratamento de erros: applyRecommendedItemDiscount não trata item não encontrado

    • Se index != -1, silenciosamente não faz nada
    • Considerar log ou tratamento explícito

🔍 Possíveis Melhorias

  1. Extrair magic numbers:

    • 75 (tamanho da imagem)
    • 4 (borderRadius)
    • 1 hora (mensagem)
  2. Constantes para cores: ZZColors.attentionLightest repetido

  3. Documentação: Adicionar doc comments nos novos métodos de CartController


Riscos Identificados

🟡 Risco Médio

Integração com cálculos de carrinho:

  • changeWithSkipTotals propositalmente pula recálculos
  • Risco: Se recommendedItems futuramente participarem de totais, código quebra
  • Mitigação: Comentário claro no código sobre uso exclusivo

Validação de desconto:

  • Desconto em itens recomendados usa as mesmas regras do carrinho
  • Risco: Limite combinado pode incluir recommendedItems indevidamente
  • Mitigação: Verificar se recommendedItems está excluído de cart.itemsDiscount

🟢 Risco Baixo

Performance:

  • Lista com shrinkWrap: true e NeverScrollableScrollPhysics
  • OK para poucos itens, mas pode ser problema com muitos recomendados

Recomendações

Para Próximo PR

  1. Adicionar testes unitários para:

    • changeWithSkipTotals
    • removeRecommendedItem
    • applyRecommendedItemDiscount
  2. Adicionar widget tests para CheckSellRecommended

  3. Documentar o motivo de changeWithSkipTotals com doc comment

  4. Considerar extrair lógica de formatação de desconto para helper

Para Future

  1. Avaliar se recommendedItems deve participar de totais do carrinho

  2. Considerar configurar mensagem de "1 hora" via backend

  3. Avaliar performance com muitos itens recomendados


Conclusão

Status: ✅ APROVADO COM RECOMENDAÇÕES

A implementação está sólida, com boa separação de responsabilidades e reuso de componentes existentes. O uso de changeWithSkipTotals é uma solução elegante para evitar recálculos desnecessários.

Os principais pontos de atenção são documentação adicional e testes. Nenhum blocker identificado.

Próximos passos sugeridos:

  1. Adicionar testes unitários
  2. Adicionar doc comments
  3. Considerar extrair constantes
  4. Merge após code review da equipe