Over in Ronald's blog he has a good trouble shooting list for when your callout does not work.
He has missed one thing out though that still affects one of my sites.
In it the callout did not fire because the entity was updated by a workflow. Someone changed a bit value to indicate they had checked the record, the record had a status on a dropdown updated and that update should have then triggered a web service call to inform a third party of its existence and passage through a QA procedure.
After some experiments we discovered that if you changed the status of the dropdown by hand and saved it the callout was fired but if we allowed the workflow to change it then it did not. On googling MSDN this turned out to be a known issue pre rollup 1 but my client was only a week from go-live when we (I) discovered this. They have still not got around to applying the roll up as far as I am aware and the workflow was altered to create a task against an admin person to change the status by hand.
Not pretty but worth knowing.
With thanks to the ICU-MSCRM blog who started me off in this direction.
A customer is looking to use the CRM to run their recruitment and training company. They are not particularly IT-literate and my spec as been a series of required fields and free reign in developing ways of tracking the recruitment and training progress of candidates.
Each training candidate has a series of targets that consist of a target date, an actual date and some details. Depending on the course they could have between 1 and 16 different targets to reach. This became a custom entity with a relationship to the Contact account (renamed Person).
Each Account (renamed Company) has can either have a collection of trainees associated with it or a number of positions needing filled. Or both. Trainee is another custom entity with some basic details of the person and who their supervisor is and the job vacancy is just a custom entity with a couple of details about the job (temp/perm, hourly rate that kind of thing) related back to the person to record who was recruited for the job.
To keep things simple for the user I decided to use an IFrame to show the lists of vacancies, trainees and targets in the relevant entities. This was because I felt the company would benefit from having this information in line with the main application form rather than jumping about the categories in the left hand pane.
So using the IIS logs I tracked down the URL for each of these lists so that they can be included in the Iframe source. It turns out that they have a relatively straight forward format. Each of the lists differed only by the value of the tabSet at the end of the line (assuming we don't count the object id because it is being passed as a variable anyway).
I also work with two shortcuts on my desk, one to take me to CRM and one to take me direct to the Loader.aspx page so that I can view page sources. This gave me the ability to find the ID for each of the menu entries in the left hand pane relating to the lists themselves. For simplicity I will remove them to avoid confusion. Lastly I did not want to waste time trying to display anything when I was creating a new account.
So the code for the account form was :
if (crmForm.FormType == 1)
document.all.IFRAME_recruitment.src="/sfa/accts/areas.aspx?oId=" + crmForm.ObjectId + "&oType=1&security=852407&tabSet=account_new_vacancies";
document.all.IFRAME_trainees.src="/sfa/accts/areas.aspx?oId=" + crmForm.ObjectId + "&oType=1&security=852407&tabSet=account_new_trainees";
I have a client that requires a separate application integrated with CRM. They need to record when a third party assigns a number to a customer they have. The third party will first assign a temporary number to the customer on first registration with their web service. After a number of days the third party will have verified the registration by snail mail and will issue a permanent number.
On first registration the return value of the web service being called is the temporary number, we record this and send it to CRM to be put against the customer's record. We then poll another web service with the temporary number until a permanent number is returned. This is then recorded against the CRM record.
However my client has manual process to go through before the customer record can be updated and they don't want the integration code to automatically update the custom field. To this end they want tasks created for staff to phone the customer and talk to them as part of the process. They also want to be able to track when a customer has not received their permanent number in due course.
To this end I need to create a number of tasks. First is to inform the CRM operator that a new customer has registered on the public facing website (not automatically brining the details across as hoaxers can enter crap). Second is to log a task when the customer is registered with the third party and their temp number is assigned. Third is to raise a task when the third party comes back with a permanent number.
Due to the way they want to process this information and report on it when the second task is raised the operator assigns it to the account it references (needs a human to do this to mitigate matching problems). When the integration code receives the permanent code it closes off the temp code task and opens a task for the permanent one (again phone calls have to be made before the record can be updated). In reporting we will simply run off all tasks that are open with a certain subject and are more than x days old.
As this requires me programmatically manipulate tasks (something I had not done before) I thought I would post my solution here to show how easy it is. It took me more time to write this post that produce this partial solution.
public Guid CloseTask(Guid taskid, string newMessage)
task theTask = (task) _service.Retrieve(EntityName.task.ToString(), taskid, new AllColumns());
theAccount = (account) _service.Retrieve(EntityName.account.ToString(),theTask.regardingobjectid.Value, new AllColumns());
SetStateTaskRequest closeTask = new SetStateTaskRequest();
closeTask.EntityId = theTask.activityid.Value;
closeTask.TaskState = TaskState.Completed;
closeTask.TaskStatus = -1; //To allow CRM to decide
SetStateTaskResponse response = (SetStateTaskResponse) _service.Execute(closeTask);
task t = new task();
t.description = newMessage;
t.subject = "Task Example";
t.regardingobjectid = new Lookup();
t.regardingobjectid.type = EntityName.account.ToString();
t.regardingobjectid.name = theAccount.name;
t.regardingobjectid.Value = theAccount.accountid.Value;
Please note that this is not a copy of the production code, this is just what I have done today on my laptop given that my client is closed for the Glasgow Fair. For starters I need to define a tight list of columns for the returned objects and not tie up bandwidth bringing across 99% of both objects when I am not using them.
Obviously the thing to note is the use of the SetStateTaskRequest object to change the state of the task rather than changing the statuscode member of the task object. There is one of these special classes for each of the entities that you would use in CRM and changing the state of them requires this method in order to do it properly.
My test bed first creates a simple task, then when I am running in break I can alter the task created to point to an account, I then run this function above and a new task is created, taking the original account reference with it.
In production the system storing new registrations from my client's web site will hold a table that will have the temp number, the original task guid and the date it was all created as well as space for the permanent number when it arrives. This allows me to pass in the task guid and take the matched account to the next task that is opened.
Any comments on this or any way you think it could be done better I would be delighted to hear.
Recently I had cause to set my sales manager up with Systems Customizer rights to allow him to alter a couple of picklists on our internal implementation. However we discovered that when he went to the Settings pane on the web front end he did not have the admin rights to see the Customization screens. I set him up with full sys admin rights in case there was something I had done to the profiles that needed overridden and got him to log back in. He shut everything down and went back in. He could now see the Customization options.
A week later he came back through to see me, he said the same problem not being able to see the Customization option. I had not changed any security profile for him so I was a bit concerned. On investigation I noted that his profile was correct and that he should in theory see it. When I looked at his computer I noticed he had Outlook running which had the CRM client installed.
On a hunch I got him to shut down the web client and reopen it, same problem. Then I got him to shut down both the web client and his Outlook, reopened the web client and lo and behold the full range of systems admin tools had re-appeared on the Settings bar.
Obviously the Outlook client is not designed for the admin tools but it would appear that besides not showing them it also does something to the security objects held in the machine that stops the web client from working as well.
Hello, I have finally taken the plunge and started my first technology blog. I hope that I can keep this up as most MSCRM blogs tend to fall by the wayside.
I intend this to be a repository of my thoughts, tips and tricks relating to MSCRM and the solutions I provide my customers with in my day-to-day job. I am still learning but I have already completed some really interesting bits of work.
Anyway, feel free to email me any suggestions or leave a comment on the blog.