Default entitlement resolver — derives a billable's entitlements from local subscription state only, with zero processor calls.
Resolution path:
- Read-only
accrue_customerslookup by(owner_type, owner_id)— a clone of the privateAccrue.Billing.fetch_customer/2, NEVER the effectful get-or-create customer path (which would hit the processor on a miss). Anil/wrong-shape billable resolves to no customer. - Entitling-subscription read via
Accrue.Billing.Query.entitling/1(active/trialing, not paused, not ended; the database twin ofAccrue.Billing.Subscription.entitling?/1; never raw.status) joined to its items, selecting{price_id, quantity}. Using the entitlement-grade fragment closes the paused fail-open gap: astatus: :activerow with a non-nilpause_collectionno longer grants entitlement. - Fold each active item through the
price_id -> planreverse index built fromAccrue.Config.entitlements/0, accumulating:active_plans— the SET of ALL active plan atoms (membership source of truth forhas_active_plan?/2),features— the UNION of every active plan's features,quantities— mergedquota_key => min(cap, quantity),grace_plans— the SUBSET ofactive_plansadmitted via the past-due grace window (empty unlesspast_due_graceis enabled).
Past-due grace overlay (ENT-09): when Accrue.Config.past_due_grace/0 is
:none (default) the base fetch stays Query.entitling/1 with the lean
{price_id, quantity} select — zero query/compute change. When grace is
enabled the fetch widens to Query.entitling_with_grace_candidates/1 (adds
:past_due only, never :unpaid), and each :past_due candidate (per
Subscription.dunning_sweepable?/1) is kept only if
PastDueGrace.within_grace?/2 is true for its past_due_since; kept rows
are tagged into grace_plans. A grace grant is an affirmative, resolved,
configured decision — never a fail-open.
An active item whose price_id is unmapped is dropped under the default
:deny (and its plan is NOT added to active_plans); under :raise the
resolver raises so the context's try/rescue collapses it to fail-closed.
:plan carries a single representative active plan (the last folded one,
or nil) for display only — membership decisions use active_plans.