Skip to content

fix(frontend): fan-out modal Payment selects relabel without re-pricing #2070

Description

@cristim

Summary

The fan-out modal's own Payment selects relabel a recommendation's payment without re-pricing it, exactly the defect #1903 fixes in the single-purchase modal. Changing a bucket's Payment, or a per-recommendation Payment inside a bucket, writes the new payment onto the row while the loaded upfront_cost, monthly_cost and savings stay at the previous payment's values. The submit path posts those unchanged numbers.

This matters more than a display bug because the backend does not re-price. validateAndTotalRecommendations sums the submitted costs into the execution row and the approval email, and recTotalCommitment multiplies the submitted monthly cost by the submitted term for the MaxPurchaseAmount cap. The recorded, emailed and cap-checked figures therefore describe a payment option the user did not buy, while the provider charges for the one they did.

Every multi-bucket or partially incompatible selection reaches this path, so it is not a rare corner.

Location

  • frontend/src/recommendations.ts:4468 (bucket-level Payment select sets b.payment)
  • frontend/src/recommendations.ts:4559 (per-recommendation Payment select writes perRecPayments)
  • consumed unchanged at frontend/src/app.ts:534

Failure scenario

A bulk selection produces an RDS bucket seeded all-upfront at $1,000 upfront. The user changes that bucket's Payment to partial-upfront. The label updates, the row still carries upfront_cost: 1000 from the all-upfront variant, and the POST submits partial-upfront paired with the all-upfront price. Test T9 in frontend/src/__tests__/purchase-modal-submit.test.ts currently encodes this un-repriced submission as the expected behaviour, and should be inverted when this is fixed.

Suggested fix

Apply the same treatment #1903 used: swap in the loaded sibling variant for the chosen payment, scaled to the bucket's capacity, and build the select's options from priced variants only so an unpriced combination cannot be chosen. The helpers added in that PR (pricedCellVariant, applyVariantChange) are reusable here.

Related

Follow-up from #1903 and #1904, found during adversarial review of their fix. The single-purchase modal is repaired; this is the same defect class in the fan-out path, which that PR deliberately left in scope for a separate change.

A second, cosmetic item belongs with it: recommendations.ts:4453 builds the bucket Payment options from paymentOptionsFor, so an incompatible seed renders as the first option even though the bucket is marked as skipped. Harmless now that the skip label, the totals and the submit filter agree, but it should go away with the re-pricing fix.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions