Skip to content

Datamodell ​

Kjerneentiteter ​

┌─────────────────┐       ┌─────────────────┐       ┌─────────────────┐
│  Organization   │       │     Event       │       │   TicketType    │
├─────────────────┤       ├─────────────────┤       ├─────────────────┤
│ id              │◄──────│ organizationId  │       │ eventId         │
│ name            │       │ id              │◄──────│ id              │
│ createdAt       │       │ name            │       │ name            │
│ updatedAt       │       │ date            │       │ price           │
└─────────────────┘       │ location        │       │ sortOrder       │
        ▲                 │ venueCapacity   │       └─────────────────┘
        │                 │ active          │
        │                 │ salesLocked     │       ┌─────────────────┐
        │                 │ showtimes[]     │       │   TicketOrder   │
        │                 │ enableQuotas    │       ├─────────────────┤
        │                 └─────────────────┘       │ id              │
        │                         ▲                 │ organizationId  │
        │                         │                 │ eventId         │
┌───────┴──────────────┐          │                 │ showtimeId      │
│OrganizationMembership│          │                 │ userId          │
├──────────────────────┤          │                 │ status (se      │
│ id                   │          │                 │   arbeidsflyt)  │
│ organizationId       │          │                 │ tickets[]       │
│ userId               │          │                 │ solgt/returnert │
│ role (member/        │          └─────────────────│ antall          │
│   ticketManager/     │                            │ totalQuantity   │
│   treasurer/         │                            │ adminNote       │
│   admin)             │                            │ archived        │
│ roles[]              │                            │ createdAt       │
│ invoiced             │                            └─────────────────┘
│ createdAt            │                            ┌─────────────────┐
└──────────────────────┘                            │   Allocation    │
        ▲                                           ├─────────────────┤
        │                                           │ id              │
┌───────┴─────────┐                                 │ organizationId  │
│     User        │                                 │ eventId         │
├─────────────────┤                                 │ showtimeId      │
│ id              │                                 │ userId          │
│ email           │                                 │ items[]         │
│ name            │                                 │ createdAt       │
│ isSuperAdmin    │                                 └─────────────────┘
│ createdAt       │
│ updatedAt       │                                 ┌─────────────────┐
└─────────────────┘                                 │  ExternalSale   │
                                                    ├─────────────────┤
                                                    │ id              │
        ┌─────────────────┐                         │ organizationId  │
        │ImpersonationAudit│                        │ eventId         │
        ├─────────────────┤                         │ showtimeId      │
        │ id              │                         │ ticketTypeId    │
        │ organizationId  │                         │ source          │
        │ adminUserId     │                         │ quantity        │
        │ targetUserId    │                         │ createdAt       │
        │ action          │                         └─────────────────┘
        │ timestamp       │
        │ duration        │
        └─────────────────┘

Relasjoner ​

  • Organization → Event: Én organisasjon har mange arrangementer (organizationId på Event)
  • Event → TicketType: Ett arrangement har mange billetttyper (eventId på TicketType)
  • Event → TicketOrder: Ett arrangement har mange bestillinger (eventId på TicketOrder)
  • Organization → OrganizationMembership: Én organisasjon har mange medlemskap (organizationId på OrganizationMembership)
  • User → OrganizationMembership: Én bruker kan tilhøre mange organisasjoner (userId på OrganizationMembership)
  • User → TicketOrder: Én bruker kan legge inn mange bestillinger (userId på TicketOrder)
  • Event → Allocation: Tildelinger er per bruker per arrangement/forestilling. Tildelingsgrenser håndheves kun når enableQuotas er true på arrangementet.
  • Event → ExternalSale: Eksternt salg spores per arrangement/forestilling
  • User → ImpersonationAudit: Revisjonslogger sporer admin- og målbruker

Bestillingsarbeidsflyt ​

VENTER → GODKJENT → LEVERT → VENTER_PÅ_AVSTEMMING → OPPGJORT
   └──────────────── avvis eller kanseller ───────────────→ KANSELLERT

Det valgfrie archived-feltet og fakturafeltene er uavhengige av statusflyten. Avstemming låser et fakturagrunnlag som bare inneholder salg, men setter aldri fakturastatus. invoicedAt betyr at en faktura er utstedt eller sendt, ikke at den er betalt.

En bestilling kan også nå VENTER_PÅ_AVSTEMMING via en overføring, ikke bare via rapportering: hvis en delvis bestillingsoverføring flytter bestillingens siste utestående billetter, går den reduserte kildebestillingen rett til awaitingReconciliation — samme tilstand den ville nådd hvis de siste billettene var rapportert solgt eller returnert i stedet.

Cosmos DB-containere ​

Følgende containere opprettes av api/scripts/setup-database.ts:

ContainerPartisjonsnøkkelTTLFormål
users/id—Brukerkontoer
organizations/id—Organisasjonsentiteter
organizationMemberships/organizationId—Bruker–organisasjon-relasjoner med roller
events/organizationId—Arrangementer, forestillinger, billetttyper
orders/organizationId—Billettbestillinger (godkjenningsflyt)
allocations/organizationId—Billettildelinger per medlem
registrations/organizationId—Direktesalg-registreringer
externalSales/organizationId—Ticketmaster / billettlukesalg
freeTickets/organizationId—Gratisbilletter
invitations/organizationId—Invitasjonskoder
rateLimits/id✅Hastighetsbegrensning (utløper automatisk)
authTokens/id✅Magic link-tokener (utløper automatisk)
impersonationAudits/organizationId—Imitasjonsøkt-logger
notifications/organizationId✅Varsler i appen (utløper etter ~30 dager)

Kapasitetsmodell ​

  • venueCapacity er grensen for hver forestilling.
  • Solgt antall beregnes per forestilling fra rapporterte bestillingssalg, direkteregistreringer, eksternt salg og fribilletter.
  • Brukt kapasitet bruker de samme kildene, men eksterne linjer bruker sitt uttrykkelige kapasitetsbidrag (eldre rader bruker hele eksternt antall).
  • Gjenstående per forestilling = max(0, venueCapacity − brukt kapasitet).
  • Samlet kapasitet = venueCapacity × antall forestillinger.
  • Samlet gjenstående er summen av gjenstående plasser for hver forestilling. Kapasitet lånes aldri mellom forestillinger.

Viktige feltmerknader ​

  • TicketOrder.createdAt: Tidspunktet bestillingen ble sendt inn. Billettansvarlig-køen bruker feltet for å vise den eldste ventende bestillingen først. Like tidspunkt brytes stabilt med stigende bestillings-ID.
  • TicketOrder.archived: Valgfritt administrativt flagg. Brukergrensesnittet viser Arkivert mens feltet er true, men status beholder livssyklusverdien og styrer fortsatt avstemmingsdata og handlinger.
  • TicketOrder.invoiceBasis: Uforanderlig øyeblikksbilde som opprettes når avstemmingen godkjennes. Linjene inneholder bare validert solgt antall multiplisert med priser fra bestillingstidspunktet. En fullstendig returnert bestilling har et tomt grunnlag med grandTotal: 0; totalAmount brukes aldri som reserve.
  • TicketOrder.invoiceBasisValid: Utledet felt i API-svaret. Det validerer det låste grunnlaget mot salgshistorikk og priser og lagres aldri.
  • TicketOrder.invoicedAt / invoicedBy / invoicedByName: Valgfrie revisjonsfelt for utstedt faktura. Manglende felt betyr alltid ikke fakturert.
  • TicketOrder.archiveOverride: Varig bevis på at en organisasjonsadministrator overstyrte invoicePrerequisite, invalidInvoiceBasis eller begge. Beviset beholdes etter gjenoppretting, mens archivedAt og archivedBy bare beskriver gjeldende arkivstatus.
  • TicketOrder.transferHistory — Valgfri liste med overføringsoppføringer, én per overføring. Hver overføring flytter bare bestillingens utestående (usolgte, ikke-returnerte) antall; bevis for solgt/returnert blir alltid værende på bestillingen det ble rapportert mot. En delvis overføring som tømmer en bestillings siste utestående billetter, setter den bestillingen rett til awaitingReconciliation. Se Delegert salg for feltet delegatedOrigin, som er kun til revisjonsformål.
  • Registration.invoicedAt / invoicedById / invoicedByName: Tidspunkt, stabil aktør-ID og visningsnavn for utstedt faktura. Eldre invoicedBy-verdier forblir visningsnavn og tolkes ikke som ID-er.
  • Event.ticketmasterCustomCategories — Deltakerlokale, arrangementsspesifikke, gjenbrukbare Ticketmaster-kategorier. De er innebygd konfigurasjon og blir aldri offentlige TicketType-dokumenter. Sletting fjerner den valgbare konfigurasjonen og frigjør plass i 50-grensen; uforanderlige salgsøyeblikksbilder bevarer historikken. Utskifting av en delt arrangementsprojeksjon beholder den lokale listen og kopierer den aldri mellom organisasjoner.
  • ExternalSale.capacityQuantity — Eksplisitt kapasitetsbidrag for nye rader. Eldre rader uten feltet bruker fortsatt totalQuantity. Rapportert antall (totalQuantity) og kapasitetsbidrag holdes som separate summer.
  • ExternalSaleTicket.externalCategory — Uforanderlig øyeblikksbilde av original egendefinert/engangsetikett, kategori-ID/-versjon, valgfri koblet billettype og countsTowardCapacity. Forespørsler med en gjenbrukbar kategori må sende nøyaktig valgt versjon. Pris og gebyr kan begge være nøyaktig null.

