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