Saltar al contenido principal

✅ PDF Attachments - CORRECT WORKFLOW

You're absolutely right! It doesn't make sense to attach a PDF to an email AFTER it's been sent!

Correct approach: Generate PDF first, then include it in the send request.


🎯 THE RIGHT WAY TO DO IT

Two-Step Workflow:

1. Generate PDF → Get base64 content
2. Send email → Include PDF in attachments

Step 1: Generate PDF

curl -X POST 'http://localhost:4000/communications/generate-pdf' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-12350",
"invoiceDate": "2025-11-01",
"companyName": "RPA Solution",
"companyAddress": "Your Address",
"companyEmail": "info@rpasolution.com",
"customerName": "John Doe",
"customerEmail": "luisrangelc@gmail.com",
"totalAmount": "$550.00",
"items": [
{
"name": "Product A",
"quantity": 2,
"unitPrice": "$100.00",
"amount": "$200.00"
},
{
"name": "Product B",
"quantity": 3,
"unitPrice": "$116.67",
"amount": "$350.00"
}
],
"subtotal": "$550.00",
"tax": "$0.00",
"paymentTerms": "Net 30",
"notes": "Thank you for your business!"
}
}'

Response:

{
"filename": "invoice-2025-11-01-temp1730123456.pdf",
"content": "JVBERi0xLjQK...BASE64_PDF_CONTENT...",
"mimeType": "application/pdf",
"sizeBytes": 45678
}

Save the content value! You'll use it in step 2.


Step 2: Send Email with PDF

curl -X POST 'http://localhost:4000/communications/send' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data-raw '{
"businessId": "33b6db4b-51c5-45ee-8d04-c01c2d157f66",
"createdBy": "6c0c4f32-d74a-4a84-892f-3bead447d765",
"channel": "email",
"type": "invoice",
"recipientType": "customer",
"recipientId": "824e2bec-8402-4846-b974-1fd543fa2cb1",
"recipientContact": "luisrangelc@gmail.com",
"subject": "Invoice #12350 - WITH PDF!",
"content": "<h2>Your Invoice is Ready!</h2><p>Dear Customer,</p><p>Please find your invoice <strong>attached as PDF</strong>.</p><p>Thank you for your business!</p>",
"attachments": [
{
"filename": "invoice-12350.pdf",
"content": "PASTE_THE_BASE64_CONTENT_FROM_STEP_1_HERE",
"mimeType": "application/pdf"
}
]
}'

Result: Email sent with PDF attachment! 📎✅


💻 FROM BACKEND CODE

In Your NestJS Service:

import { Injectable } from '@nestjs/common';
import { CommunicationsService } from '@/communications/application/communications.service';
import { GenerateCommunicationPdfUseCase } from '@/communications/application/use-cases/generate-communication-pdf.use-case';

@Injectable()
export class InvoiceService {
constructor(
private readonly communicationsService: CommunicationsService,
private readonly generatePdfUseCase: GenerateCommunicationPdfUseCase,
) {}

async sendInvoiceEmail(invoice: Invoice) {
// Step 1: Generate PDF
const pdf = await this.generatePdfUseCase.execute({
templateType: 'invoice',
templateData: {
invoiceNumber: invoice.number,
invoiceDate: this.formatDate(invoice.date),
dueDate: this.formatDate(invoice.dueDate),
companyName: invoice.business.name,
companyAddress: invoice.business.address,
companyEmail: invoice.business.email,
customerName: invoice.customer.name,
customerEmail: invoice.customer.email,
items: invoice.items.map(item => ({
name: item.product.name,
quantity: item.quantity,
unitPrice: `$${item.unitPrice.toFixed(2)}`,
amount: `$${item.total.toFixed(2)}`
})),
subtotal: `$${invoice.subtotal.toFixed(2)}`,
tax: `$${invoice.tax.toFixed(2)}`,
totalAmount: `$${invoice.total.toFixed(2)}`,
paymentTerms: 'Net 30'
}
});

// Step 2: Send email with PDF
return await this.communicationsService.send({
businessId: invoice.businessId,
createdBy: invoice.createdBy,
recipientType: 'customer',
recipientId: invoice.customerId,
recipientContact: invoice.customer.email,
channel: 'email',
type: 'invoice',
subject: `Invoice ${invoice.number} from ${invoice.business.name}`,
content: `
<h2>Invoice ${invoice.number}</h2>
<p>Dear ${invoice.customer.name},</p>
<p>Please find your invoice attached as PDF.</p>
<p><strong>Amount Due: $${invoice.total.toFixed(2)}</strong></p>
<p>Due Date: ${this.formatDate(invoice.dueDate)}</p>
<p>Thank you for your business!</p>
`,
attachments: [
{
filename: pdf.filename,
content: pdf.content, // Base64 from step 1
mimeType: pdf.mimeType
}
]
});
}
}

🎨 FROM FRONTEND (React/TypeScript)

In Your Frontend Service:

// Step 1: Add PDF generation function
export const generatePdf = async (
templateType: 'invoice' | 'payment' | 'order' | 'general',
templateData: Record<string, unknown>
) => {
const response = await api.post('/communications/generate-pdf', {
templateType,
templateData
});
return response.data;
};

