diff --git a/src/app/api/mcp/route.ts b/src/app/api/mcp/route.ts index a08bab9..94e6f71 100644 --- a/src/app/api/mcp/route.ts +++ b/src/app/api/mcp/route.ts @@ -565,7 +565,11 @@ const tools = [ "on the inbound PO PDF) to use it verbatim as `orders.order_number`. Empty / whitespace " + "values fall back to auto-generated `ORD-YYYY-MM-NNN` — same as omitting the field. On " + "collision the call surfaces an MCP error envelope with code/message `order_number_collision` " + - "and a `data` block carrying the colliding order id and attempted number.", + "and a `data` block carrying the colliding order id and attempted number.\n\n" + + "Optional `email_sender` / `email_subject` / `email_body`: stamp the inbound email metadata " + + "on the order in the same call so the operator can trace it back to its source email without " + + "a follow-up `orders.update` patch. The n8n intake pipeline fills these in from the parsed " + + "email; counter / agent runs typically leave them blank.", inputSchema: { type: "object", required: ["customer_id", "company_id", "items"], @@ -577,6 +581,9 @@ const tools = [ description: { type: "string", description: "Short description of the order (free-text)." }, rep: { type: "string", description: "Sales rep name (free-text)." }, order_number:{ type: "string", description: "Optional custom order_number (e.g. PO/ref printed on the inbound attachment). Empty/whitespace falls back to auto-generation." }, + email_sender: { type: "string", description: "Optional From: address of the inbound email — stamped on the order for audit trail." }, + email_subject:{ type: "string", description: "Optional subject line of the inbound email — stamped on the order for audit trail." }, + email_body: { type: "string", description: "Optional plain-text body of the inbound email — stamped on the order for audit trail." }, items: { type: "array", minItems: 1, @@ -3613,6 +3620,12 @@ export async function POST(req: NextRequest) { // Empty / whitespace order_number is treated as "not provided" // so the service layer falls back to auto-generation. order_number: typeof args.order_number === "string" ? args.order_number : undefined, + // Inbound email metadata — optional audit trail. Stamped + // here so the operator can trace the order back to the + // source email without a follow-up orders.update PATCH. + email_sender: typeof args.email_sender === "string" ? args.email_sender : undefined, + email_subject: typeof args.email_subject === "string" ? args.email_subject : undefined, + email_body: typeof args.email_body === "string" ? args.email_body : undefined, description: typeof args.description === "string" ? args.description : undefined, rep: typeof args.rep === "string" ? args.rep : undefined, items, diff --git a/src/app/api/v1/orders/create/route.ts b/src/app/api/v1/orders/create/route.ts index 969a5a5..1575bd8 100644 --- a/src/app/api/v1/orders/create/route.ts +++ b/src/app/api/v1/orders/create/route.ts @@ -23,6 +23,9 @@ import { * description?: string, * email_message_id?: string, // dedup key — returns existing order if one already exists * order_number?: string, // optional custom order number; empty/whitespace falls back to auto-gen + * email_sender?: string, // From: address of the inbound email (audit trail) + * email_subject?: string, // Subject line of the inbound email (audit trail) + * email_body?: string, // Plain-text body of the inbound email (audit trail) * rep?: string, * items: [ * { @@ -97,6 +100,13 @@ export async function POST(req: NextRequest) { // as the workflow sending `order_number: ""` when the AI didn't // find a PO number on the inbound attachment. order_number: typeof body?.order_number === "string" ? body.order_number : undefined, + // Inbound email metadata — optional audit-trail fields stamped + // by the n8n intake pipeline so the operator can trace the + // order back to its source email without needing the original + // attachment. + email_sender: typeof body?.email_sender === "string" ? body.email_sender : undefined, + email_subject: typeof body?.email_subject === "string" ? body.email_subject : undefined, + email_body: typeof body?.email_body === "string" ? body.email_body : undefined, description: typeof body?.description === "string" ? body.description : undefined, rep: typeof body?.rep === "string" ? body.rep : undefined, items, diff --git a/src/lib/server/orders-service.ts b/src/lib/server/orders-service.ts index 72ef1ed..3d10040 100644 --- a/src/lib/server/orders-service.ts +++ b/src/lib/server/orders-service.ts @@ -1910,6 +1910,26 @@ export interface CreateOrderInput { * dedup check entirely. */ email_message_id?: string; + /** + * Free-text sender address of the inbound email that originated + * this order (RFC 5322 From: address). Optional — the n8n intake + * pipeline stamps it for traceability so the operator can see who + * emailed the PO even when customer matching falls back to a + * placeholder. + */ + email_sender?: string; + /** + * Free-text subject line of the inbound email. Optional — same + * audit purpose as `email_sender`. + */ + email_subject?: string; + /** + * Plain-text body of the inbound email. Optional. Trimmed by the + * caller; large HTML attachments stay on `quote_attachments` / + * `order_attachements` — this field is the lightweight text-only + * snapshot for the order's audit trail. + */ + email_body?: string; /** * Optional customer-supplied order number (e.g. the PO/reference * printed on the inbound PO PDF that the n8n intake pipeline @@ -2067,6 +2087,12 @@ export async function createOrder(input: CreateOrderInput): Promise