Valgfri kobling er rapportattribusjon, ikke beholdning. Linjeantallet bidrar én gang i den koblede Ticketmaster-kolonnen for vanlig billettype, arrangementssalget bruker totalQuantity én gang, og kapasiteten bruker capacityQuantity én gang. Korreksjoner med bare samlet antall merker linjefordelingen som utilgjengelig i stedet for å endre eller dikte opp øyeblikksbildeantall.

Fakturaoppsummering per medlem ​

MemberSales.invoiceSummary beskriver alle aktive salgsposter for ett medlem og arrangement:

FeltTypeBeskrivelse
statestringawaitingSettlement, invalidBasis, readyToInvoice, partiallyInvoiced eller invoiced
eligibleCountnumberAktive registreringer pluss oppgjorte bestillinger med gyldig grunnlag
invoicedCountnumberKvalifiserte poster med fakturastatus
unresolvedOrderCountnumberAktive trykte bestillinger som ennå ikke er oppgjort
invalidBasisOrderCountnumberOppgjorte bestillinger med ugyldig låst grunnlag
invoiceAmountnumberRegistreringstotaler pluss gyldig invoiceBasis.grandTotal
allEligibleInvoicedbooleanSann når minst én post er kvalifisert og alle kvalifiserte poster er fakturert. Uavklarte eller ugyldige bestillinger er separate klarhetsblokkeringer og kan beholde state i en varseltilstand.

Delte arrangementer ​

Delt-arrangement-modulen legger til et koordineringslag over per-organisasjonens arrangementsmodell. Den introduserer to nye Cosmos DB-containere og flere nye entitetstyper. Alle eksisterende entiteter og salgsflyter er uendret; modulen er strengt additiv.

Containere ​

ContainerPartisjonsnøkkelTTLFormål
sharedEvents/sharedEventId—Delt-arrangement-rotdokumenter, deltakerliste, revisjonslogg, projeksjons-synkroniseringsjobber
sharedEventCapacityLedger/sharedEventIddefaultTtl: -1 (terminal-docs utløper)Kapasitetstellere, reservasjoner, justeringer, CapacityGuardDoc, CapacityFenceDoc

Begge containere bruker all-path-indeksering med målrettede sammensatte indekser.

Entitetsoversikt ​

┌────────────────────┐      ┌───────────────────────────┐
│    SharedEvent     │      │   SharedEventParticipant  │
├────────────────────┤      ├───────────────────────────┤
│ id (sharedEventId) │◄─────│ sharedEventId             │
│ name               │      │ organizationId            │
│ showtimes[]        │      │ status (invited/accepted/ │
│ ticketTypeTemplates│      │   active/declined/left/   │
│ capacityPolicy     │      │   removed)                │
│ sharedCapacity     │      │ role (organizer/          │
│ organizerOrgId?    │      │   participant)            │
│ status (draft/open/│      │ projectionLink?           │
│  locked/cancelling/│      │ quotaByShowtime?          │
│  cancelled/        │      └───────────────────────────┘
│  completed)        │
│ owners[]           │      ┌───────────────────────────┐
│ grants[]           │      │   SharedEventOwner        │
│ externalChannels[] │      ├───────────────────────────┤
│ schemaVersion: 1   │      │ userId                    │
└────────────────────┘      │ organizationId            │
                            │ addedAt                   │
                            └───────────────────────────┘

┌───────────────────────────┐      ┌────────────────────────────┐
│     SharedEventGrant      │      │   ExternalSalesChannel     │
├───────────────────────────┤      ├────────────────────────────┤
│ userId                    │      │ source (billetto/          │
│ organizationId            │      │   ticketmaster)            │
│ capabilities[]            │      │ mode (automation/manual)   │
│ roleTemplate?             │      │ responsibleOrgId           │
│ issuedAt                  │      │ connectionRef?             │
│ issuedBy                  │      │ status (active/disabled)   │
└───────────────────────────┘      │ assignedBy                 │
                                   │ effectiveFrom              │
                                   └────────────────────────────┘

Kapasitetsentiteter ​

┌────────────────────────────┐      ┌────────────────────────────┐
│      CapacityCounter       │      │     CapacityReservation    │
├────────────────────────────┤      ├────────────────────────────┤
│ id: "pool:{showtimeId}"    │      │ id: "{reservationId}"      │
│   eller                    │      │ sharedEventId              │
│   "quota:{orgId}:{stId}"   │      │ showtimeId                 │
│ sharedEventId              │      │ organizationId             │
│ capacity                   │      │ quantity                   │
│ reserved                   │      │ state (held/committed/     │
│ committed                  │      │   released)                │
│ health (ok/reconciling/    │      │ expiresAt                  │
│   overbooked/retired)      │      │ operationId                │
└────────────────────────────┘      └────────────────────────────┘

