Volume 4: The Document Automation Consultant

Chapter 7: Advanced Template Development

Introduction

Chapter 6 taught you the DataPublisher platform basics: how it works, deployment models, and simple template development.

Now we level up.

This chapter covers advanced techniques that transform basic templates into sophisticated, production-ready solutions that clients can't live without.

You'll learn: - Complex conditional logic patterns - Multi-level master-detail relationships - Dynamic layouts and formatting - Error handling and validation - Performance optimization - Testing and quality assurance

By the end, you'll build templates that rival custom software solutions.


Complex Conditional Logic

Beyond simple IF/THEN statements.

Nested Conditionals

Scenario: Show different content based on multiple conditions

Law firm complaint example:

{{IF JurisdictionType=Federal}}
  {{IF DiversityJurisdiction=Yes}}
    This Court has jurisdiction over this matter pursuant to 28 U.S.C. § 1332(a)
    as there is complete diversity of citizenship between the parties and the
    amount in controversy exceeds $75,000, exclusive of interest and costs.
  {{ENDIF}}

  {{IF FederalQuestion=Yes}}
    This Court has jurisdiction pursuant to 28 U.S.C. § 1331 as this action
    arises under the laws of the United States.
  {{ENDIF}}
{{ENDIF}}

{{IF JurisdictionType=State}}
  {{IF State=California}}
    This Court has jurisdiction pursuant to California Code of Civil Procedure § 410.10.
  {{ENDIF}}

  {{IF State=NewYork}}
    This Court has jurisdiction pursuant to New York CPLR § 301.
  {{ENDIF}}
{{ENDIF}}

Pattern: Outer IF determines jurisdiction type, inner IFs select specific language.

Boolean Logic (AND/OR)

DataPublisher supports: - AND: Both conditions must be true - OR: Either condition must be true - NOT: Condition must be false

Insurance proposal example:

{{IF BusinessType=Restaurant AND AnnualRevenue>1000000}}
  You qualify for our preferred commercial package with premium discounts.
{{ENDIF}}

{{IF PriorClaims>3 OR CreditScore<600}}
  Additional underwriting review required. Policy may include higher premiums or restrictions.
{{ENDIF}}

{{IF NOT NewBusiness AND YearsInBusiness>=5}}
  Mature business discount: 15% off standard rates.
{{ENDIF}}

Switch/Case Pattern

When you have many mutually-exclusive options:

Property management - lease term options:

{{IF LeaseTermMonths=6}}
  Lease Type: Short-Term (6 Month)
  Base Rent: <<BaseRent>>{{FormatCurrency}}
  Utilities: Tenant pays all utilities
  Notice to Terminate: 60 days written notice required
{{ENDIF}}

{{IF LeaseTermMonths=12}}
  Lease Type: Standard Annual
  Base Rent: <<BaseRent>>{{FormatCurrency}}
  Utilities: As specified in Utility Addendum
  Notice to Terminate: 30 days written notice required
{{ENDIF}}

{{IF LeaseTermMonths=24}}
  Lease Type: Two-Year Fixed
  Base Rent: <<Year1Rent>>{{FormatCurrency}} (Year 1), <<Year2Rent>>{{FormatCurrency}} (Year 2)
  Special Terms: Rent locked for duration, 10% discount vs month-to-month
  Early Termination: Subject to 2-month penalty
{{ENDIF}}

{{IF LeaseTermMonths=0}}
  Lease Type: Month-to-Month
  Base Rent: <<MonthToMonthRent>>{{FormatCurrency}}
  Premium: 20% above standard annual rate
  Notice to Terminate: 30 days written notice required by either party
{{ENDIF}}

Better than: Giant nested IF/ELSE structure

Fallback Pattern

Provide default content when data missing:

{{IF ClientLogo!=}}
  [Logo: <<ClientLogo>>{{InsertImage:width=2in}}]
{{ENDIF}}

{{IF ClientLogo=}}
  [Logo: <<DefaultLogo>>{{InsertImage:width=2in}}]
{{ENDIF}}

Or with coalesce function (if supported):

[Logo: <<ClientLogo | DefaultLogo>>{{InsertImage:width=2in}}]

Use case: Clients without logos get default branding, not blank space.


Multi-Level Master-Detail Relationships

Handling complex data hierarchies.

Three-Level Hierarchy

Homeschool co-op: Families → Students → Enrollments

FALL 2025 ENROLLMENT REPORT

{{ForEach:Families}}
Family: <<Families.FamilyName>>{{MakeBold}}
Address: <<Families.Address>>
Contact: <<Families.Email>>, <<Families.Phone>>{{FormatPhone}}

Students in this family:
{{ForEach:Families.Students}}
  Student: <<Families.Students.FirstName>> <<Families.Students.LastName>>
  Grade: <<Families.Students.GradeLevel>>
  Date of Birth: <<Families.Students.DateOfBirth>>{{FormatDate:MM/dd/yyyy}}

  Enrolled in these classes:
  {{ForEach:Families.Students.Enrollments}}
    - <<Families.Students.Enrollments.ClassName>> with <<Families.Students.Enrollments.TeacherName>>
      <<Families.Students.Enrollments.DayOfWeek>> at <<Families.Students.Enrollments.StartTime>>
      Fee: <<Families.Students.Enrollments.ClassFee>>{{FormatCurrency}}
  {{EndForEach}}

  Total fees for <<Families.Students.FirstName>>: <<Families.Students.TotalFees>>{{FormatCurrency}}
{{EndForEach}}

FAMILY TOTAL: <<Families.FamilyTotalFees>>{{FormatCurrency}}

────────────────────────────────────────
{{EndForEach}}

GRAND TOTAL ALL FAMILIES: <<GrandTotalFees>>{{FormatCurrency}}

Key techniques: - Nested loops (3 levels deep) - Dotted notation (Families.Students.Enrollments.ClassName) - Subtotals at each level - Visual separators between families

Aggregations in Loops

Calculations across related records:

Law firm matter report:

{{ForEach:Matters}}
Matter: <<Matters.MatterNumber>> - <<Matters.ClientName>> v. <<Matters.OpposingParty>>
Status: <<Matters.Status>>
Opened: <<Matters.OpenDate>>{{FormatDate:MM/dd/yyyy}}

Time Entries:
{{ForEach:Matters.TimeEntries}}
  <<Matters.TimeEntries.Date>>{{FormatDate:MM/dd/yyyy}} | <<Matters.TimeEntries.Attorney>> | <<Matters.TimeEntries.Hours>> hrs | <<Matters.TimeEntries.Description>>
{{EndForEach}}

Hours Summary:
  Partner Hours: <<Matters.PartnerHours>> @ <<PartnerRate>>{{FormatCurrency}}/hr = <<Matters.PartnerHours * PartnerRate>>{{FormatCurrency}}
  Associate Hours: <<Matters.AssociateHours>> @ <<AssociateRate>>{{FormatCurrency}}/hr = <<Matters.AssociateHours * AssociateRate>>{{FormatCurrency}}
  Paralegal Hours: <<Matters.ParalegalHours>> @ <<ParalegalRate>>{{FormatCurrency}}/hr = <<Matters.ParalegalHours * ParalegalRate>>{{FormatCurrency}}

Total Fees: <<Matters.TotalFees>>{{FormatCurrency}}
Amount Billed: <<Matters.AmountBilled>>{{FormatCurrency}}
Amount Collected: <<Matters.AmountCollected>>{{FormatCurrency}}
Outstanding: <<Matters.Outstanding>>{{FormatCurrency}}

{{EndForEach}}

Aggregations performed: - SUM(hours WHERE role = 'Partner') - SUM(hours * rate) per role - Total fees, billed, collected calculations

Implementation: Calculate aggregates in database query or in template logic.

Conditional Display of Details

Show details only when needed:

Event planning proposal - optional vendors:

YOUR WEDDING PROPOSAL
For: <<ClientNames>>
Date: <<EventDate>>{{FormatDate:dddd, MMMM d, yyyy}}

VENUE
<<VenueName>>
<<VenueAddress>>
Capacity: <<VenueCapacity>> guests
Rental Fee: <<VenueRentalFee>>{{FormatCurrency}}

{{IF CateringIncluded=Yes}}
CATERING
<<CatererName>>
Menu: <<MenuSelection>>
Price per person: <<CateringPricePerPerson>>{{FormatCurrency}}
Guest count: <<EstimatedGuestCount>>
Total: <<CateringTotal>>{{FormatCurrency}}

Menu Details:
{{ForEach:MenuCourses}}
  <<MenuCourses.CourseName>>: <<MenuCourses.Items>>
{{EndForEach}}
{{ENDIF}}

{{IF PhotographyIncluded=Yes}}
PHOTOGRAPHY
<<PhotographerName>>
Package: <<PhotographyPackage>>
Coverage: <<CoverageHours>> hours
Price: <<PhotographyPrice>>{{FormatCurrency}}

Deliverables:
{{ForEach:PhotographyDeliverables}}
  - <<PhotographyDeliverables.Item>>
{{EndForEach}}
{{ENDIF}}

{{IF FloralIncluded=Yes}}
FLORAL DESIGN
<<FloristName>>
Arrangements: <<FloralArrangements>>
Price: <<FloralPrice>>{{FormatCurrency}}
{{ENDIF}}

TOTAL INVESTMENT: <<ProposalTotal>>{{FormatCurrency}}

Benefit: Proposal only shows vendors client selected, not all possibilities.


Dynamic Layouts and Formatting

Making documents adapt to content.

Page Breaks

Insert page breaks conditionally:

{{ForEach:Classes}}
CLASS ROSTER
Class: <<Classes.ClassName>>
Teacher: <<Classes.TeacherName>>

{{ForEach:Classes.Students}}
  [Student information...]
{{EndForEach}}

{{IF NOT @last}}
  {{PageBreak}}
{{ENDIF}}
{{EndForEach}}

@last is special variable: TRUE for last iteration, FALSE otherwise

Result: Page break after each class roster except the last one.

Table Layouts

Dynamic table rows:

INVOICE

┌────────────────────────────────────────────────────────┐
│ Item          │ Qty │ Unit Price │ Total              │
├────────────────────────────────────────────────────────┤
{{ForEach:LineItems}}
│ <<LineItems.Description>> │ <<LineItems.Quantity>> │ <<LineItems.UnitPrice>>{{FormatCurrency}} │ <<LineItems.LineTotal>>{{FormatCurrency}} │
{{EndForEach}}
├────────────────────────────────────────────────────────┤
│                                    Subtotal: <<Subtotal>>{{FormatCurrency}} │
│                                    Tax: <<Tax>>{{FormatCurrency}} │
│                                    TOTAL: <<Total>>{{FormatCurrency}}{{MakeBold}} │
└────────────────────────────────────────────────────────┘

Table expands to accommodate any number of line items.

Conditional Sections with Distinct Formatting

Medical consent form - procedure-specific warnings:

{{IF ProcedureType=Surgery}}
SURGICAL RISKS AND WARNINGS{{MakeBold}}{{SetFontSize:14}}{{SetColor:red}}

The following risks are associated with surgical procedures:
- Infection
- Bleeding
- Adverse reaction to anesthesia
- Scarring
- [Procedure-specific risks...]

You must not eat or drink for 8 hours before surgery.
{{ENDIF}}

{{IF ProcedureType=ImagingWithContrast}}
CONTRAST MEDIA NOTICE{{MakeBold}}{{SetFontSize:14}}

This procedure uses contrast media (dye). Inform your physician if you:
- Have kidney disease
- Are allergic to iodine or shellfish
- Are pregnant or breastfeeding
- Take metformin (diabetes medication)
{{ENDIF}}

Different formatting (color, size, boldness) for different risk levels.

Responsive Text Sizing

Adjust font size based on content length:

{{IF ClientName.Length>50}}
  <<ClientName>>{{SetFontSize:10}}
{{ENDIF}}

{{IF ClientName.Length<=50}}
  <<ClientName>>{{SetFontSize:14}}
{{ENDIF}}

Long names get smaller font to fit space.


Error Handling and Validation

Preventing garbage documents.

Required Field Validation

Check for missing data before generating:

{{IF ClientName=}}
  ERROR: Client name is required but missing. Document cannot be generated.
  {{StopGeneration}}
{{ENDIF}}

{{IF InvoiceDate=}}
  ERROR: Invoice date is required but missing.
  {{StopGeneration}}
{{ENDIF}}

[Rest of template...]

{{StopGeneration}} prevents document from being created with missing critical data.

Data Type Validation

Ensure data is correct type:

{{IF NOT IsNumber(InvoiceTotal)}}
  ERROR: Invoice total must be a number. Current value: <<InvoiceTotal>>
  {{StopGeneration}}
{{ENDIF}}

{{IF NOT IsDate(EventDate)}}
  ERROR: Event date is invalid. Current value: <<EventDate>>
  {{StopGeneration}}
{{ENDIF}}

Range Validation

Ensure values are reasonable:

{{IF TaxRate<0 OR TaxRate>0.20}}
  WARNING: Tax rate seems unusual: <<TaxRate * 100>>%
  Please verify before sending to client.
{{ENDIF}}

{{IF EstimatedGuestCount<10 OR EstimatedGuestCount>500}}
  WARNING: Guest count seems unusual: <<EstimatedGuestCount>>
  Typical range is 10-500. Please verify.
{{ENDIF}}

Warnings don't stop generation, but alert user to check.

Relationship Validation

Ensure related data exists:

{{IF StudentCount=0}}
  ERROR: No students enrolled in this class. Cannot generate roster.
  {{StopGeneration}}
{{ENDIF}}

{{IF NOT TeacherAssigned}}
  WARNING: No teacher assigned to <<ClassName>>. Roster will show "TBA" for teacher.
{{ENDIF}}

User-Friendly Error Messages

Bad error:

ERROR: NULL_REFERENCE_EXCEPTION at line 247

Good error:

ERROR: Student photo is missing for Emma Johnson (Student ID: 1234).
Please upload photo or use placeholder image.
To fix: Go to Student Management → Emma Johnson → Upload Photo

Include: - What's wrong (specific) - Why it matters (context) - How to fix (actionable steps)


Performance Optimization Techniques

Making large document generation fast.

Lazy Loading Images

Don't load images until needed:

{{ForEach:Students}}
  Name: <<Students.FirstName>> <<Students.LastName>>

  {{IF IncludePhotos=Yes}}
    [Photo: <<Students.PhotoPath>>{{InsertImage:width=2in,height=2in,lazy=true}}]
  {{ENDIF}}
{{EndForEach}}

lazy=true defers image loading, dramatically improving generation speed.

Pagination for Large Datasets

Generate in chunks:

{{-- Generate documents 1-100 --}}
{{ForEach:Records WHERE RecordNumber>=1 AND RecordNumber<=100}}
  [Document content...]
{{EndForEach}}

Client generates: - Batch 1: Records 1-100 - Batch 2: Records 101-200 - Batch 3: Records 201-300 - Etc.

Prevents timeout on large batches.

Caching Lookups

Store frequently-accessed data:

{{-- Load states once, reuse multiple times --}}
{{LoadLookupTable:States}}

{{ForEach:Clients}}
  Client: <<Clients.Name>>
  State: <<States[Clients.StateCode].FullName>>
  State Capital: <<States[Clients.StateCode].Capital>>
  State Tax Rate: <<States[Clients.StateCode].TaxRate>>%
{{EndForEach}}

Without caching: Database query for each client's state (1,000 clients = 1,000 queries)

With caching: One query loads all states, then lookup in memory (1,000 clients = 1 query + fast lookups)

Minimizing Loops

Fetch only necessary data:

Inefficient:

{{ForEach:AllStudents}}
  {{IF AllStudents.ClassID=CurrentClassID}}
    [Show student...]
  {{ENDIF}}
{{EndForEach}}

Loops through all 500 students, shows 20.

Efficient:

{{ForEach:Students WHERE ClassID=CurrentClassID}}
  [Show student...]
{{EndForEach}}

Loops through only 20 students enrolled in this class.

Difference: 96% reduction in iterations.


Testing and Quality Assurance

Ensuring templates work correctly.

Test Data Strategy

Create comprehensive test datasets:

1. Happy Path Data - All fields populated correctly - Typical values - Expected scenarios - Should generate cleanly

2. Edge Case Data - Empty optional fields - Maximum lengths - Minimum values - Boundary conditions

3. Error Case Data - Missing required fields - Invalid data types - Out-of-range values - Should trigger errors

Example test family:

Family: Smith (typical)
- 2 students, 3 classes each, all data present
- Should generate perfectly

Family: TestEdgeCases (edge cases)
- 1 student with no photo (tests placeholder)
- Student with very long name (50 characters)
- Student with no allergies, no medical conditions
- Should handle gracefully

Family: TestErrors (error cases)
- Student with no name (should error)
- Student with future birth date (should warn)
- Student in 0 classes (should handle)

Manual Testing Checklist

For each template, verify:

Data accuracy - All placeholders replaced correctly - No "<>" showing in output - Calculations correct

Conditional logic - Sections show/hide correctly - All IF paths tested - Edge cases handled

Loops - All items displayed - Master-detail relationships correct - No duplicates or missing items

Formatting - Professional appearance - Consistent fonts and sizing - Page breaks in right places - Tables aligned correctly

Images - Photos display correctly - Sizing consistent - Placeholders for missing images

Multi-page documents - Headers/footers correct on all pages - Page numbers accurate - Sections don't break awkwardly

Automated Testing (Advanced)

If generating hundreds of documents:

1. Regression Testing

// Generate documents with test data
// Compare output to known-good baseline
// Flag differences

testTemplate('ClassRoster.docx', 'testData.csv', 'baseline-output.docx');
// Returns: PASS or list of differences

2. Validation Scripts

// Check generated documents for common errors
validateDocument('output.docx');
// Checks:
// - No template syntax visible (<<...>>)
// - All sections present
// - No orphan headings
// - Calculations add up

3. Load Testing

// Generate 1,000 documents, measure time
loadTest('ClassRoster.docx', 1000);
// Returns: Average time, max time, errors

Client UAT (User Acceptance Testing)

Before going live:

  1. Demo with sample data
  2. Generate documents with client's real data structure
  3. Show variety of scenarios
  4. Get feedback on formatting, layout, content

  5. Pilot with subset of real data

  6. 10-20 records
  7. Client reviews thoroughly
  8. Identify issues before full deployment

  9. Parallel run

  10. Generate documents both manually and automated
  11. Compare outputs
  12. Ensure automation matches or exceeds quality

  13. Sign-off

  14. Client approves template
  15. Document any known limitations
  16. Establish change request process

Advanced Patterns Library

Reusable solutions to common problems.

Pattern 1: Alternating Row Colors

For readability in long tables:

{{ForEach:Students}}
  {{IF @index % 2 = 0}}
    {{SetBackgroundColor:lightgray}}
  {{ENDIF}}

  [Student information row...]

  {{IF @index % 2 = 0}}
    {{SetBackgroundColor:white}}
  {{ENDIF}}
{{EndForEach}}

@index is loop counter (0, 1, 2, ...) % 2 is modulo (remainder after dividing by 2) Result: Even rows gray, odd rows white

Pattern 2: Running Totals

Show cumulative totals:

PAYMENT HISTORY

{{ForEach:Payments}}
  Date: <<Payments.Date>>{{FormatDate:MM/dd/yyyy}}
  Amount: <<Payments.Amount>>{{FormatCurrency}}
  Running Total: <<Payments.RunningTotal>>{{FormatCurrency}}
{{EndForEach}}

Total Paid: <<TotalPaid>>{{FormatCurrency}}
Balance Due: <<BalanceDue>>{{FormatCurrency}}

Calculation done in database:

SELECT 
  PaymentDate,
  Amount,
  SUM(Amount) OVER (ORDER BY PaymentDate) AS RunningTotal
FROM Payments

Pattern 3: Section Numbering

Auto-number sections:

COMPLAINT

{{ForEach:Claims}}
  CLAIM {{@index + 1}}: <<Claims.ClaimType>>{{MakeBold}}

  {{ForEach:Claims.Elements}}
    {{@parentIndex + 1}}.{{@index + 1}} <<Claims.Elements.ElementText>>
  {{EndForEach}}
{{EndForEach}}

