feat(fixed-charges): skip plan override on units-only update (PoC)#5505
Draft
ancorcruz wants to merge 6 commits into
Draft
feat(fixed-charges): skip plan override on units-only update (PoC)#5505ancorcruz wants to merge 6 commits into
ancorcruz wants to merge 6 commits into
Conversation
…rride table ## Context PoC for letting subscriptions store a per-fixed-charge unit value without materialising a plan override. The original implementation was landed under PR #4011 and reverted under PRs #4318 and #4876 while waiting for the wider plan-override refactor. Reintroducing it as the foundation for a units-only override path. ## Description Recreate the subscription_fixed_charge_units_overrides table, model, factory and model spec. No application code consumes it yet; the service and read-path wiring follow in subsequent commits.
…fixed charge ## Context Wire the SubscriptionFixedChargeUnitsOverride model into its two parents so the override can be read or built from either side without going through a join lookup. ## Description Add `has_many :fixed_charge_units_overrides` on Subscription and `has_many :subscription_units_overrides` on FixedCharge, plus the matching association assertions in the existing model specs.
… units ## Context Read-paths that consume a fixed charge's units in a subscription context need to honour the per-subscription override when one exists. Adding a single accessor on FixedCharge so callers can drop in a subscription-aware lookup without rewriting every read site. ## Description Add `FixedCharge#units_for(subscription)`. Returns the override units when a SubscriptionFixedChargeUnitsOverride exists for the pair, and falls back to the fixed charge's own `units` otherwise. Soft-discarded overrides are ignored thanks to the default scope on the override model.
…ange ## Context When a subscription's fixed-charge units are updated through the API, the current implementation always materialises a plan override plus a fixed-charge override. For customers with thousands of subscriptions on a shared plan this produces an explosion of plan overrides that are otherwise identical to the parent plan. This change introduces a units-only fast path that records the override on the dedicated table instead of cloning the plan. ## Description In `Subscriptions::UpdateOrOverrideFixedChargeService`, branch on the incoming params: when only `units` (optionally with `apply_units_immediately`) is supplied and the subscription is not already on an overridden plan, create or update a `SubscriptionFixedChargeUnitsOverride` against the parent fixed charge and emit billing events as before. Any other field change, or a subscription already on an overridden plan, keeps the existing plan-override behaviour to avoid splitting the unit value across two storage mechanisms.
## Context The fixed-charge event is the value that drives downstream billing. With the units-only override path landing, the parent fixed charge is reused for emission and its `units` no longer reflects the subscription-specific value. The event must resolve the effective units for the subscription it belongs to so billing stays accurate. ## Description In `FixedChargeEvents::CreateService`, build the event with `fixed_charge.units_for(subscription)` instead of `fixed_charge.units`. `FixedCharge#units_for` is taught to fall back to its own `units` when no subscription is passed so the existing nil-subscription validation path keeps working.
…override ## Context Unit specs cover the new service path and the units lookup helper in isolation. To trust the round-trip against the real API and job pipeline, a scenario spec drives the subscription fixed-charge endpoint end-to-end and asserts the side effects observable from the database. ## Description Add `update_subscription_fixed_charge` to the scenarios helper for hitting `PUT /api/v1/subscriptions/:external_id/fixed_charges/:code`. Add a scenario spec that creates a subscription, hits the endpoint with a units-only payload, runs the enqueued jobs, and asserts no plan override is created, no fixed-charge override is created, a SubscriptionFixedChargeUnitsOverride row is created, and the emitted fixed charge event carries the override units.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
subscription_fixed_charge_units_overridesto record per-subscription units without cloning the plan (originally added in Feat(Fixed_charges): add subscription_fixed_charge_units_override #4011, removed in Feat(fixed_charges): remove subscriptions fixed charges units override #4318 and feat(fixed-charges): drop units override table #4876 while waiting on the wider plan-override refactor).Subscriptions::UpdateOrOverrideFixedChargeService: when the request only changesunits(optionally withapply_units_immediately) and the subscription is not already on an overridden plan, write to the new table and skip the plan/fixed-charge override path. Any other field change, or a subscription already on an overridden plan, keeps the existing behaviour.FixedChargeEvents::CreateServicethrough a newFixedCharge#units_for(subscription)accessor so emitted events carry the effective units.Background
Updating a fixed charge's units for one subscription today materialises a full plan override plus a fixed-charge override.
That's correct semantically but expensive in practice: organisations with many subscriptions on a shared plan accumulate one plan override per subscription, even though only
unitsdiffers. This PoC offers a units-only fast path that avoids the plan override entirely while preserving the legacy path for any other field change.Out of scope for this PoC
Read sites still consume
fixed_charge.unitsdirectly. They need to be switched tofixed_charge.units_for(subscription)in a follow-up before this can ship beyond PoC:app/services/fees/fixed_charge_service.rbapp/services/fees/build_pay_in_advance_fixed_charge_service.rbapp/services/fees/init_from_adjusted_fixed_charge_fee_service.rbapp/services/fixed_charge_events/aggregations/preview_aggregation_service.rbThe scenario spec deliberately stops at the event level until those reads are wired.