Envio de contato da equipeStaff contact uploadEnvío de contacto del equipo
A transação de escrita que envia ao backend o cadastro, a edição ou a exclusão de um contato (membro da equipe) de um varejo. Um único builder monta o payload ContactDetails para as três ações — criar, atualizar e excluir — que compartilham a mesma estrutura e divergem só em poucos campos. Toda a construção do contrato wire vive no builder.
The write transaction that sends the backend the creation, edit or deletion of a contact (staff member) of a retail. A single builder assembles the ContactDetails payload for the three actions — create, update and delete — which share the same structure and differ only in a handful of fields. All wire-contract construction lives in the builder.
La transacción de escritura que envía al backend el alta, la edición o la eliminación de un contacto (miembro del equipo) de un punto de venta. Un único builder arma el payload ContactDetails para las tres acciones — crear, actualizar y eliminar — que comparten la misma estructura y difieren solo en unos pocos campos. 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
Quando o representante de vendas cadastra, edita ou exclui um contato de um varejo — o dono, um gerente, um balconista, o contato principal —, o app envia essa mudança ao backend por esta transação. É o momento em que a alteração feita na tela de equipe sai do dispositivo e passa a valer no sistema (Salesforce). O contato é uma pessoa ligada ao varejo: nome, telefone, e-mail, função, idioma preferido e se é o contato principal. When the sales rep registers, edits or deletes a contact of a retail — the owner, a manager, a clerk, the main contact —, the app sends that change to the backend through this transaction. It's the moment the change made on the staff screen leaves the device and takes effect in the system (Salesforce). A contact is a person tied to the retail: name, phone, email, role, preferred language and whether they are the main contact. Cuando el representante de ventas da de alta, edita o elimina un contacto de un punto de venta — el dueño, un gerente, un dependiente, el contacto principal —, la app envía ese cambio al backend por esta transacción. Es el momento en que el cambio hecho en la pantalla de equipo sale del dispositivo y pasa a valer en el sistema (Salesforce). Un contacto es una persona ligada al punto de venta: nombre, teléfono, correo, función, idioma preferido y si es el contacto principal.
Existem três ações, escolhidas automaticamente pelo que o rep faz na tela:There are three actions, chosen automatically by what the rep does on screen:Existen tres acciones, elegidas automáticamente por lo que el rep hace en pantalla:
CriarCreateCrear
Um contato novo é adicionado ao varejo. O envio vai sem identificador do contato — o backend cria o registro.A new contact is added to the retail. The send goes without a contact id — the backend creates the record.Se agrega un contacto nuevo al punto de venta. El envío va sin identificador del contacto — el backend crea el registro.
AtualizarUpdateActualizar
Um contato existente é editado. O envio identifica o contato e atualiza seus dados.An existing contact is edited. The send identifies the contact and updates its data.Se edita un contacto existente. El envío identifica el contacto y actualiza sus datos.
ExcluirDeleteEliminar
Um contato é removido/inativado. O envio marca o registro como inativo.A contact is removed/deactivated. The send marks the record as inactive.Se elimina/inactiva un contacto. El envío marca el registro como inactivo.
Escolha automáticaAutomatic choiceElección automática O rep não escolhe a ação por nome: ela é decidida pelo app — tocar em salvar num contato novo cria; salvar num contato existente atualiza; o botão de excluir exclui. O gesto é sempre o mesmo — preencher e salvar. The rep doesn't pick the action by name: the app decides it — tapping save on a new contact creates; saving an existing contact updates; the delete button deletes. The gesture is always the same — fill in and save. El rep no elige la acción por nombre: la decide la app — tocar guardar en un contacto nuevo crea; guardar un contacto existente actualiza; el botón de eliminar elimina. El gesto es siempre el mismo — completar y guardar.
Fluxo de telas que disparaScreen flow that fires itFlujo de pantallas que lo dispara
A transação é o último passo da edição de um contato. As telas pertencem à feature de Gerenciar equipe; aqui só situamos onde o envio acontece:The transaction is the last step of editing a contact. The screens belong to the Manage staff feature; here we only place where the send happens:La transacción es el último paso de la edición de un contacto. Las pantallas pertenecen a la feature de Gestionar equipo; aquí solo situamos dónde ocurre el envío:
- Detalhe da visita → Gerenciar equipeVisit detail → Manage staffDetalle de la visita → Gestionar equipoA partir de uma visita ao varejo, o rep abre a tela de equipe, que lista os contatos e os balconistas.From a retail visit, the rep opens the staff screen, which lists contacts and clerks.Desde una visita al punto de venta, el rep abre la pantalla de equipo, que lista los contactos y los dependientes.
- Adicionar ou tocar num contatoAdd or tap a contactAgregar o tocar un contactoTocar em adicionar abre o formulário em modo criação; tocar num contato da lista abre em modo edição.Tapping add opens the form in create mode; tapping a contact in the list opens it in edit mode.Tocar agregar abre el formulario en modo creación; tocar un contacto de la lista lo abre en modo edición.
- Preencher o formulário do contatoFill the contact formCompletar el formulario del contactoNome, telefone, e-mail, função, idioma, se é o contato principal — os campos visíveis variam por mercado.Name, phone, email, role, language, whether it's the main contact — the visible fields vary by market.Nombre, teléfono, correo, función, idioma, si es el contacto principal — los campos visibles varían por mercado.
- Salvar → confirmar → esta transaçãoSave → confirm → this transactionGuardar → confirmar → esta transacciónAo salvar, um modal pede confirmação; ao confirmar, o app dispara o Envio de contato. É este toque que aciona a transação (criação ou atualização).On save, a modal asks for confirmation; on confirm, the app fires the Staff contact upload. This tap is what triggers the transaction (create or update).Al guardar, un modal pide confirmación; al confirmar, la app dispara el Envío de contacto. Este toque es lo que activa la transacción (creación o actualización).
- Excluir → confirmar → esta transaçãoDelete → confirm → this transactionEliminar → confirmar → esta transacciónAo editar um contato existente, o botão de excluir (com confirmação) dispara a mesma transação, na ação de exclusão.When editing an existing contact, the delete button (with confirmation) fires the same transaction, in the delete action.Al editar un contacto existente, el botón de eliminar (con confirmación) dispara la misma transacción, en la acción de eliminación.
Visita precisa estar iniciadaVisit must be startedLa visita debe estar iniciada Criar, editar ou excluir um contato só é permitido com a visita iniciada — há uma checagem antes de abrir o formulário. Sem visita iniciada, o rep é avisado e a ação não segue. Creating, editing or deleting a contact is only allowed with the visit started — there's a check before opening the form. Without a started visit, the rep is warned and the action doesn't proceed. Crear, editar o eliminar un contacto solo se permite con la visita iniciada — hay una verificación antes de abrir el formulario. Sin visita iniciada, se avisa al rep y la acción no sigue.
Depois do envioAfter sendingDespués del envío
- Confirmação ao repConfirmation to the repConfirmación al rep
- Quando o backend aceita, o rep vê um aviso de sucesso e volta para a tela de equipe; o contato criado/editado/excluído reflete a mudança.When the backend accepts it, the rep sees a success notice and returns to the staff screen; the created/edited/deleted contact reflects the change.Cuando el backend lo acepta, el rep ve un aviso de éxito y vuelve a la pantalla de equipo; el contacto creado/editado/eliminado refleja el cambio.
- Sem internetOfflineSin internet
- Diferente do pedido, o envio de contato não é enfileirado quando offline: precisa de conexão para completar. Sem rede, o envio falha e o rep é avisado — a mudança pode ser reenviada quando a conexão voltar.Unlike an order, a contact upload is not queued when offline: it needs a connection to complete. Without network the send fails and the rep is warned — the change can be resent when the connection returns.A diferencia del pedido, el envío de contacto no se encola cuando está offline: necesita conexión para completar. Sin red el envío falla y se avisa al rep — el cambio puede reenviarse cuando vuelva la conexión.
- Acompanhar o envioTracking the sendSeguir el envío
- O status técnico do despacho (enviado, com erro) fica registrado no histórico de despachos / central de dados do app — útil para o suporte investigar um envio.The dispatch's technical status (sent, errored) is recorded in the app's dispatch history / data center — useful for support to investigate a send.El estado técnico del despacho (enviado, con error) queda registrado en el historial de despachos / centro de datos de la app — útil para que soporte investigue un envío.
Visão técnicaTechnical overviewVisión técnica
Envio de contato da equipe é a transação de saída que persiste um contato de varejo no backend (Salesforce). É disparada pelo fluxo de Gerenciar equipe (feature staff_details) quando o representante de vendas cria, edita ou exclui um contato. O único item enviado é o objeto ContactDetails.
Staff contact upload is the outbound transaction that persists a retail contact in the backend (Salesforce). It's fired by the Manage staff flow (feature staff_details) when the sales rep creates, edits or deletes a contact. The only item sent is the ContactDetails object.
Envío de contacto del equipo es la transacción de salida que persiste un contacto de punto de venta en el backend (Salesforce). Se dispara desde el flujo de Gestionar equipo (feature staff_details) cuando el representante de ventas crea, edita o elimina un contacto. El único ítem enviado es el objeto ContactDetails.
Um builder, 3 açõesOne builder, 3 actionsUn builder, 3 acciones
Um único builder cobre criar, atualizar e excluir. A ação chega no input como ContactUploadAction; não há variante de serviceName.A single builder covers create, update and delete. The action arrives on the input as ContactUploadAction; there's no serviceName variant.Un único builder cubre crear, actualizar y eliminar. La acción llega en el input como ContactUploadAction; no hay variante de serviceName.
Contrato dirigido por EMCEMC-driven contractContrato dirigido por EMC
Quais campos vão no payload é decidido por StaffConfig (End Market Configuration): CPID, método de contato preferido, designação e idioma são condicionais por mercado.Which fields go in the payload is decided by StaffConfig (End Market Configuration): CPID, preferred contact method, designation and language are conditional per market.Qué campos van en el payload lo decide StaffConfig (End Market Configuration): CPID, método de contacto preferido, designación e idioma son condicionales por mercado.
RPC genéricoGeneric RPCRPC genérico
Não há RPC por contato: tudo passa pelo mesmo sendTransaction, com o JSON serializado no campo message e o serviceName como discriminador.There's no per-contact RPC: everything goes through the same sendTransaction, with the JSON serialized into the message field and serviceName as the discriminator.No hay RPC por contacto: todo pasa por el mismo sendTransaction, con el JSON serializado en el campo message y el serviceName como discriminador.
FontesSourcesFuentes
BuildAccountContactUploadDispatcherPayloadUseCase + AccountContactUploadDispatcherPayloadInput + DispatcherType.retailUpdate + DispatcherConectaRep.proto. O input carrega entities cruas de domínio (CLAUDE.md §36); o build() constrói todo o wire.
BuildAccountContactUploadDispatcherPayloadUseCase + AccountContactUploadDispatcherPayloadInput + DispatcherType.retailUpdate + DispatcherConectaRep.proto. The input carries raw domain entities (CLAUDE.md §36); build() constructs the entire wire.
BuildAccountContactUploadDispatcherPayloadUseCase + AccountContactUploadDispatcherPayloadInput + DispatcherType.retailUpdate + DispatcherConectaRep.proto. El input lleva entities crudas de dominio (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 —type.destination.value(Salesforce)target endpoint —type.destination.value(Salesforce)endpoint destino —type.destination.value(Salesforce)serviceNamestring· #2 · discriminador —RetailerUploadAPIdiscriminator —RetailerUploadAPIdiscriminador —RetailerUploadAPIdateReferencestring· #3 ·AAAA-MM-DDdo envio (submittedAt)YYYY-MM-DDof the submission (submittedAt)AAAA-MM-DDdel envío (submittedAt)transactionReferencestring· #4 · osfiddo varejo (correlação)the retailsfid(correlation)elsfiddel punto de venta (correlación)usernamestring· #5 ·resource.username(sessão)resource.username(session)resource.username(sesión)messagestring· #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"REP"hoje (ver Pendências)"REP"literal today (see Pending)literal"REP"hoy (ver Pendientes)deviceVersionstring· #10tidint64· #11 · id de transação para idempotência/replay (0 num envio novo)transaction id for idempotency/replay (0 on a fresh send)id de transacción para idempotencia/replay (0 en un envío nuevo)
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, account, transactionReference, dateReference). O DispatcherGateway serializa payload em JSON para message, copia serviceName/dateReference/transactionReference, resolve o endpoint por type.destination, preenche os campos de dispositivo, o username e o bearer token de auth, e chama o RPC.
The builder returns a DispatcherEnvelope (type, serviceName, payload, account, transactionReference, dateReference). The DispatcherGateway serializes payload to JSON into message, copies serviceName/dateReference/transactionReference, resolves the endpoint from type.destination, fills in the device fields, the username and the auth bearer token, and calls the RPC.
El builder devuelve un DispatcherEnvelope (type, serviceName, payload, account, transactionReference, dateReference). El DispatcherGateway serializa payload a JSON en message, copia serviceName/dateReference/transactionReference, resuelve el endpoint por type.destination, completa los campos del dispositivo, el username y el bearer token de auth, y llama al RPC.
Para o contato, resendMayDuplicate == true: um reenvio pode duplicar o registro no backend; a idempotência via tid é o que mitiga isso.For the contact, resendMayDuplicate == true: a resend may duplicate the record on the backend; idempotency via tid is what mitigates it.Para el contacto, resendMayDuplicate == true: un reenvío puede duplicar el registro en el backend; la idempotencia vía tid es lo que lo mitiga.
serviceName e cruzamentosserviceName & crossoversserviceName y cruces
Uma única transação, sem variantes. O builder resolve type.resolveServiceName(hasPromotion: false) — sempre false (contato não tem promoção), então nunca há prefixo Promo_. As três ações (criar/atualizar/excluir) usam o mesmo serviceName e o mesmo DispatcherType.A single transaction, no variants. The builder resolves type.resolveServiceName(hasPromotion: false) — always false (a contact has no promotion), so there's never a Promo_ prefix. The three actions (create/update/delete) use the same serviceName and the same DispatcherType.Una única transacción, sin variantes. El builder resuelve type.resolveServiceName(hasPromotion: false) — siempre false (un contacto no tiene promoción), así que nunca hay prefijo Promo_. Las tres acciones (crear/actualizar/eliminar) usan el mismo serviceName y el mismo DispatcherType.
DispatcherType |
serviceName |
MercadosMarketsMercados | DestinoDestinationDestino |
|---|---|---|---|
retailUpdate | RetailerUploadAPI | BR · CL · ZA | Salesforce (default)Salesforce (default)Salesforce (default) |
Cross-service 06 ↔ 07Cross-service 06 ↔ 07Cross-service 06 ↔ 07
O serviceName RetailerUploadAPI (e o DispatcherType.retailUpdate) é compartilhado por dois builders: BuildRetailerUploadDispatcherPayloadUseCase — atualização dos dados do varejo, documentada em 06 · Retailer update — e BuildAccountContactUploadDispatcherPayloadUseCase (este doc). São payloads diferentes sob o mesmo serviceName: 06 envia AccountData; 07 envia ContactDetails.
The RetailerUploadAPI serviceName (and DispatcherType.retailUpdate) is shared by two builders: BuildRetailerUploadDispatcherPayloadUseCase — retail master-data update, documented in 06 · Retailer update — and BuildAccountContactUploadDispatcherPayloadUseCase (this doc). They are different payloads under the same serviceName: 06 sends AccountData; 07 sends ContactDetails.
El serviceName RetailerUploadAPI (y el DispatcherType.retailUpdate) es compartido por dos builders: BuildRetailerUploadDispatcherPayloadUseCase — actualización de los datos del punto de venta, documentada en 06 · Retailer update — y BuildAccountContactUploadDispatcherPayloadUseCase (este doc). Son payloads diferentes bajo el mismo serviceName: 06 envía AccountData; 07 envía ContactDetails.
Colisão de nomes 05 ↔ 07 (falso amigo)Naming collision 05 ↔ 07 (false friend)Colisión de nombres 05 ↔ 07 (falso amigo)
O token "account contact" aparece em dois lugares trocados — leia com atenção, porque o nome engana:The "account contact" token shows up in two swapped places — read carefully, because the name misleads:El token "account contact" aparece en dos lugares cruzados — lea con atención, porque el nombre engaña:
- O serviceName
AccountContactUploadAPInão é desta transação — pertence aDispatcherType.retailNew, o cadastro de novo varejo, documentado em 05 · New retail upload.The serviceNameAccountContactUploadAPIis not this transaction — it belongs toDispatcherType.retailNew, the new-retail registration, documented in 05 · New retail upload.El serviceNameAccountContactUploadAPIno es esta transacción — pertenece aDispatcherType.retailNew, el alta de nuevo punto de venta, documentado en 05 · New retail upload. - Este doc (07) tem o builder nomeado
BuildAccountContactUploadDispatcherPayloadUseCase(o nome "account contact"), mas oserviceNamereal que ele emite éRetailerUploadAPI— nãoAccountContactUploadAPI.This doc (07) has the builder namedBuildAccountContactUploadDispatcherPayloadUseCase(the "account contact" name), but the actualserviceNameit emits isRetailerUploadAPI— notAccountContactUploadAPI.Este doc (07) tiene el builder nombradoBuildAccountContactUploadDispatcherPayloadUseCase(el nombre "account contact"), pero elserviceNamereal que emite esRetailerUploadAPI— noAccountContactUploadAPI. - Regra prática: para este contato, o discriminador no fio é sempre
RetailerUploadAPI. O nome do builder é histórico e não reflete o serviceName.Rule of thumb: for this contact, the on-the-wire discriminator is alwaysRetailerUploadAPI. The builder name is historical and doesn't reflect the serviceName.Regla práctica: para este contacto, el discriminador en el hilo es siempreRetailerUploadAPI. El nombre del builder es histórico y no refleja el serviceName.
Como é disparadoHow it's firedCómo se dispara
A transação é orquestrada pelo fluxo de staff_details (remote-first, §36). O notifier apenas reúne entities cruas e valores injetados; o StaffOrchestrator escolhe a ação; o builder é o dono único de todo join, rename, formatação de data e derivação wire. A cascata:The transaction is orchestrated by the staff_details flow (remote-first, §36). The notifier only gathers raw entities and injected values; StaffOrchestrator picks the action; the builder is the sole owner of every join, rename, date formatting and wire derivation. The cascade:La transacción se orquesta desde el flujo de staff_details (remote-first, §36). El notifier solo reúne entities crudas y valores inyectados; StaffOrchestrator elige la acción; el builder es el dueño único de todo join, rename, formateo de fecha y derivación wire. La cascada:
- StaffDetailsNotifiersaveContact · deleteContact
- reúne entities + escolhe açãogathers entities + picks actionreúne entities + elige acciónStaffOrchestrator_sendContact
- monta input crubuilds raw inputarma input crudoAccountContactUploadDispatcherPayloadInput
- build()BuildAccountContactUploadDispatcherPayloadUseCasemonta o wireassembles the wirearma el wire
- devolvereturnsdevuelveDispatcherEnvelope
- SubmitAccountContactUploadUseCaseDispatcherOrchestratordispatch
- sendDispatcherRepository
- serializa + authserialize + authserializa + authDispatcherGateway
- sendTransactionBackendgRPC · Salesforce
- serializa + authserialize + authserializa + authDispatcherGateway
- sendDispatcherRepository
- SubmitAccountContactUploadUseCaseDispatcherOrchestratordispatch
- devolvereturnsdevuelveDispatcherEnvelope
- build()BuildAccountContactUploadDispatcherPayloadUseCasemonta o wireassembles the wirearma el wire
- monta input crubuilds raw inputarma input crudoAccountContactUploadDispatcherPayloadInput
- reúne entities + escolhe açãogathers entities + picks actionreúne entities + elige acciónStaffOrchestrator_sendContact
O input (entities cruas)The input (raw entities)El input (entities crudas)
AccountContactUploadDispatcherPayloadInput (Freezed). Carrega o dado como existe no domínio; nada de formato wire. O relógio chega como submittedAt (via DateTimeUtils.now()); o CPID chega pré-gerado (StaffIdentifierUtils.generateContactCpid()) mas só é emitido sob condição de config.AccountContactUploadDispatcherPayloadInput (Freezed). Carries data as it exists in the domain; no wire shaping. The clock arrives as submittedAt (via DateTimeUtils.now()); the CPID arrives pre-generated (StaffIdentifierUtils.generateContactCpid()) but is only emitted under a config condition.AccountContactUploadDispatcherPayloadInput (Freezed). Lleva el dato como existe en el dominio; nada de formato wire. El reloj llega como submittedAt (vía DateTimeUtils.now()); el CPID llega pre-generado (StaffIdentifierUtils.generateContactCpid()) pero solo se emite bajo una condición de config.
| CampoFieldCampo | TipoTypeTipo | PapelRoleRol |
|---|---|---|
contact | ContactEntity | contato cru — nome (o builder faz o split), telefone, e-mail, função, idioma, designação, status, b2bPortalStatus, isMainContact, birthdateraw contact — name (the builder splits it), phone, email, role, language, designation, status, b2bPortalStatus, isMainContact, birthdatecontacto crudo — nombre (el builder lo divide), teléfono, correo, función, idioma, designación, status, b2bPortalStatus, isMainContact, birthdate |
account | AccountDataEntity | varejo (cru — o builder deriva retailerID, transactionReference e o DispatchAccountEntity a partir de sfid/customerCode/name)retail (raw — the builder derives retailerID, transactionReference and the DispatchAccountEntity from sfid/customerCode/name)punto de venta (crudo — el builder deriva retailerID, transactionReference y el DispatchAccountEntity desde sfid/customerCode/name) |
staffConfig | StaffConfig | flags wire por mercado (do EMC accountEditionConfig.staffConfig): CPID, método de contato, designação+idioma, desativação B2B, recordTypeId, idioma preferidoper-market wire flags (from EMC accountEditionConfig.staffConfig): CPID, contact method, designation+language, B2B deactivation, recordTypeId, preferred languageflags wire por mercado (del EMC accountEditionConfig.staffConfig): CPID, método de contacto, designación+idioma, desactivación B2B, recordTypeId, idioma preferido |
action | ContactUploadAction | create · update · delete |
mainContactCpid | String | CPID pré-gerado; → CPID só quando config.generatesMainContactCpid && contact.isMainContactpre-generated CPID; → CPID only when config.generatesMainContactCpid && contact.isMainContactCPID pre-generado; → CPID solo cuando config.generatesMainContactCpid && contact.isMainContact |
submittedAt | DateTime | relógio (DateTimeUtils.now()) → dateReferenceclock (DateTimeUtils.now()) → dateReferencereloj (DateTimeUtils.now()) → dateReference |
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. Campo, Tipo e Origem são código cru; só a Regra é prosa. Um exemplo completo (ZA / atualização) 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. Field, Type and Source are raw code; only Rule is prose. A full example (ZA / update) 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. Campo, Tipo y Origen son código crudo; solo la Regla es prosa. Un ejemplo completo (ZA / actualización) 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 |
|---|---|---|---|
ContactDetails | array | [contactDetails] | array de um único objeto (o contato deste envio)single-element array (the contact of this send)array de un solo objeto (el contacto de este envío) |
ContactDetails objeto únicosingle objectobjeto único 18 campos (16 fixos + 2 condicionais)fields (16 fixed + 2 conditional)campos (16 fijos + 2 condicionales)
Campo JSON TipoTypeTipo Origem do DadoData sourceOrigen del Dato RegraRuleRegla contactIdstring contact.idcriação → ""; atualização/exclusão →contact.idcreate →""; update/delete →contact.idcreación →""; actualización/eliminación →contact.idretailerIDstring account.sfid— contactnamestring _splitName(contact.name)primeiro token do nome (ver Regras)first token of the name (see Rules)primer token del nombre (ver Reglas) ContactName_LNamestring _splitName(contact.name)resto do nome (após o 1º token)rest of the name (after the 1st token)resto del nombre (tras el 1.º token) ContactNumberint | string PhoneUtils.digitsOnly(contact.phone)só dígitos, como int; vazio ou não parseável →""(tipo dinâmico)digits only, asint; empty or unparseable →""(dynamic type)solo dígitos, comoint; vacío o no parseable →""(tipo dinámico)contact_titlestring Fixo: ""inerte — não há campo de título/saudação no ContactEntityinert — no title/salutation field onContactEntityinerte — no hay campo de título/saludo enContactEntityCPIDstring input.mainContactCpidconfig.generatesMainContactCpid && contact.isMainContact ? mainContactCpid : ""config.generatesMainContactCpid && contact.isMainContact ? mainContactCpid : ""config.generatesMainContactCpid && contact.isMainContact ? mainContactCpid : ""isPrimarybool contact.isMainContact— DOBstring contact.birthdateformato yyyy/MM/dd; null →""yyyy/MM/ddformat; null →""formatoyyyy/MM/dd; null →""PreferedLanguagestring config.contactPreferredLanguagevalor do EMC (BR "Portuguese" · CL "Spanish" · ZA "English")EMC value (BR "Portuguese" · CL "Spanish" · ZA "English")valor del EMC (BR "Portuguese" · CL "Spanish" · ZA "English") PreferedCntMethodstring contact.preferredMethodOfContact?.labelconfig.sendsPreferredContactMethod→ label; senão""config.sendsPreferredContactMethod→ label; else""config.sendsPreferredContactMethod→ label; si no""B2BRolestring contact.role?.labellabel do ContactRole; null →""ContactRolelabel; null →""label delContactRole; null →""B2BStatusstring _b2bStatus(...)b2bPortalStatus.wireValue; exclusão +deactivatesB2bStatusOnDelete+ ativo →"Inactive"(ver Regras)b2bPortalStatus.wireValue; delete +deactivatesB2bStatusOnDelete+ active →"Inactive"(see Rules)b2bPortalStatus.wireValue; eliminación +deactivatesB2bStatusOnDelete+ activo →"Inactive"(ver Reglas)Statusstring contact.statusexclusão → "Inactive"; senãocontact.status.titleCasedelete →"Inactive"; elsecontact.status.titleCaseeliminación →"Inactive"; si nocontact.status.titleCaseEmailstring contact.emailnull → ""null →""null →""recordTypeIdstring config.contactRecordTypeIddo EMC (BR ""· CL/ZA preenchido)from EMC (BR""· CL/ZA filled)del EMC (BR""· CL/ZA completado)contactDesignationstring contact.contactDesignation?.labelchave condicional — só quando config.sendsContactDesignationAndLanguagePreference(ZA); null →""conditional key — only whenconfig.sendsContactDesignationAndLanguagePreference(ZA); null →""clave condicional — solo cuandoconfig.sendsContactDesignationAndLanguagePreference(ZA); null →""languagePreferencestring contact.languagePreference?.isoCodechave condicional — só quando config.sendsContactDesignationAndLanguagePreference(ZA); null →LanguagePreference.fallbackIsoCode("en_US")conditional key — only whenconfig.sendsContactDesignationAndLanguagePreference(ZA); null →LanguagePreference.fallbackIsoCode("en_US")clave condicional — solo cuandoconfig.sendsContactDesignationAndLanguagePreference(ZA); null →LanguagePreference.fallbackIsoCode("en_US")
ExemploExampleEjemplo
Um payload completo (ZA / atualização, contato principal — mostra as 18 chaves, inclusive as 2 condicionais) está em docs/public/dispatcher/07_staff_contact_upload/transaction_example.json — a forma exata serializada em message (sem wrapper gRPC).
A complete payload (ZA / update, main contact — shows all 18 keys, including the 2 conditional ones) is in docs/public/dispatcher/07_staff_contact_upload/transaction_example.json — the exact shape serialized into message (no gRPC wrapper).
Un payload completo (ZA / actualización, contacto principal — muestra las 18 claves, incluidas las 2 condicionales) está en docs/public/dispatcher/07_staff_contact_upload/transaction_example.json — la forma exacta serializada en message (sin wrapper gRPC).
Regras de negócioBusiness rulesReglas de negocio
Split do nomeName splitDivisión del nombre _splitName → contactname · ContactName_LName
contact.nameé dividido por espaços (trim+\s+, tokens vazios descartados).contact.nameis split on whitespace (trim+\s+, empty tokens dropped).contact.namese divide por espacios (trim+\s+, tokens vacíos descartados).- 0 tokens →
("", ""); 1 token →(token, ""); 2+ → primeiro token emcontactname, o resto (unido por espaço) emContactName_LName.0 tokens →("", ""); 1 token →(token, ""); 2+ → first token incontactname, the rest (space-joined) inContactName_LName.0 tokens →("", ""); 1 token →(token, ""); 2+ → primer token encontactname, el resto (unido por espacio) enContactName_LName.
TelefonePhoneTeléfono ContactNumber
PhoneUtils.digitsOnly(contact.phone)remove tudo que não é dígito (null →"").PhoneUtils.digitsOnly(contact.phone)strips every non-digit (null →"").PhoneUtils.digitsOnly(contact.phone)quita todo lo que no sea dígito (null →"").- Se ficou vazio →
""; senãoint.tryParse(digits) ?? "". O valor é dinâmico: sai como número (int) quando parseável, e como string vazia caso contrário (ex.: número longo demais paraint).If empty →""; elseint.tryParse(digits) ?? "". The value is dynamic: it goes out as a number (int) when parseable, and as an empty string otherwise (e.g. a number too long forint).Si quedó vacío →""; si noint.tryParse(digits) ?? "". El valor es dinámico: sale como número (int) cuando es parseable, y como string vacía en caso contrario (p. ej. número demasiado largo paraint).
CPID do contato principalMain-contact CPIDCPID del contacto principal CPID · generatesMainContactCpid
CPID só é preenchido quando ambos: o mercado gera CPID (config.generatesMainContactCpid) e o contato é o principal (contact.isMainContact). Nesse caso vale input.mainContactCpid (gerado pelo StaffIdentifierUtils.generateContactCpid()); senão "". BR nunca gera (config false); CL e ZA geram.CPID is only filled when both: the market generates CPID (config.generatesMainContactCpid) and the contact is the main one (contact.isMainContact). Then it's input.mainContactCpid (generated by StaffIdentifierUtils.generateContactCpid()); otherwise "". BR never generates (config false); CL and ZA do.CPID solo se completa cuando ambos: el mercado genera CPID (config.generatesMainContactCpid) y el contacto es el principal (contact.isMainContact). En ese caso vale input.mainContactCpid (generado por StaffIdentifierUtils.generateContactCpid()); si no "". BR nunca genera (config false); CL y ZA sí.
Status B2B na exclusãoB2B status on deleteStatus B2B en la eliminación _b2bStatus → B2BStatus
- basebasebase
contact.b2bPortalStatus ?? unknown, emitido comowireValue(unknown→"").contact.b2bPortalStatus ?? unknown, emitted aswireValue(unknown→"").contact.b2bPortalStatus ?? unknown, emitido comowireValue(unknown→""). - override na exclusãodelete overrideoverride en eliminaciónse
deactivatesB2bStatusOnDelete(só ZA) e ação é exclusão e status atual éactive→B2bPortalStatus.inactive.wireValue("Inactive").ifdeactivatesB2bStatusOnDelete(ZA only) and action is delete and current status isactive→B2bPortalStatus.inactive.wireValue("Inactive").sideactivatesB2bStatusOnDelete(solo ZA) y la acción es eliminación y el status actual esactive→B2bPortalStatus.inactive.wireValue("Inactive").
B2BStatus (status do portal B2B) é distinto de Status (status do registro do contato).B2BStatus (the B2B portal status) is distinct from Status (the contact record status).B2BStatus (status del portal B2B) es distinto de Status (status del registro del contacto).
Status do registroRecord statusStatus del registro Status
- Exclusão → literal
"Inactive".Delete → literal"Inactive".Eliminación → literal"Inactive". - Criação/atualização →
contact.status.titleCase(statusé uma String crua do backend; ex.:"active"→"Active").Create/update →contact.status.titleCase(statusis a raw backend String; e.g."active"→"Active").Creación/actualización →contact.status.titleCase(statuses una String cruda del backend; p. ej."active"→"Active").
Chaves condicionaisConditional keysClaves condicionales contactDesignation · languagePreference
- As duas chaves só entram no objeto quando
config.sendsContactDesignationAndLanguagePreference— hoje só ZA. Em BR e CL o objeto tem 16 chaves; em ZA, 18.Both keys only enter the object whenconfig.sendsContactDesignationAndLanguagePreference— today ZA only. In BR and CL the object has 16 keys; in ZA, 18.Ambas claves solo entran en el objeto cuandoconfig.sendsContactDesignationAndLanguagePreference— hoy solo ZA. En BR y CL el objeto tiene 16 claves; en ZA, 18. languagePreferenceemite oisoCodedo idioma do contato (ex.:"af","zu"); ausente →"en_US"(fallbackIsoCode). Não confundir comPreferedLanguage, que é o rótulo por mercado do EMC.languagePreferenceemits the contact language'sisoCode(e.g."af","zu"); absent →"en_US"(fallbackIsoCode). Not to be confused withPreferedLanguage, the per-market EMC label.languagePreferenceemite elisoCodedel idioma del contacto (p. ej."af","zu"); ausente →"en_US"(fallbackIsoCode). No confundir conPreferedLanguage, el label por mercado del EMC.
Pendências / roadmapPending / roadmapPendientes / roadmap
O que o builder ainda não preenche ou envia inerte, documentado fiel ao estado atual do código (nunca descrito como se já existisse):What the builder does not yet fill, or ships inert, documented faithfully to the current code state (never described as already existing):Lo que el builder aún no completa, o envía inerte, documentado fiel al estado actual del código (nunca descrito como si ya existiera):
Não portado / pendenteNot ported / pendingNo portado / pendiente
contact_title: sempre""— oContactEntitydo app não modela título/saudação (contraste com osalutation/contactPositiondoContactDataem 05 · New retail upload).contact_title: always""— the app'sContactEntitydoesn't model title/salutation (contrast withsalutation/contactPositionon theContactDatain 05 · New retail upload).contact_title: siempre""— elContactEntityde la app no modela título/saludo (contraste consalutation/contactPositiondelContactDataen 05 · New retail upload).ContactNumber: tipo dinâmico (intou"") no mesmo campo — um número muito longo paraintvira""silenciosamente.ContactNumber: dynamic type (intor"") on the same field — a number too long forintsilently becomes"".ContactNumber: tipo dinámico (into"") en el mismo campo — un número demasiado largo paraintpasa a""silenciosamente.- Nomenclatura: o builder é
BuildAccountContactUploadDispatcherPayloadUseCase, mas oserviceNameemitido éRetailerUploadAPI— o nome do builder não reflete o serviceName (ver seção 06).Naming: the builder isBuildAccountContactUploadDispatcherPayloadUseCase, but the emittedserviceNameisRetailerUploadAPI— the builder name doesn't reflect the serviceName (see section 06).Nomenclatura: el builder esBuildAccountContactUploadDispatcherPayloadUseCase, pero elserviceNameemitido esRetailerUploadAPI— el nombre del builder no refleja el serviceName (ver sección 06). - Transporte:
deviceUuidvai como literal"REP"no gateway (pendência conhecida do Dispatcher, comum a todas as transações).Transport:deviceUuidships as the literal"REP"in the gateway (known Dispatcher pending item, common to all transactions).Transporte:deviceUuidva como literal"REP"en el gateway (pendiente conocido del Dispatcher, común a todas las transacciones).
MercadosMarketsMercados
A disponibilidade da transação vem do DispatcherType.retailUpdate.enabledMarkets — BR/CL/ZA. Não há dispatcher de contato em AR/PY/PE. O conteúdo do payload varia por mercado via StaffConfig (EMC): quais chaves entram e alguns valores fixos.Transaction availability comes from DispatcherType.retailUpdate.enabledMarkets — BR/CL/ZA. There's no contact dispatcher in AR/PY/PE. The payload content varies by market via StaffConfig (EMC): which keys enter and some fixed values.La disponibilidad de la transacción viene de DispatcherType.retailUpdate.enabledMarkets — BR/CL/ZA. No hay dispatcher de contacto en AR/PY/PE. El contenido del payload varía por mercado vía StaffConfig (EMC): qué claves entran y algunos valores fijos.
A matriz das flags de StaffConfig que dirigem o payload, por mercado (fonte: EMC):The StaffConfig flag matrix driving the payload, per market (source: EMC):La matriz de flags de StaffConfig que dirigen el payload, por mercado (fuente: EMC):
| Flag do StaffConfigStaffConfig flagFlag de StaffConfig | BR | CL | ZA |
|---|---|---|---|
generatesMainContactCpid | false | x | x |
sendsPreferredContactMethod | false | false | x |
sendsContactDesignationAndLanguagePreference | false | false | x |
deactivatesB2bStatusOnDelete | false | false | x |
contactRecordTypeId | "" | x | x |
contactPreferredLanguage | Portuguese | Spanish | English |
Contrato enxutoLean contractContrato reducido
16 chaves (sem contactDesignation/languagePreference). CPID nunca é gerado, PreferedCntMethod vai "", recordTypeId é "", o status B2B não é forçado a Inactive na exclusão. PreferedLanguage = "Portuguese".
16 keys (no contactDesignation/languagePreference). CPID is never generated, PreferedCntMethod goes "", recordTypeId is "", B2B status isn't forced to Inactive on delete. PreferedLanguage = "Portuguese".
16 claves (sin contactDesignation/languagePreference). CPID nunca se genera, PreferedCntMethod va "", recordTypeId es "", el status B2B no se fuerza a Inactive en la eliminación. PreferedLanguage = "Portuguese".
CPID + recordTypeIdCPID + recordTypeIdCPID + recordTypeId
16 chaves. Gera CPID para o contato principal e envia recordTypeId. Ainda sem designação/idioma nem método de contato preferido. PreferedLanguage = "Spanish".
16 keys. Generates CPID for the main contact and sends recordTypeId. Still no designation/language nor preferred contact method. PreferedLanguage = "Spanish".
16 claves. Genera CPID para el contacto principal y envía recordTypeId. Aún sin designación/idioma ni método de contacto preferido. PreferedLanguage = "Spanish".
Contrato completoFull contractContrato completo
18 chaves — as únicas que adicionam contactDesignation e languagePreference. Também envia PreferedCntMethod, gera CPID e desativa o status B2B na exclusão (deactivatesB2bStatusOnDelete). PreferedLanguage = "English".
18 keys — the only ones that add contactDesignation and languagePreference. Also sends PreferedCntMethod, generates CPID and deactivates the B2B status on delete (deactivatesB2bStatusOnDelete). PreferedLanguage = "English".
18 claves — las únicas que agregan contactDesignation y languagePreference. También envía PreferedCntMethod, genera CPID y desactiva el status B2B en la eliminación (deactivatesB2bStatusOnDelete). PreferedLanguage = "English".
AR · PY · PE
Existem como mercados do app (config PANGEA mínima), mas não têm dispatcher de contato — retailUpdate.enabledMarkets não os lista. A edição de contato não é disparada nesses mercados.
They exist as app markets (minimal PANGEA config), but have no contact dispatcher — retailUpdate.enabledMarkets doesn't list them. Contact editing is not fired in these markets.
Existen como mercados de la app (config PANGEA mínima), pero no tienen dispatcher de contacto — retailUpdate.enabledMarkets no los lista. La edición de contacto no se dispara en estos mercados.