Template Resolver - Location Configuration Integration
Overviewβ
The TemplateResolverService now implements the complete template resolution hierarchy with location-specific configurations.
Implementation Dateβ
October 21, 2025
Resolution Orderβ
The template resolver follows a waterfall pattern with three levels:
1. Location-Specific Template (if locationId provided)
β (if not found or inactive)
2. Business Default Template
β (if not found or inactive)
3. System Default Template
β (if not found)
4. Throw TemplateNotFoundError
How It Worksβ
1. Location-Specific Resolutionβ
When: locationId is provided and location config exists
Steps:
- Query
location_template_configtable bylocationId+documentType - Verify config is active (
isActive = true) - Fetch the configured template by
documentTemplateId - Verify template is active (
isActive = true) - Merge printer configuration from location settings
- Return with
source: "location"
Printer Config Merging:
const mergedPrinterConfig = {
...printerConfig, // Base config (if any)
autoPrint: locationConfig.autoPrint, // From location settings
copies: locationPrinterConfig.copies || 1, // From JSONB
...locationPrinterConfig // Any other printer settings
};
2. Business Default Resolutionβ
When: No location config found or location config inactive
Steps:
- Query
document_templatetable for business default - Filter:
documentType+businessId+isDefault = true+isActive = true - Return with
source: "business"
3. System Default Resolutionβ
When: No business default found
Steps:
- Query
document_templatetable for system default - Filter:
documentType+businessId IS NULL+isDefault = true+isActive = true - Return with
source: "system"
4. No Template Foundβ
When: All resolution attempts fail
Result: Throws TemplateNotFoundError
Code Changesβ
Updated Filesβ
File: apps/backend/src/pdf/domain/services/template-resolver.service.ts
Changes:
- β
Imported
LocationTemplateConfigRepository - β Injected repository in constructor
- β Added location resolution logic (step 1)
- β Merged printer config from location settings
- β Updated numbering in comments
- β Used optional chaining for cleaner code
Integration Pointsβ
PDF Generation Use Casesβ
All PDF generation use cases use the template resolver:
// In generate-sale-pdf.use-case.ts
const resolution = await this.templateResolver.resolveTemplate(
DocumentType.SALE,
sale.businessId,
sale.locationId, // π Location ID enables location-specific templates
);
// Returns:
// - template: The resolved template
// - pdfOptions: PDF generation options
// - printerConfig: Merged printer settings
// - source: "location" | "business" | "system"
Location Configuration Effectβ
When a location has a configured template:
// Location config exists:
{
locationId: "abc-123",
documentType: "sale",
documentTemplateId: "template-xyz",
autoPrint: true,
printerConfig: { copies: 2, printerProfileId: "printer-1" },
isActive: true
}
// Resolved result:
{
template: { id: "template-xyz", ... },
pdfOptions: { ... },
printerConfig: {
autoPrint: true,
copies: 2,
printerProfileId: "printer-1"
},
source: "location"
}
Printer Configurationβ
The printer config from location settings includes:
autoPrint- Whether to auto-print after generationcopies- Number of copies to print (stored in JSONB)printerProfileId- Specific printer to use (optional)- Custom settings - Any additional printer-specific settings in JSONB
Example Scenariosβ
Scenario 1: Location Has Custom Templateβ
Request: Generate sale PDF for location "Store-A"
Document Type: sale
Business ID: business-123
Location ID: Store-A
Resolution Flow:
1. β
Check location config β Found config for Store-A + sale
2. β
Load template from config β Template "Store-A Receipt"
3. β
Merge printer config β { autoPrint: true, copies: 2 }
4. β Return: source="location"
Result: Uses "Store-A Receipt" template with auto-print enabled
Scenario 2: No Location Configβ
Request: Generate sale PDF for location "Store-B"
Document Type: sale
Business ID: business-123
Location ID: Store-B
Resolution Flow:
1. β Check location config β Not found
2. β
Check business default β Found "Business Sale Template"
3. β Return: source="business"
Result: Uses business-wide default template
Scenario 3: New Business, No Custom Templatesβ
Request: Generate sale PDF
Document Type: sale
Business ID: new-business-456
Location ID: location-1
Resolution Flow:
1. β Check location config β Not found
2. β Check business default β Not found
3. β
Check system default β Found "Standard Sale Template"
4. β Return: source="system"
Result: Uses built-in system template
Scenario 4: Location Config Inactiveβ
Request: Generate sale PDF
Document Type: sale
Business ID: business-123
Location ID: Store-C
Resolution Flow:
1. β οΈ Check location config β Found but isActive=false
2. β
Check business default β Found
3. β Return: source="business"
Result: Skips inactive location config, uses business default
Benefitsβ
1. Flexibilityβ
- Each location can have unique templates
- Different receipts for different stores
- Location-specific branding
2. Fallback Safetyβ
- Always has a template to use
- Graceful degradation
- No broken PDFs
3. Printer Controlβ
- Location-specific printer settings
- Auto-print configuration per location
- Copy count per location
4. Performanceβ
- Single query to check location config
- Efficient template caching
- Usage tracking for analytics
5. Maintainabilityβ
- Clear resolution hierarchy
- Easy to debug (check source field)
- Comprehensive logging
Usage in Controllersβ
Sale Controller Exampleβ
// In sales.controller.ts
@Post(':id/generate-pdf')
async generatePdf(
@Param('id') saleId: string,
@Body() options: { locationId?: string }
) {
return this.pdfService.generateSalePdf(
saleId,
options.locationId // Pass location for custom template
);
}
PDF Service Integrationβ
The generate use cases automatically use the resolver:
// In generate-sale-pdf.use-case.ts
const resolution = await this.templateResolver.resolveTemplate(
DocumentType.SALE,
sale.businessId,
sale.locationId // Automatically checks location config
);
// Use the resolved template
const pdfBuffer = await this.generatePdf(
resolution.template,
saleData,
resolution.pdfOptions
);
// Handle auto-print if configured
if (resolution.printerConfig?.autoPrint) {
await this.printService.print(
pdfBuffer,
resolution.printerConfig
);
}
Monitoring & Debuggingβ
Log Outputβ
[TemplateResolverService] Resolving template for documentType=sale, businessId=abc, locationId=store-1
[TemplateResolverService] Found location-specific template: template-xyz for location store-1
Resolution Sourceβ
Check the source field in the response to know which template was used:
const resolution = await resolver.resolveTemplate(...);
switch (resolution.source) {
case "location":
console.log("Using location-specific template");
break;
case "business":
console.log("Using business default template");
break;
case "system":
console.log("Using system default template");
break;
}
Usage Analyticsβ
Templates track usage count:
SELECT
id,
name,
document_type,
usage_count,
last_used_at
FROM document_template
WHERE business_id = 'abc-123'
ORDER BY usage_count DESC;
Configuration Best Practicesβ
1. Set Business Defaults Firstβ
-- Set business-wide default for sales
UPDATE document_template
SET is_default = true
WHERE business_id = 'abc'
AND document_type = 'sale'
AND name = 'Standard Receipt';
2. Configure Locations as Neededβ
-- Store A uses thermal receipt
INSERT INTO location_template_config (
location_id,
document_type,
document_template_id,
auto_print,
printer_config
) VALUES (
'store-a',
'sale',
'thermal-receipt-template-id',
true,
'{"copies": 1, "printerProfileId": "thermal-80mm"}'
);
3. Keep System Templates Activeβ
-- Ensure system fallbacks exist
SELECT document_type, COUNT(*)
FROM document_template
WHERE business_id IS NULL
AND is_default = true
AND is_active = true
GROUP BY document_type;
Testingβ
Test Location Resolutionβ
# Create location config
curl -X POST http://localhost:4000/pdf/location-template-configs \
-H "Authorization: Bearer <token>" \
-d '{
"locationId": "test-location",
"documentType": "sale",
"documentTemplateId": "custom-template-id",
"autoPrint": true,
"isActive": true,
"createdBy": "user-id"
}'
# Generate PDF - should use location template
curl -X POST http://localhost:4000/sales/:saleId/pdf \
-H "Authorization: Bearer <token>"
Verify Resolutionβ
Check logs for:
Found location-specific template: <template-id> for location <location-id>
Troubleshootingβ
Location Template Not Usedβ
Check:
- Is
locationIdbeing passed to PDF generation? - Is location config
isActive = true? - Is the configured template
isActive = true? - Does the template exist in the database?
Always Using System Templateβ
Check:
- Does business have any custom templates?
- Is business default set (
isDefault = true)? - Are business templates active?
- Is
businessIdbeing passed correctly?
Printer Config Not Appliedβ
Check:
- Is location config loaded?
- Is
printerConfigJSONB valid? - Are printer settings being read in PDF service?
- Check resolution result logs
Future Enhancementsβ
- Template Inheritance - Location inherits from business
- Time-Based Templates - Different templates by time of day
- Conditional Templates - Based on order amount, customer type, etc.
- Template Versioning - Lock locations to specific versions
- A/B Testing - Test different templates at different locations
- Template Analytics - Track which templates perform best
Conclusionβ
The template resolver now fully supports location-specific configurations with:
- β Complete fallback hierarchy
- β Printer configuration merging
- β Auto-print support
- β Copy count configuration
- β Active/inactive toggle
- β Comprehensive logging
- β Usage tracking
- β Type safety
Location managers can now configure custom templates for their stores while maintaining safe fallbacks to business and system defaults! π