PDF Generation
Professional PDF document generation for quotes, comparisons, and reports
PDF Generation
The Esto platform provides powerful PDF generation capabilities for creating professional real estate documents including quotes, property comparisons, and detailed reports.
📄 Document Types
1. Property Quotes
Professional quotes with detailed property information and pricing.
2. Property Comparisons
Side-by-side property comparisons with feature analysis.
3. Contact Reports
Detailed client reports with preferences and history.
4. Marketing Materials
Brochures and flyers for property promotion.
🎨 Quote Generation
Basic Quote
// Generate a basic property quote
const quote = await client.documents.generateQuote({
propertyId: 'e_123456',
contactId: 'c_123456',
quoteDetails: {
validUntil: '2024-02-15',
specialConditions: 'Subject to financing approval',
agentName: 'Ahmed Benali',
agentPhone: '+213 123 456 789',
agentEmail: 'ahmed@esto.com',
},
});
console.log('Quote PDF URL:', quote.pdfUrl);Advanced Quote with Customization
// Generate a customized quote
const quote = await client.documents.generateQuote({
propertyId: 'e_123456',
contactId: 'c_123456',
quoteDetails: {
validUntil: '2024-02-15',
specialConditions: 'Subject to financing approval',
agentName: 'Ahmed Benali',
agentPhone: '+213 123 456 789',
agentEmail: 'ahmed@esto.com',
companyLogo: 'https://example.com/logo.png',
companyAddress: '123 Real Estate Street, Alger',
termsAndConditions: [
'Payment terms: 30% down payment',
'Closing costs: 3% of purchase price',
'Financing available through partner banks',
],
includedServices: ['Property inspection', 'Legal documentation', 'Title transfer assistance'],
},
customization: {
theme: 'modern', // 'modern', 'classic', 'minimal'
colorScheme: {
primary: '#7115d7',
secondary: '#f8f9fa',
accent: '#28a745',
},
includeImages: true,
includeFloorPlan: true,
includeNeighborhoodInfo: true,
},
});Quote Template Structure
<!-- Quote PDF Template -->
<div class="quote-container">
<!-- Header -->
<header class="quote-header">
<div class="company-info">
<img src="{{companyLogo}}" alt="Company Logo" />
<h1>Property Quote</h1>
<p>{{companyAddress}}</p>
</div>
<div class="quote-meta">
<p><strong>Quote Date:</strong> {{quoteDate}}</p>
<p><strong>Valid Until:</strong> {{validUntil}}</p>
<p><strong>Quote Number:</strong> {{quoteNumber}}</p>
</div>
</header>
<!-- Client Information -->
<section class="client-info">
<h2>Client Information</h2>
<div class="client-details">
<p><strong>Name:</strong> {{clientName}}</p>
<p><strong>Email:</strong> {{clientEmail}}</p>
<p><strong>Phone:</strong> {{clientPhone}}</p>
</div>
</section>
<!-- Property Information -->
<section class="property-info">
<h2>Property Details</h2>
<div class="property-details">
<h3>{{propertyTitle}}</h3>
<p><strong>Location:</strong> {{propertyLocation}}</p>
<p><strong>Price:</strong> {{propertyPrice}} DZD</p>
<p><strong>Surface Area:</strong> {{propertySurface}} sq.m</p>
<p><strong>Bedrooms:</strong> {{propertyBedrooms}}</p>
<p><strong>Bathrooms:</strong> {{propertyBathrooms}}</p>
</div>
<!-- Property Images -->
<div class="property-images">
{{#each propertyImages}}
<img src="{{this}}" alt="Property Image" />
{{/each}}
</div>
</section>
<!-- Price Breakdown -->
<section class="price-breakdown">
<h2>Price Breakdown</h2>
<table class="price-table">
<tr>
<td>Property Price</td>
<td>{{propertyPrice}} DZD</td>
</tr>
<tr>
<td>Agency Commission</td>
<td>{{commission}} DZD</td>
</tr>
<tr>
<td>Legal Fees</td>
<td>{{legalFees}} DZD</td>
</tr>
<tr class="total-row">
<td><strong>Total</strong></td>
<td><strong>{{totalPrice}} DZD</strong></td>
</tr>
</table>
</section>
<!-- Terms and Conditions -->
<section class="terms-conditions">
<h2>Terms and Conditions</h2>
<ul>
{{#each termsAndConditions}}
<li>{{this}}</li>
{{/each}}
</ul>
</section>
<!-- Agent Information -->
<section class="agent-info">
<h2>Contact Information</h2>
<div class="agent-details">
<p><strong>Agent:</strong> {{agentName}}</p>
<p><strong>Phone:</strong> {{agentPhone}}</p>
<p><strong>Email:</strong> {{agentEmail}}</p>
</div>
</section>
</div>🔄 Property Comparison Generation
Basic Comparison
// Generate property comparison
const comparison = await client.documents.generateComparison({
property1Id: 'e_123456',
property2Id: 'e_123457',
comparisonDetails: {
comparisonTitle: 'Hydra vs Bab Ezzouar Apartments',
includeImages: true,
includePriceAnalysis: true,
includeLocationAnalysis: true,
includeFeatureComparison: true,
},
});
console.log('Comparison PDF URL:', comparison.pdfUrl);Advanced Comparison
// Generate detailed property comparison
const comparison = await client.documents.generateComparison({
property1Id: 'e_123456',
property2Id: 'e_123457',
comparisonDetails: {
comparisonTitle: 'Hydra vs Bab Ezzouar Apartments',
includeImages: true,
includePriceAnalysis: true,
includeLocationAnalysis: true,
includeFeatureComparison: true,
includeNeighborhoodComparison: true,
includeInvestmentAnalysis: true,
customMetrics: ['Price per square meter', 'Distance to city center', 'School proximity', 'Transportation access'],
},
customization: {
theme: 'comparison',
layout: 'side-by-side', // 'side-by-side', 'table', 'cards'
colorScheme: {
property1: '#7115d7',
property2: '#28a745',
neutral: '#6c757d',
},
},
});Comparison Template Structure
<!-- Property Comparison Template -->
<div class="comparison-container">
<!-- Header -->
<header class="comparison-header">
<h1>{{comparisonTitle}}</h1>
<p>Generated on {{generationDate}}</p>
</header>
<!-- Property Overview -->
<section class="property-overview">
<div class="property property-1">
<h2>{{property1.title}}</h2>
<img src="{{property1.mainImage}}" alt="Property 1" />
<div class="property-highlights">
<p><strong>Price:</strong> {{property1.price}} DZD</p>
<p><strong>Surface:</strong> {{property1.surface}} sq.m</p>
<p><strong>Location:</strong> {{property1.location}}</p>
</div>
</div>
<div class="property property-2">
<h2>{{property2.title}}</h2>
<img src="{{property2.mainImage}}" alt="Property 2" />
<div class="property-highlights">
<p><strong>Price:</strong> {{property2.price}} DZD</p>
<p><strong>Surface:</strong> {{property2.surface}} sq.m</p>
<p><strong>Location:</strong> {{property2.location}}</p>
</div>
</div>
</section>
<!-- Comparison Table -->
<section class="comparison-table">
<h2>Detailed Comparison</h2>
<table class="comparison-table">
<thead>
<tr>
<th>Feature</th>
<th>{{property1.title}}</th>
<th>{{property2.title}}</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
{{#each comparisonFeatures}}
<tr>
<td>{{feature}}</td>
<td>{{property1Value}}</td>
<td>{{property2Value}}</td>
<td class="winner-{{winner}}">{{winner}}</td>
</tr>
{{/each}}
</tbody>
</table>
</section>
<!-- Price Analysis -->
<section class="price-analysis">
<h2>Price Analysis</h2>
<div class="price-metrics">
<div class="metric">
<h3>Price per sq.m</h3>
<p>Property 1: {{property1.pricePerSqm}} DZD/sq.m</p>
<p>Property 2: {{property2.pricePerSqm}} DZD/sq.m</p>
<p><strong>Difference:</strong> {{priceDifference}} DZD/sq.m</p>
</div>
</div>
</section>
<!-- Recommendation -->
<section class="recommendation">
<h2>Recommendation</h2>
<div class="recommendation-content">
<p>{{recommendation}}</p>
<ul>
{{#each recommendationReasons}}
<li>{{this}}</li>
{{/each}}
</ul>
</div>
</section>
</div>📊 Contact Report Generation
Basic Contact Report
// Generate contact report
const report = await client.documents.generateContactReport({
contactId: 'c_123456',
reportDetails: {
includePreferences: true,
includeHistory: true,
includeRecommendations: true,
includeNotes: true,
},
});
console.log('Contact Report PDF URL:', report.pdfUrl);Advanced Contact Report
// Generate detailed contact report
const report = await client.documents.generateContactReport({
contactId: 'c_123456',
reportDetails: {
includePreferences: true,
includeHistory: true,
includeRecommendations: true,
includeNotes: true,
includeInteractions: true,
includeMarketAnalysis: true,
includeBudgetAnalysis: true,
customSections: ['Recent Property Views', 'Saved Properties', 'Communication History'],
},
customization: {
theme: 'professional',
includeCharts: true,
includeTimeline: true,
},
});🎨 Customization Options
Theme Customization
// Available themes
const themes = {
modern: {
primaryColor: '#7115d7',
secondaryColor: '#f8f9fa',
accentColor: '#28a745',
fontFamily: 'Inter, sans-serif',
borderRadius: '8px',
shadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
},
classic: {
primaryColor: '#2c3e50',
secondaryColor: '#ecf0f1',
accentColor: '#e74c3c',
fontFamily: 'Georgia, serif',
borderRadius: '4px',
shadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
},
minimal: {
primaryColor: '#000000',
secondaryColor: '#ffffff',
accentColor: '#666666',
fontFamily: 'Helvetica, sans-serif',
borderRadius: '0px',
shadow: 'none',
},
};Layout Customization
// Available layouts
const layouts = {
'single-column': 'Vertical layout with sections',
'two-column': 'Side-by-side layout',
grid: 'Grid-based layout',
cards: 'Card-based layout',
timeline: 'Timeline layout for reports',
};Content Customization
// Content options
const contentOptions = {
includeImages: true,
includeCharts: true,
includeTables: true,
includeMaps: true,
includeQRCode: true,
includeWatermark: false,
includePageNumbers: true,
includeTableOfContents: true,
};🔧 Advanced Features
Batch PDF Generation
// Generate multiple PDFs in batch
const batchResults = await client.documents.generateBatch({
documents: [
{
type: 'quote',
propertyId: 'e_123456',
contactId: 'c_123456',
customization: { theme: 'modern' },
},
{
type: 'comparison',
property1Id: 'e_123456',
property2Id: 'e_123457',
customization: { theme: 'classic' },
},
{
type: 'contact_report',
contactId: 'c_123456',
customization: { theme: 'minimal' },
},
],
options: {
compress: true,
password: 'optional_password',
watermark: 'Esto Platform',
},
});
console.log('Batch generation completed:', batchResults);PDF Templates
// Create custom PDF template
const template = await client.documents.createTemplate({
name: 'Custom Quote Template',
type: 'quote',
html: `
<div class="custom-quote">
<header>
<h1>{{property.title}}</h1>
<p>{{property.price}} DZD</p>
</header>
<main>
<section class="property-details">
<h2>Property Details</h2>
<ul>
<li>Surface: {{property.surface}} sq.m</li>
<li>Bedrooms: {{property.bedrooms}}</li>
<li>Bathrooms: {{property.bathrooms}}</li>
</ul>
</section>
</main>
</div>
`,
css: `
.custom-quote {
font-family: Arial, sans-serif;
color: #333;
}
.custom-quote header {
background: #7115d7;
color: white;
padding: 20px;
}
`,
variables: ['property', 'client', 'agent'],
});
console.log('Template created:', template.id);PDF Watermarking
// Add watermarks to PDFs
const watermarkedPdf = await client.documents.addWatermark({
pdfUrl: 'https://example.com/document.pdf',
watermark: {
text: 'Esto Platform',
position: 'bottom-right',
opacity: 0.3,
fontSize: 12,
color: '#666666',
},
});📱 Mobile Optimization
Responsive PDFs
// Generate mobile-optimized PDFs
const mobilePdf = await client.documents.generateQuote({
propertyId: 'e_123456',
contactId: 'c_123456',
customization: {
theme: 'modern',
mobileOptimized: true,
fontSize: 'large',
imageSize: 'optimized',
layout: 'single-column',
},
});🔒 Security Features
Password Protection
// Generate password-protected PDF
const protectedPdf = await client.documents.generateQuote({
propertyId: 'e_123456',
contactId: 'c_123456',
security: {
password: 'client_password_123',
permissions: {
print: true,
copy: false,
modify: false,
},
},
});Digital Signatures
// Add digital signature to PDF
const signedPdf = await client.documents.addSignature({
pdfUrl: 'https://example.com/quote.pdf',
signature: {
name: 'Ahmed Benali',
title: 'Real Estate Agent',
certificate: 'agent_certificate.p12',
position: 'bottom-left',
reason: 'Property Quote Approval',
},
});📊 Analytics and Tracking
PDF Analytics
// Track PDF interactions
const analytics = {
trackPdfView: async (pdfId, userId) => {
await client.analytics.track({
event: 'pdf_viewed',
properties: {
pdfId,
userId,
timestamp: new Date().toISOString(),
},
});
},
trackPdfDownload: async (pdfId, userId) => {
await client.analytics.track({
event: 'pdf_downloaded',
properties: {
pdfId,
userId,
timestamp: new Date().toISOString(),
},
});
},
trackPdfShare: async (pdfId, userId, platform) => {
await client.analytics.track({
event: 'pdf_shared',
properties: {
pdfId,
userId,
platform,
timestamp: new Date().toISOString(),
},
});
},
};🧪 Testing PDF Generation
Unit Tests
describe('PDF Generation', () => {
test('should generate quote PDF', async () => {
const quote = await client.documents.generateQuote({
propertyId: 'test_property_id',
contactId: 'test_contact_id',
quoteDetails: {
validUntil: '2024-02-15',
agentName: 'Test Agent',
},
});
expect(quote.pdfUrl).toBeDefined();
expect(quote.pdfUrl).toContain('.pdf');
});
test('should generate comparison PDF', async () => {
const comparison = await client.documents.generateComparison({
property1Id: 'test_property_1',
property2Id: 'test_property_2',
});
expect(comparison.pdfUrl).toBeDefined();
});
});📚 Next Steps
- API Reference - Complete API documentation
- Data Models - Entity schemas and relationships
- Integration Guide - Implementation examples
- Testing - Testing strategies