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.
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:
- Franchise: requires and validates
cnpjandlegalName, checks name, slug and e-mail uniqueness, resolveseconomicGroup.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, derivesstatus(ACTIVEwhenoperationalStatusisOPERATIONAL), keeps only the distinct product sub-categories, and upserts byid. 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. - Address: skipped when absent; requires street, number, neighborhood, postal code and an IBGE
cityIdknown to cna-one; upserts the unit’s address in place. Failure here is logged and skipped. Since 1.2.0 the block also carriescountryId(ISO 3166-1 alpha-2, always present),foreignCityandforeignState(filled only for an address outside Brazil, wherecityId,cityNameandstateare null) and the coordinateslatitudeandlongitudeas decimal text; cna-one ignores the five. - Partners: one per distinct document; each becomes a person, an employee, an employment contract with the
FRANCHISEEjob 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 byid, 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 inperson.id; until 1.1.0 the field waspersonId. cna-one never read it: it correlates partners bydocument.
Failures are logged and reported to Sentry, never rethrown.
Kafka
Topic (aggregateRoot) | Franchise |
Message key (routingKey) | payload.id |
| Contract owner | NEXUS |
metadata.event | FranchiseCreated |
| Consumer group | franchise-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.
| Field | cna-nexus (1.2.0) | cna-one (staging, 1.1.0) |
|---|---|---|
address.countryId, foreignCity, foreignState, latitude, longitude | present | absent (ignored) |
| partner identifier | partners[].id | partners[].personId |
Custom properties
| Property | Value |
|---|---|
| Contract Ownerx-contract-owner | NEXUS |
| Kafka Topicx-kafka-topic | Franchise |
| Message Keyx-message-key | id |
| Sourcex-source | cna-nexus api/app/events/nexus/franchise/FranchiseCreated.ts |
| Driftx-drift | cna-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. |
Franchise id (UUID). Kafka message key; cna-one mirrors the unit under this id.
Unit name.
Internal unit code, unique.
Company registration number as stored (no normalisation). cna-one requires and validates it.
Legal company name. cna-one requires it.
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.
WhatsApp number in E.164 (+5511912345678). Mirrored by cna-one.
^\+[1-9]\d{6,14}$Primary contact phone in E.164. Mirrored by cna-one.
^\+[1-9]\d{6,14}$State registration number.
Tax regime (FranchiseTaxRegime), typed as string in the class.
SIMPLES_NACIONAL LUCRO_PRESUMIDO LUCRO_REAL Id in the Protheus ERP.
Inter-school sharing enabled.
Lifecycle stage (FranchiseOpeningStatus), typed as string in the class.
CREATED BUILT TRAINED SUSPENDED UNDER_REVIEW DEFAULTINGOperational status (FranchiseOperationalStatus). cna-one maps OPERATIONAL to ACTIVE, anything else to INACTIVE.
OPERATIONAL NON_OPERATIONAL UNDER_DEVELOPMENTFinancial standing (FranchiseFinancialStatus).
ACTIVE INACTIVE SUSPENDED DEFAULTING UNDER_REVIEW ISO-8601 instant when operations started.
ISO-8601 instant when operations ended.
YYYY-MM-DD start of the earliest opening contract, or null. Ignored by cna-one.
Size/type category of the unit.
Economic group of the unit, or null. cna-one requires the group to have been mirrored first (EconomicGroupCreated).
Operational region, or 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.
People behind the unit. May be empty. Roles are not included; cna-one turns each into a FRANCHISEE employee.
Products the unit is enabled to sell. cna-one keeps only the distinct sub-categories.