Call Task
As tarefas que o representante de vendas resolve dentro de uma visita: cada call task pede uma ação (verificar ruptura de produto, criar pedido, checar merchandising, responder pesquisa) e é concluída ali. Tarefas de ruptura (OOS) pedem uma resposta por produto (disponível / em falta); qualquer tarefa pode ser reagendada. Duas telas: o módulo na Detalhe da visita (lista com checkbox) e o Call Task Detail. The tasks the sales rep resolves inside a visit: each call task asks for an action (check product out-of-stock, create an order, check merchandising, answer a survey) and is completed right there. Out-of-stock (OOS) tasks ask for a per-product answer (available / missing); any task can be rescheduled. Two screens: the module in the Visit detail (checkbox list) and the Call Task Detail. Las tareas que el representante de ventas resuelve dentro de una visita: cada call task pide una acción (verificar quiebre de producto, crear pedido, revisar merchandising, responder encuesta) y se completa ahí mismo. Las tareas de quiebre (OOS) piden una respuesta por producto (disponible / faltante); cualquier tarea puede reprogramarse. Dos pantallas: el módulo en el Detalle de la visita (lista con checkbox) y el Call Task Detail.
O que é e para que serveWhat it is and what it's forQué es y para qué sirve
Uma Call Task é uma tarefa vinculada a uma visita — pense num "recado" que o representante precisa executar naquele varejo durante a visita. Cada tarefa tem uma categoria (ruptura, criação de pedido, merchandising, pesquisa…), um título, uma mensagem e um prazo. O representante conclui a tarefa e, quando é uma tarefa de ruptura (OOS), responde produto a produto se está disponível ou em falta. A Call Task is a task tied to a visit — think of it as a "note" the rep must execute at that retail during the visit. Each task has a category (out-of-stock, order creation, merchandising, survey…), a title, a message and a date. The rep completes the task and, when it's an out-of-stock (OOS) task, answers product by product whether it's available or missing. Una Call Task es una tarea vinculada a una visita — piénsala como un "recado" que el representante debe ejecutar en ese punto de venta durante la visita. Cada tarea tiene una categoría (quiebre, creación de pedido, merchandising, encuesta…), un título, un mensaje y una fecha. El representante completa la tarea y, cuando es una tarea de quiebre (OOS), responde producto por producto si está disponible o faltante.
O que preciso fazer?What must I do?¿Qué debo hacer?
Cada tarefa mostra a categoria, o título e a mensagem do que executar na visita.Each task shows the category, the title and the message of what to do on the visit.Cada tarea muestra la categoría, el título y el mensaje de qué ejecutar en la visita.
Está resolvida?Is it done?¿Está resuelta?
Um checkbox conclui a tarefa; tarefas OOS pedem uma resposta por produto antes.A checkbox completes the task; OOS tasks ask for a per-product answer first.Un checkbox completa la tarea; las OOS piden una respuesta por producto antes.
Não dá hoje?Not today?¿No hoy?
A tarefa pode ser reagendada para uma data futura (até 90 dias).The task can be rescheduled to a future date (up to 90 days).La tarea puede reprogramarse a una fecha futura (hasta 90 días).
Call Task ≠ Manager TasksCall Task ≠ Manager TasksCall Task ≠ Manager Tasks
Não confunda com as Manager Tasks (antes "Tarefas GTV"): aquelas vêm do gerente e abrem numa tela cheia própria. A Call Task vive dentro da visita, é ligada por visitSfid e só aparece com a visita iniciada.
Don't confuse it with Manager Tasks (formerly "GTV Tasks"): those come from the manager and open in their own full screen. The Call Task lives inside the visit, is linked by visitSfid and only shows once the visit has started.
No la confundas con las Manager Tasks (antes "Tareas GTV"): aquellas vienen del gerente y abren en su propia pantalla completa. La Call Task vive dentro de la visita, se vincula por visitSfid y solo aparece con la visita iniciada.
Como acessarHow to openCómo acceder
- Inicie a visitaStart the visitInicie la visitaAs Call Tasks só aparecem (e só podem ser tocadas/concluídas) com a visita iniciada — um guarda de check-in protege as duas ações.Call Tasks only show up (and can only be tapped/completed) once the visit has started — a check-in guard protects both actions.Las Call Tasks solo aparecen (y solo pueden tocarse/completarse) con la visita iniciada — un guardia de check-in protege ambas acciones.
- Abra o Detalhe da visitaOpen the Visit detailAbra el Detalle de la visitaRole até o módulo Call Tasks: uma lista de linhas, cada uma com ícone da categoria, título, tag de status e um checkbox.Scroll to the Call Tasks module: a list of rows, each with the category icon, title, status tag and a checkbox.Desplácese hasta el módulo Call Tasks: una lista de filas, cada una con el ícono de la categoría, título, etiqueta de estado y un checkbox.
- Toque numa tarefaTap a taskToque una tareaAbre o Call Task Detail — a tela cheia com a mensagem, os produtos OOS (se houver) e os botões de ação.Opens the Call Task Detail — the full screen with the message, the OOS products (if any) and the action buttons.Abre el Call Task Detail — la pantalla completa con el mensaje, los productos OOS (si hay) y los botones de acción.
Estrutura da telaScreen structureEstructura de la pantalla
A feature tem duas superfícies: o módulo dentro do Detalhe da visita e a tela de detalhe.The feature has two surfaces: the module inside the Visit detail and the detail screen.La feature tiene dos superficies: el módulo dentro del Detalle de la visita y la pantalla de detalle.
Módulo no Detalhe da visitaModule in the Visit detailMódulo en el Detalle de la visita
- TítuloTitleTítulo
- "Call Tasks". O módulo some por completo se o mercado não o habilita."Call Tasks". The whole module disappears if the market doesn't enable it."Call Tasks". El módulo entero desaparece si el mercado no lo habilita.
- Linha por tarefaRow per taskFila por tarea
- Ícone da categoria, título (até 2 linhas), tag de status colorida e um checkbox à direita.Category icon, title (up to 2 lines), colored status tag and a checkbox on the right.Ícono de la categoría, título (hasta 2 líneas), etiqueta de estado y un checkbox a la derecha.
- Lista vaziaEmpty listLista vacía
- Sem tarefas, mostra um estado vazio ("nenhuma call task").With no tasks, it shows an empty state ("no call tasks").Sin tareas, muestra un estado vacío ("ninguna call task").
Call Task Detail
- CabeçalhoHeaderEncabezado
- Data da última sincronização e o título "Call Task".Last sync date and the "Call Task" title.Fecha de última sincronización y el título "Call Task".
- Card da tarefaTask cardTarjeta de la tarea
- Ícone + nome da categoria, tag de status, título e a mensagem completa.Category icon + name, status tag, title and the full message.Ícono + nombre de la categoría, etiqueta de estado, título y el mensaje completo.
- Seção OOSOOS sectionSección OOS
- Só em tarefas de ruptura: um card por produto com dois botões (Disponível / Em falta) e um histórico de checagens anteriores.Only on out-of-stock tasks: one card per product with two buttons (Available / Out of stock) and a history of previous checks.Solo en tareas de quiebre: una tarjeta por producto con dos botones (Disponible / Faltante) y un historial de verificaciones anteriores.
- AçõesActionsAcciones
- Botão Concluir (preenchido) e botão Reagendar (contornado).Complete button (filled) and Reschedule button (outlined).Botón Completar (relleno) y botón Reprogramar (contorneado).
StatusStatusEstado
O status é derivado de um único sinal — se a tarefa foi respondida (isCompleted) — e aparece numa tag colorida:The status is derived from a single signal — whether the task was answered (isCompleted) — and shows in a colored tag:El estado se deriva de una única señal — si la tarea fue respondida (isCompleted) — y aparece en una etiqueta de color:
Concluída trava a telaCompleted locks the screenCompletada bloquea la pantalla Uma tarefa concluída abre o detalhe em modo leitura: o botão vira "Concluída", os botões de resposta OOS ficam desabilitados e o checkbox da lista aparece verde e marcado. A completed task opens the detail in read mode: the button turns "Completed", the OOS answer buttons are disabled and the list checkbox shows green and checked. Una tarea completada abre el detalle en modo lectura: el botón pasa a "Completada", los botones de respuesta OOS quedan deshabilitados y el checkbox de la lista aparece verde y marcado.
Ações: concluir, OOS, reagendarActions: complete, OOS, rescheduleAcciones: completar, OOS, reprogramar
- ConcluirCompleteCompletar
- Marca a tarefa como respondida. Pode ser feito pelo checkbox da lista (no Detalhe da visita) ou pelo botão Concluir na tela de detalhe. Numa tarefa OOS, só habilita quando todos os produtos foram respondidos.Marks the task as answered. Can be done via the list checkbox (in the Visit detail) or the Complete button on the detail screen. On an OOS task, it only enables once every product has been answered.Marca la tarea como respondida. Puede hacerse con el checkbox de la lista (en el Detalle de la visita) o con el botón Completar en la pantalla de detalle. En una tarea OOS, solo se habilita cuando todos los productos fueron respondidos.
- Responder OOSAnswer OOSResponder OOS
- Em tarefas de ruptura, cada produto tem dois botões: Disponível (verde) e Em falta (vermelho). A resposta é guardada por produto e enviada ao concluir.On out-of-stock tasks, each product has two buttons: Available (green) and Out of stock (red). The answer is kept per product and sent on completion.En tareas de quiebre, cada producto tiene dos botones: Disponible (verde) y Faltante (rojo). La respuesta se guarda por producto y se envía al completar.
- ReagendarRescheduleReprogramar
- Abre um calendário (de amanhã até +90 dias) para escolher a nova data. Disponível enquanto a tarefa não está concluída.Opens a calendar (tomorrow to +90 days) to pick the new date. Available while the task isn't completed.Abre un calendario (mañana hasta +90 días) para elegir la nueva fecha. Disponible mientras la tarea no esté completada.
Feedback do envioSubmit feedbackFeedback del envío Concluir e reagendar mostram um aviso verde de sucesso ou um aviso vermelho de erro. Concluir dispara a transação AnswerTaskAPI; reagendar dispara ReschedulingCallTaskAPI — as duas vão pelo Dispatcher. Complete and reschedule show a green success notice or a red error notice. Complete fires the AnswerTaskAPI transaction; reschedule fires ReschedulingCallTaskAPI — both go through the Dispatcher. Completar y reprogramar muestran un aviso verde de éxito o un aviso rojo de error. Completar dispara la transacción AnswerTaskAPI; reprogramar dispara ReschedulingCallTaskAPI — ambas van por el Dispatcher.
Arquitetura e fluxo de dadosArchitecture & data flowArquitectura y flujo de datos
Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Há dois fluxos distintos: a leitura (cache-first, com fallback remoto via getCallTasks) e a escrita (concluir e reagendar, cada uma via Dispatcher + uma mutação local no cache).Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. There are two distinct flows: the read (cache-first, with remote fallback via getCallTasks) and the write (complete and reschedule, each via the Dispatcher + a local cache mutation).Clean Architecture + Riverpod + Freezed + gRPC + ObjectBox. Hay dos flujos distintos: la lectura (cache-first, con fallback remoto vía getCallTasks) y la escritura (completar y reprogramar, cada una vía Dispatcher + una mutación local en el caché).
Leitura · cache-firstRead · cache-firstLectura · cache-first
A fonte default é local: o repository lê do ObjectBox; sem cache/offline, tenta o remoto (getCallTasks) e grava write-through. O Detalhe da visita filtra por visitSfid in-memory (getCallTasksForVisit); o Call Task Detail carrega o container e acha o item por id.The default source is local: the repository reads from ObjectBox; with no cache/offline it tries remote (getCallTasks) and writes through. The Visit detail filters by visitSfid in-memory (getCallTasksForVisit); the Call Task Detail loads the container and finds the item by id.La fuente por defecto es local: el repository lee de ObjectBox; sin caché/offline intenta el remoto (getCallTasks) y graba write-through. El Detalle de la visita filtra por visitSfid in-memory (getCallTasksForVisit); el Call Task Detail carga el container y encuentra el ítem por id.
- CallTasksReplygRPC proto
- toCallTasksDTOCallTasksDTODTO · Freezed
- toDomainCallTasksEntitydomain
- toModel · write-throughCallTasksModelObjectBox · cache
- toDomainCallTaskRepositoryImplgetCallTasks / forVisit
- executeGetCallTasks(ForVisit)UseCase
- watchCallTaskDetailNotifier · VisitDetailNotifier
- → UICallTaskDetailPage · VisitDetailCallTaskWidget
- watchCallTaskDetailNotifier · VisitDetailNotifier
- executeGetCallTasks(ForVisit)UseCase
- toDomainCallTaskRepositoryImplgetCallTasks / forVisit
- toModel · write-throughCallTasksModelObjectBox · cache
- toDomainCallTasksEntitydomain
- toCallTasksDTOCallTasksDTODTO · Freezed
Escrita · concluir e reagendar via DispatcherWrite · complete and reschedule via DispatcherEscritura · completar y reprogramar vía Dispatcher
Concluir monta o AnswerCallTaskDispatcherPayloadInput (com entities cruas + submittedAt), o builder produz o envelope answerTask e o submitter despacha; após o ack, o repository marca a tarefa como respondida no cache local. Reagendar é análogo com reschedulingCallTask.Complete assembles the AnswerCallTaskDispatcherPayloadInput (raw entities + submittedAt), the builder produces the answerTask envelope and the submitter dispatches; after the ack, the repository marks the task answered in the local cache. Reschedule is analogous with reschedulingCallTask.Completar arma el AnswerCallTaskDispatcherPayloadInput (entities crudas + submittedAt), el builder produce el sobre answerTask y el submitter despacha; tras el ack, el repository marca la tarea como respondida en el caché local. Reprogramar es análogo con reschedulingCallTask.
- CallTaskDetailActionsWidgetUI
- complete / rescheduleCallTaskDetailNotifier
- build(input)Build{Answer|Reschedule}CallTaskDispatcherPayloadUseCase→ DispatcherEnvelope
- submit(envelope)Submit{Answer|Reschedule}CallTaskUseCase
- dispatchDispatcherOrchestratorgRPC dispatcher
- on ack → cacheMark{...}Answered / rescheduleLocallyObjectBox
- dispatchDispatcherOrchestratorgRPC dispatcher
- submit(envelope)Submit{Answer|Reschedule}CallTaskUseCase
- build(input)Build{Answer|Reschedule}CallTaskDispatcherPayloadUseCase→ DispatcherEnvelope
- complete / rescheduleCallTaskDetailNotifier
Notas de implementaçãoImplementation notesNotas de implementación
O CallTaskDetailNotifier é family por callTaskId. As mutações do repository (markCallTaskAnswered, rescheduleCallTaskLocally) são só de cache e sempre retornam Success — o dado real vai pelo Dispatcher. O locationHierarchySfid do remoto é resolvido no repository via currentResourceProvider (§25), nunca no notifier.
CallTaskDetailNotifier is family by callTaskId. The repository mutations (markCallTaskAnswered, rescheduleCallTaskLocally) are cache-only and always return Success — the real write goes via the Dispatcher. The remote locationHierarchySfid is resolved in the repository via currentResourceProvider (§25), never in the notifier.
CallTaskDetailNotifier es family por callTaskId. Las mutaciones del repository (markCallTaskAnswered, rescheduleCallTaskLocally) son solo de caché y siempre retornan Success — la escritura real va vía Dispatcher. El locationHierarchySfid del remoto se resuelve en el repository vía currentResourceProvider (§25), nunca en el notifier.
Modelo de dadosData modelModelo de datos
O dado existe em quatro representações — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (domínio) — ligadas por mappers, com cache write-through. Os nomes se mantêm; muda pouco (enums tipados, parse de datas, relações e o rename de id no Model).The data exists in four representations — Proto (gRPC wire) → DTO (Freezed) → Model (ObjectBox) → Entity (domain) — linked by mappers, with cache write-through. Names stay the same; little changes (typed enums, date parsing, relations and the id rename in the Model).El dato existe en cuatro representaciones — Proto (wire gRPC) → DTO (Freezed) → Model (ObjectBox) → Entity (dominio) — unidas por mappers, con cache write-through. Los nombres se mantienen; cambia poco (enums tipados, parse de fechas, relaciones y el rename de id en el Model).
A lista chega num container CallTasks (callTasks[] + lastSyncAt gerado no mapper); cada item é uma CallTask de 11 campos, com CallTaskProduct (linhas OOS) e CallTaskOosHistory (histórico) aninhados. Os enums só existem tipados na Entity (CallTaskCode, CallTaskCategory); em Proto/DTO/Model trafegam como String. A seguir, na ordem: o proto, as estruturas de dados campo-a-campo por camada, e os mappers.The list arrives in a CallTasks container (callTasks[] + a mapper-generated lastSyncAt); each item is an 11-field CallTask, with nested CallTaskProduct (OOS rows) and CallTaskOosHistory (history). Enums are only typed in the Entity (CallTaskCode, CallTaskCategory); in Proto/DTO/Model they travel as String. Next, in order: the proto, the field-by-field data structures per layer, and the mappers.La lista llega en un container CallTasks (callTasks[] + un lastSyncAt generado en el mapper); cada ítem es una CallTask de 11 campos, con CallTaskProduct (filas OOS) y CallTaskOosHistory (historial) anidados. Los enums solo están tipados en la Entity (CallTaskCode, CallTaskCategory); en Proto/DTO/Model viajan como String. A continuación, en orden: el proto, las estructuras de datos campo a campo por capa, y los mappers.
Proto
TasksConectaRep.proto · proto3 · package mn.bat.conectarep.streambridge. O serviço TasksConectaRepService tem o método de Call Task (além do getTasksManager das Manager Tasks):The TasksConectaRepService service has the Call Task method (besides getTasksManager for Manager Tasks):El servicio TasksConectaRepService tiene el método de Call Task (además de getTasksManager de Manager Tasks):
getCallTasksunaryrpc getCallTasks(CallTasksRequest) returns (CallTasksReply)
path /mn.bat.conectarep.streambridge.TasksConectaRepService/getCallTasks
CallTasksRequestlocationHierarchySfidstring· #1 · hierarquia do representante de vendassales rep hierarchyjerarquía del representante de ventasdateReferencestring· #2 · optionallastModifiedDatestring· #3 · optional
Não há visitSfid no request — a busca é por hierarquia; o filtro por visita acontece no app.There's no visitSfid in the request — the fetch is by hierarchy; the per-visit filter happens app-side.No hay visitSfid en el request — la búsqueda es por jerarquía; el filtro por visita ocurre en la app.
CallTasksReplyrepeated CallTask callTasks — a lista de call tasks. Os 11 campos de cada CallTask estão nas Estruturas de dados abaixo.the list of call tasks. The 11 fields of each CallTask are in Data structures below.la lista de call tasks. Los 11 campos de cada CallTask están 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; o texto azul marca onde o tipo (ou o nome) primeiro muda (rename de id e parse de data no Model, relação ToMany no Model, enum na Entity). ¹ = optional no proto.One dropdown per structure, nested by hierarchy. Each table has one column per layer — Proto · DTO · Model · Entity; the blue text marks where the type (or name) first changes (id rename and date parse in the Model, ToMany relation in the Model, enum in the Entity). ¹ = optional in the proto.Un dropdown por estructura, anidados por jerarquía. Cada tabla tiene una columna por capa — Proto · DTO · Model · Entity; el texto azul marca dónde primero cambia el tipo (o el nombre) (rename de id y parse de fecha en el Model, relación ToMany en el Model, enum en la Entity). ¹ = optional en el proto.
CallTasks container 2 camposfieldscampos
Campo Proto DTO Model Entity callTasksrepeated CallTask List<…DTO> ToMany<…Model>List<…Entity> lastSyncAt— DateTime? DateTimeDateTime? CallTask CallTasks.callTasks[] 11 camposfieldscampos
Campo Proto DTO Model Entity idstring String callTaskIdString callTaskCodestring String String CallTaskCodevisitSfidstring String String String categorystring String String CallTaskCategorytitlestring String String String messagestring String String String urlstring String String String originalDatestring String DateTime?DateTime? answerDatestring String DateTime?DateTime? isCompletedbool bool bool bool productsrepeated CallTaskProduct List<…DTO> ToMany<…Model>List<…Entity> CallTaskProduct CallTask.products[] 6 camposfieldscampos
Campo Proto DTO Model Entity idstring String productIdString productSfidstring String String String productNamestring String String String answerstring String String String dateReferencestring String DateTime?DateTime? historyrepeated CallTaskOosHistory List<…DTO> ToMany<…Model>List<…Entity> CallTaskOosHistory CallTaskProduct.history[] 2 camposfieldscampos
Campo Proto DTO Model Entity datestring String DateTime?DateTime? statusint32 int int int
CallTaskProduct.answer = "1" disponível / "0" em falta / "" sem resposta; CallTaskOosHistory.status = 1 disponível / 0 em falta (int, não enum).CallTaskProduct.answer = "1" available / "0" out of stock / "" unanswered; CallTaskOosHistory.status = 1 available / 0 out of stock (int, not enum).CallTaskProduct.answer = "1" disponible / "0" faltante / "" sin respuesta; CallTaskOosHistory.status = 1 disponible / 0 faltante (int, no enum).
Mappers
Todas as conversões são extension, em dois arquivos (call_tasks_mapper.dart container + call_task_mapper.dart item), nas 5 direções por tipo.All conversions are extensions, in two files (call_tasks_mapper.dart container + call_task_mapper.dart item), in 5 directions per type.Todas las conversiones son extension, en dos archivos (call_tasks_mapper.dart container + call_task_mapper.dart ítem), en 5 direcciones por tipo.
| DireçãoDirectionDirección | MétodoMethodMétodo |
|---|---|
| JSON → DTO | static fromMap(Map) (carimba lastSyncAt com DateTimeUtils.now())(stamps lastSyncAt with DateTimeUtils.now())(sella lastSyncAt con DateTimeUtils.now()) |
| Proto → DTO | toCallTasksDTO() / toDTO() (carimba lastSyncAt)(stamps lastSyncAt)(sella lastSyncAt) |
| DTO → Entity | toDomain() (resolve enums fromValue + datas DateTimeUtils.tryParse)(resolves enums fromValue + dates DateTimeUtils.tryParse)(resuelve enums fromValue + fechas DateTimeUtils.tryParse) |
| Entity → Model | toModel() (enums → .value; id→callTaskId/productId; guarda StateError se lastSyncAt null)(enums → .value; id→callTaskId/productId; guards StateError if lastSyncAt null)(enums → .value; id→callTaskId/productId; guarda StateError si lastSyncAt null) |
| Model → Entity | toDomain() (callTaskId/productId→id; enums fromValue)(callTaskId/productId→id; enums fromValue)(callTaskId/productId→id; enums fromValue) |
Os únicos deltasThe only deltasLos únicos deltas
- enums tipados só na Entity (
callTaskCode→CallTaskCode,category→CallTaskCategory;Stringnas outras)enums typed only in the Entity (callTaskCode→CallTaskCode,category→CallTaskCategory;Stringelsewhere)enums tipados solo en la Entity (callTaskCode→CallTaskCode,category→CallTaskCategory;Stringen las demás) originalDate·answerDate·dateReference·dateString→DateTime?(no Model, viaDateTimeUtils.tryParse)String→DateTime?(in the Model, viaDateTimeUtils.tryParse)String→DateTime?(en el Model, víaDateTimeUtils.tryParse)- rename de
id:callTaskId(CallTask) /productId(CallTaskProduct) no Model — ObjectBox reservaidcomo@Id intidrename:callTaskId(CallTask) /productId(CallTaskProduct) in the Model — ObjectBox reservesidas@Id intrename deid:callTaskId(CallTask) /productId(CallTaskProduct) en el Model — ObjectBox reservaidcomo@Id int - relações (
callTasks,products,history) viramToManyno Modelrelations (callTasks,products,history) becomeToManyin the Modelrelaciones (callTasks,products,history) pasan aToManyen el Model lastSyncAtgerado no mapper de fronteira (JSON/Proto) comDateTimeUtils.now()— não existe no protogenerated in the boundary mapper (JSON/Proto) withDateTimeUtils.now()— absent from the protogenerado en el mapper de frontera (JSON/Proto) conDateTimeUtils.now()— no existe en el proto
Repository
O CallTaskRepositoryImpl (provider keepAlive) injeta os 3 datasources + connectivityService + useMock + ref. A leitura é cache-first; as duas mutações são só de cache (o backend recebe pelo Dispatcher).CallTaskRepositoryImpl (keepAlive provider) injects the 3 datasources + connectivityService + useMock + ref. The read is cache-first; both mutations are cache-only (the backend gets them via the Dispatcher).El CallTaskRepositoryImpl (provider keepAlive) inyecta los 3 datasources + connectivityService + useMock + ref. La lectura es cache-first; las dos mutaciones son solo de caché (el backend las recibe vía Dispatcher).
getCallTasks({source = local}) mock / local / remote
RetornaReturnsDevuelve Future<Result<CallTasksEntity, Failure>>
Ponto de entrada da leitura. Decisão de fonte:Read entry point. Source decision:Punto de entrada de la lectura. Decisión de fuente:
- getCallTasks
_useMock || source == mock→ _fetchFromMock() + cache writesource == local || !isConnected→ _fetchFromCacheOrFail() · default- senãoelsesi no → _fetchFromRemoteWithFallback()
_fetchFromRemoteWithFallback resolve resource via currentResourceProvider (§25); null → cai pro cache. Chama remote.getCallTasks(locationHierarchySfid: resource.locationHierarchyId), grava no cache; erro → fallback pro cache. _fetchFromCacheOrFail: cache ausente → Error(NetworkFailure)._fetchFromRemoteWithFallback resolves resource via currentResourceProvider (§25); null → falls to cache. Calls remote.getCallTasks(locationHierarchySfid: resource.locationHierarchyId), writes cache; error → cache fallback. _fetchFromCacheOrFail: missing cache → Error(NetworkFailure)._fetchFromRemoteWithFallback resuelve resource vía currentResourceProvider (§25); null → cae al caché. Llama remote.getCallTasks(locationHierarchySfid: resource.locationHierarchyId), graba caché; error → fallback al caché. _fetchFromCacheOrFail: caché ausente → Error(NetworkFailure).
getCallTasksForVisit({visitSfid}) filtro por visitafilter by visitfiltro por visita
RetornaReturnsDevuelve Future<Result<List<CallTaskEntity>, Failure>>
visitSfid vazio → Success([]); senão chama getCallTasks() (local) e filtra in-memory por callTask.visitSfid == visitSfid. Não dispara remoto próprio. Usado pelo Detalhe da visita.Empty visitSfid → Success([]); otherwise calls getCallTasks() (local) and filters in-memory by callTask.visitSfid == visitSfid. No remote of its own. Used by the Visit detail.visitSfid vacío → Success([]); si no llama getCallTasks() (local) y filtra in-memory por callTask.visitSfid == visitSfid. Sin remoto propio. Usado por el Detalle de la visita.
getCachedCallTasksLastSyncAt() local
RetornaReturnsDevuelve Future<DateTime?>
Delega a local.getCallTasksLastSyncAt(); erro → log + null.Delegates to local.getCallTasksLastSyncAt(); error → log + null.Delega a local.getCallTasksLastSyncAt(); error → log + null.
markCallTaskAnswered({callTaskId}) só cachecache-onlysolo caché
RetornaReturnsDevuelve Future<Result<void, Failure>>
Chama local.updateCallTaskCompleted(callTaskId:) (marca isCompleted=true, answerDate=now). try/catch só loga; sempre retorna Success(null) — o envio real vai pelo Dispatcher.Calls local.updateCallTaskCompleted(callTaskId:) (sets isCompleted=true, answerDate=now). try/catch only logs; always returns Success(null) — the real submit goes via the Dispatcher.Llama local.updateCallTaskCompleted(callTaskId:) (marca isCompleted=true, answerDate=now). try/catch solo loguea; siempre retorna Success(null) — el envío real va vía Dispatcher.
rescheduleCallTaskLocally({callTaskId, newDate}) só cachecache-onlysolo caché
RetornaReturnsDevuelve Future<Result<void, Failure>>
Chama local.updateCallTaskOriginalDate(callTaskId:, newDate:). try/catch só loga; sempre retorna Success(null) — o reagendamento real vai pelo Dispatcher.Calls local.updateCallTaskOriginalDate(callTaskId:, newDate:). try/catch only logs; always returns Success(null) — the real reschedule goes via the Dispatcher.Llama local.updateCallTaskOriginalDate(callTaskId:, newDate:). try/catch solo loguea; siempre retorna Success(null) — la reprogramación real va vía Dispatcher.
Datasources
Três datasources: Mock (assets por mercado), Local (ObjectBox, também dono das mutações de cache) e Remote (gRPC).Three datasources: Mock (per-market assets), Local (ObjectBox, also owner of the cache mutations) and Remote (gRPC).Tres datasources: Mock (assets por mercado), Local (ObjectBox, también dueño de las mutaciones de caché) y Remote (gRPC).
Remote CallTaskRemoteDataSource gRPC
Envio / fluxo: TasksConectaRepServiceClient. Erro: GrpcError → GrpcExceptionHandler.handle; outros → ServerException.Sends / flow: TasksConectaRepServiceClient. Error: GrpcError → GrpcExceptionHandler.handle; others → ServerException.Envío / flujo: TasksConectaRepServiceClient. Error: GrpcError → GrpcExceptionHandler.handle; otros → ServerException.
getCallTasks({locationHierarchySfid, dateReference?, lastModifiedDate?})
- RetornoReturnRetorno
Future<CallTasksEntity>- EnvioSendsEnvío
CallTasksRequest(locationHierarchySfid+dateReference?+lastModifiedDate?quando não-vazios)- FluxoFlowFlujo
- stub
getCallTasks;reply.toCallTasksDTO().toDomain().stubgetCallTasks;reply.toCallTasksDTO().toDomain().stubgetCallTasks;reply.toCallTasksDTO().toDomain().
Local CallTaskLocalDataSource ObjectBox
CRUD sobre 4 boxes (CallTasksModel + CallTaskModel + CallTaskProductModel + CallTaskOosHistoryModel). Erros viram CacheException.CRUD over 4 boxes (CallTasksModel + CallTaskModel + CallTaskProductModel + CallTaskOosHistoryModel). Errors become CacheException.CRUD sobre 4 boxes (CallTasksModel + CallTaskModel + CallTaskProductModel + CallTaskOosHistoryModel). Errores viran CacheException.
getCachedCallTasks()
- RetornoReturnRetorno
CallTasksEntity?- ComportamentoBehaviorComportamiento
- primeira linha do box →
toDomain();nullse vazio.first row of the box →toDomain();nullif empty.primera fila del box →toDomain();nullsi vacío.
getCallTasksLastSyncAt()
- RetornoReturnRetorno
DateTime?- ComportamentoBehaviorComportamiento
- só o timestamp da primeira linha.just the first row's timestamp.solo el timestamp de la primera fila.
saveCallTasks({entity})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
clearCallTasks()+put(entity.toModel())(substitui tudo).clearCallTasks()+put(entity.toModel())(replaces all).clearCallTasks()+put(entity.toModel())(reemplaza todo).
updateCallTaskCompleted({callTaskId})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- acha por
callTaskId, setaisCompleted=true+answerDate=DateTimeUtils.now(),put.finds bycallTaskId, setsisCompleted=true+answerDate=DateTimeUtils.now(),put.encuentra porcallTaskId, seteaisCompleted=true+answerDate=DateTimeUtils.now(),put.
updateCallTaskOriginalDate({callTaskId, newDate})
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- acha por
callTaskId, setaoriginalDate=newDate,put.finds bycallTaskId, setsoriginalDate=newDate,put.encuentra porcallTaskId, seteaoriginalDate=newDate,put.
mergeAdhocCallTasks({incoming, refreshedVisitSfids}) merge aditivo · Visita Adhocadditive merge · Adhoc Visitmerge aditivo · Visita Adhoc
- RetornoReturnRetorno
void- AssinaturaSignatureFirma
mergeAdhocCallTasks({required List<CallTaskEntity> incoming, required Set<String> refreshedVisitSfids})- ComportamentoBehaviorComportamiento
- Caminho de escrita read-modify-write chamado pela Visita Adhoc (não pelo Repository de Call Task): lê o cache via
getCachedCallTasks(), delega aAdhocCallTasksMerge.merge(current:, incoming:, refreshedVisitSfids:)e persiste viasaveCallTasks(entity:). O merge retém toda call task cujovisitSfidnão está emrefreshedVisitSfidse anexa as recebidas. ComoCallTasknão temaccountSfid, o escopo é por visita:refreshedVisitSfids= união de (visitSfiddas call tasks recebidas) ∪ (sfiddas visitas do reply), sem strings vazias. Erro →CacheException.Read-modify-write write path called by the Adhoc Visit (not by the Call Task Repository): reads the cache viagetCachedCallTasks(), delegates toAdhocCallTasksMerge.merge(current:, incoming:, refreshedVisitSfids:)and persists viasaveCallTasks(entity:). The merge retains every call task whosevisitSfidis not inrefreshedVisitSfidsand appends the incoming ones. SinceCallTaskhas noaccountSfid, the scope is per visit:refreshedVisitSfids= union of (incoming call tasks'visitSfid) ∪ (reply visits'sfid), empty strings removed. Error →CacheException.Read-modify-write llamado por la Visita Adhoc (no por el Repository de Call Task): lee el caché víagetCachedCallTasks(), delega aAdhocCallTasksMerge.merge(current:, incoming:, refreshedVisitSfids:)y persiste víasaveCallTasks(entity:). El merge retiene toda call task cuyovisitSfidno está enrefreshedVisitSfidsy anexa las recibidas. ComoCallTaskno tieneaccountSfid, el alcance es por visita:refreshedVisitSfids= unión de (visitSfidde las call tasks recibidas) ∪ (sfidde las visitas del reply), sin strings vacías. Error →CacheException.
clearCallTasks()
- RetornoReturnRetorno
void- ComportamentoBehaviorComportamiento
- esvazia os 4 boxes (history → product → callTask → container).empties the 4 boxes (history → product → callTask → container).vacía los 4 boxes (history → product → callTask → container).
Mock CallTaskMockDataSource assets
Envio / fluxo: lê currentMarketProvider e carrega assets/mocks/call_task/{market}_call_task.json. Erro: CacheException.Sends / flow: reads currentMarketProvider and loads assets/mocks/call_task/{market}_call_task.json. Error: CacheException.Envío / flujo: lee currentMarketProvider y carga assets/mocks/call_task/{market}_call_task.json. Error: CacheException.
getCallTasks()
- RetornoReturnRetorno
Future<CallTasksEntity>- ComportamentoBehaviorComportamiento
loadMockAssetString→jsonDecode→CallTasksDTOJsonMapper.fromMap→toDomain().loadMockAssetString→jsonDecode→CallTasksDTOJsonMapper.fromMap→toDomain().loadMockAssetString→jsonDecode→CallTasksDTOJsonMapper.fromMap→toDomain().
Enums e labelsEnums & labelsEnums y labels
CallTaskCategory 13 · value == name
| case | value | i18n key |
|---|---|---|
orderCreation | "orderCreation" | callTaskCategoryOrderCreation |
buyback | "buyback" | callTaskCategoryBuyback |
merchan | "merchan" | callTaskCategoryMerchan |
financialManagement | "financialManagement" | callTaskCategoryFinancialManagement |
oos | "oos" | callTaskCategoryOos |
surveys | "surveys" | callTaskCategorySurveys |
tasks | "tasks" | callTaskCategoryTasks |
avancePerformance | "avancePerformance" | callTaskCategoryAvancePerformance |
shelfWatch | "shelfWatch" | callTaskCategoryShelfWatch |
deliveriesOfTheDay | "deliveriesOfTheDay" | callTaskCategoryDeliveriesOfTheDay |
staff | "staff" | callTaskCategoryStaff |
notes | "notes" | callTaskCategoryNotes |
unknown | "unknown" | callTaskCategoryUnknown |
fromValue faz trim e cai em unknown. O icon é o mesmo para todas (ConectaIcons.tasksTypeCopyAlt); só o label muda por categoria.fromValue trims and falls back to unknown. The icon is the same for all (ConectaIcons.tasksTypeCopyAlt); only the label changes per category.fromValue hace trim y cae en unknown. El icon es el mismo para todas (ConectaIcons.tasksTypeCopyAlt); solo el label cambia por categoría.
CallTaskStatus 3 · value + tagVariant
| case | value | tagVariant |
|---|---|---|
pending | "pending" | warning (laranja)(orange)(naranja) |
completed | "completed" | positive (verde)(green)(verde) |
unknown | "unknown" | neutral |
O getter CallTaskEntity.status só produz pending/completed (derivado de isCompleted); unknown existe para fromValue. static selectable = valores menos unknown.The CallTaskEntity.status getter only yields pending/completed (derived from isCompleted); unknown exists for fromValue. static selectable = values minus unknown.El getter CallTaskEntity.status solo produce pending/completed (derivado de isCompleted); unknown existe para fromValue. static selectable = valores menos unknown.
CallTaskCode 2 · discriminador OOSOOS discriminatordiscriminador OOS
| case | value | significadomeaningsignificado |
|---|---|---|
oos | "CallTaskOOS" | tarefa de ruptura (respostas por produto)out-of-stock task (per-product answers)tarea de quiebre (respuestas por producto) |
general | "" | tarefa simples (fallback de fromValue)simple task (fromValue fallback)tarea simple (fallback de fromValue) |
DispatcherType · call task 2 · serviceName + mercadosmarketsmercados
| case | serviceName | mercadosmarketsmercados | propósitopurposepropósito |
|---|---|---|---|
answerTask | AnswerTaskAPI | BR · CL · ZA | conclusão da tarefa (+ respostas OOS)task completion (+ OOS answers)completar tarea (+ respuestas OOS) |
reschedulingCallTask | ReschedulingCallTaskAPI | BR · CL · ZA | reagendamento da call taskcall task reschedulereprogramación de la call task |
CallTaskDetailSubmissionStatus · CallTaskDetailAction estado do enviosubmit stateestado del envío
| enum | valoresvaluesvalores |
|---|---|
CallTaskDetailSubmissionStatus | idle · submitting · success · failure |
CallTaskDetailAction | none · complete · reschedule |
UseCases
Um dropdown por UseCase; dentro, cada método com assinatura, o que retorna e uso. Todos têm provider keepAlive.One dropdown per UseCase; inside, each method with its signature, what it returns and use. All have a keepAlive provider.Un dropdown por UseCase; dentro, cada método con su firma, qué devuelve y uso. Todos tienen provider keepAlive.
LeituraReadLectura
GetCallTasksUseCase 2
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({source = local}) | Result<CallTasksEntity, Failure> | delega a repository.getCallTasks. Alimenta o Call Task Detail (busca por id).delegates to repository.getCallTasks. Feeds the Call Task Detail (finds by id).delega a repository.getCallTasks. Alimenta el Call Task Detail (busca por id). |
getCachedLastSyncAt() | DateTime? | timestamp para o DataLoadInfo.timestamp for DataLoadInfo.timestamp para DataLoadInfo. |
GetCallTasksForVisitUseCase 1
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
execute({visitSfid}) | Result<List<CallTaskEntity>, Failure> | delega a repository.getCallTasksForVisit. Usado pelo Detalhe da visita.delegates to repository.getCallTasksForVisit. Used by the Visit detail.delega a repository.getCallTasksForVisit. Usado por el Detalle de la visita. |
Escrita (cache) e transaçãoWrite (cache) and transactionEscritura (caché) y transacción
MarkCallTaskAnsweredUseCase · RescheduleCallTaskUseCase 2 · só cachecache-onlysolo caché
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
MarkCallTaskAnsweredUseCase.execute({callTaskId}) | Result<void, Failure> | → repository.markCallTaskAnswered (após o ack do Dispatcher).→ repository.markCallTaskAnswered (after the Dispatcher ack).→ repository.markCallTaskAnswered (tras el ack del Dispatcher). |
RescheduleCallTaskUseCase.execute({callTaskId, newDate}) | Result<void, Failure> | → repository.rescheduleCallTaskLocally (após o ack).→ repository.rescheduleCallTaskLocally (after the ack).→ repository.rescheduleCallTaskLocally (tras el ack). |
Build{Answer|Reschedule}CallTaskDispatcherPayloadUseCase 2 · builders
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
build({input: AnswerCallTaskDispatcherPayloadInput}) | DispatcherEnvelope | implements DispatcherPayloadBuilder (§36). Input cru (callTask, visitSfid, submittedAt). DispatcherType.answerTask. Monta answerTask[] com oosAnswerList por produto.implements DispatcherPayloadBuilder (§36). Raw input (callTask, visitSfid, submittedAt). DispatcherType.answerTask. Builds answerTask[] with a per-product oosAnswerList.implements DispatcherPayloadBuilder (§36). Input crudo (callTask, visitSfid, submittedAt). DispatcherType.answerTask. Arma answerTask[] con oosAnswerList por producto. |
build({input: RescheduleCallTaskDispatcherPayloadInput}) | DispatcherEnvelope | Input cru (callTask, visitSfid, newDate, resource, submittedAt). DispatcherType.reschedulingCallTask. sfid_rep = resource.sfid derivado no build() (§25 exceção de escrita).Raw input (callTask, visitSfid, newDate, resource, submittedAt). DispatcherType.reschedulingCallTask. sfid_rep = resource.sfid derived in build() (§25 write exception).Input crudo (callTask, visitSfid, newDate, resource, submittedAt). DispatcherType.reschedulingCallTask. sfid_rep = resource.sfid derivado en build() (§25 excepción de escritura). |
Submit{Answer|Reschedule}CallTaskUseCase 2 · transportetransporttransporte
| MétodoMethodMétodo | RetornaReturnsDevuelve | UsoUseUso |
|---|---|---|
submit({envelope}) | Result<DispatcherAck, Failure> | delegate fino: orchestrator.dispatch(envelope:). Não decide o DispatcherType (isso é do builder).thin delegate: orchestrator.dispatch(envelope:). Doesn't decide the DispatcherType (the builder does).delegate fino: orchestrator.dispatch(envelope:). No decide el DispatcherType (eso es del builder). |
Payload da conclusãoCompletion payloadPayload de la conclusión
O answerTask[] leva visitUuid, answerDate (iso datetime de submittedAt), originaldate, callTaskVisit (= callTask.id), callTaskCode, answer: true e o oosAnswerList (um item por produto: answer, callTaskVisitProduct, product_name, call_task_visit). O reagendar leva sfid_visit_current, rescheduling_visit_date, sfid_rep, id_call_task_visit, call_task_visit_original_date.
The answerTask[] carries visitUuid, answerDate (iso datetime of submittedAt), originaldate, callTaskVisit (= callTask.id), callTaskCode, answer: true and the oosAnswerList (one item per product: answer, callTaskVisitProduct, product_name, call_task_visit). Reschedule carries sfid_visit_current, rescheduling_visit_date, sfid_rep, id_call_task_visit, call_task_visit_original_date.
El answerTask[] lleva visitUuid, answerDate (iso datetime de submittedAt), originaldate, callTaskVisit (= callTask.id), callTaskCode, answer: true y el oosAnswerList (un ítem por producto: answer, callTaskVisitProduct, product_name, call_task_visit). Reprogramar lleva sfid_visit_current, rescheduling_visit_date, sfid_rep, id_call_task_visit, call_task_visit_original_date.
Notifier & State
O CallTaskDetailNotifier (@riverpod, family por callTaskId) é o cérebro da tela de detalhe. O build() carrega o container do cache e acha o item por id. O State (CallTaskDetailState, Freezed) é a fonte única de verdade. A conclusão pelo checkbox da lista mora no VisitDetailNotifier (ver nota).CallTaskDetailNotifier (@riverpod, family by callTaskId) is the detail screen's brain. build() loads the container from cache and finds the item by id. The State (CallTaskDetailState, Freezed) is the single source of truth. Completing via the list checkbox lives in VisitDetailNotifier (see note).El CallTaskDetailNotifier (@riverpod, family por callTaskId) es el cerebro de la pantalla de detalle. El build() carga el container del caché y encuentra el ítem por id. El State (CallTaskDetailState, Freezed) es la fuente única de verdad. Completar por el checkbox de la lista vive en VisitDetailNotifier (ver nota).
MétodosMethodsMétodos
build({callTaskId}) cache
RetornoReturnRetorno FutureOr<CallTaskDetailState>
Carrega o container via GetCallTasksUseCase.execute() (cache), acha o item pelo id (StateError se ausente) e retorna o State com lastSyncAt do container.Loads the container via GetCallTasksUseCase.execute() (cache), finds the item by id (StateError if missing) and returns the State with the container's lastSyncAt.Carga el container vía GetCallTasksUseCase.execute() (caché), encuentra el ítem por id (StateError si ausente) y retorna el State con el lastSyncAt del container.
setProductAnswer({productId, answer})
RetornoReturnRetorno void
Copia o answer do produto correspondente (resposta OOS local); habilita/desabilita o botão Concluir via allProductsAnswered.Copies the matching product's answer (local OOS answer); enables/disables the Complete button via allProductsAnswered.Copia el answer del producto correspondiente (respuesta OOS local); habilita/deshabilita el botón Completar vía allProductsAnswered.
complete()
RetornoReturnRetorno Future<void>
Guarda canComplete; marca submitting. Monta AnswerCallTaskDispatcherPayloadInput → SubmitAnswerCallTaskUseCase; no ack de sucesso → MarkCallTaskAnsweredUseCase, seta isCompleted:true, answerDate:now, status success.Guards canComplete; sets submitting. Builds AnswerCallTaskDispatcherPayloadInput → SubmitAnswerCallTaskUseCase; on ack success → MarkCallTaskAnsweredUseCase, sets isCompleted:true, answerDate:now, status success.Guarda canComplete; marca submitting. Arma AnswerCallTaskDispatcherPayloadInput → SubmitAnswerCallTaskUseCase; en ack de éxito → MarkCallTaskAnsweredUseCase, setea isCompleted:true, answerDate:now, status success.
reschedule({newDate})
RetornoReturnRetorno Future<void>
Guarda canReschedule; resolve currentResourceProvider (null → falha). Monta RescheduleCallTaskDispatcherPayloadInput → SubmitRescheduleCallTaskUseCase; no sucesso → RescheduleCallTaskUseCase.execute, seta originalDate:newDate, status success.Guards canReschedule; resolves currentResourceProvider (null → failure). Builds RescheduleCallTaskDispatcherPayloadInput → SubmitRescheduleCallTaskUseCase; on success → RescheduleCallTaskUseCase.execute, sets originalDate:newDate, status success.Guarda canReschedule; resuelve currentResourceProvider (null → fallo). Arma RescheduleCallTaskDispatcherPayloadInput → SubmitRescheduleCallTaskUseCase; en éxito → RescheduleCallTaskUseCase.execute, setea originalDate:newDate, status success.
resetSubmissionStatus()
RetornoReturnRetorno void
Volta o submissionStatus para idle após a Page mostrar o aviso.Returns submissionStatus to idle after the Page shows the notice.Vuelve el submissionStatus a idle tras que la Page muestre el aviso.
State disponível para a PageState available to the PageState disponible para la Page
CallTaskDetailState campos + gettersfields + getterscampos + getters
| campo | tipo | default |
|---|---|---|
callTask | CallTaskEntity | required |
lastSyncAt | DateTime? | null |
submissionStatus | CallTaskDetailSubmissionStatus | idle |
lastAction | CallTaskDetailAction | none |
submissionErrorMessage | String? | null |
Getters: isOos, isCompleted, isSubmitting, allProductsAnswered (todo produto com answer não-vazio), canComplete (!isCompleted && !isSubmitting && (!isOos || allProductsAnswered)), canReschedule (!isSubmitting).Getters: isOos, isCompleted, isSubmitting, allProductsAnswered (every product with non-empty answer), canComplete (!isCompleted && !isSubmitting && (!isOos || allProductsAnswered)), canReschedule (!isSubmitting).Getters: isOos, isCompleted, isSubmitting, allProductsAnswered (todo producto con answer no vacío), canComplete (!isCompleted && !isSubmitting && (!isOos || allProductsAnswered)), canReschedule (!isSubmitting).
Integração no Detalhe da visitaVisit detail integrationIntegración en el Detalle de la visita
O VisitDetailState guarda List<CallTaskEntity> visitCallTasks; o VisitDetailNotifier._loadVisitCallTasks chama GetCallTasksForVisitUseCase e ordena (incompletas primeiro, depois por originalDate). O completeCallTask({callTaskId}) despacha answerTask e recarrega a lista — mesmo caminho de complete(), mas a partir do checkbox inline.
VisitDetailState holds List<CallTaskEntity> visitCallTasks; VisitDetailNotifier._loadVisitCallTasks calls GetCallTasksForVisitUseCase and sorts (incomplete first, then by originalDate). completeCallTask({callTaskId}) dispatches answerTask and reloads the list — same path as complete(), but from the inline checkbox.
VisitDetailState guarda List<CallTaskEntity> visitCallTasks; VisitDetailNotifier._loadVisitCallTasks llama GetCallTasksForVisitUseCase y ordena (incompletas primero, luego por originalDate). completeCallTask({callTaskId}) despacha answerTask y recarga la lista — mismo camino que complete(), pero desde el checkbox inline.
Page e widgetsPage & widgetsPage y widgets
Módulo no Detalhe da visitaModule in the Visit detailMódulo en el Detalle de la visita
- VisitDetailCallTaskWidget shrink se módulo (visit_detail_call_task) ausente
- Title "Call Tasks"
- CustomEmptyState lista vazia
- _CallTaskRow por tarefa · card xxl30
- category icon + title + CustomTag(status) + _CompleteCheckbox
- onTap → VisitStartGuard.run → goToCallTaskDetail(callTaskId)
- onComplete → VisitStartGuard.run → completeCallTask(callTaskId)
Call Task Detail
A CallTaskDetailPage (ConsumerWidget) recebe só o callTaskId (§17), observa callTaskDetailProvider(callTaskId:) e usa ref.listen para transformar o submissionStatus em ConectaNotice (verde/vermelho) + resetSubmissionStatus. Loading/erro globais. Árvore:CallTaskDetailPage (ConsumerWidget) takes only callTaskId (§17), watches callTaskDetailProvider(callTaskId:) and uses ref.listen to turn submissionStatus into a ConectaNotice (green/red) + resetSubmissionStatus. Global loading/error. Tree:La CallTaskDetailPage (ConsumerWidget) recibe solo el callTaskId (§17), observa callTaskDetailProvider(callTaskId:) y usa ref.listen para transformar el submissionStatus en ConectaNotice (verde/rojo) + resetSubmissionStatus. Loading/error globales. Árbol:
- CallTaskDetailPage
- AppPageShell displayBackButton
- CustomLoadingIndicator loading
- FailureStateView error → ref.invalidate
- SingleChildScrollView → Column data
- DataLoadInfo state.lastSyncAt
- CallTaskDetailHeaderWidget título
- CallTaskDetailCardWidget categoria + status + título + mensagem
- CallTaskDetailOosWidget shrink se !isOos ou sem produtos
- _ProductCard por produto
- _AnswerOption Disponível ("1", verde) / Em falta ("0", vermelho) setProductAnswer · disabled se completed
- history chips dot verde status==1 / vermelho · data
- _ProductCard por produto
- CallTaskDetailActionsWidget
- Concluir (filled) enable: canComplete → complete()
- Reagendar (outlined) enable: !isCompleted && !isSubmitting → _onReschedule
- CallTaskRescheduleModalContent modal (widgets/modals/) · CalendarDatePicker amanhã→+90d · Cancelar / Confirmar → reschedule(newDate)
- AppPageShell displayBackButton
Fluxo de reagendar: tocar em Reagendar → ConectaModal.show<DateTime> (ConectaModalScaffold com CalendarDatePicker, janela amanhã→+90 dias) → backWithResult<DateTime> → notifier.reschedule(newDate:). Modal e navegação ficam no widget por precisarem de BuildContext; a lógica de envio mora no Notifier (§39).Reschedule flow: tapping Reschedule → ConectaModal.show<DateTime> (ConectaModalScaffold with a CalendarDatePicker, window tomorrow→+90 days) → backWithResult<DateTime> → notifier.reschedule(newDate:). Modal and navigation stay in the widget because they need BuildContext; the submit logic lives in the Notifier (§39).Flujo de reprogramar: tocar Reprogramar → ConectaModal.show<DateTime> (ConectaModalScaffold con un CalendarDatePicker, ventana mañana→+90 días) → backWithResult<DateTime> → notifier.reschedule(newDate:). Modal y navegación quedan en el widget porque necesitan BuildContext; la lógica de envío vive en el Notifier (§39).
Notas por mercadoMarket notesNotas por mercado
A Call Task é dirigida pelo End Market Configuration — o módulo visit_detail_call_task no Detalhe da visita. As duas transações (answerTask, reschedulingCallTask) também estão habilitadas em BR/CL/ZA.Call Task is driven by the End Market Configuration — the visit_detail_call_task module in the Visit detail. Both transactions (answerTask, reschedulingCallTask) are also enabled in BR/CL/ZA.La Call Task es dirigida por el End Market Configuration — el módulo visit_detail_call_task en el Detalle de la visita. Las dos transacciones (answerTask, reschedulingCallTask) también están habilitadas en BR/CL/ZA.
HabilitadosEnabledHabilitados
Nos três, visit_detail_call_task = isVisible: true e visit_detail_task_manager = false — ou seja, o slot de tarefas no Detalhe da visita mostra Call Tasks, não Manager Tasks. Os mocks (br/cl/za_call_task.json) trazem tarefas OOS, de criação de pedido, merchandising e pesquisa.
In all three, visit_detail_call_task = isVisible: true and visit_detail_task_manager = false — i.e. the tasks slot in the Visit detail shows Call Tasks, not Manager Tasks. The mocks (br/cl/za_call_task.json) carry OOS, order-creation, merchandising and survey tasks.
En los tres, visit_detail_call_task = isVisible: true y visit_detail_task_manager = false — es decir, el slot de tareas en el Detalle de la visita muestra Call Tasks, no Manager Tasks. Los mocks (br/cl/za_call_task.json) traen tareas OOS, de creación de pedido, merchandising y encuesta.
AR · PY · PE
Não carregam bloco de módulo do Detalhe da visita (nem visit_detail_call_task nem visit_detail_task_manager), então a Call Task não existe nesses mercados. Os mocks ar/py/pe_call_task.json são stubs vazios ({}).
They carry no Visit-detail module block (neither visit_detail_call_task nor visit_detail_task_manager), so Call Task doesn't exist in those markets. The ar/py/pe_call_task.json mocks are empty stubs ({}).
No cargan bloque de módulo del Detalle de la visita (ni visit_detail_call_task ni visit_detail_task_manager), así que la Call Task no existe en esos mercados. Los mocks ar/py/pe_call_task.json son stubs vacíos ({}).
Pendências / roadmapPending / roadmapPendientes / roadmap
Fora do escopo desta versão: (1) criar uma call task pelo app + os lookups CallTaskType/CallTaskSubType/CallTaskDuration; (2) o reagendamento envia uma data futura livre — o contrato final de backend ainda está por confirmar. As mutações de cache (markCallTaskAnswered, rescheduleCallTaskLocally) são o espelho local; a verdade do backend chega pelo Dispatcher.
Out of scope for this version: (1) creating a call task from the app + the CallTaskType/CallTaskSubType/CallTaskDuration lookups; (2) reschedule sends a free future date — the final backend contract is still to be confirmed. The cache mutations (markCallTaskAnswered, rescheduleCallTaskLocally) are the local mirror; the backend truth arrives via the Dispatcher.
Fuera de alcance de esta versión: (1) crear una call task desde la app + los lookups CallTaskType/CallTaskSubType/CallTaskDuration; (2) la reprogramación envía una fecha futura libre — el contrato final de backend está por confirmar. Las mutaciones de caché (markCallTaskAnswered, rescheduleCallTaskLocally) son el espejo local; la verdad del backend llega vía Dispatcher.