// orders/create -> analytics (F23, N02). The function's candidate message is the offer name, or "A + B" when // one line carries two offers; we attribute by name. If Shopify reports only the discount title // (DISCOUNT_TITLE, "Offerlyst bundles"), the usage is kept under that title. Verify the exact title on a dev store (README). import db from "../db.server"; export interface OrderPayload { id: number | string; name?: string; currency?: string; total_price?: string; discount_applications?: { type?: string; title?: string; description?: string | null }[]; line_items?: { price?: string; quantity?: number; discount_allocations?: { amount?: string; discount_application_index?: number }[]; }[]; } export async function recordOrder( shopId: string, order: OrderPayload, offers: { id: string; name: string }[], discountTitle: string, ) { const orderId = String(order.id); const apps = order.discount_applications || []; const byName = new Map(offers.map((o) => [o.name, o.id])); const ours = new Map(); // application index -> offer names apps.forEach((a, i) => { const title = (a.title || a.description || "").trim(); if (!title) return; const parts = title.split(" + ").map((s) => s.trim()); if (parts.every((p) => byName.has(p))) ours.set(i, parts); else if (title === discountTitle && a.type !== "discount_code") ours.set(i, [discountTitle]); }); const perOffer = new Map(); for (const li of order.line_items || []) { const lineTotal = Number(li.price || 0) * (li.quantity || 0); for (const al of li.discount_allocations || []) { const names = ours.get(al.discount_application_index ?? -1); if (!names) continue; const amount = Number(al.amount || 0); // a merged line ("A + B"): split evenly, we don't know the per-offer share for (const n of names) { const e = perOffer.get(n) || { discount: 0, revenue: 0 }; e.discount += amount / names.length; e.revenue += lineTotal / names.length; perOffer.set(n, e); } } } const hasBundle = perOffer.size > 0; const currency = order.currency || "USD"; const total = Number(order.total_price || 0); const existing = await db.orderStat.findUnique({ where: { shopId_orderId: { shopId, orderId } } }); if (!existing) { await db.orderStat.create({ data: { shopId, orderId, total, hasBundle, currency } }); } for (const [name, v] of perOffer) { await db.orderUsage.upsert({ where: { shopId_orderId_offerName: { shopId, orderId, offerName: name } }, create: { shopId, orderId, orderName: order.name || null, offerId: byName.get(name) || null, offerName: name, discount: Math.round(v.discount * 100) / 100, bundleRevenue: Math.round(v.revenue * 100) / 100, orderTotal: total, currency, }, update: {}, }); } return { hasBundle, isNew: !existing }; }