Dynamics CRM: The importance of the Address entity
Earlier this year I wrote an article about considerations when customising address fields. I was happy with the reception the article got, as it sparked some interesting discussions not only with some of my clients, but also with fellow Dynamics CRM consultants.
Today I would like to expand further on that discussion and talk about an often neglected entity in Dynamics CRM: The Address entity and its importance in every single Dynamics CRM deployment out there (yes, including yours).
Address fields in customer entities: Isn’t that enough?
Let’s have a look at Accounts and Contacts (i.e.: the customer entities) in Dynamics CRM. These entities already contain address fields which are used to capture address information for a customer record. Consider the following screenshot of a default, “vanilla” form for the Account entity:
The above example illustrates most of the default address related fields available for an Account entity (note that the same fields are available for contacts). If you look at the fields available for the entity however, you will notice that there are many more address related fields available. There are two instances for each address-related field, each starting with the prefix address1 or address2, with forty six fields in total. A customer record could potentially store information about two different addresses.
Therefore, most people then consider unnecessary the use of the Address entity, and tend to neglect it completely when customising Dynamics CRM. The problem is that the Address entity is alive and kicking.
Hidden address records
I invite you to perform an Advanced Find in a deployment of Dynamics CRM containing data to look for Address records. Just click on Advanced Find to open the Advanced Find dialog, then where it says Look For, select Addresses
. Make sure that where it says Used Saved View, the option [new]
is selected. Then click on the Results button at the ribbon to execute the search query.
Well, I never created address records in this implementation of Dynamics CRM. So where did all these addresses came from?
Here is the kicker. Every time you create a new account or contact, the system creates two address records which are related in a parental relationship with the customer record: One address record containing the information populated in the address1 fields of the customer record, and a second address record containing the information populated in the address2 fields of the customer record. Every time you modify the address within the customer record, it updates the related address record and vice-versa.
Now if you try to find these address records by navigating through More Addresses when viewing a customer record, you wouldn’t find them; but you would find the address records you have manually created for the customer record. The reason being is that there is a filtered view in the Address entity, as to only show address records were the field Address Number is greater than 2 (which are the ones created by the users).
Therefore for the purpose of mail merge, Dynamics CRM relies on the address records, not on the address fields contained in the customer record. Furthermore, this entity is quite useful when integrating Dynamics CRM with ERP systems, such as Dynamics NAV. Therefore it is extremely important that when customising the addresses in Dynamics CRM that the Address entity is not neglected.
Considerations when customising address information
Although these two hidden address records are automatically created whenever a customer record is created, this isn’t a process that can be easily customised. There isn’t a workflow in the system that can be configured to consider custom address fields and to make things worse, field mappings between address and accounts and contacts can’t be configured either.
The truth about Address1 and Address2 fields: As Adam Vero explained in the comments section below, and I quote: “The addresses are NOT stored on Account and Contact records at all, they just look like they are, which is why you can’t map anything with the Address records – the fields are really on the Address entity anyway and simply shown on the Account or Contact for ease of use and simplicity of building queries, workflows etc.”
These limitation reinforce not only the points I raised in my previous article concerning address fields, but also some points I raised when creating a custom Country/Region field.
Always make sure that the default address fields are populated, even when using custom fields. For example, if you replace the default Country/Region text field for Accounts and Contacts (also Competitors and Addresses), make sure that you still populate the default Country/Region text field, either through JScript or through a workflow. This will ensure that all address information captured in the customer record is passed on to its related address records.
In other words: Consider your custom address fields simply as controls that must pass their values to the default address fields.
Also, since there are limitations on the field mappings between the Address entity the Account and Contact entity, it is not wise to allow users to edit those hidden address records if have replaced or added new address fields. One way to avoid this is to set a JScript on the onLoad of the Address form so it disable (or even hide) its fields if the value of the Address Number field is equal to either 1 or 2, which are the values of the two hidden address records for a given customer. You could even add an alert (a message box popup) when the user tries to edit one of these records, explaining that the user should do it through editing the address fields within the customer record. This will avoid mismatches between the Address record and the address fields for a customer record.
If you want to disable all fields of the Address form, Andrew Zimmer from Avtex wrote an article on how to achieve this here (archive). Here is a modified version of Adrew’s script that you could use:
// Add this script as a webresource and to the Address form
function doesControlHaveAttribute(control) {
var controlType = control.getControlType();
return controlType != "iframe" && controlType != "webresource" && controlType != "subgrid";
}
function disableFormFields(onOff) {
Xrm.Page.ui.controls.forEach(function (control, index) {
if (doesControlHaveAttribute(control)) {
control.setDisabled(onOff);
}
});
}
//Call the following function on the Address form onLoad event
function disableAddressForm(){
if (Xrm.Page.getAttribute("addressnumber").getValue() == 1 || Xrm.Page.getAttribute("addressnumber").getValue() == 2) {
disableFormFields(true);
// We can also show an alert to help users out
alert("In order to edit this address record, please edit the address information of its related customer record.");
}
}
However, since I have customised the Address form with custom fields just like I did with the Address fields for Accounts, Contacts and Competitors, I much rather hide the fields altogether because since there is no field mapping between the Address and Accounts and Contacts, the default addresses just look messy. We can achieve this by hiding the form sections with the following JScript, which I modified from Gareth Tucker’s JScript examples for Dynamics CRM 2011:
// Add this script as a webresource and to the Address form
function HideShowSection(tabName, sectionName, visible) {
try {
Xrm.Page.ui.tabs.get(tabName).sections.get(sectionName).setVisible(visible);
}
catch (err) { }
}
//Call the following function on the Address form onLoad event
function disableAddressForm(){
if (Xrm.Page.getAttribute("addressnumber").getValue() == 1 || Xrm.Page.getAttribute("addressnumber").getValue() == 2) {
HideShowSection("general", "customer address information", false);
HideShowSection("general", "phone numbers", false);
HideShowSection("general", "additional information", false);
// We can also show an alert to help users out
alert("In order to view or edit this address record, please refer to the address information stored in its related customer record.");
}
}
Just remember that for these scripts to work, you would need to add the field Address Number hidden to the form since it is not there by default.
Also, instead of displaying an alert, we could add an HTML web resource to the Address hidden by default with some instructions or information on disabled forms, and show this web resource based on the value of the Address Number field.
Bonus: Avoiding empty address records with workflows
When populating address information for accounts and contacts, many users (and I include myself in this generalisation) often disregard the Address Name and Address Type fields. In fact it is common for clients to request these fields to be removed altogether from the forms. However since two address records are going to be created no matter what, we end up with nameless address records in the Address entity, as per the third screenshot from the top.
Normally this might not be an issue for many clients. However I got a client who often perform a lot of queries against the Address entity, and having to look through lots of address records without an Address Name and Address Type can make it difficult to find a record.
The solution is to create one workflow for the Account entity and another for the Contact entity for when a record is created, that automatically populates the Address Name and Address Type fields with some default information. In my case I populate address1_name with {Account Name (Account)} - Primary Address
and address2_name with {Account Name (Account)} - Secondary Address
. You could also set the value for address1_addresstypecode and address2_addresstypecode.
The workflow for the Contact entity would be similar, but using the contact’s full name instead for naming the address, such as {Full Name (Contact)} - Primary Address
and address2_name with {Full Name (Contact)} - Secondary Address
.
Make sure these workflows also run when their respective fields change (i.e.: Account Name for Accounts and Full Name for Contacts) in case the records are renamed. Finally, you could make these workflows run on demand in order to update all of your existing account and contact records.
Another useful article, thanks!
Just for some added clarity – all addresses are stored in the address table in the database, including the default first two addresses. An address record has an ID to tell it what the parent record is (and another field linked to which entity typecode it is, eg 1 for account, 2 for contact).
The addresses are NOT stored on Account and Contact records at all, they just look like they are, which is why you can’t map anything with the Address records – the fields are really on the Address entity anyway and simply shown on the Account or Contact for ease of use and simplicity of building queries, workflows etc.
You can’t copy the fields from one to the other since they are only in one place and there is nowhere to copy them to.
The address fields are included in the filtered views in the database which are de-normalised representations of all fields from an entity’s base table, extended table (containing all custom fields) and other linked entities (eg Contact stores parent Account ID, filtered view shows the Account name as well as an extra column).
If you actually look at an Account (say) and use the left navigation to view “more addresses” you will see additional addresses other than the first two. A quick check of the “associated view” for the Address entity reveals that the only reason you don’t see the first two here is because they are explicitly filtered out by the view’s criteria looking for address number > 2
Changing this so that users can see both add1 and add2 records allows you to get at and edit these addresses directly, and if you do, those new values appear on the Account form when it is next loaded. Try it – edit an address 1 record directly, then save and close, then hit F5 to refresh the Account form. The changes are visible here. If you do File > properties you can see the modified date/time on the Account record has not been updated, since you did not change the Account record itself, only the address record which is entirely separate.
You can see it is absolutely NOT possible to have a “mismatch” between the Account / Contact address fields and the values stored in the Address entity, as they are one and the same, this is not something being copied or cleverly updated between them.
So there is no huge reason not to allow users to edit them directly, although that would be unusual for them to need to do anyway. The small benefit of forcing them to edit Add1 and Add2 from the Account or Contact form is that when they make changes there and save, the modified by / on fields will be updated to reflect the fact that a change has occurred.
I hope this extra information is useful!
Adam
Adam,
Thanks for adding more food for thought on the discussion. Here are some important points I found:
As we both mentioned, the default Address1 and Address2 records have an address number of 1 and 2 respectively. When you create a new address record, it will have an address number of 0, until you save the record, and then the address number will be automatically set. This is why the JScripts I proposed checks if the value is EITHER 1 or 2, not if it is equal or lower than 2.
Because I have added a new Country/Region dropdown for addresses and an City/County dropdown if the selected country is Ireland, this can in fact create a mismatch.
If country == Ireland
* Hide default city field
* Hide default state/province field
* Hide default zip/postal code field
* Show custom City/County field (new_irishcounty)
* Make City/County mandatory
* Text value of City/County is copied to the default city field
If country != Ireland
* Show default city field
* Show default state/province field
* Show default zip/postal code field
* Hide custom City/County field (new_irishcounty)
* Make City/County optional
In either way, every time the value of the new_countrylist dropdown changes:
* It populates the default country field with a friendly country name.
* It also clears the value of all address fields (both default and custom).
This setting is for Accounts, Contacts and the Address entity. However if the user ever decided to look for Address1 or Address2 and edit them through the Address entity, the user would see a mismatch between the default fields and the custom fields: City/County for Irish addresses, as well as the Country/Region dropdown.
I reckon the message here is that the default Address fields should always be populated, even if you use a custom control.
Thanks for the article, it helped clear up some questions I had about the address fields. On a related note, I have a user that routinely stores 3 addresses per contact. They want to have a 3rd set of address fields on the contact form. Is the best way to just add "dummy" fields and use them to create a related address entity through JScript or a workflow when they save? If I do that and they then update the address entity directly without using the contact form the values will be out of sync. I guess I could store the addressid of the 3rd address on the contact entity and check when an address is updated to see if it is one that is also stored on the contact entity and update that the same way. Clear as mud, right?
Hi Mike,
Unfortunately there is no way to replicate this sync that exists between the address 1 and address2 fields of a contact form with the address entity (at least not in a neat way) for a 3rd address using JScripts and workflows. I can see that working so-so with a plug-in but even in that case I can see how it can mess it up in some exceptional circumstances; so a plug-in wouldn't be perfect either.
I would tell your user to use the "more address" entity for that. Remember: there isn't a mapping between the contact/account and address entities.
Just a further couple of thoughts having come back to this thread:
– another reason to ensure you copy data from custom controls such as option sets or lookups into the default address fields is that these are used on records such as Quotes when using the “Lookup Address” functionality. There is no way (that I know of) to customise the behaviour of this to pick up other fields as well or instead of the built in ones.
– on the same topic, I discovered recently that this function of fetching addresses has a slightly bizarre filter – it will only show addresses from the customer record which have the “Address Name” field populated. I usually recommend people use this anyway as it is an easy way to disambiguate addresses and advocate using whatever terms make sense to them such as “head office”, “regional sales office”, “southern distribution centre” etc. Now I have implemented scripts to ensure it is filled in (based on address type but overwritable by the user, and address type is required if line 1 or postcode is filled in)
Thank you so much for your article! The blank address book (sans Address Name) is incredibly helpful! It was baffling me and the workflow to ensure the value is populated is very helpful. Thanks again! Starr
Anybody else think this is ridiculous?
What does 'More Addresses' mean to a user – it's just nonsense.
Why can't we relate Addresses to custom entities? Useless for customization.
Why 2 'magic' addresses? Why not 1 or 200? What dictates that '2 addresses are useful'?
Why security ignored for addresses? MS couldn't be bothered with a full implementation?
Sorry for a ranting comment! But it seems half-cut. Why not have a simple 'Address' entity that can be added to Accounts and related to Contacts (and custom entities). Simple 1:N relationship.
Finally, developers are constrained by the framework but seems that MS can 'subvert it' to model business entities in the (crazy) way they see fit.
Well Ryan, I certainly think that there is little use to More Addresses unless Microsoft fixes this relationship limitation. My last post entitled "Almost xRM" talks about this limitation.
Excellent article
Although this is from 2012 this article should be reposted again. The address relationship remains often forgot, and over looked. I myself had no idea until digging into it last year. Thank you for this post. Great work!