// Step 2: Use in your component
const handleSendInvoiceWithPdf = async (invoice: Invoice) => {
try {
// Generate PDF
const pdf = await generatePdf('invoice', {
invoiceNumber: invoice.number,
invoiceDate: formatDate(invoice.date),
companyName: 'RPA Solution',
customerName: invoice.customerName,
totalAmount: formatCurrency(invoice.total),
items: invoice.items.map(item => ({
name: item.name,
quantity: item.quantity,
unitPrice: formatCurrency(item.price),
amount: formatCurrency(item.total)
}))
});

// Send email with PDF
await sendCommunication({
recipientContact: invoice.customerEmail,
channel: 'email',
type: 'invoice',
subject: `Invoice ${invoice.number}`,
content: '<p>Your invoice is attached.</p>',
attachments: [
{
filename: pdf.filename,
content: pdf.content,
mimeType: pdf.mimeType
}
]
});

toast.success('Invoice sent with PDF!');
} catch (error) {
toast.error('Failed to send invoice');
}
};

🔄 COMPLETE FLOW DIAGRAM

Frontend/API Call

1. POST /communications/generate-pdf
{
templateType: "invoice",
templateData: { ... }
}

Returns: { filename, content (base64), mimeType }

2. POST /communications/send
{
subject: "Invoice #123",
content: "Invoice attached",
attachments: [
{
filename: "invoice.pdf",
content: "BASE64_FROM_STEP_1", ← Include PDF here!
mimeType: "application/pdf"
}
]
}

Email Adapter (SendGrid)

Email Sent WITH PDF Attachment! ✅

Customer Receives Email with PDF! 📧📎

🎯 NEW ENDPOINT EXPLAINED

POST /communications/generate-pdf ✨ NEW!

Purpose: Generate a PDF that you can then include in send request

Request:

{
"templateType": "invoice",
"templateData": { ... }
}

Response:

{
"filename": "invoice-2025-11-01-temp123.pdf",
"content": "JVBERi0xLjQK...base64...",
"mimeType": "application/pdf",
"sizeBytes": 45678
}

Then use the content in:

{
"attachments": [
{
"filename": "invoice.pdf",
"content": "PASTE_CONTENT_HERE", ← From generate-pdf response
"mimeType": "application/pdf"
}
]
}

🧪 COMPLETE TEST EXAMPLE

Test the Correct Workflow:

# Terminal 1: Generate PDF
PDF_RESPONSE=$(curl -X POST 'http://localhost:4000/communications/generate-pdf' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-12350",
"invoiceDate": "2025-11-01",
"companyName": "RPA Solution",
"customerName": "Test Customer",
"totalAmount": "$100.00"
}
}')

# Extract base64 content
PDF_CONTENT=$(echo $PDF_RESPONSE | jq -r '.content')

# Terminal 2: Send with PDF
curl -X POST 'http://localhost:4000/communications/send' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d "{
\"businessId\": \"33b6db4b-51c5-45ee-8d04-c01c2d157f66\",
\"createdBy\": \"6c0c4f32-d74a-4a84-892f-3bead447d765\",
\"channel\": \"email\",
\"type\": \"invoice\",
\"recipientType\": \"customer\",
\"recipientContact\": \"luisrangelc@gmail.com\",
\"subject\": \"Invoice #12350 WITH PDF!\",
\"content\": \"<p>Invoice attached as PDF</p>\",
\"attachments\": [
{
\"filename\": \"invoice-12350.pdf\",
\"content\": \"$PDF_CONTENT\",
\"mimeType\": \"application/pdf\"
}
]
}"

📊 WHAT ABOUT /attach-pdf?

When to Use /attach-pdf:

DON'T USE for normal workflow - Email already sent
DO USE for: Saving PDF records to database for existing communications
DO USE for: Tracking what PDFs were generated
DO USE for: Historical record-keeping

It's more of an archive/tracking feature, not for sending!


🎊 UPDATED API DESIGN

New Endpoints:

POST /communications/generate-pdf          ← Generate PDF, get base64
POST /communications/send ← Send with attachments
POST /communications/:id/attach-pdf ← Save PDF record (archive)
GET /communications/:id/attachments ← View PDF records
GET /communications/pdf-templates ← List templates

🚀 TRY THIS NOW

Simple Test with Minimal Data:

# Step 1: Generate PDF
curl -X POST 'http://localhost:4000/communications/generate-pdf' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-TEST",
"invoiceDate": "2025-11-01",
"companyName": "RPA Solution",
"customerName": "Test Customer",
"totalAmount": "$99.99"
}
}' > pdf-response.json

# Check the response
cat pdf-response.json | jq '.'

# You'll see:
# {
# "filename": "invoice-2025-11-01-temp...pdf",
# "content": "JVBERi0xLjQK...", ← Copy this!
# "mimeType": "application/pdf",
# "sizeBytes": 12345
# }

# Step 2: Copy the "content" value and use it in send
# (Or use jq to extract it automatically as shown in complete example above)

