Where this file lives
- This path (
docs/coupons/membership-coupon-task-list.md) is inside the rakletv3 repo.- This file tracks the implementation and verification checklist for membership coupon flow alignment and ongoing membership coupon setup on the current feature branch.
Membership coupon implementation task list¶
Generated: 2026-05-08
Repo: rakletadmin/rakletv3
Base for review: origin/master...HEAD
Why this file exists¶
The branch spans multiple surfaces and mixes:
- business-rule changes
- UI alignment
- approval/payment persistence
- manager configuration behavior
- test coverage
- PR/review follow-ups
This checklist makes the scope explicit so the branch can be reviewed and landed without losing hidden edge cases.
Diff-derived implementation scope¶
Current coupon-related branch diff touches:
- helper / services
- checkout controllers and views
- signup/apply controllers and views
- admin Angular controllers and templates
- V2 coupon API
- unit tests
- manager coupon locale strings
- ongoing coupon model fields
- ongoing signup/application validation
- subscription-level coupon attachment
- ongoing coupon helper tests
Files outside coupon behavior should not expand the scope further unless they are required to support the rules below.
Canonical rule set to protect¶
All tasks and tests in this file are derived from these rules:
- Membership coupons support plan restrictions through
ApplicablePlanIds. - Membership coupons support application-fee inclusion through
AppliesToApplicationFee. - Percentage coupons apply to all qualifying invoices in migrated flows.
- Fixed coupons apply to one highest qualifying invoice only.
- If fixed-coupon candidates tie on amount, use the latest original invoice order.
- Percentage coupons consume one redemption per discounted invoice.
- Fixed coupons consume one redemption.
- Signup/apply group flows use one shared synthetic registration fee line.
- In signup/apply/application approval preview, the shared registration fee is eligible when any selected matching plan qualifies and the coupon allows application fee.
- In checkout, real inscription debts remain plan-specific.
- Group sub-member paid plans must be eligible for plan-restricted coupon matching.
- Preview, approval, and checkout persistence must agree on the discounted line set.
ApplicationTypemissing/null meansOneTime.- Ongoing coupons are accepted through the ongoing helper on signup/application and checkout/pay surfaces that discount a real membership-fee line.
MembershipCouponHelperremains the one-time invoice/debt helper and rejects ongoing coupons by default.OngoingMembershipCouponHelperowns ongoing validation and attachment rules.- Ongoing fixed coupons target
MembershipFeebefore application fee. MaxRedemptionsis not the billing-period counter forOngoingDurationType.Multiple.
Task breakdown¶
Phase 1: Shared rule engine¶
- [x] Centralize membership coupon filtering in
MembershipCouponHelper. - [x] Carry
MembershipPlanIdseparately from invoice/debt id. - [x] Support percentage multi-invoice allocation.
- [x] Keep fixed coupons single-target.
- [x] Return concrete
AppliedInvoiceDiscounts. - [x] Return
RequiredRedemptionsbased on discounted invoice count.
Subtasks¶
- [x] Filter out non-positive invoice amounts before coupon targeting.
- [x] Match plan restrictions against
MembershipPlanId. - [x] Differentiate synthetic registration fee vs real checkout inscription debt.
- [x] Split primary and sub-member membership discounts into ordered queues.
Phase 2: Manager coupon configuration¶
- [x] Persist
ApplicablePlanIdsvia V2 coupon API. - [x] Persist
AppliesToApplicationFeevia V2 coupon API. - [x] Normalize plan ids on save/update.
- [x] Explain fixed/percentage/application-fee behavior on create/detail screens.
- [x] Preserve plan restrictions when the detail screen cannot load plans safely.
Subtasks¶
- [x] Keep PATCH semantics backward-compatible when new fields are omitted.
- [x] Block unsafe plan-restriction editing on plan-load failure.
- [x] Add localized help / warning text for the new rules.
Phase 3: Signup and apply preview alignment¶
- [x] Build coupon invoice lines for parent plans.
- [x] Build coupon invoice lines for group sub-member paid plans.
- [x] Build the shared registration fee coupon line.
- [x] Map helper output back to:
- [x] main membership rows
- [x] sub-member rows
- [x] registration fee
- [x] Keep coupon entry available in multi-plan scenarios.
Subtasks¶
- [x] Make sure same-plan primary and sub-member rows do not consume the wrong discount queue entry.
- [x] Keep signup and apply display behavior aligned.
Phase 4: Admin waiting-application alignment¶
- [x] Stop using subtotal-only percentage estimation.
- [x] Mirror shared helper rules in the Angular controller.
- [x] Render line-level discount for main plan rows.
- [x] Render line-level discount for registration fee.
- [x] Render line-level discount for signup-origin sub-member subscription rows.
Subtasks¶
- [x] Reset previously calculated discount fields before recomputing.
- [x] Keep the total discount row equal to the sum of line-level discounts.
Phase 5: Application approval persistence¶
- [x] Reuse the same invoice-line model used in preview.
- [x] Apply registration fee discount to inscription debt.
- [x] Apply primary membership discounts in original queue order.
- [x] Apply sub-member membership discounts in original queue order.
- [x] Avoid tagging a payment with
CouponIdwhen no discount was actually applied.
Subtasks¶
- [x] Keep deferred-membership organizations charging only the correct discountable portion.
- [x] Avoid extra plan fetches where the current approval context already has the plan list.
Phase 6: Checkout / pay alignment¶
- [x] Stop relying on a single
DiscountedInvoiceIdfor percentage coupons. - [x] Use
AppliedInvoiceDiscountsfor row display and total calculation. - [x] Use the same discounted set for:
- [x] Stripe payment flow
- [x] saved-card payment flow
- [x] zero-total coupon-covered flow
- [x] coupon metadata persistence on debts
- [x] Keep fixed coupon highlighting stable.
Subtasks¶
- [x] Escape coupon plan ids safely in inline checkout script output.
- [x] Avoid duplicate
InvoiceIddictionary failures by grouping/summing. - [x] Keep Razor syntax compatible with the legacy ASP.NET parser.
Phase 7: Manager coupon list visibility¶
- [x] Decide whether the list page should remain compact or show plan/application-fee scope directly.
- [x] If expanded, render:
- [x] plan summary
- [x] application fee inclusion
- [ ] richer redemption explanation when needed
Current state:
- controller computes plan-summary metadata
- template keeps the existing columns but now shows plan scope and application-fee inclusion inside each coupon row
Phase 8: Documentation¶
- [x] Update the current-state process document.
- [x] Add a page-by-page surface flow document.
- [x] Add an implementation / verification task list.
- [x] Update docs again after the final test pass if the scope changes further.
Phase 9: Unit tests¶
- [x] Add rule-complete unit coverage for the migrated helper behavior.
- [x] Cover every new helper-level business rule introduced by the branch.
- [x] Cover ongoing helper boundary, duration, targeting, and attach behavior.
- [ ] Cover approval/payment stale-
CouponIdprotection with a dedicated service-level test if a stable test seam is introduced later.
Required helper test matrix¶
- [x] unrestricted percentage coupon discounts every qualifying invoice
- [x] unrestricted fixed coupon chooses the highest qualifying invoice
- [x] fixed coupon tie chooses latest original invoice order
- [x] fixed coupon larger than highest qualifying invoice is rejected
- [x] plan-restricted percentage coupon discounts every matching membership line
- [x] plan-restricted percentage coupon discounts matching shared registration fee in signup/apply/application preview
- [x] plan-restricted percentage coupon does not discount mismatched checkout inscription debt
- [x] application-fee-disabled coupon removes all inscription fee lines
- [x] wrong-plan coupon returns the correct user-facing error path
- [x] legacy coupon with missing
AppliesToApplicationFeeremains backward-compatible - [x] queue split preserves primary/sub-member ordering for same-plan duplicates
- [x] required redemption count equals number of discounted invoices
- [x] old one-time helper rejects ongoing coupons
- [x] ongoing helper delegates one-time coupons back to one-time required-redemption behavior
- [x] ongoing duration is required
- [x] ongoing
Multiplerequires a positive period count - [x] ongoing fixed coupons target membership fee before application fee
- [x] application-fee-only carts are invalid for ongoing coupons
- [x] ongoing attach writes
CustomMembership.CouponId - [x] one-time attach does not write
CustomMembership.CouponId
Required approval / checkout-oriented regression tests¶
- [x] checkout-style debt ids still match by
MembershipPlanId - [x] sub-member fee lines are included for variable-price group flows
- [x] fixed-price group sub-member rows remain excluded from coupon fee-line expansion
- [x] synthetic registration fee is included only when a matching plan exists
- [ ] approval/payment tagging does not keep stale
CouponIdwhen no debt discount is applied
Current automated status:
MembershipCouponHelperTestsnow covers 41 helper-level cases- approval/payment stale-
CouponIdprotection is still verified through implementation review and manual approval regression, not a dedicated unit test
Phase 10: PR and review metadata¶
- [x] Update PR description so it matches the final rule set.
- [x] Explicitly explain:
- [x] percentage vs fixed behavior
- [x] shared registration fee vs real checkout inscription debts
- [x] group sub-member coverage
- [x] redemption counting change
- [x] manager/admin/public surface alignment
- [x] Mention manual regression focus areas.
- [x] Mention automated test coverage that was added.
Phase 11: Ongoing membership coupon implementation¶
- [x] Add
CouponApplicationType. - [x] Add
OngoingDurationType. - [x] Add
CouponDoc.ApplicationTypewith backward-compatibleOneTimedefault. - [x] Add
CouponDoc.OngoingDurationType. - [x] Add
CouponDoc.OngoingDurationPeriods. - [x] Add matching
CouponViewModelfields. - [x] Add
CustomMembership.CouponId. - [x] Add admin create/detail/list controls and summaries for ongoing coupons.
- [x] Validate ongoing duration settings in
V2CouponsController. - [x] Keep redeemed coupon updates limited to
EndDateandMaxRedemptions. - [x] Separate ongoing signup validation into
OngoingMembershipCouponHelper. - [x] Keep
MembershipCouponHelperas one-time invoice/debt validation. - [x] Attach ongoing coupon id to the discounted subscription during approval.
- [x] Add helper-level tests for duration, membership-first targeting, one-time delegation, wrong-plan, application-fee-only, and attach behavior.
- [x] Implement scheduled WebJob application of attached ongoing coupons for generated membership-fee debts.
- [x] Apply attached ongoing coupons to scheduled payment amount calculation.
- [x] Apply attached ongoing coupons to before-due reminder balance checks.
- [ ] Add WebJob/integration coverage for scheduled debt/payment/reminder wiring if a suitable harness is introduced.
Ongoing period tracking decision¶
MaxRedemptionslimits subscription attachments/redemptions.MaxRedemptionsis not theMultiplebilling-period counter.- The WebJob phase derives applied periods from real membership-fee financial records that carry:
CustomMembershipIdDebtType == MembershipFeeCouponId == subscription.CouponId- positive
DiscountAmount - The first signup membership-fee discount counts as period 1.
Manual verification matrix¶
Signup / apply¶
- [ ] single plan, percentage, no plan restriction
- [ ] single plan, fixed, no plan restriction
- [ ] group parent + paid sub-members, unrestricted percentage
- [ ] group parent + paid sub-members, plan-restricted to parent only
- [ ] group parent + paid sub-members, plan-restricted to sub-plan only
- [ ] plan-restricted coupon with application fee on
- [ ] plan-restricted coupon with application fee off
Admin waiting application¶
- [ ] subtotal, discount total, and line items all agree
- [ ] sub-member subscription rows show discount amounts when applicable
- [ ] registration fee display matches public signup/apply preview
Approval¶
- [ ] approved primary membership payment/debt receives correct discount
- [ ] approved sub-member payment rows receive correct discount
- [ ] inscription debt receives correct registration fee discount
- [ ] redemption count increases by actual discounted invoice count
Checkout / pay¶
- [ ] unrestricted percentage over multiple selected invoices
- [ ] plan-restricted percentage over multiple selected invoices
- [ ] fixed coupon over multiple selected invoices
- [ ] application fee on/off combinations
- [ ] zero-total coupon-covered checkout
- [ ] saved-card checkout
- [ ] Stripe checkout
- [ ] ongoing checkout against an existing membership-fee debt
- [ ] ongoing checkout does not overwrite an already attached subscription coupon
Completion gate¶
The branch is not fully complete until all of the following are true:
- docs match the current implemented rules
- unit tests cover the new rule matrix
- PR description matches the final behavior
- checkout, signup/apply, admin preview, and approval are described consistently
- no stale "single target for percentage" language remains in docs or PR text
Final review snapshot¶
Reviewed on 2026-05-13 from the worktree checkout of feature/ongoing-membership-coupons at d6a63df40b.
Comment-driven checks:
- [x] Partial PATCH updates preserve existing ongoing application and duration settings.
- [x]
CustomMembership.CouponIdhas an explicit EF migration inModels/Migrations/202605080847127_AddCouponIdToCustomMemberships.*. - [x] Checkout/pay ongoing coupon acceptance is documented as intentional and is guarded by membership-fee discount and existing subscription-coupon checks.
- [x] Scheduled debt generation uses staged plan currency for fixed-coupon currency checks.
- [x] Scheduled debt and reminder paths reuse
SubscriptionCouponPricingServiceinstances where the review found repeated lookups. - [x] Request/display paths no longer leave undisposed
RakletDbpricing contexts in the reviewed controller paths. - [x] Turkish manager strings for ongoing coupon setup avoid mixed English UI copy.
Validation notes:
git diff --check origin/master...HEADwas rerun after this documentation update and should stay clean.- GitHub
jschecks were blocked before product tests by missingRaklet.Backend/package-lock.jsonin the workflow cache path. - GitHub
dotnetchecks were blocked before compiling changed code by missingpackages/MSTest.TestAdapter.3.1.1/build/net462/MSTest.TestAdapter.propson the runner. - The branch is still behind
origin/masterby 2 commits at this review point.