┌────────────────────────────┐      ┌────────────────────────────┐
│    CapacityAdjustment      │      │      CapacityFenceDoc      │
├────────────────────────────┤      ├────────────────────────────┤
│ id: "adjustment:           │      │ id: "fence:{sharedEventId}"│
│   {operationId}"           │      │ state (open/cancelling/    │
│ operationId                │      │   cancelled)               │
│ showtimeId                 │      │ (ingen TTL — permanent)    │
│ organizationId             │      └────────────────────────────┘
│ kind:                      │
│   wholeEventCancellation   │
│   administrativeCorrection │
│   externalObservation      │
│   reconciliation           │
│ signedDelta                │
│ originalQty?               │
│ correctedQty?              │
│ actor / authority / reason │
└────────────────────────────┘

Revisjonslogg ​

Revisjonsloggen er append-only. Ingen post redigeres eller slettes noen gang. Det finnes 32 definerte SharedEventAuditAction-verdier som dekker: listeoverganger, eierendringer, tildelingsutstedelse/-tilbakekalling, kapasitetspolicy-endringer, kapasitetsredigeringer, avlysingshendelser, eksternt kanal-endringer, rekonsilierings-reparasjoner og generering, tilbakekalling og aksept av invitasjonslenker.

Invitasjonslenke-entitet ​

┌──────────────────────────────────────────────┐
│       SharedEventInvitationLink              │
├──────────────────────────────────────────────┤
│ id: "invite:{first-16-hex-of-token-hash}"    │
│ documentType: "sharedEventInvitationLink"    │
│ sharedEventId                                │
│ tokenHash (SHA-256 av råtoken — råtoken      │
│   lagres aldri)                              │
│ status ("active" | "used" | "revoked")       │
│ createdBy / createdByOrganizationId          │
│ createdAt / expiresAt                        │
│ usedBy? / usedByOrganizationId? / usedAt?    │
│ revokedAt? / revokedBy?                      │
│ emailDeliveryAttempted (boolean)             │
│ ttl (auto-utløp etter 14 dager + 24t buffer) │
│ schemaVersion: 1                             │
└──────────────────────────────────────────────┘

Invitasjonslenke-dokumenter lagres i sharedEvents-containeren under samme partisjon som det delte arrangementet (/sharedEventId). Dette gjenbruker den eksisterende containeren og partisjonering; ingen ny container er nødvendig.

Nøkkelegenskaper:

  • tokenHash: SHA-256 av råtoken. Råtoken returneres til arrangøren nøyaktig én gang ved opprettelse og lagres, logges eller returneres aldri igjen. Dokument-IDen avledes deterministisk fra hash-prefikset.
  • emailDeliveryAttempted: true dersom e-postsending ble forsøkt. E-postadressen lagres aldri.
  • ttl: Cosmos TTL gjør at dokumentet utløper automatisk (14 dagers lenke-levetid pluss en 24-timers buffer for pågående operasjoner).
  • usedByOrganizationId: Settes atomisk ved aksept. Dersom aksept-sagaen avbrytes etter at invitasjonen er merket used, men før listen oppdateres, fungerer dette feltet som et varig gjenopprettingsmerke for idempotent gjenforsøk.

Aksept bruker ETag-betinget erstatning slik at nøyaktig én samtidige akseptant vinner; alle andre mottar et generisk «lenken er ikke tilgjengelig»-svar.

Varsler om invitasjoner i appen (D16) ​

Når en invitasjonslenke opprettes med en valgfri e-postadresse, utfører plattformen et best-effort asynkront oppslagsbieffekt. Dersom e-posten stemmer overens med en kjent, godkjent organisasjonsadmin, oppretter systemet én varsling i appen per organisasjonskontekst den adminen administrerer. Varslene lagres i den eksisterende notifications-containeren partisjonert etter /organizationId — ingen ny container er nødvendig.

Relevante felt for dette varslingstypen (fullstendig varslingsdokument følger standardskjemaet):

FeltVerdi / formMerknader
type"shared_event_invitation_received"Lagt til i NotificationType-unionen i D16
title"Shared Event Invitation"Lokaliseres i klienten
message"You have been invited to participate in {sharedEventName}."Visningsstreng
actionUrl"/shared-events/pending-invitations"Tokenfrirute, sesjonsgodkjent
meta.sharedEventIdstrengIdentifikator for delt arrangement
meta.sharedEventNamestrengVisningsnavn for delt arrangement
meta.organizerOrganizationNamestrengVisningsnavn for arrangørorganisasjonen
incidentKey"se:invite-notif:{invitationDocId}:{recipientUserId}:{orgId}"Dedup-nøkkel — eksponeres ikke i noe API-svar eller grensesnitt
ttl≤ 15 dager (begrenset av invitasjonens gjenværende levetid)Dynamisk TTL spesifikk for denne varslingstypen; beregnet fra invitasjonens gjenværende levetid, ikke den generelle TTL-policyen for notifications-containeren

Personverninvarianter:

  • Varslingsdokumentet inneholder aldri mottakerens e-postadresse, råtoken, token-hash eller invitasjonsdokument-ID.
  • Arrangøren kan ikke avgjøre fra noe API-svar, grensesnittilstand eller tilgjengelig endepunkt om en match ble funnet, en varsling ble opprettet, lest eller reagert på.

Dedup- og fan-ut-regler:

  • Dedup per {invitationDocId}:{recipientUserId}:{organizationId} — et samtid eller gjentatt fan-ut-forsøk for den samme tripelen er idempotent (Cosmos 409 behandles som suksess).
  • Fan-ut er begrenset til 10 organisasjonspartisjoner per invitasjon.
  • Oppslaget og fan-uten er fire-and-forget: enhver feil påvirker ikke invitasjonslenke-opprettelsessvaret.

TTL-justering:

  • Invitasjons-TTL: 14 dager (pluss 24-timers buffer). Varsler bruker en dynamisk TTL begrenset til 15 dager, beregnet fra invitasjonens gjenværende levetid.
  • En varsling kan overleve invitasjonens utløp. Utdaterte varsler fører til en tom side for ventende invitasjoner, ikke en feil.

API-ruter for ventende invitasjoner:

MetodeRuteTilgangBeskrivelse
GET/api/shared-event-invitations/pendingPåkrevd (sesjon)Returnerer aktive invitasjonslenker for delte arrangementer der den autentiserte brukeren er admin i minst én berettiget organisasjon. Ingen invitasjonstoken kreves. Organisasjonsuavhengig (ingen ?organizationId=-parameter); berettigede admin-organisasjoner utledes fra den autentiserte sesjonen. Returnerer et begrenset sett med opptil 50 resultater (usortert).
POST/api/shared-event-invitations/pending/acceptPåkrevd (sesjon)Aksepterer en invitasjon via inn-app-stien. Bruker samme ETag-betingede lineariseringspunkt som tokenbasert aksept — nøyaktig én samtid akseptant vinner.

Forespørselskropp for POST /api/shared-event-invitations/pending/accept:

FeltTypeBeskrivelse
invitationIdstrengInvitasjonsdokument-ID (fra listen over ventende invitasjoner)
sharedEventIdstrengDelt-arrangement-ID (partisjonsnøkkel for punktoppslag)
organizationIdstrengOrganisasjons-ID som aksepteres for (brukervalgt)

Bakenden verifiserer at invitasjonens lagrede recipientUserId samsvarer med den autentiserte sesjonbrukeren før domenesjekker utføres. Et avvik returnerer det samme generiske «lenken er ikke tilgjengelig»-svaret som alle andre feilstier.

Lokal arrangementsprojeksjonslenke ​

Når en deltaker aksepterer, opprettes en lokal arrangementsprojeksjon fra de delte malene og kobles til det delte arrangementet via en SharedEventProjectionLink innebygd i arrangementsdokumentet:

FeltTypeBeskrivelse
sharedEventIdstringDet delte arrangementet denne projeksjonen tilhører
roleorganizer | participantDenne organisasjonens rolle
capacityPolicyquota | poolArvet fra delt arrangement
participantStatusParticipantStatusGjeldende listestatus

Et frittstående arrangement (uten delt-arrangement-lenke) er uendret og fortsetter å oppføre seg som før.

Relasjoner ​

  • SharedEvent → SharedEventParticipant: Én delt arrangement har 2..N deltakere (én per organisasjon).
  • SharedEvent → SharedEventOwner: Én delt arrangement har 1..N navngitte eiere.
  • SharedEvent → SharedEventGrant: Én delt arrangement har 0..N navngitte brukertildelinger.
  • SharedEvent → ExternalSalesChannel: Én delt arrangement har 0..N kanaltildelinger (én per kilde).
  • SharedEvent → SharedEventAuditEntry: Append-only-logg; ubegrenset, paginert.
  • SharedEventParticipant → Event (projeksjon): Én deltaker har maks én lokal arrangementsprojeksjon per delt arrangement.
  • sharedEventCapacityLedger → CapacityCounter: Én teller per (modus, org, forestilling)-kombinasjon.
  • sharedEventCapacityLedger → CapacityReservation: Én reservasjon per aktiv kasse.
  • sharedEventCapacityLedger → CapacityFenceDoc: Én permanent sperring per delt arrangement (opprettes ved første reservasjon eller avlysing).

Tellernøkkelskjema ​

ModusTellernøkkelformatHåndhevingsomfang
Poolpool:{showtimeId}Alle organisasjoner deler én teller per forestilling
Kvotequota:{organizationId}:{showtimeId}Én teller per org per forestilling

Hva som er utsatt ​

Følgende ekstern-kanal-integrasjonsarbeid er ikke implementert i gjeldende modul og er utsatt til #242:

  • Billetto OAuth-legitimasjonsløser og token-livssyklus.
  • Billetto webhook-mottak og deltaker-/refusjons-/avlysingssynkronisering.
  • Leverandørens runtime-operasjoner (avlysingshendelser, refusjonsregistreringer, deltakerlisteer).

connectionRef-feltet på ExternalSalesChannel lagrer kun en Azure Key Vault-hemmelighets-URI-referanse. Ingen rå legitimasjon lagres i Cosmos.


Organisasjonens kapabilitetsmodus ​

Organization.organizationMode er valgfritt:

Lagret verdiEffektiv modusBetydning
FraværendefullFullstendige produktmuligheter; dette er den kanoniske representasjonen av full
"sellingPartner"sellingPartnerBegrenset organisasjon som driver delegert salg, men ikke eier arrangementer eller deltar i delte arrangementer

Modusen gjenbruker det eksisterende Organization-dokumentet, medlemskap, roller, innstillinger, varsler og den omvendte indeksen delegatedEventSales[]. Den innfører ikke en separat organisasjonstype eller container.

Koordinering av modusendring ligger også på organisasjonsdokumentet:

FeltTypeBeskrivelse
organizationModeTransitionOrganizationModeTransition?Én pågående nedgraderingsmarkør med id, targetMode, startedAt, expiresAt og requestedBy
ownerStateLeasesOrganizationOwnerStateLease[]?Kortvarige skrivelåser med id, eierkapabilitet, startedAt og expiresAt
_etagstringPåkrevd betingelse for nøyaktig gjenoppretting av en foreldet operasjon

En nedgradering fra full til sellingPartner oppretter først overgangsmarkøren, avviser nye låser for eiertilstand, kontrollerer eide arrangementer, deltakelse i delte arrangementer og utgående aktive eller inviterte delegeringer, og fullfører deretter med ETag-betinget erstatning. Skrivere av eiertilstand tar en lås før de oppretter inkompatibel tilstand. Utløp gjør operasjonen kvalifisert for operatørkontroll; det fjerner ikke operasjonen automatisk.

Uttrykkelig gjenoppretting fjerner bare en nøyaktig foreldet overgang eller lås der type, ID og ETag fortsatt matcher. Gjenoppretting endrer ikke modusen og hopper ikke over avhengighetssjekker. Operatøren kjører nedgraderingen på nytt etterpå.

Koordinering på tvers av dokumenter

Modusoverganger og synkronisering av delegeringens omvendte indeks bruker autoritative lesinger, ETag-betingelser, låser, nye forsøk og reparasjon på tvers av eksisterende dokumenter. De er ikke transaksjoner på tvers av containere og hevder ikke slik atomisitet.

Delegert salg (salgspartnere) ​

Modulen for delegert salg lar en arrangementseier gi en annen organisasjon en avgrenset, selvbetjent salgsmulighet for et vanlig (ikke delt) arrangement, uten noe medeierskap eller delte styringsrettigheter. EventSalesDelegation ligger på eierens Event-dokument og er den eneste autorisasjonskilden; DelegatedEventSalesRef er en denormalisert, etter hvert konsistent omvendt-indeks-projeksjon på partnerens eget Organization-dokument.

EventSalesDelegation ​

Ligger i Event.salesDelegations[] på eierens arrangementsdokument:

FeltTypeBeskrivelse
idstringUUID som identifiserer denne delegeringen
partnerOrganizationIdstringDen inviterte partnerorganisasjonen
partnerOrganizationNamestringHydrert øyeblikksbilde; synkroniseres ikke på nytt ved partnerens navnebytte
statusEventSalesDelegationStatus"invited" | "active" | "declined" | "revoked"
invitedBy / invitedByNamestringEieradministratoren som sendte invitasjonen
invitedAtstringISO-tidspunkt
invitationTokenHashstring?SHA-256-hash av engangs-invitasjonstoken; til stede kun mens status === "invited"
invitationExpiresAtstring?ISO-tidspunkt; 14 dagers levetid
respondedBy / respondedByName / respondedAtstring?Partneradministratoren som godtok eller avslo
revokedBy / revokedByName / revokedAtstring?Eieradministratoren som tilbakekalte, og når
revokedReasonstring?Obligatorisk fritekstbegrunnelse ved tilbakekalling
permissionsEventSalesDelegationPermissionsFast, ikke-konfigurerbar rettighetspakke (se under)
assignmentVersionnumberOptimistisk samtidighetsteller
orderSubmissionMode"directDelivered" | "ownerApproval"Reservert skjemaplass kun — ikke implementert; fravær betyr alltid "directDelivered". Dette feltet styrer ikke v1.2.0-flyten for medlemmenes selvbetjente ventende/godkjente bestillinger beskrevet under — den flyten styres utelukkende av canApproveOwnMemberOrders og den vanlige livssyklusen til TicketOrder.status, ikke av denne reserverte plassen.