💡 EVEN SIMPLER: DIRECT ATTACHMENT

If you already have a PDF file, just send it directly:

# If you have a PDF file locally:
BASE64_PDF=$(base64 -i your-invoice.pdf)

# Send directly
curl -X POST 'http://localhost:4000/communications/send' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d "{
\"businessId\": \"33b6db4b-51c5-45ee-8d04-c01c2d157f66\",
\"createdBy\": \"6c0c4f32-d74a-4a84-892f-3bead447d765\",
\"channel\": \"email\",
\"type\": \"invoice\",
\"recipientContact\": \"luisrangelc@gmail.com\",
\"subject\": \"Invoice #12350\",
\"content\": \"Invoice attached\",
\"attachments\": [
{
\"filename\": \"invoice.pdf\",
\"content\": \"$BASE64_PDF\",
\"mimeType\": \"application/pdf\"
}
]
}"

Create a Unified Service Method:

@Injectable()
export class InvoiceEmailService {
constructor(
private readonly generatePdfUseCase: GenerateCommunicationPdfUseCase,
private readonly communicationsService: CommunicationsService,
) {}

/**
* Send invoice email with PDF attachment (all in one!)
*/
async sendInvoiceWithPdf(invoice: Invoice): Promise<Communication> {
// 1. Generate PDF
const pdf = await this.generatePdfUseCase.execute({
templateType: 'invoice',
templateData: this.buildInvoiceData(invoice)
});

// 2. Send with PDF attached
return await this.communicationsService.send({
businessId: invoice.businessId,
createdBy: invoice.createdBy,
recipientType: 'customer',
recipientId: invoice.customerId,
recipientContact: invoice.customerEmail,
channel: 'email',
type: 'invoice',
subject: `Invoice ${invoice.number} from ${invoice.business.name}`,
content: this.buildEmailContent(invoice),
attachments: [
{
filename: pdf.filename,
content: pdf.content,
mimeType: pdf.mimeType
}
]
});
}

private buildInvoiceData(invoice: Invoice) {
return {
invoiceNumber: invoice.number,
invoiceDate: formatDate(invoice.date),
dueDate: formatDate(invoice.dueDate),
companyName: invoice.business.name,
companyAddress: invoice.business.address,
customerName: invoice.customer.name,
customerEmail: invoice.customer.email,
items: invoice.items.map(item => ({
name: item.product.name,
quantity: item.quantity,
unitPrice: formatCurrency(item.unitPrice),
amount: formatCurrency(item.total)
})),
subtotal: formatCurrency(invoice.subtotal),
tax: formatCurrency(invoice.tax),
totalAmount: formatCurrency(invoice.total),
paymentTerms: 'Net 30'
};
}

private buildEmailContent(invoice: Invoice): string {
return `
<h2>Invoice ${invoice.number}</h2>
<p>Dear ${invoice.customer.name},</p>
<p>Please find your invoice attached as PDF.</p>
<p><strong>Amount Due: $${invoice.total.toFixed(2)}</strong></p>
<p>Due Date: ${formatDate(invoice.dueDate)}</p>
<p>Thank you for your business!</p>
`;
}
}

// Usage:
await invoiceEmailService.sendInvoiceWithPdf(invoice);
// ✅ Generates PDF and sends email in one call!

📋 API SUMMARY (UPDATED)

EndpointPurposeWhen to Use
POST /communications/generate-pdfGenerate PDF, get base64Before sending
POST /communications/sendSend with attachmentsSend email with PDF
POST /communications/:id/attach-pdfSave PDF recordArchive/tracking only
GET /communications/:id/attachmentsView PDF recordsSee what was attached

CORRECTED WORKFLOW

Before (Wrong):

1. Send email ❌
2. Attach PDF ❌ (email already sent!)
3. ??? (can't modify sent email)

After (Correct):

1. Generate PDF ✅
2. Get base64 content ✅
3. Send email with PDF in attachments ✅
4. Customer receives email with PDF! ✅

🎊 TRY IT NOW!

Restart backend (to get the new endpoint), then:

# 1. Generate PDF
curl -X POST 'http://localhost:4000/communications/generate-pdf' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-NEW",
"companyName": "RPA Solution",
"customerName": "Your Name",
"totalAmount": "$99.99"
}
}'

# Copy the "content" from response

# 2. Send with that PDF
curl -X POST 'http://localhost:4000/communications/send' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
...
"attachments": [{
"filename": "invoice.pdf",
"content": "PASTE_HERE",
"mimeType": "application/pdf"
}]
}'

# 3. Check your Gmail - PDF attached! 📎

🏆 SUMMARY

You were right! The workflow needed to be redesigned.

New design:

  • generate-pdf → Returns PDF base64
  • send → Include PDF in attachments
  • ✅ Makes logical sense!
  • ✅ Works like it should!

Old design issues:

  • ❌ attach-pdf after send (doesn't work)
  • ❌ Can't modify sent emails
  • ❌ Confusing workflow

Now it's perfect! 🎉


Restart backend and try the new /generate-pdf endpoint! 🚀