Odômetro da jornadaJourney odometerOdómetro de la jornada
A transação de escrita que registra a quilometragem do veículo no início e no fim da jornada de trabalho do representante de vendas, junto com o horário e a posição GPS de cada ponta. É a mesma transação em dois momentos: abertura (com a leitura inicial) e fechamento (com a leitura final). Toda a construção do contrato wire vive no builder. The write transaction that records the vehicle's mileage at the start and end of the sales rep's work journey, together with the time and GPS position at each end. It's the same transaction at two moments: opening (with the initial reading) and closing (with the final reading). All wire-contract construction lives in the builder. La transacción de escritura que registra el kilometraje del vehículo al inicio y al final de la jornada de trabajo del representante de ventas, junto con la hora y la posición GPS de cada punta. Es la misma transacción en dos momentos: apertura (con la lectura inicial) y cierre (con la lectura final). Toda la construcción del contrato wire vive en el builder.
O que é e quando aconteceWhat it is and when it happensQué es y cuándo ocurre
No início do dia de trabalho, o representante de vendas registra a quilometragem do veículo (a leitura do odômetro) antes de sair para as visitas. No fim do dia, ao encerrar a jornada, registra a quilometragem final. Cada um desses dois momentos envia esta transação ao backend — é assim que a empresa acompanha o deslocamento diário de cada representante. At the start of the work day, the sales rep records the vehicle's mileage (the odometer reading) before heading out for visits. At the end of the day, when ending the journey, they record the final mileage. Each of these two moments sends this transaction to the backend — this is how the company tracks each rep's daily travel. Al inicio del día de trabajo, el representante de ventas registra el kilometraje del vehículo (la lectura del odómetro) antes de salir a las visitas. Al final del día, al cerrar la jornada, registra el kilometraje final. Cada uno de esos dos momentos envía esta transacción al backend — así la empresa sigue el desplazamiento diario de cada representante.
São dois envios da mesma jornada, ligados por um identificador comum:There are two sends of the same journey, linked by a common identifier:Son dos envíos de la misma jornada, unidos por un identificador común:
Abertura da jornadaJourney openingApertura de la jornada
O rep informa a leitura inicial do odômetro. O app captura o horário e a posição GPS e envia a jornada como aberta.The rep enters the initial odometer reading. The app captures the time and GPS position and sends the journey as open.El rep informa la lectura inicial del odómetro. La app captura la hora y la posición GPS y envía la jornada como abierta.
Fechamento da jornadaJourney closingCierre de la jornada
Ao encerrar o dia, o rep informa a leitura final. O app acrescenta o horário e a posição de chegada e envia a jornada como fechada.When ending the day, the rep enters the final reading. The app adds the arrival time and position and sends the journey as closed.Al cerrar el día, el rep informa la lectura final. La app agrega la hora y la posición de llegada y envía la jornada como cerrada.
Placa junto na aberturaPlate alongside on openingPlaca junto en la apertura Onde o mercado exige, a mesma tela de abertura também pede a placa do veículo, que segue numa transação própria — ver Placa do veículo. Para o rep, é um único gesto de "começar o dia". Where the market requires it, the same opening screen also asks for the vehicle plate, which goes in its own transaction — see Car license plate. For the rep, it's a single "start the day" gesture. Donde el mercado lo exige, la misma pantalla de apertura también pide la placa del vehículo, que va en su propia transacción — ver Placa del vehículo. Para el rep, es un único gesto de "empezar el día".
Fluxo de telas que disparaScreen flow that fires itFlujo de pantallas que lo dispara
As telas de abertura e encerramento pertencem à feature de Jornada; aqui só situamos onde cada envio acontece:The opening and closing screens belong to the Journey feature; here we only place where each send happens:Las pantallas de apertura y cierre pertenecen a la feature de Jornada; aquí solo situamos dónde ocurre cada envío:
- Login / abertura do appLogin / app openingLogin / apertura de la appApós entrar, se não há uma jornada aberta o app leva o rep direto à tela de Jornada.After signing in, if there's no open journey the app takes the rep straight to the Journey screen.Tras ingresar, si no hay una jornada abierta la app lleva al rep directo a la pantalla de Jornada.
- Tela de Jornada → aberturaJourney screen → openingPantalla de Jornada → aperturaO rep digita a leitura do odômetro (e a placa, se pedida) e toca em iniciar. Esse toque dispara o envio de abertura desta transação; só depois o rep chega às telas do dia.The rep types the odometer reading (and the plate, if asked) and taps start. That tap fires this transaction's opening send; only then does the rep reach the day's screens.El rep escribe la lectura del odómetro (y la placa, si se pide) y toca iniciar. Ese toque dispara el envío de apertura de esta transacción; solo después el rep llega a las pantallas del día.
- Trabalho do diaDay's workTrabajo del díaVisitas, pedidos e demais tarefas seguem normalmente, com a jornada aberta em segundo plano.Visits, orders and other tasks proceed as usual, with the journey open in the background.Visitas, pedidos y demás tareas siguen normalmente, con la jornada abierta en segundo plano.
- Encerrar jornada → fechamentoEnd journey → closingCerrar jornada → cierrePelo menu, o rep abre "Encerrar jornada", digita a leitura final e confirma. Esse toque dispara o envio de fechamento — a mesma transação, agora com os dados de chegada.From the menu, the rep opens "End journey", types the final reading and confirms. That tap fires the closing send — the same transaction, now with the arrival data.Desde el menú, el rep abre "Cerrar jornada", escribe la lectura final y confirma. Ese toque dispara el envío de cierre — la misma transacción, ahora con los datos de llegada.
Depois do envioAfter sendingDespués del envío
- Confirmação ao repConfirmation to the repConfirmación al rep
- Quando o backend aceita a abertura, o rep segue para o dia de trabalho; quando aceita o fechamento, a jornada é encerrada. Se o envio falha, a ação não avança e o rep pode tentar de novo.When the backend accepts the opening, the rep moves on to the work day; when it accepts the closing, the journey ends. If the send fails, the action doesn't proceed and the rep can try again.Cuando el backend acepta la apertura, el rep sigue al día de trabajo; cuando acepta el cierre, la jornada se cierra. Si el envío falla, la acción no avanza y el rep puede intentar de nuevo.
- Sem internetOfflineSin internet
- Como todas as transações do Dispatcher, o envio pode entrar em fila e ser reenviado quando a conexão volta. Um identificador de transação evita registros duplicados no reenvio.Like all Dispatcher transactions, the send may be queued and retried when the connection returns. A transaction id avoids duplicate records on retry.Como todas las transacciones del Dispatcher, el envío puede quedar en cola y reintentarse cuando vuelve la conexión. Un identificador de transacción evita registros duplicados en el reenvío.
- Acompanhar o envioTracking the sendSeguir el envío
- O status técnico do despacho (enviado, em fila, com erro) pode ser acompanhado na central de dados / tracking de despachos do app — útil para o suporte investigar uma jornada.The dispatch's technical status (sent, queued, errored) can be followed in the app's data center / dispatch tracking — useful for support to investigate a journey.El estado técnico del despacho (enviado, en cola, con error) puede seguirse en el centro de datos / tracking de despachos de la app — útil para que soporte investigue una jornada.
Visão técnicaTechnical overviewVisión técnica
Odômetro da jornada é a transação de saída que persiste a quilometragem de uma jornada de trabalho. Ela é disparada duas vezes por jornada: na abertura (StartOdometerJourneyUseCase) e no fechamento (FinishOdometerJourneyUseCase). Os dois envios usam o mesmo builder, o mesmo serviceName e o mesmo uid/ReconId; divergem apenas nos campos de fim e no Status/isNew.
Journey odometer is the outbound transaction that persists a work journey's mileage. It's fired twice per journey: on opening (StartOdometerJourneyUseCase) and on closing (FinishOdometerJourneyUseCase). Both sends use the same builder, the same serviceName and the same uid/ReconId; they diverge only in the end fields and in Status/isNew.
Odómetro de la jornada es la transacción de salida que persiste el kilometraje de una jornada de trabajo. Se dispara dos veces por jornada: en la apertura (StartOdometerJourneyUseCase) y en el cierre (FinishOdometerJourneyUseCase). Ambos envíos usan el mismo builder, el mismo serviceName y el mismo uid/ReconId; divergen solo en los campos de fin y en Status/isNew.
Payload enxutoLean payloadPayload conciso
Duas listas de um objeto — Odometer (leituras, tempo, GPS) e ReconStatus (id de reconciliação, status, van). 14 chaves no total.Two single-object arrays — Odometer (readings, time, GPS) and ReconStatus (recon id, status, van). 14 keys in total.Dos listas de un objeto — Odometer (lecturas, tiempo, GPS) y ReconStatus (id de reconciliación, estado, van). 14 claves en total.
Abertura ⇄ fechamentoOpen ⇄ closeApertura ⇄ cierre
Não há campo de "modo" no input: a variação vem do próprio estado da OdometerJourneyEntity (status, campos de fim nulos ou não).There's no "mode" field on the input: the variation comes from the OdometerJourneyEntity's own state (status, end fields null or not).No hay campo de "modo" en el input: la variación viene del propio estado de la OdometerJourneyEntity (status, campos de fin nulos o no).
RPC genéricoGeneric RPCRPC genérico
Não há RPC por jornada: tudo passa pelo mesmo sendTransaction, com o JSON no campo message e o serviceName como discriminador.There's no per-journey RPC: everything goes through the same sendTransaction, with the JSON in the message field and serviceName as the discriminator.No hay RPC por jornada: todo pasa por el mismo sendTransaction, con el JSON en el campo message y el serviceName como discriminador.
FontesSourcesFuentes
BuildOdometerJourneyDispatcherPayloadUseCase + OdometerJourneyDispatcherPayloadInput + DispatcherType.odometerJourney + DispatcherConectaRep.proto. O input carrega entities cruas (OdometerJourneyEntity + ResourceEntity, CLAUDE.md §36); o build() constrói todo o wire.
BuildOdometerJourneyDispatcherPayloadUseCase + OdometerJourneyDispatcherPayloadInput + DispatcherType.odometerJourney + DispatcherConectaRep.proto. The input carries raw entities (OdometerJourneyEntity + ResourceEntity, CLAUDE.md §36); build() constructs the entire wire.
BuildOdometerJourneyDispatcherPayloadUseCase + OdometerJourneyDispatcherPayloadInput + DispatcherType.odometerJourney + DispatcherConectaRep.proto. El input lleva entities crudas (OdometerJourneyEntity + ResourceEntity, CLAUDE.md §36); build() construye todo el wire.
Transporte gRPCgRPC transportTransporte gRPC
DispatcherConectaRep.proto · proto3 · package mn.bat.conectarep.dispatcher. O serviço expõe um único RPC genérico — não existe mensagem por transação. TODA transação de escrita do app usa este mesmo sendTransaction; o que muda é o serviceName (discriminador) e o JSON dentro de message.The service exposes a single generic RPC — there's no per-transaction message. EVERY write transaction in the app uses this same sendTransaction; what changes is the serviceName (discriminator) and the JSON inside message.El servicio expone un único RPC genérico — no existe mensaje por transacción. TODA transacción de escritura de la app usa este mismo sendTransaction; lo que cambia es el serviceName (discriminador) y el JSON dentro de message.
sendTransactionunaryrpc sendTransaction(InboxTransactionRequest) returns (InboxTransactionReply)
path /mn.bat.conectarep.dispatcher.DispatcherConectaRepService/sendTransaction
InboxTransactionRequestendpointstring· #1 · endpoint alvo (config de ambiente)target endpoint (environment config)endpoint destino (config de ambiente)serviceNamestring· #2 · discriminador — sempreOdometerAPI(sem prefixoPromo_:hasPromotion: false)discriminator — alwaysOdometerAPI(noPromo_prefix:hasPromotion: false)discriminador — siempreOdometerAPI(sin prefijoPromo_:hasPromotion: false)dateReferencestring· #3 ·AAAA-MM-DD—formatDate(journey.startedAt)(formatoisoDate)YYYY-MM-DD—formatDate(journey.startedAt)(isoDateformat)AAAA-MM-DD—formatDate(journey.startedAt)(formatoisoDate)transactionReferencestring· #4 ·journey.uuid(correlação abertura ⇄ fechamento)journey.uuid(opening ⇄ closing correlation)journey.uuid(correlación apertura ⇄ cierre)usernamestring· #5messagestring· #6 · o payload JSON serializado (a tabela da seção 08)the JSON payload serialized (the table in section 08)el payload JSON serializado (la tabla de la sección 08)manufacturerstring· #7 · dado do dispositivodevice datadato del dispositivomodelstring· #8 · dado do dispositivodevice datadato del dispositivodeviceUuidstring· #9 · literal provisório hoje (ver Pendências)provisional literal today (see Pending)literal provisional hoy (ver Pendientes)deviceVersionstring· #10tidint64· #11 · id de transação para idempotência/replaytransaction id for idempotency/replayid de transacción para idempotencia/replay
InboxTransactionReplystatusint32· #1 · status do ack (0 = sucesso)ack status (0 = success)status del ack (0 = éxito)messagestring· #2 · mensagem do backendbackend messagemensaje del backendtransactionIdint32· #3 · id atribuído pelo backend (correlação)backend-assigned id (correlation)id asignado por el backend (correlación)
Envelope → Request
O builder devolve um DispatcherEnvelope (type, serviceName, payload, transactionReference, dateReference — sem account). O DispatcherGateway serializa payload em JSON para message, copia serviceName/dateReference/transactionReference, preenche os campos de dispositivo e o bearer token de auth, e chama o RPC. O destino da transação é DispatcherDestination.salesforce.
The builder returns a DispatcherEnvelope (type, serviceName, payload, transactionReference, dateReference — no account). The DispatcherGateway serializes payload to JSON into message, copies serviceName/dateReference/transactionReference, fills in the device fields and the auth bearer token, and calls the RPC. The transaction destination is DispatcherDestination.salesforce.
El builder devuelve un DispatcherEnvelope (type, serviceName, payload, transactionReference, dateReference — sin account). El DispatcherGateway serializa payload a JSON en message, copia serviceName/dateReference/transactionReference, completa los campos del dispositivo y el bearer token de auth, y llama al RPC. El destino de la transacción es DispatcherDestination.salesforce.
Os dois envios e serviceNameThe two sends & serviceNameLos dos envíos y serviceName
Um único DispatcherType (odometerJourney) e um único serviceName (OdometerAPI) cobrem os dois envios. Não há campo de tipo no input — o envio de fechamento se distingue pelo próprio estado da OdometerJourneyEntity: no StartOdometerJourneyUseCase a entity nasce status: open com os campos de fim nulos; no FinishOdometerJourneyUseCase ela é copiada para status: closed com odometerEnd/endedAt/endLatitude/endLongitude preenchidos.A single DispatcherType (odometerJourney) and a single serviceName (OdometerAPI) cover both sends. There's no type field on the input — the closing send is distinguished by the OdometerJourneyEntity's own state: in StartOdometerJourneyUseCase the entity is born status: open with null end fields; in FinishOdometerJourneyUseCase it's copied to status: closed with odometerEnd/endedAt/endLatitude/endLongitude filled.Un único DispatcherType (odometerJourney) y un único serviceName (OdometerAPI) cubren ambos envíos. No hay campo de tipo en el input — el envío de cierre se distingue por el propio estado de la OdometerJourneyEntity: en StartOdometerJourneyUseCase la entity nace status: open con los campos de fin nulos; en FinishOdometerJourneyUseCase se copia a status: closed con odometerEnd/endedAt/endLatitude/endLongitude completados.
| EnvioSendEnvío | serviceName |
Status · isNew |
Campos de fimEnd fieldsCampos de fin |
|---|---|---|---|
AberturaOpeningApertura (start) | OdometerAPI | Open · "1" | OdameterEnd, endtime, endlatitude, endlongitude = "" (entity com fins nulos)OdameterEnd, endtime, endlatitude, endlongitude = "" (entity with null ends)OdameterEnd, endtime, endlatitude, endlongitude = "" (entity con fines nulos) |
FechamentoClosingCierre (finish) | OdometerAPI | Closed · "0" | todos preenchidos a partir da leitura/posição de chegadaall filled from the arrival reading/positiontodos completados desde la lectura/posición de llegada |
isNew · Status
isNew é derivado de journey.isClosed ? "0" : "1" e Status de journey.status.wireValue (Open/Closed). Como uid, ReconId e SubmissionDate são os mesmos nos dois envios, o backend reconcilia a jornada por esse par.
isNew is derived from journey.isClosed ? "0" : "1" and Status from journey.status.wireValue (Open/Closed). Since uid, ReconId and SubmissionDate are the same across both sends, the backend reconciles the journey by that pair.
isNew se deriva de journey.isClosed ? "0" : "1" y Status de journey.status.wireValue (Open/Closed). Como uid, ReconId y SubmissionDate son los mismos en ambos envíos, el backend reconcilia la jornada por ese par.
Como é disparadoHow it's firedCómo se dispara
A transação é orquestrada pelo fluxo de jornada (remote-first, §36). O JourneyNotifier (abertura) e o fluxo de encerramento chamam os use cases, que montam a entity crua e a passam ao builder. O builder é o dono único da formatação de data e da derivação wire. A cascata:The transaction is orchestrated by the journey flow (remote-first, §36). The JourneyNotifier (opening) and the end-journey flow call the use cases, which assemble the raw entity and pass it to the builder. The builder is the sole owner of date formatting and wire derivation. The cascade:La transacción se orquesta desde el flujo de jornada (remote-first, §36). El JourneyNotifier (apertura) y el flujo de cierre llaman a los use cases, que arman la entity cruda y la pasan al builder. El builder es el dueño único del formateo de fecha y la derivación wire. La cascada:
- Journey / End journeynotifier
- gera uuid/reconId, define statusgenerates uuid/reconId, sets statusgenera uuid/reconId, define statusStart / Finish OdometerJourneyUseCase
- entity cruaraw entityentity crudaOdometerJourneyDispatcherPayloadInput
- build()BuildOdometerJourneyDispatcherPayloadUseCasemonta o wireassembles the wirearma el wire
- devolvereturnsdevuelveDispatcherEnvelope
- SubmitOdometerJourneyUseCaseDispatcherOrchestrator
- serializa + authserialize + authserializa + authDispatcherGateway
- sendTransactionBackendgRPC
- serializa + authserialize + authserializa + authDispatcherGateway
- SubmitOdometerJourneyUseCaseDispatcherOrchestrator
- devolvereturnsdevuelveDispatcherEnvelope
- build()BuildOdometerJourneyDispatcherPayloadUseCasemonta o wireassembles the wirearma el wire
- entity cruaraw entityentity crudaOdometerJourneyDispatcherPayloadInput
- gera uuid/reconId, define statusgenerates uuid/reconId, sets statusgenera uuid/reconId, define statusStart / Finish OdometerJourneyUseCase
O input (entities cruas)The input (raw entities)El input (entities crudas)
OdometerJourneyDispatcherPayloadInput (Freezed). Carrega o dado como existe no domínio; nada de formato wire. O relógio, o GPS, o uuid e o reconId já chegam resolvidos dentro da OdometerJourneyEntity (montada nos use cases de start/finish).OdometerJourneyDispatcherPayloadInput (Freezed). Carries data as it exists in the domain; no wire shaping. The clock, GPS, uuid and reconId arrive already resolved inside the OdometerJourneyEntity (assembled in the start/finish use cases).OdometerJourneyDispatcherPayloadInput (Freezed). Lleva el dato como existe en el dominio; nada de formato wire. El reloj, el GPS, el uuid y el reconId ya llegan resueltos dentro de la OdometerJourneyEntity (armada en los use cases de start/finish).
| CampoFieldCampo | TipoTypeTipo | PapelRoleRol |
|---|---|---|
journey | OdometerJourneyEntity | a jornada crua: uuid, reconId, status, odometerStart/End, startedAt/endedAt, GPS de início/fimthe raw journey: uuid, reconId, status, odometerStart/End, startedAt/endedAt, start/end GPSla jornada cruda: uuid, reconId, status, odometerStart/End, startedAt/endedAt, GPS de inicio/fin |
resource | ResourceEntity | representante de vendas (cru — o builder deriva vanId = resource.locationSfid)sales rep (raw — the builder derives vanId = resource.locationSfid)representante de ventas (crudo — el builder deriva vanId = resource.locationSfid) |
Payload (message)
O JSON serializado no campo message do request. Cada tabela abaixo tem 4 colunas — Campo JSON · Tipo · Origem do Dado · Regra — e lista toda chave que o build() emite (14 no total). Campo, Tipo e Origem são código cru; só a Regra é prosa. Um exemplo completo (envio de fechamento) está em transaction_example.json, ao lado deste doc.The JSON serialized into the request's message field. Each table below has 4 columns — JSON field · Type · Data source · Rule — and lists every key that build() emits (14 in total). Field, Type and Source are raw code; only Rule is prose. A full example (closing send) sits in transaction_example.json, next to this doc.El JSON serializado en el campo message del request. Cada tabla abajo tiene 4 columnas — Campo JSON · Tipo · Origen del Dato · Regla — y lista toda clave que build() emite (14 en total). Campo, Tipo y Origen son código crudo; solo la Regla es prosa. Un ejemplo completo (envío de cierre) está en transaction_example.json, junto a este doc.
Raiz do payloadPayload rootRaíz del payload
| Campo JSON | TipoTypeTipo | Origem do DadoData sourceOrigen del Dato | RegraRuleRegla |
|---|---|---|---|
Odometer | array | journey | array de um único objeto (leituras + tempo + GPS)single-element array (readings + time + GPS)array de un solo objeto (lecturas + tiempo + GPS) |
ReconStatus | array | journey + resource | array de um único objeto (reconciliação + status + van)single-element array (reconciliation + status + van)array de un solo objeto (reconciliación + estado + van) |
Odometer objeto únicosingle objectobjeto único 10 camposfieldscampos
Campo JSON TipoTypeTipo Origem do DadoData sourceOrigen del Dato RegraRuleRegla uidstring journey.uuidid da jornada ( SLREP-<uuid v4>); igual nos dois enviosjourney id (SLREP-<uuid v4>); same across both sendsid de la jornada (SLREP-<uuid v4>); igual en ambos envíosOdameterStartstring journey.odometerStart.toString()leitura inicial; chave com typo mantida verbatim (contrato)initial reading; misspelled key kept verbatim (contract)lectura inicial; clave con typo mantenida verbatim (contrato) OdameterEndstring journey.odometerEnd?.toString() ?? ""leitura final; ""na abertura; typo mantido verbatimfinal reading;""on opening; typo kept verbatimlectura final;""en la apertura; typo mantenido verbatimstarttimestring journey.startedAtformato yyyy/MM/dd HH:mm:ssyyyy/MM/dd HH:mm:ssformatformatoyyyy/MM/dd HH:mm:ssendtimestring journey.endedAt""na abertura; senãoyyyy/MM/dd HH:mm:ss""on opening; elseyyyy/MM/dd HH:mm:ss""en la apertura; si noyyyy/MM/dd HH:mm:ssstartlatitudestring journey.startLatitude.toString()GPS na abertura ( 0.0quando indisponível)GPS on opening (0.0when unavailable)GPS en la apertura (0.0cuando no disponible)startlongitudestring journey.startLongitude.toString()GPS na abertura ( 0.0quando indisponível)GPS on opening (0.0when unavailable)GPS en la apertura (0.0cuando no disponible)endlatitudestring journey.endLatitude?.toString() ?? """"na abertura; GPS de chegada no fechamento""on opening; arrival GPS on closing""en la apertura; GPS de llegada en el cierreendlongitudestring journey.endLongitude?.toString() ?? """"na abertura; GPS de chegada no fechamento""on opening; arrival GPS on closing""en la apertura; GPS de llegada en el cierreSubmissionDatestring journey.startedAtformato yyyy/MM/dd; sempre a data de abertura (igual nos dois envios)yyyy/MM/ddformat; always the opening date (same across both sends)formatoyyyy/MM/dd; siempre la fecha de apertura (igual en ambos envíos)ReconStatus objeto únicosingle objectobjeto único 4 camposfieldscampos
Campo JSON TipoTypeTipo Origem do DadoData sourceOrigen del Dato RegraRuleRegla ReconIdstring journey.reconId{uuid}_{yyyy.MM.dd da abertura}— composto no use case de start; igual nos dois envios{uuid}_{opening yyyy.MM.dd}— composed in the start use case; same across both sends{uuid}_{yyyy.MM.dd de apertura}— compuesto en el use case de start; igual en ambos envíosStatusstring journey.status.wireValue"Open"na abertura,"Closed"no fechamento"Open"on opening,"Closed"on closing"Open"en la apertura,"Closed"en el cierreisNewstring journey.isClosed ? "0" : "1""1"na abertura,"0"no fechamento"1"on opening,"0"on closing"1"en la apertura,"0"en el cierrevanIdstring resource.locationSfidSFID da localização/van do rep (derivado da ResourceEntitycrua)rep's location/van SFID (derived from the rawResourceEntity)SFID de la ubicación/van del rep (derivado de laResourceEntitycruda)
Formatação de dataDate formattingFormateo de fecha DateTimeUtils · DateFormatType
starttime/endtime:DateFormatType.slashYearMonthDayWithTime→yyyy/MM/dd HH:mm:ss.starttime/endtime:DateFormatType.slashYearMonthDayWithTime→yyyy/MM/dd HH:mm:ss.starttime/endtime:DateFormatType.slashYearMonthDayWithTime→yyyy/MM/dd HH:mm:ss.SubmissionDate:DateFormatType.slashYearMonthDay→yyyy/MM/dd(sempre sobrestartedAt).SubmissionDate:DateFormatType.slashYearMonthDay→yyyy/MM/dd(always overstartedAt).SubmissionDate:DateFormatType.slashYearMonthDay→yyyy/MM/dd(siempre sobrestartedAt).dateReferencedo envelope:DateTimeUtils.formatDate(dateTime: journey.startedAt)com o defaultisoDate→yyyy-MM-dd(traço, não barra).envelopedateReference:DateTimeUtils.formatDate(dateTime: journey.startedAt)with theisoDatedefault →yyyy-MM-dd(dash, not slash).dateReferencedel envelope:DateTimeUtils.formatDate(dateTime: journey.startedAt)con el defaultisoDate→yyyy-MM-dd(guion, no barra).ReconIdusaDateFormatType.dotYearMonthDay→yyyy.MM.dd(composto noStartOdometerJourneyUseCase, não no builder).ReconIdusesDateFormatType.dotYearMonthDay→yyyy.MM.dd(composed inStartOdometerJourneyUseCase, not in the builder).ReconIdusaDateFormatType.dotYearMonthDay→yyyy.MM.dd(compuesto enStartOdometerJourneyUseCase, no en el builder).
Regras de negócioBusiness rulesReglas de negocio
Só os deltasOnly the deltasSolo los deltas
- Um único par abertura/fechamento por jornada. O
uid, oReconIde oSubmissionDatesão idênticos nos dois envios — é o par de correlação que o backend usa para casar a leitura inicial com a final.One opening/closing pair per journey. Theuid,ReconIdandSubmissionDateare identical across both sends — the correlation pair the backend uses to match the initial reading with the final one.Un único par apertura/cierre por jornada. Eluid, elReconIdy elSubmissionDateson idénticos en ambos envíos — es el par de correlación que el backend usa para casar la lectura inicial con la final. - Remote-first. Tanto
StartOdometerJourneyUseCasequantoFinishOdometerJourneyUseCasesó gravam a jornada no local (repository.saveJourney) após o envio remoto ser aceito; se o envio falha, a entity não é persistida e a ação não avança.Remote-first. BothStartOdometerJourneyUseCaseandFinishOdometerJourneyUseCaseonly persist the journey locally (repository.saveJourney) after the remote send is accepted; if the send fails, the entity isn't persisted and the action doesn't proceed.Remote-first. TantoStartOdometerJourneyUseCasecomoFinishOdometerJourneyUseCasesolo persisten la jornada localmente (repository.saveJourney) tras aceptarse el envío remoto; si el envío falla, la entity no se persiste y la acción no avanza. - Odômetro > 0. O
JourneyNotifierrejeita leitura<= 0(JourneyActionResult.invalidOdometer) antes de disparar o envio — o builder recebe sempre um valor válido.Odometer > 0. TheJourneyNotifierrejects a<= 0reading (JourneyActionResult.invalidOdometer) before firing the send — the builder always receives a valid value.Odómetro > 0. ElJourneyNotifierrechaza lectura<= 0(JourneyActionResult.invalidOdometer) antes de disparar el envío — el builder siempre recibe un valor válido. - Sem prefixo
Promo_.type.resolveServiceName(hasPromotion: false)— a jornada nunca carrega promoção, então oserviceNameé sempreOdometerAPI.NoPromo_prefix.type.resolveServiceName(hasPromotion: false)— a journey never carries a promotion, so theserviceNameis alwaysOdometerAPI.Sin prefijoPromo_.type.resolveServiceName(hasPromotion: false)— la jornada nunca lleva promoción, así que elserviceNamees siempreOdometerAPI. - Números como string. Odômetro, latitude e longitude vão como
String(.toString()) — o contrato wire trata tudo como texto. GPS indisponível vira"0.0".Numbers as strings. Odometer, latitude and longitude go asString(.toString()) — the wire contract treats everything as text. Unavailable GPS becomes"0.0".Números como string. Odómetro, latitud y longitud van comoString(.toString()) — el contrato wire trata todo como texto. GPS no disponible se vuelve"0.0".
Pendências / roadmapPending / roadmapPendientes / roadmap
O que o builder envia com particularidade ou fora do ideal, documentado fiel ao estado atual do código (nunca descrito como se já estivesse corrigido):What the builder ships with a quirk or below the ideal, documented faithfully to the current code state (never described as already fixed):Lo que el builder envía con particularidad o fuera del ideal, documentado fiel al estado actual del código (nunca descrito como si ya estuviera corregido):
Particularidades / pendenteQuirks / pendingParticularidades / pendiente
OdameterStart/OdameterEnd: chaves com typo ("Odameter" em vez de "Odometer") mantidas verbatim — é o contrato do backend, não um bug a corrigir no app.OdameterStart/OdameterEnd: misspelled keys ("Odameter" instead of "Odometer") kept verbatim — it's the backend contract, not an app bug to fix.OdameterStart/OdameterEnd: claves con typo ("Odameter" en vez de "Odometer") mantenidas verbatim — es el contrato del backend, no un bug a corregir en la app.- GPS indisponível:
latitude/longitudecaem para0.0noJourneyNotifier(position?.latitude ?? 0.0) e chegam ao payload como"0.0"— não há re-tentativa de captura de posição.Unavailable GPS:latitude/longitudefall back to0.0in theJourneyNotifier(position?.latitude ?? 0.0) and reach the payload as"0.0"— there's no position-capture retry.GPS no disponible:latitude/longitudecaen a0.0en elJourneyNotifier(position?.latitude ?? 0.0) y llegan al payload como"0.0"— no hay reintento de captura de posición. - Transporte:
deviceUuidvai como literal provisório no gateway (pendência conhecida do Dispatcher, comum a todas as transações).Transport:deviceUuidships as a provisional literal in the gateway (known Dispatcher pending item, common to all transactions).Transporte:deviceUuidva como literal provisional en el gateway (pendiente conocido del Dispatcher, común a todas las transacciones).
Transação irmãSister transactionTransacción hermana
A abertura da jornada, onde o mercado exige, dispara junto a transação de placa do veículo (CarLicensePlate, só BR) — documentada em 30 · Placa do veículo (doc separado). São transações distintas, com serviceName e mercados diferentes.
Journey opening, where the market requires it, fires the vehicle-plate transaction alongside (CarLicensePlate, BR only) — documented in 30 · Car license plate (separate doc). They're distinct transactions, with different serviceName and markets.
La apertura de la jornada, donde el mercado lo exige, dispara junto la transacción de placa del vehículo (CarLicensePlate, solo BR) — documentada en 30 · Placa del vehículo (doc separado). Son transacciones distintas, con serviceName y mercados diferentes.
MercadosMarketsMercados
A disponibilidade da transação vem de DispatcherType.odometerJourney.enabledMarkets = [BR, CL, ZA]. AR/PY/PE existem como mercados do app (config PANGEA mínima), mas não têm dispatcher de odômetro.Transaction availability comes from DispatcherType.odometerJourney.enabledMarkets = [BR, CL, ZA]. AR/PY/PE exist as app markets (minimal PANGEA config), but have no odometer dispatcher.La disponibilidad de la transacción viene de DispatcherType.odometerJourney.enabledMarkets = [BR, CL, ZA]. AR/PY/PE existen como mercados de la app (config PANGEA mínima), pero no tienen dispatcher de odómetro.
Mesmo payloadSame payloadMismo payload
Nos três mercados a transação é idêntica: mesmo serviceName, mesmas 14 chaves e o mesmo par abertura/fechamento. O builder não ramifica por mercado.
In all three markets the transaction is identical: same serviceName, same 14 keys and the same opening/closing pair. The builder doesn't branch by market.
En los tres mercados la transacción es idéntica: mismo serviceName, mismas 14 claves y el mismo par apertura/cierre. El builder no ramifica por mercado.
Placa juntoPlate alongsidePlaca junto
A transação de placa do veículo (CarLicensePlate), disparada na mesma abertura, é só BR — o odômetro vai além (BR/CL/ZA). A visibilidade do campo de placa na tela vem do EMC (journeyConfig).
The vehicle-plate transaction (CarLicensePlate), fired on the same opening, is BR only — the odometer goes further (BR/CL/ZA). The plate field's visibility on screen comes from the EMC (journeyConfig).
La transacción de placa del vehículo (CarLicensePlate), disparada en la misma apertura, es solo BR — el odómetro va más allá (BR/CL/ZA). La visibilidad del campo de placa en pantalla viene del EMC (journeyConfig).
AR · PY · PE
Existem como mercados do app (config PANGEA mínima), mas não têm dispatcher de odômetro — não estão em enabledMarkets. A jornada com odômetro não é disparada nesses mercados.
They exist as app markets (minimal PANGEA config), but have no odometer dispatcher — they're not in enabledMarkets. The odometer journey isn't fired in these markets.
Existen como mercados de la app (config PANGEA mínima), pero no tienen dispatcher de odómetro — no están en enabledMarkets. La jornada con odómetro no se dispara en estos mercados.