DocumentaçãoDocumentationDocumentaciónOne Conecta
ÍndiceIndexÍndice
Baixar .mdDownload .mdBajar .md
Feature · ChamadosFeature · CasesFeature · Casos

ChamadosCasesCasos

A lista de chamados (atendimentos / SAC) dos varejos sob a hierarquia do representante de vendas, com status, prioridade e drill-down ao detalhe. É quase só leitura: a única escrita é encerrar um chamado — e só no Brasil. The list of cases (service / support tickets) from the retails under the sales rep's hierarchy, with status, priority and drill-down to detail. It's almost read-only: the single write is closing a case — and only in Brazil. La lista de casos (atenciones / SAC) de los puntos de venta bajo la jerarquía del representante de ventas, con estado, prioridad y acceso al detalle. Es casi solo lectura: la única escritura es cerrar un caso — y solo en Brasil.

PúblicoAudiencePúblico
Representante · QA · Suporte · DevRep · QA · Support · DevRepresentante · QA · Soporte · Dev
Onde ficaWhereDónde
Home → grade de ações → ChamadosHome → actions grid → CasesHome → grilla de acciones → Casos
RelacionadoRelatedRelacionado
AtualizadoUpdatedActualizado
22/07/20262026-07-22
Disponível emAvailable inDisponible en BR
01

O que é e para que serveWhat it is and what it's forQué es y para qué sirve

A tela de Chamados reúne os atendimentos (tickets de suporte / SAC do consumidor) abertos para os varejos sob a hierarquia do representante de vendas. Responde três perguntas: The Cases screen gathers the service tickets (support / consumer SAC) opened for the retails under the sales rep's hierarchy. It answers three questions: La pantalla de Casos reúne las atenciones (tickets de soporte / SAC del consumidor) abiertas para los puntos de venta bajo la jerarquía del representante de ventas. Responde tres preguntas:

Quais chamados existem?Which cases exist?¿Qué casos hay?

Um card por chamado, com número, varejo, status, prioridade e data de abertura.One card per case, with number, retail, status, priority and open date.Una tarjeta por caso, con número, punto de venta, estado, prioridad y fecha de apertura.

Em que pé está?Where does it stand?¿En qué punto está?

O detalhe mostra contato, dados do caso, manifestação e o bloco SAC do consumidor.The detail shows contact, case data, manifestation and the consumer SAC block.El detalle muestra contacto, datos del caso, manifestación y el bloque SAC del consumidor.

O que fazer com um?What to do with one?¿Qué hacer con uno?

Se ainda estiver Novo, o chamado pode ser encerrado com uma justificativa.If still New, the case can be closed with a justification.Si aún está Nuevo, el caso puede ser cerrado con una justificación.

Só BrasilBrazil onlySolo Brasil Chamados é habilitado apenas no Brasil (pela End Market Configuration). Nos demais mercados a ação nem aparece na Home. A lista é leitura; a única escrita — encerrar — também é exclusiva do Brasil. Cases is enabled only in Brazil (via End Market Configuration). In the other markets the action doesn't even show on Home. The list is read-only; the single write — close — is also Brazil-only. Casos está habilitado solo en Brasil (vía End Market Configuration). En los demás mercados la acción ni aparece en el Home. La lista es de lectura; la única escritura — cerrar — también es exclusiva de Brasil.

02

Como acessarHow to openCómo acceder

  1. Pela grade de ações da HomeFrom the Home actions gridDesde la grilla de acciones del HomeNo módulo Ações do representante da Home, toque no atalho Chamados (mostra a contagem de pendentes).In the Home Rep actions module, tap the Cases tile (it shows the pending count).En el módulo Acciones del representante del Home, toque el atajo Casos (muestra el conteo de pendientes).
  2. A lista abreThe list opensLa lista abreMostra os chamados em cards. Puxe para baixo para atualizar; role para carregar mais.It shows the cases as cards. Pull down to refresh; scroll to load more.Muestra los casos en tarjetas. Deslice hacia abajo para actualizar; desplace para cargar más.
  3. Toque num cardTap a cardToque una tarjetaAbre o detalhe do chamado, com todos os dados e o botão de encerrar (quando cabível).Opens the case detail, with all data and the close button (when applicable).Abre el detalle del caso, con todos los datos y el botón de cerrar (cuando corresponde).
03

Estrutura da telaScreen structureEstructura de la pantalla

ListaListLista

CabeçalhoHeaderEncabezado
Ícone + título "Chamados" e a data da última sincronização.Icon + "Cases" title and the last sync date.Ícono + título "Casos" y la fecha de última sincronización.
CardsCardsTarjetas
Um por chamado: número do chamado em destaque e quatro campos — varejo, status, data de abertura e prioridade.One per case: the case number highlighted and four fields — retail, status, open date and priority.Una por caso: el número del caso destacado y cuatro campos — punto de venta, estado, fecha de apertura y prioridad.
Carregar maisLoad moreCargar más
A lista mostra 15 por vez e carrega mais ao rolar até o fim (rolagem infinita, sem busca/filtro).The list shows 15 at a time and loads more as you scroll to the end (infinite scroll, no search/filter).La lista muestra 15 por vez y carga más al desplazar hasta el final (scroll infinito, sin búsqueda/filtro).
Lista vaziaEmpty listLista vacía
Sem chamados, mostra um estado vazio com ícone e mensagem.With no cases, it shows an empty state with icon and message.Sin casos, muestra un estado vacío con ícono y mensaje.

DetalheDetailDetalle

