feat(po): email PO to vendor � PDF link in an Outlook draft (#14) #101
No reviewers
Labels
No labels
bug
claude-failed
claude-pr
claude-queue
claude-working
epic
feature
interactive
portal
triaged
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: shad0w/pelagia-portal#101
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/email-po-to-vendor"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #14.
Summary
Adds an Email to vendor button on the PO detail. It's available once the PO is approved (
MGR_APPROVED→CLOSED) — and again after payment — when the vendor has a primary-contact email. Clicking it opens an Outlook draft addressed to that contact with a time-limited PDF download link in the body. The user reviews and sends it.Per the answered design question, this uses the R2 link approach (a
mailto:can't attach a file): render the PO to a real PDF, store it, and link it.Pipeline
prepareVendorEmail(poId)(po/[id]/email-actions.ts) — auth + view check + approved-status + vendor-email checks.renderPoPdf→ PdfService (new standalone Express + Playwright microservice, the GstService/EpfoService pattern) renders the existing/api/po/[id]/export?format=pdf&pdf=1page to a real PDF via headless Chromium.po-pdf/…) → presigned 7-day download URL.mailto:(recipient + subject + body-with-link); the client opens it.Notes
svctoken (PDF_SERVICE_TOKEN) so PdfService can fetch the page without a user session (read-only, PDF only), andpdf=1strips the on-screen print button +window.print()auto-trigger. PdfService is SSRF-guarded (shared token + optionalALLOWED_ORIGIN).PDF_SERVICE_URL/PDF_SERVICE_TOKEN; if unset, the action returns a friendly "not configured" error (the rest of the app is unaffected).Deploy
Run PdfService alongside the app (like GstService/EpfoService):
cd PdfService && npm install && npm run dev(port 3005). SetPDF_SERVICE_URL,PDF_SERVICE_TOKEN, and (if PdfService can't reach the app viaNEXTAUTH_URL)APP_INTERNAL_URL. In production, R2 must be configured so the presigned link is reachable by the external vendor.Tests
email-vendor.test.ts(6): builds the mailto to the primary contact with the link, available post-payment, and refuses non-approved POs / missing vendor email / unconfigured service / unauthenticated callers (PdfService + storage mocked).tsc --noEmitclean (app + PdfService).🤖 Generated with Claude Code
Adds an "Email to vendor" button on the PO detail (available once approved, through CLOSED, and again after payment) that opens an Outlook draft addressed to the vendor's primary contact with a time-limited PDF download link. Since mailto: can't attach files, the PDF is rendered and stored, and the draft carries a link (the approach chosen for this issue): - PdfService/: new standalone Express + Playwright microservice (GstService/ EpfoService pattern) — POST /pdf { url } renders a page to a real PDF via headless Chromium. SSRF-guarded (shared token + optional origin allowlist). - export route: accepts a server-only `svc` token (PDF_SERVICE_TOKEN) so PdfService can fetch /api/po/[id]/export?format=pdf without a user session; `pdf=1` drops the print button + window.print() auto-trigger. - lib/pdf-service.ts renderPoPdf(); prepareVendorEmail() server action renders → uploads to R2 (po-pdf/…) → presigns a 7-day link → returns a mailto draft. - po-detail: EmailVendorButton, shown when approved + vendor has a contact email. - Gated by PDF_SERVICE_URL/PDF_SERVICE_TOKEN; friendly error if unconfigured. - No DB model/migration. Tests: prepareVendorEmail (6, PdfService/storage mocked). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>