Add referral/discount code to FCC carrier page + REF-JAYK05 agent
Frontend (order/fcc-carrier-registration): - Add a referral/discount code box on the review step that validates against /api/v1/discount/:code and shows the discount line + adjusted total. Discount applies to service fee + add-ons, never state filing fees. - Prefill + auto-apply from ?code= / ?ref= query param (referral links). Backend (fcc-carrier-registration route): - Accept discount_code, validate it, store discount_code/discount_cents, and subtract from the total. Checkout already reads discount_cents to apply the Stripe coupon. - Create a pending commission when the code belongs to an active sales agent. Commission fix (agents.createCommission): - Percent-type agents now earn commission_pct on ALL order types. Previously canada_crtc/formation/bundle used flat defaults and ignored percent agents. Agent: created sales agent Jay Kordic (The Horizon Group) with custom code REF-JAYK05 -> client gets 5% off discountable services, agent earns 15%. Idempotent setup script in scripts/create_agent_jaykordic.cjs.
This commit is contained in:
parent
1584a6692b
commit
53857574d3
4 changed files with 293 additions and 8 deletions
|
|
@ -37,19 +37,23 @@ export async function createCommission(params: {
|
|||
// Calculate commission amount
|
||||
let commissionCents = agent.commission_default_cents || 30000; // $300 default
|
||||
const overrides = agent.commission_overrides || {};
|
||||
|
||||
// Check for service-specific override
|
||||
|
||||
// Precedence:
|
||||
// 1. Explicit per-service override (always wins, flat cents)
|
||||
// 2. Percent-based agents earn commission_pct of the order on EVERY order type
|
||||
// 3. Otherwise fall back to per-type flat defaults
|
||||
if (params.serviceSlug && overrides[params.serviceSlug]) {
|
||||
commissionCents = overrides[params.serviceSlug];
|
||||
} else if (agent.commission_type === "percent") {
|
||||
// Percent agents (e.g. referral partners on a flat % deal) get the same
|
||||
// percentage regardless of order type. order_amount_cents is the total paid.
|
||||
commissionCents = Math.round((params.orderAmountCents * (agent.commission_pct || 10)) / 100);
|
||||
} else if (params.orderType === "canada_crtc") {
|
||||
commissionCents = overrides["canada-crtc"] || 30000;
|
||||
} else if (params.orderType === "formation") {
|
||||
commissionCents = overrides["formation"] || 5000;
|
||||
} else if (params.orderType === "bundle") {
|
||||
commissionCents = overrides["bundle"] || 10000;
|
||||
} else if (agent.commission_type === "percent") {
|
||||
// For compliance services, use percentage
|
||||
commissionCents = Math.round((params.orderAmountCents * (agent.commission_pct || 10)) / 100);
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
address_street, address_city, address_state, address_zip,
|
||||
service_wizard, services,
|
||||
engagement_accepted,
|
||||
discount_code,
|
||||
} = req.body ?? {};
|
||||
|
||||
// Validate required fields
|
||||
|
|
@ -104,6 +105,53 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
if (includeOcn) addonFeeCents += OCN_FEE_CENTS;
|
||||
const pucFeeCents = statePucStates.length * STATE_PUC_FEE_CENTS;
|
||||
|
||||
// ── Discount / referral code ──────────────────────────────────────────────
|
||||
// Discounts apply to the Performance West service fee only. State filing fees
|
||||
// (passed through at cost) are never discountable, matching the CRTC flow.
|
||||
let discountCents = 0;
|
||||
let normalizedDiscountCode: string | null = null;
|
||||
if (discount_code && typeof discount_code === "string" && discount_code.trim().length >= 2) {
|
||||
const code = discount_code.toUpperCase().trim();
|
||||
try {
|
||||
const dcResult = await pool.query(
|
||||
"SELECT * FROM discount_codes WHERE code = $1",
|
||||
[code],
|
||||
);
|
||||
if (dcResult.rows.length > 0) {
|
||||
const dc = dcResult.rows[0];
|
||||
const now = new Date();
|
||||
const active = dc.active === true;
|
||||
const notExpired = !dc.expires_at || new Date(dc.expires_at) >= now;
|
||||
const started = !dc.starts_at || new Date(dc.starts_at) <= now;
|
||||
const underGlobalLimit = dc.max_uses === null || dc.current_uses < dc.max_uses;
|
||||
// Scope check: allow codes scoped to this service (or unscoped)
|
||||
let inScope = true;
|
||||
if (dc.applies_to) {
|
||||
const allowed = String(dc.applies_to).split(",").map((s: string) => s.trim().toLowerCase());
|
||||
inScope = allowed.includes("fcc_carrier_registration") || allowed.includes("all");
|
||||
}
|
||||
// Email allowlist check
|
||||
let emailOk = true;
|
||||
if (dc.allowed_emails && dc.allowed_emails.length > 0) {
|
||||
const allowed = dc.allowed_emails.map((e: string) => e.toLowerCase());
|
||||
emailOk = allowed.includes(customer_email.toLowerCase().trim());
|
||||
}
|
||||
if (active && notExpired && started && underGlobalLimit && inScope && emailOk) {
|
||||
// Discountable base = service fee + add-ons (not state filing fees).
|
||||
const discountable = BASE_FEE_CENTS + addonFeeCents;
|
||||
if (dc.discount_type === "percent") {
|
||||
discountCents = Math.round((discountable * dc.discount_value) / 100);
|
||||
} else {
|
||||
discountCents = Math.min(dc.discount_value, discountable);
|
||||
}
|
||||
normalizedDiscountCode = code;
|
||||
}
|
||||
}
|
||||
} catch (dcErr) {
|
||||
console.warn("[fcc-carrier-reg] Discount lookup failed (non-fatal):", dcErr);
|
||||
}
|
||||
}
|
||||
|
||||
const orderNumber = generateOrderNumber();
|
||||
|
||||
const result = await pool.query(
|
||||
|
|
@ -118,11 +166,12 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
state_puc_states,
|
||||
service_fee_cents, formation_fee_cents, state_fee_cents,
|
||||
puc_fee_cents, addon_fee_cents,
|
||||
discount_code, discount_cents,
|
||||
engagement_accepted_at, engagement_accepted_ip
|
||||
) VALUES (
|
||||
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,
|
||||
$19::jsonb,$20,$21,$22,$23,$24,$25,$26,$27,$28::text[],
|
||||
$29,$30,$31,$32,$33,$34,$35
|
||||
$29,$30,$31,$32,$33,$34,$35,$36,$37
|
||||
) RETURNING *`,
|
||||
[
|
||||
orderNumber,
|
||||
|
|
@ -158,6 +207,8 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
stateFeeCents,
|
||||
pucFeeCents,
|
||||
addonFeeCents,
|
||||
normalizedDiscountCode,
|
||||
discountCents,
|
||||
engagement_accepted ? new Date().toISOString() : null,
|
||||
engagement_accepted ? (req.ip || req.headers["x-forwarded-for"] || null) : null,
|
||||
],
|
||||
|
|
@ -202,10 +253,38 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
}
|
||||
}
|
||||
|
||||
const totalCents = BASE_FEE_CENTS + formationFeeCents + stateFeeCents + pucFeeCents + addonFeeCents;
|
||||
const subtotalCents = BASE_FEE_CENTS + formationFeeCents + stateFeeCents + pucFeeCents + addonFeeCents;
|
||||
const totalCents = Math.max(0, subtotalCents - discountCents);
|
||||
|
||||
// If this order used a sales agent's referral code, record a pending commission.
|
||||
if (normalizedDiscountCode) {
|
||||
try {
|
||||
const agentCheck = await pool.query(
|
||||
"SELECT sa.agent_code FROM sales_agents sa JOIN discount_codes dc ON sa.discount_code_id = dc.id WHERE dc.code = $1 AND sa.active = TRUE",
|
||||
[normalizedDiscountCode],
|
||||
);
|
||||
if (agentCheck.rows.length > 0) {
|
||||
const { createCommission } = await import("./agents.js");
|
||||
await createCommission({
|
||||
agentCode: agentCheck.rows[0].agent_code,
|
||||
orderType: "fcc_carrier_registration",
|
||||
orderId: order.id,
|
||||
orderNumber: orderNumber,
|
||||
serviceSlug: "fcc-carrier-registration",
|
||||
customerName: customer_name.trim(),
|
||||
customerEmail: customer_email.toLowerCase().trim(),
|
||||
orderAmountCents: totalCents,
|
||||
discountCents: discountCents,
|
||||
});
|
||||
}
|
||||
} catch (commErr) {
|
||||
console.warn("[fcc-carrier-reg] Commission creation failed (non-fatal):", commErr);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[fcc-carrier-reg] Created ${orderNumber}: ${entity_source} for ${customer_email} — $${(totalCents / 100).toFixed(2)}`,
|
||||
`[fcc-carrier-reg] Created ${orderNumber}: ${entity_source} for ${customer_email} — $${(totalCents / 100).toFixed(2)}` +
|
||||
(discountCents > 0 ? ` (discount ${normalizedDiscountCode} -$${(discountCents / 100).toFixed(2)})` : ""),
|
||||
);
|
||||
|
||||
res.json({
|
||||
|
|
@ -219,6 +298,9 @@ router.post("/api/v1/fcc-carrier-registration", async (req: Request, res: Respon
|
|||
formation: formationFeeCents + stateFeeCents,
|
||||
addons: addonFeeCents,
|
||||
puc: pucFeeCents,
|
||||
subtotal: subtotalCents,
|
||||
discount_code: normalizedDiscountCode,
|
||||
discount_cents: discountCents,
|
||||
total: totalCents,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue