Skip to content

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:

  1. Membership coupons support plan restrictions through ApplicablePlanIds.
  2. Membership coupons support application-fee inclusion through AppliesToApplicationFee.
  3. Percentage coupons apply to all qualifying invoices in migrated flows.
  4. Fixed coupons apply to one highest qualifying invoice only.
  5. If fixed-coupon candidates tie on amount, use the latest original invoice order.
  6. Percentage coupons consume one redemption per discounted invoice.
  7. Fixed coupons consume one redemption.
  8. Signup/apply group flows use one shared synthetic registration fee line.
  9. In signup/apply/application approval preview, the shared registration fee is eligible when any selected matching plan qualifies and the coupon allows application fee.
  10. In checkout, real inscription debts remain plan-specific.
  11. Group sub-member paid plans must be eligible for plan-restricted coupon matching.
  12. Preview, approval, and checkout persistence must agree on the discounted line set.
  13. ApplicationType missing/null means OneTime.
  14. Ongoing coupons are accepted through the ongoing helper on signup/application and checkout/pay surfaces that discount a real membership-fee line.
  15. MembershipCouponHelper remains the one-time invoice/debt helper and rejects ongoing coupons by default.
  16. OngoingMembershipCouponHelper owns ongoing validation and attachment rules.
  17. Ongoing fixed coupons target MembershipFee before application fee.
  18. MaxRedemptions is not the billing-period counter for OngoingDurationType.Multiple.

Task breakdown

Phase 1: Shared rule engine

  • [x] Centralize membership coupon filtering in MembershipCouponHelper.
  • [x] Carry MembershipPlanId separately from invoice/debt id.
  • [x] Support percentage multi-invoice allocation.
  • [x] Keep fixed coupons single-target.
  • [x] Return concrete AppliedInvoiceDiscounts.
  • [x] Return RequiredRedemptions based 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 ApplicablePlanIds via V2 coupon API.
  • [x] Persist AppliesToApplicationFee via 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 CouponId when 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 DiscountedInvoiceId for percentage coupons.
  • [x] Use AppliedInvoiceDiscounts for 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 InvoiceId dictionary 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-CouponId protection 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 AppliesToApplicationFee remains 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 Multiple requires 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 CouponId when no debt discount is applied

Current automated status:

  • MembershipCouponHelperTests now covers 41 helper-level cases
  • approval/payment stale-CouponId protection 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.ApplicationType with backward-compatible OneTime default.
  • [x] Add CouponDoc.OngoingDurationType.
  • [x] Add CouponDoc.OngoingDurationPeriods.
  • [x] Add matching CouponViewModel fields.
  • [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 EndDate and MaxRedemptions.
  • [x] Separate ongoing signup validation into OngoingMembershipCouponHelper.
  • [x] Keep MembershipCouponHelper as 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

  • MaxRedemptions limits subscription attachments/redemptions.
  • MaxRedemptions is not the Multiple billing-period counter.
  • The WebJob phase derives applied periods from real membership-fee financial records that carry:
  • CustomMembershipId
  • DebtType == MembershipFee
  • CouponId == 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.CouponId has an explicit EF migration in Models/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 SubscriptionCouponPricingService instances where the review found repeated lookups.
  • [x] Request/display paths no longer leave undisposed RakletDb pricing 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...HEAD was rerun after this documentation update and should stay clean.
  • GitHub js checks were blocked before product tests by missing Raklet.Backend/package-lock.json in the workflow cache path.
  • GitHub dotnet checks were blocked before compiling changed code by missing packages/MSTest.TestAdapter.3.1.1/build/net462/MSTest.TestAdapter.props on the runner.
  • The branch is still behind origin/master by 2 commits at this review point.