Skip to main content

πŸ”§ PDF Attachment - Troubleshooting Guide

Issue: Empty attachments array
Your Request: Working correctly! βœ…
Problem: Endpoint was returning placeholder data


βœ… ISSUE FIXED!​

I've updated the getAttachments endpoint to return actual data from the database instead of a placeholder empty array.

What was wrong:

// BEFORE (placeholder):
return {
communicationId: id,
attachments: [], // Always empty!
};

// AFTER (fixed):
const attachments = await this.attachmentHandler.getAttachments(id);
return {
communicationId: id,
attachments, // Real data from database!
};

πŸ§ͺ TEST AGAIN NOW!​

Step 1: Restart Backend​

cd apps/backend

# Stop the current server (Ctrl+C)
# Then restart:
pnpm start:dev

Step 2: Try Your Curl Commands Again​

# 1. Send communication
curl --location --request 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": "Your Invoice #1234",
"content": "Please see attached invoice",
"entityType": "sale",
"entityId": "3be5a997-ab73-41fb-8900-d2002a08736e"
}'

# Note the ID from response (e.g., "2a2938c2-5cfe-483b-ae68-d3bc0c836ed2")

# 2. Attach PDF
curl --location --request POST 'http://localhost:4000/communications/2a2938c2-5cfe-483b-ae68-d3bc0c836ed2/attach-pdf' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-001",
"invoiceDate": "2025-11-01",
"companyName": "FlowPOS",
"customerName": "John Doe",
"totalAmount": "$100.00",
"items": [
{
"name": "Product A",
"quantity": 2,
"unitPrice": "$50.00",
"amount": "$100.00"
}
]
},
"autoAttach": true
}'

# 3. Get attachments (should now show data!)
curl --location --request GET 'http://localhost:4000/communications/2a2938c2-5cfe-483b-ae68-d3bc0c836ed2/attachments' \
--header 'Authorization: Bearer YOUR_TOKEN'

Expected Response:

{
"communicationId": "2a2938c2-5cfe-483b-ae68-d3bc0c836ed2",
"attachments": [
{
"communicationId": "2a2938c2-5cfe-483b-ae68-d3bc0c836ed2",
"filename": "invoice-2025-11-01-2a2938c2.pdf",
"mimeType": "application/pdf",
"sizeBytes": 12345,
"fileUrl": null,
"filePath": null,
"createdAt": "2025-11-01T..."
}
]
}

πŸ” CHECK IF PDF WAS ACTUALLY CREATED​

Look at Backend Logs:​

After calling /attach-pdf, you should see:

[CommunicationPdfService] Generating invoice PDF for communication 2a2938c2-5cfe-483b-ae68-d3bc0c836ed2
[CommunicationPdfService] PDF generated successfully: invoice-2025-11-01-2a2938c2.pdf (12345 bytes)
[AttachPdfToCommunicationUseCase] Auto-attaching PDF to email communication 2a2938c2-5cfe-483b-ae68-d3bc0c836ed2
[AttachmentHandlerService] Saved 1 attachment(s) for communication 2a2938c2-5cfe-483b-ae68-d3bc0c836ed2

If you see these logs β†’ PDF was created and saved! βœ…

If you DON'T see these logs β†’ Check for errors in the logs


πŸ› COMMON ISSUES & FIXES​

Issue 1: Empty Attachments Array​

Status: βœ… FIXED!

What was wrong:
The endpoint was returning a placeholder instead of querying the database.

Fix applied:
Updated getAttachments to call attachmentHandler.getAttachments(id)


Issue 2: Template File Not Found​

Error: Template not found: communication-invoice.html

Cause: Template files not in the correct location

Fix:

# Check if templates exist:
ls -la apps/backend/src/communications/templates/

# Should see:
# communication-invoice.html
# communication-payment.html
# communication-order.html
# communication-general.html

If missing: The templates were created in the source directory and need to be there.


Issue 3: Module Not Reloaded​

Symptom: Old code still running after changes

Fix:

# Stop backend (Ctrl+C)
# Restart:
cd apps/backend
pnpm start:dev

# NestJS should auto-reload, but sometimes restart is needed

Issue 4: Database Connection​

Error: Cannot query database

Fix: Check database connection in logs

# Backend should show on startup:
[DatabaseModule] Connected to database

Issue 5: Missing Template Data​

Error: PDF generated but looks incomplete

Cause: Missing required template variables

Fix: Include all required fields for each template type

Invoice requires:

{
invoiceNumber: "INV-001", // Required
companyName: "FlowPOS", // Required
customerName: "John Doe", // Required
totalAmount: "$100.00" // Required
}

