Commercial Operations Phased Spec
This is the executable spec sheet for commercial operations in apps/admin.
Use this document to implement, review, and ship the commercialization program end to end.
Related strategy doc:
1. Purpose
Build a reliable commercial operating system that supports:
- safe auto-dispatch of eligible leads
- delivery-based billing (not sales-outcome billing)
- manual Stripe-first invoicing at the current stage
- auditable reconciliation between delivery events and invoices
2. Scope And Constraints
In scope
- Delivery ledger and finance-ready references.
- Monthly invoice workflow with manual Stripe issuance.
- Intake-triggered auto-dispatch with exception handling.
- Operator UI for drafts, issued invoices, and reconciliation status.
- Guardrails, tests, and rollout controls.
Out of scope (for this spec)
- Full Stripe API automation for invoice creation and payment sync.
- Outcome-based pricing or pay-on-sale contracts.
- Installer self-service portal.
- Advanced pricing optimization models.
3. Canonical Commercial Rules
- Billable event is assignment transition to
sent. - Cycle-cap usage defaults from that same delivery event but remains independently releasable.
- Exclusive product maps to lead path
recommended_installerand 1 installer assignment. - Shared product maps to lead path
compare_quotesand up to 3 installer assignments. - Unit price is snapped at delivery event creation and does not mutate retroactively.
- Credits are only for objective operational defects, not conversion outcomes.
4. Source Of Truth Boundaries
- App ledger is source of truth for delivery eligibility and amounts.
- Stripe is source of truth for invoice issuance and payment collection artifacts.
- App stores Stripe references for reconciliation and audit:
stripeCustomerIdstripeInvoiceId- optional invoice URL
- optional payment external reference
5. Data Model Spec
Target file:
apps/admin/convex/schema.ts
5.1 New table: installerPricingPlans
Fields:
installerId: Id<"installers">effectiveFrom: numbereffectiveTo?: numberexclusivePriceCents: numbersharedPriceCents: numbercurrency: "AUD"(literal for now)createdAt: numberlastUpdatedAt: number
Indexes:
by_installer_effective_fromoninstallerId, effectiveFromby_effective_windowoneffectiveFrom, effectiveTo
5.2 New table: deliveryEvents
Fields:
assignmentId: Id<"routingAssignments">leadId: Id<"leads">installerId: Id<"installers">deliveredAt: numberproductType: "exclusive" | "shared"unitPriceCents: numbercurrency: "AUD"eventStatus: "billable" | "credited" | "void"creditReasonCode?: stringinvoiceId?: Id<"invoices">stripeInvoiceId?: stringcreatedAt: numberlastUpdatedAt: number
Indexes:
by_assignmentonassignmentId(enforce idempotency in code)by_installer_delivered_atoninstallerId, deliveredAtby_status_delivered_atoneventStatus, deliveredAtby_invoiceoninvoiceId
5.3 New table: invoices
Fields:
installerId: Id<"installers">periodStart: numberperiodEnd: numberstatus: "draft" | "issued" | "paid" | "partially_paid" | "void"subtotalCents: numbercreditsCents: numbertotalCents: numberbalanceCents: numbercurrency: "AUD"issuedAt?: numberdueAt?: numberpaidAt?: numberstripeInvoiceId?: stringstripeInvoiceUrl?: stringnotes?: stringcreatedAt: numberlastUpdatedAt: number
Indexes:
by_installer_periodoninstallerId, periodStartby_status_periodonstatus, periodStartby_stripe_invoice_idonstripeInvoiceId
5.4 New table: invoiceLineItems
Fields:
invoiceId: Id<"invoices">deliveryEventId: Id<"deliveryEvents">description: stringquantity: numberunitPriceCents: numberlineTotalCents: numbercreatedAt: number
Indexes:
by_invoiceoninvoiceIdby_delivery_eventondeliveryEventId
5.5 New table: invoiceAdjustments
Fields:
invoiceId: Id<"invoices">type: "credit" | "debit"reasonCode: stringamountCents: numbernotes?: stringactorUserId: Id<"users">actorEmail: stringcreatedAt: number
Indexes:
by_invoice_created_atoninvoiceId, createdAt
5.6 New table: payments
Fields:
invoiceId: Id<"invoices">amountCents: numberreceivedAt: numbermethod: stringreference?: stringexternalPaymentRef?: stringcreatedAt: number
Indexes:
by_invoice_received_atoninvoiceId, receivedAt
6. Backend Function Spec
Primary target modules:
apps/admin/convex/routing.tsapps/admin/convex/leads.tsapps/admin/convex/http.ts- new
apps/admin/convex/billing.ts - optional
apps/admin/convex/internal/billingInternal.ts
6.1 Delivery event emission
Trigger point:
- Assignment status transition to
sent.
Rules:
- One delivery event per assignment (
by_assignmentuniqueness guard in mutation logic). - Determine product type from assignment
selectedPath. - Resolve active pricing plan by installer and delivery timestamp.
- Snapshot
unitPriceCentsandcurrency.
6.2 Auto-dispatch internal entrypoint
Implement an internal mutation callable from intake flow without interactive admin session.
Rules:
- Reuse same eligibility and capacity logic as existing routing behavior.
- If dispatch fails, write explicit failure record and notify ops.
- Do not silently swallow dispatch exceptions.
6.3 Intake-trigger dispatch wiring
After successful lead insert in apps/admin/convex/leads.ts:
- attempt auto-dispatch when policy allows
- otherwise push lead to manual exception queue path
6.4 Billing operations API
Public/admin functions:
previewInvoicePeriod({ periodStart, periodEnd, installerId? })generateInvoiceDrafts({ periodStart, periodEnd, installerIds? })markInvoiceIssued({ invoiceId, stripeInvoiceId, stripeInvoiceUrl?, dueAt })recordInvoicePayment({ invoiceId, amountCents, receivedAt, method, reference? })addInvoiceAdjustment({ invoiceId, type, reasonCode, amountCents, notes? })listInvoices({ status?, installerId?, periodStart?, periodEnd? })listDeliveryEvents({ installerId?, status?, periodStart?, periodEnd? })exportInstallerInvoicePayload({ invoiceId })
7. Admin UI Spec
7.1 New surfaces
- Billing workspace route (suggested
/billingunder admin app). - Invoice draft list and issue action.
- Issued invoice detail with Stripe reference fields.
- Delivery events table with finance status, cap status, filters, and status chips.
7.2 Existing surface updates
- Installer detail: pricing plan management.
- Lead detail / assignment history: billable event status, cap status, and linked invoice reference.
- Dashboard cards: cap used this period, invoiced amount, outstanding balance.
7.3 Required UI states
- empty state
- loading state
- partial failure state for bulk invoice generation
- validation error state for missing Stripe reference on issue
8. Phased Delivery Plan
Phase 0: Manual Stripe Operating Baseline
Objective:
- Start billing operations without Stripe API automation.
Deliverables:
- Delivery events table + writer.
- Draft invoice generation in app.
- Export payload for manual Stripe invoice creation.
- Capture
stripeInvoiceIdback into app.
Acceptance criteria:
- Month-end can run with no spreadsheets required for event aggregation.
- Every issued invoice has app record and Stripe reference.
- At least one dry-run monthly close succeeds in staging/dev data.
Phase A: Ledger Hardening
Objective:
- Make delivery and invoice records immutable where needed and auditable.
Deliverables:
- finance-safe invariants on issue/finalization.
- adjustment and payment write paths.
- reconciliation status query.
Acceptance criteria:
- No modification of draft line composition after issue except adjustments.
- Reconciliation view reports mismatch and missing Stripe refs clearly.
Phase B: App-Assisted Invoice Ops
Objective:
- Reduce operator effort for issuing and reconciling invoices.
Deliverables:
- invoice issue workflow with validation.
- payment/partial payment updates.
- arrears and overdue dashboard metrics.
Acceptance criteria:
- Operator can execute full period close from admin UI.
- Invoice status lifecycle transitions are validated and auditable.
Phase C: Auto-Dispatch Defaulting
Objective:
- Shift from manual routing-first to policy auto-dispatch-first.
Deliverables:
- intake-triggered internal auto-dispatch.
- exception queue for failed or ineligible leads.
- monitoring for dispatch latency and failure rate.
Acceptance criteria:
- Eligible new leads are dispatched without operator action.
- Failures are visible and actionable within one queue view.
Phase D: Scale Controls And Nice-To-Haves
Objective:
- Improve manager ergonomics and operational throughput.
Deliverables:
- quick-win management features from readiness addendum.
- bulk installer operations and saved views.
- escalation automation and auth hardening later in phase.
Acceptance criteria:
- Day-to-day manager workflows require fewer manual steps.
- escalation and permission controls remain safe under growth.
9. Validation And Test Spec
Backend tests
- Delivery event idempotency test (single event per assignment).
- Price snapshot correctness test at effective date boundaries.
- Invoice draft generation totals test.
- Adjustment and payment balance math test.
- Intake auto-dispatch success/failure path tests.
UI validation
- Invoice generation flow (draft to issued).
- Manual Stripe reference capture flow.
- Reconciliation status display correctness.
- Delivery event filtering and export behavior.
Operational dry runs
- Simulated month close in non-prod with seeded leads and assignments.
- Manual Stripe invoice entry for sample installers.
- Reconciliation check after marking payments.
10. Rollback And Safety Plan
- Feature flag intake auto-dispatch path.
- Keep manual routing controls active during Phase C rollout.
- If delivery event writer fails, block invoice generation and raise ops alert.
- Never delete delivery or invoice rows in rollback; use status transitions and adjustments.
11. Monthly Operating Runbook (Manual Stripe)
Week 1 cadence:
- Review previous period invoice statuses and outstanding balances.
- Confirm pricing plan changes for current period.
Month-end close:
- Run draft generation for period.
- Review outliers and credits.
- Export per-installer payload.
- Create Stripe invoices manually.
- Save Stripe invoice references in app.
- Mark invoices issued in app.
Weekly reconciliation:
- Check paid/partial/overdue status.
- Record payments in app.
- Resolve mismatches between app balance and Stripe state.
12. Open Decisions (Must Be Locked Before Build Starts)
- exact invoice period boundaries and timezone rules
- invoice numbering convention (app-generated vs Stripe-only)
- GST display and tax line strategy
- credit reason code taxonomy
- Stripe role ownership (creator, approver, reconciler)
13. Dependency Map
- Phase 0 depends on finalized pricing policy and credit reason taxonomy.
- Phase A depends on Phase 0 delivery event and invoice draft records existing.
- Phase B depends on Phase A ledger immutability and reconciliation queries.
- Phase C depends on stable assignment and delivery event writes from earlier phases.
- Phase D can run partially in parallel, but escalation automation should wait until Phase C is stable.
14. Go/No-Go Checklist (Before Starting Build)
All items must be explicitly decided and written before implementation starts:
- Invoice period boundaries and timezone source.
- GST behavior and whether amounts are tax-inclusive or tax-exclusive in app totals.
- Credit reason code list and approval policy.
- Stripe account ownership model and operator permissions.
- Which roles can issue invoices, add credits, and record payments.
- Manual Stripe workflow cadence and owner for weekly reconciliation.
- Naming and status conventions approved for new billing tables and UI labels.
15. Phase 0 First Sprint Plan (Execution Starter)
Use this as the first implementation sprint to avoid over-scoping.
Sprint goal
- Produce billable delivery ledger and export-ready draft invoice payloads.
Sprint scope
- Add
installerPricingPlans,deliveryEvents, andinvoicesschema tables. - Write delivery events on assignment transition to
sentwith idempotency. - Add billing draft generation and list queries.
- Add export payload query for manual Stripe invoice creation.
- Add minimal admin UI for draft invoice list and Stripe reference capture.
Sprint out of scope
- Full payment reconciliation UI.
- Credit/debit adjustment workflow.
- Auto-dispatch default behavior.
Sprint acceptance checks
- For a test period, generated draft totals match delivery event sums.
- No duplicate delivery events are created for repeated
senttransitions. - Stripe invoice id can be captured and persisted to invoice records.
- Export payload is sufficient for manual Stripe invoice creation.
16. AI Execution Protocol
Use this section as the build contract for AI-assisted implementation.
- Treat this file as implementation spec.
- Treat readiness doc as strategy context.
- Implement one phase at a time.
- Do not start next phase until acceptance criteria for current phase are met.
- For each phase:
- create task checklist
- implement schema and function changes
- implement UI changes
- run validations/tests
- update planning docs to reflect actual completion
17. Progress Tracking Template
Use this simple tracker directly in PRs or daily notes:
- Phase:
- Objective:
- Completed this cycle:
- Validation run:
- Blockers:
- Next step: