Saltar al contenido principal

PDF Module — Architecture Reference

Last Updated: 2026-03-24 Module: apps/backend/src/pdf/ Pattern: Hexagonal Architecture (Ports & Adapters)


Module Structure

pdf/
├── pdf.module.ts # NestJS module definition
├── application/
│ ├── dtos/
│ │ ├── pdf-controller.dto.ts # PDF generation DTOs
│ │ └── template.dto.ts # Template management DTOs
│ ├── ports/
│ │ ├── pdf-generator.port.ts # PDF generation contract
│ │ └── template-renderer.port.ts # Template rendering contract
│ ├── services/
│ │ └── template-resolver.service.ts # Template resolution chain
│ ├── transformers/
│ │ ├── sale-data.transformer.ts # (21 transformers, one per document type)
│ │ └── ...
│ └── use-cases/
│ ├── base-generate-pdf.use-case.ts # Abstract base for all PDF generation
│ ├── generate-sale-pdf.use-case.ts # (21 concrete use cases)
│ ├── upload-template.use-case.ts # Template CRUD use cases
│ ├── update-template.use-case.ts
│ ├── delete-template.use-case.ts
│ ├── get-templates.use-case.ts
│ ├── test-template.use-case.ts
│ ├── preview-pdf.use-case.ts
│ ├── generate-custom-pdf.use-case.ts
│ └── *-location-template-config.use-case.ts # Location config CRUD
├── domain/
│ ├── exceptions/
│ │ └── template.exceptions.ts # TemplateNotFoundError, etc.
│ ├── ports/
│ │ ├── template-repository.port.ts # Repository contract + types
│ │ ├── location-template-config-repository.port.ts
│ │ └── template-audit.port.ts # Audit logging contract
│ ├── services/
│ │ └── template-validator.service.ts # Pure domain validation (no deps)
│ └── value-objects/
│ └── pdf-options.vo.ts # Page size, margins, orientation
└── infrastructure/
├── adapters/
│ ├── puppeteer-pdf-generator.adapter.ts # Headless Chrome PDF gen (retry logic)
│ ├── handlebars-template-renderer.adapter.ts # Handlebars compilation
│ ├── file-system-pdf-storage.adapter.ts # File system storage
│ ├── template-audit.adapter.ts # Kysely-based audit logging
│ └── template-cache.adapter.ts # LRU cache for compiled templates
├── controllers/
│ ├── pdf.controller.ts # POST /pdf/generate, /pdf/preview
│ ├── template.controller.ts # CRUD /pdf/templates/*
│ └── location-template-config.controller.ts # CRUD /pdf/location-template-configs/*
├── queues/
│ └── preview-generation.processor.ts # BullMQ async preview generation
├── repositories/
│ ├── template.repository.ts # Kysely impl of TemplateRepositoryPort
│ └── location-template-config.repository.ts
└── templates/
└── *.hbs # 18 Handlebars file-based templates (legacy)

Dependency Flow

Controllers → Use Cases → Application Services → Domain Ports

Infrastructure Adapters ──────────────────────────────┘

All cross-layer dependencies point inward toward the domain:

  • Domain: Zero framework dependencies. Ports (interfaces), exceptions, value objects, pure validators.
  • Application: Orchestration. Use cases depend on domain ports via injection. Contains DTOs, transformers, and the template resolver service.
  • Infrastructure: Concrete implementations. Adapters implement domain ports. Controllers handle HTTP. Repositories use Kysely.

Key Design Decisions

Template Resolution Chain

TemplateResolverService resolves templates in priority order:

  1. Explicit template ID — caller passes a specific template UUID
  2. Location-specificlocation_template_config for the location + document type
  3. Business-level — default template for the business + document type
  4. System-level — global system template for the document type
  5. Fallback — file-based Handlebars template from infrastructure/templates/

Base Use Case Pattern

All 21 PDF generation use cases extend BaseGeneratePdfUseCase<TEntity>:

  • Subclasses implement: getDocumentType(), getFallbackTemplateName(), getEntityBusinessId(), generateFilename()
  • Base class handles: template resolution, caching, PDF generation, error handling
  • Each use case has a paired DataTransformer that maps the entity to template data

Port Bindings (pdf.module.ts)