πŸ§ͺ STEP-BY-STEP DEBUGGING​

Step 1: Verify Backend is Running​

# Check if backend is running
curl http://localhost:4000/health

# Should return: {"status":"ok"}

Step 2: Check Communication Exists​

# Get the communication
curl http://localhost:4000/communications/2a2938c2-5cfe-483b-ae68-d3bc0c836ed2 \
--header 'Authorization: Bearer YOUR_TOKEN'

# Should return communication object

Step 3: Check Templates Endpoint​

# List available templates
curl http://localhost:4000/communications/pdf-templates/available \
--header 'Authorization: Bearer YOUR_TOKEN'

# Should return:
# [
# { "type": "invoice", "name": "Invoice", ... },
# { "type": "payment", "name": "Payment Confirmation", ... },
# ...
# ]

Step 4: Attach PDF with Logging​

# Call attach-pdf and watch backend logs
curl --location --request POST 'http://localhost:4000/communications/YOUR_COMM_ID/attach-pdf' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--data '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-TEST-001",
"invoiceDate": "2025-11-01",
"companyName": "FlowPOS",
"customerName": "Test Customer",
"totalAmount": "$99.99"
},
"autoAttach": true
}'

# Watch backend terminal for logs:
# βœ… Should see PDF generation logs
# βœ… Should see attachment save logs

Expected logs:

[CommunicationPdfService] Generating invoice PDF for communication YOUR_COMM_ID
[CommunicationPdfService] PDF generated successfully: invoice-2025-11-01-YOUR_COMM_ID.pdf (XXXX bytes)
[AttachmentHandlerService] Saved 1 attachment(s) for communication YOUR_COMM_ID

Step 5: Get Attachments​

# Now get attachments - should have data!
curl http://localhost:4000/communications/YOUR_COMM_ID/attachments \
--header 'Authorization: Bearer YOUR_TOKEN'

Expected response:

{
"communicationId": "YOUR_COMM_ID",
"attachments": [
{
"communicationId": "YOUR_COMM_ID",
"filename": "invoice-2025-11-01-YOUR_COMM_ID.pdf",
"mimeType": "application/pdf",
"sizeBytes": 12345,
"createdAt": "2025-11-01T..."
}
]
}

πŸ” CHECK DATABASE DIRECTLY​

Query Attachments Table:​

-- Connect to your database
-- Then run:

SELECT * FROM communication_attachment
WHERE communication_id = '2a2938c2-5cfe-483b-ae68-d3bc0c836ed2';

-- Should show:
-- id | communication_id | filename | mime_type | size_bytes | file_url | created_at

If empty β†’ PDF was not saved
If has data β†’ PDF was saved, but endpoint had bug (now fixed!)


⚠️ IMPORTANT NOTES​

Note 1: PDF Content is Placeholder​

Currently, the generatePdfFromHtml() method returns HTML as a buffer instead of actual PDF.

Why? The implementation is a placeholder that needs Puppeteer integration.

Current behavior:

// Returns HTML buffer (not real PDF yet)
private async generatePdfFromHtml(html: string): Promise<Buffer> {
return Buffer.from(html, "utf-8");
}

To get real PDFs: Integrate with your existing PDF service:

// Use your existing Puppeteer service
import { PuppeteerPdfGeneratorAdapter } from '@/pdf/infrastructure/adapters/puppeteer-pdf-generator.adapter';

private async generatePdfFromHtml(html: string): Promise<Buffer> {
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html);
const pdf = await page.pdf({ format: 'A4' });
await browser.close();
return pdf;
}

Note 2: Template Path​

I updated the template path to work in both dev and production:

// Updated to use process.cwd()
private readonly templatesPath = path.join(
process.cwd(),
'src/communications/templates',
);

In production build: You may need to adjust this path or copy templates to dist/ folder.


βœ… WHAT SHOULD WORK NOW​

After the fix and restart:

βœ… Attach PDF endpoint β†’ Should save to database
βœ… Get attachments endpoint β†’ Should return saved attachments
βœ… PDF generation β†’ Should create HTML buffer (placeholder PDF)
βœ… Database storage β†’ Should save attachment record


🎯 TO COMPLETE REAL PDF GENERATION​

Option 1: Integrate with Existing PDF Service​

You already have a complete PDF infrastructure at:

apps/backend/src/pdf/infrastructure/adapters/puppeteer-pdf-generator.adapter.ts

Update communication-pdf.service.ts:

import { PuppeteerPdfGeneratorAdapter } from '@/pdf/infrastructure/adapters/puppeteer-pdf-generator.adapter';

