event

Franchise Partner Added

A person became a partner of a franchise unit, with their roles and whether they are a primary contact of the unit. One message per partner. Consumed by cna-nexus to invalidate the economic group statistics cache and by cna-one to give the partner access to the franchise.

Event Topic: FranchiseKey: franchiseIdPayload drift

Overview

Fact: a Franchise PartnerFranchise PartnerEntityv1.1.0The link between a franchise unit and a person behind it, with one or more roles - FRANCHISEE (leads the unit) or OPERAT...Ownercna-platformView docs row was added. The payload carries the unit, the partner’s roles, whether the partner is a primary contact of the unit and a snapshot of the person.

Since 1.2.0 isPrimaryContact says whether the partner is a primary contact of the franchise, the partner the franchisor team usually talks to. It belongs to the link with this franchise, not to the person: the same person may be a contact of one unit and not of another, so it is not part of Partner UpdatedPartner UpdatedEventv1.0.0The personal data of a franchise partner changed in CNA Nexus - name, legal name, e-mail, phone or birth date. Snapshot ...Ownercna-platformSchemaMapView docs. A franchise may have none, one or several. Operations and Implementation set it by hand in cna-nexus; contracts never set it, so a partner added by a contract or a legal info edit arrives with false, and a partner recreated by an audit reversal arrives with the flag they had.

Since 1.1.0 person.birthDate is a calendar day, YYYY-MM-DD, with no time zone: the column is a date, and a consumer that built a Date from the former midnight-UTC timestamp and stored it in a negative offset landed on the day before.

When it is published. CNA NexusCNA NexusServicev1.0.0Franchise network back-office (franchises, economic groups, partners, contracts and amendments, document storage, AI doc...PublishesInviteUser, ContractActivationRequested +13SubscribesContractActivationRequested, AmendmentApplyRequested +4Ownercna-platformMapRepoView docs, in addPartners, which runs inside replaceFranchisePartners whenever a franchise is created from a contract, its legal information is edited, or a contract is applied onto it, and when an audit reversal recreates a removed partner. Registered with manager.afterCommit(), one message per added partner, batched in a single send. The same commit may also carry Franchise Partner RemovedFranchise Partner RemovedEventv1.2.0A person stopped being a partner of a franchise unit. One message per partner. Consumed by cna-nexus to invalidate the e...Ownercna-platformSchemaMapView docs and Franchise Partner UpdatedFranchise Partner UpdatedEventv1.2.0The roles or the primary contact flag of a franchise partner changed. Carries both before and after. Consumed by cna-nex...Ownercna-platformSchemaMapView docs messages. A later change to the person’s own data does not republish this event; it travels as Partner UpdatedPartner UpdatedEventv1.0.0The personal data of a franchise partner changed in CNA Nexus - name, legal name, e-mail, phone or birth date. Snapshot ...Ownercna-platformSchemaMapView docs on Partner (Kafka topic)Partner (Kafka topic)Channelv1.0.0Kafka topic for the Partner aggregate - a person in their capacity as partner of one or more franchise units. Carries Pa...Ownercna-platformView docs (target contract).

What the consumer does. cna-nexus (touchEconomicGroupOnFranchisePartnerChange, group economic-group-partner-touch): loads the franchise; if it has an economic group, bumps the group’s updatedAt. That timestamp versions the Redis cache of the economic group statistics (units, franchisees, active groups), so the next read recomputes them. Franchises without a group are ignored. CNA OneCNA OneServicev1.0.0Franchise operating system (franchises and employees, pricing, products and learning books, school operations). Publishe...PublishesGrantApplicationAccess, EmployeeCreated +6SubscribesInviteUser, FranchiseCreated +12Ownercna-platformMapRepoView docs (group franchise-franchise-events, handler syncFranchisePartner, on staging through cna-br/cna-one#903, not yet in production): mirrors the partner as an employee with the FRANCHISEE job role and access to the franchise; a partner of a franchise cna-one does not carry is logged and dropped. Its 1.1.0 copy does not read isPrimaryContact.

Kafka

Topic (aggregateRoot)Franchise
Message key (routingKey)payload.franchiseId
Contract ownerNEXUS
metadata.eventFranchisePartnerAdded
Consumer groupseconomic-group-partner-touch (cna-nexus), franchise-franchise-events (cna-one, pinned to 1.1.0)

Payload schema

Source of truth

export type FranchisePartnerEventPerson = {
id: string;
document: string;
documentType: PersonsDocumentType;
name: string;
email?: string | null;
phone?: string | null;
birthDate?: string | null;
legalName?: string | null;
isFranchisee: boolean;
};
export type FranchisePartnerEventPayload = {
franchiseId: string;
roles: FranchisePartnerRole[];
isPrimaryContact: boolean;
person: FranchisePartnerEventPerson;
};
export class FranchisePartnerAdded extends Event<FranchisePartnerEventPayload> {
static readonly owner = 'NEXUS';
static readonly aggregateRoot = 'Franchise';
static readonly routingKey = 'franchiseId';
static readonly version = '1.2.0';
}

Known drift

Observed 2026-10-05.

  • cna-nexus api/app/events/nexus/franchise/FranchisePartnerAdded.ts: production (v0.14.0) sends the 1.1.0 payload, with person.birthDate as YYYY-MM-DD, but declares static readonly version = '1.0.0', so metadata.version reads 1.0.0. staging declares 1.1.0 (cna-br/cna-nexus#587). The 1.2.0 payload, with isPrimaryContact, is on cna-nexus branch jc/feature/CWH-52595-publish-primary-contact-in-partner-events (pull request pending) and declares 1.2.0. No pin: cna-nexus is the producer. This note goes when the class declaring 1.2.0 is in production.
  • cna-one api/app/events/nexus/franchise/FranchisePartnerAdded.ts on staging (cna-br/cna-one#903, not yet in production) declares version = '1.1.0' and has no isPrimaryContact; it also types roles and person.documentType as string instead of the enums. cna-one’s receives is pinned to 1.1.0 until a copy carrying isPrimaryContact is in production (no pull request yet).
  • person.isFranchisee is true when the person holds FRANCHISEE in any franchise, not only in franchiseId.

Custom properties

PropertyValue
Contract Ownerx-contract-ownerNEXUS
Kafka Topicx-kafka-topicFranchise
Message Keyx-message-keyfranchiseId
Sourcex-sourcecna-nexus api/app/events/nexus/franchise/FranchisePartnerAdded.ts
Driftx-driftcna-nexus production sends the 1.1.0 payload but its class declares version 1.0.0, and 1.2.0 (isPrimaryContact) is pending in a pull request; cna-one's copy is 1.1.0, without isPrimaryContact, and its receives is pinned to 1.1.0 until it is updated and in production; isFranchisee is global across franchises, not scoped to franchiseId.
13 properties
franchiseIdstring
required

Franchise id (UUID). Kafka message key.

rolesarray[string]
required

Roles of the partner in this unit (FranchisePartnerRole). Never empty.

isPrimaryContactboolean
required

Whether the partner is a primary contact of this franchise for the franchisor team. Belongs to the link with this franchise, not to the person. Set by hand in cna-nexus: false for a partner added by a contract or a legal info edit; a reverted removal brings back the flag the partner had.

personobject
required

Snapshot of the person (common.persons).