Kvalifisering, ikke godkjenning

Enhver organisasjon som finnes og ikke er slettet (og ikke er den inviterende organisasjonen selv), er kvalifisert til å bli invitert — det finnes ingen egen verifiserings- eller godkjenningsprosess. Hvordan målorganisasjonen identifiseres er rollestyrt (en administrators e-postadresse for vanlige admins, et organisasjonsnavnsøk kun for superadmins) — se API-referanse → Delegert salg for forespørselskontrakten. Selve kvalifiseringsregelen er uendret; kun inndata for oppslag er forskjellig.

EventSalesDelegationPermissions ​

Fast for v1 — enhver aktiv delegering gir nøyaktig denne pakken, og ikke noe annet. Den kan ikke konfigureres per delegering eller per organisasjon:

FeltTypeBeskrivelse
canCreateSalestruePartneren kan opprette delegerte bestillinger for sine egne godkjente medlemmer
canReportSoldReturnedtruePartneren kan rapportere solgt/returnert antall på sine egne delegerte bestillinger
canViewOwnAttributedReportstruePartneren kan se sine egne attribuerte salg, aldri eierens fullstendige rapporter
canApproveOwnMemberOrderstrue?Lagt til i v1.2.0 (#357 fase 4, D1). Partnerorganisasjonens billettansvarlig+ kan godkjenne, levere, godkjenne-og-levere, og kansellere-mens-ventende for egne medlemmers selvbetjente bestillinger. Valgfri i typen kun for bakoverkompatibilitet: en delegeringspost skrevet før v1.2.0 lagrer ikke dette feltet. Fravær på en ellers aktiv delegering leses som gitt — ingen migrering eller etterfylling kreves for at eksisterende delegeringer skal få denne muligheten.

DelegatedEventSalesRef ​

Ligger i Organization.delegatedEventSales[] på partnerens organisasjonsdokument:

FeltTypeBeskrivelse
delegationIdstringMatcher eierens EventSalesDelegation.id
eventIdstringDet delegerte arrangementet
ownerOrganizationIdstringEierens organisasjons-ID (brukes til å slå opp partisjonsnøkkel uten en søk på tvers av partisjoner)
ownerOrganizationNamestringØyeblikksbilde for visning
statusEventSalesDelegationStatusDenormalisert kopi
transitionVersionnumber?Overgangsversjonen projeksjonen ble laget fra; eldre overganger kan ikke overskrive nyere

Attribusjonsfelt for delegert salg på TicketOrder ​

Disse valgfrie feltene er fraværende på en eiers egne direkte salg (standard) og satt på en bestilling en partner oppretter via delegering:

FeltTypeBeskrivelse
soldByOrganizationIdstring?Levende myndighet. Den handlende partnerorganisasjonen. Gir partneren løpende rett til å rapportere solgt/returnert på denne bestillingen.
soldByOrganizationNamestring?Servergyldig visningsnavn. Navngir en organisasjon, ikke en person — sensureres aldri bort for vanlige eiermedlemmer.
soldByUserId / soldByUserNamestring?Partnerens ansatte som utførte handlingen. Navngir en person hos partneren — sensureres bort for vanlige eiermedlemmer.
delegationIdstring?Tilbakereferanse til EventSalesDelegation.id
buyerUserId / buyerUserName / buyerUserEmailstring?Partnerorganisasjonens medlem billetten gjelder for. Sensureres bort for vanlige eiermedlemmer; synlig for billettansvarlig/kasserer/admin hos eieren.
delegatedOriginDelegatedOriginAudit?Settes kun av en overføring som flytter dette lagerbeholdningen til et eiermedlem. Kun til revisjonsformål; gir ingen myndighet.

Myndighet versus revisjon

soldByOrganizationId, soldByUserId, soldByUserName og delegationId er levende salgs-/skrivemyndighet — de må aldri stemples på lagerbeholdning som er overført til et eiermedlem. En overføring fjerner disse fire feltene fra mottakerbestillingen og registrerer i stedet det historiske faktumet i delegatedOrigin (kun revisjon, leses av ingenting som gir tilgang).

Personvern- og revisjonsgrense ​

api/src/shared/order-read-projection.ts håndhever en sperreliste på hvert bestillingssvar:

  • Alltid sensurert for vanlige eiermedlemmer: buyerUserId, buyerUserName, buyerUserEmail, soldByUserId, soldByUserName, delegatedOrigin — dette er partnerens (eller den delegerte kjøperens) personlige identitet.
  • Aldri sensurert: soldByOrganizationId, soldByOrganizationName, delegationId — disse navngir en organisasjon, ikke en person, så et vanlig eiermedlem ser «Solgt av {organisasjon}»-attribusjon.
  • Full synlighet: Rollene billettansvarlig, kasserer og admin hos eierorganisasjonen får alltid den usensurerte nyttelasten.
  • Selv-unntak: En leser som selv er kjøper-av-rekord på sin egen bestilling, beholder sine egne identitetsfelt uansett rolle.

Ingen persondata om partnerens ansatte eller den delegerte kjøperen eksponeres heller for motparten hos partneren — den partnervendte bestillingsprojeksjonen fra de delegerte endepunktene er en egen, minimal tillatelsesliste som aldri inneholder eierens interne identifikatorer.

Projeksjon for partnerens salgssammendrag ​

DelegatedPartnerSalesSummary er en responsprojeksjon, ikke et lagret dokument. Serveren bygger den fra autoritative bestillinger i eierens ticketOrders-partisjon etter å ha løst gjeldende aktive delegering:

FeltBeskrivelse
totalsAntall bestillinger, bestilt, solgt, returnert, utestående, grossSoldRevenue og valuta
byTicketType / byShowtimeSamlet antall og bruttoinntekt basert på prisbevis fra bestillingstidspunktet
byMemberRader for nåværende godkjente medlemmer med operativ bestillingsdetalj; identiteter som ikke kan bevises, samles i én rad uten attribusjon
lifecycleAntall for ventende, godkjent, levert, venter på avstemming, oppgjort og kansellert
scopeMaskinlesbar angivelse av partnerpublikum, bestillinger under gjeldende delegering og utelatte data
settlementAngir partnerOrganizationOffPlatform og netSettlementAvailable: false

Projeksjonen utelater eierens direktesalg, andre partnere, eksternt salg, eierens kapasitet, kontaktopplysninger, notater, fakturastatus, arrangørkontroller, Cosmos-metadata og fortsettelsestokener. Kansellerte bestillinger inngår i livssyklustallene, men bidrar ikke til solgt, returnert, utestående eller bruttoinntekt.

Rapporteringskanal og oppgjørsansvar ​

En delegert bestilling er aldri et enkelt eiermedlems salg. Eieren beholder ansvaret for eventuelt oppgjør med partnerorganisasjonen utenfor plattformen i v1 (#307 beslutning 4; #357 D1 beholder avstemmings- og oppgjørsansvaret hos eieren). Plattformen lagrer ingen kompensasjonsvilkår, beregner ikke nettooppgjør, utsteder ikke partnerfaktura og sporer ikke oppgjørsbetaling. Alle eierens rapporteringsflater plasserer derfor bestillingen i partnerkanalen:

FlateDelegert bestilling
DashboardStats.salesByMemberUtelatt
DashboardStats.salesByPartnerOrganizationGruppert på soldByOrganizationId
ShowtimeSales.sold (medlemskanal)Utelatt — telles i ShowtimeSales.partnerSold
ShowtimeSales.totalSold / remainingInkludert (en solgt billett opptar en plass uansett kanal)
GET /management/members/{userId}/sales/{eventId}Utelatt
PUT /management/members/{userId}/invoice/{eventId}Avvist — det utstedes aldri medlemsfaktura for partnersolgte billetter
Arkiveringskrav for oppgjort bestillingIngen medlemsfakturaforutsetning (getSettledArchiveBlockers utelater invoicePrerequisite); kravet om gyldig fakturagrunnlag gjelder fortsatt
CSV-/Excel-eksportEgen SoldBy-kolonne / eget «Per partnerorganisasjon»-avsnitt

Fordi eierens medlemsfakturering avviser disse bestillingene og det ennå ikke finnes fakturamerking på organisasjonsnivå, forblir invoicedAt permanent usatt på en oppgjort delegert bestilling. Eierflatene sier derfor at bestillingen gjøres opp med salgspartneren, i stedet for å påstå at en medlemsfaktura mangler.

Av samme grunn har en oppgjort delegert bestilling ingen medlemsfakturaforutsetning ved arkivering: invoicedAt kunne aldri settes av noen rute eieren har tilgang til, så kravet tvang hver partnersolgte bestilling inn i administratoroverstyring for alltid og utelukket den fra masse-arkivering. En slik bestilling arkiveres normalt så snart det frosne fakturagrunnlaget er gyldig. invalidInvoiceBasis-sperren er uendret for begge kanaler — den handler om at registrerte solgte antall og beløp er korrekte, uavhengig av hvem som solgte billetten.


Neste: API-referanse · Se også: Arkitektur

Built with VitePress