Warning: This customization is unsupported by Microsoft. Make sure you back up your files in case any undesired affects happen. Research any unsupported changes and test them on your development environment before making any changes to production!
One of the limitations of custom entities created in CRM 4.0 is the inability to perform an entity rollup to show all of a custom entity's activities and the activities of its child entities. A working example of this rollup behavior is exhibited when you look at the Activity or History page for an Account entry. You will see all the Activities for the Account's child record as well as all the Activites for the Account's Contacts(its child entity).
To overcome this limitation, I did some research on how the Activity and History pages for Accounts generate the rollup. I fired up SQL Server Profiler to intercept any queries made to the SQL Server database while I loaded an Account's Activity page within Internet Explorer. The profiler showed that the stored procedure p_RollupByAccount performed all the heavy lifting. The p_RollupByAccount creates a temporary table called #RollupIds which stores all the GUIDs for the Account and its child Contacts and then a subsequent query pulls in all the activities for GUIDs in this table.
Next, I created a custom entity called Manager (cc1_manager) and created a 1:N relationship to Contact (cc1_manager_to_contact) as shown below. Note that the Relationship Attribute is titled cc1_parentmanagerid, which we will use later.
Open up the p_RollupByAccount stored procedure and create a backup as we'll be making some changes. The original code has this section at the beginning of the stored procedure to find the currently opened Account, its sub Accounts and their Contacts.
declare @Level int set @Level = 1 while (@@ROWCOUNT != 0 and @Level <= 5) BEGIN set @Level = @Level + 1 insert into #RollupIds (RollupId) ( select distinct AccountId from AccountBase acc (NOLOCK) join #RollupIds ri on ( acc.ParentAccountId = ri.RollupId ) where acc.AccountId not in ( select RollupId from #RollupIds ) ) END insert into #RollupIds (RollupId) ( select distinct ContactId from ContactBase (NOLOCK) where AccountId = @AccountId )We replace the section above with our modified code below. The code starts by checking to see if the @AccountId GUID passed in is in the cc1_managerBase table. If it is, we'll know it's a Manager GUID and look for Contacts that have a cc1_parentmanagerid set to the Manager GUID. If the GUID passed in is in the AccountBase table, we'll know it is an Account and process it using the original code.
declare @Level int set @Level = 1 -- ModifiedSQL: Conditional logic to check if passed in @AccountId guid is the guid of a Manager IF exists (SELECT cc1_managerId FROM cc1_managerBase WHERE cc1_managerId = @AccountId) Begin -- ModifiedSQL: custom code to add managerID to the Rollup table -- @Level has been removed as we have not set a Manager to Manager relationship to establish a Manager/Sub Manager hierarchy while (@@ROWCOUNT != 0) BEGIN insert into #RollupIds (RollupId) ( select distinct cc1_managerId from cc1_managerBase acc (NOLOCK) -- ModifiedSQL: look in custom entity table cc1_managerBase where acc.cc1_managerId not in ( select RollupId from #RollupIds ) ) END insert into #RollupIds(RollupId) ( select distinct c.ContactId from ContactBase as c (NOLOCK) join dbo.ContactExtensionBase as ceb (NOLOCK)ON c.ContactId = ceb.ContactId join #RollupIds ri on ( ceb.cc1_parentmanagerid = ri.RollupId ) where not exists( select RollupId from #RollupIds where c.ContactId = RollupId ) ) End -- ModifiedSQL: Conditional logic to check if passed in @AccountId guid is the guid of an Account If exists (SELECT AccountID FROM AccountBase WHERE AccountID = @AccountId) Begin -- original code for account while (@@ROWCOUNT != 0 and @Level <= 5) BEGIN set @Level = @Level + 1 insert into #RollupIds (RollupId) ( select distinct AccountId from AccountBase acc (NOLOCK) join #RollupIds ri on ( acc.ParentAccountId = ri.RollupId ) where acc.AccountId not in ( select RollupId from #RollupIds ) ) END insert into #RollupIds (RollupId) ( select distinct ContactId from ContactBase (NOLOCK) where AccountId = @AccountId ) EndNow that we have the database half completed, we'll move onto the modifications to the UI. By design, Custom Entities call a different aspx page to pull up all the Activitiy and History items. Whereas Accounts call the page "/sfa/accts/areas.aspx" Custom Entities call "userdefined/areas.aspx" Unfortunately the page used by Custom Entities does not use a stored procedure to generate the entity rollup list, since entity rollups are not a feature of custom entities. This is the reason why I've chosen to use the p_RollupByAccount stored procedure and repuropose "/sfa/accts/areas.aspx" to work for both Accounts and our Custom Entity Managers.
Navigate to the Customizations->Manager->Form->Form Properties->OnLoad Edit page to switch out the old "userdefined/areas.aspx" activity and history page with "/sfa/accts/areas.aspx" The javascript code below locates the "Activity" and "History" elements on the DOM of the page and replaces their onclick behaviour to open up "/sfa/accts/areas.aspx" instead. Since "/sfa/accts/areas.aspx" calls p_RollupByAccount by feeding in the GUID of the currently open object(be it an Account or a Manager), it'll grab the list of activity or history items just as we planned.
var navActivities; var navActivityHistory; navActivities = document.all.navActivities; navActivityHistory = document.all.navActivityHistory; if (navActivities != null) { navActivities.onclick = function() { loadArea('areaActivities'); document.all.areaActivitiesFrame.src="/sfa/accts/areas.aspx?oId=" + crmForm.ObjectId + "&oType=1&security=852023&tabSet=areaActivities"; };} else { alert("navActivities Not Found"); } if (navActivityHistory != null) { navActivityHistory.onclick = function() { loadArea('areaActivityHistory'); document.all.areaActivityHistoryFrame.src="/sfa/accts/areas.aspx?oId=" + crmForm.ObjectId + "&oType=1&security=852023&tabSet=areaActivityHistory"; };} else { alert("navHistory Not Found"); }
Publish the changes to the Manager entity and you now have the ability to have activity entity rollups for custom entities. Use it wisely!