Tuesday, December 14, 2004

Microsoft CRM Customization -- Programming Closed Email Activity

A Neat thing I found on Microsoft CRM Customization -- programming Closed Email Activity

One of the roles of our Exchange Event Handler/Sink is creation MS CRM Closed Activity in handling incoming and outgoing email messages. The interaction with Microsoft CRM uses two approached – using MS CRM SDK (handling inbound and outbound XML messages) and via direct access to MS CRM Database. Let’s first look at the Closed Activity creation algorithm:
1.First we need to understand the entity we need to create activity for: Account, Lead or Contact. The selection should use specific criteria – in our case this is email address:if ((crmAccount = crmConnector.GetAccount(mailboxFrom)) != null) {}else if ((crmContact = crmConnector.GetContact(mailboxFrom)) != null) {}else if ((crmLead = crmConnector.GetLead(mailboxFrom)) != null) {}
2.Then we have to get GUID of MS CRM user, who owns this entity, C# code like this:crmUser = crmConnector.GetUser(crmAccount.GetOwnerId());
3.Next step is closed Activity creation:emailId = crmConnector.CreateEmailActivity(crmUser.GetId(), Microsoft.Crm.Platform.Types.ObjectType.otAccount, crmAccount.GetId(), Microsoft.Crm.Platform.Types.ObjectType.otSystemUser, crmUser.GetId(), crmAccount.GetEmailAddress(), crmUser.GetEmailAddress(), sSubject, sBody);
4.The method to create closed activity:public Guid CreateEmailActivity(Guid userId, int fromObjectType, Guid fromObjectId, int toObjectType, Guid toObjectId, string mailFrom, string mailTo, string subject, string body) {try{log.Debug("Prepare for Mail Activity Creating");// BizUser proxy objectMicrosoft.Crm.Platform.Proxy.BizUser bizUser = new Microsoft.Crm.Platform.Proxy.BizUser();ICredentials credentials = new NetworkCredential(sysUserId, sysPassword, sysDomain);bizUser.Url = crmDir + "BizUser.srf";bizUser.Credentials = credentials;Microsoft.Crm.Platform.Proxy.CUserAuth userAuth = bizUser.WhoAmI();// CRMEmail proxy objectMicrosoft.Crm.Platform.Proxy.CRMEmail email = new Microsoft.Crm.Platform.Proxy.CRMEmail();email.Credentials = credentials;email.Url = crmDir + "CRMEmail.srf";// Set up the XML string for the activitystring strActivityXml = ";strActivityXml += ";strActivityXml += ") + "]]>";strActivityXml += ";strActivityXml += userId.ToString("B") + ";strActivityXml += ";// Set up the XML string for the activity partiesstring strPartiesXml = ";strPartiesXml += ";strPartiesXml += " + mailTo + ";if (toObjectType == Microsoft.Crm.Platform.Types.ObjectType.otSystemUser) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otSystemUser.ToString() + ";}else if (toObjectType == Microsoft.Crm.Platform.Types.ObjectType.otAccount) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otAccount.ToString() + ";}else if (toObjectType == Microsoft.Crm.Platform.Types.ObjectType.otContact) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otContact.ToString() + ";}else if (toObjectType == Microsoft.Crm.Platform.Types.ObjectType.otLead) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otLead.ToString() + ";}strPartiesXml += "+ toObjectId.ToString("B") + ";strPartiesXml += ";strPartiesXml += Microsoft.Crm.Platform.Types.ACTIVITY_PARTY_TYPE.ACTIVITY_PARTY_TO_RECIPIENT.ToString();strPartiesXml += ";strPartiesXml += ";strPartiesXml += ";strPartiesXml += " + mailFrom + ";if (fromObjectType == Microsoft.Crm.Platform.Types.ObjectType.otSystemUser) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otSystemUser.ToString() + ";}else if (fromObjectType == Microsoft.Crm.Platform.Types.ObjectType.otAccount) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otAccount.ToString() + ";}else if (fromObjectType == Microsoft.Crm.Platform.Types.ObjectType.otContact) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otContact.ToString() + ";}else if (fromObjectType == Microsoft.Crm.Platform.Types.ObjectType.otLead) {strPartiesXml += " + Microsoft.Crm.Platform.Types.ObjectType.otLead.ToString() + ";}strPartiesXml += "+ fromObjectId.ToString("B") + ";strPartiesXml += ";strPartiesXml += Microsoft.Crm.Platform.Types.ACTIVITY_PARTY_TYPE.ACTIVITY_PARTY_SENDER.ToString();strPartiesXml += ";strPartiesXml += ";strPartiesXml += ";log.Debug(strPartiesXml);// Create the e-mail objectGuid emailId = new Guid(email.Create(userAuth, strActivityXml, strPartiesXml));return emailId;}catch (System.Web.Services.Protocols.SoapException e){log.Debug("ErrorMessage: " + e.Message + " " + e.Detail.OuterXml + " Source: " + e.Source);}catch (Exception e) {log.Debug(e.Message + "\r\n" + e.StackTrace);}return new Guid();}
5.To make the activity just created be shown correctly you need to setup it’s flags according to MS CRM standards:public void UpdateActivityCodes(Guid emailId) {try {OleDbCommand command = conn.CreateCommand();command.CommandText = "UPDATE ActivityBase SET DirectionCode = (?), StateCode = (?), PriorityCode = (?) WHERE ActivityId = (?)";command.Prepare();command.Parameters.Add(new OleDbParameter("DirectionCode", Microsoft.Crm.Platform.Types.EVENT_DIRECTION.ED_INCOMING));command.Parameters.Add(new OleDbParameter("StateCode", Microsoft.Crm.Platform.Types.ACTIVITY_STATE.ACTS_CLOSED));command.Parameters.Add(new OleDbParameter("PriorityCode", Microsoft.Crm.Platform.Types.PRIORITY_CODE.PC_MEDIUM));command.Parameters.Add(new OleDbParameter("ActivityId", emailId));log.Debug("Prepare to update activity code " + emailId.ToString("B") + " in ActivityBase");command.ExecuteNonQuery();}catch(Exception e) {log.Debug(e.Message + "\r\n" + e.StackTrace);}}public void UpdateActivityQueueCodes(Guid emailId, Guid queueId) {try {OleDbCommand command = conn.CreateCommand();command.CommandText = "UPDATE QueueItemBase SET Priority = (?), State = (?), QueueId = (?) WHERE ObjectId = (?)";command.Prepare();command.Parameters.Add(new OleDbParameter("Priority", Microsoft.Crm.Platform.Types.PRIORITY_CODE.PC_MEDIUM));command.Parameters.Add(new OleDbParameter("State", Microsoft.Crm.Platform.Types.ACTIVITY_STATE.ACTS_CLOSED));command.Parameters.Add(new OleDbParameter("QueueId", queueId));command.Parameters.Add(new OleDbParameter("ObjectId", emailId));log.Debug("Prepare to update activity queue code " + emailId.ToString("B") + " in QueueItemBase");command.ExecuteNonQuery();}catch(Exception e) {log.Debug(e.Message + "\r\n" + e.StackTrace);}}

Source : http://www.articlecity.com/


Tuesday, December 07, 2004

Heat and Crystal Sub Reports

There appears to be a bug in All versions of HEAT 8 (including the newly released HEAT 8.03) It causes Call Logging to GPF when running a custom report with one or more sub-reports.
When you run a report from the Report menu in Call Logging and then close the report after viewing the details it causes Call Logging to crash with a CallLog32.exe error.

There appears to be no work around at this point although it has been logged as a bug.

Monday, December 06, 2004

Goldmine and the BDE

Goldmine Setup
--------------

- if it doesn't find a local bde installation (usually denoted by idapi32.dll)
it will setup the BDE registry entries in
HKLM\SOFTWARE\Borland\Database Enginepointing to the version of BDE from where setup is run (in this case on the Goldmine drive)

- if it does find a local bde installation, it will attempt to merge the Goldmine settings
(stored in ..\BDEShare\IDAPI32.cfg) with the setting of those of the local installation.
(IDAPI32.cfg in the local BDE folders)

- Additionally Setup creates three ODBC connections:
GMSales_Mktg
GMSales_Mktg System
ManCon Metric Log (Used when Goldmine is integrated with HEAT)

These connections are setup mainly for the user'c convenience when building reports. If no applications or reports depend on them, they can be safely deleted.

To manually run BDE - (Run BDEADMIN.exe from file:////GoldmineServer/GoldmineShare/Setup/BDEShare)
or C:\Program Files\Common Files\BDEShare)

When Goldmine was initially installed, the BDE engine was configured to include the
following MSSQL Aliases:

Goldmine These are 'MSSQL aliases' pointing back to the server directly
Goldmine_Test (ie not ODBC dependant)

These describe where the Goldmine databases are sitting
eg Config Settings:
Server Name: DatabaseServer
Database Name: Goldmine
User Name: sa (must be a specifed username - cannot use Windows pass-through)
... etc

These settings can be edited manually, however as it is in binary format, it cannot easily be parsed through scripting. The entire file can be migrated between systems to transfer these settings. And is prone to corruption

- The BDE engine 'absorbs' ODBC connections present on any machine it is running on, and creates equivalent BDE Aliases - such that BDE applications can access data through ODBC connections.
These connections, in addition to 'pure' BDE aliases are stored entirely within Idapi32.cfg

Each BDE Alias is self contained with regards to database parameters and connectivity settings / optimisations eg: BLOB SIZE

- The BDE also requires registry entries which describe where the BDE engine and IDAPI32.cfg is sitting:

[HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine]
"DLLPATH"="g:\\setup\\bdeshare"
"CONFIGFILE01"="g:\\setup\\bdeshare\\idapi32.cfg"

and also any database drivers that are loaded .... (various keys beneath the parent Database Engine Key)


Running Goldmine
----------------

- Username is extracted from Windows environment variables. The 'User.dbf' dbase file is checked from within the Goldmine program directory to see whether the current username exists in the file (with a blank password).
If the user exists, and the password is blank, the goldmine load process will continue
If the user does not exist, or the password is not blank, it will prompt
- Then the local Registry is checked for BDE settings
- IDAPI.DLL and CFG are searched for in location specified in registry - bombs out if it can't find them
- Checks GM.INI in the Server Program file directory for entries like the following:
[GoldMine]
GoldDir=MSSQL: GoldMine: dbo:
CommonDir=MSSQL: GoldMine: dbo:

This specifies which alias to use in the BDE engine when connecting to the database
- It resolves the two aliases
(GoldDir - Goldmine internal Database structure - screen layouts, etc)
(CommonDir - User Data portion of database)
This means the application and User Data portions of the database can be run on seperate servers if rqd
- Looks to the Goldir Alias, for (amongst others) a table called SPFILES
(This table is created through the front end, whenever new Goldmine databases are created)
It contains information similar to the following:

DIRNAMEDIRPATHUSERIDDIRCODEDBPASSWORDDRIVER
Goldmine ProductionMSSQL: Goldmine: dbo:12producGMSQLEncrypted PasswordMSSQL


It then follows the entries in the DIRPATH column depending on what matches the entries from GM.INI for CommonDir
If there is no match, or multiple matches, Goldmine will prompt for the database to be used, otherwise it will
a

Once the database has been resolved, the application then verifies the current user logon details against the USERID
column. The USERID column contains a Goldmine application group which controls application security

The current architecture for Goldmine is such that the BDE Settings in the registry of each local machine point to the BDE files on the Goldmine server. NOTE This CAN be a UNC path

On systems with PA Report Query or other applications that utilise BDE, conflicts may occur if any of the above configuration is modified. It should be possible to make apps co-exist with Goldmine so long as the above configuration is honoured.