Code Review — Task 01 (196866) — [Front] Novo layout do card de produto
Branch: feat/196869_upsell_status_pedido
Base: develop (merge-base cc743fd8)
Data: 30/06/2026
Escopo: 15 arquivos (+1418 / −39 linhas), 2 commits vs develop
Resumo
A branch entrega o novo card de produto no detalhe do status do pedido (CartStatusProductCardNew), com DTO de apresentação, mapper de descontos alinhado aos RF 4–8, novos campos em ProductCartDetail e modelo CartItemRecommendation, além de bateria de testes focada no mapper e no widget. A arquitetura separa regra (ProductCardDiscountMapper) de UI (ProductCardDTO + card), o que facilita manutenção.
Após triage (grilling 30/06/2026), Issues 2–4 eram falsos positivos dado o contrato da API (discountOrigin substitui discountMarkdown no cart detail). Issue 1 corrigida com toApiString(). Lacunas de teste em DTO, mapper e widget resolvidas (Issues 7, 9, 10). Nits 13–14 corrigidos.
Testes executados na revisão inicial: 36 testes (4 arquivos).
Testes após correções: flutter test em 5 arquivos — 43 testes, todos passando.
Comparação de cobertura de testes (develop vs branch tarefa)
Método: flutter test --coverage com conjuntos comparáveis ao que existe em cada branch.
| Comando | Branch |
|---|---|
test/models/product/product_cart_detail_test.dart | develop |
Os 4 testes novos (cart_item_recommendation, product_cart_detail_new_fields, product_card_discount_mapper, cart_status_product_card_new) | feat/196869_upsell_status_pedido |
Cobertura de linhas (LCOV) apenas nos arquivos lib/ alterados ou introduzidos nesta entrega:
| Arquivo | develop | Branch tarefa | Δ |
|---|---|---|---|
lib/models/cart/cart_item_recommendation.dart | N/A (arquivo inexistente) | 89,7% (26/29) | — |
lib/models/product/product_cart_detail.dart | 90,9% (30/33) | 50,0% (20/40) | −40,9 pp* |
lib/screens/cart_status/cart_status_detail_screen.dart | 0,8% (2/263) | 0,8% (2/266) | ≈0 |
lib/screens/cart_status/models/product_card_dto.dart | N/A | 54,8% (17/31) | — |
lib/screens/cart_status/utils/product_card_discount_mapper.dart | N/A | 100,0% (32/32) | — |
lib/screens/cart_status/widgets/cart_status_product_card_new.dart | N/A | 96,2% (50/52) | — |
lib/shared/enum/cart_item_discount_origin.dart | N/A | 100,0% (4/4) | — |
lib/shared/enum/discount_type.dart | N/A | 60,0% (3/5) | — |
lib/shared/utils/logger_utils.dart | 37,5% (3/8) | 66,7% (4/6) | +29,2 pp** |
lib/theme_widgets/banner/zz_confirmation_banner.dart | N/A | 100,0% (12/12) | — |
| Total agregado do escopo | 11,5% (35/304) | 35,6% (170/477) | +24,1 pp |
* Na branch tarefa o modelo ganhou linhas novas (discountOrigin, fromRecommendation, hasEmployeeDiscount); o teste legado em develop não cobre esses caminhos — o teste dedicado product_cart_detail_new_fields_test.dart cobre o parse, mas não todas as linhas do toJson/roundtrip.
** Refatoração do logger alterou o número de linhas instrumentadas; a comparação direta é indicativa, não equivalente funcional.
Leitura: a entrega melhora cobertura no núcleo da feature (mapper 100%, card ~96%). product_card_dto e integração em cart_status_detail_screen ficam abaixo do ideal; em develop não havia testes de cart_status para este layout.
Achados
Issue 1 — Severity: bug
- Arquivo:
lib/models/product/product_cart_detail.dart:95 - Descrição:
toJsonusadiscountOrigin.name(ex.:markdown,manual), enquantoCartItemDiscountOriginParser.fromJsonespera literais da API (Markdown,Manual,Both). RoundtripfromJson → toJson → fromJsonperde a origem correta. - Sugestão: Serializar com os mesmos literais da API (ex.: método
toApiString()) e adicionar teste de roundtrip emproduct_cart_detail_new_fields_test.dart. - Status: resolved —
toApiString()emcart_item_discount_origin.dart; roundtrip emproduct_cart_detail_new_fields_test.dart
Issue 2 — Severity: bug
- Arquivo:
lib/screens/cart_status/utils/product_card_discount_mapper.dart:31 - Descrição: Para
discountOrigin == both, o tipo vira apenasmanualoufuncionarioe o valor exibido usadiscount/discountValue. O card antigo (cart_status_product_card.dart) ainda exibia Remarcação viadiscountMarkdownem linha separada. Pedidos com markdown + desconto manual podem mostrar valor incompleto. - Sugestão: Validar RF 5 com negócio; se ambos coexistem, compor exibição (ex.: considerar
discountMarkdownoufullPrice - pricealém do desconto manual). - Status: wontfix — RF-05.7 correto;
Bothexibemanual/funcionarioviadiscount/discountValue; linha Remarcação removida por RF-02.4
Issue 3 — Severity: bug
- Arquivo:
lib/screens/cart_status/utils/product_card_discount_mapper.dart:26 - Descrição: Com
discountOrigin == none(ou campo ausente) ediscountMarkdown > 0, o novo fluxo retornaDiscountType.noneeR$ 0,00. O card legado exibia remarcação nesse cenário — regressão para API/pedidos sem o campo novo. - Sugestão: Fallback no mapper: se
discountOrigin == noneediscountMarkdown > 0, tratar comosale(ou usardiscountMarkdownno cálculo). - Status: wontfix —
discountMarkdownnão vem da API de cart detail (cart-detail.json); markdown viadiscountOrigin+fullPrice/price
Issue 4 — Severity: bug
- Arquivo:
lib/screens/cart_status/utils/product_card_discount_mapper.dart:72 - Descrição: Para
manual/funcionario, sódiscount == 1ou2calculam valor; outros códigos deixamcalculatedValueem 0 sem tratamento explícito, podendo exibir rótulo de desconto com valor zerado mesmo comprice < fullPrice. - Sugestão: Tratar
discountinválido com fallback documentado ou teste que fixe o comportamento esperado. - Status: wontfix — API garante
discount∈ 2; R$ 0,00 é comportamento defensivo (RF-04.4)
Issue 5 — Severity: suggestion
- Arquivo:
lib/shared/utils/logger_utils.dart:4 - Descrição: Refatoração do logger (singleton, remoção de
printTime) não está ligada ao layout do card; aumenta superfície de revisão e risco colateral. - Sugestão: Mover para commit/PR separado ou reverter nesta branch.
- Status: wontfix — refatoração mantida (singleton, sem
printTime)
Issue 6 — Severity: suggestion
- Arquivo:
lib/screens/cart_status/widgets/cart_status_product_card.dart:99 - Descrição: Widget legado permanece após troca em
cart_status_detail_screen, com lógica de remarcação que o novo card não replica integralmente. - Sugestão: Deprecar/remover após validação de paridade ou documentar coexistência temporária.
- Status: wontfix — legado removido; único uso era
_ContentProducts; atualizar RF-01 nos docs (follow-up FU-04)
Issue 7 — Severity: suggestion
- Arquivo:
lib/screens/cart_status/models/product_card_dto.dart:31 - Descrição: Sem testes para
fromOrderItemefromRecommended, que montam labels,showRecommendationTag,valoreprecoFinal. - Sugestão: Criar
test/screens/cart_status/models/product_card_dto_test.dart. - Status: resolved —
test/screens/cart_status/models/product_card_dto_test.dartcriado
Issue 8 — Severity: suggestion
- Arquivo:
lib/theme_widgets/banner/zz_confirmation_banner.dart:6 - Descrição: Banner exportado no design system e usado no card, sem teste de widget dedicado (só indireto no card).
- Sugestão: Teste mínimo de renderização e ícone customizado.
- Status: follow-up — teste dedicado de
ZzConfirmationBanner(FU-01)
Issue 9 — Severity: suggestion
- Arquivo:
test/screens/cart_status/widgets/cart_status_product_card_new_test.dart:35 - Descrição: Cobertura limitada a
orderIteme presença de textos; não exercitarecommended, tipossale/funcionarionem valores monetários formatados. - Sugestão: Ampliar cenários e asserts em
- R$ …/ preços finais. - Status: resolved — cenários
recommended,sale,funcionarioe valores R$ adicionados
Issue 10 — Severity: suggestion
- Arquivo:
test/screens/cart_status/utils/product_card_discount_mapper_test.dart - Descrição: Falta caso explícito para
DiscountType.funcionarioemformatDiscountDisplaye para RF 7.5 (prioridadediscount > 0sobreprice < fullPriceem recomendados). - Sugestão: Adicionar os dois testes.
- Status: resolved — testes
funcionarioemformatDiscountDisplaye RF 7.5 adicionados
Issue 11 — Severity: suggestion
- Arquivo:
lib/screens/cart_status/cart_status_detail_screen.dart:544 - Descrição: Integração na lista de produtos (padding, uso do novo card) sem teste de widget/golden.
- Sugestão: Teste com
productCartDetailsmockados em_ContentProducts. - Status: follow-up — widget test de
_ContentProducts(FU-02)
Issue 12 — Severity: nit
- Arquivo:
lib/models/cart/cart_item_recommendation.dart:7 - Descrição: Campos mutáveis, em contraste com DTOs imutáveis (
ProductCardDTO). - Sugestão: Tornar
finalse o modelo for somente leitura após parse. - Status: wontfix — mutabilidade alinhada ao padrão do projeto (
ProductCartDetail)
Issue 13 — Severity: nit
- Arquivo:
lib/theme_widgets/banner/zz_confirmation_banner.dart:20 - Descrição:
paddinghorizontal0e comentário//12,emZZFontSize.xxxspodem afetar alinhamento visual. - Sugestão: Remover comentário e alinhar padding ao design.
- Status: resolved — padding horizontal 12; comentário removido
Issue 14 — Severity: nit
- Arquivo:
test/screens/cart_status/widgets/cart_status_product_card_new_test.dart:49 - Descrição:
find.text('1')é frágil (colisão com outros widgets); arquivo sem newline final. - Sugestão: Buscar quantidade no contexto da linha "Qtd."; adicionar newline EOF.
- Status: resolved —
quantityInQtdRow()no contexto de "Qtd: "; newline EOF
Issue 15 — Severity: nit
- Arquivo:
lib/screens/cart_status/widgets/cart_status_product_card_new.dart - Descrição: Factories
orderItem/recommendednão expõemKeyopcional para itens emListView. - Sugestão: Repassar
Key? keyao construtor. - Status: follow-up —
Key?nas factories (task 196865, FU-03)
Contagem de issues (pós-triage)
| Status | Quantidade | Issues |
|---|---|---|
| resolved | 6 | 1, 7, 9, 10, 13, 14 |
| wontfix | 6 | 2, 3, 4, 5, 6, 12 |
| follow-up | 3 | 8, 11, 15 |
Veredito: aprovável para merge. Correções obrigatórias aplicadas (Issue 1 + testes 7/9/10 + nits 13/14). Follow-ups: FU-01 (banner test), FU-02 (screen test), FU-03 (Key?), FU-04 (atualizar RF-01 nos docs).