The global address book normalizes the name and address data held against entities such as customers and suppliers. Changing the name, address, or contact information against a customer will also appear to change the address of the vendor if it is linked to the same address book entry and this information isn't actually stored in the vendor record.
It does so by associating the entity with a record in DirPartyTable
. This table also has some special case tables that inherit from it, as shown in the following diagram (which was taken using the Type hierarchy browser):
The tables generally used are DirPartyTable
and DirPerson
. Looking at these tables, there doesn't appear to be enough fields. We have the following fields against DirPartyTable
:
KnownAs
LanguageId
Name
NameAlias
PartyNumber
PrimaryAddressLocation
PrimaryContactEmail
PrimaryContactFax
PrimaryContactPhone
PrimaryContactTelex
PrimaryContactURL
The fields beginning with PrimaryContact
are primary key relations to LogisticsElectronicAddress
. The PrimaryAddressLocation
relates to the entity's primary address location.
The structure of electronic addresses in AX is designed to be as flexible as possible so there isn't a field for each type of communication, such as phone, fax, and e-mail. There is a record that relates to a table that determines the type of communication.
The main tables used in this set are as follows:
Table |
Relation |
Purpose |
---|---|---|
|
A global address book record | |
|
|
This relates the address book record to the location records |
|
|
The location we are associating with the address book record |
|
|
This stores the communication address and type, such as e-mail or telephone number |
|
|
This maps the address to the location role |
|
|
This determines the location type, such as business, not the type of communication |
Although this design is designed to be fully flexible, there has to be a basis by which we can determine the type. This is the LogisticsElectronicAddressTypes
enum. This doesn't differentiate between types of telephones, a mobile or cellphone number is when Type
is Phone
and isMobilePhone
is Yes
.
This level of flexibility comes at a cost, and even when armed with relationships, it is still difficult to navigate at table level. Thankfully, we can use a view to simplify this. Locate the LogisticsEntityContactInfoView
view and open the table browser. We usually won't use the tables to get the contact information data; we would normally use this view.
The PrimaryAddressLocation
field reveals how addresses are stored in Dynamics AX. This doesn't relate to an address but a location. A location could be something like 'main office', which in turn relates to an address record in LogisticsPostalAddress
.
This design is made to handle situations such as this: a company has moved to a new address and this address might be known in advance. The LogisticsPostalAddress
table is a ValidTimeState
table, which means that changes to the records in this table create a new version. We can also say that the address will change at a specific date/time. When the interface shows the current record, it does so by selecting a record where the current date and time fall between the ValidFrom
and ValidTo
fields.
A good example of this in use is a sales order. Against a customer, we can set up a primary location, which is associated with an address. When the sales order is created, the current address from LogisticsPostalAddress
is associated with it—not the location. Should the customer's address change, the address associated with the sales order record will not be changed, so the order will be delivered to the address that was set when the order was created.
The ValidTimeState
tables will be discussed in Chapter 10, Advanced Development Techniques.
The logistics tables define locations (LogisticsLocation
) and their addresses (LogisticsPostalAddress
). But looking at the location associated with a customer, we can have multiple roles, such as delivery address, invoice address, and so on. These location roles are defined in data by the LogisticsLocationRole
and LogisticsLocationRoleTranslation
tables. The translation table allows the client to present the role in the any of the supported languages.
To take the example of a customer, the tables involved in this are listed in the following table:
Table |
Relation |
Purpose |
---|---|---|
|
The global address book record | |
|
|
Relates the address book record to location records |
|
|
Associates the location's role with the party location record |
|
|
The location we are associating with the address book record |
|
|
The address of the location |
The other main address tables to be aware of are those starting with LogisticsAddress
. These are the backing tables for the address element, such as country, city, and so on. This can seem a little complicated, especially if we wish to create an address programmatically. Fortunately, Microsoft has written views that flatten some of this information for easier access. Some useful views are:
DirPartyPostalAddressView
LogisticsPostalAddressView
LogisticsEntityPostalAddressView
The DirPartyPostalAddressView
view is particularly interesting, as it also allows us to create records. Like all views in AX, it is read-only. However, we can write to the record buffer, and submit it to a class that will do all the hard work for us.
The code to create an address from a party record (for example,CustTable.Party
) is as follows:
DirPartyPostalAddressView addressView; addressView.Party = _partyRecId; addressView.IsLocationOwner = NoYes::Yes; addressView.IsPrimary = _isPrimary; addressView.State = _state; // must exist or blank addressView.County = _county; // must exist or blank addressView.City = _city; addressView.Street = _street; addressView.ZipCode = _postalcode; addressView.CountryRegionId = _country; // must exist if(_isPrimary) addressView.LocationName = "Primary address"; addressView = DirPartyLocationEntity::createAddressForParty( addressView, _roles);
The _roles
variable is a container of record IDs from LogisticsLocationRole
. To get the business role, use the following lines of code:
RefRecId roleId = LogisticsLocationRole::findBytype( LogisticsLocationRoleType::Business).RecId;
If we wish to pass a single role, we can use the syntax [roledId]
as the parameter. We can use a similar technique to create an electronic address in this case:
private RefRecId createElectronicAddress( LogisticsElectronicAddressMethodType _type, Description _description, LogisticsElectronicAddressLocator _locator, PhoneLocal _extension, RefRecId _locationRecId, NoYes _isPrimary, NoYes _isMobile, NoYes _isSMS) { LogisticsElectronicAddress electronic; LogisticsLocation location; RefRecId childLocRecId; // A location record id would be passed if we // were adding an electronic address // to a logistics address, for example, head office phone number select firstOnly RecId from location where location.ParentLocation == _locationRecId && location.IsPostalAddress == false; childLocRecId = location.RecId; if(childLocRecId == 0) { childLocRecId = LogisticsLocation::create(_description, false, _locationRecId).RecId; } electronic.clear(); electronic.initValue(); electronic.Type = _type; electronic.Description = _description; electronic.IsMobilePhone = _isMobile; electronic.Locator = _locator; electronic.Location = childLocRecId; electronic.LocatorExtension = _extension; electric.IsInstantMessage = _isSMS; if(_isMobil == NoYes::No) electric.IsPrimary = _isPrimary; electric.insert(); return electric.RecId; }
18.188.96.5