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 (
organizationIdpå Event) - Event → TicketType: Ett arrangement har mange billetttyper (
eventIdpå TicketType) - Event → TicketOrder: Ett arrangement har mange bestillinger (
eventIdpå TicketOrder) - Organization → OrganizationMembership: Én organisasjon har mange medlemskap (
organizationIdpå OrganizationMembership) - User → OrganizationMembership: Én bruker kan tilhøre mange organisasjoner (
userIdpå OrganizationMembership) - User → TicketOrder: Én bruker kan legge inn mange bestillinger (
userIdpå TicketOrder) - Event → Allocation: Tildelinger er per bruker per arrangement/forestilling. Tildelingsgrenser håndheves kun når
enableQuotasertruepå 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 ───────────────→ KANSELLERTDet 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:
| Container | Partisjonsnøkkel | TTL | Formå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
venueCapacityer grensen for hver forestilling.- Solgt antall beregnes per forestilling fra rapporterte bestillingssalg, direkteregistreringer, eksternt salg og fribilletter.
- Gjenstående per forestilling =
max(0, venueCapacity − solgt antall). - 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 ertrue, menstatusbeholder 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 medgrandTotal: 0;totalAmountbrukes 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 overstyrteinvoicePrerequisite,invalidInvoiceBasiseller begge. Beviset beholdes etter gjenoppretting, mensarchivedAtogarchivedBybare 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 tilawaitingReconciliation. Se Delegert salg for feltetdelegatedOrigin, som er kun til revisjonsformål.Registration.invoicedAt/invoicedById/invoicedByName: Tidspunkt, stabil aktør-ID og visningsnavn for utstedt faktura. EldreinvoicedBy-verdier forblir visningsnavn og tolkes ikke som ID-er.
Fakturaoppsummering per medlem
MemberSales.invoiceSummary beskriver alle aktive salgsposter for ett medlem og arrangement:
| Felt | Type | Beskrivelse |
|---|---|---|
state | string | awaitingSettlement, invalidBasis, readyToInvoice, partiallyInvoiced eller invoiced |
eligibleCount | number | Aktive registreringer pluss oppgjorte bestillinger med gyldig grunnlag |
invoicedCount | number | Kvalifiserte poster med fakturastatus |
unresolvedOrderCount | number | Aktive trykte bestillinger som ennå ikke er oppgjort |
invalidBasisOrderCount | number | Oppgjorte bestillinger med ugyldig låst grunnlag |
invoiceAmount | number | Registreringstotaler pluss gyldig invoiceBasis.grandTotal |
allEligibleInvoiced | boolean | Sann 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
| Container | Partisjonsnøkkel | TTL | Formål |
|---|---|---|---|
sharedEvents | /sharedEventId | — | Delt-arrangement-rotdokumenter, deltakerliste, revisjonslogg, projeksjons-synkroniseringsjobber |
sharedEventCapacityLedger | /sharedEventId | defaultTtl: -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:truedersom 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 merketused, 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):
| Felt | Verdi / form | Merknader |
|---|---|---|
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.sharedEventId | streng | Identifikator for delt arrangement |
meta.sharedEventName | streng | Visningsnavn for delt arrangement |
meta.organizerOrganizationName | streng | Visningsnavn 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:
| Metode | Rute | Tilgang | Beskrivelse |
|---|---|---|---|
GET | /api/shared-event-invitations/pending | På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/accept | På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:
| Felt | Type | Beskrivelse |
|---|---|---|
invitationId | streng | Invitasjonsdokument-ID (fra listen over ventende invitasjoner) |
sharedEventId | streng | Delt-arrangement-ID (partisjonsnøkkel for punktoppslag) |
organizationId | streng | Organisasjons-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:
| Felt | Type | Beskrivelse |
|---|---|---|
sharedEventId | string | Det delte arrangementet denne projeksjonen tilhører |
role | organizer | participant | Denne organisasjonens rolle |
capacityPolicy | quota | pool | Arvet fra delt arrangement |
participantStatus | ParticipantStatus | Gjeldende 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
| Modus | Tellernøkkelformat | Håndhevingsomfang |
|---|---|---|
| Pool | pool:{showtimeId} | Alle organisasjoner deler én teller per forestilling |
| Kvote | quota:{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.
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:
| Felt | Type | Beskrivelse |
|---|---|---|
id | string | UUID som identifiserer denne delegeringen |
partnerOrganizationId | string | Den inviterte partnerorganisasjonen |
partnerOrganizationName | string | Hydrert øyeblikksbilde; synkroniseres ikke på nytt ved partnerens navnebytte |
status | EventSalesDelegationStatus | "invited" | "active" | "declined" | "revoked" |
invitedBy / invitedByName | string | Eieradministratoren som sendte invitasjonen |
invitedAt | string | ISO-tidspunkt |
invitationTokenHash | string? | SHA-256-hash av engangs-invitasjonstoken; til stede kun mens status === "invited" |
invitationExpiresAt | string? | ISO-tidspunkt; 14 dagers levetid |
respondedBy / respondedByName / respondedAt | string? | Partneradministratoren som godtok eller avslo |
revokedBy / revokedByName / revokedAt | string? | Eieradministratoren som tilbakekalte, og når |
revokedReason | string? | Obligatorisk fritekstbegrunnelse ved tilbakekalling |
permissions | EventSalesDelegationPermissions | Fast, ikke-konfigurerbar rettighetspakke (se under) |
assignmentVersion | number | Optimistisk 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:
| Felt | Type | Beskrivelse |
|---|---|---|
canCreateSales | true | Partneren kan opprette delegerte bestillinger for sine egne godkjente medlemmer |
canReportSoldReturned | true | Partneren kan rapportere solgt/returnert antall på sine egne delegerte bestillinger |
canViewOwnAttributedReports | true | Partneren kan se sine egne attribuerte salg, aldri eierens fullstendige rapporter |
canApproveOwnMemberOrders | true? | 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:
| Felt | Type | Beskrivelse |
|---|---|---|
delegationId | string | Matcher eierens EventSalesDelegation.id |
eventId | string | Det delegerte arrangementet |
ownerOrganizationId | string | Eierens organisasjons-ID (brukes til å slå opp partisjonsnøkkel uten en søk på tvers av partisjoner) |
ownerOrganizationName | string | Øyeblikksbilde for visning |
status | EventSalesDelegationStatus | Denormalisert kopi |
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:
| Felt | Type | Beskrivelse |
|---|---|---|
soldByOrganizationId | string? | Levende myndighet. Den handlende partnerorganisasjonen. Gir partneren løpende rett til å rapportere solgt/returnert på denne bestillingen. |
soldByOrganizationName | string? | Servergyldig visningsnavn. Navngir en organisasjon, ikke en person — sensureres aldri bort for vanlige eiermedlemmer. |
soldByUserId / soldByUserName | string? | Partnerens ansatte som utførte handlingen. Navngir en person hos partneren — sensureres bort for vanlige eiermedlemmer. |
delegationId | string? | Tilbakereferanse til EventSalesDelegation.id |
buyerUserId / buyerUserName / buyerUserEmail | string? | Partnerorganisasjonens medlem billetten gjelder for. Sensureres bort for vanlige eiermedlemmer; synlig for billettansvarlig/kasserer/admin hos eieren. |
delegatedOrigin | DelegatedOriginAudit? | 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.
Rapporteringskanal og oppgjørsansvar
En delegert bestilling er aldri et enkelt eiermedlems salg. Eieren gjør den opp med partnerorganisasjonen, utenfor plattformen i v1 (#307 beslutning 4; #357 D1 beholder avstemming, fakturering og oppgjør utelukkende hos eieren). Alle eierens rapporteringsflater plasserer den derfor i partnerkanalen:
| Flate | Delegert bestilling |
|---|---|
DashboardStats.salesByMember | Utelatt |
DashboardStats.salesByPartnerOrganization | Gruppert på soldByOrganizationId |
ShowtimeSales.sold (medlemskanal) | Utelatt — telles i ShowtimeSales.partnerSold |
ShowtimeSales.totalSold / remaining | Inkludert (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 bestilling | Ingen medlemsfakturaforutsetning (getSettledArchiveBlockers utelater invoicePrerequisite); kravet om gyldig fakturagrunnlag gjelder fortsatt |
| CSV-/Excel-eksport | Egen 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