fix batch SO item_code (use erpnext_item) + notification surcharge breakdown

ROOT CAUSE of orders never fulfilling: the batch Sales Order used the service
SLUG as item_code (e.g. 'mcs150-update') but ERPNext items use the catalog
erpnext_item codes ('MCS150-UPDATE'), so SO creation threw 'Item not found' ->
no SO -> no portal -> no fulfillment. Now maps slug -> erpnext_item (falls back
to COMPLIANCE-SERVICE). DOT ERPNext items were also missing — created them.

Notification: show Subtotal / Discount / Card surcharge / Total so totals like
$35.54 (= $34.50 + $1.04 surcharge) are transparent instead of looking wrong.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-31 20:01:29 -05:00
parent f4230e1cb1
commit 869bcac287

View file

@ -1268,26 +1268,29 @@ export async function handlePaymentComplete(
const customerName = (order.customer_name as string) || "Unknown";
const customerEmail = (order.customer_email as string) || "";
let totalCents = 0;
let subtotalCents = 0;
let discountCents = 0;
let surchargeCents = 0;
const serviceNames: string[] = [];
for (const r of rows) {
const fee = Number(r.service_fee_cents || r.total_cents || 0);
const disc = Number(r.discount_cents || 0);
const sur = Number(r.surcharge_cents || 0);
const gov = Number(r.gov_fee_cents || 0);
totalCents += fee - disc + sur + gov;
discountCents += disc;
subtotalCents += Number(r.service_fee_cents || r.total_cents || 0) + Number(r.gov_fee_cents || 0);
discountCents += Number(r.discount_cents || 0);
surchargeCents += Number(r.surcharge_cents || 0);
serviceNames.push((r.service_name as string) || (r.service_slug as string) || "");
}
const totalCents = subtotalCents - discountCents + surchargeCents;
const totalDollars = (totalCents / 100).toFixed(2);
const serviceLine = serviceNames.length <= 1
? `Service: ${serviceNames[0] || order_type}\n`
: `Services (${serviceNames.length}):\n • ${serviceNames.join("\n • ")}\n`;
const subtotalLine = `Subtotal: $${(subtotalCents / 100).toFixed(2)}\n`;
const discountLine = discountCents > 0
? `Discount: -$${(discountCents / 100).toFixed(2)} (${order.discount_code || "promo"})\n`
: "";
const surchargeLine = surchargeCents > 0
? `Card surcharge: +$${(surchargeCents / 100).toFixed(2)}\n`
: "";
// Show DOT# or FRN/CORES ID depending on service type
const intake = (typeof order.intake_data === "object" && order.intake_data) || {};
const dotNumber = (intake as any).dot_number || "";
@ -1300,8 +1303,10 @@ export async function handlePaymentComplete(
+ `Email: ${customerEmail}\n`
+ idLine
+ serviceLine
+ `Total: $${totalDollars}\n`
+ subtotalLine
+ discountLine
+ surchargeLine
+ `Total: $${totalDollars}\n`
+ `Payment: ${paymentMethod}\n`
+ `Order: ${order_id}\n`
+ `Type: ${order_type}`;
@ -1475,12 +1480,15 @@ export async function handlePaymentComplete(
} catch { /* non-fatal */ }
// Build line items for ERPNext — show full price with discount on each line
const { COMPLIANCE_SERVICES } = await import("./compliance-orders.js");
const items: Record<string, unknown>[] = [];
for (const bo of batchRows.rows as any[]) {
const priceCents = (bo.service_fee_cents || 0) + (bo.gov_fee_cents || 0);
const discCents = bo.discount_cents || 0;
// Map slug -> ERPNext item code (items use erpnext_item codes, not slugs)
const itemCode = (COMPLIANCE_SERVICES as any)[bo.service_slug as string]?.erpnext_item || "COMPLIANCE-SERVICE";
items.push({
item_code: bo.service_slug,
item_code: itemCode,
item_name: bo.service_name || bo.service_slug,
qty: 1,
rate: priceCents / 100,