17 domain design docs covering architecture, accounts, inventory, rentals, lessons, repairs, POS, payments, batch repairs, delivery, billing, accounting, deployment, licensing, installer, and backend tech architecture. Plus implementation roadmap (doc 18) and personnel management (doc 19). Key design decisions documented: - company/location model (multi-tenant + multi-location) - member entity (renamed from student to support multiple adults) - Stripe vs Global Payments billing ownership differences - User/location/terminal licensing model - Valkey 8 instead of Redis
9.9 KiB
Music Store Management Platform
Domain Design: Billing Date Management
Version 1.0 | Draft
1. Overview
Billing date management covers how recurring subscription charges are scheduled, how customers request date changes, and how the system handles proration when dates shift. This applies to both lesson enrollments and instrument rentals.
Stripe owns the billing schedule via the billing_cycle_anchor on each subscription. The application manages the billing_anchor_day preference in its own database and calls Stripe's API to apply changes. All date changes are fully audited and reversible by creating a new change entry.
2. Billing Anchor Day
The billing anchor day is the day of month a subscription renews and the customer is charged. It is capped at day 28 to avoid issues with February and months shorter than 31 days. Customers requesting the 29th, 30th, or 31st are set to the 28th with a staff note explaining why.
Scenario
Behavior
New enrollment / rental
Default anchor = day of start date, capped at 28
Customer requests change
Staff updates anchor — Stripe prorates, new cycle begins on new day
Consolidating subscriptions
One anchor adjusted to match other — proration applied to adjusted subscription
Splitting consolidated billing
Line item removed, new standalone subscription created on requested day
AIM migration
Anchor set to match existing AIM billing date where known
Paused subscription
Change applied immediately, no proration — takes effect when subscription resumes
3. Schema
3.1 rental and enrollment — additional columns
These four columns are added to both the rental and enrollment tables:
Column
Type
Notes
billing_anchor_day
integer
Day of month (1–28) charge occurs
billing_anchor_changed_at
timestamptz
When anchor was last changed
billing_anchor_changed_by
uuid FK
Employee who made the change
billing_anchor_change_reason
text
Required justification — mandatory field
3.2 billing_anchor_change_log
Append-only audit log of every billing date change. Records are never updated or deleted.
Column
Type
Notes
id
uuid PK
company_id
uuid FK
entity_type
enum
rental | enrollment
entity_id
uuid
rental.id or enrollment.id
account_id
uuid FK
For reporting
stripe_subscription_id
varchar
Stripe subscription affected
previous_anchor_day
integer
Old billing day
new_anchor_day
integer
New billing day
proration_amount
numeric(10,2)
Credit or charge applied — sourced from Stripe response
proration_direction
enum
credit | charge | none
subscription_was_paused
boolean
True if change made while subscription paused
changed_by
uuid FK
Employee who made the change
reason
text
Required justification
stripe_invoice_id
varchar
Proration invoice from Stripe if applicable
bulk_change_id
uuid
Groups entries from a single bulk change operation
created_at
timestamptz
4. Proration Logic
When a billing anchor day changes mid-cycle Stripe calculates the proration automatically. The application captures the result and records it in billing_anchor_change_log. Staff should preview the proration amount before confirming the change.
4.1 Moving to an Earlier Day
Example: Currently billed on the 20th, customer requests the 5th.Today is the 12th. Monthly rate: $50.Customer has paid through the 20th.New cycle: 12th → 5th of next month (24 days).Stripe issues a proration credit for unused days already paid.New cycle begins on the 5th of next month.
4.2 Moving to a Later Day
Example: Currently billed on the 5th, customer requests the 20th.Today is the 12th. Monthly rate: $50.Customer has paid through the 5th.Extended period: 5th → 20th = 15 extra days.Stripe issues a proration charge for the extended period.New cycle begins on the 20th.
4.3 Consolidating Two Subscriptions
Example: Rental A billed on the 5th, Rental B billed on the 20th.Staff consolidates — both will bill on the 5th.Rental B subscription anchor updated to 5th.Proration applied to Rental B for the period shift.Rental B added as line item to Rental A's subscription.Original Rental B standalone subscription cancelled.Both now charge together on the 5th.
4.4 Splitting a Consolidated Subscription
Example: Rental A and Rental B both on the 5th (consolidated).Customer wants Rental B moved to the 20th.Rental B line item removed from consolidated subscription.New standalone subscription created for Rental B, anchor = 20th.Proration applied for Rental B's partial period.Rental A continues unchanged on the 5th.
5. Edge Cases
5.1 Pending Invoice Within 48 Hours of Billing
If a billing date change is requested within 48 hours of the current anchor day Stripe may already be generating the upcoming invoice. The system must handle this explicitly.
-
System checks for pending Stripe invoices before allowing anchor change
-
If pending invoice exists within 48-hour window, staff is shown a warning
-
Staff can choose to: wait until after invoice clears, or proceed and void/recreate the pending invoice
-
Voiding a pending invoice requires manager approval — logged in audit trail
-
Recommended default: block change until after billing date passes, then apply
5.2 Failed Payment Then Date Change
If a payment has failed and the customer calls to request a billing date change, the outstanding balance must be resolved first.
-
System checks for outstanding failed invoices on account before allowing anchor change
-
If failed invoice exists, staff is blocked from changing anchor until resolved
-
Resolution options: collect payment on failed invoice, write off with manager approval, or payment plan
-
Once resolved, anchor change proceeds normally
-
Prevents customers from using date changes to evade failed payment follow-up
5.3 Paused Subscriptions
If a lesson or rental subscription is paused when a date change is requested, the change is applied immediately but no proration is generated since no active billing is occurring.
-
Anchor day updated in database immediately
-
Stripe subscription anchor updated via API
-
proration_direction = 'none' recorded in change log
-
subscription_was_paused = true flagged in log for clarity
-
When subscription resumes it bills on the new anchor day
5.4 Rent-to-Own Equity on Date Change
Equity in a rent-to-own rental is calculated per payment received, not per calendar month. A billing date change shifts when the next payment posts but does not affect total equity earned or the buyout calculation.
-
Equity per payment = monthly_rate × rto_equity_percent — unchanged by date shift
-
rto_equity_accumulated updated after each successful Stripe payment webhook
-
If customer is within one payment of buyout eligibility, staff should be notified at time of date change
-
Buyout amount remains: rto_purchase_price minus rto_equity_accumulated
5.5 Bulk Date Change — Multiple Subscriptions
A parent account with multiple children may have several lesson and rental subscriptions all billing on different dates. The system supports changing all subscriptions on an account to a single anchor day in one operation.
Bulk Change Preview Screen
Before confirming a bulk change, staff is shown a summary screen displaying:
-
Each subscription being changed — entity type, description, current anchor, new anchor
-
Proration amount per subscription — credit or charge
-
Net total proration across all subscriptions — credit or charge to customer
-
Warning if any subscription has a pending invoice or failed payment
-
Confirmation required before any Stripe API calls are made
Bulk Change Execution
-
All changes grouped under a shared bulk_change_id in billing_anchor_change_log
-
Stripe API calls made sequentially — not in parallel to avoid rate limit issues
-
If any Stripe call fails, completed changes are rolled back via Stripe API
-
Staff shown which subscriptions succeeded and which failed if partial failure occurs
-
Full rollback preferred — partial bulk changes create confusion for customer billing
Bulk Change Schema
The bulk_change_id UUID is generated at the start of the operation and written to every billing_anchor_change_log entry created during that operation. This allows the full bulk change to be queried and audited as a single unit.
6. Business Rules
-
Billing anchor day must be between 1 and 28 inclusive — enforced at API and UI level
-
Requests for 29th, 30th, 31st are automatically capped to 28th with staff notification
-
Every anchor change requires a reason — reason field is mandatory
-
Change log record written before Stripe API call — if Stripe fails, log is rolled back
-
Failed payment on account blocks anchor change until resolved
-
Pending invoice within 48 hours triggers warning — staff must acknowledge before proceeding
-
Bulk changes require preview confirmation before execution
-
Bulk changes rolled back fully if any subscription fails
-
Paused subscription date changes produce no proration
-
Rent-to-own equity is unaffected by date changes
-
All change log records are immutable — no updates or deletes permitted
7. API Operations
Endpoint
Description
GET /billing/anchor/preview
Preview proration for proposed anchor change — no changes made
POST /billing/anchor/change
Execute single anchor change for one rental or enrollment
GET /billing/anchor/bulk-preview
Preview proration summary for bulk change across all account subscriptions
POST /billing/anchor/bulk-change
Execute bulk anchor change for all selected subscriptions on account
GET /billing/anchor/history/:entityId
Return full anchor change history for a rental or enrollment
8. Reporting
-
Billing date changes by employee — frequency and reason breakdown
-
Proration credits issued by month — financial impact tracking
-
Proration charges issued by month
-
Accounts with frequent date changes — potential indicator of billing issues
-
Bulk changes log — full history of multi-subscription operations
-
Failed payment + date change attempts — flagged for manager review