Documento de Requisitos — Criar tela de recomendação upsell
Tarefa: #193223
Contexto: Feature: Upsell
Visão Geral
O que é
Tela "Recomendar" no fluxo de venda do zzapp, exibida entre Cliente e Conferir quando as três condições de upsell forem atendidas (feature flag + loja habilitada + carrinho estoque loja), avaliadas pelo UpsellRecommendationController.isActive.
Como funciona
- Entra na tela → chama API externa (CPF, StoreId, itens do cart) → retorna até 3 produtos com dados de estoque da loja
- Cards exibem chips
NumerationListTextcom indicadores: bolinha vermelha (estoque < 3) e cinza (sem estoque) - Toque no trailing (plus_circle) → modal (
AddProductBottomSheetadaptado) → seleciona tamanho → card mostra chip único + check verde - ADICIONAR RECOMENDAÇÃO → itens selecionados viram
CartItemDTO→ salvos emcartDTO.recommendedItems(lista separada) → navega para Conferir - NÃO QUERO RECOMENDAR → limpa seleções → navega para Conferir
Escopo desta task
| Inclui | Não inclui |
|---|---|
Tela StatelessWidget + AppGetBuilder | API de recomendação externa (outra task) |
UpsellRecommendationCard (stand-alone) | POST cart com recommendedItems (outra task) |
UpsellRecommendationController (fetch, cache, retry) | Seção recomendações no Conferir (outra task) |
UpsellRecommendationItem (model de tela) | Navegação step Recomendar (outra task) |
CartDTO.recommendedItems + CartController.addRecommendedProducts() | Feature flags (task-07) |
| Estados: loading, cards, vazio, erro | Controller base isActive (task-08) |
Cache e preservação
Estado mantido no controller (lazySingleton) enquanto activeCartDTO != null. Retorno à tela na mesma venda restaura cache sem re-fetch da API. Reset automático ao finalizar venda conforme ADR-010.
Papéis Envolvidos
- Vendedor(a) — visualiza a tela de recomendação, seleciona tamanhos dos itens recomendados, decide quais recomendações adicionar ao carrinho ou pular o fluxo.
Requisitos Funcionais
RF-01 — Chamada à API de recomendação ao entrar na tela
História de Usuário:
Como sistema, devo chamar a API externa de recomendação ao entrar na tela de upsell, se ainda não tiver sido executada para a venda ativa.
Critérios de Aceitação:
-
QUANDO a tela de recomendação for exibida E a chamada à API de recomendação ainda não tiver sido executada para a venda ativa ENTÃO o sistema DEVE disparar a chamada com os parâmetros: CPF do cliente, StoreId e itens do carrinho (SKU, size).
-
QUANDO a chamada à API estiver em andamento ENTÃO a tela DEVE exibir skeleton de carregamento.
-
QUANDO a API retornar resposta com sucesso ENTÃO a tela DEVE substituir o skeleton pela lista de cards ou empty state, conforme o resultado.
Casos de Borda:
- API já chamada anteriormente para esta venda: não repetir chamada, usar dados em cache no controller.
RF-02 — Exibição da lista de itens recomendados
História de Usuário:
Como vendedor(a), quero visualizar cards dos produtos recomendados para decidir quais adicionar ao carrinho.
Critérios de Aceitação:
-
QUANDO a API retornar itens recomendados ENTÃO a tela DEVE exibir cada item como um card usando o widget UpsellRecommendationCard.
-
QUANDO a API retornar múltiplos itens ENTÃO a tela DEVE exibi-los em ListView vertical, um abaixo do outro.
-
QUANDO a API retornar sucesso com lista vazia (0 itens) ENTÃO a tela DEVE exibir o empty state "Nenhuma recomendação encontrada" centralizado, utilizando o padrão ZzEmptyState/NoContentEmptyState do projeto.
-
QUANDO a API retornar erro ENTÃO a tela DEVE exibir estado de erro centralizado via ZzEmptyState com: title "Ocorreu um problema", subtitle "Tivemos um problema ao carregar os dados.\nTente atualizar a página", image "assets/images/no_content.svg" (ou similar do projeto), e botão ZzFilledButton "TENTE NOVAMENTE" que chama controller.retry().
Casos de Borda:
- API retorna mais de 3 itens: a API externa é responsável por limitar a no máximo 3.
- Item com imagem nula: ZzUrlImage trata fallback internamente.
- Botão retry no estado de erro: re-dispara fetchRecommendations() e volta ao skeleton.
RF-03 — Card de item recomendado — estado não selecionado
História de Usuário:
Como vendedor(a), quero visualizar todos os tamanhos disponíveis em estoque na loja para um item recomendado não selecionado.
Critérios de Aceitação:
-
QUANDO o item NÃO estiver selecionado ENTÃO o card DEVE exibir chips NumerationListText com todos os tamanhos presentes em sizesStockStore, mapeados com lowQuantity: quantity < 3 e noStock: quantity == 0.
-
QUANDO o item NÃO estiver selecionado ENTÃO o indicador trailing DEVE ser o ícone PhosphorIcons.plus_circle na cor primaryRegular.
-
QUANDO o item NÃO estiver selecionado E a vendedora tocar no indicador trailing (plus_circle) ENTÃO o sistema DEVE abrir a modal de seleção de tamanho.
Casos de Borda:
- Item sem nenhum tamanho com estoque na loja: todos os chips renderizados como noStock (cinza) com badge "Tamanho indisponível".
- Tamanhos com quantidade < 3: exibir indicador de baixa quantidade (bolinha vermelha no canto superior direito do chip).
- Toque no corpo do card (fora do trailing): nenhuma ação. Apenas o ícone trailing dispara a modal.
RF-04 — Card de item recomendado — estado selecionado
História de Usuário:
Como vendedor(a), quero visualizar o tamanho selecionado de um item recomendado e poder desselecioná-lo.
Critérios de Aceitação:
-
QUANDO o item estiver selecionado ENTÃO o card DEVE exibir um único chip NumerationListText com o tamanho selecionado (lowQuantity: false, noStock: false), substituindo a lista completa de chips.
-
QUANDO o item estiver selecionado ENTÃO o indicador trailing DEVE ser o ícone PhosphorIcons.check_circle_fill na cor primaryRegular.
-
QUANDO o item estiver selecionado E a vendedora tocar no indicador trailing (check_circle_fill) ENTÃO o sistema DEVE desselecionar o item (selectedSize = null, volta ao estado não selecionado com todos os chips restaurados).
Casos de Borda:
- Desselecionar item que era o único selecionado: botão ADICIONAR RECOMENDAÇÃO deve exibir modal de alerta ao ser clicado.
- Toque no corpo do card (fora do trailing): nenhuma ação. Apenas o ícone trailing desseleciona.
RF-05 — Modal de seleção de tamanho
História de Usuário:
Como vendedor(a), quero selecionar um tamanho específico para o item recomendado através de uma modal.
Critérios de Aceitação:
-
QUANDO a vendedora tocar no indicador trailing (plus_circle) de um card não selecionado ENTÃO o sistema DEVE abrir modal via GetxBottomSheet.showBottomSheet seguindo o padrão visual do AddProductBottomSheet (título "Selecione o tamanho", chips circulares 45x45, seção "Estoque da loja").
-
QUANDO a modal estiver aberta ENTÃO o sistema DEVE permitir clique apenas em tamanhos com estoque > 0 na loja.
-
QUANDO a vendedora selecionar um tamanho E clicar no botão "ADICIONAR PRODUTO" ENTÃO o sistema DEVE fechar a modal, marcar o item como selecionado com o tamanho escolhido e atualizar o card para estado selecionado.
-
QUANDO a vendedora fechar a modal sem clicar em "ADICIONAR PRODUTO" ENTÃO o sistema NÃO DEVE alterar o estado do card que abriu a modal.
Casos de Borda:
- Botão ADICIONAR PRODUTO permanece desabilitado até um tamanho ser selecionado.
- Produto com apenas 1 tamanho em estoque: modal abre normalmente exibindo o único chip disponível. Não há auto-seleção — vendedora precisa tocar no chip e confirmar.
- Modal usa AppGetBuilder ou lógica local (UpsellRecommendationController) para reatividade da seleção.
RF-06 — Skeleton de carregamento
História de Usuário:
Como vendedor(a), quero ver um indicador de carregamento enquanto as recomendações são buscadas.
Critérios de Aceitação:
-
QUANDO a tela de recomendação estiver carregando os dados da API ENTÃO o sistema DEVE exibir skeleton de carregamento no lugar da lista.
-
QUANDO os dados forem recebidos com sucesso (lista ou vazio) ENTÃO o sistema DEVE substituir o skeleton pela lista de cards ou empty state.
-
QUANDO a API retornar erro ENTÃO o sistema DEVE substituir o skeleton pelo estado de erro centralizado (título, descrição, botão TENTE NOVAMENTE).
Casos de Borda:
- Skeleton deve ter aparência consistente com o layout dos cards (altura, espaçamento) para evitar salto de layout.
- Reutilizar padrão de skeleton existente no projeto (ex: GridViewShimmer adaptado para lista).
- Botão TENTE NOVAMENTE no estado de erro: ao ser pressionado, retorna ao skeleton e re-dispara fetchRecommendations().
RF-07 — Botão ADICIONAR RECOMENDAÇÃO
História de Usuário:
Como vendedor(a), quero confirmar as recomendações selecionadas e salvá-las no carrinho.
Critérios de Aceitação:
-
QUANDO o skeleton for finalizado e a lista de itens estiver visível ENTÃO o botão ADICIONAR RECOMENDAÇÃO DEVE iniciar habilitado.
-
QUANDO a vendedora clicar no botão SEM nenhum item com selectedSize != null ENTÃO o sistema DEVE exibir modal de alerta informando que é necessário selecionar ao menos um tamanho de um produto.
-
QUANDO a vendedora clicar no botão COM ao menos um item no estado selecionado ENTÃO o sistema DEVE: filtrar itens com isSelected == true, converter cada um para CartItemDTO (sku, name, image, size: selectedSize, quantity: 1, price, fullPrice, discountType: none, discountValue: 0), salvar a lista em cartDTO.recommendedItems via CartController.addRecommendedProducts(), e navegar para a fase de conferir produtos.
-
QUANDO a tela estiver em estado de erro (API falhou) ENTÃO o botão ADICIONAR RECOMENDAÇÃO DEVE permanecer desabilitado.
-
QUANDO a vendedora retornar à tela de recomendação após já ter salvo recomendações ENTÃO o botão DEVE permitir alterar seleções (adicionar/remover/trocar tamanho) e salvar novamente (sobrescreve recommendedItems anteriores).
Casos de Borda:
- recommendedItems não compõe itemsTotal/total do cart — são itens sugestivos, não itens do carrinho.
- Botão desabilitado em estado de erro: reabilitado quando fetchRecommendations() retornar com sucesso.
RF-08 — Botão NÃO QUERO RECOMENDAR
História de Usuário:
Como vendedor(a), quero pular o fluxo de recomendação e ir direto para conferir o pedido.
Critérios de Aceitação:
-
QUANDO o skeleton for finalizado ENTÃO o botão NÃO QUERO RECOMENDAR DEVE iniciar habilitado e permanecer habilitado em todos os estados (lista, vazio, erro).
-
QUANDO a vendedora clicar no botão ENTÃO o sistema DEVE alterar o estado de todos os cards para não selecionado (selectedSize = null).
-
QUANDO a vendedora clicar no botão ENTÃO o sistema DEVE navegar para a fase de conferir pedido.
Casos de Borda:
- Navegação deve ocorrer mesmo se houver itens previamente selecionados — estes são limpos antes de avançar.
- Botão disponível mesmo durante estado de erro: vendedora pode desistir do fluxo upsell a qualquer momento.
RF-09 — Model UpsellRecommendationItem
História de Usuário:
Como desenvolvedor, preciso de uma model dedicada para os itens de recomendação upsell que gerencia o estado de seleção e alimenta os chips NumerationListText.
Critérios de Aceitação:
-
A model DEVE conter os campos de produto: id, sku, name, image, price, fullPrice, sizesStockStore, hasStock.
-
A model DEVE conter campo mutável selectedSize (String?) para estado de seleção da tela.
-
A model DEVE expor getter isSelected (retorna selectedSize != null).
-
A model DEVE expor getter hasMarkdown (retorna fullPrice != null && fullPrice! > price).
-
A model DEVE expor método
List<Widget> buildSizeChips({String? highlightSize})que retorna lista de NumerationListText para todos os sizesStockStore, com lowQuantity: quantity < 3 e noStock: quantity == 0. Se highlightSize for informado, apenas o chip correspondente é retornado. -
A model DEVE possuir método toProduct() que retorna Product preenchendo os campos consumidos por padrões existentes (imagem, nome, SKU, preço, markdown, hasStock).
Casos de Borda:
- image pode ser nulo: toProduct() deve tratar com images vazio e ZzUrlImage usa fallback interno.
- fullPrice nulo: hasMarkdown retorna false, preço exibido normalmente.
- sizesStockStore vazio: buildSizeChips retorna lista vazia; card exibe badge "Tamanho indisponível".
RF-10 — Card UpsellRecommendationCard (stand-alone)
História de Usuário:
Como desenvolvedor, preciso de um card dedicado para itens de recomendação upsell que renderiza tamanhos como chips NumerationListText com indicadores de estoque.
Critérios de Aceitação:
-
O card DEVE ser um StatelessWidget com layout similar ao NewCardProductList: Container com Row (ZzUrlImage 60x60 + Column de detalhes + trailing).
-
O card DEVE receber um UpsellRecommendationItem e callbacks onToggle (selecionar/desselecionar) e onSelectSize (abrir modal).
-
QUANDO o item estiver selecionado ENTÃO a seção de tamanhos DEVE exibir um único chip NumerationListText com o size selecionado (lowQuantity: false, noStock: false).
-
QUANDO o item NÃO estiver selecionado ENTÃO a seção de tamanhos DEVE exibir Wrap de chips NumerationListText para todos os sizesStockStore, mapeados conforme item.buildSizeChips().
-
O trailing DEVE ser um GestureDetector com Icon(PhosphorIcons.plus_circle, color: primaryRegular, size: 20) quando não selecionado e Icon(PhosphorIcons.check_circle_fill, color: primaryRegular, size: 20) quando selecionado.
-
QUANDO o item estiver selecionado E o usuário tocar no trailing ENTÃO o sistema DEVE chamar onToggle() para desselecionar.
-
QUANDO o item NÃO estiver selecionado E o usuário tocar no trailing ENTÃO o sistema DEVE chamar onSelectSize() para abrir a modal.
-
QUANDO hasStock == false ENTÃO o badge "Tamanho indisponível" DEVE ser renderizado abaixo dos detalhes.
-
O corpo do card (fora do trailing) NÃO DEVE ter ação de toque. Apenas o trailing é interativo.
Casos de Borda:
- Item com sizesStockStore vazio: seção de tamanhos vazia, badge "Tamanho indisponível" visível.
- Desselecionar item que era o único selecionado: botão ADICIONAR RECOMENDAÇÃO deve ficar sem efeito (exibe modal de alerta no clique).
RF-11 — Integração com UpsellRecommendationController
História de Usuário:
Como desenvolvedor, preciso estender o UpsellRecommendationController (criado na task-08) com o estado da tela de recomendação.
Critérios de Aceitação:
-
O controller DEVE expor
RxList<UpsellRecommendationItem> recommendedItemspara a lista de itens. -
O controller DEVE expor RxBool isLoading e RxBool hasError para controle de estado da tela.
-
O controller DEVE expor RxBool hasLoadedRecommendations que indica se já carregou dados da API para a venda atual.
-
O controller DEVE possuir método fetchRecommendations() que: se hasLoadedRecommendations == false, chama a API externa; se true, apenas exibe o cache (sem re-fetch). Após sucesso, seta hasLoadedRecommendations = true.
-
O controller DEVE possuir método retry() que redefine hasError = false, isLoading = true e re-dispara fetchRecommendations().
-
O controller DEVE possuir método toggleItemSelection(UpsellRecommendationItem item, String? size) que: se size != null, define selectedSize = size; se size == null, define selectedSize = null (desseleciona).
-
O controller DEVE possuir método skipAllRecommendations() que limpa selectedSize de todos os itens no recommendedItems.
-
O controller DEVE possuir getter hasAnySelected que retorna true se ao menos um item tem isSelected == true.
-
O controller DEVE subscrever-se a OpenSalesController.activeCartDTO. QUANDO activeCartDTO emitir null ENTÃO o controller DEVE resetar: recommendedItems.clear(), hasLoadedRecommendations = false, hasError = false, isLoading = false, isActive = false.
Casos de Borda:
- fetchRecommendations() com API inexistente: retornar hasError = true (fallback para estado de erro).
- retry() chamado durante carregamento: ignorar (isLoading já true).
- Ao sair e retornar para a tela na mesma venda: estado preservado (cache ativo), sem re-fetch.
RF-12 — Estrutura da tela (StatelessWidget + AppGetBuilder)
História de Usuário:
Como desenvolvedor, a tela deve seguir os padrões arquiteturais do zzapp: StatelessWidget com AppGetBuilder.
Critérios de Aceitação:
-
A tela DEVE ser um StatelessWidget.
-
A tela DEVE acessar o controller via
getIt.get<UpsellRecommendationController>(). -
A tela DEVE embrulhar o corpo em
AppGetBuilder(init: controller, builder: () { ... }). -
A tela DEVE possuir ZzAppBar com título da página e NewSellHomeButton.getActions().
-
A tela DEVE possuir bottomNavigationBar com os dois botões: ADICIONAR RECOMENDAÇÃO (ZzFilledButton, expanded) e NÃO QUERO RECOMENDAR (texto secundário).
-
QUANDO o controller.hasError == true ENTÃO o corpo DEVE renderizar o estado de erro centralizado (ZzEmptyState com title, subtitle, botão TENTE NOVAMENTE).
-
QUANDO o controller.isLoading == true ENTÃO o corpo DEVE renderizar o skeleton de carregamento.
-
QUANDO a tela for construída e controller.hasLoadedRecommendations == false ENTÃO o sistema DEVE disparar fetchRecommendations() no initState do AppGetBuilder.
-
QUANDO a tela for construída e controller.hasLoadedRecommendations == true ENTÃO o sistema DEVE exibir os itens em cache sem disparar fetch.
Casos de Borda:
- AppGetBuilder aninhado: se a tela pai (NewSellScreen) também usa AppGetBuilder, o rebuild do pai recria o AppGetBuilder interno — comportamento esperado. O cache no controller (lazySingleton) garante que o estado sobrevive ao rebuild.
- Bottom bar visível durante estado de erro: ADICIONAR RECOMENDAÇÃO desabilitado, NÃO QUERO RECOMENDAR habilitado.
- Bottom bar visível durante skeleton: ambos os botões visíveis mas sem ação até carregamento concluir.
- Retorno à tela após salvar recomendações: tela restaura estado do cache (itens + seleções preservadas), vendedora pode alterar seleções e salvar novamente.
RF-13 — Campo recommendedItems no CartDTO
História de Usuário:
Como desenvolvedor, preciso que o CartDTO suporte uma lista separada de itens recomendados que não compõem o total do carrinho.
Critérios de Aceitação:
-
CartDTO DEVE possuir campo
List<CartItemDTO>? recommendedItems(nullable, default null). -
recommendedItemsNÃO DEVE compor os cálculos deitemsTotalnemtotaldo cart. -
O campo DEVE ser serializado em
toJson()e deserializado emCartDTO.fromJson(). -
O campo DEVE ser suportado no
copyWith()com o padrãoValue()/Nil()existente. -
A extension
MutableCartDTODEVE possuir métodoaddRecommendedItems(List<CartItemDTO> items)que substituirecommendedItemspela nova lista.
Casos de Borda:
- cart vazio (cartItems.isEmpty) com recommendedItems preenchido: permitido — recommendedItems são independentes.
- recommendedItems vazio ou null após salvar:
copyWith(recommendedItems: Value([]))para lista vazia;copyWith(recommendedItems: const Nil())para null. - Apenas itens com selectedSize != null viram CartItemDTO: filtro é responsabilidade do caller (addRecommendedProducts), não do CartDTO.
RF-14 — CartController.addRecommendedProducts()
História de Usuário:
Como desenvolvedor, preciso de um método no CartController que converta UpsellRecommendationItem selecionados em CartItemDTO e os salve no cartDTO.recommendedItems.
Critérios de Aceitação:
-
O CartController DEVE possuir método
addRecommendedProducts(List<UpsellRecommendationItem> items). -
O método DEVE filtrar apenas itens com
isSelected == true. -
Para cada item filtrado, o método DEVE criar um CartItemDTO com: productId = item.id, name = item.name, sku = item.sku, image = item.image, quantity = 1, size = item.selectedSize, unitPrice = item.fullPrice ?? item.price, unitPriceWithDiscount = item.price, discountType = CartItemDTODiscountType.none, discountValue = 0, brand = item.brand, hasStock = item.hasStock.
-
O método DEVE usar o padrão
changeWith()(síncrono, sem chamada de API) para mutar o cartDTO, chamandocart.addRecommendedItems(cartItemDTOs). -
Itens sem selectedSize (isSelected == false) NÃO DEVEM ser incluídos na lista.
Casos de Borda:
- Lista de entrada vazia ou sem itens selecionados: recommendedItems é setado como lista vazia ([]).
- Chamadas subsequentes: sobrescreve recommendedItems anteriores (comportamento de replace, não append).
- Se cartDTO for null (sem venda ativa): método não deve executar; logar warning.
RF-15 — Preservação de estado da tela na mesma venda
História de Usuário:
Como vendedor(a), quero que minhas seleções na tela de recomendação sejam preservadas enquanto o atendimento estiver em aberto, para poder revisar e ajustar as recomendações antes de finalizar.
Critérios de Aceitação:
-
QUANDO a vendedora navegar para fora da tela de recomendação (ex: Conferir) E retornar na mesma venda ENTÃO o sistema DEVE exibir a lista de recomendações e seleções exatamente como estavam, sem re-fetch da API.
-
QUANDO a vendedora retornar à tela ENTÃO ela DEVE poder alterar seleções: adicionar novos tamanhos, trocar tamanho, desselecionar itens, ou selecionar itens antes não selecionados.
-
QUANDO a vendedora clicar em ADICIONAR RECOMENDAÇÃO novamente ENTÃO o sistema DEVE sobrescrever recommendedItems no CartDTO com a nova lista de seleções.
-
QUANDO o atendimento for finalizado (activeCartDTO → null) ENTÃO o sistema DEVE limpar todo o estado: recommendedItems.clear(), hasLoadedRecommendations = false, hasError = false, isLoading = false, isActive = false.
-
QUANDO uma nova venda for iniciada ENTÃO o controller DEVE estar limpo (resetado) e fetchRecommendations() DEVE ser disparado novamente ao entrar na tela.
Casos de Borda:
- Vendedora seleciona itens → ADICIONAR RECOMENDAÇÃO → Conferir → volta → desseleciona tudo → ADICIONAR RECOMENDAÇÃO: recommendedItems fica vazio ([]), permitido.
- Vendedora inicia venda → vai para Recomendação → volta para Produtos → adiciona mais produtos → volta para Recomendação: estado preservado, sem re-fetch.
- Vendedora clica no botão Home (NewSellHomeButton) e inicia nova venda: activeCartDTO → null → controller reseta → nova tela faz fetch limpo.
Dependências
| Dependência | Descrição | Status |
|---|---|---|
| Task 08 — Stepper condicional | Criação do UpsellRecommendationController (lazySingleton, GetxController, isActive, auto-reset) e registro em get_it.dart. Sem esta task, o controller não existe no escopo de DI. | Pendente |
| Task 07 — Feature flags upsell | Registro das chaves enableUpsellRecommendation e enableUpsellRecommendationByCodeStore no FirebaseRemoteConfigKeysEnum e FirebaseRemoteConfigServiceDictionary. Necessário para o controller avaliar isActive. | Pendente |
| API de recomendação externa | Endpoint que recebe CPF do cliente, StoreId e itens do carrinho (SKU, size) e retorna lista de produtos recomendados (formato similar a busca de produtos, máximo 3 itens). Desenvolvida em outra task. | Pendente |
| API de persistência (POST cart) | Alteração do endpoint POST cart para receber propriedade opcional recommendedItems no body, contendo os itens com tamanhos selecionados. Desenvolvida em outra task. | Pendente |
| Tela de Conferir — seção de recomendações | Nova seção na tela de Conferir (CheckSellScreen) para exibir cartDTO.recommendedItems como cards similares aos cartItems, com suporte a desconto por item. Desenvolvida em outra task. | Pendente |
| Navegação para step Recomendar | Lógica de navegação do botão AVANÇAR entre Cliente → Recomendar (quando upsell ativo) → Conferir. Necessário para a tela ser alcançável no fluxo de venda. | Pendente |
Questões em Aberto
- Nenhuma questão em aberto identificada até o momento.