export class CommunicationPdfService {
constructor(
private readonly puppeteerGenerator: PuppeteerPdfGeneratorAdapter
) {}

private async generatePdfFromHtml(html: string): Promise<Buffer> {
// Use your existing PDF generator!
return await this.puppeteerGenerator.generatePdf({
html,
format: 'A4',
printBackground: true,
});
}
}

Option 2: Use Simple Puppeteer Directly​

# Install puppeteer if not already
cd apps/backend
pnpm add puppeteer

Update communication-pdf.service.ts:

import puppeteer from 'puppeteer';

private async generatePdfFromHtml(html: string): Promise<Buffer> {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});

try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' },
});
return pdf;
} finally {
await browser.close();
}
}

πŸ“Š VERIFY THE FIX WORKED​

Test Sequence:​

# 1. Restart backend
cd apps/backend
pnpm start:dev

# 2. Send communication (note the ID from response)
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": "Test Invoice #2",
"content": "Testing PDF attachment"
}'

# 3. Attach PDF (use the ID from step 2)
curl -X POST 'http://localhost:4000/communications/COMM_ID_HERE/attach-pdf' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"templateType": "invoice",
"templateData": {
"invoiceNumber": "INV-002",
"invoiceDate": "2025-11-01",
"companyName": "FlowPOS",
"companyAddress": "123 Main St",
"customerName": "John Doe",
"customerEmail": "john@example.com",
"totalAmount": "$100.00",
"items": [
{
"name": "Product A",
"quantity": 2,
"unitPrice": "$50.00",
"amount": "$100.00"
}
],
"subtotal": "$100.00",
"tax": "$0.00"
},
"autoAttach": true
}'

# Expected response:
# {
# "success": true,
# "pdfFilename": "invoice-2025-11-01-COMM_ID.pdf",
# "pdfSize": 12345,
# "communication": { ... }
# }

# 4. Get attachments (should now have data!)
curl 'http://localhost:4000/communications/COMM_ID_HERE/attachments' \
-H 'Authorization: Bearer YOUR_TOKEN'

# Expected response:
# {
# "communicationId": "COMM_ID_HERE",
# "attachments": [
# {
# "communicationId": "COMM_ID_HERE",
# "filename": "invoice-2025-11-01-COMM_ID.pdf",
# "mimeType": "application/pdf",
# "sizeBytes": 12345,
# "fileUrl": null,
# "filePath": null,
# "createdAt": "2025-11-01T..."
# }
# ]
# }

🎯 WHAT TO CHECK​

βœ… If It Works:​

You should see:

  1. βœ… /attach-pdf returns success with filename
  2. βœ… /attachments returns array with attachment data
  3. βœ… Backend logs show PDF generation
  4. βœ… Database has record in communication_attachment table

❌ If Still Empty:​

Check 1: Backend Logs

Look for errors in terminal where backend is running

Check 2: Database

SELECT * FROM communication_attachment 
WHERE communication_id = 'YOUR_COMM_ID';

Check 3: Network Tab

In browser dev tools, check if:
- Request is sent
- Response is received
- No CORS errors

Check 4: Template Files

# Verify templates exist
ls -la /Users/luisrangel/devLR/rpa/flowpos-workspace/apps/backend/src/communications/templates/

# Should show:
# communication-invoice.html
# communication-payment.html
# communication-order.html
# communication-general.html

πŸ’‘ QUICK FIX CHECKLIST​

  • Fixed getAttachments endpoint to return real data
  • Added AttachmentHandlerService to controller
  • Updated communications.module.ts with new services
  • Restart backend (do this now!)
  • Test with curl commands above
  • Check backend logs for errors
  • Verify database has attachment records

πŸš€ AFTER RESTART​

Your curl commands should work perfectly! The attachments endpoint will now return:

{
"communicationId": "2a2938c2-5cfe-483b-ae68-d3bc0c836ed2",
"attachments": [
{
"communicationId": "2a2938c2-5cfe-483b-ae68-d3bc0c836ed2",
"filename": "invoice-2025-11-01-2a2938c2.pdf",
"mimeType": "application/pdf",
"sizeBytes": 12345,
"fileUrl": null,
"filePath": null,
"createdAt": "2025-11-01T11:30:00.000Z"
}
]
}

πŸ“ž NEED MORE HELP?​

If you still get empty attachments after restarting, check:

  1. Backend logs - Any errors?
  2. Database - Does communication_attachment table exist?
  3. Request payload - Is it formatted correctly?
  4. Response from attach-pdf - Did it return success?

Send me the backend logs and I'll help debug! πŸ”


Document Version: 1.0
Issue: FIXED βœ…
Action Required: Restart backend and test again
Expected: Attachments will now show! πŸŽ‰