Allow multiple referral codes per sales agent
Drop the UNIQUE constraint on sales_agents.email (migration 084) so a single agent (person/company) can hold several referral codes, each with its own client discount and commission split. All commission lookups already key on the unique agent_code, so no lookup logic changes. Agent-creation endpoint now: - accepts repeat emails (creates an additional code instead of 409) - accepts client_discount_value, commission_type, commission_pct per code - reports existing codes for the email in the response Both Jay Kordic codes (REF-JKORDIC 7%/12%, REF-JAYK05 5%/15%) now share his real email jay_kordic@thehorizongroup.biz.
This commit is contained in:
parent
53857574d3
commit
345979ed00
2 changed files with 57 additions and 15 deletions
23
api/migrations/084_multi_code_per_agent.sql
Normal file
23
api/migrations/084_multi_code_per_agent.sql
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- 084_multi_code_per_agent.sql
|
||||||
|
-- Allow a single sales agent (person/company) to have MULTIPLE referral codes,
|
||||||
|
-- each with its own client discount and agent commission split.
|
||||||
|
--
|
||||||
|
-- Background: each sales_agents row already represents one referral code
|
||||||
|
-- (it owns a unique agent_code and links to one discount_code). The only thing
|
||||||
|
-- that blocked the same person from having several codes was the UNIQUE
|
||||||
|
-- constraint on sales_agents.email. We drop that constraint (keeping a plain
|
||||||
|
-- index for lookups) so the same email can back several codes/splits.
|
||||||
|
--
|
||||||
|
-- All commission/referral lookups key on the unique agent_code, so no other
|
||||||
|
-- logic changes are required at the DB level.
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Drop the UNIQUE constraint on email (name is auto-generated by Postgres).
|
||||||
|
ALTER TABLE sales_agents DROP CONSTRAINT IF EXISTS sales_agents_email_key;
|
||||||
|
|
||||||
|
-- Keep a non-unique index so email lookups stay fast.
|
||||||
|
-- (idx_agents_email already exists from migration 011; create defensively.)
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_agents_email ON sales_agents (email);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -76,18 +76,30 @@ export async function createCommission(params: {
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
router.post("/api/v1/admin/agents", requireAdmin, async (req, res) => {
|
router.post("/api/v1/admin/agents", requireAdmin, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { name, email, phone, company, commission_default_cents, commission_pct, commission_overrides, notes } = req.body ?? {};
|
const {
|
||||||
|
name, email, phone, company,
|
||||||
|
commission_type, commission_default_cents, commission_pct, commission_overrides,
|
||||||
|
client_discount_value, notes,
|
||||||
|
} = req.body ?? {};
|
||||||
if (!name || !email) {
|
if (!name || !email) {
|
||||||
res.status(400).json({ error: "Name and email are required." });
|
res.status(400).json({ error: "Name and email are required." });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate email
|
// A single agent (person/company) may hold MULTIPLE referral codes, each with
|
||||||
const existing = await pool.query("SELECT id FROM sales_agents WHERE email = $1", [email.toLowerCase().trim()]);
|
// its own client discount + commission split. We therefore allow repeat emails;
|
||||||
if (existing.rows.length > 0) {
|
// each call creates a new code. We surface any existing codes for the same email
|
||||||
res.status(409).json({ error: "An agent with this email already exists." });
|
// in the response so the caller knows it's an additional code, not a mistake.
|
||||||
return;
|
const existing = await pool.query(
|
||||||
}
|
"SELECT agent_code, commission_pct FROM sales_agents WHERE email = $1 ORDER BY created_at",
|
||||||
|
[email.toLowerCase().trim()],
|
||||||
|
);
|
||||||
|
const existingCodes = existing.rows.map((r) => r.agent_code);
|
||||||
|
|
||||||
|
// Per-code config (defaults preserve prior behaviour: 5% client discount, 10% commission)
|
||||||
|
const clientDiscount = client_discount_value !== undefined ? client_discount_value : 5;
|
||||||
|
const commType = commission_type === "flat" ? "flat" : "percent";
|
||||||
|
const commPct = commission_pct !== undefined ? commission_pct : 10;
|
||||||
|
|
||||||
// Generate unique agent code
|
// Generate unique agent code
|
||||||
let agentCode = generateAgentCode();
|
let agentCode = generateAgentCode();
|
||||||
|
|
@ -99,22 +111,23 @@ router.post("/api/v1/admin/agents", requireAdmin, async (req, res) => {
|
||||||
attempts++;
|
attempts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the linked discount code (5% off service fees)
|
// Create the linked discount code (client discount off service fees;
|
||||||
|
// referral_pct mirrors the agent commission for reporting).
|
||||||
const dcResult = await pool.query(
|
const dcResult = await pool.query(
|
||||||
`INSERT INTO discount_codes (code, description, discount_type, discount_value, referral_partner, referral_email, referral_pct, active)
|
`INSERT INTO discount_codes (code, description, discount_type, discount_value, referral_partner, referral_email, referral_pct, active)
|
||||||
VALUES ($1, $2, 'percent', 5, $3, $4, 0, TRUE)
|
VALUES ($1, $2, 'percent', $3, $4, $5, $6, TRUE)
|
||||||
RETURNING id`,
|
RETURNING id`,
|
||||||
[agentCode, `Sales agent: ${name}`, name, email.toLowerCase().trim()],
|
[agentCode, `Sales agent: ${name}`, clientDiscount, name, email.toLowerCase().trim(), commType === "percent" ? commPct : 0],
|
||||||
);
|
);
|
||||||
const discountCodeId = dcResult.rows[0].id;
|
const discountCodeId = dcResult.rows[0].id;
|
||||||
|
|
||||||
// Create the agent
|
// Create the agent (one row per referral code)
|
||||||
const result = await pool.query(
|
const result = await pool.query(
|
||||||
`INSERT INTO sales_agents (agent_code, discount_code_id, name, email, phone, company, commission_default_cents, commission_pct, commission_overrides, notes, onboarded_at)
|
`INSERT INTO sales_agents (agent_code, discount_code_id, name, email, phone, company, commission_type, commission_default_cents, commission_pct, commission_overrides, notes, onboarded_at)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now())
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now())
|
||||||
RETURNING id, agent_code`,
|
RETURNING id, agent_code`,
|
||||||
[agentCode, discountCodeId, name, email.toLowerCase().trim(), phone || null, company || null,
|
[agentCode, discountCodeId, name, email.toLowerCase().trim(), phone || null, company || null,
|
||||||
commission_default_cents || 30000, commission_pct || 10,
|
commType, commission_default_cents || 30000, commPct,
|
||||||
JSON.stringify(commission_overrides || {}), notes || null],
|
JSON.stringify(commission_overrides || {}), notes || null],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -123,8 +136,14 @@ router.post("/api/v1/admin/agents", requireAdmin, async (req, res) => {
|
||||||
success: true,
|
success: true,
|
||||||
agent_id: agent.id,
|
agent_id: agent.id,
|
||||||
agent_code: agent.agent_code,
|
agent_code: agent.agent_code,
|
||||||
|
client_discount_value: clientDiscount,
|
||||||
|
commission_type: commType,
|
||||||
|
commission_pct: commType === "percent" ? commPct : undefined,
|
||||||
|
existing_codes_for_email: existingCodes,
|
||||||
referral_url: `https://performancewest.net/order/canada-crtc?code=${agent.agent_code}`,
|
referral_url: `https://performancewest.net/order/canada-crtc?code=${agent.agent_code}`,
|
||||||
message: `Agent created. Referral code: ${agent.agent_code}. Client gets 5% off, agent earns commission per sale.`,
|
message: existingCodes.length > 0
|
||||||
|
? `Additional referral code created for ${email}. New code: ${agent.agent_code} (${clientDiscount}% client discount${commType === "percent" ? `, ${commPct}% commission` : ""}). This agent now has ${existingCodes.length + 1} codes.`
|
||||||
|
: `Agent created. Referral code: ${agent.agent_code}. Client gets ${clientDiscount}% off, agent earns commission per sale.`,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[agents] Create error:", err);
|
console.error("[agents] Create error:", err);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue