Location Template Configuration - Schema Adjustments
Overviewβ
Documentation of adjustments made to align the backend implementation with the actual database schema.
Database Schema Differencesβ
Actual Database Schemaβ
The location_template_config table has these columns:
id- UUID primary keylocation_id- UUID (references location)document_type- VARCHARdocument_template_id- UUID (references document_template)printer_config- JSONB (flexible configuration storage)auto_print- BOOLEANis_active- BOOLEANcreated_at- TIMESTAMPTZcreated_by- UUIDupdated_at- TIMESTAMPTZupdated_by- UUID
Missing Fields (Originally Planned)β
- β
business_id- Not in schema (location already has business relationship) - β
copies- Not a column (stored inprinter_configJSONB instead)
Implementation Adjustmentsβ
1. Field Storage Strategyβ
Copies Field:
- Before: Separate
copiescolumn - After: Stored in
printerConfigJSONB object - Reason: Schema uses flexible JSONB for all printer-related settings
Business ID:
- Before: Separate
business_idcolumn - After: Removed (location relationship implies business)
- Reason: Redundant - location is already tied to a business
2. Data Transformationβ
Backend Response Transformerβ
private transformResponse(entity: LocationTemplateConfigEntity) {
const printerConfig = entity.printerConfig as Record<string, unknown> | null;
return {
...entity,
templateId: entity.documentTemplateId, // Alias for frontend
copies: (printerConfig?.copies as number) || 1, // Extract from JSONB
printerProfileId: printerConfig?.printerProfileId as string | undefined,
template: entity.templateName ? {
name: entity.templateName,
version: entity.templateVersion,
isDefault: entity.templateIsDefault,
} : undefined,
};
}
What it does:
- Extracts
copiesfromprinterConfigJSONB - Adds
templateIdalias (frontend expects this) - Extracts
printerProfileIdfromprinterConfig - Builds template object from joined fields
Frontend Service Mappingβ
Create/Update: Stores copies in printerConfig
printerConfig: {
printerProfileId: data.printerProfileId,
copies: data.copies // Store here instead of top-level
}
Response: Extracts copies back to top-level for UI
copies: (printerConfig?.copies as number) || 1
3. API Changesβ
Removed Query Parametersβ
- β
businessIdfrom all GET endpoints - Location already has business relationship via FK
Updated Endpointsβ
Before:
GET /pdf/location-template-configs?locationId=xxx&businessId=xxx
GET /pdf/location-template-configs/:id?businessId=xxx
DELETE /pdf/location-template-configs/:id?businessId=xxx
After:
GET /pdf/location-template-configs?locationId=xxx
GET /pdf/location-template-configs/:id
DELETE /pdf/location-template-configs/:id
4. Validation Changesβ
DTO Updates:
- Removed
businessIdfrom DTOs - Removed
copiesfrom DTOs (handled inprinterConfig) - Kept
printerConfigas flexible JSONB object
Domain Entity:
- Removed
businessIdfield - Removed
copiesfield - Removed
copiesvalidation fromisValid()
5. Repository Query Optimizationβ
Template Join:
.selectFrom("locationTemplateConfig as ltc")
.leftJoin("documentTemplate as dt", "ltc.documentTemplateId", "dt.id")
.selectAll("ltc")
.select([
"dt.name as templateName",
"dt.version as templateVersion",
"dt.isDefault as templateIsDefault",
])
Benefits:
- Single query returns config + template info
- No N+1 query problem
- Frontend gets template name/version without extra calls
Frontend Compatibility Layerβ
Service Layer Handles Mappingβ
Frontend β Backend (Create):
Frontend sends:
{
templateId: "uuid",
copies: 2,
printerProfileId: "uuid"
}
Backend receives:
{
documentTemplateId: "uuid",
printerConfig: {
copies: 2,
printerProfileId: "uuid"
}
}
Backend β Frontend (Response):
Backend returns:
{
documentTemplateId: "uuid",
printerConfig: { copies: 2, printerProfileId: "uuid" },
templateName: "Sales Receipt",
templateVersion: 1
}
Frontend receives:
{
documentTemplateId: "uuid",
templateId: "uuid", // Alias added
copies: 2, // Extracted
printerProfileId: "uuid", // Extracted
template: {
name: "Sales Receipt",
version: 1
}
}
Frontend Components Unchangedβ
The UI components continue to work with:
copiesas a top-level fieldtemplateIdas the template reference- All existing prop interfaces
The service layer transparently handles the mapping, so no component changes needed.
Benefits of This Approachβ
1. Schema Flexibilityβ
β
printerConfig JSONB can store any printer-related settings
β
Easy to add new printer options without schema changes
β
Different printer types can have different config structures
2. Data Normalizationβ
β
No redundant businessId storage
β
Location β Business relationship already established
β
Cleaner schema with less redundancy
3. Performanceβ
β Single query with JOIN gets all needed data β No extra queries for template info β Frontend gets complete data in one request
4. Backward Compatibilityβ
β Frontend components don't need changes β Service layer handles all transformations β Type safety maintained throughout
Testing Considerationsβ
Test JSONB Storageβ
# Copies stored in printerConfig
curl -X POST /pdf/location-template-configs \
-d '{
"printerConfig": {
"copies": 2,
"printerProfileId": "uuid",
"otherSettings": "value"
}
}'
Verify Extractionβ
# Response should have copies extracted
GET /pdf/location-template-configs?locationId=xxx
Response:
{
"copies": 2, # Extracted from printerConfig
"printerConfig": { "copies": 2, ... } # Original JSONB
}
Migration Path (If Needed)β
If you need to add copies or businessId as columns later:
-- Add copies column
ALTER TABLE location_template_config
ADD COLUMN copies INTEGER DEFAULT 1;
-- Migrate data from JSONB
UPDATE location_template_config
SET copies = COALESCE((printer_config->>'copies')::INTEGER, 1);
-- Add business_id if needed
ALTER TABLE location_template_config
ADD COLUMN business_id UUID REFERENCES business(id);
-- Populate from location relationship
UPDATE location_template_config ltc
SET business_id = l.business_id
FROM location l
WHERE ltc.location_id = l.id;
Conclusionβ
The implementation successfully adapts to the actual database schema while maintaining:
- β Full type safety
- β Clean separation of concerns
- β Frontend compatibility
- β Flexible printer configuration
- β Optimal query performance
- β No breaking changes to UI
The JSONB approach for printerConfig provides maximum flexibility for future printer-related features without requiring schema changes.