[object Object]

← back to Dw Pitch Followup

pitch: list2 letter follows up on the specific QUOTE (number, total, items) not generic samples

c6994a4bb8660ee770ef62a2061b70fb506d40be · 2026-07-14 11:20:16 -0700 · Steve

New quote-followup branch in composeDeterministic for list2: cites the actual quote #(s), total, and quoted items with amounts, and asks to convert to an order. Sanitizes leaked dimension strings out of the project ref and strips empty "(Ref.)" from item names. Verified against a real $6,693.60 two-quote client.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit c6994a4bb8660ee770ef62a2061b70fb506d40be
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Jul 14 11:20:16 2026 -0700

    pitch: list2 letter follows up on the specific QUOTE (number, total, items) not generic samples
    
    New quote-followup branch in composeDeterministic for list2: cites the actual quote #(s), total, and quoted items with amounts, and asks to convert to an order. Sanitizes leaked dimension strings out of the project ref and strips empty "(Ref.)" from item names. Verified against a real $6,693.60 two-quote client.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 server.js | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index a5ec916..ba37c53 100644
--- a/server.js
+++ b/server.js
@@ -310,7 +310,10 @@ function composeDeterministic(list, row, ctx, seed, showMfr) {
     const alt = (list === 'list1' ? row.patterns : row.details) || [];
     sampleItems = alt.map((p) => clientLabel(p, showMfr)).filter(Boolean).slice(0, 8).map((n) => `  • ${n}`);
   }
-  const proj = String(row.project || '').trim();
+  // Only treat `project` as a real project name — reject dimension/spec strings that leak in from
+  // the invoice line (e.g. '19.6" wide x 32.8 ft. per bolt') so the letter never says "your … project".
+  let proj = String(row.project || '').trim();
+  if (!/[a-z]{3}/i.test(proj) || /\b(wide|ft|bolt|inch|yard|sq|roll|panel|per\b)\b/i.test(proj) || /\d\s*["'”]/.test(proj)) proj = '';
   const projRef = proj ? ` for your ${proj} project` : '';
 
   // seed-varied flavor so each Regenerate reads fresh
@@ -347,6 +350,27 @@ function composeDeterministic(list, row, ctx, seed, showMfr) {
     return { subject: 'Thank you for your order — Designer Wallcoverings', body: tyBody, engine: 'thank-you' };
   }
 
+  // LIST 2 — "Quoted · No Order": the letter is about the SPECIFIC quote (number, total, items)
+  // and asks to move it to an order — not the generic "how did the samples go" note.
+  const fmtMoney = (n) => '$' + (Number(n) || 0).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+  if (list === 'list2') {
+    const quotes = (Array.isArray(row.merch_invoices) ? row.merch_invoices : []).filter((i) => !i.booked);
+    const qtotal = row.quote_total || quotes.reduce((s, i) => s + (Number(i.total) || 0), 0);
+    const qnums = quotes.map((i) => (i.invoice ? `#${i.invoice}` : '')).filter(Boolean);
+    const qRef = qnums.length === 1 ? ` (quote ${qnums[0]})` : (qnums.length > 1 ? ` (quotes ${qnums.slice(0, 3).join(', ')})` : '');
+    const qItems = quotes.map((i) => { const it = clientLabel(i.item || '', showMfr).replace(/\(\s*Ref\.?\s*\)/gi, '').replace(/\s{2,}/g, ' ').trim(); return (it && /[a-z]/i.test(it)) ? `  • ${it}${i.total ? ` — ${fmtMoney(i.total)}` : ''}` : null; }).filter(Boolean);
+    const totalStr = qtotal ? ` totaling ${fmtMoney(qtotal)}` : '';
+    const qbody =
+      `Hi ${greetName},\n\n` +
+      `${g} I wanted to follow up on the quote we put together for you${projRef}${qRef}${totalStr}. We'd love to help you bring it to life — if you're ready to move forward, we'll confirm current pricing and stock and get your order placed right away.${tradeLine}\n\n` +
+      (qItems.length ? `Here's what we quoted…\n\n${qItems.slice(0, 12).join('\n')}\n\n` : '') +
+      `If anything's changed — quantities, colorways, or timing — just let me know and I'll update it. ${q}\n\n` +
+      `${signNames} — Designer Wallcoverings\n` +
+      `designerwallcoverings.com  ·  1-888-373-4564\n\n` +
+      `P.S. Happy to hold pricing or walk through options on a quick call — just say the word.`;
+    return { subject: `Following up on your Designer Wallcoverings quote${qnums.length === 1 ? ` ${qnums[0]}` : ''}`, body: qbody, engine: 'quote-followup' };
+  }
+
   // ~3 tight sentences: (1) follow-up + final-purchase ask, (2) witty samples/stock + we're-here-to-help
   // [+trade], (3) project-details line then a closing question. Then the sample reminder + sign-off + PS.
   const body =

← f9b6323 pitch: Sent log is now LIVE — merge Gmail Sent (recipient-en  ·  back to Dw Pitch Followup  ·  pitch: letter panel shows product thumbnails + DW product-pa 9aa4cdc →