A task that often arises for HotDocs developers is: how to create an interview that will allow the user to generate multiple documents from a single template in one session. For example, a lawyer may wish to assemble a number of deeds for a client, where it would be convenient to enter all the data in one interview session, as opposed to assembling the template over and over.
The online HotDocs developer support system provides a set of advanced tutorials, one of which provides a solution to this problem by the use of explicit indexing. However, the method described can be quite cumbersome if you need to apply it to a template containing many variables, because you have to add an explicit index to each variable.
In this blog, I’ll describe a different approach which simplifies the process and eliminates the need for explicit indexing. Sample templates are provided at the end of this blog post.
Video Tutorial
Below is a video tutorial I made to demonstrate how the system works. (Best viewed at 720p HD quality.)
The techniques I describe in the video may also be used for the following common scenario:
I’d like to produce wills for a married couple. The two documents will be almost the same, and I’d rather not have to create two separate templates, because that means any changes I make to one I have to make to the other, and I know from experience that unless I’m extremely careful, they inevitably get out of sync.
Moreover, it would be nice to have the flexibility to include different provisions in each spouse’s assembled document. For example, spouse 1 and spouse 2 may be distributing different items of tangible personal property.
One approach, described in the HotDocs Wiki under the heading Use Same Answer File to Assemble Two Different Documents, allows the user to click a script link that runs a computation which swaps answers, depending on whether user is assembling a document for spouse 1 or spouse 2. This method is handy, but it’s not “smart” enough to allow us to preserve different answers for spouse 1 vs. spouse 2 in one answer file. Once you enter the answers for spouse 2, the data for spouse 1 is gone.
Another approach I’ve seen is to use one template, but create two answer files, one for each spouse. This solves the problem of storing variations between the two wills, but if later you want to develop a letter template that describes the provisions included in each spouse’s document, you’re out of luck, because the information you need is split into two answer files.
Yet another approach is to create duplicate variables for each spouse; for example:
Spouse 1 has foreign assets Spouse 2 has foreign assets
This would solve our problem, but if you have a template with many variables, it can get rather cumbersome, and inconsistencies can crop up quite easily (for example, you may create or modify one spouse’s variable and then forget to create or modify the other spouse’s).
I’ve found that the “wrapper” dialog approach works well for this scenario, allowing you to have different answers for each spouse and keep everything in one answer file.
Since we are asking a series of dialogs and/or variables and want to save a different set of answers for each spouse, we could think of this as a REPEAT procedure with two iterations, one for each spouse.
So let’s say I’ve come up with a will interview consisting of a set of dialogs, and I need to ask the interview once for spouse 1 and once for spouse 2. Using the wrapper repeat dialog approach, I can drag all the dialogs onto a wrapper dialog that is limited to two repetitions (one for each spouse).
So instead of an interview script that looks like this:
where Dialogs 1 – 5 have been dragged onto the Will Wrapper, and a LIMIT 2 command has been inserted in its dialog script. (Note: we will want also to insert a prompt somewhere in our interview to alert the user that the first repetition of Will Wrapper is for spouse 1’s answers and the second repetition is for spouse 2’s answers.)
The "swapping" of spouses' names can be achieved by adding something like this to the wrapper dialog script:
LIMIT 2
IF COUNTER = 2 // set defaults for spouse 2 DEFAULT Testator Name TO Executor Name[1] DEFAULT Executor Name TO Testator Name[1] DEFAULT Testator Address TO Testator Address[1] DEFAULT Testator City TO Testator City[1] DEFAULT Testator State TO Testator State[1] DEFAULT Testator ZIP TO Testator ZIP[1] END IF
We’ve already talked about the flexibility we gain by doing this, but let’s consider what we lose as well. Since repeats can only be nested up to four levels deep, if our interview already contains nesting to the fourth level, then we would not be able to use a wrapper dialog. But, in my experience, using 4 levels of nesting is pretty rare.
Another thing we lose by doing this would be the ability to insert commands in the interview to do processing between Dialogs 1 - 5. Any such processing would have to be inserted (1) before the wrapper dialog, (2) in one or more dialog scripts of Dialogs 1 – 5, or (3) after the wrapper dialog. This means that if you have processing that needs to occur between Dialogs 1 and 2 that can’t be put in a dialog script, then this system may not work. However, in light of the instant updating functionality of HotDocs, I can’t see that this would be a big problem.
Best Practice: If you have any variables in your interview that are asked alone without being on a dialog (i.e., using the command ASK VAR), then you should put those on a dialog, either alone or in combination with other variables, and make sure the dialog is placed on the wrapper dialog. The reason is that if we simply drag a lone variable onto the wrapper dialog, it doesn’t “pop out” and could be easily overlooked in the interview process.
Sample Templates
Click here to download a HotDocs HDI file that will install a library with sample templates for the video tutorial.
Click here to download a HotDocs HDI file that will install a library illustrating the wrapper dialog approach for a set of wills.
While HotDocs offers connectivity to external databases, it does not offer any internal database feature or built-in method of handling contact information. However, over the years I’ve developed some scripting techniques to:
(1) store data in HotDocs answer files in a table-like format;
(2) provide an intuitive interface allowing the user to select table records; and
(3) merge selected records into templates in a convenient and flexible manner.
I call this collection of techniques the “Indexed Data System.” All scripting is done in HotDocs; there are no "hacks," add-ins or external software involved. I’ve used it to code hundreds of templates with excellent results. If you’ve ever grappled with this problem, I think you’ll find it quite useful as a strategy for managing contact information, and it has the potential to be expanded for other uses as well.
Please free to contact me with questions, comments, suggestions, improvements, etc.
Sample Template
Click here to download a zip file containing a sample HotDocs template and component file with an implementation of an Indexed Data System. The sample template is in DOCX format, but the same principles apply for a WordPerfect HotDocs template.
Video Tutorial
Below is a video tutorial I made to demonstrate how the system works. (Best viewed at 720p HD quality.)
If you’d like to try out the Indexed Data System, the tutorial, along with the sample template, should be enough to get you going, but I’ve also provided a fuller discussion below, which digs a little deeper. In particular, I recommend that you look at the Caveats section.
What Are Some Advantages of the Indexed Data System?
Advantages for users:
Allows users to input names, addresses, etc. once only at the beginning of the interview process. For example, the user may input the information for a particular person, and then designate the person for a variety of roles (executor, agent, beneficiary, child, etc.) without having to retype the person’s name or other data.
Provides a familiar “dropdown” interface that allows users to make quick selections during an interview.
Allows users to add or modify names on the fly (i.e., during assembly).
Advantages for developers:
Can dramatically reduce the number of variables that the developer needs to create for a template or template set. For example, instead of creating the following 49 variables:
Testator first name
Spouse first name
Child first name
Executor 1 first name
Executor 2 first name
Executor 3 first name
TPP distributee first name
Testator middle name
Spouse middle name
Child middle name
Executor 1 middle name
Executor 2 middle name
Executor 3 middle name
TPP distributee middle name
Testator last name
Spouse last name
Child last name
Executor 1 last name
Executor 2 last name
Executor 3 last name
TPP distributee last name
Testator street address
Spouse street address
Child street address
Executor 1 street address
Executor 2 street address
Executor 3 street address
TPP distributee street address
Testator city
Spouse city
Child city
Executor 1 city
Executor 2 city
Executor 3 city
TPP distributee city
Testator state
Spouse state
Child state
Executor 1 state
Executor 2 state
Executor 3 state
TPP distributee state
Testator zip
Spouse zip
Child zip
Executor 1 zip
Executor 2 zip
Executor 3 zip
TPP distributee zip
The developer would only need to create 21 variables:
c_First name
c_Middle name
c_Last name
c_Street address
c_City
c_State
c_Zip
Testator_sel
Testator_id
Spouse_sel
Spouse_id
Child_sel
Child_id
Executor 1_sel
Executor 1_id
Executor 2_sel
Executor 2_id
Executor 3_sel
Executor 3_id
TPP distributee_sel
TPP distributee_id
Each dropdown variable operates independently, so the developer may group as many as needed on a single dialog. In contrast, HotDocs database components and answer sources (also known as “pick lists”) allow only one record to be selected per dialog.
Allows the developer to filter and/or sort the list of records displayed by a dropdown variable using simple or complex criteria. For example, you may have one dropdown variable that displays only minor children and another that displays only organizations that are charities.
Since all table data is stored in the answer file, there is no need to establish connections to external databases.
If desired, the developer may import and combine data from multiple data sources, such as external databases, Excel spreadsheets, and Outlook contacts, into the Indexed Data System and use the data in a unified way.
HotDocs Versions
Implementations of the Indexed Data System have been tested in HotDocs Developer 11.x, HotDocs Developer 10.x, and HotDocs Document Services/HotDocs Server.
Analogues
The table below suggests ways in which the Indexed Data System is analogous to a database or spreadsheet. If you are familiar with databases and/or spreadsheets, this may be a helpful introduction to the concepts and workings of the system.
HotDocs Term
Database Analogue
Spreadsheet Analogue
Repeating dialog
Table (or recordset)
Worksheet
Repetition
Record
Row
Variable (on repeating dialog)
Field
Column
Type of Variable (on repeating dialog)
Field data type
Column data type
Index (position in repeating dialog)
Autonumber key field
N/A
Selector variable (a specialized multiple choice variable; populated by script using data from repeating dialog)
Dropdown list box
Dropdown list
Variable Naming Conventions
For purposes of this discussion, I won’t use suffixes indicating variable type for text, number, multiple choice, or date variables. (This is not meant to discourage such use; just to simplify things.) However, I do use the following suffixes and prefixes:
_sel
Selector variable (a specialized multiple choice variable)
_id
A specialized computation variable
_rpt
Repeating dialog
c_
Prefix for a variable on the contacts table
Steps for Setting Up a Basic Indexed Data System
1. Create a repeating dialog to store all of your person/organization data. You can use the sample I’ve provided as a starting point. The repeating dialog may be thought of as a table in which each iteration is a “record.” For purposes of this discussion, I’ve named this repeating dialog “Contacts_rpt” and will sometimes refer to it as the “contacts table.” I will also sometimes refer to an iteration in Contacts_rpt as a “record.”
2. Create a variable for each type of data you wish to store for each contact. Variables may be of type text, number, date, true/false, or multiple choice. Also include a multiple choice variable “c_Type” with options “P” for person and “O” for organization, and a hidden auxiliary variable as indicated below.
c_Type c_Gender c_First name c_Middle name c_Last name c_Organization name (displayed only if the contact is an organization) c_Street address c_City c_State c_Zip c_Full name (hidden; to store a person's full name for convenience)
3. Create a selector (multiple choice) variable for each role that a contact plays in your template. (I like to style the multiple choice variables as dropdowns, but you may use any style that you like.) For example:
Testator_sel Spouse_sel Child_sel Executor 1_sel Executor 2_sel Executor 3_sel TPP distributee_sel [“TPP” is short for “tangible personal property”]
You don’t need to add any multiple choice options to these variables, because the options will be added during assembly (after the user inputs the names of the parties). However, keep in mind that each selector variable selection will consist of an index number (in text form) as the variable option and a name as the option prompt. As mentioned above, the index number is going to tell us at which iteration of the contacts dialog a particular name is stored.
4. For each selector variable you create, create a companion computation variable which converts the selected option (text) number to a numeric index. (The numeric index will point to a record in the contacts dialog). I’ll call these specialized computation variables “ID variables.” For example:
Selector Variable Name
Corresponding ID Variable Name
ID Variable Script
Testator_sel
Testator_id
INTEGER(Testator_sel)
Spouse_sel
Spouse_id
INTEGER(Spouse_sel)
Child_sel
Child_id
INTEGER(Child_sel)
Executor 1_sel
Executor 1_id
INTEGER(Executor 1_sel)
Executor 2_sel
Executor 2_id
INTEGER(Executor 2_sel)
Executor 3_sel
Executor 3_id
INTEGER(Executor 3_sel)
TPP distributee_sel
TPP distributee_id
INTEGER(TPP distributee_sel)
Note: You could actually skip this step and simply insert "INTEGER(Testator_sel)" wherever you need to convert a selector variable value to an integer, but I think creating the ID variable is worthwhile because it makes the template more readable and saves you the trouble of inserting a bunch of INTEGER expressions in the template.
5. Create a computation script called “Process selector variables” to add options to each selector variable based on what the user has input in the contacts dialog.
"" ASK NONE // Clear selector variables (listed in alpha order) CLEAR Child_sel CLEAR Executor 1_sel CLEAR Executor 2_sel CLEAR Executor 3_sel CLEAR Testator_sel CLEAR Spouse_sel CLEAR TPP distributee_sel // Add names to selector variables REPEAT Contacts_rpt // set multiple choice value to the index no. and multiple choice prompt to full name SET item TO "«COUNTER»" + "|" + c_Full name ADD item TO Child_sel ADD item TO Executor 1_sel ADD item TO Executor 2_sel ADD item TO Executor 3_sel ADD item TO Testator_sel ADD item TO Spouse_sel ADD item TO TPP distributee_sel END REPEAT ASK ALL
Note that the vertical bar in the text portion of the ADD instruction separates the multiple choice variable option (i.e., the contact index) from the option prompt (i.e., the contact full name).
Coding the Template
Once you have set up the HotDocs components for an Indexed Data System, to merge data from the contacts table into your templates, you then insert variables in the following format:
«c_City[Testator_id]»
or, if you wish to use a format (such as all caps):
«c_City[Testator_id]:LIKE THIS»
Since the above code may well look foreign to you, let’s break it down. As mentioned above, Testator_id is a computation variable with script:
INTEGER(Testator_sel)
The value of the selector variable Testator_sel is going to be the index number (in text form) of the option that the user has chosen for testator. So, let’s say the user has chosen Jane Jetson as testator, and Jane’s index is 3 (i.e., she is at position 3 in the contacts table). Then
«c_City[Testator_id]:LIKE THIS»
resolves to:
«c_City[3]:LIKE THIS»
Via explicit indexing, HotDocs interprets this as: Return the value of the variable c_City for the 3rd iteration of Contacts_rpt, and make it all caps. So if the user entered Orbit City as Jane’s city, the result would be:
ORBIT CITY
As shown in the tutorial, you can use the HotDocs Component Manager to drag and drop contact variables and ID variables directly into the template that you are coding. You can also use the HotDocs ribbon to format these indexed variables.
Caveats
Although in theory the number of records that can be entered in a table is limited only by the maximum number of repetitions that HotDocs allows in a repeating dialog, in practice this system works best for tables with relatively few (say, under 100) records. If you will be working with large lists (100+ records), this system may not be right for you, because the processing could slow down assembly performance. If this is a concern for you, you should run some tests and weigh the benefits and performance of this system against the built-in database capabilities of HotDocs.
(1) Note that the Maximum WHILE iterations limit controls the maximum-allowed explicit index for a repeated variable. Thus, you should be sure that the Maximum WHILE iterations is set to an appropriate value to allow your users to enter a sufficient number of records in the contacts table. You set this value at the Component File Properties dialog box.
(2) The implementation of the Indexed Data System described here is tied to the order in which records are entered in the table. If, after setting up a table and making selections based on it, the user then deletes or moves records, it may throw off any selections that have been previously made by the user. Thus, you should alert your users to avoid moving or deleting records after they have already made selections during an assembly.
I’ve developed a more complex version of the Indexed Data System that is independent of the list order, so if this is a concern for you, contact me and I’ll provide further details.
Later Topics
So there you have the basics of the Indexed Data System. Later topics will include:
Let’s say that we’d like to modify our will template to set it up so that the testator’s spouse is the initial executor by default, but you want the user to be able to select a different person or financial institution as executor. The code for this is quite straightforward:
DEFAULT Executor 1_sel[1] TO Spouse_sel
Recall that a selector variable is simply a specialized multiple choice variable, and so we are setting the default value for one multiple choice variable to that of another. The index number 1 in brackets is not necessary, but it serves as a reminder that the variable Executor 1_sel is on a repeating dialog and that we are only setting the default of the first level.
In the case where a certain party will always play a certain role in a scenario, e.g., if husband and wife will always be initial trustees of a joint trust, you would use a SET instruction instead of a DEFAULT instruction:
SET Initial trustee 1_sel TO Husband_sel SET Initial trustee 2_sel TO Wife_sel
(In this scenario, you would not display the initial trustees dialog to the user; if you did, the user would not be able to change the initial trustee selections.)
Sorting Names in Selector Variables
To list names alphabetically in selector variables, we need to create a sortable version of each person’s name before adding the names to the selector variables. To do this, we can add an auxiliary variable “c_Sortable name” to the contacts table and then add the following line to the dialog script:
SET c_Sortable name TO c_Last name + ", " + c_First name + " " + SPACE( c_Middle name )
Or if the contact is an organization:
SET c_Sortable name TO c_Organization name
Now we can modify “Process selector variables” to sort the names in each selector variable using c_Sortable name:
// Add names to selector variables // set multiple choice value to the index no. and multiple choice prompt to full name
REPEAT Contacts_rpt ASCEND c_Sortable name SET Temp TO "«COUNTER»" + "|" + c_Full name
ADD Temp TO Child_sel ADD Temp TO Executor 1_sel ADD Temp TO Executor 2_sel ADD Temp TO Executor 3_sel ADD Temp TO Testator_sel ADD Temp TO Spouse_sel ADD Temp TO TPP distributee_sel
END REPEAT
SET Temp TO ""
ASK ALL
Filtering Selector Variable Values
In the examples I have given of populating selector variables, all of the names input by the user are added to the list. But what if you want only names with certain attributes to be listed on particular selector variables? For example, you may want the user to be able to select a charity, but it wouldn’t make sense to display the names of persons in that particular variable. So you could modify the “Process selector variables” computation as follows:
// Add names to selector variables // set multiple choice value to the index no. and multiple choice prompt to full name
REPEAT Contacts_rpt ASCEND c_Sortable name
SET Temp TO "«COUNTER»" + "|" + c_Full name
// the following variables may be a person or an organization ADD Temp TO Executor 1_sel ADD Temp TO Executor 2_sel ADD Temp TO Executor 3_sel ADD Temp TO TPP distributee_sel
// the following variables may be a person only IF c_Type = “P” // person ADD Temp TO Child_sel ADD Temp TO Testator_sel ADD Temp TO Spouse_sel END IF
// the following variable may be an organization only IF c_Type = “O” // organization ADD Temp TO Charity_sel END IF END REPEAT
SET Temp TO ""
ASK ALL
Comparing Selector Variables
In expressions, you can test equivalencies between selector variables. For example, if you wanted to test whether wife was selected as agent for a durable power of attorney, you might write something like this:
IF Durable power agent_sel = Wife_sel // Do something ELSE // Do something else END IF
However, when doing any variable comparison, it is good practice to test whether or not the variable(s) have been answered:
IF ANSWERED ( Durable power agent_sel ) AND ANSWERED ( Wife_sel ) IF Durable power agent_sel = Wife_sel // Do something ELSE // Do something else END IF END IF
So these are some basic selector variable techniques. Please feel free to contact me if you have questions about different uses or scenarios.
Lee Knight Consulting, based in San Antonio, Texas, provides expert document automation services based on the HotDocs platform. Services include complex HotDocs template and interview development, HotDocs API programming, and integration of HotDocs with Microsoft Office applications. Lee Knight has been developing document automation systems since 1999.
Estate Planning Domain Expertise: Lee has particular expertise in the development of HotDocs templates for estate planning and has developed hundreds of templates. Lee is closely associated with partners in the California estate planning practice group at DLA Piper US LLP, and has drafted over 1,800 trust agreements, wills, and other estate planning documents.
Education: Lee Knight graduated magna cum laude with honors with a B.A. in English from Dartmouth College.