NúmeroNumberNúmero
O número do chamado em destaque no topo.The case number highlighted at the top.El número del caso destacado arriba.
Varejo & contatoRetail & contactPunto de venta y contacto
Card com varejo, cargo, responsável, telefone, e-mail, SAP ID e CNPJ/CPF (formatado por mercado).Card with retail, role, responsible, phone, e-mail, SAP ID and tax ID (formatted per market).Tarjeta con punto de venta, cargo, responsable, teléfono, correo, SAP ID y ID fiscal (formateado por mercado).
Dados do chamadoCase dataDatos del caso
Card com status, prioridade, responsável, solicitante, assunto, descrição e datas (atualização, encerramento, abertura).Card with status, priority, owner, requestor, subject, description and dates (updated, closing, open).Tarjeta con estado, prioridad, responsable, solicitante, asunto, descripción y fechas (actualización, cierre, apertura).
AdicionaisAdditionalAdicionales
Card com empresa, linha, produto/assunto, variedade e a manifestação (grupo, tipo, procedimento).Card with company, line, product subject, variety and the manifestation (group, type, procedure).Tarjeta con empresa, línea, producto/asunto, variedad y la manifestación (grupo, tipo, procedimiento).
Botão encerrarClose buttonBotón cerrar
Aparece quando o status é Novo.Shows only when the status is New.Aparece solo cuando el estado es Nuevo.
04

Status e prioridadeStatus & priorityEstado y prioridad

O chamado tem um status e uma prioridade. Diferente do status de pedido, aqui os rótulos são traduzidos (i18n) — o valor de wire (inglês) é mapeado para o idioma do app. Valores possíveis:A case has a status and a priority. Unlike the order status, here the labels are translated (i18n) — the wire value (English) is mapped to the app language. Possible values:Un caso tiene un estado y una prioridad. A diferencia del estado del pedido, aquí las etiquetas se traducen (i18n) — el valor de wire (inglés) se mapea al idioma de la app. Valores posibles:

StatusStatusEstado
New · Working · Escalated · On Hold · Closed
PrioridadePriorityPrioridad
High · Medium · Low

EncerramentoClosingCierre O botão Encerrar só existe enquanto o chamado está New. Depois de encerrado (ou em qualquer outro status), não há ação — a tela é consulta. The Close button only exists while the case is New. Once closed (or in any other status), there's no action — the screen is read-only. El botón Cerrar solo existe mientras el caso está New. Una vez cerrado (o en cualquier otro estado), no hay acción — la pantalla es de consulta.

05

Ação: encerrar chamadoAction: close caseAcción: cerrar caso

A única escrita da feature. Encerra o chamado, exigindo uma justificativa. É enviada primeiro ao backend (via Dispatcher) e, só se der certo, o chamado passa a Closed localmente.The feature's only write. It closes the case, requiring a justification. It's sent to the backend first (via the Dispatcher) and, only on success, the case becomes Closed locally.La única escritura de la feature. Cierra el caso, exigiendo una justificación. Se envía primero al backend (vía Dispatcher) y, solo si tiene éxito, el caso pasa a Closed localmente.

  1. Toque em "Encerrar chamado"Tap "Close case"Toque "Cerrar caso"Abre um modal com um campo de texto para a justificativa.A modal opens with a text field for the justification.Abre un modal con un campo de texto para la justificación.
  2. Escreva a justificativaWrite the justificationEscriba la justificaciónMínimo de 50 caracteres; um contador mostra o progresso e o botão de confirmar só habilita ao atingir o mínimo.Minimum 50 characters; a counter shows progress and the confirm button only enables once the minimum is reached.Mínimo 50 caracteres; un contador muestra el progreso y el botón de confirmar solo se habilita al alcanzar el mínimo.
  3. ConfirmeConfirmConfirmeO envio segue; em sucesso, aparece um aviso verde, a lista é atualizada e a tela volta. Em erro, um aviso vermelho e o chamado permanece aberto.The submit runs; on success, a green notice appears, the list refreshes and the screen goes back. On error, a red notice shows and the case stays open.El envío corre; en éxito, aparece un aviso verde, la lista se actualiza y la pantalla vuelve. En error, un aviso rojo y el caso permanece abierto.
06

Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos

Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Há dois fluxos distintos: a leitura (lista e detalhe) e a escrita (encerrar, via Dispatcher). O dado de leitura atravessa quatro representações ligadas por mappers, com cache write-through.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. There are two distinct flows: the read (list and detail) and the write (close, via the Dispatcher). Read data crosses four representations linked by mappers, with cache write-through.Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Hay dos flujos distintos: la lectura (lista y detalle) y la escritura (cerrar, vía Dispatcher). El dato de lectura atraviesa cuatro representaciones unidas por mappers, con cache write-through.

Leitura · lista (cache + remoto no refresh)Read · list (cache + remote on refresh)Lectura · lista (caché + remoto en refresh)

Um único RPC (getCases) traz todos os chamados. O build() da lista lê do cache (fonte local); o pull-to-refresh força a fonte remote, que grava de volta no ObjectBox:A single RPC (getCases) returns every case. The list's build() reads from cache (local source); pull-to-refresh forces the remote source, which writes back to ObjectBox:Un único RPC (getCases) trae todos los casos. El build() de la lista lee del caché (fuente local); el pull-to-refresh fuerza la fuente remote, que graba de vuelta en ObjectBox:

  • CaseManagementReplygRPC proto
    • toCasesDTOCasesDTODTO · Freezed
      • toDomainCasesEntitydomain
        • toModelCasesModelObjectBox
          • toDomainCasesEntitydomain · cache
            • watchCasesNotifier + State
              • → UICasesPage

Leitura · detalhe (só cache)Read · detail (cache-only)Lectura · detalle (solo caché)

O detalhe não chama RPC: lê um chamado do cache pelo sfid (getCachedBySfid, §28 cat. A):Detail calls no RPC: it reads one case from cache by sfid (getCachedBySfid, §28 cat. A):El detalle no llama RPC: lee un caso del caché por sfid (getCachedBySfid, §28 cat. A):

  • CaseModelObjectBox · cache
    • getCases → firstWhereOrNullCaseManagementLocalDataSource
      • toDomainCaseEntitydomain
        • getCachedBySfidGetCasesUseCase
          • build (cache-only)CaseDetailNotifier + State
            • → UICaseDetailPage

