feat(email): SMTP mailer behind the EmailService seam (EMAIL_PROVIDER=smtp); Resend stays the hosted default (#1746)
SmtpEmailService (nodemailer 9.0.5, exact-pinned) behind the existing EmailService seam. Provider resolution: EMAIL_PROVIDER wins, else RESEND_API_KEY selects Resend (hosted byte-identical), else SMTP_HOST selects SMTP. From header is built exactly like the Resend service after #1956 (no 'via <app>', fromAddress honored, platform-sender retry). STARTTLS is required by default (requireTLS) with SMTP_REQUIRE_TLS=false as an explicit opt-out for a plaintext LAN relay. Docs, env examples and the generated extension registry updated.
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
* failure never blocks the send; it surfaces as a PAYMENT_LINK_FAILED
|
||||
* warning on the response once the email is delivered.
|
||||
* 7. Final PDF render with the real number.
|
||||
* 8. Email send via Resend (the email extension). Fail → 502
|
||||
* 8. Email send via the email extension (Resend or SMTP). Fail → 502
|
||||
* INVOICE_SEND_PROVIDER_FAILED. The number IS consumed at this point;
|
||||
* same orphan-window as :mark-sent (architecturally tracked).
|
||||
* 9. POINT OF NO RETURN. Steps below are best-effort; failures surface
|
||||
@@ -123,14 +123,14 @@ registerEndpoint({
|
||||
path: '/api/v1/companies/:companyId/invoices/:id/send',
|
||||
summary: 'Send a draft invoice to the customer by email.',
|
||||
description:
|
||||
'The full send pipeline: preflight PDF render → allocate F-series number atomically → final PDF render → email via Resend (PDF attachment, copy to company) → flip status to sent → post journal entry (real invoice, unless kontantmetoden or defer_invoice_booking) → archive PDF as underlag → emit invoice.sent. Email failure is a hard 502 before state changes; post-email failures surface as warnings but the invoice IS marked sent.',
|
||||
'The full send pipeline: preflight PDF render → allocate F-series number atomically → final PDF render → email via the email extension (Resend or SMTP; PDF attachment, copy to company) → flip status to sent → post journal entry (real invoice, unless kontantmetoden or defer_invoice_booking) → archive PDF as underlag → emit invoice.sent. Email failure is a hard 502 before state changes; post-email failures surface as warnings but the invoice IS marked sent.',
|
||||
useWhen:
|
||||
'You want Accounted to deliver the invoice to the customer via email. For invoices delivered through another channel (Peppol, postal, own SMTP) use :mark-sent instead.',
|
||||
doNotUseFor:
|
||||
'Re-sending an already-sent invoice (returns 409 INVOICE_UPDATE_NOT_DRAFT). Sending a delivery note (no F-series lifecycle). Sending a credit note (use the :credit endpoint to issue the kreditfaktura; subsequent re-send of the credit note via :mark-sent is the supported path).',
|
||||
pitfalls: [
|
||||
'Idempotency-Key is mandatory.',
|
||||
'Email service must be configured: without RESEND_API_KEY + RESEND_FROM_EMAIL the endpoint returns 503 INVOICE_SEND_EMAIL_NOT_CONFIGURED.',
|
||||
'Email service must be configured: without RESEND_API_KEY + RESEND_FROM_EMAIL (or an SMTP relay via EMAIL_PROVIDER=smtp) the endpoint returns 503 INVOICE_SEND_EMAIL_NOT_CONFIGURED.',
|
||||
'Customer must have an email address. 400 INVOICE_SEND_NO_CUSTOMER_EMAIL otherwise.',
|
||||
'A cancelled invoice is rejected (400 INVOICE_SEND_CANCELLED): its F-series number is preserved for compliance but the document is not a valid faktura.',
|
||||
'Email failure before the status flip leaves the F-series number consumed but the invoice in `draft` status. Same orphan window as :mark-sent (architecturally tracked, matches internal route).',
|
||||
|
||||
Reference in New Issue
Block a user