Skip to content

Requirements: Direct Email Send (Email Orders)

Functional requirements for the front-end direct-email-send feature (PDEV-969). These describe observable system behavior once implemented — not tasks, coding style, or tests (those live in specification.md, verification.md, and implementation-changes.md). Rationale and contract detail are in design.md; the codebase gap is in ui-implementation-analysis.md.

  • REQ-ES-001 — When the active tenant has an EmailConfiguration in Operational state whose identity.sendingDomainSlug contains the PROCUREMENT_EMAIL_SLUG_TOKEN ("procurement"), the Email Order composer presents the full direct-send form. Otherwise it presents the restricted copy-only form (DQ-001).
  • REQ-ES-002 — The toggle is resolved on composer open via the BFF config-status route (a quick configuration/query); it is not cached for the initial implementation (neither SPA nor BFF). If caching is ever needed it lives in the BFF route, never the SPA (DQ-002, revised). A failed lookup resolves to not enabled (restricted form).
  • REQ-ES-003 — In the restricted form there is no Send action; Copy to clipboard is the primary action, and the UI explains that direct send is unavailable (provisioning tracked by PDEV-971).
  • REQ-ES-010 — In the full form the user can edit: To, Cc, From recipients (chip fields); Subject; greeting; introduction line; each line item’s quantity, unit price, and per-line Notes (REQ-ES-062); an optional order-wide note; and the sign-off.
  • REQ-ES-011 — For V1, To starts empty with a placeholder cue, and Cc is seeded with the logged-in user’s email (self-CC, DQ-012); From defaults to the same address. Pre-population of recipients from supplier data is out of scope (PDEV-977).
  • REQ-ES-012Subject is editable, defaulting to Order for {supplier} — {MMM d, yyyy} in the en-US locale (DQ-003).
  • REQ-ES-013 — The UI From value is sent as the message Reply-To (replyToEmail); the actual From is fixed by the configuration’s sender. The field label remains “From” (DQ-004).
  • REQ-ES-014Revert all restores every field to its default and is shown only when at least one field has diverged. ⌘Z / Ctrl+Z undoes the most recent recipient change.
  • REQ-ES-015 — An empty note contributes nothing to the sent or copied output; a non-empty note appears under a “Notes” heading.
  • REQ-ES-020Send sends the format of the active Body preview (HTML Preview → HTML, Plain Text Preview → plain text); the Send split button’s menu offers Direct Send and Open in email (DQ-013/DQ-014, revising DQ-011). Send is enabled only in a preview mode and fires on ⌘↵ / Ctrl+↵.
  • REQ-ES-021 — An HTML send populates htmlBody (with a plain-text textBody alternate); a plain-text send populates textBody only.
  • REQ-ES-022 — The send is performed server-side via POST /v1/shop-access/email/job with required X-Tenant-Id (from the session) and a client-minted Idempotency-Key that is stable across retries of one send attempt (DQ-007). The send is idempotent on that key.
  • REQ-ES-023 — The request carries configurationEId (from the cached toggle), recipients { to, cc, bcc: [] }, subject, the body per the chosen format, replyToEmail, and attachments: [] (DQ-009).
  • REQ-ES-024 — On a successful send the selected order-queue cards are accepted (the existing event/acceptmarkItemStale → refresh → close flow); on failure no card is accepted and the user sees an actionable error.
  • REQ-ES-030Copy to clipboard writes both a rich-HTML (text/html) and a plain-text (text/plain) representation of the body to the clipboard, matching today’s EmailPanel (REQ parity, DQ-011).
  • REQ-ES-040 — Before any send the front-end validates: ≥ 1 To address; all To/Cc/Reply-To addresses RFC-valid; non-empty subject; and no CR/LF or control characters in addresses, subject, or Reply-To. A failure blocks the send and surfaces a specific error (DQ-006).
  • REQ-ES-041 — User-entered content is HTML-escaped at compose time, and composition runs in the BFF via the shared compose-email-html util (DQ-020): the SPA sends structured content and uses the same util only for live preview; the BFF composes the authoritative body. This keeps the sent body structurally safe (DQ-006).
  • REQ-ES-042 — The shared BFF send route runs the composed htmlBody through sanitize-html (allow-list: safe formatting tags; http/https/mailto links only; no images; no event handlers/<script>/<style>/<iframe>) and sends the sanitized result — defense-in-depth over composition’s escaping; no 400-reject (DQ-010, revised R7). Backend-side hardening is tracked by PDEV-976.
  • REQ-ES-043 — The BFF send/config routes never log recipients or body content (extends the no-header-logging guardrail).
  • REQ-ES-050 — Send and Copy outcomes are confirmed with a toast; the Send toast names the format (e.g. “Email sent (HTML)”). Backend error states (not-sendable, suppressed recipient, validation) are surfaced as actionable messages rather than failing opaquely.

Composer ergonomics (stakeholder review — Round 5)

Section titled “Composer ergonomics (stakeholder review — Round 5)”
  • REQ-ES-060Reusable shell (DQ-016). The composer is decomposed so the recipient fields, panel shell, Body view toggle, and send/format/Open-in-email controls are reusable by other email features (e.g. invitations); the Body content is the feature-specific slot. Reuse candidates follow the ui-implementation-analysis.md §3 procedure and the ux-prototype promotion ticket.
  • REQ-ES-061Grid-consistent inline edit (DQ-017). Editable fields use the AG-grid inline-edit pattern (click the value to edit, hover highlight, Enter commit / Esc cancel) — no pencil-badge affordance — matching Items / Vendor / PO-Lite. Per-field revert and global Revert all / ⌘Z are retained.
  • REQ-ES-062Per-line Notes (DQ-018). The items table has an editable per-line Notes column for vendor specs, rendered into both the HTML body (column) and the plain-text body (indented Note: sub-line). The order-wide global Note field is retained.
  • REQ-ES-063Last format = session default (DQ-019). The chosen send format (HTML vs plain text) persists as the session default: after the first send the composer opens on the last-used preview rather than resetting to Compose. Client-only session state; no backend.
  • Recipient pre-population from supplier data (separate ticket).
  • Authorization/permission gating of the send action (handled separately).
  • Per-send From override / multi-From (PDEV-903), attachments and BCC, PO-Line order emails (PDEV-970), and provisioning configurations (PDEV-971).