Injection TokenConcrete Implementation
"PdfGeneratorPort"PuppeteerPdfGeneratorAdapter
"TemplateRendererPort"HandlebarsTemplateRendererAdapter
"PdfStoragePort"FileSystemPdfStorageAdapter
TEMPLATE_REPOSITORY_PORTTemplateRepository
LOCATION_TEMPLATE_CONFIG_REPOSITORY_PORTLocationTemplateConfigRepository
TEMPLATE_AUDIT_PORTTemplateAuditAdapter

Puppeteer Retry Logic

PuppeteerPdfGeneratorAdapter uses executeWithRetry() (max 3 attempts) with exponential backoff for both generatePdf and generatePdfFromUrl. Retryable errors: protocol errors, target closed, navigation timeouts.

Template Cache

TemplateCacheAdapter uses an LRU cache (100 entries) for compiled Handlebars templates. Cache key = template UUID. Invalidated on template update/delete.


API Endpoints

PDF Generation (PdfController/pdf)

MethodPathDescription
POST/pdf/generateGenerate PDF from stored template + data
POST/pdf/generate-from-htmlGenerate PDF from raw HTML
POST/pdf/previewPreview template as rendered HTML
GET/pdf/template-filesList file-based templates (legacy)
POST/pdf/templates/clear-cacheClear file template cache
POST/pdf/templates/:templateId/reloadReload specific template
GET/pdf/healthService health check

Template Management (TemplateController/pdf/templates)

MethodPathDescription
POST/pdf/templates/uploadUpload new template (rate limited: 10/min)
GET/pdf/templatesList templates with pagination + search
GET/pdf/templates/availableAll templates (business + system)
GET/pdf/templates/healthHealth check with cache stats (public)
GET/pdf/templates/cache/statsCache statistics (public)
GET/pdf/templates/variables/:documentTypeTemplate variables for document type
GET/pdf/templates/:idGet template by ID
PATCH/pdf/templates/:idUpdate template
DELETE/pdf/templates/:idSoft-delete template
POST/pdf/templates/:id/testTest template with sample data → PDF
POST/pdf/templates/testTest template (alt endpoint)
POST/pdf/templates/previewPreview template rendering as HTML
GET/pdf/templates/:id/auditAudit history for template
POST/pdf/templates/cache/clearClear all template caches

Location Template Config (LocationTemplateConfigController/pdf/location-template-configs)

MethodPathDescription
POST/pdf/location-template-configsCreate config (rate limited: 20/min)
GET/pdf/location-template-configsGet configs by location (query: locationId)
GET/pdf/location-template-configs/by-document-typeGet by location + doc type
GET/pdf/location-template-configs/:idGet config by ID
PATCH/pdf/location-template-configs/:idUpdate config (rate limited: 30/min)
DELETE/pdf/location-template-configs/:idDelete config (rate limited: 20/min)

Supported Document Types

All types defined in DocumentType enum (packages/global/enums/document-type.enums.ts):

sale, purchase, purchase_order, quote, cash_register_session, cash_register_z_report, accounts_receivable_receipt, accounts_payable_payment, service_booking, inventory_adjustment, transfer_request, transfer_dispatch_note, transfer_goods_receipt, inventory_transfer, contractor_assignment, goods_received_note, material_consumption, production_run, credit_note, debit_note, cancellation


Bruno API Collections

API collections for testing are at: api-client/flowpos/collections/pdf/

pdf/
├── opencollection.yml
├── Generate PDF from Template.yml
├── Preview HTML (No PDF Generation).yml
├── Generate PDF from HTML Content.yml
├── Check health.yml
├── ...
├── templates/
│ ├── folder.yml
│ ├── templates.yml (list)
│ ├── template - upload.yml
│ ├── template - get by id.yml
│ ├── template - update.yml
│ ├── template - delete.yml
│ ├── template - test.yml
│ ├── template - test (alt).yml
│ ├── template - preview.yml
│ ├── template - available.yml
│ ├── template - variables.yml
│ ├── template - audit.yml
│ ├── health.yml
│ ├── cache stats.yml
│ └── Clear cache.yml
└── location-template-configs/
├── folder.yml
├── create config.yml
├── get by location.yml
├── get by location and type.yml
├── get by id.yml
├── update config.yml
└── delete config.yml