Skip to content

Migrate billing and contact_persons from sponsor to sponsorForConference #314

@Starefossen

Description

@Starefossen

Problem

billing and contact_persons currently live on the sponsor document (the global company record). This creates two issues:

  1. Billing details change between conferences. A sponsor may use different billing emails, PO numbers, or references from year to year. Storing billing on the global record means updating it overwrites the previous year's data.
  2. Contact persons rotate. The people handling a sponsorship often change between events. A sponsor's contact for 2025 may not be the same person for 2026, yet the current model only tracks one set of contacts globally.

Both fields are inherently per-engagement data and should live on sponsorForConference alongside the other relationship-specific fields (tier, status, contract value, etc.).

Proposal

Phase 1: Migrate billing to sponsorForConference

This is the clearest win — billing is strictly per-conference data.

Schema changes:

  • Add billing object to sponsorForConference schema (same shape: email, reference, comments)
  • Keep the field on sponsor temporarily for backwards compatibility

Migration:

  • Write a Sanity migration that copies sponsor.billingsponsorForConference.billing for all existing records
  • Only copy if sponsorForConference.billing is not already set

Code changes (surface area):

  • src/lib/sponsor/sanity.ts — GROQ queries (3 occurrences) that project billing{} from sponsor
  • src/lib/sponsor/types.tsSponsorData, SponsorInput, SponsorUpdateInput interfaces
  • src/server/schemas/sponsor.tsBillingInfoSchema / CreateSponsorSchema
  • src/app/(admin)/admin/sponsors/contacts/page.tsx — reads conferenceSponsor.sponsor.billing
  • src/components/admin/sponsor/SponsorTierManagement.tsx — checks sponsor.sponsor.billing
  • src/lib/conference/sanity.ts — GROQ query projecting billing{}
  • src/lib/tickets/api.tsbillingCrm query

Phase 2: Migrate contact_persons to sponsorForConference

More nuanced — contacts have a dual role (global company directory vs. per-conference relationship).

Option A: Move entirely to sponsorForConference

  • Simplest model, each engagement tracks its own contacts
  • Downside: when creating a new conference entry for a returning sponsor, contacts must be copied or re-entered

Option B: Keep on sponsor as a global directory, add per-conference override on sponsorForConference

  • sponsorForConference.contact_persons overrides sponsor.contact_persons when present
  • CRM and email operations read from the conference-level contacts
  • Global contacts serve as defaults / address book for new engagements
  • More complex but preserves the "company directory" use case

Code changes (surface area):

  • src/lib/sponsor/sanity.ts — GROQ queries (3 occurrences) projecting contact_persons[]
  • src/lib/sponsor/types.ts — type definitions
  • src/lib/sponsor/audience.ts — Resend audience sync reads contact_persons
  • src/lib/sponsor/validation.ts — contact person validation
  • src/server/schemas/sponsor.tsContactPersonSchema
  • src/app/(admin)/admin/sponsors/contacts/page.tsx — reads conferenceSponsor.sponsor.contact_persons
  • src/app/(admin)/admin/api/sponsors/email/send/route.ts — reads contacts for sending
  • src/app/(admin)/admin/api/sponsors/email/audience/sync/route.ts — audience sync
  • src/app/(admin)/admin/api/sponsors/email/discount/route.ts — discount code emails
  • src/components/admin/sponsor/SponsorTierManagement.tsx — missing-contacts check

Cleanup

After migration is verified:

  • Remove billing from sponsor schema (or mark deprecated)
  • If Option A for contacts: remove contact_persons from sponsor schema
  • Update docs/SPONSOR_SYSTEM.md data model section

Acceptance Criteria

  • billing lives on sponsorForConference and all reads/writes use it there
  • Decision made on contact_persons approach (Option A vs B)
  • Contact persons accessible per-conference in CRM, email, and contacts pages
  • Sanity migration script for existing data
  • No regressions in sponsor email sending, audience sync, or contacts page
  • Existing conference data preserved during migration

Dependencies

None — can be worked independently. Should be completed before #303 (contract signing) since contract emails will need per-conference contact data.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions