fix(subs): resolve the plan version at the SALE instant, not validFrom

Selling/quoting a subscription resolved the plan version against `validFrom`,
but validFrom is a DATE (midnight UTC for "starts today"). A plan published
later the same day (effectiveFrom 15:22) then failed `effectiveFrom ≤ validFrom`
(00:00), so resolvePlanVersion returned null → "no active plan for that planId",
and the form's selectedPlan went null (hiding the new count field too).

The plan/price in force is determined by WHEN THE SALE HAPPENS, not by the
coverage start — like a tariff, the customer buys today's published rate. Resolve
at new Date() in all three sites (validate, priceSale, /quote); validFrom is kept
only for span pricing. Verified the two live plans now resolve.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-20 19:55:29 +02:00
parent ff04ec10be
commit c64457020f
+9 -3
View File
@@ -112,7 +112,10 @@ export async function subscriptionRoutes(
} else if (Date.parse(to) <= Date.parse(from)) {
errs.push("validTo must be after validFrom");
} else {
const plan = resolvePlanVersion(db, b.planId.trim(), from);
// Resolve the plan version at the SALE instant (now) — the customer buys today's
// published plan/price. (validFrom is the coverage start, which may be midnight
// today and predate a plan published this afternoon.)
const plan = resolvePlanVersion(db, b.planId.trim(), new Date().toISOString());
if (!plan) errs.push("no active plan found for the selected planId");
}
}
@@ -205,7 +208,9 @@ export async function subscriptionRoutes(
if (!b.planId?.trim() || !b.validTo?.trim()) return null;
const validFrom = b.validFrom?.trim() || new Date().toISOString();
const validTo = b.validTo.trim();
const plan = resolvePlanVersion(db, b.planId.trim(), validFrom);
// Plan version is resolved at the SALE instant (now), not validFrom (which is the
// coverage start and may predate a plan published later today).
const plan = resolvePlanVersion(db, b.planId.trim(), new Date().toISOString());
if (!plan) return null;
const quantity = b.quantity != null && b.quantity > 0 ? Math.round(b.quantity) : 1;
const base = priceSubscriptionSpan(plan, validFrom, validTo);
@@ -276,7 +281,8 @@ export async function subscriptionRoutes(
if (Date.parse(validTo) <= Date.parse(validFrom)) {
return reply.code(400).send({ error: "validTo must be after validFrom" });
}
const plan = resolvePlanVersion(db, b.planId.trim(), validFrom);
// Resolve at the sale instant (now), not validFrom — see priceSale.
const plan = resolvePlanVersion(db, b.planId.trim(), new Date().toISOString());
if (!plan) return reply.code(404).send({ error: "no active plan for that planId" });
const quantity = b.quantity != null && b.quantity > 0 ? Math.round(b.quantity) : 1;
const base = priceSubscriptionSpan(plan, validFrom, validTo);