Extending the v8.x Infor CRM (formerly Saleslogix) Entity Model to Allow for Extended Entity Audit Logging

One of the great things about the entity model in the Infor CRM web is the ability to easily set up auditing on changes to fields in an entity. All you need to do is set up a history table and then simply check the entity properties you wish to have audited.

One shortcoming that has always existed is the ability to audit extended entities off the main entity. These extended entities represent one-to-one tables. In the Application Architect you can define the table to record History based on the parent table by default. However if you choose to do that and then attempt to make a change in the web client to one of the extended entities property you get a casting error. For instance if you have an extended entity off ITicket called ITicketExt you would get an error when you try to record a history change to a property in the ITicketExt entity saying unable to convert ITicketExt to ITicket. This is because the Ticket history table expects a parent of type ITicket, not ITicketExt. If we look at how a entity is implemented (which is where the auditing is wired up) we can see it contains a method called NewAuditEntry. This is where the casting error happens. Here is that code:

        public override object NewAuditEntry()
        {
            IAuditTable at = (IAuditTable)EntityFactory.Create(typeof(ITicketHistory));                      
            at.SetEntity(this);            
            return at;
        }

We can see that is is attempting to set the audit table objects parent to this. This works great if this id the Ticket but if you are trying to use the extended entity of TicketExt you will get the cast error on this line. It would be sweet if we could just change the line

at.SetEntity(this);

to

at.SetEntity(this.Ticket);

Well lets do it!

These implementations are generated dynamically when you do a web platform build. The implementations use a code template file that generates the actual implementation which is then compiled into the Sage.Saleslogix.Entities.dll. Knowing that a template is in use we can modify the core template to handle extended entities.

WARNING! MODIFY THIS ONLY IF YOU KNOW WHAT YOU ARE DOING. INCORRECT MODIFICATION CAN PREVENT YOU FROM BUILDING YOUR ENTITY MODEL. MAKE A BACKUP FIRST BEFORE MAKING THESE CHANGES SO YOU CAN REVERT TO NORMAL IF YOU BREAK SOMETHING!

Here is step by step run down.

  • Open the Application Architect and the Virtual File System Explorer.
  • In the Model/Entity Model/Code Templates/Entity folder you will see a file “Default-Class-Saleslogix.Class.codetemplate.xml
  • BACK THIS FILE UP
  • Open the file.
  • Search for “NewAuditEntry”
  • You should see a method that looks like this:
        public override object NewAuditEntry()
        {
<#  if (isHistoryTable) { #>
            IHistory h = (IHistory)EntityFactory.Create(typeof(<#= historyTypeName #>));
<#   if (entity.Name == "Address") { #>
            SetAddressHistoryKeys(h);
<#   } else { #>
<#    var aco = metadata.FindACOParent; #>
<#    if (aco != null) { #>
            h.<#= aco.Name #>Id = Id;
<#     if (aco.Name != "Account" && entity.Name != aco.Name) { #>
            h.AccountId = (string)<#= aco.Name #>.Account.Id;
            h.AccountName = <#= aco.Name #>.Account.AccountName;
<#     } #>
<#    } #>
<#   } #>
            return h;
<#  } else { #>
            IAuditTable at = (IAuditTable)EntityFactory.Create(typeof(<#= historyTypeName #>));           
            at.SetEntity(this);
            return at;
<#  } #>
        }

At the end we can see the 3 lines of code shown earlier with the at.SetEntity() line. There are parts of this code enclosed in

<# #>

tags. This is the code which runs during the construct of the web platform. The lines without these tags are what actually gets rendered into the final implementations. Knowing this we can add an if statement checking if this is an extension entity, and if so changing our SetEntity input parameter accordingly, like so:

<#if (entity.IsExtension && IsEntityIncluded(entity.ExtendedEntity)) { #>      
            at.SetEntity(this.<#= entity.ExtendedEntity.Name #>);
<#     } #>
<#   else { #>           
            at.SetEntity(this);
<#     } #>      

The final complete method looks like:

        public override object NewAuditEntry()
        {
<#  if (isHistoryTable) { #>
            IHistory h = (IHistory)EntityFactory.Create(typeof(<#= historyTypeName #>));
<#   if (entity.Name == "Address") { #>
            SetAddressHistoryKeys(h);
<#   } else { #>
<#    var aco = metadata.FindACOParent; #>
<#    if (aco != null) { #>
            h.<#= aco.Name #>Id = Id;
<#     if (aco.Name != "Account" && entity.Name != aco.Name) { #>
            h.AccountId = (string)<#= aco.Name #>.Account.Id;
            h.AccountName = <#= aco.Name #>.Account.AccountName;
<#     } #>
<#    } #>
<#   } #>
            return h;
<#  } else { #>
            IAuditTable at = (IAuditTable)EntityFactory.Create(typeof(<#= historyTypeName #>));           
<#if (entity.IsExtension && IsEntityIncluded(entity.ExtendedEntity)) { #>      
            at.SetEntity(this.<#= entity.ExtendedEntity.Name #>);
<#     } #>
<#   else { #>           
            at.SetEntity(this);
<#     } #>            
            return at;
<#  } #>
        }

After making this change, save your template file. Close the Application Architect and re-open (these templates are chached on opening). Do a build and now the implementation on an extension entity will have its method like this:

        public override object NewAuditEntry()
        {
            IAuditTable at = (IAuditTable)EntityFactory.Create(typeof(ITicketHistory));                      
            at.SetEntity(this.Ticket);            
            return at;
        }

TA-DA!

ABOUT THE AUTHOR

Kris Halsrud

Kris Halsrud is a Senior Analyst / Developer for Customer FX Corporation.

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe To Our Newsletter

Join our mailing list to receive the latest Infor CRM (Saleslogix) news and product updates!

You have Successfully Subscribed!