Escrita · encerrar via DispatcherWrite · close via DispatcherEscritura · cerrar vía Dispatcher

Remote-first (§36): o notifier monta o envelope CaseUploadAPI, despacha pelo Dispatcher e, só em sucesso, marca o chamado como Closed no cache local. Não passa pelo CaseManagementRepository de leitura:Remote-first (§36): the notifier builds the CaseUploadAPI envelope, dispatches through the Dispatcher and, only on success, marks the case as Closed in the local cache. It does not go through the read CaseManagementRepository:Remote-first (§36): el notifier arma el sobre CaseUploadAPI, despacha por el Dispatcher y, solo en éxito, marca el caso como Closed en el caché local. No pasa por el CaseManagementRepository de lectura:

  • CaseDetailCloseButtonWidgetUI · modal
    • closeCase(comment)CaseDetailNotifier
      • build(input)BuildCaseUploadDispatcherPayloadUseCase→ DispatcherEnvelope
        • submit(envelope)SubmitCaseUploadUseCase
          • dispatchDispatcherOrchestratorgRPC dispatcher
            • on success → markCaseClosedSaveCaseClosureUseCasecache local

Pendências / roadmapPending / roadmapPendientes / roadmap

  • Sem busca, ordenação ou filtros na lista (só rolagem infinita de 15 em 15) — diferente de Pedidos.No search, sort or filters on the list (only 15-at-a-time infinite scroll) — unlike Orders.Sin búsqueda, orden ni filtros en la lista (solo scroll infinito de 15 en 15) — a diferencia de Pedidos.
  • lastModifiedDate existe no proto/datasource mas não é usado — não há sync incremental hoje.lastModifiedDate exists in the proto/datasource but is not used — no incremental sync today.lastModifiedDate existe en el proto/datasource pero no se usa — no hay sync incremental hoy.
  • Encerrar é a única escrita, e de NewClosed; não há reabrir nem editar. A transação CaseUploadAPI está documentada à parte.Close is the only write, and only NewClosed; no reopen or edit. The CaseUploadAPI transaction is documented separately.Cerrar es la única escritura, y solo NewClosed; no hay reabrir ni editar. La transacción CaseUploadAPI está documentada aparte.
07

Modelo de dadosData modelModelo de datos

O mesmo chamado existe em quatro representações quase idênticas ao longo das camadas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (domínio) — e cada fronteira é atravessada por um mapper. Os nomes se mantêm; muda muito pouco. O fetch é write-through: todo retorno é gravado no ObjectBox e a UI lê do cache.The same case exists in four near-identical representations across the layers — Proto (gRPC wire) → DTO (Freezed) → Model (ObjectBox) → Entity (domain) — and each boundary is crossed by a mapper. Names stay the same; very little changes. Fetch is write-through: every response is written to ObjectBox and the UI reads from cache.El mismo caso existe en cuatro representaciones casi idénticas a lo largo de las capas — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (dominio) — y cada frontera se cruza con un mapper. Los nombres se mantienen; cambia muy poco. El fetch es write-through: toda respuesta se graba en ObjectBox y la UI lee del caché.

A lista chega num container CasesEntity (lastSyncAt gerado no mapper + items[]); cada item é um Case de 27 campos, com um bloco aninhado CaseSac (SAC do consumidor). Aqui há uma particularidade: só o campo role vira enum tipado (ContactRole?) na Entity — status e priority ficam String em todas as camadas (o enum é resolvido só na UI). A seguir: o proto, as estruturas campo-a-campo, e os mappers.The list arrives in a CasesEntity container (lastSyncAt generated in the mapper + items[]); each item is a 27-field Case with a nested CaseSac block (consumer SAC). There's one quirk here: only the role field becomes a typed enum (ContactRole?) in the Entity — status and priority stay String in every layer (the enum is resolved only in the UI). Next: the proto, the field-by-field structures, and the mappers.La lista llega en un container CasesEntity (lastSyncAt generado en el mapper + items[]); cada ítem es un Case de 27 campos, con un bloque anidado CaseSac (SAC del consumidor). Hay una particularidad: solo el campo role se vuelve enum tipado (ContactRole?) en la Entity — status y priority quedan String en todas las capas (el enum se resuelve solo en la UI). A continuación: el proto, las estructuras campo a campo, y los mappers.

Proto

CaseManagementConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge. Um serviço (CaseManagementConectaRepService), um método unário:One service (CaseManagementConectaRepService), a single unary method:Un servicio (CaseManagementConectaRepService), un método unario:

getCasesunary
MétodoMethodMétodo

rpc getCases(CaseManagementRequest) returns (CaseManagementReply)

path /mn.bat.conectarep.streambridge.CaseManagementConectaRepService/getCases

Request · CaseManagementRequest
locationHierarchySfid
string · #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)
lastModifiedDate
string · #2 · optional (não usado hoje)optional (not used today)optional (no usado hoy)
Reply · CaseManagementReply

repeated CaseInfo casesa lista de chamados. Os 27 campos de CaseInfo estão detalhados nas Estruturas de dados abaixo.the list of cases. CaseInfo's 27 fields are detailed in Data structures below.la lista de casos. Los 27 campos de CaseInfo están detallados en Estructuras de datos abajo.

Estruturas de dadosData structuresEstructuras de datos

