Files
lunarfront-app/planning/02_Domain_Accounts_Customers.md
Ryan Moon 81894a5d23 Update planning docs for processor-agnostic payment linking
Replace stripe_customer_id on account with account_processor_link
table. Update account_payment_method to use processor enum +
processor_payment_method_id instead of Stripe-specific fields.
Supports multiple simultaneous processors for migration scenarios.
2026-03-27 17:45:00 -05:00

5.8 KiB

Music Store Management Platform

Domain Design: Accounts & Customers

1. Overview

The Accounts & Customers domain is the foundational entity model for the platform. All billing, lessons, rentals, repairs, and sales link back to an Account. This domain handles the distinction between the billing entity (Account) and the individual people (Students/Contacts) associated with it.

2. Core Concepts

2.1 Account

An Account is the billing entity. It holds the Stripe customer reference and payment preferences. An account may represent a single adult customer, a parent/guardian paying for children, or a business customer.

  • One Stripe customer ID per account

  • Holds billing preferences (consolidated vs split)

  • Has one or more associated members

  • All invoices and statements roll up to the account level

2.2 Member

A Member is an individual person associated with an account — adults, children, or anyone who takes lessons, rents instruments, or needs to be tracked under the account. Multiple members per account supports family billing (e.g. two parents and three children all on one account). For a single adult managing their own billing, the account holder and member are the same person.

  • Linked to account for billing

  • Has their own lesson enrollments, rental history, and repair history

  • Multiple members per account (family billing — any mix of adults and minors)

  • is_minor flag derived from date_of_birth — controls whether parent/guardian consent and portal access rules apply

  • Each member can have their own email and phone — useful for adult members receiving their own notifications

3. Database Schema

3.1 account

Column

Type

Notes

id

uuid PK

Primary key

company_id

uuid FK

Tenant scoping — identifies which company owns this record

account_number

varchar

Human-readable account ID

name

varchar

Account/business name

email

varchar

Primary contact email

phone

varchar

Primary phone

address

jsonb

Street, city, state, zip

billing_mode

enum

consolidated | split

notes

text

Internal staff notes

legacy_id

varchar

AIM original customer ID

legacy_source

varchar

'aim' for migrated records

migrated_at

timestamptz

When record was imported

created_at

timestamptz

Record creation timestamp

updated_at

timestamptz

Last update timestamp

3.2 member

Column

Type

Notes

id

uuid PK

Primary key

account_id

uuid FK

Billing account

company_id

uuid FK

Tenant scoping — identifies which company owns this record

first_name

varchar

last_name

varchar

date_of_birth

date

Used to derive is_minor status

is_minor

boolean

True if under 18 — derived from date_of_birth, controls consent and portal access rules

email

varchar

Member's own email — each adult can have their own

phone

varchar

Member's contact number

notes

text

Staff notes

legacy_id

varchar

AIM original ID

created_at

timestamptz

Links an account to a payment processor's customer record. This is processor-agnostic — supports Stripe, Global Payments, or any future processor. Replaces the previous stripe_customer_id column that was directly on the account table.

Column

Type

Notes

id

uuid PK

account_id

uuid FK

company_id

uuid FK

Tenant scoping

processor

enum

stripe | global_payments

processor_customer_id

varchar

The processor's customer ID (e.g. cus_xxx for Stripe, GP customer token for Global Payments)

is_active

boolean

Allows deactivating without deleting

created_at

timestamptz

An account may have links to multiple processors simultaneously — this is expected during migration from one processor to another (e.g. AIM legacy → Stripe transition).

3.4 account_payment_method

Tracks payment methods on file. Card data lives in the processor — this table only stores references for display and selection.

Column

Type

Notes

id

uuid PK

account_id

uuid FK

company_id

uuid FK

Tenant scoping

processor

enum

stripe | global_payments

processor_payment_method_id

varchar

Processor's payment method reference (pm_xxx for Stripe, token for GP)

card_brand

varchar

visa, mastercard, etc.

last_four

char(4)

Display only

exp_month

integer

exp_year

integer

is_default

boolean

Default payment method for account

requires_update

boolean

True if migrated from legacy processor and needs re-entry

created_at

timestamptz

4. Business Rules

  • Every member must belong to exactly one account

  • An account must have at least one member (can be the account holder themselves)

  • Billing always targets the account, never the member directly

  • An account can have multiple payment methods but only one default

  • Consolidated billing: all active subscriptions on one account roll to one Stripe customer

  • Split billing: each enrollment/rental can have its own billing date and subscription

  • Account deletion is soft-delete only — financial history must be retained

  • Duplicate account detection on email and phone during creation

5. Key Workflows

5.1 New Account Creation (Walk-in)

  • Staff searches by name/phone/email to check for existing account

  • If none found, creates new account with contact details

  • Adds member record(s) — could be same person, spouse, or children

  • Optionally captures payment method via Stripe Elements

  • Stripe customer created in background, ID stored on account

5.2 Account Lookup at POS

  • Search by account number, name, phone, or email

  • Returns account summary: members, active rentals, active lessons, balance

  • Staff selects account to attach transaction

5.3 AIM Migration

  • AIM customer records imported with legacy_id and legacy_source='aim'

  • Duplicate detection run before import — staff reviews conflicts

  • Payment methods flagged requires_update=true until customer re-enters card in Stripe