Result:

CLAIM 1: BREACH OF CONTRACT
  1.1 Plaintiff and Defendant entered into contract...
  1.2 Plaintiff performed all obligations...
  1.3 Defendant failed to perform...

CLAIM 2: FRAUD
  2.1 Defendant made false representations...
  2.2 Defendant knew representations were false...

Pattern 4: Conditional Pluralization

Grammar-correct text:

This class has <<StudentCount>> student{{IF StudentCount!=1}}s{{ENDIF}}.

You have <<ClassCount>> class{{IF ClassCount=1}}{{ENDIF}}{{IF ClassCount!=1}}es{{ENDIF}} remaining.

<<DaysUntilEvent>> day{{IF DaysUntilEvent!=1}}s{{ENDIF}} until your event!

Result: - "1 student" (singular) - "5 students" (plural) - "1 class" (singular) - "3 classes" (plural)

Pattern 5: Smart Abbreviations

Show full or abbreviated based on space:

{{IF AvailableWidth>6in}}
  <<States[StateCode].FullName>>
{{ENDIF}}

{{IF AvailableWidth<=6in}}
  <<StateCode>>
{{ENDIF}}

Result: - Wide layout: "California" - Narrow layout: "CA"


Troubleshooting Common Issues

When things go wrong.

Issue 1: Placeholder Not Replaced

Symptom: "<>" appears in document

Causes: 1. Field name misspelled in template 2. Field doesn't exist in data source 3. Field is empty (NULL or blank)

Solutions: - Check field name spelling - Verify field exists in CSV/database - Use fallback: <>

Issue 2: Loop Not Executing

Symptom: Content inside {{ForEach}} doesn't appear

Causes: 1. No related records (empty child table) 2. Relationship not defined correctly 3. WHERE clause filters out all records

Solutions: - Check data: Do child records exist? - Verify foreign key relationships - Remove WHERE clause to test - Add diagnostic: {{Students.Count}} students found

Issue 3: Conditional Always Shows/Hides

Symptom: Section always appears or never appears

Causes: 1. Comparison syntax wrong (= vs ==) 2. Data type mismatch (comparing number to string) 3. Field is NULL (NULL != anything)

Solutions: - Use correct comparison operator (IF FieldName=Value) - Check data types (IF Age>18, not IF Age>"18") - Handle NULL: {{IF FieldName!=}} [field has value] {{ENDIF}}

Issue 4: Formatting Not Applied

Symptom: {{MakeBold}} or {{SetColor:red}} doesn't work

Causes: 1. Function applied to wrong element 2. Syntax error (typo in function name) 3. Function not supported in this context

Solutions: - Apply to field: <>{{MakeBold}} - Check spelling: MakeBold (not MakeBOLD) - Test simpler formatting first

Issue 5: Images Not Displaying

Symptom: [Photo] or blank space where image should be

Causes: 1. File path incorrect or file doesn't exist 2. File format not supported 3. Image too large (memory issue) 4. Permissions issue (server can't read file)

Solutions: - Verify file exists at path specified - Use supported formats (JPG, PNG) - Resize large images before inserting - Check file permissions


Key Takeaways

Advanced templates require: - Complex conditional logic (nested IFs, Boolean AND/OR) - Multi-level master-detail (Families→Students→Enrollments) - Dynamic layouts (page breaks, tables, responsive sizing) - Error handling (validation, user-friendly messages) - Performance optimization (lazy loading, caching, smart queries) - Thorough testing (happy path, edge cases, errors)

Patterns to master: - Alternating row colors - Running totals - Section numbering - Conditional pluralization - Smart abbreviations

Troubleshooting skills: - Diagnostic techniques - Common pitfalls - Systematic debugging

Quality standards: - Professional formatting - Grammatically correct - Error-free - Performant - Maintainable

With these advanced techniques, you can build production-ready templates that solve real business problems and delight clients.

In the next chapter, we shift from building to selling: how to find your first clients and get them to say yes.


End of Chapter 7

Next: Chapter 8 - Finding Your First Clients