Um dropdown por estrutura, aninhados pela hierarquia. Cada tabela tem uma coluna por camada — Proto · DTO · Model · Entity; as células com borda marcam onde o tipo/nome primeiro muda (enum na Entity, ToOne no Model, rename no Model). ¹ = optional no proto.One dropdown per structure, nested by hierarchy. Each table has one column per layer — Proto · DTO · Model · Entity; bordered cells mark where the type/name first changes (enum in the Entity, ToOne in the Model, rename in the Model). ¹ = optional in the proto.Un dropdown por estructura, anidados por jerarquía. Cada tabla tiene una columna por capa — Proto · DTO · Model · Entity; las celdas con borde marcan dónde primero cambia el tipo/nombre (enum en la Entity, ToOne en el Model, rename en el Model). ¹ = optional en el proto.

  • Case raiz · CaseInfo 27 camposfieldscampos
    CampoProtoDTOModelEntity
    idstringStringcaseSfidString
    caseNumberstringStringStringString
    statusstringString?String?String?
    prioritystringString?String?String?
    openDatestringString?String?String?
    closingDatestring¹String?String?String?
    lastUpdatedstringString?String?String?
    ownerstringString?String?String?
    requestorstringString?String?String?
    subjectstringString?String?String?
    descriptionstringString?String?String?
    storeNamestringString?String?String?
    contactNamestringString?String?String?
    rolestringString?String?ContactRole?
    phonestringString?String?String?
    emailstringString?String?String?
    taxIdstringString?String?String?
    sapCustomerIdstringString?String?String?
    companystringString?String?String?
    linestringString?String?String?
    productSubjectstringString?String?String?
    varietystringString?String?String?
    manifestationstringString?String?String?
    manifestationTypestringString?String?String?
    manifestationGroupstringString?String?String?
    procedurestringString?String?String?
    sacCaseSac¹…DTO?ToOne<…Model>…Entity?
    • CaseSac Case.sac 13 camposfieldscampos
      CampoProtoDTOModelEntity
      consumerNamestringString?String?String?
      taxNumberstringString?String?String?
      statusstringString?String?String?
      lotNumberstringString?String?String?
      productionDatestringString?String?String?
      birthDatestringString?String?String?
      phonestringString?String?String?
      emailstringString?String?String?
      zipCodestringString?String?String?
      statestringString?String?String?
      citystringString?String?String?
      neighborhoodstringString?String?String?
      addressstringString?String?String?

O container Cases (CasesEntity/CasesModel) tem 2 campos: lastSyncAt (DateTime) e itemsList<Case> na Entity / ToMany<CaseModel> no Model.The Cases container (CasesEntity/CasesModel) has 2 fields: lastSyncAt (DateTime) and itemsList<Case> in the Entity / ToMany<CaseModel> in the Model.El container Cases (CasesEntity/CasesModel) tiene 2 campos: lastSyncAt (DateTime) e itemsList<Case> en la Entity / ToMany<CaseModel> en el Model.

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ónMétodoMethodMétodo
JSON → DTOstatic fromMap(Map) (container + CaseDTOMapper/CaseSacDTOMapper)(container + CaseDTOMapper/CaseSacDTOMapper)(container + CaseDTOMapper/CaseSacDTOMapper)
Proto → DTOtoCasesDTO() / toDTO() (guarda hasClosingDate()/hasSac())(guards hasClosingDate()/hasSac())(guarda hasClosingDate()/hasSac())
DTO → EntitytoDomain() (resolve role: ContactRole.fromLabel)(resolves role: ContactRole.fromLabel)(resuelve role: ContactRole.fromLabel)
Entity → ModeltoModel() (role → .label; id → caseSfid; popula ToOne/ToMany)(role → .label; id → caseSfid; fills ToOne/ToMany)(role → .label; id → caseSfid; llena ToOne/ToMany)
Model → EntitytoDomain()

