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
- Cria cópia do cart via
Justificativa: Evita loops infinitos de recálculo ao editar descontos em itens recomendados.
removeRecommendedItem(int index)
- Remove item do array
recommendedItemspelo í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
changeWithSkipTotalspara 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
CheckSellRecommendedna árvore de widgets (linha 121) - Posicionamento: Entre
CheckSellProductseCheckSellAddress
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
AppGetBuildercomcartControllerpara reatividade - Retorna
SizedBox.shrink()serecommendedItemsfor null ou vazio
Componentes Internos:
_RecommendedProductCard
Exibe cada item com:
- Imagem:
ZzUrlImage(75x75, borderRadius 4) - Nome:
ZZTextNewcommaxLines: 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
CheckSellProductDiscountcom 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
- Separação de responsabilidades: Widget dedicado para itens recomendados
- Reuso de componentes:
CheckSellProductDiscountadaptado com callback - Evita side effects:
changeWithSkipTotalsprevine recálculos desnecessários - UX informativa: Mensagem clara sobre disponibilidade (1 hora)
- Validação de desconto: Aplica as mesmas regras do carrinho principal
⚠️ Pontos de Atenção
-
Hardcoded de 1 hora: A mensagem de "1 hora" está hardcoded na UI
- Recomendação: Vir de configuração ou constantes
-
Nil vs Value: Uso de
const Nil()vsn.Value(items)para array vazio- Verificar se isso é consistente com outras partes do código
-
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
- Recomendação: Adicionar testes para os novos métodos de
-
Tratamento de erros:
applyRecommendedItemDiscountnão trata item não encontrado- Se
index != -1, silenciosamente não faz nada - Considerar log ou tratamento explícito
- Se
🔍 Possíveis Melhorias
-
Extrair magic numbers:
75(tamanho da imagem)4(borderRadius)1 hora(mensagem)
-
Constantes para cores:
ZZColors.attentionLightestrepetido -
Documentação: Adicionar doc comments nos novos métodos de
CartController
Riscos Identificados
🟡 Risco Médio
Integração com cálculos de carrinho:
changeWithSkipTotalspropositalmente pula recálculos- Risco: Se
recommendedItemsfuturamente 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
recommendedItemsindevidamente - Mitigação: Verificar se
recommendedItemsestá excluído decart.itemsDiscount
🟢 Risco Baixo
Performance:
- Lista com
shrinkWrap: trueeNeverScrollableScrollPhysics - OK para poucos itens, mas pode ser problema com muitos recomendados
Recomendações
Para Próximo PR
-
Adicionar testes unitários para:
changeWithSkipTotalsremoveRecommendedItemapplyRecommendedItemDiscount
-
Adicionar widget tests para
CheckSellRecommended -
Documentar o motivo de
changeWithSkipTotalscom doc comment -
Considerar extrair lógica de formatação de desconto para helper
Para Future
-
Avaliar se
recommendedItemsdeve participar de totais do carrinho -
Considerar configurar mensagem de "1 hora" via backend
-
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:
- Adicionar testes unitários
- Adicionar doc comments
- Considerar extrair constantes
- Merge após code review da equipe