Lista de varejosRetail listLista de puntos de venta
Os varejos sob a hierarquia do representante de vendas, num só lugar — com busca, criação de visita planejada e ad hoc. Cobre também o detalhe do varejo (cadastro completo, aberto pela visita) e a edição do cadastro — endereço, dias, entrega e outros campos que o mercado permite alterar. The retails under the sales rep's hierarchy, in one place — with search and planned and ad hoc visit creation. It also covers the retail detail (full profile, opened from the visit) and the editing of that profile — address, days, delivery and any other fields the market allows to change. Los puntos de venta bajo la jerarquía del representante de ventas, en un solo lugar — con búsqueda y creación de visita planificada y ad hoc. Cubre también el detalle del punto de venta (ficha completa, abierta desde la visita) y la edición de esa ficha — dirección, días, entrega y demás campos que el mercado permita cambiar.
O que é e para que serveWhat it is and what it's forQué es y para qué sirve
A feature de Varejos tem duas telas irmãs que compartilham o mesmo domínio de "varejo" (account/PDV): a lista — os varejos da zona do representante de vendas, de onde ele cria visitas — e o detalhe — o cadastro completo de um varejo, aberto de dentro de uma visita, onde alguns campos podem ser editados. Responde três perguntas do dia a dia: The Retails feature has two sibling screens sharing the same "retail" domain (account/PDV): the list — the retails in the sales rep's zone, from which they create visits — and the detail — a retail's full profile, opened from inside a visit, where some fields can be edited. It answers three everyday questions: La feature de Puntos de venta tiene dos pantallas hermanas que comparten el mismo dominio de "punto de venta" (account/PDV): la lista — los puntos de venta de la zona del representante de ventas, desde donde crea visitas — y el detalle — la ficha completa de un punto de venta, abierta desde una visita, donde algunos campos pueden editarse. Responde tres preguntas del día a día:
Quais varejos existem?Which retails exist?¿Qué puntos de venta hay?
Um card por varejo da zona, com nome, SAP e endereço; busca por qualquer um deles.One card per retail in the zone, with name, SAP and address; search by any of them.Una tarjeta por punto de venta de la zona, con nombre, SAP y dirección; búsqueda por cualquiera.
Como criar uma visita?How to create a visit?¿Cómo crear una visita?
O card oferece Planejar (agenda futura) ou Ad Hoc (agora), quando o backend permite cada uma.The card offers Plan (future schedule) or Ad Hoc (now), when the backend allows each.La tarjeta ofrece Planificar (agenda futura) o Ad Hoc (ahora), cuando el backend lo permite.
O que dá para editar?What can be edited?¿Qué se puede editar?
No detalhe: nome, endereço, dias, entrega, categorias e mais — cada campo conforme o mercado.In the detail: name, address, days, delivery, categories and more — each field per market.En el detalle: nombre, dirección, días, entrega, categorías y más — cada campo según el mercado.
Duas telas, um domínioTwo screens, one domainDos pantallas, un dominio
A lista (retails) e o detalhe (retail_detail) são pastas separadas, mas documentadas juntas por serem o par que fala do mesmo varejo. A lista trabalha com o dado enxuto do próprio serviço de Varejos; o detalhe lê o varejo de dentro de uma visita (Detalhe da visita).
The list (retails) and the detail (retail_detail) are separate folders, documented together because they are the pair describing the same retail. The list uses the lean data from the Retails service itself; the detail reads the retail from inside a visit (Visit detail).
La lista (retails) y el detalle (retail_detail) son carpetas separadas, documentadas juntas por ser el par que habla del mismo punto de venta. La lista usa el dato liviano del propio servicio de Puntos de venta; el detalle lee el punto de venta desde una visita (Detalle de visita).
Como acessarHow to openCómo acceder
- Lista — pelo menu lateralList — from the drawerLista — desde el menú lateralAbra o menu (drawer) e toque em Varejos. O item só aparece nos mercados que o habilitam no menu. É uma tela roteada (abre com seta de voltar), não uma aba base.Open the drawer and tap Retails. The item appears only in markets that enable it in the menu. It's a routed screen (opens with a back arrow), not a base tab.Abra el menú y toque Puntos de venta. El ítem aparece solo en los mercados que lo habilitan en el menú. Es una pantalla ruteada (abre con flecha de volver), no una pestaña base.
- Lista — pelo atalho da HomeList — from the Home shortcutLista — desde el atajo del HomeO módulo de ações do representante na Home também abre a lista de Varejos.The rep-actions module on Home also opens the Retails list.El módulo de acciones del representante en el Home también abre la lista de Puntos de venta.
- Detalhe — pela visitaDetail — from the visitDetalle — desde la visitaNo Detalhe da visita, toque no card do cliente. O detalhe abre pelo
visitSfid— não pela lista de Varejos (tocar num card da lista não navega).In the Visit detail, tap the client card. The detail opens byvisitSfid— not from the Retails list (tapping a list card doesn't navigate).En el Detalle de visita, toque la tarjeta del cliente. El detalle abre porvisitSfid— no desde la lista de Puntos de venta (tocar una tarjeta de la lista no navega).
Estrutura da telaScreen structureEstructura de la pantalla
Lista de varejosRetail listLista de puntos de venta
- CabeçalhoHeaderEncabezado
- Título "Varejos" e a data da última sincronização dos dados (
DataLoadInfo)."Retails" title and the data's last sync date (DataLoadInfo).Título "Puntos de venta" y la fecha de última sincronización (DataLoadInfo). - BuscaSearchBúsqueda
- Campo que filtra por nome, SAP ou endereço enquanto você digita.A field filtering by name, SAP or address as you type.Campo que filtra por nombre, SAP o dirección mientras escribe.
- Varejos na minha zonaRetails in my zonePuntos de venta en mi zona
- Uma seção com um card por varejo: nome, SAP e — quando o backend permite — os botões Planejar e/ou Ad Hoc.A section with one card per retail: name, SAP and — when the backend allows — the Plan and/or Ad Hoc buttons.Una sección con una tarjeta por punto de venta: nombre, SAP y — cuando el backend lo permite — los botones Planificar y/o Ad Hoc.
- Contador "X de Y""X of Y" counterContador "X de Y"
- Mostra quantos varejos estão visíveis do total filtrado; a lista carrega mais ao rolar (20 por vez).Shows how many retails are visible of the filtered total; the list loads more as you scroll (20 at a time).Muestra cuántos puntos de venta están visibles del total filtrado; la lista carga más al desplazar (20 por vez).
Detalhe do varejoRetail detailDetalle del punto de venta
Uma coluna rolável (puxe para atualizar). Cada campo é uma seção que o mercado pode mostrar/ocultar e tornar editável ou não:A scrollable column (pull to refresh). Each field is a section the market can show/hide and make editable or not:Una columna desplazable (deslice para actualizar). Cada campo es una sección que el mercado puede mostrar/ocultar y hacer editable o no:
- CabeçalhoHeaderEncabezado
- Última sincronização, card do varejo (nome + SAP + selo de inadimplência) e o título "Detalhe do varejo".Last sync, retail card (name + SAP + overdue badge) and the "Retail detail" title.Última sincronización, tarjeta del punto de venta (nombre + SAP + sello de mora) y el título "Detalle del punto de venta".
- IdentificaçãoIdentificationIdentificación
- SAP, CNPJ/documento, inscrição estadual, nome (editável por modal), nome comercial e funcionários (abre Gerenciar equipe).SAP, tax ID, state registration, name (editable via modal), commercial name and employees (opens Manage staff).SAP, documento fiscal, inscripción estatal, nombre (editable por modal), nombre comercial y empleados (abre Gestionar equipo).
- Endereço e classificaçãoAddress & classificationDirección y clasificación
- Endereço (editável em página), classificação local, dias de funcionamento, categorias vendidas e subtipo de PDV.Address (editable in a page), local classification, operating days, categories sold and outlet subtype.Dirección (editable en página), clasificación local, días de operación, categorías vendidas y subtipo de PDV.
- Comercial e rotaCommercial & routeComercial y ruta
- Bandeira, key account, faixa de volume, classe de merchandising, frequências de pedido/visita, dia de visita, entrega (editável em página) e a solicitação de cadastro de rota.Banner, key account, volume range, merchandising class, order/visit frequencies, visit day, delivery (editable in a page) and the route registration request.Bandera, key account, rango de volumen, clase de merchandising, frecuencias de pedido/visita, día de visita, entrega (editable en página) y la solicitud de registro de ruta.
- FinanceiroFinancialFinanciero
- Lead time, limite de crédito, dias de crédito e formas de pagamento — somente leitura.Lead time, credit limit, credit days and payment methods — read-only.Lead time, límite de crédito, días de crédito y formas de pago — solo lectura.
Estados e disponibilidadeStates & availabilityEstados y disponibilidad
Varejos não tem um "status" como o pedido. O que muda de card para card é quais ações o backend permite e a condição do varejo:Retails has no "status" like an order. What varies card to card is which actions the backend allows and the retail's condition:Puntos de venta no tiene un "estado" como el pedido. Lo que cambia de tarjeta a tarjeta es qué acciones permite el backend y la condición del punto de venta:
- Botão PlanejarPlan buttonBotón Planificar
- Aparece só quando
isPlannedVisitCreationAvailableé verdadeiro para aquele varejo.Shows only whenisPlannedVisitCreationAvailableis true for that retail.Aparece solo cuandoisPlannedVisitCreationAvailablees verdadero para ese punto de venta. - Botão Ad HocAd Hoc buttonBotón Ad Hoc
- Aparece só quando
isAdhocVisitCreationAvailableé verdadeiro; depois de criar, some (o cache marca o varejo). Durante a criação, mostra "Criando visita…".Shows only whenisAdhocVisitCreationAvailableis true; after creating, it disappears (the cache flags the retail). While creating, it shows "Creating visit…".Aparece solo cuandoisAdhocVisitCreationAvailablees verdadero; tras crear, desaparece (el caché marca el punto de venta). Durante la creación, muestra "Creando visita…". - InadimplênciaOverdueMora
- No detalhe, o card do varejo mostra um selo quando
isOverdueé verdadeiro.In the detail, the retail card shows a badge whenisOverdueis true.En el detalle, la tarjeta muestra un sello cuandoisOverduees verdadero. - Editando / SalvandoEditing / SavingEditando / Guardando
- Cada seção editável do detalhe entra em modo editando (rascunho) e, ao confirmar, em salvando até o envio terminar.Each editable detail section enters editing mode (draft) and, on confirm, saving until the submit finishes.Cada sección editable del detalle entra en modo editando (borrador) y, al confirmar, en guardando hasta terminar el envío.
Ações: visitas e ediçãoActions: visits & editingAcciones: visitas y edición
Criar visita (lista)Create visit (list)Crear visita (lista)
- PlanejarPlanPlanificar
- Abre um modal com um calendário. Escolhida a data futura, o app cria uma visita agendada (transação
VisitUploadAPI) e confirma por um aviso verde.Opens a modal with a calendar. With a future date chosen, the app creates a scheduled visit (VisitUploadAPItransaction) and confirms with a green notice.Abre un modal con un calendario. Con la fecha futura elegida, la app crea una visita agendada (transacciónVisitUploadAPI) y confirma con un aviso verde. - Ad HocAd HocAd Hoc
- Abre um modal de confirmação; ao confirmar, cria uma visita imediata via RPC
getAdhocVisite baixa todo o pacote de dados do varejo. Detalhado na seção ◆ Visita Ad Hoc.Opens a confirmation modal; on confirm, creates an immediate visit via thegetAdhocVisitRPC and downloads the retail's whole data package. Detailed in the ◆ Ad Hoc visit section.Abre un modal de confirmación; al confirmar, crea una visita inmediata vía el RPCgetAdhocVisity descarga todo el paquete de datos del punto de venta. Detallado en la sección ◆ Visita Ad Hoc.
Editar o cadastro (detalhe)Edit the profile (detail)Editar la ficha (detalle)
Cada campo editável tem um modo de edição definido pelo mercado: inline (edita na própria seção), modal (uma janela — ex.: nome) ou página (uma tela dedicada — endereço e entrega). A edição só é permitida com a visita iniciada (guarda de início de visita). Fluxo comum:Each editable field has an edit mode set by the market: inline (edits in the section itself), modal (a dialog — e.g. name) or page (a dedicated screen — address and delivery). Editing is only allowed with the visit started (a visit-start guard). Common flow:Cada campo editable tiene un modo de edición definido por el mercado: inline (edita en la propia sección), modal (una ventana — ej.: nombre) o página (una pantalla dedicada — dirección y entrega). La edición solo se permite con la visita iniciada (guarda de inicio de visita). Flujo común:
- Toque em editarTap editToque editarAbre o modal/página conforme o modo do campo, com os valores atuais.Opens the modal/page per the field's mode, prefilled with current values.Abre el modal/página según el modo del campo, con los valores actuales.
- Altere e confirmeChange and confirmCambie y confirmeO app envia primeiro ao backend (
RetailerUploadAPI) e só então grava a alteração no cache local da visita.The app sends to the backend first (RetailerUploadAPI) and only then writes the change to the visit's local cache.La app envía primero al backend (RetailerUploadAPI) y solo entonces graba el cambio en el caché local de la visita. - Solicitação de cadastro (rota)Registration request (route)Solicitud de registro (ruta)A tela de entrega envia uma solicitação de rota (com data de início) pela mesma transação, marcada como pedido de rota.The delivery screen submits a route request (with a start date) through the same transaction, flagged as a route request.La pantalla de entrega envía una solicitud de ruta (con fecha de inicio) por la misma transacción, marcada como pedido de ruta.
Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos
Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. São quatro fluxos distintos: a leitura da lista (um RPC próprio, write-through), a leitura do detalhe (só cache, a partir da visita), a escrita das edições (Dispatcher, remote-first) e a criação de visita ad hoc (RPC agregado + merge nos caches).Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. There are four distinct flows: the list read (its own RPC, write-through), the detail read (cache-only, from the visit), the edit writes (Dispatcher, remote-first) and the ad hoc visit creation (aggregate RPC + merge into caches).Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Son cuatro flujos distintos: la lectura de la lista (un RPC propio, write-through), la lectura del detalle (solo caché, desde la visita), las escrituras de edición (Dispatcher, remote-first) y la creación de visita ad hoc (RPC agregado + merge en los cachés).
Leitura da lista · write-throughList read · write-throughLectura de la lista · write-through
Um único RPC (getRetails) traz os varejos da hierarquia; o dado atravessa Proto → DTO → Entity → Model → Entity, com cache write-through:A single RPC (getRetails) returns the hierarchy's retails; data crosses Proto → DTO → Entity → Model → Entity, with cache write-through:Un único RPC (getRetails) trae los puntos de venta de la jerarquía; el dato atraviesa Proto → DTO → Entity → Model → Entity, con cache write-through:
- RetailsReplygRPC proto
- toRetailsDTORetailsDTODTO · Freezed
- toDomainRetailsEntitydomain
- toModel / saveRetailsRetailsModelObjectBox
- toDomainRetailsEntitydomain · cache
- watchRetailsNotifier + State
- → UIRetailsPage
- watchRetailsNotifier + State
- toDomainRetailsEntitydomain · cache
- toModel / saveRetailsRetailsModelObjectBox
- toDomainRetailsEntitydomain
- toRetailsDTORetailsDTODTO · Freezed
Leitura do detalhe · só cache (via visita)Detail read · cache-only (via visit)Lectura del detalle · solo caché (vía visita)
O detalhe não usa a lista de Varejos nem o serviço getRetails. Ele lê a visita do cache pelo visitSfid e usa o AccountDataEntity que vive dentro dela (ver Detalhe da visita). Também busca config (EMC) e dados de referência para dirigir os campos:The detail does not use the Retails list nor the getRetails service. It reads the visit from cache by visitSfid and uses the AccountDataEntity living inside it (see Visit detail). It also fetches config (EMC) and reference data to drive the fields:El detalle no usa la lista de Puntos de venta ni el servicio getRetails. Lee la visita del caché por visitSfid y usa el AccountDataEntity que vive dentro (ver Detalle de visita). También busca config (EMC) y datos de referencia para dirigir los campos:
- VisitModelObjectBox · cache
- getCachedBySfidGetVisitsUseCasevisit.accountData
- + EMC + ReferenceDataRetailDetailNotifier._load
- buildRetailDetailStateaccount + draftAccount + config
- → UIRetailDetailPage
- buildRetailDetailStateaccount + draftAccount + config
- + EMC + ReferenceDataRetailDetailNotifier._load
- getCachedBySfidGetVisitsUseCasevisit.accountData
Escrita das edições · Dispatcher (remote-first)Edit writes · Dispatcher (remote-first)Escrituras de edición · Dispatcher (remote-first)
Toda edição de campo despacha um envelope RetailerUploadAPI pelo Dispatcher e só grava no cache da visita depois do sucesso (§36):Every field edit dispatches a RetailerUploadAPI envelope through the Dispatcher and only writes to the visit cache after success (§36):Toda edición de campo despacha un sobre RetailerUploadAPI por el Dispatcher y solo graba en el caché de la visita tras el éxito (§36):
- RetailDetail widget / edit pageUI
- save*()RetailDetailNotifier
- build(input)BuildRetailerUploadDispatcherPayloadUseCase→ DispatcherEnvelope
- submit → dispatchSubmitRetailerUploadUseCaseDispatcherOrchestrator
- on Ack successSaveAccountUpdateUseCase→ visit cache
- submit → dispatchSubmitRetailerUploadUseCaseDispatcherOrchestrator
- build(input)BuildRetailerUploadDispatcherPayloadUseCase→ DispatcherEnvelope
- save*()RetailDetailNotifier
Criação de visita ad hoc · RPC agregado + mergeAd hoc visit creation · aggregate RPC + mergeCreación de visita ad hoc · RPC agregado + merge
O botão Ad Hoc chama getAdhocVisit (que cria a visita no Salesforce e devolve todo o pacote de dados do varejo) e faz merge aditivo em 12 caches locais. Detalhado em ◆ Visita Ad Hoc:The Ad Hoc button calls getAdhocVisit (which creates the visit in Salesforce and returns the retail's whole data package) and does an additive merge into 12 local caches. Detailed in ◆ Ad Hoc visit:El botón Ad Hoc llama getAdhocVisit (que crea la visita en Salesforce y devuelve todo el paquete de datos del punto de venta) y hace un merge aditivo en 12 cachés locales. Detallado en ◆ Visita Ad Hoc:
- _AdhocBadgeUI · retail card
- create()AdhocVisitCreationprovider · keepAlive
- executeCreateAdhocVisitUseCase
- getAdhocVisitAdhocVisitRepositoryImpl→ AdhocVisitResultEntity
- merge (12 caches)AdhocVisitMergeCoordinator
- markAdhocVisitCreated · invalidateConectaNotice + Retails/Visits
- merge (12 caches)AdhocVisitMergeCoordinator
- getAdhocVisitAdhocVisitRepositoryImpl→ AdhocVisitResultEntity
- executeCreateAdhocVisitUseCase
- create()AdhocVisitCreationprovider · keepAlive
Modelo de dadosData modelModelo de datos
São dois modelos independentes. (1) O da lista: um Retail enxuto (7 campos), próprio do serviço de Varejos, que existe em quatro representações Proto → DTO → Model → Entity ligadas por mappers, com cache write-through. (2) O do detalhe: o AccountDataEntity, que pertence ao agregado da Visita — o detalhe apenas o lê do cache e o edita; seu mapeamento cross-camada completo está em Detalhe da visita.There are two independent models. (1) The list one: a lean Retail (7 fields), owned by the Retails service, existing in four representations Proto → DTO → Model → Entity linked by mappers, with cache write-through. (2) The detail one: AccountDataEntity, which belongs to the Visit aggregate — the detail only reads it from cache and edits it; its full cross-layer mapping lives in Visit detail.Son dos modelos independientes. (1) El de la lista: un Retail liviano (7 campos), propio del servicio de Puntos de venta, que existe en cuatro representaciones Proto → DTO → Model → Entity unidas por mappers, con cache write-through. (2) El del detalle: el AccountDataEntity, que pertenece al agregado de la Visita — el detalle solo lo lee del caché y lo edita; su mapeo cross-capa completo está en Detalle de visita.
A lista chega num container RetailsEntity (lastSyncAt gerado no mapper + retails[]); cada item é um Retail de 7 campos, sem sub-estruturas. O modelo é plano e sem deltas de tipo — todos os campos são String/bool. A escrita (edição/adhoc/visita) não tem modelo próprio: a edição envia via RetailerUpload e o ad hoc devolve um agregado de 11 estruturas com 16 listas (ver ◆ Visita Ad Hoc).The list arrives in a RetailsEntity container (lastSyncAt generated in the mapper + retails[]); each item is a 7-field Retail, no sub-structures. The model is flat and delta-free — every field is String/bool. Writes (edit/adhoc/visit) have no model of their own: the edit submits via RetailerUpload and ad hoc returns an 11-structure aggregate with 16 lists (see ◆ Ad Hoc visit).La lista llega en un container RetailsEntity (lastSyncAt generado en el mapper + retails[]); cada ítem es un Retail de 7 campos, sin sub-estructuras. El modelo es plano y sin deltas de tipo — todos los campos son String/bool. Las escrituras (edición/adhoc/visita) no tienen modelo propio: la edición envía vía RetailerUpload y el ad hoc devuelve un agregado de 11 estructuras con 16 listas (ver ◆ Visita Ad Hoc).
Proto
RetailsConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge. A lista usa o RetailsConectaRepService (um método unário). O detalhe não chama RPC (lê do cache da visita); o ad hoc usa um serviço próprio (AdhocVisitConectaRepService):The list uses RetailsConectaRepService (a single unary method). The detail calls no RPC (reads the visit cache); ad hoc uses its own service (AdhocVisitConectaRepService):La lista usa RetailsConectaRepService (un método unario). El detalle no llama RPC (lee del caché de la visita); el ad hoc usa un servicio propio (AdhocVisitConectaRepService):
getRetailsunaryrpc getRetails(RetailsRequest) returns (RetailsReply)
path /mn.bat.conectarep.streambridge.RetailsConectaRepService/getRetails
RetailsRequestlocationHierarchySfidstring· #1 · hierarquia do representante de vendas (resolvida no repository viacurrentResourceProvider)sales rep hierarchy (resolved in the repository viacurrentResourceProvider)jerarquía del representante de ventas (resuelta en el repository víacurrentResourceProvider)dateReferencestring· #2 · optionallastModifiedDatestring· #3 · optional (não usado hoje)optional (not used today)optional (no usado hoy)
RetailsReplyrepeated Retail retails (#1) — a lista de varejos. Os campos page/pageSize/totalItems (#2–#4) não são mapeados (paginação server-side não usada). Os 7 campos de Retail estão nas Estruturas de dados abaixo.— the list of retails. The page/pageSize/totalItems fields (#2–#4) are not mapped (server-side pagination unused). Retail's 7 fields are in Data structures below.— la lista de puntos de venta. Los campos page/pageSize/totalItems (#2–#4) no se mapean (paginación server-side no usada). Los 7 campos de Retail están en Estructuras de datos abajo.
getAdhocVisitunary · agregadounary · aggregateunary · agregadorpc getAdhocVisit(AdhocVisitRequest) returns (AdhocVisitReply)
AdhocVisitConectaRep.proto · path /mn.bat.conectarep.streambridge.AdhocVisitConectaRepService/getAdhocVisit
AdhocVisitRequestusernamestring· #1 · docurrentResourcefromcurrentResourcedelcurrentResourceaccountSfidstring· #2 · o varejo escolhidothe chosen retailel punto de venta elegidorepSfidstring· #3 ·resource.sfidgeoLatitude / geoLongitudedouble· #4/#5 · optional ·LocationService.currentPositionLocationService.currentPositionLocationService.currentPosition
AdhocVisitReplyDiferente dos outros serviços, parte de username + accountSfid (não de locationHierarchySfid) e devolve 11 estruturas agregadas (visitas, tasks, surveys, stockControl, promoções, spots, pedidos, merchandising, marginCalculator, financeiro, catálogo). Detalhado em ◆ Visita Ad Hoc.Unlike other services it starts from username + accountSfid (not locationHierarchySfid) and returns 11 aggregated structures (visits, tasks, surveys, stockControl, promotions, spots, orders, merchandising, marginCalculator, financial, catalog). Detailed in ◆ Ad Hoc visit.A diferencia de otros servicios parte de username + accountSfid (no locationHierarchySfid) y devuelve 11 estructuras agregadas (visitas, tasks, surveys, stockControl, promociones, spots, pedidos, merchandising, marginCalculator, financiero, catálogo). Detallado en ◆ Visita Ad Hoc.
Estruturas de dadosData structuresEstructuras de datos
O modelo da lista tem só o Retail — uma coluna por camada (Proto · DTO · Model · Entity). O modelo do detalhe (AccountData) pertence à Visita; abaixo está a sua superfície editável (campos que o detalhe lê/edita) — o cross-camada completo é da Visita.The list model has only Retail — one column per layer (Proto · DTO · Model · Entity). The detail model (AccountData) belongs to the Visit; below is its editable surface (fields the detail reads/edits) — the full cross-layer is the Visit's.El modelo de la lista tiene solo Retail — una columna por capa (Proto · DTO · Model · Entity). El modelo del detalle (AccountData) pertenece a la Visita; abajo está su superficie editable (campos que el detalle lee/edita) — el cross-capa completo es de la Visita.
Retail raiz · listaroot · listraíz · lista 7 campos7 fields7 campos
Campo Proto DTO Model Entity accountSfidstring String String String accountNamestring String String String accountSapCustomerIdstring String String String addressstring String String String isAdhocVisitCreationAvailablebool bool bool bool isPlannedVisitCreationAvailablebool bool bool bool taxIdstring¹ String? String? String? AccountData detalhe · da Visitadetail · from Visitdetalle · de la Visita superfície editáveleditable surfacesuperficie editable
Campo (Entity) Tipo Editável?Editable?¿Editable? sfid / customerCodeString — nameString modalcommercialNameString? — taxCode / stateRegistrationString? — addressAddressEntity?pageoperatingDaysList<String> inlineopeningTime / closingTimeString? inlinecategoriesSoldList<CategoryForSale>inlineoutletSubtypeSfidNamePairEntity?inlinelocalClassificationLocalClassificationEntity?inlinerouteInfoRouteInfoEntity?pagebannerName / keyAccountType / volumeRange / merchandisingClassString? — creditLimit / baseCreditLimit / overdueAmountdouble? — creditDays / totalStaffint? — paymentMethodsList<String> — supplierDataSupplierDataEntity?— staffStaffEntitytela própriaown screenpantalla propia status / isOverdue / isBlockedToSales / isB2B / hasCompetitionString / bool? — AddressEntity AccountData.address 7 campos7 fields7 campos
Campo Tipo fullAddress / street / streetComplement / neighborhood / postalCodeString? city / stateSfidNamePairEntity?RouteInfoEntity AccountData.routeInfo visit · order · delivery
Campo Tipo visit(RouteVisitEntity)sfid? · frequency? · day?order(RouteOrderEntity)sfid? · frequency?delivery(RouteDeliveryEntity)preferredDay? · isFlexible?
Mappers
Do modelo da lista (RetailMapper / RetailsMapper), 5 direções por extension:For the list model (RetailMapper / RetailsMapper), 5 directions via extension:Del modelo de la lista (RetailMapper / RetailsMapper), 5 direcciones por extension:
| DireçãoDirectionDirección | MétodoMethodMétodo |
|---|---|
| JSON → DTO | static fromMap(Map) (container gera lastSyncAt com DateTimeUtils.now())(container generates lastSyncAt with DateTimeUtils.now())(el container genera lastSyncAt con DateTimeUtils.now()) |
| Proto → DTO | toDTO() / toRetailsDTO() (taxId vazio → null; lastSyncAt = now)(empty taxId → null; lastSyncAt = now)(taxId vacío → null; lastSyncAt = now) |
| DTO → Entity | toDomain() |
| Entity → Model | toModel() (popula ToMany<RetailModel>)(fills ToMany<RetailModel>)(llena ToMany<RetailModel>) |
| Model → Entity | toDomain() |
Os únicos deltasThe only deltasLos únicos deltas
Retailnão tem delta de tipo — os 7 campos são idênticos em Proto/DTO/Model/Entity (String/bool)Retailhas no type delta — the 7 fields are identical across Proto/DTO/Model/Entity (String/bool)Retailno tiene delta de tipo — los 7 campos son idénticos en Proto/DTO/Model/Entity (String/bool)taxIdstringvazio no proto →nullno DTO em diante (optional,¹)empty protostring→nullfrom the DTO on (optional,¹)stringvacío en el proto →nulldel DTO en adelante (optional,¹)RetailsReply.page / pageSize / totalItemsdescartados (não mapeados)dropped (not mapped)descartados (no mapeados)lastSyncAtgerado no mapper comDateTimeUtils.now()(o proto não envia)generated in the mapper withDateTimeUtils.now()(the proto doesn't send it)generado en el mapper conDateTimeUtils.now()(el proto no lo envía)AccountData.categoriesSoldenumCategoryForSaletipado só na Entity (mapeamento na Visita)enumCategoryForSaletyped only in the Entity (mapping in the Visit)enumCategoryForSaletipado solo en la Entity (mapeo en la Visita)
Repository
Três repositórios entram em jogo: RetailRepository (a lista), RetailUpdateRepository (grava a edição no cache da visita) e AdhocVisitRepository (o RPC agregado + merge). Um dropdown por método — assinatura, retorno e comportamento.Three repositories are involved: RetailRepository (the list), RetailUpdateRepository (writes the edit to the visit cache) and AdhocVisitRepository (the aggregate RPC + merge). One dropdown per method — signature, return and behavior.Tres repositorios intervienen: RetailRepository (la lista), RetailUpdateRepository (graba la edición en el caché de la visita) y AdhocVisitRepository (el RPC agregado + merge). Un dropdown por método — firma, retorno y comportamiento.
RetailRepositoryImpl
getRetails({source}) mock / local / remote
RetornaReturnsDevuelve Result<RetailsEntity, Failure>
Árvore de decisão de fonteSource decision treeÁrbol de decisión de fuente
useMock== true ouorosource == mock→_fetchFromMock(): lê o mock por mercado, mapeia, grava no cache.→_fetchFromMock(): reads the per-market mock, maps, writes to cache.→_fetchFromMock(): lee el mock por mercado, mapea, graba en caché.source == localou offlineor offlineu offline→_fetchFromCacheOrFail(): cache; vazio →NetworkFailure.→_fetchFromCacheOrFail(): cache; empty →NetworkFailure.→_fetchFromCacheOrFail(): caché; vacío →NetworkFailure.- senão (remoto + conectado)otherwise (remote + connected)si no (remoto + conectado)→
_fetchFromRemoteWithFallback(): lêcurrentResourceProvider;null→ cache; senão chama o remoto comlocationHierarchyId, mapeia, grava; em erro, fallback pro cache.→_fetchFromRemoteWithFallback(): readscurrentResourceProvider;null→ cache; else calls remote withlocationHierarchyId, maps, writes; on error, falls back to cache.→_fetchFromRemoteWithFallback(): leecurrentResourceProvider;null→ caché; si no llama al remoto conlocationHierarchyId, mapea, graba; en error, fallback al caché.
getCachedRetails() local
RetornaReturnsDevuelve Result<RetailsEntity?, Failure>
Só cache; null vira Success(null).Cache only; null becomes Success(null).Solo caché; null es Success(null).
getCachedRetailsLastSyncAt() local
RetornaReturnsDevuelve DateTime?
Timestamp da última sync do container, para o DataLoadInfo.The container's last-sync timestamp, for DataLoadInfo.Timestamp de última sincronización del container, para DataLoadInfo.
saveRetails({entity}) local
RetornaReturnsDevuelve Result<void, Failure>
Destrutivo: clearRetails() + regrava (boxes filhas em cascata). Cache-writer após cada fetch.Destructive: clearRetails() + rewrites (child boxes cascade). Cache-writer after each fetch.Destructivo: clearRetails() + regraba (boxes hijas en cascada). Cache-writer tras cada fetch.
markAdhocVisitCreated({accountSfid}) local
RetornaReturnsDevuelve Result<void, Failure>
Vira isAdhocVisitCreationAvailable = false naquele varejo no cache (esconde o botão Ad Hoc). Chamado após criar a visita ad hoc.Flips isAdhocVisitCreationAvailable = false for that retail in cache (hides the Ad Hoc button). Called after creating the ad hoc visit.Cambia isAdhocVisitCreationAvailable = false para ese punto de venta en caché (oculta el botón Ad Hoc). Llamado tras crear la visita ad hoc.
RetailUpdateRepositoryImpl
Persistência local apenas — grava a alteração dentro da visita no cache (delegando ao VisitLocalDataSource). É o passo "grava no local após o sucesso do remoto" do padrão remote-first (§36); o envio remoto é feito antes, no Notifier, via RetailerUpload.Local persistence only — writes the change inside the visit in cache (delegating to VisitLocalDataSource). It's the "write local after remote success" step of the remote-first pattern (§36); the remote submit happens first, in the Notifier, via RetailerUpload.Persistencia local solamente — graba el cambio dentro de la visita en caché (delegando al VisitLocalDataSource). Es el paso "grabar local tras el éxito remoto" del patrón remote-first (§36); el envío remoto ocurre antes, en el Notifier, vía RetailerUpload.
saveAccountUpdate({visitSfid, newAccount})
- RetornoReturnRetorno
Result<AccountDataEntity, Failure>- ComportamentoBehaviorComportamiento
updateAccountInVisitno cache da visita e devolve o novo account.updateAccountInVisitin the visit cache and returns the new account.updateAccountInVisiten el caché de la visita y devuelve el nuevo account.
saveContactUpdate / removeContact / saveClerkUpdate staff
Upsert/remoção de contatos e clerks dentro da visita — usados pela feature de equipe (Gerenciar equipe), acessível pelo detalhe. Retornam Result<ContactEntity> / Result<void> / Result<ClerkEntity>.Upsert/removal of contacts and clerks inside the visit — used by the staff feature (Manage staff), reachable from the detail. Return Result<ContactEntity> / Result<void> / Result<ClerkEntity>.Upsert/eliminación de contactos y clerks dentro de la visita — usados por la feature de equipo (Gestionar equipo), accesible desde el detalle. Devuelven Result<ContactEntity> / Result<void> / Result<ClerkEntity>.
AdhocVisitRepositoryImpl
createAdhocVisit({accountSfid, geoLatitude?, geoLongitude?}) remote + merge
RetornaReturnsDevuelve Result<AdhocVisitResultEntity, Failure>
Lê currentResource (null → UnknownFailure); chama getAdhocVisit (mock devolve vazio — online-only); se o reply não traz visita, retorna BusinessFailure; senão chama _mergeCoordinator.merge e devolve o agregado. Detalhado em ◆ Visita Ad Hoc.Reads currentResource (null → UnknownFailure); calls getAdhocVisit (mock returns empty — online-only); if the reply has no visit, returns BusinessFailure; else calls _mergeCoordinator.merge and returns the aggregate. Detailed in ◆ Ad Hoc visit.Lee currentResource (null → UnknownFailure); llama getAdhocVisit (mock devuelve vacío — online-only); si el reply no trae visita, devuelve BusinessFailure; si no llama _mergeCoordinator.merge y devuelve el agregado. Detallado en ◆ Visita Ad Hoc.
Datasources
Um card por datasource (dropdown). No corpo: método, envio, retorno, fluxo de uso e tratamento de erro.One card per datasource (dropdown). In the body: method, what it sends, return, usage flow and error handling.Un card por datasource (dropdown). En el cuerpo: método, envío, retorno, flujo de uso y manejo de errores.
Remote RetailRemoteDataSource gRPC
getRetails({locationHierarchySfid, dateReference?})
- EnvioSendsEnvío
- monta
RetailsRequeste chama_client.getRetails(request)(RetailsConectaRepServiceClient).buildsRetailsRequestand calls_client.getRetails(request)(RetailsConectaRepServiceClient).armaRetailsRequesty llama_client.getRetails(request)(RetailsConectaRepServiceClient). - RetornoReturnRetorno
RetailsDTO(viaresponse.toRetailsDTO())(viaresponse.toRetailsDTO())(víaresponse.toRetailsDTO())- Fluxo de usoUsage flowFlujo de uso
- caminho remoto do repository; o resultado é gravado no cache.the repository's remote path; result is written to cache.camino remoto del repository; el resultado se graba en caché.
- Tratamento de erroError handlingManejo de errores
GrpcError→GrpcExceptionHandler; outros →ServerException. Repository faz fallback pro cache.GrpcError→GrpcExceptionHandler; others →ServerException. Repository falls back to cache.GrpcError→GrpcExceptionHandler; otros →ServerException. Repository hace fallback al caché.
Local RetailLocalDataSource ObjectBox
Envio / fluxo: persistência via ObjectBox, boxes RetailsModel e RetailModel — sem rede. Erro: falhas propagam como CacheException.Sends / flow: ObjectBox persistence, RetailsModel and RetailModel boxes — no network. Error: failures propagate as CacheException.Envío / flujo: persistencia vía ObjectBox, boxes RetailsModel y RetailModel — sin red. Error: fallos propagan como CacheException.
getRetails()
- RetornoReturnRetorno
RetailsEntity?—models.first.toDomain()
getRetailsLastSyncAt()
- RetornoReturnRetorno
DateTime?—models.first.lastSyncAt
saveRetails({entity})
- ComportamentoBehaviorComportamiento
- destrutivo:
clearRetails()+_box.put(entity.toModel()).destructive:clearRetails()+_box.put(entity.toModel()).destructivo:clearRetails()+_box.put(entity.toModel()).
markAdhocVisitCreated({accountSfid})
- ComportamentoBehaviorComportamiento
- read-modify-write:
copyWith(isAdhocVisitCreationAvailable:false)no varejo doaccountSfid.read-modify-write:copyWith(isAdhocVisitCreationAvailable:false)on theaccountSfidretail.read-modify-write:copyWith(isAdhocVisitCreationAvailable:false)en el punto de venta delaccountSfid.
clearRetails()
- ComportamentoBehaviorComportamiento
- limpa
RetailModeleRetailsModel(filhas→raiz).clearsRetailModelandRetailsModel(children→root).limpiaRetailModelyRetailsModel(hijas→raíz).
Mock RetailMockDataSource JSON
getRetails()
- EnvioSendsEnvío
- carrega o asset
retails/retailspor mercado (real vs sintético viauseRealMockData) — sem rede.loads theretails/retailsasset per market (real vs synthetic viauseRealMockData) — no network.carga el assetretails/retailspor mercado (real vs sintético víauseRealMockData) — sin red. - RetornoReturnRetorno
RetailsDTO(viaRetailsDTOJsonMapper.fromMap)(viaRetailsDTOJsonMapper.fromMap)(víaRetailsDTOJsonMapper.fromMap)
Remote AdhocVisitRemoteDataSource gRPC
getAdhocVisit({username, accountSfid, repSfid, geoLatitude?, geoLongitude?})
- EnvioSendsEnvío
- monta
AdhocVisitRequeste chama_client.getAdhocVisit(request).buildsAdhocVisitRequestand calls_client.getAdhocVisit(request).armaAdhocVisitRequesty llama_client.getAdhocVisit(request). - RetornoReturnRetorno
AdhocVisitResultDTO(viaresponse.toDTO())(viaresponse.toDTO())(víaresponse.toDTO())- Tratamento de erroError handlingManejo de errores
GrpcError→GrpcExceptionHandler; outros →ServerException.GrpcError→GrpcExceptionHandler; others →ServerException.GrpcError→GrpcExceptionHandler; otros →ServerException.
Mock AdhocVisitMockDataSource vazio · online-onlyempty · online-onlyvacío · online-only
Retorna um AdhocVisitResultDTO vazio por decisão do usuário — o ad hoc é validado só online contra o streambridge real.Returns an empty AdhocVisitResultDTO by decision — ad hoc is validated online-only against the real streambridge.Devuelve un AdhocVisitResultDTO vacío por decisión — el ad hoc se valida solo online contra el streambridge real.
Local RetailUpdateLocalDataSource delega à Visitadelegates to Visitdelega a la Visita
Fino wrapper sobre o VisitLocalDataSource: updateAccountInVisit, upsertContactInVisit, removeContactFromVisit, upsertClerkInVisit. Não tem box própria — o cadastro do varejo mora na box da Visita.Thin wrapper over VisitLocalDataSource: updateAccountInVisit, upsertContactInVisit, removeContactFromVisit, upsertClerkInVisit. No box of its own — the retail profile lives in the Visit box.Wrapper fino sobre VisitLocalDataSource: updateAccountInVisit, upsertContactInVisit, removeContactFromVisit, upsertClerkInVisit. Sin box propia — la ficha del punto de venta vive en la box de la Visita.
Enums e labelsEnums & labelsEnums y labels
Os enums da feature. Retail não tem enum (7 campos String/bool); os enums abaixo governam a edição do detalhe, a config por mercado e o ad hoc.The feature's enums. Retail has none (7 String/bool fields); the enums below govern detail editing, per-market config and ad hoc.Los enums de la feature. Retail no tiene enum (7 campos String/bool); los enums abajo gobiernan la edición del detalle, la config por mercado y el ad hoc.
RetailDetailSection 7
| case | o que editawhat it editsqué edita |
|---|---|
accountName | nome (modal)name (modal)nombre (modal) |
outletSubtype | subtipo de PDVoutlet subtypesubtipo de PDV |
localClassification | classificação locallocal classificationclasificación local |
operatingDays | dias + horáriosdays + hoursdías + horarios |
categoriesSold | categorias vendidascategories soldcategorías vendidas |
address | endereço (página)address (page)dirección (página) |
registrationRequest | rota/entrega (página, isRouteRequest)route/delivery (page, isRouteRequest)ruta/entrega (página, isRouteRequest) |
EditMode 4 · value
| case | value | notanotenota |
|---|---|---|
none | "none" | não editávelnot editableno editable |
inline | "inline" | edita na própria seçãoedits in the sectionedita en la sección |
modal | "modal" | janela (ex.: nome)dialog (e.g. name)ventana (ej.: nombre) |
page | "page" | tela dedicada (endereço/entrega)dedicated screen (address/delivery)pantalla dedicada (dirección/entrega) |
RetailerUploadModule 3 · value
| case | value |
|---|---|
accountSnapshot | "accountSnapshot" |
address | "address" |
localClassification | "localClassification" |
GeoCallFrequency 4 · wire · i18n
| case | wireValue | multiplier | i18n key |
|---|---|---|---|
weekly | "0001" | 1 | frequencyWeekly |
biweekly | "0002" | 2 | frequencyBiweekly |
monthly | "0004" | 4 | frequencyMonthly |
bimonthly | "0008" | 8 | frequencyBimonthly |
CategoryForSale 17 · wire · label
| case | wireValue | label |
|---|---|---|
fmc | "fmc" | FMC |
thpDevices | "thp_devices" | THP Devices |
thpSticks | "thp_sticks" | THP Sticks |
vapourDevices | "vapour_devices" | Vapour Devices |
vapourLiquids | "vapour_liquids" | Vapour Liquids |
oral | "oral" | Oral |
ryo | "ryo" | RYO |
myo | "myo" | MYO |
otp | "otp" | OTP |
otpAccessories | "otp_accessories" | OTP Accessories |
otherEa | "other_ea" | Other EA |
otherUnit | "other_unit" | Other UNIT |
otherPce | "other_pce" | Other PCE |
vuse | "vuse" | VUSE |
partnership | "partnership" | Partnership |
nc | "nc" | NC |
unknown | "" | — |
WeekDay 7 · wire
| case | wire |
|---|---|
monday | "Monday" |
tuesday | "Tuesday" |
wednesday | "Wednesday" |
thursday | "Thursday" |
friday | "Friday" |
saturday | "Saturday" |
sunday | "Sunday" |
AdhocVisitCreationPhase 4
| case |
|---|
idle |
creating |
completed |
failed |
ModuleType (retail_*) 24 · seções do detalhe24 · detail sections24 · secciones del detalle
| case | value |
|---|---|
retailSapCode | "retail_sap_code" |
retailAccountName | "retail_account_name" |
retailCommercialName | "retail_commercial_name" |
retailEmployees | "retail_employees" |
retailAddress | "retail_address" |
retailLocalClassification | "retail_local_classification" |
retailOperatingDays | "retail_operating_days" |
retailCategoriesSold | "retail_categories_sold" |
retailOutletSubtype | "retail_outlet_subtype" |
retailBanner | "retail_banner" |
retailOrderFrequency | "retail_order_frequency" |
retailSellerVisitFrequency | "retail_seller_visit_frequency" |
retailLeadTime | "retail_lead_time" |
retailCreditLimit | "retail_credit_limit" |
retailCreditLimitDays | "retail_credit_limit_days" |
retailPaymentMethod | "retail_payment_method" |
retailTaxId | "retail_tax_id" |
retailStateRegistration | "retail_state_registration" |
retailKeyAccount | "retail_key_account" |
retailVolumeRange | "retail_volume_range" |
retailMerchandising | "retail_merchandising" |
retailVisitDay | "retail_visit_day" |
retailPreferredDeliveryDay | "retail_preferred_delivery_day" |
retailStartDate | "retail_start_date" |
UseCases
Um dropdown por UseCase; dentro, cada método com assinatura, o que retorna e uso. Os da lista/ad hoc delegam ao repository; os de dispatcher (Build*/Submit*) montam e despacham o envelope.One dropdown per UseCase; inside, each method with its signature, return and use. List/ad hoc ones delegate to the repository; the dispatcher ones (Build*/Submit*) assemble and dispatch the envelope.Un dropdown por UseCase; dentro, cada método con su firma, qué devuelve y uso. Los de lista/ad hoc delegan al repository; los de dispatcher (Build*/Submit*) arman y despachan el sobre.
GetRetailsUseCase 3 · a listathe listla lista
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({source}) | Result<RetailsEntity, Failure> | Ponto de entrada da lista → repository.getRetails.List entry point → repository.getRetails.Punto de entrada de la lista → repository.getRetails. |
getCached() | Result<RetailsEntity?, Failure> | Só cache; null → Success(null).Cache only; null → Success(null).Solo caché; null → Success(null). |
getCachedLastSyncAt() | DateTime? | Timestamp para o DataLoadInfo.Timestamp for DataLoadInfo.Timestamp para DataLoadInfo. |
SaveAccountUpdateUseCase 1
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({visitSfid, newAccount}) | Result<AccountDataEntity, Failure> | Grava a edição no cache da visita (passo local pós-sucesso remoto).Writes the edit to the visit cache (local step after remote success).Graba la edición en el caché de la visita (paso local tras éxito remoto). |
BuildRetailerUploadDispatcherPayloadUseCase 1 · dispatcher1 · dispatcher1 · dispatcher
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
build({input: RetailerUploadDispatcherPayloadInput}) | DispatcherEnvelope | Monta o envelope RetailerUploadAPI a partir do account atual + novo, config e geo. Payload em 4 colunas: 06 · RetailerUploadAPI.Assembles the RetailerUploadAPI envelope from current + new account, config and geo. 4-column payload: 06 · RetailerUploadAPI.Arma el sobre RetailerUploadAPI desde el account actual + nuevo, config y geo. Payload en 4 columnas: 06 · RetailerUploadAPI. |
SubmitRetailerUploadUseCase 1 · submit
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
submit({envelope}) | Result<DispatcherAck, Failure> | Despacha via DispatcherOrchestrator.Dispatches via DispatcherOrchestrator.Despacha vía DispatcherOrchestrator. |
CreateAdhocVisitUseCase 1
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({accountSfid, geoLatitude?, geoLongitude?}) | Result<AdhocVisitResultEntity, Failure> | → repository.createAdhocVisit (RPC + merge). Ver ◆ Visita Ad Hoc.→ repository.createAdhocVisit (RPC + merge). See ◆ Ad Hoc visit.→ repository.createAdhocVisit (RPC + merge). Ver ◆ Visita Ad Hoc. |
BuildVisitUploadDispatcherPayloadUseCase · SubmitVisitUploadUseCase visita planejadaplanned visitvisita planificada
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
build({input: VisitUploadDispatcherPayloadInput}) | DispatcherEnvelope | Monta o envelope VisitUploadAPI (status scheduled) para a visita planejada criada na lista.Assembles the VisitUploadAPI envelope (scheduled status) for the planned visit created in the list.Arma el sobre VisitUploadAPI (estado scheduled) para la visita planificada creada en la lista. |
submit({envelope}) | Result<DispatcherAck, Failure> | Despacha via orchestrator.Dispatches via orchestrator.Despacha vía orchestrator. |
GetVisitsUseCase · GetEndMarketConfigurationUseCase · GetReferenceDataUseCase consumidos pelo detalheused by the detailusados por el detalle
| UseCase | Método usadoMethod usedMétodo usado | PapelRoleRol |
|---|---|---|
GetVisitsUseCase | getCachedBySfid · getCached · execute(remote) | a visita (fonte do account) + lastSyncAt; execute(remote) no refresh.the visit (account source) + lastSyncAt; execute(remote) on refresh.la visita (fuente del account) + lastSyncAt; execute(remote) en el refresh. |
GetEndMarketConfigurationUseCase | execute | accountEditionConfig (visibilidade/edição por campo) + retailerUploadConfig.accountEditionConfig (per-field visibility/editing) + retailerUploadConfig.accountEditionConfig (visibilidad/edición por campo) + retailerUploadConfig. |
GetReferenceDataUseCase | execute | categorias, classificações locais, subtipos e hierarquia geográfica (para os dropdowns de edição).categories, local classifications, subtypes and geo hierarchy (for the edit dropdowns).categorías, clasificaciones locales, subtipos y jerarquía geográfica (para los dropdowns de edición). |
Notifier & State
Três peças de estado: o RetailsNotifier (lista), o RetailDetailNotifier (detalhe, family por visitSfid) e o AdhocVisitCreation — este é um Provider cross-page (§33), não um Notifier de tela: keepAlive, family por accountSfid, para o progresso da criação sobreviver à navegação.Three state pieces: RetailsNotifier (list), RetailDetailNotifier (detail, family by visitSfid) and AdhocVisitCreation — the latter is a cross-page Provider (§33), not a screen Notifier: keepAlive, family by accountSfid, so creation progress survives navigation.Tres piezas de estado: RetailsNotifier (lista), RetailDetailNotifier (detalle, family por visitSfid) y AdhocVisitCreation — este es un Provider cross-page (§33), no un Notifier de pantalla: keepAlive, family por accountSfid, para que el progreso de creación sobreviva a la navegación.
Métodos — RetailsNotifierMethods — RetailsNotifierMétodos — RetailsNotifier
build() / _load({source = local}) private
Return Future<RetailsState>
Observa os UseCases e retorna _load(), que busca a lista (default cache) e monta retails + lastSyncAt.Watches the UseCases and returns _load(), which fetches the list (default cache) and assembles retails + lastSyncAt.Observa los UseCases y retorna _load(), que busca la lista (default caché) y arma retails + lastSyncAt.
refresh({source = remote}) pull-to-refresh
Return Future<void>
Recarrega via _load(remote) e preserva a searchQuery. Sem AsyncValue.loading.Reloads via _load(remote) and preserves searchQuery. No AsyncValue.loading.Recarga vía _load(remote) y preserva searchQuery. Sin AsyncValue.loading.
setSearchQuery({query})
Atualiza a busca e reseta visibleCount para 20.Updates the search and resets visibleCount to 20.Actualiza la búsqueda y resetea visibleCount a 20.
loadMore()
Incrementa visibleCount em 20 (paginação só visual).Increments visibleCount by 20 (visual-only pagination).Incrementa visibleCount en 20 (paginación solo visual).
createPlannedVisit({retail, plannedDate})
Return Future<Result<void, Failure>>
Monta o input, chama Build/SubmitVisitUpload (status scheduled) e devolve o resultado do Ack (a page mostra o aviso).Assembles the input, calls Build/SubmitVisitUpload (scheduled status) and returns the Ack result (the page shows the notice).Arma el input, llama Build/SubmitVisitUpload (estado scheduled) y devuelve el resultado del Ack (la page muestra el aviso).
State — RetailsStateState — RetailsStateState — RetailsState
RetailsState campos + gettersfields + getterscampos + getters
| campo | tipo | default |
|---|---|---|
retails | List<RetailEntity> | [] |
lastSyncAt | DateTime? | null |
searchQuery | String | "" |
visibleCount | int | 20 (kRetailsPageSize) |
Getters: filteredRetails (busca por nome/SAP/endereço), visibleRetails (recorte), totalFilteredRetails, hasMoreToLoad. Filtragem/paginação client-side.Getters: filteredRetails (search by name/SAP/address), visibleRetails (slice), totalFilteredRetails, hasMoreToLoad. Filtering/pagination client-side.Getters: filteredRetails (búsqueda por nombre/SAP/dirección), visibleRetails (recorte), totalFilteredRetails, hasMoreToLoad. Filtrado/paginación client-side.
Métodos — RetailDetailNotifierMethods — RetailDetailNotifierMétodos — RetailDetailNotifier
build({visitSfid}) / _load()
Busca em paralelo EMC + reference data + a visita (getCachedBySfid) + o container (lastSyncAt); monta o State com account=draftAccount=visit.accountData, os catálogos e os módulos visíveis.Fetches in parallel EMC + reference data + the visit (getCachedBySfid) + the container (lastSyncAt); builds the State with account=draftAccount=visit.accountData, the catalogs and the visible modules.Busca en paralelo EMC + reference data + la visita (getCachedBySfid) + el container (lastSyncAt); arma el State con account=draftAccount=visit.accountData, los catálogos y los módulos visibles.
refresh() pull-to-refresh
Faz getVisits(remote), recarrega via _load() e preserva editingSections/savingSections.Runs getVisits(remote), reloads via _load() and preserves editingSections/savingSections.Ejecuta getVisits(remote), recarga vía _load() y preserva editingSections/savingSections.
toggleEditMode({section}) · updateDraft({newDraft})
Liga/desliga o modo edição da seção (resetando o rascunho ao account) e atualiza o draftAccount conforme o usuário edita.Toggles the section's edit mode (resetting draft to account) and updates draftAccount as the user edits.Alterna el modo edición de la sección (reseteando el borrador a account) y actualiza draftAccount mientras el usuario edita.
saveAccountName / saveDraftSection / saveAddress / saveRegistrationRequest → envio→ submit→ envío
Return Future<Failure?>
Todos chamam o privado _submitAccountUpdate({section, newAccount, startDate}): marca saving, monta o RetailerUploadDispatcherPayloadInput (account novo + atual, resource, config, geo no caso de endereço, isRouteRequest no cadastro), despacha (remote) e, no Ack de sucesso, grava no cache via SaveAccountUpdate; em falha, restaura o draftAccount.All call private _submitAccountUpdate({section, newAccount, startDate}): marks saving, builds the RetailerUploadDispatcherPayloadInput (new + current account, resource, config, geo for address, isRouteRequest for registration), dispatches (remote) and, on success Ack, writes to cache via SaveAccountUpdate; on failure, restores draftAccount.Todos llaman al privado _submitAccountUpdate({section, newAccount, startDate}): marca saving, arma el RetailerUploadDispatcherPayloadInput (account nuevo + actual, resource, config, geo en dirección, isRouteRequest en el registro), despacha (remote) y, en el Ack de éxito, graba en caché vía SaveAccountUpdate; en fallo, restaura el draftAccount.
State — RetailDetailStateState — RetailDetailStateState — RetailDetailState
RetailDetailState campos + gettersfields + getterscampos + getters
| campo | tipo |
|---|---|
visitSfid | String |
account / draftAccount | AccountDataEntity |
availableCategories | List<CategoryForSale> |
localClassificationDefinitions | List<LocalClassificationDefinitionEntity> |
outletSubtypes | List<SfidNamePairEntity> |
geographicalHierarchy | GeographicalHierarchyEntity |
visibleModules | List<ModuleConfig> |
retailerUploadConfig | RetailerUploadConfig |
editingSections / savingSections | Set<RetailDetailSection> |
lastSyncAt | DateTime? |
Getters: isEditing/isSaving/hasChanges(section), isAnySaving, getModule/isModuleVisible/isModuleEditable/editModeFor(type), isFieldVisible/isFieldEditable(type,field), selectedOutletSubtype, selectedLocalClassificationDefinition/Option, availableStates, geoStateFor/geoCityFor.Getters: isEditing/isSaving/hasChanges(section), isAnySaving, getModule/isModuleVisible/isModuleEditable/editModeFor(type), isFieldVisible/isFieldEditable(type,field), selectedOutletSubtype, selectedLocalClassificationDefinition/Option, availableStates, geoStateFor/geoCityFor.Getters: isEditing/isSaving/hasChanges(section), isAnySaving, getModule/isModuleVisible/isModuleEditable/editModeFor(type), isFieldVisible/isFieldEditable(type,field), selectedOutletSubtype, selectedLocalClassificationDefinition/Option, availableStates, geoStateFor/geoCityFor.
Provider cross-page — AdhocVisitCreationCross-page Provider — AdhocVisitCreationProvider cross-page — AdhocVisitCreation
create({retailName, accountSapCustomerId}) keepAlive · family(accountSfid)
Guarda contra reentrância (creating); seta a fase creating; lê geo; chama CreateAdhocVisitUseCase.execute. Em sucesso: markAdhocVisitCreated, invalidate de retailsProvider e visitsProvider, fase completed e aviso verde "Visita ad hoc criada — {name} ({sap})" via AppRouter.navigatorKey. Em falha: fase failed + aviso de erro.Guards reentrancy (creating); sets creating phase; reads geo; calls CreateAdhocVisitUseCase.execute. On success: markAdhocVisitCreated, invalidate of retailsProvider and visitsProvider, completed phase and a green notice "Ad hoc visit created — {name} ({sap})" via AppRouter.navigatorKey. On failure: failed phase + error notice.Protege reentrancia (creating); setea la fase creating; lee geo; llama CreateAdhocVisitUseCase.execute. En éxito: markAdhocVisitCreated, invalidate de retailsProvider y visitsProvider, fase completed y un aviso verde "Visita ad hoc creada — {name} ({sap})" vía AppRouter.navigatorKey. En fallo: fase failed + aviso de error.
State (AdhocVisitCreationState): phase (AdhocVisitCreationPhase), retailName, accountSapCustomerId, failure.State (AdhocVisitCreationState): phase (AdhocVisitCreationPhase), retailName, accountSapCustomerId, failure.State (AdhocVisitCreationState): phase (AdhocVisitCreationPhase), retailName, accountSapCustomerId, failure.
Page e widgetsPage & widgetsPage y widgets
Lista — RetailsPageList — RetailsPageLista — RetailsPage
A RetailsPage (ConsumerWidget) observa o retailsProvider; loading e erro são globais (when). Os modais vivem sob o card que os abre:RetailsPage (ConsumerWidget) watches retailsProvider; loading and error are global (when). Modals live under the card that opens them:RetailsPage (ConsumerWidget) observa retailsProvider; loading y error son globales (when). Los modales viven bajo la tarjeta que los abre:
- RetailsPage
- AppPageShell displayBackButton · no drawer
- CustomLoadingIndicator loading
- FailureStateView error → invalidate
- _RetailsBody data · scroll → loadMore()
- RetailsHeaderWidget DataLoadInfo + título
- CustomInput busca → setSearchQuery
- RetailsInMyZoneSectionWidget
- CustomEmptyState vazio
- InfiniteScrollListView<RetailEntity> → loadMore()
- RetailCardWidget nome · SAP
- _PlannedBadge isPlanned… → CreatePlannedVisitModalContent (calendário) → createPlannedVisit
- _AdhocBadge ConsumerWidget · isAdhoc… → CreateAdhocVisitModalContent → AdhocVisitCreation.create · "Criando visita…"
- RetailCardWidget nome · SAP
- PaginationCountIndicator X de Y
- AppPageShell displayBackButton · no drawer
Detalhe — RetailDetailPageDetail — RetailDetailPageDetalle — RetailDetailPage
A RetailDetailPage (family por visitSfid) observa o retailDetailProvider. Cada seção é um widget self-checking (some se o módulo não é visível). A edição passa pelo VisitStartGuard; endereço e entrega abrem páginas próprias; nome abre um modal:RetailDetailPage (family by visitSfid) watches retailDetailProvider. Each section is a self-checking widget (hides if its module isn't visible). Editing goes through VisitStartGuard; address and delivery open their own pages; name opens a modal:RetailDetailPage (family por visitSfid) observa retailDetailProvider. Cada sección es un widget self-checking (se oculta si su módulo no es visible). La edición pasa por VisitStartGuard; dirección y entrega abren páginas propias; nombre abre un modal:
- RetailDetailPage
- AppPageShell displayBackButton
- CustomLoadingIndicator loading
- FailureStateView error → invalidate
- CustomPullToRefresh data → refresh()
- DataLoadInfo lastSyncAt
- AccountHeaderCard nome · SAP · overdue
- RetailDetailFieldSection ×N SAP · taxId · reg. estadual · nome (modal) · nome comercial · bandeira · key account · volume · merch · frequências · dia visita · lead time · crédito · dias crédito
- RetailDetailTextFieldModalContent modal · editar nome → saveAccountName
- RetailDetailEmployeesWidget → VisitStartGuard → goToManageStaff
- RetailDetailAddressSectionWidget EditMode.page → VisitStartGuard → goToEditAddress
- EditAddressPage rua · complemento · bairro · CEP · estado/cidade (dropdowns) · geo → saveAddress
- RetailDetailLocalClassificationWidget inline → saveDraftSection
- RetailDetailOperatingDaysWidget inline · dias + horários → saveDraftSection
- RetailDetailCategoriesWidget inline · CategoryForSale → saveDraftSection
- RetailDetailOutletSubtypeWidget inline → saveDraftSection
- RetailDetailDeliveryWidget retailPreferredDeliveryDay
- RetailDetailRegistrationRequestButtonWidget → VisitStartGuard → goToEditVisitDelivery
- EditVisitDeliveryPage freq. pedido/visita · dia visita/entrega · data início → saveRegistrationRequest (isRouteRequest)
- RetailDetailPaymentMethodsWidget read-only
- AppPageShell displayBackButton
Visita Ad HocAd Hoc visitVisita Ad Hoc
A criação de visita ad hoc é um comportamento da tela de Varejos (não tem tela própria), por isso é documentada aqui. O botão AD HOC do card cria uma visita imediata para um varejo da zona e traz, num só RPC, todo o pacote de dados daquele varejo — que é fundido nos caches locais.Ad hoc visit creation is a behavior of the Retails screen (it has no screen of its own), so it's documented here. The card's AD HOC button creates an immediate visit for a zone retail and brings, in a single RPC, that retail's whole data package — which is merged into the local caches.La creación de visita ad hoc es un comportamiento de la pantalla de Puntos de venta (no tiene pantalla propia), por eso se documenta aquí. El botón AD HOC de la tarjeta crea una visita inmediata para un punto de venta de la zona y trae, en un solo RPC, todo el paquete de datos de ese punto de venta — que se fusiona en los cachés locales.
Não passa pelo DispatcherNot via the DispatcherNo pasa por el Dispatcher
Diferente das edições e da visita planejada, o ad hoc não usa o Dispatcher (Ack-only): é um RPC read-style síncrono que já cria a visita no Salesforce e devolve os dados. Parte de username + accountSfid, não de locationHierarchySfid.
Unlike edits and the planned visit, ad hoc does not use the Dispatcher (Ack-only): it's a synchronous read-style RPC that already creates the visit in Salesforce and returns the data. It starts from username + accountSfid, not locationHierarchySfid.
A diferencia de las ediciones y la visita planificada, el ad hoc no usa el Dispatcher (Ack-only): es un RPC read-style síncrono que ya crea la visita en Salesforce y devuelve los datos. Parte de username + accountSfid, no de locationHierarchySfid.
Merge aditivo (crítico)Additive merge (critical)Merge aditivo (crítico)
O reply tem 11 estruturas (16 listas na Entity). O AdhocVisitMergeCoordinator aplica um merge aditivo e escopado ao account novo — nunca save*/clear* (que dariam wipe) — em 12 datasources locais, cada chamada isolada (best-effort). O lastSyncAt do root não é bumpado (só uma fatia foi atualizada). Duas famílias de merge:The reply has 11 structures (16 lists in the Entity). The AdhocVisitMergeCoordinator applies an additive, account-scoped merge — never save*/clear* (which would wipe) — into 12 local datasources, each call isolated (best-effort). The root lastSyncAt is not bumped (only a slice was updated). Two merge families:El reply tiene 11 estructuras (16 listas en la Entity). El AdhocVisitMergeCoordinator aplica un merge aditivo y acotado al account nuevo — nunca save*/clear* (que harían wipe) — en 12 datasources locales, cada llamada aislada (best-effort). El lastSyncAt del root no se bumpa (solo se actualizó una porción). Dos familias de merge:
| Datasource · métodoDatasource · methodDatasource · método | Família / chaveFamily / keyFamilia / clave |
|---|---|
visit · mergeAdhocVisits | A — item único por account (upsert no topo)A — one item per account (upsert on top)A — un ítem por account (upsert arriba) |
task · mergeAdhocTasks | A |
order · mergeAdhocOrders | A · Orders |
stockControl · mergeAdhocStockControl | A · por sfid/accountIdA · by sfid/accountIdA · por sfid/accountId |
financialManagement · mergeAdhocFinancialManagement | A · debitOpenItems/payments/creditNotes; banks global preservadoA · debitOpenItems/payments/creditNotes; banks kept globalA · debitOpenItems/payments/creditNotes; banks global preservado |
merchandisingImageAudits · mergeAdhocMerchandisingAudits | A · chave = accountCodeA · key = accountCodeA · clave = accountCode |
merchandisingServiceOrders · mergeAdhocMerchandisingServiceOrders | A |
surveys · mergeAdhocSurveys | B — compartilhado (anexa .accountData)B — shared (appends .accountData)B — compartido (anexa .accountData) |
promotion · mergeAdhocPromotionCatalog | B · .accountDataList + .orderChoiceListB · .accountDataList + .orderChoiceListB · .accountDataList + .orderChoiceList |
marginCalculator · mergeAdhocMarginCalculator | B · union em .accountSfidsB · union in .accountSfidsB · union en .accountSfids |
productCatalog · mergeAdhocProductCatalog | B · .soqByAccount + .salesHistoryB · .soqByAccount + .salesHistoryB · .soqByAccount + .salesHistory |
merchandisingAssets · mergeAdhocMerchandisingAssets | B · .itemsB · .itemsB · .items |
Pendências / roadmapPending / roadmapPendencias / roadmap
Ad hoc é online-only: o mock retorna vazio (validação contra o streambridge real; testes no futuro). merchandising.digitalContent fica fora do merge (sem entity/model → lista vazia + TODO). Algumas estruturas ainda chegam vazias do backend (documentadas em missing-data), mas o merge já está implementado completo. Na lista, tocar num card não navega para o detalhe (o handler é no-op hoje) — o detalhe abre só pela visita.
Ad hoc is online-only: the mock returns empty (validated against the real streambridge; tests later). merchandising.digitalContent is out of the merge (no entity/model → empty list + TODO). Some structures still arrive empty from the backend (tracked in missing-data), but the merge is already fully implemented. In the list, tapping a card does not navigate to the detail (the handler is a no-op today) — the detail opens only from the visit.
Ad hoc es online-only: el mock devuelve vacío (validación contra el streambridge real; tests a futuro). merchandising.digitalContent queda fuera del merge (sin entity/model → lista vacía + TODO). Algunas estructuras aún llegan vacías del backend (documentadas en missing-data), pero el merge ya está implementado completo. En la lista, tocar una tarjeta no navega al detalle (el handler es no-op hoy) — el detalle abre solo desde la visita.
Notas por mercadoMarket notesNotas por mercado
Varejos é dirigido por configuração de mercado (End Market Configuration): o item de menu retails, o accountEditionConfig (quais campos aparecem e como editam) e o retailerUploadConfig (o que a transação envia). Está habilitado em três mercados:Retails is driven by market configuration (End Market Configuration): the retails menu item, the accountEditionConfig (which fields show and how they edit) and the retailerUploadConfig (what the transaction sends). It's enabled in three markets:Puntos de venta se rige por configuración de mercado (End Market Configuration): el ítem de menú retails, el accountEditionConfig (qué campos aparecen y cómo editan) y el retailerUploadConfig (qué envía la transacción). Está habilitado en tres mercados:
HabilitadoEnabledHabilitado
O item retails está no menuConfig dos três, e cada um traz um accountEditionConfig. As transações da feature — RetailerUploadAPI (edição) e VisitUploadAPI (visita planejada) — têm enabledMarkets BR/CL/ZA; o RPC getAdhocVisit segue os mesmos mercados. Quais campos aparecem e como se editam (inline/modal/página) varia por mercado, pelo accountEditionConfig.
The retails item is in all three menuConfigs, and each carries an accountEditionConfig. The feature's transactions — RetailerUploadAPI (edit) and VisitUploadAPI (planned visit) — have BR/CL/ZA enabledMarkets; the getAdhocVisit RPC follows the same markets. Which fields show and how they edit (inline/modal/page) varies per market, via accountEditionConfig.
El ítem retails está en los tres menuConfig, y cada uno trae un accountEditionConfig. Las transacciones de la feature — RetailerUploadAPI (edición) y VisitUploadAPI (visita planificada) — tienen enabledMarkets BR/CL/ZA; el RPC getAdhocVisit sigue los mismos mercados. Qué campos aparecen y cómo se editan (inline/modal/página) varía por mercado, vía accountEditionConfig.
AR · PY · PE
Existem como mercados do app (config mínima PANGEA), mas não têm retails no menu nem accountEditionConfig — a feature não é alcançável. Documentos e moeda são formatados por mercado quando os campos financeiros aparecem (BR/CL estilo 1.234,56; ZA estilo 1,234.56).
They exist as app markets (PANGEA minimal config), but have no retails menu item nor accountEditionConfig — the feature is unreachable. Documents and currency are per-market when the financial fields show (BR/CL as 1.234,56; ZA as 1,234.56).
Existen como mercados de la app (config mínima PANGEA), pero no tienen el ítem retails ni accountEditionConfig — la feature no es alcanzable. Documentos y moneda se formatean por mercado cuando aparecen los campos financieros (BR/CL como 1.234,56; ZA como 1,234.56).