Os únicos deltasThe only deltasLos únicos deltas

  • role é o único enum tipado, e só na Entity (ContactRole?); String nas outras camadas.role is the only typed enum, and only in the Entity (ContactRole?); String in the other layers.role es el único enum tipado, y solo en la Entity (ContactRole?); String en las demás capas.
  • status e priority ficam String em todas as camadas — o enum (CaseStatus/CasePriority) é resolvido só na UI via fromValue.status and priority stay String in all layers — the enum (CaseStatus/CasePriority) is resolved only in the UI via fromValue.status y priority quedan String en todas las capas — el enum (CaseStatus/CasePriority) se resuelve solo en la UI vía fromValue.
  • rename idcaseSfid no Model (o @Id int do ObjectBox é separado).rename idcaseSfid in the Model (ObjectBox's @Id int is separate).rename idcaseSfid en el Model (el @Id int de ObjectBox es aparte).
  • Case.sac vira ToOne<CaseSacModel> e Cases.items vira ToMany<CaseModel> no Model.Case.sac becomes ToOne<CaseSacModel> and Cases.items becomes ToMany<CaseModel> in the Model.Case.sac pasa a ToOne<CaseSacModel> y Cases.items pasa a ToMany<CaseModel> en el Model.
  • lastSyncAt gerado no mapper com DateTimeUtils.now() (o backend não envia o campo).generated in the mapper with DateTimeUtils.now() (the backend doesn't send it).generado en el mapper con DateTimeUtils.now() (el backend no envía el campo).
08

Repository

CaseManagementRepositoryImpl implementaimplementsimplementa CaseManagementRepositoryInterface 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:

getCases({source}) mock / local / remote

RetornaReturnsDevuelve Result<CasesEntity, Failure>

Ponto de entrada da lista: decide a fonte pela source + flags, mapeia e grava no cache (write-through). Chamado pelo CasesNotifier.List entry point: picks the source from source + flags, maps and writes to cache (write-through). Called by CasesNotifier.Punto de entrada de la lista: elige la fuente por source + flags, mapea y graba en caché (write-through). Llamado por CasesNotifier.

Árvore de decisão de fonteSource decision treeÁrbol de decisión de fuente

  1. useMockData == true ouoro source == mock_fetchFromMock(): lê o mock, mapeia, grava no cache._fetchFromMock(): reads the mock, maps, writes to cache._fetchFromMock(): lee el mock, mapea, graba en caché.
  2. source == local ou offlineor offlineu offline_fetchFromCacheOrFail(): cache; se vazio, Error(NetworkFailure). É o caminho do build() da lista._fetchFromCacheOrFail(): cache; if empty, Error(NetworkFailure). It's the list's build() path._fetchFromCacheOrFail(): caché; si vacío, Error(NetworkFailure). Es el camino del build() de la lista.
  3. senão (remoto + conectado)otherwise (remote + connected)si no (remoto + conectado)_fetchFromRemoteWithFallback(): lê currentResourceProvider; se null cai pro cache; senão chama o remoto com locationHierarchyId (§25), mapeia, grava no cache; em erro, fallback pro cache. É o caminho do pull-to-refresh._fetchFromRemoteWithFallback(): reads currentResourceProvider; if null falls back to cache; else calls remote with locationHierarchyId (§25), maps, writes to cache; on error, falls back to cache. It's the pull-to-refresh path._fetchFromRemoteWithFallback(): lee currentResourceProvider; si null cae al caché; si no llama al remoto con locationHierarchyId (§25), mapea, graba en caché; en error, fallback al caché. Es el camino del pull-to-refresh.
getCachedCases() local

RetornaReturnsDevuelve Result<CasesEntity?, Failure>

Só cache. null vira Success(null), não erro.Cache only. null becomes Success(null), not an error.Solo caché. null es Success(null), no error.

getCachedCasesLastSyncAt() 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.

getCachedCaseBySfid({caseSfid}) local

RetornaReturnsDevuelve Result<CaseEntity?, Failure>

getCachedCases() e faz firstWhereOrNull(id == caseSfid). Alimenta o detalhe (§28 cat. A) — nunca dispara remoto.Reads getCachedCases() and does firstWhereOrNull(id == caseSfid). Feeds the detail (§28 cat. A) — never triggers remote.Lee getCachedCases() y hace firstWhereOrNull(id == caseSfid). Alimenta el detalle (§28 cat. A) — nunca dispara remoto.

saveCases({cases}) local

RetornaReturnsDevuelve Result<void, Failure>

Destrutivo: clearCases() + regrava tudo (cascata das boxes filhas). Cache-writer chamado após cada fetch bem-sucedido.Destructive: clearCases() + rewrites everything (child boxes cascade). Cache-writer called after each successful fetch.Destructivo: clearCases() + regraba todo (cascada de boxes hijas). Cache-writer llamado tras cada fetch exitoso.

markCaseClosed({caseSfid}) local · escritawriteescritura

RetornaReturnsDevuelve Result<void, Failure>

Marca só no cache local o CaseModel (status = CaseStatus.closed.wireValue). A escrita remota é do Dispatcher (não deste repository) — o notifier chama este método só depois do dispatch dar certo (remote-first, §36).Marks the CaseModel in the local cache only (status = CaseStatus.closed.wireValue). The remote write is the Dispatcher's (not this repository's) — the notifier calls this method only after the dispatch succeeds (remote-first, §36).Marca solo en el caché local el CaseModel (status = CaseStatus.closed.wireValue). La escritura remota es del Dispatcher (no de este repository) — el notifier llama a este método solo después de que el dispatch tenga éxito (remote-first, §36).

09

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 CaseManagementRemoteDataSource gRPC
getCases({locationHierarchySfid, lastModifiedDate?})
EnvioSendsEnvío
monta CaseManagementRequest e chama _client.getCases(request) no client CaseManagementConectaRepServiceClient (via caseManagementServiceClientProvider). lastModifiedDate só é setado se não-null (hoje o repository não passa).builds CaseManagementRequest and calls _client.getCases(request) on CaseManagementConectaRepServiceClient (via caseManagementServiceClientProvider). lastModifiedDate is set only when non-null (today the repository doesn't pass it).arma CaseManagementRequest y llama _client.getCases(request) en CaseManagementConectaRepServiceClient (vía caseManagementServiceClientProvider). lastModifiedDate solo se setea si no es null (hoy el repository no lo pasa).
RetornoReturnRetorno
CasesDTO (via response.toCasesDTO())(via response.toCasesDTO())(vía response.toCasesDTO())
Fluxo de usoUsage flowFlujo de uso
chamado pelo caminho remoto do repository (_fetchFromRemoteWithFallback), no pull-to-refresh; o resultado é gravado no cache.called by the repository's remote path (_fetchFromRemoteWithFallback), on pull-to-refresh; the result is written to cache.llamado por el camino remoto del repository (_fetchFromRemoteWithFallback), en pull-to-refresh; el resultado se graba en caché.
Tratamento de erroError handlingManejo de errores
GrpcErrorGrpcExceptionHandler; outros → ServerException. Em erro, o repository faz fallback pro cache.GrpcErrorGrpcExceptionHandler; others → ServerException. On error, the repository falls back to cache.GrpcErrorGrpcExceptionHandler; otros → ServerException. En error, el repository hace fallback al caché.
Local CaseManagementLocalDataSource ObjectBox

Envio / fluxo: persistência local via ObjectBox (ObjectBoxDatabase), boxes CasesModel, CaseModel e CaseSacModel — sem rede. Erro: falhas de persistência propagam como CacheException (não engolidas).Sends / flow: local persistence via ObjectBox (ObjectBoxDatabase), CasesModel, CaseModel and CaseSacModel boxes — no network. Error: persistence failures propagate as CacheException (not swallowed).Envío / flujo: persistencia local vía ObjectBox (ObjectBoxDatabase), boxes CasesModel, CaseModel y CaseSacModel — sin red. Error: fallos de persistencia propagan como CacheException (no tragados).

getCases()
RetornoReturnRetorno
CasesEntity?
ComportamentoBehaviorComportamiento
models.first.toDomain() — o agregado único, ou null se o cache está vazio.models.first.toDomain() — the single aggregate, or null if the cache is empty.models.first.toDomain() — el agregado único, o null si el caché está vacío.
getCasesLastSyncAt()
RetornoReturnRetorno
DateTime?
ComportamentoBehaviorComportamiento
models.first.lastSyncAt
saveCases({entity})
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
destrutivo: clearCases() + _box.put(entity.toModel()) (grava boxes filhas em cascata).destructive: clearCases() + _box.put(entity.toModel()) (writes child boxes in cascade).destructivo: clearCases() + _box.put(entity.toModel()) (graba boxes hijas en cascada).
markCaseClosed({caseSfid})
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
busca o CaseModel por caseSfid em getAll(), seta status = CaseStatus.closed.wireValue e faz put.finds the CaseModel by caseSfid in getAll(), sets status = CaseStatus.closed.wireValue and puts it.busca el CaseModel por caseSfid en getAll(), setea status = CaseStatus.closed.wireValue y hace put.
clearCases()
RetornoReturnRetorno
void
ComportamentoBehaviorComportamiento
limpa as boxes na ordem filhas→raiz (CaseSacModelCaseModelCasesModel).clears the boxes children→root (CaseSacModelCaseModelCasesModel).limpia las boxes hijas→raíz (CaseSacModelCaseModelCasesModel).
Mock CaseManagementMockDataSource JSON
getCases()
EnvioSendsEnvío
carrega o asset JSON case_management/cases (por mercado, real vs sintético via useRealMockData) — sem rede.loads the JSON asset case_management/cases (per market, real vs synthetic via useRealMockData) — no network.carga el asset JSON case_management/cases (por mercado, real vs sintético vía useRealMockData) — sin red.
RetornoReturnRetorno
CasesDTO (via CasesDTOJsonMapper.fromMap)(via CasesDTOJsonMapper.fromMap)(vía CasesDTOJsonMapper.fromMap)
Fluxo de usoUsage flowFlujo de uso
usado quando useMockData está ligado ou source == mock; grava no cache como um fetch normal.used when useMockData is on or source == mock; writes to cache like a normal fetch.usado cuando useMockData está activo o source == mock; graba en caché como un fetch normal.
Tratamento de erroError handlingManejo de errores
asset ausente ou JSON inválido propaga como CacheException.missing asset or invalid JSON propagates as CacheException.asset ausente o JSON inválido propaga como CacheException.
10

Enums e labelsEnums & labelsEnums y labels

status/priority/role trafegam como String nas camadas de dado; só role é tipado na Entity. status/priority são resolvidos por enum apenas na UI (via fromValue), com label traduzido (i18n). Lista completa:status/priority/role travel as String in the data layers; only role is typed in the Entity. status/priority are resolved to enums only in the UI (via fromValue), with a translated label (i18n). Full list:status/priority/role viajan como String en las capas de dato; solo role es tipado en la Entity. status/priority se resuelven a enums solo en la UI (vía fromValue), con label traducido (i18n). Lista completa:

CaseStatus 6 · wireValue + i18n
casewireValuei18n key
newCase"New"caseStatusNew
working"Working"caseStatusWorking
escalated"Escalated"caseStatusEscalated
onHold"On Hold"caseStatusOnHold
closed"Closed"caseStatusClosed
unknown""rawFallbackrawFallbackrawFallback
CasePriority 4 · wireValue + i18n
casewireValuei18n key
high"High"casePriorityHigh
medium"Medium"casePriorityMedium
low"Low"casePriorityLow
unknown""rawFallbackrawFallbackrawFallback
ContactRole 8 · compartilhado · labelshared · labelcompartido · label

De reference_data; o único enum tipado na CaseEntity (campo role, via fromLabel).From reference_data; the only enum typed in CaseEntity (the role field, via fromLabel).De reference_data; el único enum tipado en CaseEntity (el campo role, vía fromLabel).

caselabel
clerk"Clerk"
supervisor"Supervisor"
manager"Manager"
attendant"Attendant"
owner"Owner"
primaryContact"Primary Contact"
staff"Staff"
unknown""
DispatcherType · caseUpload 1 · serviceName + mercadosmarketsmercados
caseserviceNameenabledMarkets
caseUpload"CaseUploadAPI"[BR]
11

UseCases

Um dropdown por UseCase, com tabela Método · Retorna · Uso.One dropdown per UseCase, with a Method · Returns · Use table.Un dropdown por UseCase, con tabla Método · Devuelve · Uso.

GetCasesUseCase 4 métodosmethodsmétodos
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
execute({source = local})Result<CasesEntity, Failure>lista; build() usa local, refresh usa remote.list; build() uses local, refresh uses remote.lista; build() usa local, refresh usa remote.
getCached()Result<CasesEntity?, Failure>cache-only do container.cache-only of the container.cache-only del container.
getCachedLastSyncAt()DateTime?DataLoadInfo
getCachedBySfid({caseSfid})Result<CaseEntity?, Failure>alimenta o detalhe (§28 cat. A).feeds the detail (§28 cat. A).alimenta el detalle (§28 cat. A).
SaveCaseClosureUseCase 1 · escrita locallocal writeescritura local
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
execute({caseSfid})Result<void, Failure>delega markCaseClosed (cache local). Chamado pelo notifier só após o dispatch dar certo.delegates markCaseClosed (local cache). Called by the notifier only after the dispatch succeeds.delega markCaseClosed (caché local). Llamado por el notifier solo tras el dispatch exitoso.
BuildCaseUploadDispatcherPayloadUseCase 1 · builder
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
build({input: CaseUploadDispatcherPayloadInput})DispatcherEnvelopeimplements DispatcherPayloadBuilder (§36). Monta o JSON CaseUploadInfo (Status: "Closed", CReason: comment.trim(), CaseID: id, UpdById: resource.name) e o envelope (DispatcherType.caseUpload, DispatchAccountEntity do varejo, transactionReference = id). Input com entities cruas.implements DispatcherPayloadBuilder (§36). Builds the CaseUploadInfo JSON (Status: "Closed", CReason: comment.trim(), CaseID: id, UpdById: resource.name) and the envelope (DispatcherType.caseUpload, retail DispatchAccountEntity, transactionReference = id). Input with raw entities.implements DispatcherPayloadBuilder (§36). Arma el JSON CaseUploadInfo (Status: "Closed", CReason: comment.trim(), CaseID: id, UpdById: resource.name) y el sobre (DispatcherType.caseUpload, DispatchAccountEntity del punto de venta, transactionReference = id). Input con entities crudas.
SubmitCaseUploadUseCase 1 · transportetransporttransporte
MétodoMethodMétodoRetornaReturnsDevuelveUsoUseUso
submit({envelope})Result<DispatcherAck, Failure>delegate fino: encaminha ao DispatcherOrchestrator.dispatch. Não decide DispatcherType (é do builder).thin delegate: forwards to DispatcherOrchestrator.dispatch. Doesn't decide DispatcherType (the builder does).delegate fino: reenvía a DispatcherOrchestrator.dispatch. No decide DispatcherType (es del builder).
12

Notifier & State

Dois notifiers: CasesNotifier (lista) e CaseDetailNotifier (detalhe, family por caseSfid). O State Freezed é a fonte única de verdade da page.Two notifiers: CasesNotifier (list) and CaseDetailNotifier (detail, family by caseSfid). The Freezed State is the page's single source of truth.Dos notifiers: CasesNotifier (lista) y CaseDetailNotifier (detalle, family por caseSfid). El State Freezed es la fuente única de verdad de la page.

Métodos · listaMethods · listMétodos · lista

CasesNotifier.build()

Watch de getCasesUseCaseProvider e guardedBuild(() => _load()) (mixin AsyncGuard). _load({source = local}) monta o State a partir de execute(source:) (getOrThrow).Watches getCasesUseCaseProvider and guardedBuild(() => _load()) (AsyncGuard mixin). _load({source = local}) builds the State from execute(source:) (getOrThrow).Observa getCasesUseCaseProvider y guardedBuild(() => _load()) (mixin AsyncGuard). _load({source = local}) arma el State desde execute(source:) (getOrThrow).

loadMore()

Só client-state: incrementa visibleCount em kCasesPageSize (15), com clamp em totalCases. Não busca dado.Client-state only: bumps visibleCount by kCasesPageSize (15), clamped to totalCases. No data fetch.Solo client-state: incrementa visibleCount en kCasesPageSize (15), con clamp en totalCases. No busca dato.

refresh()

runGuarded(() => _load(source: remote)) — pull-to-refresh; força o remoto (sem AsyncValue.loading). Nome canônico refresh (§37).runGuarded(() => _load(source: remote)) — pull-to-refresh; forces remote (no AsyncValue.loading). Canonical name refresh (§37).runGuarded(() => _load(source: remote)) — pull-to-refresh; fuerza el remoto (sin AsyncValue.loading). Nombre canónico refresh (§37).

Métodos · detalheMethods · detailMétodos · detalle

CaseDetailNotifier.build({caseSfid})

Watch dos 4 useCases (get + build + submit + saveClosure) e _load cache-only via getCachedBySfid; ausente → BusinessFailure.Watches the 4 useCases (get + build + submit + saveClosure) and _load cache-only via getCachedBySfid; missing → BusinessFailure.Observa los 4 useCases (get + build + submit + saveClosure) y _load cache-only vía getCachedBySfid; ausente → BusinessFailure.

closeCase({comment}) → Future<Failure?>

Remote-first (§36/§39). Lê currentResourceProvider; seta isClosing: true; monta o DispatcherEnvelope (input com caseEntity, resource, comment, submittedAt: DateTimeUtils.now()); despacha via SubmitCaseUploadUseCase.Remote-first (§36/§39). Reads currentResourceProvider; sets isClosing: true; builds the DispatcherEnvelope (input with caseEntity, resource, comment, submittedAt: DateTimeUtils.now()); dispatches via SubmitCaseUploadUseCase.Remote-first (§36/§39). Lee currentResourceProvider; setea isClosing: true; arma el DispatcherEnvelope (input con caseEntity, resource, comment, submittedAt: DateTimeUtils.now()); despacha vía SubmitCaseUploadUseCase.

Sucesso (ack.isSuccess) → SaveCaseClosureUseCase.execute (marca cache local) + State com status = Closed e isClosing: false; retorna null. Falha → reverte isClosing e retorna a Failure (o widget mostra o ConectaNotice.error).Success (ack.isSuccess) → SaveCaseClosureUseCase.execute (marks local cache) + State with status = Closed and isClosing: false; returns null. Failure → reverts isClosing and returns the Failure (the widget shows ConectaNotice.error).Éxito (ack.isSuccess) → SaveCaseClosureUseCase.execute (marca caché local) + State con status = Closed e isClosing: false; devuelve null. Falla → revierte isClosing y devuelve la Failure (el widget muestra ConectaNotice.error).

State disponível para a PageState available to the PageState disponible para la Page

CasesState 2 campos + 5 gettersfields + 5 getterscampos + 5 getters
Campo / getterField / getterCampo / getterTipo / o que dáType / what it givesTipo / qué da
casesAsyncValue<CasesEntity>
visibleCountint (default 15)(default 15)(default 15)
lastSyncAtDateTime?
allCasesList<CaseEntity>
visibleCasesallCases.take(visibleCount)
totalCasesint
hasMoreToLoadvisibleCount < totalCases
CaseDetailState 2 campos + 1 getterfields + 1 gettercampos + 1 getter
Campo / getterField / getterCampo / getterTipo / o que dáType / what it givesTipo / qué da
caseEntityCaseEntity
isClosingbool (default false)(default false)(default false)
canCloseCaseStatus.fromValue(status) == newCase
13

Page e widgetsPage & widgetsPage y widgets

ListaListLista

  • CasesPage ConsumerWidget · AppPageShell
    • estadosstatesestados: CustomLoadingIndicator · FailureStateView (onRetry → invalidate)
      • _CasesBody ConsumerStatefulWidget · scroll infinito
        • CustomPullToRefresh onRefresh → notifier.refresh()
        • DataLoadInfo state.lastSyncAt
        • CasesHeaderWidget ícone + títuloicon + titleícono + título
        • CasesListWidget
          • CustomEmptyState quando vaziawhen emptycuando vacía
          • CaseCardWidget um por chamado · tap → goToCaseDetailone per case · tap → goToCaseDetailuno por caso · tap → goToCaseDetail
            • CaseFieldWidget label + valor ("-" se vazio)label + value ("-" if empty)label + valor ("-" si vacío)

DetalheDetailDetalle

  • CaseDetailPage ConsumerWidget · family(caseSfid) · AppPageShell
    • estadosstatesestados: CustomLoadingIndicator · FailureStateView
      • _CaseDetailBody SingleChildScrollView
        • CasesHeaderWidget
        • número do chamadocase numbernúmero del caso CustomText
        • CaseDetailSectionCardWidget varejo & contatoretail & contactpunto de venta y contacto
        • CaseDetailSectionCardWidget dados do chamadocase datadatos del caso
        • CaseDetailSectionCardWidget adicionaisadditionaladicionales
        • CaseDetailCloseButtonWidget self-check: SizedBox.shrink() se !canCloseself-check: SizedBox.shrink() if !canCloseself-check: SizedBox.shrink() si !canClose
          • CaseClosureModalContent modal · textarea min 50 chars · backWithResult<String>modal · textarea min 50 chars · backWithResult<String>modal · textarea mín 50 chars · backWithResult<String>

Fluxo do encerrar: o botão abre o ConectaModal com o CaseClosureModalContent; o comentário (≥ 50 chars) volta por backWithResult; o widget chama notifier.closeCase(comment); em sucesso, invalidate(casesProvider) + ConectaNotice.success + AppRouter.back.Close flow: the button opens ConectaModal with CaseClosureModalContent; the comment (≥ 50 chars) returns via backWithResult; the widget calls notifier.closeCase(comment); on success, invalidate(casesProvider) + ConectaNotice.success + AppRouter.back.Flujo de cierre: el botón abre ConectaModal con CaseClosureModalContent; el comentario (≥ 50 chars) vuelve vía backWithResult; el widget llama notifier.closeCase(comment); en éxito, invalidate(casesProvider) + ConectaNotice.success + AppRouter.back.

Notas por mercadoMarket notesNotas por mercado

Chamados é dirigido pela End Market Configuration: o atalho cases vive dentro do módulo rep_actions da Home. Só o Brasil declara esse detalhe (isVisible: true); em CL/ZA o módulo existe mas o detalhe cases está ausente; em AR/PY/PE o próprio rep_actions não existe. A escrita (CaseUploadAPI) também é BR-only (enabledMarkets: [BR]).Cases is driven by End Market Configuration: the cases shortcut lives inside Home's rep_actions module. Only Brazil declares that detail (isVisible: true); in CL/ZA the module exists but the cases detail is absent; in AR/PY/PE rep_actions itself doesn't exist. The write (CaseUploadAPI) is also BR-only (enabledMarkets: [BR]).Casos está dirigido por la End Market Configuration: el atajo cases vive dentro del módulo rep_actions del Home. Solo Brasil declara ese detalle (isVisible: true); en CL/ZA el módulo existe pero el detalle cases está ausente; en AR/PY/PE el propio rep_actions no existe. La escritura (CaseUploadAPI) también es BR-only (enabledMarkets: [BR]).

BRx CL ZA AR PY PE
disponívelavailabledisponible presente, desligadopresent, offpresente, apagado ausenteabsentausente
Chave EMCEMC keyClave EMCBRCLZAARPYPE
rep_actions (módulo)(module)(módulo) x x x
rep_actions › cases (detalhe)(detail)(detalle) x
DispatcherType.caseUpload (escrita)(write)(escritura) x
BR

Único mercadoOnly marketÚnico mercado Único com Chamados habilitado. O bloco CaseSac (SAC do consumidor: nome, CPF, lote, datas, endereço) é tipicamente brasileiro. O CNPJ/CPF do taxId é formatado por mercado via DocumentUtils.formatCompany. The only one with Cases enabled. The CaseSac block (consumer SAC: name, tax number, lot, dates, address) is typically Brazilian. The taxId is formatted per market via DocumentUtils.formatCompany. El único con Casos habilitado. El bloque CaseSac (SAC del consumidor: nombre, ID fiscal, lote, fechas, dirección) es típicamente brasileño. El taxId se formatea por mercado vía DocumentUtils.formatCompany.

CL · ZA · AR · PY · PE O atalho de Chamados não aparece na Home e não há rota de entrada. As camadas de dado (proto, DTO, model, mappers, mock por mercado) existem no código, mas sem a chave EMC a feature fica inacessível. The Cases shortcut doesn't appear on Home and there's no entry route. The data layers (proto, DTO, model, mappers, per-market mock) exist in the code, but without the EMC key the feature is unreachable. El atajo de Casos no aparece en el Home y no hay ruta de entrada. Las capas de dato (proto, DTO, model, mappers, mock por mercado) existen en el código, pero sin la clave EMC la feature queda inaccesible.