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:
@@ -112,7 +112,10 @@ export async function subscriptionRoutes(
|
|||||||
} else if (Date.parse(to) <= Date.parse(from)) {
|
} else if (Date.parse(to) <= Date.parse(from)) {
|
||||||
errs.push("validTo must be after validFrom");
|
errs.push("validTo must be after validFrom");
|
||||||
} else {
|
} 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");
|
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;
|
if (!b.planId?.trim() || !b.validTo?.trim()) return null;
|
||||||
const validFrom = b.validFrom?.trim() || new Date().toISOString();
|
const validFrom = b.validFrom?.trim() || new Date().toISOString();
|
||||||
const validTo = b.validTo.trim();
|
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;
|
if (!plan) return null;
|
||||||
const quantity = b.quantity != null && b.quantity > 0 ? Math.round(b.quantity) : 1;
|
const quantity = b.quantity != null && b.quantity > 0 ? Math.round(b.quantity) : 1;
|
||||||
const base = priceSubscriptionSpan(plan, validFrom, validTo);
|
const base = priceSubscriptionSpan(plan, validFrom, validTo);
|
||||||
@@ -276,7 +281,8 @@ export async function subscriptionRoutes(
|
|||||||
if (Date.parse(validTo) <= Date.parse(validFrom)) {
|
if (Date.parse(validTo) <= Date.parse(validFrom)) {
|
||||||
return reply.code(400).send({ error: "validTo must be after 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" });
|
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 quantity = b.quantity != null && b.quantity > 0 ? Math.round(b.quantity) : 1;
|
||||||
const base = priceSubscriptionSpan(plan, validFrom, validTo);
|
const base = priceSubscriptionSpan(plan, validFrom, validTo);
|
||||||
|
|||||||
Reference in New Issue
Block a user