Lista de visitasVisit listLista de visitas
A agenda de visitas do representante de vendas: os varejos que ele precisa visitar, agrupados por tipo, com busca, setores, filtros e acesso ao detalhe. É também onde o app controla que só uma visita fica ativa por vez (check-in). Somente leitura da lista — o roteiro de dentro da visita vive no detalhe. The sales rep's visit agenda: the retails they need to visit, grouped by type, with search, sectors, filters and drill-down to detail. It's also where the app enforces that only one visit is active at a time (check-in). The list is read-only — the in-visit playbook lives in the detail. La agenda de visitas del representante de ventas: los puntos de venta que debe visitar, agrupados por tipo, con búsqueda, sectores, filtros y acceso al detalle. Es también donde la app controla que solo una visita esté activa a la vez (check-in). La lista es solo lectura — el guion dentro de la visita vive en el detalle.
O que é e para que serveWhat it is and what it's forQué es y para qué sirve
A Lista de visitas é a agenda do dia do representante de vendas: reúne os varejos que ele precisa atender sob a sua hierarquia de localização. Cada linha é um card de visita com os dados do varejo e indicadores. Responde três perguntas: The Visit list is the sales rep's daily agenda: it gathers the retails they must serve under their location hierarchy. Each row is a visit card with the retail's data and indicators. It answers three questions: La Lista de visitas es la agenda diaria del representante de ventas: reúne los puntos de venta que debe atender bajo su jerarquía de ubicación. Cada fila es una tarjeta de visita con los datos del punto de venta e indicadores. Responde tres preguntas:
Quem eu visito?Who do I visit?¿A quién visito?
Um card por visita, agrupado por presencial / digital, com nome, código e endereço do varejo.One card per visit, grouped by physical / digital, with the retail's name, code and address.Una tarjeta por visita, agrupada por presencial / digital, con nombre, código y dirección del punto de venta.
Qual está em andamento?Which one is in progress?¿Cuál está en curso?
O card da visita iniciada fica destacado e um botão flutuante leva direto a ela.The started visit's card is highlighted and a floating button jumps straight to it.La tarjeta de la visita iniciada queda destacada y un botón flotante lleva directo a ella.
O que fazer numa?What to do with one?¿Qué hacer en una?
Tocar num card abre o detalhe da visita, onde ficam tarefas, metas e o check-in.Tapping a card opens the visit detail, where tasks, targets and check-in live.Tocar una tarjeta abre el detalle de la visita, donde están tareas, metas y el check-in.
Escopo desta telaScope of this screenAlcance de esta pantalla Esta doc cobre a lista de visitas e o controle de visita única ativa. Tarefas, metas do dia, ferramentas e o roteiro completo de dentro da visita vivem no Detalhe da visita — a lista só navega até lá. This doc covers the visit list and the single-active-visit rule. Tasks, targets of the day, tools and the full in-visit playbook live in the Visit detail — the list only navigates there. Esta doc cubre la lista de visitas y la regla de visita única activa. Tareas, metas del día, herramientas y el guion completo dentro de la visita viven en el Detalle de la visita — la lista solo navega allí.
Como acessarHow to openCómo acceder
- Pela barra inferiorFrom the bottom barDesde la barra inferiorToque na aba Visitas na navegação inferior. É uma das telas base do app.Tap the Visits tab in the bottom navigation. It's one of the app's base screens.Toque la pestaña Visitas en la navegación inferior. Es una de las pantallas base de la app.
- A lista abreThe list opensLa lista abreComeça na aba Presencial (padrão). Puxe para baixo para atualizar.It starts on the Physical tab (default). Pull down to refresh.Empieza en la pestaña Presencial (por defecto). Deslice hacia abajo para actualizar.
- Visita em andamentoVisit in progressVisita en cursoSe você já iniciou uma visita, um botão flutuante "Em andamento" aparece — toque para rolar direto até o card dela.If you've already started a visit, an "In progress" floating button appears — tap it to scroll straight to its card.Si ya inició una visita, aparece un botón flotante "En curso" — tóquelo para desplazarse directo a su tarjeta.
Estrutura da telaScreen structureEstructura de la pantalla
Cada bloco abaixo aparece só quando o mercado o habilita (via End Market Configuration) — a tela é montada por configuração, não fixa.Each block below appears only when the market enables it (via End Market Configuration) — the screen is config-driven, not hardcoded.Cada bloque abajo aparece solo cuando el mercado lo habilita (vía End Market Configuration) — la pantalla se arma por configuración, no es fija.
- CabeçalhoHeaderEncabezado
- Título "Visitas" e a data da última sincronização dos dados."Visits" title and the last sync date of the data.Título "Visitas" y la fecha de última sincronización.
- AbasTabsPestañas
- Presencial / Digital (e opcionalmente Ambas). Filtram a lista pelo tipo de recurso da visita.Physical / Digital (and optionally Both). They filter the list by the visit's resource type.Presencial / Digital (y opcionalmente Ambas). Filtran la lista por el tipo de recurso de la visita.
- SetorizadorSectorizerSectorizador
- Chips com contador: Presencial, Digital, Duplicadas (mesmo varejo nos dois canais) e FDR (visita ad hoc).Chips with a counter: Physical, Digital, Duplicated (same retail in both channels) and FDR (ad hoc visit).Chips con contador: Presencial, Digital, Duplicadas (mismo punto de venta en ambos canales) y FDR (visita ad hoc).
- BuscaSearchBúsqueda
- Campo que filtra por nome, código, nome comercial e endereço do varejo.A field that filters by the retail's name, code, commercial name and address.Campo que filtra por nombre, código, nombre comercial y dirección del punto de venta.
- AçõesActionsAcciones
- Botões Ordenar e Filtrar (Filtrar aparece só onde há abas de filtro configuradas).Sort and Filter buttons (Filter shows only where filter tabs are configured).Botones Ordenar y Filtrar (Filtrar solo donde hay pestañas de filtro configuradas).
- CardsCardsTarjetas
- Um por visita, montado por seções configuráveis: dados do varejo, atividades, meta do dia, pilares comerciais e a linha inferior. O card da visita iniciada fica destacado.One per visit, assembled from configurable sections: retail data, activities, target of the day, commercial pillars and the bottom row. The started visit's card is highlighted.Una por visita, armada con secciones configurables: datos del punto de venta, actividades, meta del día, pilares comerciales y la fila inferior. La tarjeta de la visita iniciada queda destacada.
- Contador "X de Y""X of Y" counterContador "X de Y"
- Mostra quantas visitas estão visíveis do total filtrado; a lista carrega mais ao rolar.Shows how many visits are visible out of the filtered total; the list loads more as you scroll.Muestra cuántas visitas están visibles del total filtrado; la lista carga más al desplazar.
- Botão flutuanteFloating buttonBotón flotante
- "Em andamento" — só aparece quando existe uma visita iniciada; leva o scroll até o card dela."In progress" — appears only when a visit is started; scrolls to its card."En curso" — aparece solo cuando hay una visita iniciada; desplaza hasta su tarjeta.
Status e check-inStatus & check-inEstado y check-in
Cada visita tem um status, refletido na cor do avatar do card (a lista completa está na seção técnica Enums):Each visit has a status, shown by the card avatar's color (the full list is in the technical Enums section):Cada visita tiene un estado, reflejado en el color del avatar de la tarjeta (la lista completa está en la sección técnica Enums):
Só uma visita ativa por vezOne active visit at a timeSolo una visita activa a la vez Iniciar (check-in) e finalizar uma visita acontece no detalhe. A lista protege essa regra: se você já tem uma visita em andamento e toca em outro card, o app pergunta se quer finalizar a atual antes de abrir a nova. Ao confirmar, a visita atual é finalizada em segundo plano e o app avisa se o envio foi enviado, enfileirado (offline) ou falhou. Starting (check-in) and finishing a visit happen in the detail. The list guards this rule: if you already have a visit in progress and tap another card, the app asks whether to finish the current one before opening the new. On confirm, the current visit is finished in the background and the app reports whether the upload was sent, queued (offline) or failed. Iniciar (check-in) y finalizar una visita ocurren en el detalle. La lista protege esa regla: si ya tiene una visita en curso y toca otra tarjeta, la app pregunta si desea finalizar la actual antes de abrir la nueva. Al confirmar, la visita actual se finaliza en segundo plano y la app avisa si el envío fue enviado, encolado (offline) o falló.
Buscar, setorizar, filtrar e ordenarSearch, sector, filter and sortBuscar, sectorizar, filtrar y ordenar
BuscaSearchBúsqueda
Filtra enquanto você digita por nome, nome comercial, código do varejo e pelos campos de endereço (rua, bairro, cidade, estado, CEP). Vários termos separados por espaço precisam todos aparecer.Filters as you type by name, commercial name, retail code and address fields (street, neighborhood, city, state, postal code). Multiple space-separated terms must all match.Filtra mientras escribe por nombre, nombre comercial, código del punto de venta y campos de dirección (calle, barrio, ciudad, estado, código postal). Varios términos separados por espacio deben coincidir todos.
SetoresSectorsSectores
Chips que restringem a lista: Presencial, Digital, Duplicadas (o mesmo varejo aparece nos dois canais) e FDR (visitas ad hoc). Tocar no setor ativo o desmarca.Chips that narrow the list: Physical, Digital, Duplicated (same retail appears in both channels) and FDR (ad hoc visits). Tapping the active sector clears it.Chips que acotan la lista: Presencial, Digital, Duplicadas (el mismo punto de venta aparece en ambos canales) y FDR (visitas ad hoc). Tocar el sector activo lo desmarca.
FiltrosFiltersFiltros
O modal de filtros é montado por mercado: cada aba de filtro e cada critério (ex.: status do varejo, situação financeira, tipo de visita, visita planejada) vêm do End Market Configuration. Cada filtro cruza os códigos de filtro que a própria visita traz. Valem após tocar em Aplicar.The filter modal is built per market: each filter tab and criterion (e.g. retail status, financial status, visit type, planned visit) comes from End Market Configuration. Each filter matches the filter codes carried by the visit itself. They apply after you tap Apply.El modal de filtros se arma por mercado: cada pestaña de filtro y criterio (ej.: estado del punto de venta, situación financiera, tipo de visita, visita planificada) viene del End Market Configuration. Cada filtro cruza los códigos de filtro que trae la propia visita. Valen tras tocar Aplicar.
OrdenaçãoSortOrdenación
Três opções: ordem de rota (padrão, mantém a ordem que veio do backend), nome do varejo e status (por progressão da visita).Three options: route order (default, keeps the backend order), retail name and status (by visit progression).Tres opciones: orden de ruta (por defecto, mantiene el orden del backend), nombre del punto de venta y estado (por progresión de la visita).
Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos
Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. A lista é somente leitura: um único RPC (getVisitList) traz a visita completa, com cache write-through (todo fetch grava no ObjectBox). O check-in (iniciar/finalizar) é um caminho separado: grava o status localmente e despacha a transação VisitUploadAPI.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. The list is read-only: a single RPC (getVisitList) returns the full visit, with cache write-through (every fetch writes to ObjectBox). Check-in (start/finish) is a separate path: it writes the status locally and dispatches the VisitUploadAPI transaction.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. La lista es solo lectura: un único RPC (getVisitList) trae la visita completa, con cache write-through (todo fetch graba en ObjectBox). El check-in (iniciar/finalizar) es un camino aparte: graba el estado localmente y despacha la transacción VisitUploadAPI.
Leitura da listaReading the listLectura de la lista
- VisitReplygRPC proto
- toVisitsDTOVisitsDTODTO · Freezed
- toDomainVisitsEntitydomain
- toModelVisitsModelObjectBox
- toDomainVisitsEntitydomain · cache
- watchVisitsNotifier + State
- → UIVisitsPage
- watchVisitsNotifier + State
- toDomainVisitsEntitydomain · cache
- toModelVisitsModelObjectBox
- toDomainVisitsEntitydomain
- toVisitsDTOVisitsDTODTO · Freezed
Check-in (iniciar / finalizar)Check-in (start / finish)Check-in (iniciar / finalizar)
Orquestrado pelo provider compartilhado VisitContext (acionado sobretudo pelo Detalhe da visita; a lista dispara apenas o finalizar, via guarda de visita única):Orchestrated by the shared VisitContext provider (triggered mainly by the Visit detail; the list only triggers finish, via the single-visit guard):Orquestado por el provider compartido VisitContext (accionado sobre todo por el Detalle de la visita; la lista dispara solo el finalizar, vía la guarda de visita única):
- VisitContext.start/endVisitshared provider
- saveVisitStatusObjectBoxstatus local (startedAt/endedAt)
- build(input)BuildVisitUploadDispatcherPayloadUseCase
- submitSubmitVisitUploadUseCaseDispatcher
- VisitUploadAPIsent · queued · failed
- submitSubmitVisitUploadUseCaseDispatcher
- build(input)BuildVisitUploadDispatcherPayloadUseCase
- saveVisitStatusObjectBoxstatus local (startedAt/endedAt)
Cross-linksCross-linksCross-links O payload e o contrato do envio estão em Transação · VisitUploadAPI. As telas dentro da visita, em Detalhe da visita. The upload payload and contract live in Transaction · VisitUploadAPI. The in-visit screens, in Visit detail. El payload y el contrato del envío están en Transacción · VisitUploadAPI. Las pantallas dentro de la visita, en Detalle de la visita.
Modelo de dadosData modelModelo de datos
A mesma visita existe em quatro representações quase idênticas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (domínio) — e cada fronteira é atravessada por um mapper. Os nomes dos campos se mantêm; muda pouco (enums tipados na Entity, relações no Model, e dois campos de check-in que só existem local). O fetch é write-through: todo retorno grava no ObjectBox e a UI passa a ler do cache.The same visit exists in four near-identical representations — Proto (gRPC wire) → DTO (Freezed) → Model (ObjectBox) → Entity (domain) — and each boundary is crossed by a mapper. Field names stay the same; little changes (enums typed in the Entity, relations in the Model, and two check-in fields that exist only locally). Fetch is write-through: every response is written to ObjectBox and the UI reads from cache.La misma visita existe en cuatro representaciones casi idénticas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (dominio) — y cada frontera se cruza con un mapper. Los nombres se mantienen; cambia poco (enums tipados en la Entity, relaciones en el Model, y dos campos de check-in que solo existen local). El fetch es write-through: toda respuesta se graba en ObjectBox y la UI lee del caché.
A lista chega num container VisitsEntity (lastSyncAt gerado no mapper + visits[]); cada item é um Visit de 26 campos de wire, com muitas sub-estruturas aninhadas. Os enums só existem tipados na Entity; em Proto/DTO/Model trafegam como String. A seguir, na ordem: o proto, as estruturas de dados campo-a-campo, e os mappers.The list arrives in a VisitsEntity container (lastSyncAt generated in the mapper + visits[]); each item is a 26-wire-field Visit with many nested sub-structures. Enums are only typed in the Entity; in Proto/DTO/Model they travel as String. Next, in order: the proto, the field-by-field data structures, and the mappers.La lista llega en un container VisitsEntity (lastSyncAt generado en el mapper + visits[]); cada ítem es un Visit de 26 campos de wire, con muchas sub-estructuras anidadas. Los enums solo están tipados en la Entity; en Proto/DTO/Model viajan como String. A continuación, en orden: el proto, las estructuras de datos campo a campo, y los mappers.
Proto
VisitConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge (importa KpiConectaRep e CommonConectaRep). Um serviço (VisitConectaRepService), um método unário:One service (VisitConectaRepService), a single unary method:Un servicio (VisitConectaRepService), un método unario:
getVisitListunaryrpc getVisitList(VisitRequest) returns (VisitReply)
path /mn.bat.conectarep.streambridge.VisitConectaRepService/getVisitList
VisitRequestlocationHierarchySfidstring· #1 · hierarquia do representante de vendas (resolvida no repository)sales rep hierarchy (resolved in the repository)jerarquía del representante de ventas (resuelta en el repository)dateReferencestring· #2 · optionallastModifiedDatestring· #3 · optional (não usado hoje)optional (not used today)optional (no usado hoy)
VisitReplyrepeated Visit visitList — a lista de visitas. Os campos de Visit estão detalhados nas Estruturas de dados abaixo.the list of visits. Visit's fields are detailed in Data structures below.la lista de visitas. Los campos de Visit están detallados en Estructuras de datos abajo.
Estruturas de dadosData structuresEstructuras de datos
Colunas Proto · DTO · Model · Entity, uma linha por campo, tipo repetido nas 4 colunas. O delta (texto azul) marca onde o tipo primeiro muda: relação ToMany/ToOne no Model, enum na Entity, parse de data no Model. ¹ = optional no proto. startedAt/endedAt só existem no Model/Entity (gravados no check-in local).Columns Proto · DTO · Model · Entity, one row per field, type repeated across the 4 columns. The delta (blue text) marks where the type first changes: ToMany/ToOne relation in the Model, enum in the Entity, date parse in the Model. ¹ = optional in the proto. startedAt/endedAt exist only in Model/Entity (written on local check-in).Columnas Proto · DTO · Model · Entity, una fila por campo, tipo repetido en las 4 columnas. El delta (texto azul) marca dónde primero cambia el tipo: relación ToMany/ToOne en el Model, enum en la Entity, parse de fecha en el Model. ¹ = optional en el proto. startedAt/endedAt solo existen en Model/Entity (grabados en el check-in local).
Visit raiz 26 + 2 camposfieldscampos
Campo Proto DTO Model Entity sfidstring String String String visitResourceTypestring String String ResourceTypeaccountDataAccount …DTO ToOne<…Model>AccountDataEntity statusstring String String VisitStatusisTelesalesbool bool bool bool previousVisitDatestring String? String? String? nextVisitDatestring String? String? String? hasSmartInvestmentbool¹ bool bool bool hasDeliverybool¹ bool bool bool labelsrepeated Label List<…DTO> ToMany<…Model>List<…Entity> kpisrepeated KpiInfo List<…DTO> ToMany<…Model>List<VisitKpiEntity> sellableCategoriesIndicatorrepeated CategoryIndicator List<…DTO> ToMany<…Model>List<…Entity> salesCategoriesIndicatorrepeated SalesCategoryIndicator List<…DTO> ToMany<…Model>List<…Entity> commercialPillarsrepeated PillarsKpi List<…DTO> ToMany<…Model>List<CommercialPillarEntity> filtersFilter …DTO? ToOne<…Model>VisitFiltersEntity? monthlyVolumerepeated MonthlyVolume List<…DTO> ToMany<…Model>List<…Entity> monthlyTargetMonthlyTarget¹ …DTO? ToOne<…Model>…Entity? targetsOfTheDayrepeated TargetOfTheDay List<…DTO> ToMany<…Model>List<…Entity> deliveryTrackingrepeated DeliveryTracking List<…DTO> ToMany<…Model>List<…Entity> dashboardsDashboards …DTO? ToOne<…Model>VisitDashboardsEntity? competitorChecksrepeated CompetitorCheck List<…DTO> ToMany<…Model>List<…Entity> shelfWatchShelfWatch¹ …DTO? ToOne<…Model>…Entity? typestring String String String visitDatestring String? String? String? visitResourcestring String String String boostPlanScriptstring¹ String? String? String? startedAt— — DateTime?DateTime? endedAt— — DateTime?DateTime? AccountData Visit.accountData · proto Account 42 camposfieldscampos
Campo Proto DTO Model Entity sfidstring String String String namestring String String String customerCodestring String String String addressAddress …DTO? ToOne<…Model>AddressEntity? taxCodestring String? String? String? stateRegistrationstring String? String? String? commercialNamestring String? String? String? totalStaffint32 int? int? int? contactNamestring String? String? String? contactPhonestring String? String? String? latitudedouble double? double? double? longitudedouble double? double? double? operatingDaysrepeated string List<String> List<String> List<String> openingTimestring String? String? String? closingTimestring String? String? String? categoriesSoldrepeated string List<String> List<String> List<CategoryForSale>outletSubtypeSfidNamePair …DTO? ToOne<…Model>SfidNamePairEntity? bannerNamestring String? String? String? keyAccountTypestring String? String? String? volumeRangestring String? String? String? merchandisingClassstring String? String? String? localClassificationLocalClassification …DTO? ToOne<…Model>…Entity? routeInfoRouteInfo …DTO? ToOne<…Model>RouteInfoEntity? supplierDataSupplier¹ …DTO? ToOne<…Model>SupplierDataEntity? creditLimitdouble double? double? double? baseCreditLimitdouble double? double? double? creditDaysint32 int? int? int? paymentMethodsrepeated string List<String> List<String> List<String> overdueAmountdouble double? double? double? statusAccount → statusstring String String String isB2Bbool bool? bool? bool? isOverduebool bool? bool? bool? isBlockedToSalesbool bool? bool? bool? hasCompetitionbool bool? bool? bool? hasIllegalProductsbool bool? bool? bool? deactivationDatestring String? String? String? reactivationDatestring String? String? String? girostring String? String? String? canalstring String? String? String? faxstring String? String? String? distributorBadDebtPendingbool bool bool bool staffStaff …DTO ToOne<…Model>StaffEntity Address AccountData.address 7 camposfieldscampos
Campo Proto DTO Model Entity fullAddressstring String? String? String? streetstring String? String? String? streetComplementstring String? String? String? neighborhoodstring String? String? String? postalCodestring String? String? String? citySfidNamePair …DTO? ToOne<…Model>SfidNamePairEntity? stateSfidNamePair …DTO? ToOne<…Model>SfidNamePairEntity?
Label Visit.labels[] 3 camposfieldscampos
Campo Proto DTO Model Entity labelstring String String String backgroundColorstring String String String textColorstring String String String VisitKpi Visit.kpis[] · proto KpiInfo 2 camposfieldscampos
Campo Proto DTO Model Entity kpiNamestring String String String valuedouble double double double CategoryIndicator Visit.sellableCategoriesIndicator[] 2 camposfieldscampos
Campo Proto DTO Model Entity categoryNamestring String String String hasTargetbool bool bool bool SalesCategoryIndicator Visit.salesCategoriesIndicator[] 2 camposfieldscampos
Campo Proto DTO Model Entity categoryNamestring String String String statusstring String String String CommercialPillar Visit.commercialPillars[] · proto PillarsKpi 6 camposfieldscampos
Campo Proto DTO Model Entity kpiNamestring String String String targetdouble double double double realizeddouble double double double differencedouble double double double percentagedouble double double double isAchievedbool¹ bool? bool? bool? Filter Visit.filters 2 listaslistslistas
Campo Proto DTO Model Entity normalFiltersrepeated FilterInfo List<…DTO> ToMany<…Model>List<FilterItemEntity> commercialPillarsFilterrepeated FilterInfo List<…DTO> ToMany<…Model>List<FilterItemEntity> FilterInfo Filter.*[] · = FilterItemEntity 2 camposfieldscampos
Campo Proto DTO Model Entity filterCodestring String String String valuesrepeated string List<String> List<String> List<String>
MonthlyVolume Visit.monthlyVolume[] 5 camposfieldscampos
Campo Proto DTO Model Entity categorystring String String String realizeddouble double double double targetdouble double double double differencedouble double double double percentagedouble double double double TargetOfTheDay Visit.targetsOfTheDay[] 6 + details
Campo Proto DTO Model Entity categorystring String String String realizeddouble double double double targetdouble double double double differencedouble double double double percentagedouble double double double unitOfMeasurementstring¹ String? String? String? detailsrepeated TargetDetail List<…DTO> ToMany<…Model>List<TargetDetailEntity> TargetDetail TargetOfTheDay.details[] 5 camposfieldscampos
Campo Proto DTO Model Entity detailNamestring String String String realizeddouble double double double targetdouble double double double differencedouble double double double percentagedouble double double double
Estruturas só do detalheDetail-only structuresEstructuras solo del detalle
O Visit ainda carrega sub-árvores que a lista não renderiza — MonthlyTarget (categorias → itens → períodos), os 7 Dashboards (campaign, partnership, conectaVoce, conectaNegocios, conectaAMPM, boostPlan, smartInvestment), DeliveryTracking, CompetitorCheck, ShelfWatch e AccountData.staff (contacts/clerks). São wire da mesma resposta, mas consumidas na tela de detalhe — documentadas lá campo-a-campo.
The Visit also carries sub-trees the list doesn't render — MonthlyTarget (categories → items → periods), the 7 Dashboards (campaign, partnership, conectaVoce, conectaNegocios, conectaAMPM, boostPlan, smartInvestment), DeliveryTracking, CompetitorCheck, ShelfWatch and AccountData.staff (contacts/clerks). They're wire of the same response, but consumed on the detail screen — documented there field-by-field.
El Visit también carga sub-árboles que la lista no renderiza — MonthlyTarget (categorías → ítems → períodos), los 7 Dashboards (campaign, partnership, conectaVoce, conectaNegocios, conectaAMPM, boostPlan, smartInvestment), DeliveryTracking, CompetitorCheck, ShelfWatch y AccountData.staff (contacts/clerks). Son wire de la misma respuesta, pero se consumen en la pantalla de detalle — documentados allí campo a campo.
Mappers
As conversões entre as camadas, todas como extension (5 direções por tipo):The conversions between layers, all as extensions (5 directions per type):Las conversiones entre capas, todas como extension (5 direcciones por tipo):
| DireçãoDirectionDirección | MétodoMethodMétodo |
|---|---|
| JSON → DTO | static fromMap(Map) |
| Proto → DTO | toDTO() |
| DTO → Entity | toDomain() (resolve enums: ResourceType.fromString, VisitStatus.fromString…)(resolves enums: ResourceType.fromString, VisitStatus.fromString…)(resuelve enums: ResourceType.fromString, VisitStatus.fromString…) |
| Entity → Model | toModel() (enums → .value; popula ToMany/ToOne; carrega startedAt/endedAt)(enums → .value; fills relations; carries startedAt/endedAt)(enums → .value; llena relaciones; carga startedAt/endedAt) |
| Model → Entity | toDomain() |
Os únicos deltasThe only deltasLos únicos deltas
visitResourceType→ResourceType,status→VisitStatustipados só na Entitytyped only in the Entitytipados solo en la EntityAccountData.categoriesSoldList<String>→List<CategoryForSale>na Entityin the Entityen la EntitystartedAt/endedAtsó no Model/Entity — gravados no check-in, ausentes no wireModel/Entity only — written on check-in, absent from the wiresolo en Model/Entity — grabados en el check-in, ausentes del wire- relações viram
ToMany/ToOneno Modelrelations becomeToMany/ToOnein the Modelrelaciones pasan aToMany/ToOneen el Model typevazio no proto vira"unknown"já no DTOempty in the proto becomes"unknown"at the DTOvacío en el proto pasa a"unknown"ya en el DTOlastSyncAtgerado no mapper comDateTimeUtils.now()generated in the mapper withDateTimeUtils.now()generado en el mapper conDateTimeUtils.now()
Repository
VisitRepositoryImpl implementaimplementsimplementa VisitRepositoryInterface e injeta os 3 datasources (mock/local/remote) + ConnectivityService + a flag useMockData + Ref. Método a método:and injects the 3 datasources (mock/local/remote) + ConnectivityService + the useMockData flag + Ref. Method by method:e inyecta los 3 datasources (mock/local/remote) + ConnectivityService + la flag useMockData + Ref. Método a método:
getVisits({source}) mock / local / remote
RetornaReturnsDevuelve Result<VisitsEntity?, Failure>
Ponto de entrada da lista: decide a fonte pela source + flags, mapeia e grava no cache (write-through). Chamado pelo VisitsNotifier.List entry point: picks the source from source + flags, maps and writes to cache (write-through). Called by VisitsNotifier.Punto de entrada de la lista: elige la fuente por source + flags, mapea y graba en caché (write-through). Llamado por VisitsNotifier.
Árvore de decisão de fonteSource decision treeÁrbol de decisión de fuente
useMockData== true ouorosource == mock→_fetchFromMock(): lê o mock, mapeia, grava no cache. A flag global tem precedência máxima.→_fetchFromMock(): reads the mock, maps, writes to cache. The global flag has top precedence.→_fetchFromMock(): lee el mock, mapea, graba en caché. La flag global tiene máxima precedencia.source == localou offlineor offlineu offline→getCachedVisits()(sem rede).→getCachedVisits()(no network).→getCachedVisits()(sin red).- senão (remoto + conectado)otherwise (remote + connected)si no (remoto + conectado)→
_fetchFromRemoteWithFallback(): lêcurrentResourceProvider; senullcai pro cache; senão chama o remoto comresource.locationHierarchyId, mapeia, grava no cache; em erro, fallback pro cache.→_fetchFromRemoteWithFallback(): readscurrentResourceProvider; ifnullfalls back to cache; else calls remote withresource.locationHierarchyId, maps, writes to cache; on error, falls back to cache.→_fetchFromRemoteWithFallback(): leecurrentResourceProvider; sinullcae al caché; si no llama al remoto conresource.locationHierarchyId, mapea, graba en caché; en error, fallback al caché.
getCachedVisits() local
RetornaReturnsDevuelve Result<VisitsEntity?, Failure>
Só cache. null vira Success(null), não erro — quem chama trata "sem dados" sem falha.Cache only. null becomes Success(null), not an error — callers handle "no data" without a failure.Solo caché. null es Success(null), no error — quien llama trata "sin datos" sin fallo.
getCachedVisitsLastSyncAt() local
RetornaReturnsDevuelve DateTime? (sem Result)(no Result)(sin Result)
Timestamp da última sincronização do container, para o DataLoadInfo.The container's last-sync timestamp, for DataLoadInfo.Timestamp de última sincronización del container, para DataLoadInfo.
getCachedVisitBySfid({visitSfid}) local
RetornaReturnsDevuelve Result<VisitEntity, Failure>
Busca 1 visita no cache pelo sfid; ausente → Error(CacheFailure). Alimenta o Visit Detail (§28 cat. A) — nunca dispara remoto.Fetches 1 visit from cache by sfid; missing → Error(CacheFailure). Feeds Visit Detail (§28 cat. A) — never triggers remote.Busca 1 visita en caché por sfid; ausente → Error(CacheFailure). Alimenta el Visit Detail (§28 cat. A) — nunca dispara remoto.
getCachedVisitByAccountSfid({accountSfid}) local
RetornaReturnsDevuelve Result<VisitEntity?, Failure>
Varre o cache e devolve a primeira visita cujo accountData.sfid bate; sem match → Success(null). Usado por quem parte do varejo (ex.: retail detail) para achar a visita.Scans the cache and returns the first visit whose accountData.sfid matches; no match → Success(null). Used by callers starting from the retail (e.g. retail detail) to find the visit.Recorre el caché y devuelve la primera visita cuyo accountData.sfid coincide; sin match → Success(null). Usado por quien parte del punto de venta (ej.: retail detail) para hallar la visita.
saveVisits({entity}) local
RetornaReturnsDevuelve Result<void, Failure>
Destrutivo: limpa e regrava tudo (cascata das boxes filhas), reaplicando o progresso client-side (status/started/ended da visita iniciada) por cima do dado fresco. É o cache-writer chamado após cada fetch bem-sucedido.Destructive: clears and rewrites everything (child boxes cascade), re-applying client-side progress (started visit's status/started/ended) on top of the fresh data. It's the cache-writer called after each successful fetch.Destructivo: limpia y regraba todo (cascada de boxes hijas), reaplicando el progreso client-side (status/started/ended de la visita iniciada) sobre el dato fresco. Es el cache-writer llamado tras cada fetch exitoso.
saveVisitStatus({visitSfid, status, startedAt?, endedAt?}) local · check-inlocal · check-inlocal · check-in
RetornaReturnsDevuelve Result<void, Failure>
Grava o novo status da visita somente no cache local (updateVisitStatusInVisit), sem rede. O envio remoto do check-in é separado, feito pelo VisitContext via VisitUploadAPI (ver seção 06).Writes the visit's new status to the local cache only (updateVisitStatusInVisit), no network. The remote check-in upload is separate, done by VisitContext via VisitUploadAPI (see section 06).Graba el nuevo estado de la visita solo en el caché local (updateVisitStatusInVisit), sin red. El envío remoto del check-in es separado, hecho por VisitContext vía VisitUploadAPI (ver sección 06).
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 VisitRemoteDataSource gRPC
getVisits({locationHierarchySfid, dateReference?})
- EnvioSendsEnvío
- monta
VisitRequeste chama_client.getVisitList(request)noVisitConectaRepServiceClient(viavisitServiceClientProvider).buildsVisitRequestand calls_client.getVisitList(request)onVisitConectaRepServiceClient(viavisitServiceClientProvider).armaVisitRequesty llama_client.getVisitList(request)enVisitConectaRepServiceClient(víavisitServiceClientProvider). - RetornoReturnRetorno
VisitsDTO(viaresponse.toVisitsDTO())(viaresponse.toVisitsDTO())(víaresponse.toVisitsDTO())- Fluxo de usoUsage flowFlujo de uso
- chamado pelo caminho remoto do repository (
_fetchFromRemoteWithFallback), quando online e sem mock; o resultado é gravado no cache.called by the repository's remote path (_fetchFromRemoteWithFallback), when online and not mocking; the result is written to cache.llamado por el camino remoto del repository (_fetchFromRemoteWithFallback), online y sin mock; el resultado se graba en caché. - Tratamento de erroError handlingManejo de errores
GrpcError→GrpcExceptionHandler; outros →ServerException. Em erro, o repository faz fallback pro cache.GrpcError→GrpcExceptionHandler; others →ServerException. On error, the repository falls back to cache.GrpcError→GrpcExceptionHandler; otros →ServerException. En error, el repository hace fallback al caché.
Local VisitLocalDataSource ObjectBox
Envio / fluxo: persistência local via ObjectBox (boxes VisitsModel e VisitModel) — sem rede. Alimenta os caminhos cache do repository. Erro: falhas de persistência propagam como exceção (não engolidas). Além dos métodos da lista abaixo, expõe mutações usadas pelo detalhe/staff (updateAccountInVisit, upsert/removeContactInVisit, upsertClerkInVisit, mergeAdhocVisits).Sends / flow: local persistence via ObjectBox (VisitsModel and VisitModel boxes) — no network. Feeds the repository's cache paths. Error: persistence failures propagate as exceptions (not swallowed). Beyond the list methods below, it exposes mutations used by detail/staff (updateAccountInVisit, upsert/removeContactInVisit, upsertClerkInVisit, mergeAdhocVisits).Envío / flujo: persistencia local vía ObjectBox (boxes VisitsModel y VisitModel) — sin red. Alimenta los caminos caché del repository. Error: fallos de persistencia propagan como excepción (no tragados). Además de los métodos de la lista abajo, expone mutaciones usadas por el detalle/staff (updateAccountInVisit, upsert/removeContactInVisit, upsertClerkInVisit, mergeAdhocVisits).
getVisits()
- RetornoReturnRetorno
VisitsEntity?- ComportamentoBehaviorComportamiento
- o agregado único do cache, ou
nullse vazio.the single cached aggregate, ornullif empty.el agregado único del caché, onullsi está vacío.
getVisitsLastSyncAt()
- RetornoReturnRetorno
DateTime?- ComportamentoBehaviorComportamiento
- timestamp da última sync do container.the container's last-sync timestamp.timestamp de última sincronización del container.
getVisitBySfid({visitSfid})
- RetornoReturnRetorno
VisitEntity?- ComportamentoBehaviorComportamiento
- busca 1 visita pelo
sfid. É o método que alimenta o Visit Detail.fetches 1 visit bysfid. This is the method feeding Visit Detail.busca 1 visita porsfid. Es el método que alimenta el Visit Detail.
saveVisits({entity})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- destrutivo: limpa e regrava tudo, reaplicando o progresso client-side da visita iniciada. Cache-writer após cada fetch.destructive: clears and rewrites everything, re-applying the started visit's client-side progress. Cache-writer after each fetch.destructivo: limpia y regraba todo, reaplicando el progreso client-side de la visita iniciada. Cache-writer tras cada fetch.
updateVisitStatusInVisit({visitSfid, status, startedAt?, endedAt?})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- grava status +
startedAt/endedAtda visita no cache (o dado de check-in). Chamado pelosaveVisitStatus.writes the visit's status +startedAt/endedAtto the cache (the check-in data). Called bysaveVisitStatus.graba status +startedAt/endedAtde la visita en el caché (el dato de check-in). Llamado porsaveVisitStatus.
clearVisits()
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- limpa todas as boxes na ordem filhas→raízes.clears all boxes children→roots.limpia todas las boxes hijas→raíces.
Mock VisitMockDataSource JSON
getVisits()
- EnvioSendsEnvío
- carrega o asset JSON
visits/visits(por mercado, real vs sintético viauseRealMockData) — sem rede.loads the JSON assetvisits/visits(per market, real vs synthetic viauseRealMockData) — no network.carga el asset JSONvisits/visits(por mercado, real vs sintético víauseRealMockData) — sin red. - RetornoReturnRetorno
VisitsDTO(viaVisitsDTOJsonMapper.fromMap)(viaVisitsDTOJsonMapper.fromMap)(víaVisitsDTOJsonMapper.fromMap)- Fluxo de usoUsage flowFlujo de uso
- usado quando
useMockDataestá ligado ousource == mock; grava no cache como um fetch normal.used whenuseMockDatais on orsource == mock; writes to cache like a normal fetch.usado cuandouseMockDataestá activo osource == mock; graba en caché como un fetch normal. - Tratamento de erroError handlingManejo de errores
- asset ausente ou JSON inválido →
CacheException(sem rede envolvida).missing asset or invalid JSON →CacheException(no network involved).asset ausente o JSON inválido →CacheException(sin red involucrada).
Enums e labelsEnums & labelsEnums y labels
Os enums só existem tipados na camada Entity; em DTO/Model/Proto trafegam como String. Enums de wire (status, tipos) são exibidos crus, sem tradução (decisão de produto). Lista completa dos enums da lista:Enums are only typed in the Entity layer; in DTO/Model/Proto they travel as String. Wire enums (status, types) are shown raw, untranslated (product decision). Full list of the list's enums:Los enums solo están tipados en la Entity; en DTO/Model/Proto viajan como String. Los enums de wire (estado, tipos) se muestran crudos, sin traducir (decisión de producto). Lista completa de los enums de la lista:
VisitStatus 6 · cor do avatar6 · avatar color6 · color del avatar
| case | value | cor avataravatar colorcolor avatar |
|---|---|---|
scheduled | "scheduled" | divider |
notStarted | "not_started" | divider |
started | "started" | warning |
completed | "completed" | success |
cancelled | "cancelled" | error |
unknown | "unknown" | divider |
ResourceType 4
| case | value | itemsitemsitems |
|---|---|---|
physical | "physical" | preSalesRep · promptSalesRep · universalRep · deliveryRep |
digital | "digital" | webAgentDirect |
telesales | "telesales" | telesalesAnalyst |
unknown | "unknown" | — |
VisitType 3
| case | value |
|---|---|
planned | "planned" |
adhoc | "adhoc" |
unknown | "unknown" |
VisitSector 4
| case | value | notanotenota |
|---|---|---|
physical | "physical" | recurso presencialphysical resourcerecurso presencial |
digital | "digital" | digital + telesalesdigital + telesalesdigital + telesales |
duplicated | "duplicated" | varejo nos dois canaisretail in both channelspunto de venta en ambos canales |
fdr | "fdr" | visita ad hoc (type == adhoc)ad hoc visit (type == adhoc)visita ad hoc (type == adhoc) |
VisitSort 3
| case | notanotenota |
|---|---|
routeOrder | padrão · ordem do backend (sem reordenar)default · backend order (no reorder)por defecto · orden del backend (sin reordenar) |
customerName | nome do varejoretail namenombre del punto de venta |
status | por progressão (depois nome)by progression (then name)por progresión (luego nombre) |
VisitsTabFilter 3
| case |
|---|
physical |
digital |
both |
VisitFilterGroup 2
| case | value |
|---|---|
defaultFilters | "default" |
commercialPillars | "commercial_pillars" |
FilterSelectionMode 2
| case | value |
|---|---|
single | "single" |
multi | "multi" |
CommercialPillarName 8
| case | value |
|---|---|
effectiveness | "effectiveness" |
primeCompliance | "prime_compliance" |
productivity | "productivity" |
capilarity | "capilarity" |
overdue | "overdue" |
positivationPartnership | "positivation_partnership" |
fatPartnership | "fat_partnership" |
unknown | "" |
VisitKpiName 6
| case | value |
|---|---|
targetOfTheDay | "target_of_the_day" |
targetOfTheDayNc | "target_of_the_day_nc" |
realizedOfTheDay | "realized_of_the_day" |
realizedOfTheDayNc | "realized_of_the_day_nc" |
realizedOfTheWeek | "realized_of_the_week" |
targetOfTheWeek | "target_of_the_week" |
AccountStatus 5
| case | value |
|---|---|
active | "active" |
temporarilyDeactivated | "temporarily_deactivated" |
permanentDeactivationRequest | "permanent_deactivation_request" |
disablePermanent | "disable_permanent" |
unknown | "unknown" |
FinancialStatus 2
| case | value |
|---|---|
compliant | "compliant" |
overdue | "overdue" |
VisitStatusUpdateOutcome 3 · resultado do check-in3 · check-in result3 · resultado del check-in
| case | notanotenota |
|---|---|
sent | enviado ao backendsent to backendenviado al backend |
queued | offline · enfileirado no Dispatcheroffline · queued in the Dispatcheroffline · encolado en el Dispatcher |
failed | falhou · status revertidofailed · status revertedfalló · estado revertido |
Enums do detalheDetail enumsEnums del detalle
AccountData.categoriesSold é tipado como CategoryForSale (enum de reference_data, definido fora de Visitas). Os enums de staff (ContactRole, B2bPortalStatus, ContactDesignation, LanguagePreference, ClerkProgramLayer, ClerkStatus…) pertencem ao Detalhe da visita.
AccountData.categoriesSold is typed as CategoryForSale (a reference_data enum, defined outside Visits). The staff enums (ContactRole, B2bPortalStatus, ContactDesignation, LanguagePreference, ClerkProgramLayer, ClerkStatus…) belong to the Visit detail.
AccountData.categoriesSold se tipa como CategoryForSale (enum de reference_data, definido fuera de Visitas). Los enums de staff (ContactRole, B2bPortalStatus, ContactDesignation, LanguagePreference, ClerkProgramLayer, ClerkStatus…) pertenecen al Detalle de la visita.
UseCases
Um dropdown por UseCase; dentro, cada método com assinatura, o que retorna e uso. Delegam ao repository (sem lógica extra) e seus providers são keepAlive.One dropdown per UseCase; inside, each method with its signature, what it returns and use. They delegate to the repository (no extra logic) and their providers are keepAlive.Un dropdown por UseCase; dentro, cada método con su firma, qué devuelve y uso. Delegan al repository (sin lógica extra) y sus providers son keepAlive.
GetVisitsUseCase 5 · a listathe listla lista
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({source}) | Result<VisitsEntity?, Failure> | Ponto de entrada da lista. Roteia por source → repository.getVisits. Chamado pelo VisitsNotifier.List entry point. Routes by source → repository.getVisits. Called by VisitsNotifier.Punto de entrada de la lista. Rutea por source → repository.getVisits. Llamado por VisitsNotifier. |
getCached() | Result<VisitsEntity?, Failure> | Só cache; null vira Success(null). Usado no refresh(local).Cache only; null becomes Success(null). Used on refresh(local).Solo caché; null es Success(null). Usado en refresh(local). |
getCachedLastSyncAt() | DateTime? | Timestamp da última sincronização. Alimenta o DataLoadInfo.Last-sync timestamp. Feeds DataLoadInfo.Timestamp de última sincronización. Alimenta DataLoadInfo. |
getCachedBySfid({visitSfid}) | Result<VisitEntity, Failure> | 1 visita do cache (ausente → Error(CacheFailure)). Alimenta o Visit Detail e o VisitContext (§28 cat. A).1 visit from cache (missing → Error(CacheFailure)). Feeds Visit Detail and VisitContext (§28 cat. A).1 visita del caché (ausente → Error(CacheFailure)). Alimenta el Visit Detail y el VisitContext (§28 cat. A). |
getCachedByAccountSfid({accountSfid}) | Result<VisitEntity?, Failure> | Acha a visita de um varejo pelo accountSfid (cache). Usado por telas que partem do varejo.Finds a retail's visit by accountSfid (cache). Used by screens starting from the retail.Halla la visita de un punto de venta por accountSfid (caché). Usado por pantallas que parten del punto de venta. |
SaveVisitStatusUseCase 1 · check-in1 · check-in1 · check-in
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({visitSfid, status, startedAt?, endedAt?}) | Result<void, Failure> | Persiste o status da visita só no cache local. Chamado pelo VisitContext (shared) no check-in; o envio remoto é separado (VisitUploadAPI).Persists the visit status to the local cache only. Called by VisitContext (shared) on check-in; the remote upload is separate (VisitUploadAPI).Persiste el estado de la visita solo en el caché local. Llamado por VisitContext (shared) en el check-in; el envío remoto es separado (VisitUploadAPI). |
Check-in — UseCases do envioCheck-in — upload UseCasesCheck-in — UseCases del envío
O envio remoto do check-in usa BuildVisitUploadDispatcherPayloadUseCase (monta o payload) + SubmitVisitUploadUseCase (despacha via Dispatcher). Ambos vivem no fluxo do VisitContext — detalhados em VisitUploadAPI.
The remote check-in upload uses BuildVisitUploadDispatcherPayloadUseCase (builds the payload) + SubmitVisitUploadUseCase (dispatches via the Dispatcher). Both live in the VisitContext flow — detailed in VisitUploadAPI.
El envío remoto del check-in usa BuildVisitUploadDispatcherPayloadUseCase (arma el payload) + SubmitVisitUploadUseCase (despacha vía Dispatcher). Ambos viven en el flujo del VisitContext — detallados en VisitUploadAPI.
Notifier & State
O VisitsNotifier (@riverpod, with AsyncGuard<VisitsState>) é o cérebro da tela. O build() observa o GetVisitsUseCase, escuta DataSyncType.visits (→ refresh(local) quando a sync em background grava) e retorna _load(). O State (VisitsState, Freezed) é a fonte única de verdade da page: guarda as visitas, os módulos habilitados (EMC) e todo o estado de cliente (aba, setor, busca, filtros, sort, paginação). Toda filtragem/ordenação/paginação é client-side, em getters do State.The VisitsNotifier (@riverpod, with AsyncGuard<VisitsState>) is the screen's brain. build() watches GetVisitsUseCase, listens to DataSyncType.visits (→ refresh(local) when background sync writes) and returns _load(). The State (VisitsState, Freezed) is the page's single source of truth: it holds the visits, the enabled modules (EMC) and all client state (tab, sector, search, filters, sort, pagination). All filtering/sorting/pagination is client-side, in State getters.El VisitsNotifier (@riverpod, with AsyncGuard<VisitsState>) es el cerebro de la pantalla. El build() observa GetVisitsUseCase, escucha DataSyncType.visits (→ refresh(local) cuando la sync en background graba) y retorna _load(). El State (VisitsState, Freezed) es la fuente única de verdad de la page: guarda las visitas, los módulos habilitados (EMC) y todo el estado de cliente (pestaña, sector, búsqueda, filtros, sort, paginación). Todo filtrado/orden/paginación es client-side, en getters del State.
MétodosMethodsMétodos
_load({source}) private
RetornoReturnRetorno Future<VisitsState>
Dono único da montagem do State: busca EMC config + visitas em paralelo e monta visits, lastSyncAt, visibleModules (só isVisible) e filterTabs. Chamado pelo build() e pelo refresh().Sole owner of building the State: fetches EMC config + visits in parallel and assembles visits, lastSyncAt, visibleModules (isVisible only) and filterTabs. Called by build() and refresh().Dueño único del armado del State: busca EMC config + visitas en paralelo y arma visits, lastSyncAt, visibleModules (solo isVisible) y filterTabs. Llamado por build() y refresh().
refresh({source = remote}) pull-to-refresh
RetornoReturnRetorno Future<void>
Recarrega via _load() (dentro de runGuarded) e preserva o estado de cliente: aba, setor, busca, filtros e sort. Não seta AsyncValue.loading (o pull-to-refresh tem indicador próprio).Reloads via _load() (inside runGuarded) and preserves client state: tab, sector, search, filters and sort. Doesn't set AsyncValue.loading (pull-to-refresh has its own indicator).Recarga vía _load() (dentro de runGuarded) y preserva el estado de cliente: pestaña, sector, búsqueda, filtros y sort. No setea AsyncValue.loading (pull-to-refresh tiene su propio indicador).
selectTab({tab})
RetornoReturnRetorno void
Fixa a aba (Presencial/Digital/Ambas). Reseta visibleCount.Sets the tab (Physical/Digital/Both). Resets visibleCount.Fija la pestaña (Presencial/Digital/Ambas). Resetea visibleCount.
selectSector({sector})
RetornoReturnRetorno void
Liga/desliga o setor (tocar no ativo desmarca). Reseta visibleCount.Toggles the sector (tapping the active one clears it). Resets visibleCount.Alterna el sector (tocar el activo lo desmarca). Resetea visibleCount.
setSearchQuery({query})
RetornoReturnRetorno void
Atualiza o termo de busca (nome/código/endereço). Reseta visibleCount; a filtragem acontece nos getters.Updates the search term (name/code/address). Resets visibleCount; filtering happens in the getters.Actualiza el término de búsqueda (nombre/código/dirección). Resetea visibleCount; el filtrado ocurre en los getters.
applyModalFilters({selectedFilters})
RetornoReturnRetorno void
Aplica os filtros do modal (Map<String, Set<String>> por código de filtro). Reseta visibleCount.Applies the modal filters (Map<String, Set<String>> by filter code). Resets visibleCount.Aplica los filtros del modal (Map<String, Set<String>> por código de filtro). Resetea visibleCount.
clearModalFilters()
RetornoReturnRetorno void
Limpa todos os filtros do modal. Reseta visibleCount.Clears all modal filters. Resets visibleCount.Limpia todos los filtros del modal. Resetea visibleCount.
setSort({sort})
RetornoReturnRetorno void
Define a ordenação (VisitSort). Reseta visibleCount.Sets the sort (VisitSort). Resets visibleCount.Define la ordenación (VisitSort). Resetea visibleCount.
revealInProgressVisit() visita ativaactive visitvisita activa
RetornoReturnRetorno void
Ajusta filtros (aba pelo tipo, sem setor/busca) para revelar a visita iniciada e amplia visibleCount até incluí-la. Acionado pelo botão flutuante "Em andamento".Adjusts filters (tab by type, no sector/search) to reveal the started visit and grows visibleCount until it's included. Triggered by the "In progress" floating button.Ajusta filtros (pestaña por tipo, sin sector/búsqueda) para revelar la visita iniciada y amplía visibleCount hasta incluirla. Accionado por el botón flotante "En curso".
loadMore()
RetornoReturnRetorno void
Incrementa visibleCount em 20 (paginação apenas visual — os dados já estão em memória).Increments visibleCount by 20 (visual-only pagination — data is already in memory).Incrementa visibleCount en 20 (paginación solo visual — los datos ya están en memoria).
State disponível para a PageState available to the PageState disponible para la Page
VisitsState campos + gettersfields + getterscampos + getters
| campo | tipo | default |
|---|---|---|
visits | List<VisitEntity> | [] |
lastSyncAt | DateTime? | null |
visibleModules | List<ModuleConfig> | [] |
filterTabs | List<FilterTabConfig> | [] |
selectedTab | VisitsTabFilter | physical |
selectedSector | VisitSector? | null |
searchQuery | String | "" |
selectedFilters | Map<String, Set<String>> | {} |
sort | VisitSort | routeOrder |
visibleCount | int | 20 |
Getters: filteredVisits (filtro + sort), visibleVisits (recorte), totalFilteredVisits, hasMoreToLoad, inProgressVisit, sectorCount, getModule, showsSearchBar, showsSectorizer, showsFilter, showsListActions, hasActiveSort, hasActiveModalFilters, hasAnyActiveFilter, e os toggles de card via EMC showsCardRetailData/Activities/Daily/DailyNc/CommercialPillars/Insights/Sales + allowedSalesCategories/visibleSalesCategories. O filtro combina aba + setor + busca (multi-token em nome/comercial/endereço) + filtros do modal (cruzando os filterCode da visita).Getters: filteredVisits (filter + sort), visibleVisits (slice), totalFilteredVisits, hasMoreToLoad, inProgressVisit, sectorCount, getModule, showsSearchBar, showsSectorizer, showsFilter, showsListActions, hasActiveSort, hasActiveModalFilters, hasAnyActiveFilter, plus the EMC card toggles showsCardRetailData/Activities/Daily/DailyNc/CommercialPillars/Insights/Sales + allowedSalesCategories/visibleSalesCategories. Filtering combines tab + sector + search (multi-token over name/commercial/address) + modal filters (matching the visit's filterCode).Getters: filteredVisits, visibleVisits, totalFilteredVisits, hasMoreToLoad, inProgressVisit, sectorCount, getModule, showsSearchBar, showsSectorizer, showsFilter, showsListActions, hasActiveSort, hasActiveModalFilters, hasAnyActiveFilter, y los toggles de card vía EMC showsCardRetailData/Activities/Daily/DailyNc/CommercialPillars/Insights/Sales + allowedSalesCategories/visibleSalesCategories. El filtro combina pestaña + sector + búsqueda (multi-token en nombre/comercial/dirección) + filtros del modal (cruzando los filterCode de la visita).
Page e widgetsPage & widgetsPage y widgets
A VisitsPage (ConsumerWidget) observa o visitsProvider. Loading e erro são globais (visitsAsync.when); o conteúdo (_VisitsBody, um Stack) existe só no ramo data. O botão flutuante "Em andamento" fica sobreposto. Árvore de composição:VisitsPage (ConsumerWidget) watches visitsProvider. Loading and error are global (visitsAsync.when); content (_VisitsBody, a Stack) exists only in the data branch. The "In progress" floating button is overlaid. Composition tree:VisitsPage (ConsumerWidget) observa visitsProvider. Loading y error son globales (visitsAsync.when); el contenido (_VisitsBody, un Stack) existe solo en la rama data. El botón flotante "En curso" queda superpuesto. Árbol de composición:
- VisitsPage
- AppPageShell drawer · connectivity · search · notifications
- CustomLoadingIndicator loading
- FailureStateView error → invalidate
- _VisitsBody · Stack data
- CustomPullToRefresh → refresh()
- VisitsHeaderWidget DataLoadInfo + título
- VisitsTabBarWidget Presencial/Digital → selectTab
- VisitsSectorizerWidget chips + contador → selectSector
- CustomInput busca → setSearchQuery (se showsSearchBar)
- VisitsActionsWidget ConectaSortButton · ConectaFilterButton
- VisitsSortModalContent modal · VisitSort.selectable → setSort
- VisitsFiltersModalContent modal · filterTabs (EMC) · single/multi → applyModalFilters / clearModalFilters
- VisitsListSectionWidget
- CustomEmptyState vazio
- InfiniteScrollListView scroll → loadMore()
- VisitCardWidget KeyedSubtree p/ visita ativa · onTap → ActiveVisitGuard → goToVisitDetail
- VisitCardRetailDataWidget avatar (VisitAvatarWidget) + dados do varejo
- VisitCardActivitiesWidget
- VisitCardDailyWidget
- VisitCardCommercialPillarsWidget
- VisitCardBottomRowWidget
- VisitInProgressModalContent modal (via ActiveVisitGuard) · "finalizar visita atual?" → endVisit()
- VisitCardWidget KeyedSubtree p/ visita ativa · onTap → ActiveVisitGuard → goToVisitDetail
- PaginationCountIndicator X de Y
- InProgressIndicatorWidget Positioned · flutuante · isActive = inProgressVisit != null → revealInProgressVisit + scroll
- CustomPullToRefresh → refresh()
- AppPageShell drawer · connectivity · search · notifications
Cada seção do card aparece só quando o mercado a habilita (showsCard* via EMC). O ActiveVisitGuard intercepta o toque: se há outra visita iniciada, abre o VisitInProgressModalContent; ao confirmar, finaliza a atual em segundo plano (VisitContext.endVisit) e navega. Iniciar/finalizar a visita em si mora no Detalhe da visita.Each card section shows only when the market enables it (showsCard* via EMC). ActiveVisitGuard intercepts the tap: if another visit is started, it opens VisitInProgressModalContent; on confirm it finishes the current one in the background (VisitContext.endVisit) and navigates. Starting/finishing the visit itself lives in the Visit detail.Cada sección de la tarjeta aparece solo cuando el mercado la habilita (showsCard* vía EMC). El ActiveVisitGuard intercepta el toque: si hay otra visita iniciada, abre el VisitInProgressModalContent; al confirmar finaliza la actual en segundo plano (VisitContext.endVisit) y navega. Iniciar/finalizar la visita en sí vive en el Detalle de la visita.
Notas por mercadoMarket notesNotas por mercado
Visitas é dirigido por configuração de mercado (End Market Configuration): a presença do visitsConfig habilita a tela, e cada módulo/aba de filtro/seção de card é declarado por mercado. Está habilitado em três mercados — os mesmos do envio de check-in (VisitUploadAPI, DispatcherType.visit.enabledMarkets):Visits is driven by market configuration (End Market Configuration): the presence of visitsConfig enables the screen, and each module/filter tab/card section is declared per market. It's enabled in three markets — the same as the check-in upload (VisitUploadAPI, DispatcherType.visit.enabledMarkets):Visitas se rige por configuración de mercado (End Market Configuration): la presencia del visitsConfig habilita la pantalla, y cada módulo/pestaña de filtro/sección de tarjeta se declara por mercado. Está habilitado en tres mercados — los mismos del envío de check-in (VisitUploadAPI, DispatcherType.visit.enabledMarkets):
Base estruturalStructural baseBase estructural ZA é a base do EMC: BR/CL recebem só as features que têm. Módulos exclusivos de ZA (ex.: metas mensais no detalhe) não entram em BR/CL — mas a lista em si é equivalente nos três. ZA is the EMC base: BR/CL only get the features they have. ZA-exclusive modules (e.g. monthly targets in the detail) don't reach BR/CL — but the list itself is equivalent across all three. ZA es la base del EMC: BR/CL reciben solo las features que tienen. Módulos exclusivos de ZA (ej.: metas mensuales en el detalle) no llegan a BR/CL — pero la lista en sí es equivalente en los tres.
Setores e filtrosSectors & filtersSectores y filtros
As abas de filtro (status do varejo, situação financeira, tipo de visita, visita planejada) e os setores são declarados por mercado no visitsConfig — o conteúdo pode variar entre BR e CL.
Filter tabs (retail status, financial status, visit type, planned visit) and sectors are declared per market in visitsConfig — the content can differ between BR and CL.
Las pestañas de filtro (estado del punto de venta, situación financiera, tipo de visita, visita planificada) y los sectores se declaran por mercado en visitsConfig — el contenido puede variar entre BR y CL.
AR · PY · PE
Existem como mercados do app, mas não têm visitsConfig no End Market Configuration — a tela de Visitas não é renderizada (config PANGEA mínima). Também não estão em DispatcherType.visit.enabledMarkets.
They exist as app markets, but have no visitsConfig in End Market Configuration — the Visits screen isn't rendered (minimal PANGEA config). They're also not in DispatcherType.visit.enabledMarkets.
Existen como mercados de la app, pero no tienen visitsConfig en End Market Configuration — la pantalla de Visitas no se renderiza (config PANGEA mínima). Tampoco están en DispatcherType.visit.enabledMarkets.