Skip to content

feat(catalog): attach the plan ecosystem to catalog plans - #6304

Open
rsempe wants to merge 2 commits into
mainfrom
bil-644
Open

feat(catalog): attach the plan ecosystem to catalog plans#6304
rsempe wants to merge 2 commits into
mainfrom
bil-644

Conversation

@rsempe

@rsempe rsempe commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What this is

The shared plan ecosystem — coupon targets, plan taxes, entitlements — must be able to point at a catalog plan, not only a legacy Plan. Additive: existing rows keep their plan_id.

Changes

Nullable catalog_plan_id (+ index + a NOT VALID → validated FK) added to coupon_targets, plans_taxes, entitlement_entitlements — the rate_phase XOR pattern, real Postgres FKs.

  • plans_taxes: plan_id relaxed to nullable; unique (catalog_plan_id, tax_id) index mirroring the legacy (plan_id, tax_id) one (also serves the FK lookup, so no standalone column index). Model validates exactly one of plan / catalog_plan.
  • entitlement_entitlements: unique partial (entitlement_feature_id, catalog_plan_id) index mirroring the plan one; the DB exactly_one_parent check swapped from plan XOR subscription to num_nonnulls(plan, catalog_plan, subscription) = 1, and the model validation counts all three.
  • coupon_targets: model validates a target points at a legacy or a catalog plan, never both (billable-metric targets unaffected).

## Context

PR 1 of the catalog-plans split (Dive-In 6): the shared plan ecosystem —
coupon targets, plan taxes and entitlements — must be able to point at a
catalog plan, not only a legacy plan. This is additive; existing rows keep
their plan_id.

## Description

Add a nullable catalog_plan_id (+ index + not-valid-then-validated foreign
key) to coupon_targets, plans_taxes and entitlement_entitlements, the
rate_phase XOR pattern. plans_taxes.plan_id becomes nullable and gains a
unique (catalog_plan_id, tax_id) index mirroring the legacy one;
entitlements gain a unique (feature, catalog_plan_id) index and their
exactly-one-parent check now counts plan, catalog_plan and subscription.
Each model validates that it references a single plan (or exactly one
parent), and the factories gain catalog-plan variants.
@lago-claude-ai-agent

Copy link
Copy Markdown
Contributor

Automated pre-review (advisory, not a required check) — verdict: HOLD · CI green

HOLD — plans_taxes loses its database-enforced parent invariant when plan_id becomes nullable.

  • Add and validate a check constraint requiring exactly one of plan_id and catalog_plan_id; model validation alone can be bypassed by direct/bulk writes and permits rows with neither or both parents at the database level.
  • Add regression coverage that bypasses model validation and proves the database rejects both invalid parent combinations.

## Context

Making plans_taxes.plan_id nullable dropped its database-enforced
guarantee that a tax always has a plan, leaving only the model validation
— which direct or bulk writes bypass.

## Description

Add a check constraint requiring exactly one of plan_id and
catalog_plan_id on plans_taxes (not valid, then validated in the follow-up
migration), matching the entitlement parent check. Cover it with specs
that bypass model validation and prove the database rejects a row with
neither or both parents.
@lago-claude-ai-agent

Copy link
Copy Markdown
Contributor

Automated pre-review (advisory, not a required check) — verdict: HOLD · CI green

HOLD: The new foreign keys are only wired from the join records, leaving CatalogPlan out of the association graph used throughout the application.

  • Mirror the legacy plan relationships on CatalogPlan (coupon_targets/coupons, applied_taxes/taxes, and entitlements/entitlement_values) and expose catalog plans from the corresponding opposite-side models where legacy plans are exposed (Coupon, Tax, and Entitlement::Feature). Add association coverage for those sibling models.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant