event

Franchise Created

A franchise unit was created in CNA Nexus by activating an opening contract. Full snapshot of the unit; cna-one mirrors it, its address and its partners as employees.

Event Topic: FranchiseKey: idPayload drift

Overview

Fact: a franchise unit now exists in Nexus. The payload is a full snapshot of the aggregate re-read after commit: unit data, statuses, category, economic group, region, address, partners (without roles) and products.

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 createFranchiseForOpening, the only place a franchise is created: the activation of an OPENING ContractContractEntityv1.0.0A franchise contract in CNA Nexus - opening, renewal or resale - uploaded as a document, analysed by AI, reviewed by a p...Ownercna-platformView docs with no franchise yet (see Contract Activation RequestedContract Activation RequestedEventv1.0.0A user saved a contract for activation. cna-nexus's activation consumer turns it ACTIVE and creates or updates the franc...Ownercna-platformSchemaMapView docs). Registered with manager.afterCommit(), so it goes out only after the unit, its brand, address, territory and partners are committed. The same commit also emits one Franchise Partner AddedFranchise Partner AddedEventv1.1.0A person became a partner of a franchise unit, with their roles. One message per partner. Consumed by cna-nexus itself t...Ownercna-platformSchemaMapView docs per partner.

What the consumer does. CNA OneCNA OneServicev1.0.0Franchise operating system (franchises and employees, pricing, products and learning books, school operations). Publishe...PublishesGrantApplicationAccess, EmployeeCreated +6SubscribesInviteUser, FranchiseCreated +9Ownercna-platformMapRepoView docs (franchiseConsumer, handler syncFranchise), in three blocks:

  1. Franchise: requires and validates cnpj and legalName, checks name, slug and e-mail uniqueness, resolves economicGroup.id (it must have been mirrored by Economic Group CreatedEconomic Group CreatedEventv1.0.0An economic group was created in CNA Nexus. cna-one mirrors it by id so that franchises can reference it.Ownercna-platformSchemaMapView docs first, otherwise the message fails), maps the enums onto cna-one’s own, derives status (ACTIVE when operationalStatus is OPERATIONAL), keeps only the distinct product sub-categories, and upserts by id. Since 1.1.0 the snapshot also carries the unit’s primary contact (email, whatsapp, phone), which cna-one mirrors into the franchise; the e-mail must be unique among its franchises. If this block fails nothing is written.
  2. Address: skipped when absent; requires street, number, neighborhood, postal code and an IBGE cityId known to cna-one; upserts the unit’s address in place. Failure here is logged and skipped. Since 1.2.0 the block also carries countryId (ISO 3166-1 alpha-2, always present), foreignCity and foreignState (filled only for an address outside Brazil, where cityId, cityName and state are null) and the coordinates latitude and longitude as decimal text; cna-one ignores the five.
  3. Partners: one per distinct document; each becomes a person, an employee, an employment contract with the FRANCHISEE job role and an active staff link, in one transaction. cna-one then publishes its own Employee CreatedEmployee CreatedEventv1.0.0An employee was created in CNA One, with the person it points at. Consumed by cna-auth to provision the account and the ...Ownercna-platformSchemaMapView docs (new employee) or Employee UpdatedEmployee UpdatedEventv1.0.0An employee or the person it points at changed in CNA One. Consumed by cna-auth to update the account and the user, incl...Ownercna-platformSchemaMapView docs (already known), which is what provisions the login in cna-auth. One bad partner is skipped without stopping the others. Since 1.2.0 each partner is identified by id, the person id the Franchise Partner AddedFranchise Partner AddedEventv1.1.0A person became a partner of a franchise unit, with their roles. One message per partner. Consumed by cna-nexus itself t...Ownercna-platformSchemaMapView docs family already carries in person.id; until 1.1.0 the field was personId. cna-one never read it: it correlates partners by document.

Failures are logged and reported to Sentry, never rethrown.

Kafka

Topic (aggregateRoot)Franchise
Message key (routingKey)payload.id
Contract ownerNEXUS
metadata.eventFranchiseCreated
Consumer groupfranchise-franchise-events

Payload schema

Source of truth

export type FranchisePartnerData = {
id: string;
document: string;
documentType: string;
name: string;
legalName?: string | null;
email?: string | null;
};
export type FranchiseAddressData = {
street?: string | null;
number?: string | null;
complement?: string | null;
neighborhood?: string | null;
postalCode?: string | null;
cityId?: number | null;
cityName?: string | null;
state?: string | null;
countryId: string;
foreignCity?: string | null;
foreignState?: string | null;
latitude?: string | null;
longitude?: string | null;
};
export type FranchisePayload = {
id: string;
name: string;
code?: string | null;
cnpj?: string | null;
legalName?: string | null;
email?: string | null;
whatsapp?: string | null;
phone?: string | null;
stateRegistration?: string | null;
taxRegime?: string | null;
protheusId?: string | null;
conectaEnabled: boolean;
openingStatus: string;
operationalStatus: string;
financialStatus?: string | null;
operationalStartAt?: string | null;
operationalEndAt?: string | null;
firstContractStartsOn?: string | null;
category: FranchiseCategoryData;
economicGroup?: FranchiseEconomicGroupData | null;
operationalRegion?: FranchiseOperationalRegionData | null;
address?: FranchiseAddressData | null;
partners: FranchisePartnerData[];
products: FranchiseProductData[];
};
export class FranchiseCreated extends Event<FranchisePayload> {
static readonly owner = 'NEXUS';
static readonly aggregateRoot = 'Franchise';
static readonly routingKey = 'id';
static readonly version = '1.2.0';
}

Known drift

cna-one api/app/events/nexus/franchise/FranchiseCreated.ts differs from the canonical type. Its copy was aligned to 1.1.0 by cna-br/cna-one#962 (merged into staging, not yet in production) and does not have the 1.2.0 changes. Production still runs the 1.0.0 copy, so cna-one’s receives is pinned to 1.0.0 until #962 and the 1.2.0 alignment are in production. None of the differences changes what cna-one stores: it ignores the address fields below and correlates partners by document.

Fieldcna-nexus (1.2.0)cna-one (staging, 1.1.0)
address.countryId, foreignCity, foreignState, latitude, longitudepresentabsent (ignored)
partner identifierpartners[].idpartners[].personId

Custom properties

PropertyValue
Contract Ownerx-contract-ownerNEXUS
Kafka Topicx-kafka-topicFranchise
Message Keyx-message-keyid
Sourcex-sourcecna-nexus api/app/events/nexus/franchise/FranchiseCreated.ts
Driftx-driftcna-one's copy of FranchisePayload matches 1.1.0 (cna-br/cna-one#962, in staging) and lags 1.2.0: address lacks countryId, foreignCity, foreignState, latitude and longitude; partners[] still uses personId instead of id. Production runs the 1.0.0 copy, so cna-one's receives is pinned to 1.0.0 until #962 and the 1.2.0 alignment are in production.
54 properties
idstring
required

Franchise id (UUID). Kafka message key; cna-one mirrors the unit under this id.

namestring
required

Unit name.

codestring | null

Internal unit code, unique.

cnpjstring | null

Company registration number as stored (no normalisation). cna-one requires and validates it.

legalNamestring | null

Legal company name. cna-one requires it.

emailstring | null<email>

Primary contact e-mail of the unit (franchises.email in cna-nexus, not unique there). cna-one mirrors it and requires it to be unique across its franchises.

whatsappstring | null

WhatsApp number in E.164 (+5511912345678). Mirrored by cna-one.

Match pattern: ^\+[1-9]\d{6,14}$
phonestring | null

Primary contact phone in E.164. Mirrored by cna-one.

Match pattern: ^\+[1-9]\d{6,14}$
stateRegistrationstring | null

State registration number.

taxRegimestring | null

Tax regime (FranchiseTaxRegime), typed as string in the class.

Allowed values: SIMPLES_NACIONAL LUCRO_PRESUMIDO LUCRO_REAL
protheusIdstring | null

Id in the Protheus ERP.

conectaEnabledboolean
required

Inter-school sharing enabled.

openingStatusstring
required

Lifecycle stage (FranchiseOpeningStatus), typed as string in the class.

Allowed values: CREATED BUILT TRAINED SUSPENDED UNDER_REVIEW DEFAULTING
operationalStatusstring
required

Operational status (FranchiseOperationalStatus). cna-one maps OPERATIONAL to ACTIVE, anything else to INACTIVE.

Allowed values: OPERATIONAL NON_OPERATIONAL UNDER_DEVELOPMENT
financialStatusstring | null

Financial standing (FranchiseFinancialStatus).

Allowed values: ACTIVE INACTIVE SUSPENDED DEFAULTING UNDER_REVIEW
operationalStartAtstring | null<date-time>

ISO-8601 instant when operations started.

operationalEndAtstring | null<date-time>

ISO-8601 instant when operations ended.

firstContractStartsOnstring | null<date>

YYYY-MM-DD start of the earliest opening contract, or null. Ignored by cna-one.

categoryobject
required

Size/type category of the unit.

economicGroupobject | null

Economic group of the unit, or null. cna-one requires the group to have been mirrored first (EconomicGroupCreated).

operationalRegionobject | null

Operational region, or null.

addressobject | null

Unit address, or null. cna-one requires street, number, neighborhood, postalCode and cityId to mirror it; it ignores countryId, foreignCity, foreignState, latitude and longitude.

partnersarray[object]
required

People behind the unit. May be empty. Roles are not included; cna-one turns each into a FRANCHISEE employee.

productsarray[object]
required

Products the unit is enabled to sell. cna-one keeps only the distinct sub-categories.