Friday, May 30, 2008

Plugins triggered off the SetState Message

I was writing a plugin that needed to be triggered off of the SetState event, or message and I was having issues getting it to actually fire. I wrote out the plugin, registered it and set it all up but when I would change the state of the record I registered the plugin against nothing would happen. Nothing in the logs, nothing in the system logs, nothing anywhere.

After some hunting I found out that in order to get plugins to work off of the SetState message you must also register the plugin against the SetStateDynamicEntity message as well. I had to completely unregister my plugin and then register it. That piece may have been because I forgot to make my class public in my assembly but that was an unfortunate oversight on my part.

Any way, I hunted and fought this for a fair number of hours before I found my problem. Hopefully this will help someone else avoid the spending the same amount of time I spent looking for a solution to their problem.

David Fronk
Dynamic Methods Inc.

16 comments:

Singh said...

Hi I tried that too. I had same problem. The problem is not just that I wrote a pluin on setstate of the opportunity. The pluggin get triggered only if I open a closed opportuntity. It does not work if I close an opportuntity as won or lose.

Dynamic Methods said...

Singh,

The Opportunity is actually a special case. I ran into this issue shortly after I ran into the SetState issue. The Opportunity has 2 special methods for when it changes state, the methods are called Win and Lose. So, to access the trigger of the state changes of an Opportunity you need to have code going off of the Win trigger, Lose trigger, as well as the SetState and SetStateDynamic triggers.

Register your plugins for the Opportunity that way and you should be all set.

David Fronk
Dynamic Methods Inc.

Ryan Price said...

This problem bit me too, didn't think I'd have to enable both triggers at the same time (I'd tried them both on their own).

Anyway, I have another special case to add (pun intended).

If you 'close' an incident (case), it will fire the 'Close' trigger, not the 'SetState' trigger.

If you 'reactivate' a closed case, it fires the 'SetState' trigger.

If you change the 'Status Reason' (a.k.a. StatusCode), but don't close or cancel the case (i.e, it's still open), then it fires the 'Update' trigger.

