Skip to main content

Code Review v2 - develop vs feat/194760_upsell_v1

Data: 2026-06-10 Projeto: coezzion_vendas_app (Flutter/Dart, GetX) Autor da análise: opencode


1. Escopo Analisado

  • Comparação: git diff develop...feat/194760_upsell_v1 (merge-base)
  • Commits da feature (6):
CommitMensagem
d72d3ca3feat(#194760): adiciona feature flags de upsell no remote config
796fdba6feat(#194760): adiciona step recomendar no stepper de venda
952e4b12feat(#194760): implementa tela e lógica de recomendação upsell
73952f3crefactor(#194760): migração de ícones/refactor bottom bar
ed58687cfix(#194760): ajuste de referências de ícone
ba14becefeat(#194760): integra recomendados no checkout

2. Resumo do Diff

MétricaValor
Arquivos alterados42
Linhas adicionadas4.086
Linhas removidas142
Tamanho do diff172 KB, 4.997 linhas
Commits na feature6

Distribuição:

  • lib/ (34 arquivos): 10 novos, 24 modificados
  • test/ (8 arquivos): 7 novos, 1 modificado
  • 1 modificação no pubspec.yaml

Principais blocos:

ÁreaStatusDescrição
upsell_recommendation_controller.dartNovo (222 linhas)Controller principal do fluxo de upsell
upsell_recommendation_screen.dartNovo (142 linhas)Tela de recomendação
upsell_recommendation_card.dartNovo (240 linhas)Card de produto recomendado
upsell_bottom_bar.dartNovo (244 linhas)Bottom bar com máquina de estados
upsell_size_selection_modal.dartNovo (136 linhas)Modal de seleção de tamanho
check_sell_recommended.dartNovo (359 linhas)Seção de recomendados no checkout
cart_controller.dartModificado (+75 linhas)AddRecommendedProducts + discount logic
cart_dto.dartModificado (+22 linhas)Campo recommendedItems com serialização
new_sell_stepper.dartModificado (+84 linhas)Nova etapa SteppSell.recomendar
stepper.dart / stepp_sell.dartModificadosStep enum + model
firebase_remote_config_service.dartModificado (+14 linhas)Feature flag upsell
upsell_navigation_helper.dartNovo (43 linhas)Helper de navegação
zz_error_state.dartNovo (59 linhas)Widget de erro reutilizável
zz_empty_state_new.dartModificado (+32 linhas)Factory withIcon

3. Cobertura de Testes

3.1 Global

Comando executado em ambas as branches:

flutter test --coverage
BranchLFLHCobertura (%)Δ vs develop
develop45.0882.4235,37%
feat/194760_upsell_v145.7132.7926,11%+0,74 p.p.
  • Ganho relativo de cobertura: +13,78%
  • Arquivos de teste executados: 33 (feat) vs 26 (develop) → +7 novos arquivos de teste

3.2 Cobertura por Arquivo (arquivos do diff)

ArquivoLFLHCoberturaΔ CoberturaStatus
upsell_recommendation_controller.dart926873,91%N/A🟢
cart_controller.dart3397823,01%+18,02 p.p.🟡
cart_dto.dart21616475,93%+3,38 p.p.🟢
upsell_recommendation_item.dart553665,45%N/A🟢
stepper.dart9555,56%+55,56 p.p.🟢
get_it.dart33225275,90%+0,79 p.p.🟢
new_sell_stepper.dart7022,86%+2,86 p.p.🔴
new_sell.dart3912,56%+2,56 p.p.🔴
check_sell_screen.dart9111,10%+1,10 p.p.🔴
zz_empty_state_new.dart1616,25%+6,25 p.p.🔴
check_address_customer_screen.dart9811,02%+1,02 p.p.🔴
upsell_bottom_bar.dart7034,29%N/A🔴
upsell_recommendation_screen.dart5100,00%N/A🔴
upsell_recommendation_card.dartmedido via widget testN/A🟡
upsell_size_selection_modal.dartmedido via widget testN/A🟡
check_sell_recommended.dart11800,00%N/A🔴
check_sell_product_discount.dart20700,00%N/A🔴
upsell_navigation_helper.dart1200,00%N/A🔴
zz_error_state.dart1200,00%N/A🔴
pages_titles.dart14400,00%N/A🔴
firebase_remote_config_service.dart19900,00%N/A🔴
app_pages.dart48900,00%N/A🔴
app_routes.dartconst, sem linhas instrumentadasN/A

3.3 Arquivos de Teste Criados/Modificados

ArquivoLinhasTipo
upsell_recommendation_controller_test.dart515Unitário (controller)
cart_dto_test.dart+417Unitário (modelo)
cart_controller_add_recommended_products_test.dart352Unitário (controller)
upsell_recommendation_card_test.dart322Widget test
upsell_size_selection_modal_test.dart272Widget test
upsell_recommendation_item_test.dart242Unitário (modelo)
upsell_bottom_bar_test.dart107Widget test
stepper_model_test.dart43Unitário (modelo)

Total de linhas de teste novas: ~2.270


4. Análise Detalhada por Arquivo

4.1 upsell_recommendation_controller.dart (NOVO, 222 linhas, 73,91% cobertura)

O que faz: Controller GetX que gerencia todo o fluxo de recomendação: elegibilidade, fetch, retry, toggle de seleção e integração com o carrinho.

Achados:

  • [MÉDIO] Race condition em _onCartChanged / _resetState: Quando o carrinho muda de um carrinho elegível para outro também elegível, hasLoadedRecommendations pode permanecer true, impedindo um novo fetch. As recomendações do carrinho anterior podem ser exibidas para o novo carrinho. Ver linhas _onCartChanged e needFetchRecommendations.
  • [BAIXO] Catch silencioso em fetchRecommendations(): A exceção é logada mas não há telemetria/evento de erro estruturado. Diagnóstico em produção fica prejudicado.
  • [BAIXO] selectedSize mutável exposto publicamente: A variável selectedSize é pública e mutável, podendo ser alterada externamente sem passar pelos métodos de toggle.
  • [POSITIVO] Lógica de elegibilidade bem isolada: Método isCartEligible verifica feature flag + venda em aberto + loja OK.
  • [POSITIVO] Testes robustos: 73,91% de cobertura com 515 linhas de teste cobrindo fetch, retry, toggle, reset, e cenários de borda.

4.2 upsell_recommendation_screen.dart (NOVO, 142 linhas, 0% cobertura)

O que faz: Tela GetView que exibe a lista de recomendações em um GridView, integrada com o controller e bottom bar.

Achados:

  • [MÉDIO] Zero cobertura de teste: Nenhum teste de tela foi implementado. A tela tem 51 linhas instrumentadas, 0 cobertas.
  • [POSITIVO] Responsabilidade bem definida: Apenas orquestra layout, delega estado para o controller e dados para os cards.

4.3 check_sell_recommended.dart (NOVO, 359 linhas, 0% cobertura)

O que faz: Seção de itens recomendados dentro da tela de fechamento do pedido (checkout). Exibe cards com imagem, nome, preço, tamanho e botão de adicionar.

Achados:

  • [ALTO] Crítico — maior arquivo novo sem testes: 359 linhas, 118 linhas instrumentadas no lcov, zero cobertura. É o maior risco desta feature.
  • [MÉDIO] Complexidade visual sem cobertura: Contém lógica de exibição condicional, estado de carregamento e integração com CartController. Qualquer regressão aqui afeta o checkout diretamente.
  • [POSITIVO] Separação em widget dedicado: Embora sem testes, o código está isolado em um widget próprio (não acoplado ao check_sell_screen).

4.4 cart_controller.dart (MODIFICADO, +75 linhas, 23,01% → novo teste dedicado)

O que faz: Adiciona addRecommendedProducts(), removeRecommendedItem() e applyRecommendedItemDiscount() para gerenciar itens recomendados no carrinho.

Achados:

  • [BAIXO] Force unwrap em cart.recommendedItems!: Os métodos removeRecommendedItem() e applyRecommendedItemDiscount() fazem force unwrap na lista. Se recommendedItems for null, quebra em runtime. A UI atual protege, mas é frágil para chamadas futuras.
  • [POSITIVO] Teste dedicado criado: Arquivo cart_controller_add_recommended_products_test.dart com 352 linhas cobrindo o novo fluxo.
  • [POSITIVO] Cobertura total subiu de 5% para 23%: Ganho expressivo no controller mais crítico do app.

4.5 cart_dto.dart (MODIFICADO, +22 linhas, 75,93% cobertura)

O que faz: Adiciona campo recommendedItems (List<UpsellRecommendationItem>) com serialização JSON (cartItemRecommendations) e método toJsonPut() omitindo o campo.

Achados:

  • [BAIXO] toJsonPut() omite recommendedItems: Confirmado que toJsonPut() é usado apenas em deleteCartDTO(). Semanticamente correto (DELETE não precisa de recomendados). Risco baixo.
  • [POSITIVO] Sólida cobertura: 75,93%, uma das mais altas do projeto.
  • [INFO] JSON key cartItemRecommendations: Renomeada de recommendedItems para cartItemRecommendations. Compatibilidade com API precisa ser validada em ambiente de homologação.

4.6 upsell_recommendation_card.dart (NOVO, 240 linhas)

O que faz: Card de produto com imagem, preço, nome e botão de seleção de tamanho.

Achados:

  • [POSITIVO] Widget test abrangente: 322 linhas de widget test cobrindo estados normal, selecionado, e interação.
  • [POSITIVO] Separação clara: Card puramente apresentacional, sem lógica de negócio.

4.7 upsell_size_selection_modal.dart (NOVO, 136 linhas)

O que faz: BottomSheet de seleção de grade de tamanhos.

Achados:

  • [BAIXO] Zero cobertura unitária: Não há teste específico para o modal isoladamente. É exercitado apenas indiretamente pelo widget test do card.
  • [POSITIVO] Widget test de integração: 272 linhas de teste cobrindo o fluxo completo.

4.8 upsell_bottom_bar.dart (NOVO, 244 linhas, 4,29% cobertura)

O que faz: Bottom bar com 4 estados via máquina de estados: loading (shimmer), error, empty e withItems. Contém 5 widgets privados.

Achados:

  • [MÉDIO] Cobertura muito baixa: Apenas 4,29% (3 de 70 linhas instrumentadas cobertas). Os estados shimmer, erro e vazio não são testados isoladamente.
  • [POSITIVO] Arquitetura de máquina de estados: Padrão limpo com widgets privados _UpsellBottomBarSkeleton, _UpsellBottomBarError, _UpsellBottomBarEmpty e _UpsellBottomBarWithItems.
  • [POSITIVO] Widget test existe: 107 linhas, mas cobre apenas o fluxo principal.

4.9 new_sell_stepper.dart (MODIFICADO, +84 linhas, 2,86% cobertura)

O que faz: Adiciona a etapa recomendar no stepper de nova venda.

Achados:

  • [BAIXO] Cobertura quase zero: 2,86%. O stepper existente já não tinha testes e a modificação manteve o padrão.
  • [INFO] Mudança puramente declarativa: Adiciona um novo Step na lista, sem lógica condicional complexa.

4.10 upsell_navigation_helper.dart (NOVO, 43 linhas, 0% cobertura)

O que faz: Helper com método navigateToUpsell que navega para a tela de upsell usando pushNamed, com try/catch para rota não registrada.

Achados:

  • [MÉDIO] Catch silencioso: Se a rota não estiver registrada, o erro é suprimido sem feedback ao usuário.
  • [MÉDIO] Zero cobertura: 12 linhas instrumentadas, nenhuma coberta.

4.11 zz_error_state.dart (NOVO, 59 linhas, 0% cobertura)

O que faz: Widget de estado de erro reutilizável com ícone, mensagem e botão de retry.

Achados:

  • [BAIXO] Sem testes: Widget puramente visual, sem lógica de negócio. Risco menor.

4.12 firebase_remote_config_service.dart (MODIFICADO, +14 linhas, 0% cobertura)

O que faz: Adiciona chave upsell_recommendation ao Remote Config.

Achados:

  • [BAIXO] 0% de cobertura: Arquivo de serviço externo (Firebase), difícil de testar sem mocking. O valor real é validado em QA.

4.13 Arquivos de rota/stepper/páginas (MODIFICADOS)

  • app_pages.dart (+9 linhas): Nova rota /upsell_recommendation (0% cobertura)
  • app_routes.dart (+2 linhas): Constante AppRoutes.upsellRecommendation
  • stepp_sell.dart (+21 linhas): Novo enum SteppSell.recomendar
  • stepper.dart (+12 linhas, 55,56% cobertura): Adiciona step no modelo
  • pages_titles.dart (+5 linhas): Título da tela para recomendar
  • open_sales_screen.dart (+4 linhas): Botão de upsell em vendas abertas
  • 6 screens de customer/product: Apenas +2 linhas cada (passagem de parâmetro)

5. Mapa de Riscos

IDRiscoSeveridadeArquivoLinhasMitigação
R1check_sell_recommended sem testes (359 LOC)ALTOcheck_sell_recommended.dart118 inst.Criar widget tests para estados loading/dados/vazio
R2Race condition em troca de carrinho elegívelMÉDIOupsell_recommendation_controller.dart92 inst.Resetar hasLoadedRecommendations em _onCartChanged
R3Catch silencioso em navigateToUpsellMÉDIOupsell_navigation_helper.dart12 inst.Adicionar snackbar de erro
R4_resetState não limpa selectedSizeMÉDIOupsell_recommendation_controller.dart~5Resetar selectedSize no _resetState
R5selectedSize público e mutávelMÉDIOupsell_recommendation_controller.dart1Tornar privado com getter
R6upsell_navigation_helper 0% coberturaMÉDIOupsell_navigation_helper.dart12 inst.Teste unitário simples
R7upsell_recommendation_screen 0% coberturaMÉDIOupsell_recommendation_screen.dart51 inst.Widget test de tela
R8Force unwrap em recommendedItems!BAIXOcart_controller.dart2Guard clause if (cart.recommendedItems == null)
R9toJsonPut() omite recommendedItemsBAIXOcart_dto.dart1Confirmado correto (só usado em DELETE)
R10Stepper sem cobertura adicionalBAIXOnew_sell_stepper.dart84 mod.Cobertura consistente com o existente
R11Firebase Remote Config 0% coberturaBAIXOfirebase_remote_config_service.dart14 mod.Teste de integração separado
R12zz_error_state.dart sem testesBAIXOzz_error_state.dart59Widget puramente visual
R13app_pages.dart 0% coberturaBAIXOapp_pages.dart9 mod.Teste de rota

6. Diagramas

6.1 Diagrama de Sequência — Fluxo de Upsell

6.2 Máquina de Estados — UpsellRecommendationController

6.3 Máquina de Estados — UpsellBottomBar

6.4 Árvore de Decisão — Elegibilidade para Upsell

6.5 Stepper — Antes vs Depois

6.6 Diagrama de Arquitetura — Impacto do Upsell

6.7 Matriz de Impacto do Fluxo


7. Análise de Regressão

7.1 Impacto em Arquivos Existentes

ArquivoTipo de MudançaRisco de Regressão
cart_controller.dart+75 linhas (add/remove/discount recommended)MÉDIO — Fluxo crítico de carrinho
cart_dto.dart+22 linhas (novo campo + serialização)BAIXO — Testado (75,93%)
get_it.dart+2 linhas (registro provider)BAIXO — Injeção de dependência
new_sell_stepper.dart+84 linhas (novo step)BAIXO — Puramente aditivo
stepper.dart / stepp_sell.dart+12 / +21 linhasBAIXO — Apenas novo enum value
firebase_remote_config_service.dart+14 linhasBAIXO — Feature flag
6 telas de customer/product+2 linhas cadaMUITO BAIXO — Só passagem de parâmetro
check_sell_product_discount.dart-68 linhas refactor (ícones)BAIXO — Substituição cosmética
check_sell_screen.dart+8 linhasMUITO BAIXO — Import + condicional
open_sales_screen.dart+4 linhasMUITO BAIXO — Botão condicional
zz_empty_state_new.dart+32 linhasBAIXO — Factory novo
pubspec.yaml+1 linha (phosphor_flutter)MUITO BAIXO — Dep adicional

7.2 Compatibilidade de Ícones

O commit 73952f3c migrou parcialmente de flutter_phosphor_icons para phosphor_flutter. Ambos os pacotes coexistem no pubspec.yaml. ~80 usos do pacote antigo permanecem no restante do código. Mudança não breaking, apenas os arquivos tocados pelo diff foram migrados.


8. Qualidade do Código

8.1 Padrões e Arquitetura

AspectoAvaliação
Separação de responsabilidades✅ Controller, Provider, Screen, Widgets bem separados
Uso de GetX✅ Controller estende GetXController, tela estende GetView
Feature flag✅ Upsell controlado por Remote Config
Injeção de dependência✅ Provider registrado em get_it.dart
Tratamento de erros⚠️ Catch silencioso em 2 locais (R3, R2)
Imutabilidade⚠️ selectedSize público e mutável (R5)

8.2 Convenções do Projeto

ConvençãoSeguido?
Nomes de arquivo em snake_case
Classes em PascalCase
Constantes em lowerCamelCase
DefaultApiController como base✅ (UpsellRecommendationController)
DefaultModelInterface nos DTOs✅ (CartDTO, UpsellRecommendationItem)
Testes em test/ espelhando lib/
Widgets privados com _✅ (bottom bar)

9. Métricas de Esforço

MétricaValor
Linhas totais no diff4.997
Linhas de código novo (lib/)~1.817
Linhas de teste novo (test/)~2.270
Proporção teste/código1,25:1
Arquivos novos17
Arquivos modificados25
Commits6

10. Resumo de Riscos

SeveridadeQuantidadeIDs
🔴 ALTO1R1
🟡 MÉDIO5R2, R3, R4, R5, R6
🟢 BAIXO7R7, R8, R9, R10, R11, R12, R13

Risco ALTO (R1): check_sell_recommended.dart com 359 linhas e 0% de cobertura. Este widget é o ponto de integração entre o fluxo de upsell e o checkout.


11. Recomendações Pré-Merge

Bloqueantes

  1. Criar widget tests para check_sell_recommended.dart — 359 LOC, 0% cobertura, integração direta com checkout (R1).

Altamente Recomendados

  1. Corrigir race condition em _onCartChanged — Resetar hasLoadedRecommendations quando o carrinho mudar (R2).
  2. Adicionar feedback visual no upsell_navigation_helper — Substituir catch silencioso por snackbar ou log visível (R3).
  3. Resetar selectedSize em _resetState — Evitar estado inconsistente entre carregamentos (R4).
  4. Widget test para upsell_recommendation_screen — Garantir que a tela renderiza corretamente (R7).

Bom ter

  1. Proteger force unwrap em cart_controller.dart — Guard clause para recommendedItems (R8).
  2. Teste unitário para upsell_navigation_helper — Verificar navegação com rota válida/inválida (R6).

12. Conclusão

CritérioStatus
Cobertura de testes⚠️ Adequada para código novo (73,91% no controller principal)
Gap crítico de cobertura🔴 check_sell_recommended.dart (0%)
Risco de regressão🟡 Médio — controlado por testes nos controllers
ArquiteturaBem estruturada, segue padrões do projeto
DocumentaçãoCódigo legível, nomes descritivos
Feature flagProtegido por Remote Config

Rating Final

APROVADO COM AJUSTES (não bloqueante)

O código é bem estruturado e segue as convenções do projeto. A cobertura de testes no controller principal (73,91%) e a proporção teste/código (1,25:1) são pontos positivos.

O principal ponto de atenção é check_sell_recommended.dart — o maior arquivo novo (359 linhas) está sem cobertura de testes e representa o ponto de integração com o checkout. Recomenda-se fortemente a criação de widget tests antes do merge.

Os demais riscos identificados (race condition, catch silencioso, mutabilidade) são de severidade média e podem ser endereçados em um PR de follow-up.


Relatório gerado em 2026-06-10 por opencode

Artefatos salvos em:

  • /Users/alt-zz/projetos/arezzo/USs/194760/review/1-cover-feat_194760_upsell_v1
  • /Users/alt-zz/projetos/arezzo/USs/194760/review/1-cover-develop
  • /Users/alt-zz/projetos/arezzo/USs/194760/review/cover-feat-194760_upsell_v1
  • /Users/alt-zz/projetos/arezzo/USs/194760/review/cover-develop
  • /var/folders/1h/9gkphkrj6_l39x_m74stwpv80000gq/T/opencode/194760_review/full.diff
  • /var/folders/1h/9gkphkrj6_l39x_m74stwpv80000gq/T/opencode/194760_review/lcov-feat.info
  • /var/folders/1h/9gkphkrj6_l39x_m74stwpv80000gq/T/opencode/194760_review/lcov-develop.info