'Canceling' a case seems to fire the 'SetState' trigger as well (even though there's a 'Cancel' trigger lurking in there).

/Ryan Price
Activiser Limited

Dynamic Methods said...

Ryan,

Thanks for the additional notes on triggers to watch out for.

David Fronk
Dynamic Methods Inc.

Ramzi GHRIBI said...

Hi,
i just need to close a letter acampaignactivity ! but in the plugin registration if i choose close message i cant choose campaignactivity ! so with message shall i use? thx

Dynamic Methods said...

Ramzi GHRIBI,

I would suggest trying the SetState message to close/complete a CampaignActivity. That should close it for you. Otherwise I may not fully understand your question.

Hope this helps,

David Fronk
Dynamic Methods Inc.

Vishnu said...

Could I please explain a little bit more how to write plugin for win and lose trigger or I would be highly obliged, if a piece of code is attached.

Thanks

Dynamic Methods said...

Vishnu,

Thanks for the comment, please check my post for today. It should be up shortly and should answer your question.

David Fronk
Dynamic Methods Inc.

Andrew said...

Hi David,

How can I track the time of "Resolve a case"?

I need to track the time of duration for a case.

duration = start time - close time


I tried "close" message for incident and also the "create" message for
incidentresolution under child pipeline. I am using pre-stage.


My plug-in can be triggered. But I can update the case with my
calulcated time.
It is said "The object cannot be updated because it is read-only".


How can I resolve this issue?


Thanks

Dynamic Methods said...

Andrew,

I'm not 100% sure which part of the time you are trying to capture so I'm going to take a stab and see. I think you want to see the total time it took to resolve a case from the time it was opened to the time it was finished. If I'm incorrect in that understanding I apologize, just let me know.

As for how I would go about doing this you could try using the CalculateTotalTimeIncident Message first to calculate your total time. If you don't I would change your formula to close time - start time. To get your closed time you could use the modified on in your PreImage data on the CloseIncident Message. But it really depends on where you are planning on storing your duration data that will determine which plugin trigger you should use.

If you want the duration on the Case then you will want to use the PreStage of the CloseIncident. But if you want it on the CaseResolution Activity then you will need the PreCreate of the CaseResolution. Just like you stated in your comment so I think you're on the right track. It sounds like you want to keep the data on the Case so here's what I would do.

Since it appears you want the data on the Case I would set up a plugin to trigger on the PreCloseIncident trigger. Also, since there is no out of the box duration field on the Case you will need to set up your own custom duration or number field to track the total duration of a Case. Run you calculations and inject your update to the custom duration field into the entity that is being pushed to the database. It should not be read-only yet so you should be okay. I'm not sure why you are getting that message in your code currently, my only thoughts are that you are either updating a field that isn't allowed to be updated via the API's or your plugin is really set up to update the Case on the PostCloseIncident.

I hope that helps a little. You really are on the right track, it just sounds like there are a couple of hurdles to get over and then you'll be all set.

David Fronk
Dynamic Methods Inc.

Andrew said...

Thanks, David,

Now I am using the Pre Stage of Update incident. It is working as what I want.

But the PreStage of the CloseIncident or PreCreate of the CaseResolution, both of them are not working. Because they are happened after the case update.

I feel it is by design.

Andrew

Dynamic Methods said...

Andrew,

Thanks for the follow up. It does make logical sense that they would update before closing out the Case, that was a great catch.

I agree, it seems like that order of operations is by design.

Thanks again,

David Fronk
Dynamic Methods Inc.

Anonymous said...

Hai Andrew

I need to trigger a plugin when a case is resolved, i have registered my code in CLOSE event. When executed the case displays a error "The given Key is not present in the Dictionary"

entity = (DynamicEntity)context.InputParameters.Properties["Target"];
DynamicEntity Entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
string s = ((Key)Entity[Entity.Name + "id"]).Value.ToString();
Guid oppid = new Guid(s);

Entity.Properties["new_resolveddate"] = CrmTypes.CreateCrmDateTimeFromUser(DateTime.Now.AddDays(7));

Pls correct me.
Thanks in advance

Dynamic Methods said...

Hai Andrew,

The error "The given Key is not present in the Dictionary" means that whatever property you are looking for isn't in the propertybag. This typically is due to a misspelling or typo on the part of the developer. I ran into this dozens of times everytime I code a plugin. Debug your code and see which line is actually causing the error, but my bet would be on the Entity.Properties["new_resolveddate"] = ...

First off, in order to modify a field, or one of the properties, you need to create a corresponding property to be added to the PropertyBag of the DynamicEntity. Look at the "Using Dynamic Entities" in the CRM 4.0 SDK, particularly about the updating portion. That should help you out.

Secondly my guess would be that "new_resolveddate" is spelled differently in the backend. I can't tell you how many times I've transposed letters in the backend and not realized it for months to come.

Take a look at that article and I think you'll be able to figure it out.

David Fronk
Dynamic Methods Inc.

Mac said...

It would seem to me that whoever in Redmond decided it was a good idea to have 20-30 events to trap should have their head examined. I hate the 4.0 plugin model. It was so much easier to write callouts in 3.0.

Just give me 1 event to trap (change) and a pre and post image and I can easily inspect what's changed in the before/after attributes.

KISS!!

Dynamic Methods said...

Mac,

One thing to consider, even if you had one and only one method you would have to handle all of the sub-events. If a record changed from active to inactive and you were supposed to trigger an update your update may fail because the API's would no longer allow it.

Don't get me wrong, the API's aren't quite looking like pricing matrixes or licensing options, but from a programmer's prespective, we're not far off. It would definitely be nice to have these triggers simplified, or more easily accessible/known. But with the numerous options you have a lot of flexibility and segregation of events to keep code from interfering with other code.

Submit you comments to Microsoft's feedback page for CRM, the team is actually really good at reading through the requests.

David Fronk
Dynamic Methods Inc.

Post a Comment