tag:blogger.com,1999:blog-5183071091127729722024-03-18T05:09:05.312-04:00Gonzalo Ruiz - Dynamics CRMThis is a blog about my experiences with Microsoft Dynamics CRM, tips and tricks as well as news from the CRM community.Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.comBlogger102125tag:blogger.com,1999:blog-518307109112772972.post-14100023289668384702016-11-12T12:28:00.000-05:002016-11-12T12:28:02.903-05:00Que hay de nuevo en Dynamics 365: Mesa de Expertos interactiva Nov 2016<i>Para mis seguidores en español, la Comunidad CRM/365 tendra un evento muy oportuno para aprender sobre las novedades de Dynamics 365</i><br />
<i><br /></i>
No te pierdas la Mesa de Expertos este próximo miércoles 16 de Noviembre para aprender sobre las novedades del nuevo lanzamiento que estará disponible en Diciembre 2016 para CRM Online.<br />
<br />
Si tienes preguntas esta es tu oportunidad para que respondamos a tus preguntas. Simplemente envia tu pregunta por anticipado medio de Twitter con el hashtag #enfoco365.<br />
<br />
Para registrarte en el evento sigue este link: <a href="http://www.comunidadcrm.com/blog/event/16-noviembre-microsoft-dynamics-365-comienzo-una-nueva-mesa-expertos/#.WCdO8PkrKUk">http://www.comunidadcrm.com/blog/event/16-noviembre-microsoft-dynamics-365-comienzo-una-nueva-mesa-expertos/#.WCdO8PkrKUk</a><br />
<br />
Dependiendo del tiempo, haremos demostraciones de los nuevos features tal como la nueva navegación, el nuevo diseñador de procesos, los nuevos editable grids y el nuevo modelo de aplicaciones introducido por Dynamics 365. Esperamos verlos pronto!Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-75225313784616432722016-10-13T14:00:00.000-04:002016-10-13T14:00:11.496-04:00How to modify the Quick Create form for tasks and activities<div class="MsoNormal">
<i>This is one of those post that will explain a problem we
have in CRM but unfortunately there is no great solution I know so far for this
problem.</i><o:p></o:p></div>
<div class="MsoNormal">
<i><br /></i></div>
<div class="MsoNormal">
When you insert the social pane in a given entity, you will
notice that there is a quick way to add activities (phone call, task, etc.)
were the user does not have to leave the screen in order to add these records:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFYdyHi9fWQN77x2D2U5TypHZV8PL4Ldc6MGQDT2NLgIip7noIfFoZHr5aQjGLQRU7H-ZXttdy_qVpQRcYQanMTzpWrr0adH54ElwOHeHprbtp8BlosoyDOrU0QEKKrRIidsgFiC2NCDAe/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFYdyHi9fWQN77x2D2U5TypHZV8PL4Ldc6MGQDT2NLgIip7noIfFoZHr5aQjGLQRU7H-ZXttdy_qVpQRcYQanMTzpWrr0adH54ElwOHeHprbtp8BlosoyDOrU0QEKKrRIidsgFiC2NCDAe/s1600/1.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype
id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t"
path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_1" o:spid="_x0000_i1030" type="#_x0000_t75"
style='width:359.25pt;height:250.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
This makes the user experience great for adding tasks to a
given record quickly. However, while this is beautiful for customer demos, in
real life things are rarely this simple. For example, we’ve had to add another
required field on the task called “Task Type”, but how can I add this field on
this form?!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Things that I have tried:</b><o:p></o:p></div>
<div class="MsoNormal">
<b><br /></b></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]-->1.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Modify the quick create view on Task entity.
However, there are no “Quick Create” forms on task entity:<o:p></o:p></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnyZyFAMhD22KJUR7V0io9worE0NIjdqT7XbPqxng2y30ut10aPkP-VMWOaOhCOfswWTukcsUpLP_BHHacCO_SNugRr0cBKdt2OZXYvL4f7zfzzq1trFxBLN2HJ1RN8GulBZy4vTgYJGJ1/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnyZyFAMhD22KJUR7V0io9worE0NIjdqT7XbPqxng2y30ut10aPkP-VMWOaOhCOfswWTukcsUpLP_BHHacCO_SNugRr0cBKdt2OZXYvL4f7zfzzq1trFxBLN2HJ1RN8GulBZy4vTgYJGJ1/s1600/2.png" /></a></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="MsoListParagraphCxSpMiddle">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_2" o:spid="_x0000_i1029" type="#_x0000_t75" style='width:389.25pt;
height:154.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle">
<br /></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]-->2.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Try to see if the Quick Create forms are defined
at the parent entity “Activity”. However, this entity has no forms defined
since it is a special entity:<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRuX6k_ClPVZHCPt7JIlYptVHk5tvrWxVB9_d2qNrx3LBTiCeiZPKjLu_8PoSwYV18J4oWppJs6fMTPi4IYQtq5ExSABNcEKVbRHtiErVr1FlqTXE-MGmy7dRUQyCxknGH8A-d6uw2SSag/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRuX6k_ClPVZHCPt7JIlYptVHk5tvrWxVB9_d2qNrx3LBTiCeiZPKjLu_8PoSwYV18J4oWppJs6fMTPi4IYQtq5ExSABNcEKVbRHtiErVr1FlqTXE-MGmy7dRUQyCxknGH8A-d6uw2SSag/s1600/3.png" /></a></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="MsoListParagraphCxSpMiddle">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_3" o:spid="_x0000_i1028" type="#_x0000_t75" style='width:122.25pt;
height:109.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle">
<br /></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]-->3.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Then I thought maybe I can create my own quick
create form in the task entity and set the form priority higher so it supersedes
the one that comes out of the box. However, when I check the Quick Create form
order I can only see my form:<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh68l9mqE6gHSNQcXHoeGSgSznXAhdCzMrOkg_HmvsbXO3YOcMs5nUBJm65uhW740lccBR9fKzGA2kEsXsuEL8p1MfCM5kieGwdSzY0pM9nuYQeI3wMPNwIvVcvVgNeb3wfLDcVxU-nl04H/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh68l9mqE6gHSNQcXHoeGSgSznXAhdCzMrOkg_HmvsbXO3YOcMs5nUBJm65uhW740lccBR9fKzGA2kEsXsuEL8p1MfCM5kieGwdSzY0pM9nuYQeI3wMPNwIvVcvVgNeb3wfLDcVxU-nl04H/s1600/4.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBFkGZNkabVNB4mblsKEtUjVhKCIpXSAudFmxNkcq18D-FRskB1kL3uekncHzTSSqWMjoiK0k9i-XsJQ_oE-eKvKZ_dsJvKMkIL91spnGFEzaLxzZHiYn5Bi7gmZDI1Uks2FPhvQRvP9hC/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBFkGZNkabVNB4mblsKEtUjVhKCIpXSAudFmxNkcq18D-FRskB1kL3uekncHzTSSqWMjoiK0k9i-XsJQ_oE-eKvKZ_dsJvKMkIL91spnGFEzaLxzZHiYn5Bi7gmZDI1Uks2FPhvQRvP9hC/s1600/5.png" /></a></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="MsoListParagraphCxSpLast">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_5" o:spid="_x0000_i1027" type="#_x0000_t75" style='width:338.25pt;
height:143.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal" style="margin-left: .25in; text-indent: .25in;">
<!--[if gte vml 1]><v:shape id="Picture_x0020_4"
o:spid="_x0000_i1026" type="#_x0000_t75" style='width:140.25pt;height:136.5pt;
visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal" style="margin-left: .25in; text-indent: .25in;">
It seems like
the out-of-the-box quick create form is completely hidden. When I publish my
changes and look my new form is not really used<o:p></o:p></div>
<div class="MsoNormal" style="margin-left: .25in; text-indent: .25in;">
<br /></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]-->4.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Then I thought maybe we can remove “Add Task”
from the social pane so that users would be forced to add tasks the old way and
be able to use the form we need. However, the Social Pane is not very
configurable and the only things that can be set is whether to show Activities
or Notes by default, which is really not useful.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So this actually forced us in some cases to remove the
social pane all together and go back to the sub-grid approach. However, if you
need to capture notes then you might be out of luck!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Also note that in the Quick Create menu in CRM, when you try
“Task” it will actually open the default full form, rather than any Quick
Create form that you define, so you have control but you cannot leverage the
quick create feature for activities apparently.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7PUUFtZw3C1GeuwAaTMS-ReKfHwfL6Y393-RJ8nS7ST3pW7I50wvVbi9LW64c39rcf42fcHi3dqAufB7UJ-vq46o40QQx9B2ZA2URgEiIIeESLGKAqVe1d4GxhKR7DHtbFC3NuIrFlNrP/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7PUUFtZw3C1GeuwAaTMS-ReKfHwfL6Y393-RJ8nS7ST3pW7I50wvVbi9LW64c39rcf42fcHi3dqAufB7UJ-vq46o40QQx9B2ZA2URgEiIIeESLGKAqVe1d4GxhKR7DHtbFC3NuIrFlNrP/s1600/6.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_6" o:spid="_x0000_i1025" type="#_x0000_t75" style='width:462pt;
height:113.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
We also tried to hide the activities from appearing in the
quick create menu but this was not possible because you cannot unselect the
setting to show activities in the quick create menu. This is perhaps one of the
most annoying things with quick create and stems from the fact that Microsoft
has locked down the configurability of this feature which often just renders it
useless in the real world but only pretty for customer demos. This sadly seems
to be the case for many features that are also locked down for extension or
configurability. If you have any workaround I’d be very happy to hear!<o:p></o:p></div>
<div class="MsoNormal" style="margin-left: .25in; text-indent: .25in;">
<br /></div>
<div class="MsoNormal" style="margin-left: .25in; text-indent: .25in;">
<br /></div>
<div class="MsoListParagraphCxSpFirst">
<br /></div>
<br />
<div class="MsoListParagraphCxSpLast">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-19411877835456286962016-10-06T13:30:00.000-04:002016-10-06T13:30:13.372-04:00Transporting SLAs in a CRM solution<div class="MsoNormal">
<i>This post explores some of the nuisances when transporting
SLAs in a CRM solution. I did most of my tests in CRM 2015 and CRM 2016
environments but behavior might vary between different versions.<o:p></o:p></i></div>
<div class="MsoNormal">
<i><br /></i></div>
<div class="MsoNormal">
When you include an SLA to the solution there are some
special things that will happen. In this example, I have a blank solution and I
have added only my SLA to the solution. As expected, the only component in the
solution is the SLA itself:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgweZrFv4dxlWQL9eJgQJEpHIRPD3rGuiDqIUHwR7Z-nbgrSgf_tjW4ur8s_nAaFsUQJpJG8HkVJSapn-e4nILxUcxTaMuKoYCnRR5TprCZsYV_r5I9Hj6Mdaq4-Y2khhUr0NyJZDAHy5N8/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgweZrFv4dxlWQL9eJgQJEpHIRPD3rGuiDqIUHwR7Z-nbgrSgf_tjW4ur8s_nAaFsUQJpJG8HkVJSapn-e4nILxUcxTaMuKoYCnRR5TprCZsYV_r5I9Hj6Mdaq4-Y2khhUr0NyJZDAHy5N8/s1600/1.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype
id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t"
path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_1" o:spid="_x0000_i1028" type="#_x0000_t75"
style='width:289.5pt;height:159.75pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
However, after you export the solution for the first time,
you will notice that magically some processes get added to your solution
automatically after export without any indication that this has happened:<o:p></o:p></div>
<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvU6pf3wnu5d9_lZrrRVhfiFJ-y7Ly0h9OI9r9FLeRynkkF28ViV7bCFts3NHgM3KfR203iQm8qEtTTh4CArws0oSn6d26MT5xmhTkEx1gH6P7CUUTl7PmFxj6feXp5oPon5NId7XAt8dY/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvU6pf3wnu5d9_lZrrRVhfiFJ-y7Ly0h9OI9r9FLeRynkkF28ViV7bCFts3NHgM3KfR203iQm8qEtTTh4CArws0oSn6d26MT5xmhTkEx1gH6P7CUUTl7PmFxj6feXp5oPon5NId7XAt8dY/s1600/2.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Furthermore, in older versions of CRM (2015) you will also
see that the “Process”, “Case” and “SLA KPI Instance” entities get added to the
solution magically which can be quite confusing: <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipJjIvrUO8HUn_qzULNLFsp_zexmj5naNMgqidV87Ol8RMj-e53Bns7M_A34S5wvNND5A4vB9RylLdnCOnrYGDNJsNSGXDv-Ki3QP9scczfK4GYs-SDk-SVfVXxBMQdpNUuN9WMFco0ehU/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipJjIvrUO8HUn_qzULNLFsp_zexmj5naNMgqidV87Ol8RMj-e53Bns7M_A34S5wvNND5A4vB9RylLdnCOnrYGDNJsNSGXDv-Ki3QP9scczfK4GYs-SDk-SVfVXxBMQdpNUuN9WMFco0ehU/s1600/3.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_3" o:spid="_x0000_i1026" type="#_x0000_t75" style='width:371.25pt;
height:198.75pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
But why is this happening? The reason why you see some processes
added to the solution is because SLAs are implemented as CRM processes
(workflows) behind the scenes, so at the moment you export an SLA, you need to
export at the same time the process definitions for the SLA. There wil be one
process per SLA plus one additional process for each SLA Item you have added to
your SLA (1 item in my example). That is because SLA Items are also implemented
as workflows. Now, the question of why we have to surface these implementation
details to the user is in my opinion a bug, there is no reason to show the user
these components in the solution and should be hidden because they are
implementation details of the SLA that happen behind the scenes and causes
confusion more than anything about what these processes are. If you try to open
one of these processes you will notice that they are read only and completely
system managed depending on the SLA and SLA Item definitions that you have
provided in the user interface.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Another problem with such implementation is that if you
rename the SLA Items, then their corresponding workflow/process is not renamed
accordingly which can cause even more confusion since they will keep the
original name that was given to the SLA Item during creation. I have even run
in a situation in which solution export fails because it could not find the
corresponding SLA Item process once I had renamed the SLA Item (although I was
not able to reproduce this problem consistently).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The reason why you see the “Case”, “Process” and “SLA KPI
Instance” entities added to the solution in CRM 2015 is most likely a bug and
something that was fixed in CRM 2016. Even if you remove these entities from
the solution, they will get added back automatically next time you export the
solution so not worth trying to remove them manually!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Another thing I wanted to explore is how the Business Hours
get transported in the solution. For example, I have defined my business hours
as follows:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE0Unh-EC3maaH906wbGBcP5aJ1g988fM59GGDxzEcb45a-84ytVPkIur_oONY61KeG6QDYz93Te4kZ5ZErHWsH8L6mqLhjJHjs6yPx8TeFpr7-sh-iunetF3Q5cpS0YMsXtpVh2nbRhRt/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE0Unh-EC3maaH906wbGBcP5aJ1g988fM59GGDxzEcb45a-84ytVPkIur_oONY61KeG6QDYz93Te4kZ5ZErHWsH8L6mqLhjJHjs6yPx8TeFpr7-sh-iunetF3Q5cpS0YMsXtpVh2nbRhRt/s1600/4.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I have found out be inspecting the solution XML that the
business hours are not included at all in the solution, therefore when you
transport the SLA to another environment the business hours will be blank for
the given SLA. You will then need to set it manually in the target environment.
I have researched whether I could use the Configuration Migration utility that
comes with the SDK in order to migrate business hours and include in the
solution deployment package but as it turns out both the Business Hours and the
Holiday Schedule are implemented as entries in the “Calendar” entity which is
not supported by the Configuration Migration tool, and AFAIK is also not
possible to import these using Excel import file. Therefore, you have no option
than to re-create these records in your target environment (manually or
automate via SDK) and link them to the existing SLA. You might be able to
import the individual “Holiday” records to the Holiday Schedule in an automated
fashion such as import but I have not validated that far what can be done.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Luckily creating the Business Hours and Holiday Schedule is
not a very long task, and you should be able to create them only once in each
target environment. After than re-importing an existing SLA should preserve the
link to the Business Hours you had set previously.<o:p></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-8501092459252526102016-09-30T10:04:00.000-04:002016-10-01T10:04:26.035-04:00Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-74411991882389206732016-09-27T13:00:00.000-04:002016-09-27T13:00:05.890-04:00State Machines in CRM for Status Transitions<i><br /></i><div class="MsoNormal">
<i>Those who come more from an engineering background might be
familiar with the concept of state machines. This article explains an easy
implementation in CRM.</i><o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="MsoNormal">
State machines are basically a way to model an object’s life
cycle, including the different states or statuses it can have along with the
transitions that are possible to go from one state to another. In an example
below we have an airline ticket and how the status of the ticket can transition
in different scenarios:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8p6iJGi94xjSFcZZTPiZja7VUCEkXUNWlasvl3uShbY9981P2uhHV2QfLJYIQeuRUH9sW3bh_psdKG0B_ZCWCnz0wE1rSmfgnh5qslkyjZSk_zZFWEbXPFnWQTws58HPr-Lckjx90s3an/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8p6iJGi94xjSFcZZTPiZja7VUCEkXUNWlasvl3uShbY9981P2uhHV2QfLJYIQeuRUH9sW3bh_psdKG0B_ZCWCnz0wE1rSmfgnh5qslkyjZSk_zZFWEbXPFnWQTws58HPr-Lckjx90s3an/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype id="_x0000_t75"
coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe"
filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_1" o:spid="_x0000_i1029" type="#_x0000_t75"
alt="Make reservation Made Used Pay Money Paid Print Ticket Ticketed Give Ticket Cancel Cancel/ Refund Cancelled Cancel/ Refund "
style='width:436.5pt;height:234pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png"
o:title=" Refund "/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
The key about state machines is that it restricts invalid
state changes. For example, you cannot go from “Used” to “paid” because once
the ticket is used, it can no longer change. In some sate machines you might
also want to specify which states are read-only as opposed to “editable” and
you can even define additional conditions such as having the correct privilege
(e.g. only a manager is able to issue a refund).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Now you can apply this concept of state machines to CRM
entities. Although these state machines can be quite complex in real life, in
CRM entities most of the times this is not too complex. If you have a simple
state machine to model, there is the feature in CRM (often overlooked) called
status transitions. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI00FfSwleVLjpN-iL18-AI3DM2wqnOfEHSs6L15r2u28P8CB_UCPVs71sWd5h7J4vRqmW3heu2Dmh18ekOrddcZXMz7rv3ooWjGiFvMOlfHLtH8zHTMR5WymJxG0BbNkROALu0t9utXL-/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI00FfSwleVLjpN-iL18-AI3DM2wqnOfEHSs6L15r2u28P8CB_UCPVs71sWd5h7J4vRqmW3heu2Dmh18ekOrddcZXMz7rv3ooWjGiFvMOlfHLtH8zHTMR5WymJxG0BbNkROALu0t9utXL-/s1600/2.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_2" o:spid="_x0000_i1028" type="#_x0000_t75" style='width:140.25pt;
height:30pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You can actually define which status can lead to which
status. Below is the CRM implementation of the state machine of my example:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgroVGQ2hS3cQpap0L5quJyOFweWNCIJe7YiSDzM-bVtoEHDep3ijsA9a3Y7fUxhuYZcKf-mGEtrRb1nO33qpegRF90_0Iv_I_fVN9gDl2Ov16zjuju2P1WLN91_K0xUaNNKFa5jxg-ogMw/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgroVGQ2hS3cQpap0L5quJyOFweWNCIJe7YiSDzM-bVtoEHDep3ijsA9a3Y7fUxhuYZcKf-mGEtrRb1nO33qpegRF90_0Iv_I_fVN9gDl2Ov16zjuju2P1WLN91_K0xUaNNKFa5jxg-ogMw/s1600/3.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_3" o:spid="_x0000_i1027" type="#_x0000_t75" style='width:468pt;
height:178.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
Note that CRM requires that for each “Active” status you
must have defined a valid transition to an “inactive” state (at least this is
mentioned in the documentation here: <a href="https://technet.microsoft.com/en-us/library/dn660979.aspx">https://technet.microsoft.com/en-us/library/dn660979.aspx</a>
although I’m not sure where it is enforced if it is).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Now in the CRM form you will notice that it will remove the
invalid state transitions from the available status reasons. For example, if my
record is on status reason “Reserved” I cannot change it to “Ticketed” because
I need to mark it as paid first:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-iCVSU1Tqz0wKbkL36MaTc0Nrm4TUWI8ziS2EmFtYz-7MwQHTI0GZ1mLwuCQuuRfnUqpMti_aR1rnCBl6Ro111ZjIQ9OsqyoZqyZhBYw9NWO4jFl_xPyA-FRUToRSimDNmDxgwhcopK4o/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-iCVSU1Tqz0wKbkL36MaTc0Nrm4TUWI8ziS2EmFtYz-7MwQHTI0GZ1mLwuCQuuRfnUqpMti_aR1rnCBl6Ro111ZjIQ9OsqyoZqyZhBYw9NWO4jFl_xPyA-FRUToRSimDNmDxgwhcopK4o/s1600/4.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_4" o:spid="_x0000_i1026" type="#_x0000_t75" style='width:254.25pt;
height:89.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If I had not defined a transition from the current status
reason to an “Inactive” state then I am not able to de-activate the record and
change the state to “Inactive”:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWcbZ5rN_4tuCcECjm_5IpahFkuADNlDlbKBjdqCxK-rHBCmTUuU7ZQuhbUL7JCBO21lzuR4qpV4HlRp7RVjvZ73LZXQJ8KHde7VG8K7BIez0p3CGaZi8nRb3g6azZht6tVoSNgDB22k7/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWcbZ5rN_4tuCcECjm_5IpahFkuADNlDlbKBjdqCxK-rHBCmTUuU7ZQuhbUL7JCBO21lzuR4qpV4HlRp7RVjvZ73LZXQJ8KHde7VG8K7BIez0p3CGaZi8nRb3g6azZht6tVoSNgDB22k7/s1600/5.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_5" o:spid="_x0000_i1025" type="#_x0000_t75" style='width:342.75pt;
height:137.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
Also note that the transition validations are valid even
outside the scope of CRM forms. If you try to perform an invalid status update
from workflow/plugin/SDK you would get this error:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<errorcode>-2147160050</errorcode><o:p></o:p></div>
<div class="MsoNormal">
<message>Entity: new_airplaneticket does not have valid status
code</message><o:p></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-53314181391336284902016-09-26T11:30:00.000-04:002016-09-26T18:20:20.336-04:00CRM SLA Failure and Warning times, not what you would expect!<div class="MsoNormal">
When you configure an SLA item in CRM, you have the option
to specify the “Failure” and “Warning” times as a duration. <o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xVidn_TPRHjl7UsXap4nvypd2Mrxp_ge7Bwi9DxUsArub7_ijdqECBBb36EdmOOZe5Jn-HpMyutBZVRCYcaVNrFaqnPJ5hhShmWpmkjjDUdh0ltq6m5Gkoo0cQVUwkTd0h_X2WCeRA3n/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xVidn_TPRHjl7UsXap4nvypd2Mrxp_ge7Bwi9DxUsArub7_ijdqECBBb36EdmOOZe5Jn-HpMyutBZVRCYcaVNrFaqnPJ5hhShmWpmkjjDUdh0ltq6m5Gkoo0cQVUwkTd0h_X2WCeRA3n/s1600/1.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype
id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t"
path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_1" o:spid="_x0000_i1028" type="#_x0000_t75"
style='width:197.25pt;height:77.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The format of these fields Is the same as any other CRM
duration field, but what does it really mean? For example, if you set it to 10
days, is it 10 calendar days? Is it 10 business days? You might be surprised it
is neither!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
First let’s backtrack a little bit and look at the SLA definition.
At the SLA level you can define “Business Hours” which captures the business
days of the week, business hours of the day as well as any holidays during
which an SLA should not apply. Let’s see what happens to your 10 day SLA
depending on your business hour configuration in different scenarios. For
simplicity I will assume you do not have “pause” when the record is on-hold.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]--><b>1.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->No Business Hours</b><o:p></o:p></div>
<div class="MsoNormal">
If you leave this “Business Hours” field blank, then the
system will assume 24x7 and therefore, the “Failure” and “Warning” times you
set are simply calendar days, it will be simply a duration which is quite straight-forward.
Therefore, when you create the record, you will have exactly 10 days (240
hours) before the SLA fails.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]--><b>2.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Business Hours Configured (Work Days only)</b><o:p></o:p></div>
<div class="MsoNormal">
In this case you configure your Business Hours only for the
work days (e.g. Mon-Fri) but you leave the work hours as 24-hour (i.e. your
business day has 24 hours):<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOg1ZYXc-9KsUmSgMzgG7YFZQcfOauy73V3rn_on8O75GlEOuJWbDEN6PyK2ypfmFqvR183e6ZSZ7Gi3hIrigEZJK2HtZyf9683KvezL0I1V7FAKzMIaN-NRTrziXTdvjyvGQlt8kFgGo9/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOg1ZYXc-9KsUmSgMzgG7YFZQcfOauy73V3rn_on8O75GlEOuJWbDEN6PyK2ypfmFqvR183e6ZSZ7Gi3hIrigEZJK2HtZyf9683KvezL0I1V7FAKzMIaN-NRTrziXTdvjyvGQlt8kFgGo9/s1600/2.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_2" o:spid="_x0000_i1027" type="#_x0000_t75" style='width:322.5pt;
height:124.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
In this case, what happens to your 10-day SLA is that it
becomes a 10 business day SLA. Therefore, when you create a case, you will have
14 calendar days before the SLA fails because the weekend days will not be
counted.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<!--[if !supportLists]--><b>3.<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><!--[endif]-->Business Hours Configured (Including Work Hours)</b><o:p></o:p></div>
<div class="MsoNormal">
Now this is where things get really messy unexpectedly.
Imagine you configure your business hours to be Mon-Fri from 09:00 to 17:00 so
that you have 8 working hours per business day. What happens to the 10-day SLA
failure? <o:p></o:p></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_3" o:spid="_x0000_i1026" type="#_x0000_t75" style='width:300pt;
height:100.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png"
o:title=""/>
</v:shape><![endif]--></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPUGXKIwi0PLpUVdkjrj81KIgg9eX0ejg27txlVYrAfdCbiauCBWvUi9MrfLK-Rn2UbFRpFIBCnYh_TLOxsv-9S2F58AjwMwlAhbtZ99KwYDxiFsP1P2Lu30VP8WmIfJJr2OQvmIR8jIp9/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPUGXKIwi0PLpUVdkjrj81KIgg9eX0ejg27txlVYrAfdCbiauCBWvUi9MrfLK-Rn2UbFRpFIBCnYh_TLOxsv-9S2F58AjwMwlAhbtZ99KwYDxiFsP1P2Lu30VP8WmIfJJr2OQvmIR8jIp9/s1600/3.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I create a case and to my surprise, the system gives me
almost 42 calendar days to resolve the case before failing SLA:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmbSXcc-u8_tVzgY1lyvViDM7xTmaPzycdkXZKQhzZjd4gRKStN41rln2fN6qS7jiWREnWdtrrAg5DAFVjgwp1ZDDBDquzkZ02tMQO85BLZMNpOIfuWFPVaeUpxDPe5jO-dR6webHVTqDa/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmbSXcc-u8_tVzgY1lyvViDM7xTmaPzycdkXZKQhzZjd4gRKStN41rln2fN6qS7jiWREnWdtrrAg5DAFVjgwp1ZDDBDquzkZ02tMQO85BLZMNpOIfuWFPVaeUpxDPe5jO-dR6webHVTqDa/s1600/4.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_4" o:spid="_x0000_i1025" type="#_x0000_t75" style='width:221.25pt;
height:55.5pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
This seems really random. What happens here is that the “Failure”
time of 10 days is actually a <i><u>duration</u></i>
(not the actual count of calendar or business days). In other words, the SLA
will fail after 240 “business hours”. Because I only have 8 business hours per
day then this means 30 business days and because of the weekends it ends up
giving me almost 42 days. This is definitely not what I would expected when I
configured by failure time to 10 days. Therefore, I just decided to leave my
work hours as 12am-12am (24 hours) and then I would fall under #2 above in
which I get 10 business days for resolving the case as I expected.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Adding holidays to your calendar works similar to the work
days, it will just exclude the entire day from the count. Do not expect the
timer to “pause” when you are outside of working hours because the timer has
actually been increased to account for the time that you will have outside of
business hours, so the timer countdown is always real time (duration) and can
only pause when you configure pause for “on-hold” status.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
I hope you find this useful, I think unless you have really
fast SLAs (defined in hours or minutes) it almost makes no sense to think about
configuring working hours. My conclusion is that if you SLAs which are defined
in number of days you most likely should leave the work hours to 24 hours in
order for the timer to make sense. Alternatively if you keep working hours you would need to define your SLA items as a function of business hours, so for my example, if the business hours are from 9am to 5pm then that means that for a 10 business day SLA I would need to define my failure time as 10*(number of business hours per day) = 80 hours. The small caveat here is that if you change your business hours then you need to update all your SLA items as the above formula could have changed. In either case you should still configure your workdays to
exclude weekends if desired though.<o:p></o:p></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com4tag:blogger.com,1999:blog-518307109112772972.post-11222021165621832112016-09-20T20:38:00.001-04:002016-09-23T08:02:11.598-04:00CRM Alternate keys with OptionSet fields<div class="MsoNormal">
The alternative keys feature introduced in CRM 2015 U1 turns
out to be extremely useful, especially for integration scenrios in which you
might want to keep a record of an “external key” from another system or you
want to enforce duplicate prevention (for real).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In some cases you would like to make the alternative key
contain an OptionSet type of field. For example, I wanted to define an
alternative key based on “Contact Type” and “Email” so that CRM could check
that no contacts of the same type with the same email address. <o:p></o:p></div>
<div class="MsoNormal">
However, when you try to define the key on the contact
entity I could no select my “Contact Type” field as part of the key:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVezjADpVdVeqjF2NGQsk1xDRDZVu7yiU6CU8eC5MN62K0g9UCi7386Wr_tTpw8JLs_LqLnUqsvgN6kVD6maBZRSrdVZ2pAPMZs0xphbu_yYbkwTjXoyuojh8C_PyyPVVaX0Ri-7dwbfC0/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVezjADpVdVeqjF2NGQsk1xDRDZVu7yiU6CU8eC5MN62K0g9UCi7386Wr_tTpw8JLs_LqLnUqsvgN6kVD6maBZRSrdVZ2pAPMZs0xphbu_yYbkwTjXoyuojh8C_PyyPVVaX0Ri-7dwbfC0/s1600/1.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So I can see a number of fields and realize that only fields
of numeric or text value are available to pick. After some research I found
that it is documented in the <a href="https://msdn.microsoft.com/en-us/library/dn932139.aspx">SDK</a> that only
these fields can be added to a key:<o:p></o:p></div>
<div>
<br /></div>
<br />
<div class="MsoNormal">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEittr4chdJEfuZoiXSaHfy07ctnRP3q8wQGUNzvQIQbRdlKGTjmCYUW075dbkPprb8bvwllNR78DgE2tbOBjkzKK23x_oTKwd-oWLMyfMa8n47dyqc7ZGoBb2m_7VP2eJMlMOjk88CItXcA/s1600/2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEittr4chdJEfuZoiXSaHfy07ctnRP3q8wQGUNzvQIQbRdlKGTjmCYUW075dbkPprb8bvwllNR78DgE2tbOBjkzKK23x_oTKwd-oWLMyfMa8n47dyqc7ZGoBb2m_7VP2eJMlMOjk88CItXcA/s1600/2.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
So no luck! The only option you have is to create a hidden
text field which will hold the value of your OptionSet but as text and then add
that field to your key. I try to define this field as a calculated fiueld but
then again, calculated fields cannot be added to alternate keys (this is by
experiment, not by documentation). Therefore, I had to define a business rule
to populate that field based on the options of your OptionSet. In my example,
my OptionSet has options “Nurse” and “Doctor” so I create the business rule as
follows (note you should set “Scope” to “Entity”)<o:p></o:p><br />
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vrOG57A55r5YhB3GjEuNhRiejpXmFHgos0KRz4cEXZpUd49-yb4Zlm6XjogaNqGnkrsy73oJEyBoB6x18sctwGiPUkKWw96YDEb5zco9xXl3zWSxTJl_jqySVCq0C1qlZR3Y6qOno6No/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vrOG57A55r5YhB3GjEuNhRiejpXmFHgos0KRz4cEXZpUd49-yb4Zlm6XjogaNqGnkrsy73oJEyBoB6x18sctwGiPUkKWw96YDEb5zco9xXl3zWSxTJl_jqySVCq0C1qlZR3Y6qOno6No/s1600/3.png" /></a></div>
<div>
<br /></div>
<br />
<div class="MsoNormal">
Now I am able to add the calculated field to my key:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4IBjt0Lrhotpk708PzvPPDVsXa9YMDueIfTOgAVC7CRCmXGof-HBhC4uFn9-KXBJbqbkxzhCHN0m0aIkONDDrV1H7GOuXz4P0yjncbPiy2mezJNaD7vqccjNuI7y1aXU6LkRn-88HknKY/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4IBjt0Lrhotpk708PzvPPDVsXa9YMDueIfTOgAVC7CRCmXGof-HBhC4uFn9-KXBJbqbkxzhCHN0m0aIkONDDrV1H7GOuXz4P0yjncbPiy2mezJNaD7vqccjNuI7y1aXU6LkRn-88HknKY/s1600/4.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
After this is done, I can test to make sure I can create
both a nurse and a doctor with the same email but if I try to create 2 nurses
with the same email I get the error: <o:p></o:p></div>
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix5gDqXBk1A1GLkdLCRcbLT5mRcoEJboltiRc3ge5_IBAHUTHA_yAzNXiRXmyXiGbfBEDvtGIv6Y0mkKE_1ZqdEyr3M9w5Hj8Hb7AH6LQxebzEAI_oV6wtZquAdUxtjOMuLnB6PvOKiF0n/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix5gDqXBk1A1GLkdLCRcbLT5mRcoEJboltiRc3ge5_IBAHUTHA_yAzNXiRXmyXiGbfBEDvtGIv6Y0mkKE_1ZqdEyr3M9w5Hj8Hb7AH6LQxebzEAI_oV6wtZquAdUxtjOMuLnB6PvOKiF0n/s1600/5.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So this works. It is not the most elegant solution since you
are essentially duplicating data. However, if you must include an OptionSet
field as part of a key this could be an easy way to enforce it <span style="font-family: "wingdings";">:-)</span><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Enjoy!!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-80784066864693926582016-09-19T20:18:00.000-04:002016-09-19T20:18:03.299-04:00CRM Advanced Find with N:N Relationships<div class="separator" style="clear: both; text-align: center;">
</div>
<br /><div class="MsoNormal">
<i>An old CRM problem is that sometimes when you define N:N
relationships between 2 entities, you find this relationship not available for
search in Advanced Find. Don’t worry about it this article will give you the
work-around you need.</i><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Consider a scenario in which I have accounts and I have
industry entities. An account can be in multiple industries and an industry can
have multiple accounts; therefore I have defined an N:N Relationship between
Account and Industry entity:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFxmnqUTAlSdf_AhieX2S5jXrIyd9izjWs45oSdBar45hddEG3xiyvRBkL9_Nt7MbNI7abpfFu9RGY_BmhDKZX4MqTt_z27rdJuncnYMozNmgnZsKsEpiVatkDNPbfOAEEaK09cbupL3fA/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFxmnqUTAlSdf_AhieX2S5jXrIyd9izjWs45oSdBar45hddEG3xiyvRBkL9_Nt7MbNI7abpfFu9RGY_BmhDKZX4MqTt_z27rdJuncnYMozNmgnZsKsEpiVatkDNPbfOAEEaK09cbupL3fA/s640/1.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="MsoNormal">
<br /></div>
<br /><div class="MsoNormal">
Now as part of the requirements I want to enable users to be
able to build Advanced Find queries based on this relationship. For example:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
</div>
<ul>
<li>·<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><span style="text-indent: -0.25in;">Find all accounts in the “Banking” industry</span></li>
<li>·<span style="font-size: 7pt; font-stretch: normal; font-variant-numeric: normal; line-height: normal;">
</span><span style="text-indent: -0.25in;">List the accounts with industry code FIN002</span></li>
</ul>
<!--[if !supportLists]--><o:p></o:p><br />
<div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<br /></div>
<div class="MsoNormal">
So you might think this is was easy to do with Advanced
find, however, you might not be able to find this relationship when looking for
the related records under account in Advanced Find:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkS4N-97iyV-wknbEqNicVwpvYatD5OUZ3eF83z2RjFrPtWlhToCirIxSDlf4ggnUomUlPHaymVROvyrWJJlXmXi-LsZS7H84r0R3FmudlH4sKpty7VUo93qU8dzM6uH1NRa7CYAnqvwQ/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkS4N-97iyV-wknbEqNicVwpvYatD5OUZ3eF83z2RjFrPtWlhToCirIxSDlf4ggnUomUlPHaymVROvyrWJJlXmXi-LsZS7H84r0R3FmudlH4sKpty7VUo93qU8dzM6uH1NRa7CYAnqvwQ/s1600/2.png" /></a></div>
<div>
<br /></div>
<br /><div class="MsoNormal">
So I went back to the N:N relationship definition to see if
I did something wrong. I found that there is a checkbox called “Searchable” and
was already set to “Yes”:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzbGEzyIFpfldGTGSJgEK49l0QtMtis9wTXSWsb5LoWKxpNMa18YySgkCZR7lAUR2zDYOUPEiBeXqUDuwtBun2qh73sC9Q-PMzqUoMD3gz4y-7ENXkfGZhdpKl4epRKY1_acKCcedWtk_U/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzbGEzyIFpfldGTGSJgEK49l0QtMtis9wTXSWsb5LoWKxpNMa18YySgkCZR7lAUR2zDYOUPEiBeXqUDuwtBun2qh73sC9Q-PMzqUoMD3gz4y-7ENXkfGZhdpKl4epRKY1_acKCcedWtk_U/s1600/3.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_3" o:spid="_x0000_i1028" type="#_x0000_t75" style='width:180pt;
height:92.25pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
So why I cannot search based on this relationship? The
answer is tricky. The relationship actually appears in advanced find whenever you
set the “Display Option” to “Use Plural Name” or “Custom Label” on the side of
the relationship with you want to show under the “related” section in advanced
find. In my example, I would need to set the “Display Option” to “Plural Name”
on the Industry side of the relationship so that I can perform the queries such
as retrieving all accounts in a given industry:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3yShe5FZuUIOdQIDs-lkKdeVbvlcWlP8VB3cjSCw8Igyj9o1CBmFDb4fJWx_sa4LOaJ8FLcueOPlrmQHmkbylQtTDvNPkhED9CQClXzkA4Jc2BFROyYyVtDcwxfjdiYINcBqoIybQMr9Z/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3yShe5FZuUIOdQIDs-lkKdeVbvlcWlP8VB3cjSCw8Igyj9o1CBmFDb4fJWx_sa4LOaJ8FLcueOPlrmQHmkbylQtTDvNPkhED9CQClXzkA4Jc2BFROyYyVtDcwxfjdiYINcBqoIybQMr9Z/s1600/4.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_4" o:spid="_x0000_i1027" type="#_x0000_t75" style='width:446.25pt;
height:84pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
After I do this and publish all customizations, the “Industry”
entity magically appears in advanced find allowing me to perform my query:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlrGmsenAoirSVaqUB1OR97naWvGj898MBjurX1jZig2pt28XiWNRqn05jRyXcLP5mef91vpYE8L25nYcCUmmDqVM27Fl827NRNbX-zg9tSZlg-wTa1uAFPuJIbX9I97_x0nk4k5RsUFWA/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlrGmsenAoirSVaqUB1OR97naWvGj898MBjurX1jZig2pt28XiWNRqn05jRyXcLP5mef91vpYE8L25nYcCUmmDqVM27Fl827NRNbX-zg9tSZlg-wTa1uAFPuJIbX9I97_x0nk4k5RsUFWA/s1600/5.png" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_5" o:spid="_x0000_i1026" type="#_x0000_t75" style='width:201pt;
height:210.75pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shape
id="Picture_x0020_6" o:spid="_x0000_i1025" type="#_x0000_t75" style='width:377.25pt;
height:132.75pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png"
o:title=""/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYYukYDKDeP_trTlynjpsC19AyIdISgZx-AjVE808aAYMd9ih5H9E_fsOzCMme0EstlsTRqlgeabwQpTQm1kzvoZDd4yM3v7gz0xlz1ck6sVwSoaJoDQcqhgceYbLv8zmzgQVGDsMvcMxE/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYYukYDKDeP_trTlynjpsC19AyIdISgZx-AjVE808aAYMd9ih5H9E_fsOzCMme0EstlsTRqlgeabwQpTQm1kzvoZDd4yM3v7gz0xlz1ck6sVwSoaJoDQcqhgceYbLv8zmzgQVGDsMvcMxE/s1600/6.png" /></a></div>
<br />
<br />
<div class="MsoNormal">
And voila. Now the only question I still have is: What the
heck is that “Searchable” checkbox on the relationship for? Feel free to comment
if you know the answer <span style="font-family: Wingdings; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-char-type: symbol; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-symbol-font-family: Wingdings;">J</span>
<o:p></o:p></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-10119733443148395452016-08-15T15:06:00.000-04:002016-08-15T15:06:04.928-04:00Business Rules vs. Sync workflows in CRM<div class="MsoNormal">
<i>With the introduction of the “Entity” scope in Business
Rules, we can now configure them to execute on the server instead of a form
(client-side) logic. However, this brings a dilemma on whether to use business rules
or synchronous workflows for server level validations and business logic
implementation.</i><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Business rules that are defined on the scope of a <i>form</i> opens the door to defining form
business logic that is configurable and requires no coding (something that
previously would require JavaScript). Although there are many limitations to
business rules, they enable simple business logic to be implemented by a
configurator rather than a developer. However, in many scenarios the data in
CRM is not created/updated via a CRM <i>form</i>.
For example, bulk data import, workflows, plugins, API/SDK calls would bypass
the CRM forms and therefore would also bypass any JavaScript or form Business
Rule validations you might have configured. These type of server-side
validation had typically required plugins or synchronous workflows.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Luckily, business rules now have a new scope “<i>Entity</i>” which applies at the server
level instead of the form level. What this means is that these business rules,
just like plugins and sync workflows, will execute even if you are not using a
CRM form in your transaction because they happen on the platform (server side)
instead of the form (browser/client side). There are certainly a large number
of limitations on what business rules can do and there is still a large number
of scenarios for which you must write code (plugins) to implement more complex
rules. However, I sometimes get asked the question: <b>If I can implement my server-side business logic as a business rule or
a sync workflow, which one should I use?</b><o:p></o:p></div>
<div class="MsoNormal">
<b><br /></b></div>
<div class="MsoNormal">
Although I would love to hear from the broader community
what is your position on the question above, I will provide my own personal
view. If the question is asked, then I assume whatever you need to implement
can be done with either business rules or sync workflows <b>(what are the limitations of each is a separate topic not covered in
this post)</b>. And in that case my answer is almost always to use business
rules if you can. This is why:<o:p></o:p></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
</div>
<ol>
<li><span style="text-indent: -0.25in;"> Business Rules are contained in the entity
itself in the solution which I find somewhat cleaner and more organized, you
can review which business rules apply to each entity and when exporting a
solution (CRM 2016+) decide which business rules to export. You can argue
processes have a primary entity and you can use that field for the same
“organization” purpose (sure I give you that).</span></li>
<li><span style="text-indent: -0.25in;"> Business Rules are actually implemented as sync
workflows! The business rule is nothing more than an abstraction of a sync
workflow but behind the scenes each business rule is converted to a sync
workflow. However, I find the abstraction useful, mostly because I can
interpret business rules a lot easier in my head than workflows. If you export
a solution with a business rule you will notice the business rules are actually
“processes” just like sync workflows so there is a conversion layer from </span><i style="text-indent: -0.25in;">entity</i><span style="text-indent: -0.25in;"> business rules to sync workflows.
What this means is that there are basically 2 separate designers (process
designer and business rule designer) that produce the same workflow XAML
definition that is executed by the same runtime. While both workflows and
business rules are actually stored in the “Process” table/entity there seems to
be a slight difference in the triggers: The workflow triggers are stored in the
same Process table but the business rule triggers have their own
“ProcessTrigger” (anyway that’s all implementation details).</span></li>
<li><span style="text-indent: -0.25in;"> Workflow designer is quite outdated and probably
reaching its end of life. However, it seems Microsoft is investing more and
more in the business rules designer and supporting more and more scenarios with
each CRM release. I personally find it more pleasant to work with the business
rule designer than the old process designer.</span></li>
</ol>
<o:p></o:p><br />
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -.25in;">
<o:p></o:p></div>
<div class="MsoNormal">
On the other hand, sync workflows can be more useful if you
want to at some point enable additional capabilities not supported by business
rules. For example, user-level scope of a workflow (as opposed to Organization)
and some of the other settings available in the process designer which are not
available in the business rules designer.<o:p></o:p></div>
<br />
<div class="MsoNormal">
In summary, sync workflows (and by extension process
designer) have a lot more capabilities than business rules (and business rule
designer). But if you have some business logic which can be implemented with
either, then would suggest using business rules in that case instead of
synchronous workflows.<o:p></o:p></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com1tag:blogger.com,1999:blog-518307109112772972.post-69108784400984056652016-07-19T14:00:00.000-04:002016-07-19T14:00:42.741-04:00SLA Behavior when Reopening CRM Cases<div class="MsoNormal">
<i>After a case is resolved, the user can reactivate the case
but what happens to the SLA when this occurs? In some scenarios you might want
to restart the SLA timer while in other cases you’d want to SLA the remain
unchanged. This posts explores a bit both possibilities. Note that this post
was tested with CRM 2015 and enhanced SLAs. </i><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
One of my recent requirements was that whenever a case is
reopened, the applicable SLA would need to restart counting again as if it were
a new case that just got created. This lead me to explore what kind of control
we have on SLAs when a resolved/cancelled case is brought back to in progress.</div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In my first attempt, I had created an enhanced SLA for which
the “Applicable From” field was the CreatedOn field, meaning that the “start”
of the SLA timer is the creation date of the case:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUp8t-rS2Bl17joZlN1AKeh1eVAT0dI_ZB7nvctM-YAFkDkkulRZT2lg8wybKvSX6xcXdbT_P_24riMOABw5yxxhFF-UcYkbmxS9La6Gpyla-0Lf-VMHVhSlfnmBMYHCDFlZv92lC8hSUt/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUp8t-rS2Bl17joZlN1AKeh1eVAT0dI_ZB7nvctM-YAFkDkkulRZT2lg8wybKvSX6xcXdbT_P_24riMOABw5yxxhFF-UcYkbmxS9La6Gpyla-0Lf-VMHVhSlfnmBMYHCDFlZv92lC8hSUt/s1600/1.png" /></a></div>
<br />
In my test SLA I give the users 5 minutes to resolve the
case before the SLA is considered as “failed”. Then I created a case and
resolved it after 1 minute which causes the case to succeed the SLA. I can see
in the associated SLA KPI instance that everything looks as expected:<br />
<div class="MsoNormal">
<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEien02vxofey6y4fP5n27HzMJ9mFX957Da0kTN6yTTlBQXs9S8TqGTf0J16qCealYPY8IOrxNGwGDEyOPiF3kzRoPGNocrKA8NqNhERGWEV5t-BUpBPfEP7jmIDiCNW42LDnBywzExi0TIP/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEien02vxofey6y4fP5n27HzMJ9mFX957Da0kTN6yTTlBQXs9S8TqGTf0J16qCealYPY8IOrxNGwGDEyOPiF3kzRoPGNocrKA8NqNhERGWEV5t-BUpBPfEP7jmIDiCNW42LDnBywzExi0TIP/s1600/2.png" /></a></div>
<br /><div class="MsoNormal">
Note: The SLA KPI Instance is the entity in CRM that holds
the information about the SLA of a given case, there is one SLA KPI instance
per case that has an SLA.<o:p></o:p></div>
<div class="MsoNormal">
Now I reactivate the case and notice that although the case
is now active, the SLA KPI instance has not changed, so the case is considered
to have succeeded the KPI even though it is still active (there is no timer
anymore):<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfYxXyh2zsucitEnu7A9gi20coLYWsbJpLva1CNr3XRkTtpxxTF0mGSp-d96jyVIq_ILp_bfiIgB-PuE6tt7Q5Olk2y1pO-Yw7bQBNGySibcv5xWiFZLdy8x6fiMbR9gA356cLX4S_cBYu/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfYxXyh2zsucitEnu7A9gi20coLYWsbJpLva1CNr3XRkTtpxxTF0mGSp-d96jyVIq_ILp_bfiIgB-PuE6tt7Q5Olk2y1pO-Yw7bQBNGySibcv5xWiFZLdy8x6fiMbR9gA356cLX4S_cBYu/s1600/3.png" /></a></div>
<br /><div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I can edit anything in the case and resolve it again and no
change is logged to the original KPI instance and there will be no new KPI
instance either. Even if I leave the case open for months before resolving it,
this case is always considered to have met the KPI. Whether this behavior is
expected or not is debatable because in some other scenarios you might have a
manager re-open a case just to enter som additional information before closing
it again and you might not want to lose the original SLA KPI instance
information in that case. However, in my requirement, the SLA timer should
start again, so what can we do?<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
It will not be possible to generate a new SLA KPI instance
when a case is reactivated because this feature is not flexible to do so in
CRM. However, we can trick to overwrite the existing SLA KPI instance and
update it as though it were a new case when the case is reactivated. However,
you will lose the historic SLA instance information from the first time that
the case was resolved.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To do so, the first thing you need to do is base your SLA on
some other field that is not the “Created On” date. I believe it is a good
practice to create a custom DateTime field in the case from which you always
base your SLAs so this way you can implement any business logic to set the
value of that field depending on many conditions (something you cannot do with
the CreatedOn field which is read-only and will never change). So I created a
new SLA based on my custom field:<o:p></o:p></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_X5ogwBsSTI26OjYcb9KQOO_f1wL2S1xpmI9xrqorg8qv_Xd8Sb8qqjHeOxElRUAlAE_BKGdDWmalMFHVN1UFCJDpVkDiD-XxB10DhW-1AC9ORxsRh8roCB-SLTWSVuY8vJ7nUSBhg8tl/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_X5ogwBsSTI26OjYcb9KQOO_f1wL2S1xpmI9xrqorg8qv_Xd8Sb8qqjHeOxElRUAlAE_BKGdDWmalMFHVN1UFCJDpVkDiD-XxB10DhW-1AC9ORxsRh8roCB-SLTWSVuY8vJ7nUSBhg8tl/s1600/4.png" /></a></div>
<br /><div class="MsoNormal">
Now I can set a simple business rule to populate the “SLA
start” field with the value of “Created On” whenever a new case is created.
Thus far, my SLA will behave the same as it did when I had it defined based on “Created
On”. The only difference now is that I will also create a workflow which will
update the SLA start field when the case is reopened and will set the value to “Execution
Time” (time the case is reactivated). <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
When I repeat the same example as before, I notice that once
I reactivate the case, the old SLA KPI instance information is lost and the
entire SLA is recalculated and updated; this happens because a change occurs in
the field on which the SLA is based (SLA start) which forces everything to
re-calculate. So I achieve my requirement of restarting the SLA timer when the
case is reopened. However, I would have much rather liked CRM to generate a new
instance of an SLA KPI each time I reactivate the case so this way I can track
the SLA timer after reopening the case but at the same time I do not lose the
old KPI information which can be useful for reporting. I haven’t found a way to
do so, but I would be happy to hear if anyone has solved this problem.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
There I however, a big caveat with this approach and that is
that because the “old” SLA KPI information is lost when reactivating the case
then users can “cheat” by simply reactivating and re-resolving cases for which
they did not meet the KPI and all of the sudden their KPIs look like they never
missed any SLA! <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you don’t want your SLA timer to restart when a case is
re-activated you just need to make sure that the “Applicable From” field that
you use in your SLA definition will never change when a case is reopened (as is
the case with the “Created On” field).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-34318184925977758552016-07-01T10:10:00.000-04:002016-10-01T10:11:25.861-04:00Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-35821513835437539652016-04-11T14:30:00.000-04:002016-04-11T14:30:15.249-04:00Including Reference Assemblies in plugins<div class="MsoNormal">
<i>I recently had to reference the Newtonsoft.Json dll from
within a CRM plugin and run into the infamous problem that there is no real
support for referenced assemblies from custom plugins or workflow activities.
This post provides a workaround using ILMerge.</i><o:p></o:p></div>
<div class="MsoNormal">
<i><br /></i></div>
<div class="MsoNormal">
<h4>
The Problem</h4>
<br />
This has been a feature gap since plugins were introduced in
CRM, you often want to reference external dll libraries from within your
plugin. First, let’s look at how the CLR (.Net) loads referenced assemblies in
order to understand the problem a little better. During runtime, what happens
with referenced assemblies is that the CLR will try to load the referenced
assemblies from some predetermined locations. For example, it will try to
locate and load that assembly from the GAC or from the same directory as where
the current process is executing. When you compile an assembly and it has other
references, what happens is that the referenced assemblies get dropped in the
same bin folder as your compiled assembly so they can be easily located and loaded
at runtime.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
That works great, and in CRM there is also the concept of
the “bin” folder but the usage of it is a bit deprecated or not recommended
(see ….). So in theory if you have access to the CRM server you could drop all
your referenced assemblies in the “assembly\bin” folder or you can install your
referenced assemblies in the server GAC and ten they will be able to be loaded
at runtime. However, this is a big problem because you don’t always have access
to the server and because depending on a specific server is risky as your
servers might change, there could be a load balancer with multiple servers,
etc.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So what else can you do if you have to register your plugin
in the database? Well, when you register your plugin in the database what
happens is that CRM will deserialize your dll content and load your plugin type
explicitly, but you have no way of telling CRM that it must also load another
assembly and you have no way to upload a reference assembly to the database.
Certainly all .Net native assemblies like System.dll can be loaded because you
can assume .Net to be available from all CRM servers, but what if your
reference assembly is a third party like Newtonsoft.Json.dll ? In cloud
environments (plugins in sandbox mode), reference assemblies can be dangerous
because you do not know what code they are running in your shared cloud
servers, so this probably explains why there is no support for reference
assemblies in CRM. For plugin assemblies this risk is easily mitigated by running
your plugin under partial trust in the CRM sandbox which uses code access
security, .Net framework’s way of allowing external code to execute in a safe
manner. This is why your plugins would fail to do things like try to read a
local file or shut down the current server (you wouldn’t expect that to work in
cloud environments!).<o:p></o:p></div>
<div class="MsoNormal">
<br />
<br />
<h4>
The Solution</h4>
<br /></div>
<div class="MsoNormal">
So at this point you are left with 2 options: You can either
compile the source code of your reference assemblies into your plugin assembly
(if you have the source code) or you can ILMerge the reference assembly with
your plugin assembly. In this example I will focus on the second option and I
will illustrate exactly how I did it for the example of Newtonsoft.Json.dll in
the following 3 simple steps:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<br /></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<br />
<span style="text-indent: -18pt;">1.</span><span style="font-size: 7pt; font-stretch: normal; text-indent: -18pt;"> </span><span style="font-size: 7pt; font-stretch: normal; text-indent: -18pt;"> </span><span style="text-indent: -18pt;">Add ILMerge Nuget to your plugins project</span></div>
<div class="MsoListParagraph" style="text-indent: -18pt;">
<o:p></o:p></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<br />
<span style="text-indent: -18pt;">2.</span><span style="font-size: 7pt; font-stretch: normal; text-indent: -18pt;">
</span><span style="text-indent: -18pt;">Add Newtonsoft.Json Nuget package to your
plugins project</span><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUVbNVP35uax6mNdJPubzDCiu1RjPGBZ5b9DKDD6u1dv2ZIHcT2P3gry5gyEU0TpgjcgyrfLeePHS7Q5aLqqDD4aO2N1Y60H6fmLa25u9QO-cZuOywLYA9S3aW0hkkyriulZGFNi-VHa-h/s1600/img.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center; text-indent: -18pt;"><img border="0" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUVbNVP35uax6mNdJPubzDCiu1RjPGBZ5b9DKDD6u1dv2ZIHcT2P3gry5gyEU0TpgjcgyrfLeePHS7Q5aLqqDD4aO2N1Y60H6fmLa25u9QO-cZuOywLYA9S3aW0hkkyriulZGFNi-VHa-h/s320/img.png" width="320" /></a><!--[if !supportLists]--><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<br />
<span style="text-indent: -18pt;">3.</span><span style="font-size: 7pt; font-stretch: normal; text-indent: -18pt;">
</span><span style="text-indent: -18pt;">Now you simply need to edit your project
configuration in Visual Studio such that when you build your plugin it
automatically merges it with your reference assemblies (so you will never have
to manually do ilmerge). To do this go to the “Build Events” tab of your
project settings and under “Post Build event command line” box enter the
following:</span><o:p></o:p></div>
<div class="MsoListParagraph" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast">
<br /></div>
<div class="MsoNormal">
<span style="font-family: "courier new" , "courier" , monospace;">$(SolutionDir)packages\ilmerge.2.14.1208\tools\ILMerge.exe
</span><span style="font-family: "courier new" , "courier" , monospace;">/keyfile:$(SolutionDir)/<your file="" key="">.snk /target:"library"
/copyattrs /out:$(TargetDir)$(TargetName)$(TargetExt)
$(ProjectDir)$(IntermediateOutputPath)$(TargetName)$(TargetExt)
$(SolutionDir)packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll<span style="font-size: x-small;"><o:p></o:p></span></your></span></div>
<div class="MsoListParagraphCxSpFirst">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoListParagraph">
<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast">
Note you might need to modify the command above
according to the version of the Nuget package you installed in previous step
and you have to specify the location of your SNK file that you use to sign your
plugin assembly.</div>
<div class="MsoListParagraphCxSpLast">
<br /></div>
<div class="MsoListParagraphCxSpLast">
Now when you build your plugin assembly, the
Newtonsoft.Json.dll is embedded inside your plugin so CRM will not need to load
the reference assembly. There you go, I hope you find this useful and simple
enough. <o:p></o:p></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com3tag:blogger.com,1999:blog-518307109112772972.post-39955846295510951952016-03-14T14:00:00.000-04:002016-03-14T14:00:37.739-04:00How to set EntityCollection output parameters in custom actions<div class="MsoNormal">
<i>One of the greatest things about custom actions is that they
support defining input and output arguments of type EntityCollection. However,
because the process designer does not support UI for consuming this type then
we have to do it via code. This article explains some tips if you think that
could be useful for you.</i></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you are familiar with custom actions introduced in CRM
2013, you might have noticed that they have input and out parameters. What is
interesting is that a data type “EntityCollection” is supported which means
your action can take as input a number of entities or it can produce a number
of entities as the result (like RetrieveMultiple). The first thing you might
wonder is why the heck would you want to use EntityCollection as output
arguments, it if can’t even be consumed from the workflow designer. But if you
are reading this post chances are you have a scenario in mind. In many cases
you might want EntityCollection as input parameters, but is more unusual to
need an output of EntityCollection. I can think of a few scenarios, and most
recently I had to use this for encapsulating a set of business logic in a
custom action which would then return a set of entities. This custom action
might be consumed from external systems and makes it simple to implement all
the business logic in a custom action so from the external clients it will be
straight-forward to consume this action without having to call various SDK
operations and implementing the logic at the client.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Once you have identified the scenario you need to keep in
mind that the result (output) of your custom action will need to be consumed
only from code (external client, JavaScript, plugins, etc.) because
unfortunately the CRM native process designer is still incapable of
understanding entity collections. Now, how can you set an output parameter in
your custom action that is type of EntityCollection if you cannot do that via
the process designer?<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
My first instinct was to implement a custom workflow
activity and then insert it in the action definition as a custom process step:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<!--[if gte vml 1]><v:shapetype
id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t"
path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
<v:stroke joinstyle="miter"/>
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0"/>
<v:f eqn="sum @0 1 0"/>
<v:f eqn="sum 0 0 @1"/>
<v:f eqn="prod @2 1 2"/>
<v:f eqn="prod @3 21600 pixelWidth"/>
<v:f eqn="prod @3 21600 pixelHeight"/>
<v:f eqn="sum @0 0 1"/>
<v:f eqn="prod @6 1 2"/>
<v:f eqn="prod @7 21600 pixelWidth"/>
<v:f eqn="sum @8 21600 0"/>
<v:f eqn="prod @7 21600 pixelHeight"/>
<v:f eqn="sum @10 21600 0"/>
</v:formulas>
<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/>
<o:lock v:ext="edit" aspectratio="t"/>
</v:shapetype><v:shape id="Picture_x0020_3" o:spid="_x0000_i1027" type="#_x0000_t75"
style='width:256.8pt;height:285pt;visibility:visible;mso-wrap-style:square'>
<v:imagedata src="file:///C:/Users/GONZAL~1.RUI/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png"
o:title="SNAGHTML14b351da"/>
</v:shape><![endif]--><!--[if !vml]--><!--[endif]--><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Q63ncj3qjoKUmcgd2xa8z0sWte84gOtA4s4hmeahWBIY7Rc6M62keVArErslnePLoXCQE39mdrb68kKB7UNe6PXtX7TsjiQWRv2pRbrjoaLsN6g3GOdpMZjOI4TMzByphWPbkBtRcfvZ/s1600/Capture1.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Q63ncj3qjoKUmcgd2xa8z0sWte84gOtA4s4hmeahWBIY7Rc6M62keVArErslnePLoXCQE39mdrb68kKB7UNe6PXtX7TsjiQWRv2pRbrjoaLsN6g3GOdpMZjOI4TMzByphWPbkBtRcfvZ/s1600/Capture1.PNG" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Inside the custom workflow activity, you have full access to
the IExecutionContext, including the InputParameters and OutputParameters
collections. Therefore, I assumed I you could set your EntityCollection output
parameter via code as seen below: <o:p></o:p></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRfkNH5iP80sIIoIqNRxe3ToUP3XxkibT7UlVbZdbCaoJhjfYqtUecOVrdUd1YS9rlt-K5N7D3kmvClBmucvWGkGX8jpkf8Ao21XQxyHVjG_eNxdpB_6fRRTiyQ4Q5tS9MlhzSTSsyDLMt/s1600/Capture2.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRfkNH5iP80sIIoIqNRxe3ToUP3XxkibT7UlVbZdbCaoJhjfYqtUecOVrdUd1YS9rlt-K5N7D3kmvClBmucvWGkGX8jpkf8Ao21XQxyHVjG_eNxdpB_6fRRTiyQ4Q5tS9MlhzSTSsyDLMt/s1600/Capture2.PNG" /></a><br /><div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
While technically this compiles and executes fine, I was
surprised to see that when calling my action, the output parameter was blank,
even though I was setting the value from the custom workflow activity. I
imagine this might be due to the fact that output parameters are only set after
the main pipeline operation and perhaps the custom workflow activity is executing
a little bit before that thus all output parameters get wiped after the main
operation but before the value is returned to the client.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Therefore, I had to change my approach. I implemented the
exact same code, but this time I used a plugin registered on the PostOperation
of my custom action, this way I can make sure that the main operation has
already passed and whatever I store in the output parameters collection will be
returned to the calling client. This finally worked!<o:p></o:p></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNXPfip3KZ30NWf2tJMFFHRgM6_awoB9VNVASO9lP5fsqhehd5ceZK35Qr9-AzFhX9lsP5Jx2ZkpC6qine-u_dBC_2Mdyg_gcaokub1tmW4sGZQqd-TbMs9KDkSuKCLWBbq8A4Q8GgdCGA/s1600/Capture3.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNXPfip3KZ30NWf2tJMFFHRgM6_awoB9VNVASO9lP5fsqhehd5ceZK35Qr9-AzFhX9lsP5Jx2ZkpC6qine-u_dBC_2Mdyg_gcaokub1tmW4sGZQqd-TbMs9KDkSuKCLWBbq8A4Q8GgdCGA/s1600/Capture3.PNG" /></a><br /><div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So the lesson here is that setting output parameters (in general)
must be done in the PostOperation part of the pipeline, therefore it can only
be done via plugins!<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Keep in mind that setting input parameters as
EntityCollection is more trivial because it is up to the calling client to pass
those parameters, and in this case, although you cannot consume those input
parameters inside the action process designer, you will be able to access them
from either a custom workflow activity inside your action or from plugins. I
tend to prefer custom workflow activities because this way your entire logic is
defined inside the action itself and there is no additional logic running in
plugins, but it depends on the scenario and complexity too.<o:p></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com3tag:blogger.com,1999:blog-518307109112772972.post-74024655848514660832016-01-11T13:30:00.000-05:002016-01-11T13:30:04.099-05:00OptionSet vs Lookup for implementing lists in CRM<em>In most CRM implementations we experience the dilemma of custom entity
vs. OptionSet for implementing lists. This post seeks to point some of the
considerations to keep in mind when making this decision.</em>
<br />
<em><br /></em>
Imagine you want to capture the "Industry" of your leads/accounts as this
information might be valuable for reporting and BI. Every organization has
different definitions of "industries" because there is no global standard list
of industries that is useful for everyone. So you decide to create and maintain
your own list of industries. Now the problem becomes: Should you implement this
list as an "Industry" entity or should you simply create an OptionSet with the
list of industries?
<br />
As everything in the technology consulting industry (pun intended), the
answer is always: It depends! There is no exact formula of science to solve this
dilemma but I've had numerous discussions with colleagues and customers in this
respect and I thought of gathering some guidance that might help you decide:
<br />
<br />
<blockquote>
<b>1. </b><strong>How often does this list change and who maintains
it?</strong></blockquote>
If your list of industries is changing often or if you expect business users
to contribute by maintaining this list, it is recommended you implement as a
custom entity rather than option set. The reason is that the custom entity adds
richer flexibility to create, update or remove values without having to involve
IT. You can easily control who can edit the list by leveraging the security
roles on the custom entity. However, if your list rarely changes (e.g. list of
countries) and it needs to go through IT then you should probably use Option
Set.
<br />
<br />
<blockquote>
<strong>2. Do you need to support multiple
languages?</strong></blockquote>
For us in Canada we usually always implement CRM in both English and French.
If your industry list is defined as an OptionSet you can support languages much
easier because it is just a matter of translating labels so that each user will
always see the list in their preferred language. However, if you use a custom
entity, the list is data (instead of metadata) and data does not have the notion
of language. Things get complicated here. The typical solution is to use a
concatenated primary fields in which a workflow automatically appends English
and French labels together with a separator. For example you have an industry
record called "Travel / Voyage". But this gets too complex if you have more than
2 languages and creates unnecessary long labels. Not to mention language laws
dictating which language should appear first can cause a headache. For this
aspect, the winner is by far Option Sets.
<br />
<br />
<blockquote>
<strong>3. Do you need to capture additional
fields?</strong></blockquote>
Imagine in our example, you also need to capture an industry code and size
along with the industry name. It would be almost impossible to achieve this with
Option Sets so you would definitely need to implement your industry list as a
custom entity. Even if today you only need the name, consider whether it is
likely that in the future you have to capture additional attributes and if so,
you should favor using a custom entity over Option Sets.
<br />
<blockquote>
<strong>4. Environment transport and
synchronization</strong></blockquote>
The nice thing about option sets is that when you transport a solution you
are also transporting all the labels and values to your target environment and
you are sure that the list is consistent in your dev/test/prod environments so
you can rely on specific values existing in the list. However, if you implement
as an entity, you will never know who will create, update or delete values from
one environment and not the other. So you will end up with different value in
each environment. Furthermore, you might end up with equal values but different
Ids. For example, you might have industry "automobile" in dev with a specific
GUID but the same industry in production with a different GUID. This takes me to
the next point about:
<br />
<br />
<blockquote>
<strong>5. Workflows and reports/views</strong></blockquote>
You might want to do industry specific reports or workflows and include logic
such as "if this customer is in the banking industry then do X". Using
OptionSets this is safe because the value "Banking Industry" is consistent
across all environments so you can add such condition to your workflow. However,
if you use a custom entity, you cannot rely on values having the same GUID in
all environments so you cannot define such logic safely. The workaround would be
that in your workflow/view/report you say "if industry.name contains 'banking'
then do X". This works in most cases but is not completely safe; for example,
somebody might have renamed "banking" to "finance" and suddenly your workflow is
broken. The safest is to use an OptionSet which you are sure will exist for a
specific value. If you rely on reports and workflows such as the one described
above you might be better off using Option Sets.
<br />
<br />
<blockquote>
<strong>6. Dependent lists</strong></blockquote>
Consider whether your list will have another dependent list. For example list
of states/provinces depends on the value selected from the list of countries.
You can achieve dependent lists as both OptionSets and custom entities. The only
difference is that for custom entities it is easier because you don't need to
write any code, simply configure the form to show only states/provinces related
to the country selected (assuming there is a relationship between state/province
and country). I think this is the least important factor but is a small
advantage of custom entities as it is easier to filter the list based on another
related value. If you define your list as a custom entity you can even do
advanced filtering such as displaying only industries which exist in the country
of the current lead (assuming N:N relationship between country and industry).
That can be much harder to do via OptionSet (custom development) while it is
just a matter of configuring the form in the case of custom entity.
<br />
<br />
<br />
I usually go in the order above to make a decision as the first items have
more impact than the last items of the list above. I hope this can help you
assess the best solution for your situation, let me know if you have other
considerations I missed!Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com4tag:blogger.com,1999:blog-518307109112772972.post-67629510842606612542015-10-14T13:08:00.000-04:002015-10-14T13:08:00.160-04:00Comunidad CRM y la Mesa de Expertos – Otoño 2016<p><em>Announcing the Expert Table for Fall 2016 for my Spanish readers, don’t miss out this event!</em> <p>Come lo ha venido hacienda la Comunidad CRM, estará presentando las novedades de CRM 2016 por medio de una mesa redonda con la participación de varios MVP de habla hispana . Desafortunadamente no podré asistir a este evento puesto que estaré de viaje. Sin embargo, no se pierdan esta increíble oportunidad de aprender sobre las novedades de la próxima versión de CRM y sobre todo la oportunidad de hacerle preguntas a nuestros expertos en vivo. <a href="http://www.comunidadcrm.com/blog/event/15-de-octubre-enterate-lo-que-se-vendra-en-microsoft-dynamics-crm-2016-mesa-de-expertos-mvps/">Registrate aqui!</a> Este es el extracto de introducción a la próxima mesa: <p>Si, leíste bien. CRM 2016. Ya se asoma una nueva versión que estará disponible tanto en CRM Online como On-premises y cargada, muy cargada de novedades por demás interesantes. <p>Es por ello que los expertos más destacados de habla hispana en Dynamics CRM se vuelven a reunir para compartir las novedades de esta nueva versión, entre otras: <ul> <li>La nueva experiencia de servicio – el Interactive Service Hub</li> <li>La nueva versión de la Base de Conocimiento</li> <li>Power BI para Servicio</li> <li>La nueva app para Outlook</li> <li>La nueva búsqueda</li> <li>Trabajo desconectado (“offline”) y novedades en las apps móvliles</li> <li>Plantillas para integración con Excel y Word</li> <li>OneDrive</li> <li>Novedades de la plataforma</li> <li>Social CRM</li></ul> <p>Y hay muchas más novedades! Inscríbete y compártenos que opinas de CRM 2016. Recuerda utilizar #mesa2016. <p>Aprovecho también para felicitar a los líderes de la comunidad de todo su esfuerzo para compartir contenido y crear conocimiento a nivel mundial sobre CRM en español. También los invito a navegar el sitio para ver la cantidad de recursos y cursos a su disposición. <p><a name="_MailAutoSig"></a> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-14162589449627854342015-10-13T13:53:00.000-04:002015-10-13T13:53:00.396-04:00Don’t forget your case title and customer<p><em>Case is a special entity in CRM because it has many out-of-the-box special logic. Sometimes this can be problematic as it forces us to use some fields we might not need.</em></p> <p>I’ve always found many customer scenarios in which the “Customer” field adds no value to the case and we would like to remove it. Although less often, you might have found a scenario in which the “Title” field of the case is also not needed. The bad news is that CRM forces you to use those 2 fields in the case, otherwise you might encounter some issues.</p> <p>For the “Customer” field you can get around it but you need a plugin which is going to set your customer to a dummy hard-coded customer on the Pre-Validate operation whenever a case is created. I haven’t seen this work with sync workflows or business rules (although I haven’t investigated much), unfortunately seems like a plugin might be required for this. Once you have this plugin you can simply hide the Customer field from the form, make it read only and remove this field from all the views. Then your Customer field will be used but not really used. Note that in order to hide this field from the form you might want to make it read-only and then move it to a new "section and hide the entire section. This will help you keep things clean on your form by moving all the “unused” but “locked” fields in a hidden form section.</p> <p>Regarding the title field, CRM will not prevent you from not using this field. You can also hide it an make it read-only and things will seem to work fine. However, as soon as you start using SLA’s you will run into this problem:</p> <p align="left"><em><font color="#000000" size="1">Workflow '{F5245D54-9BA6-4E4B-AB86-9131A076FB4D}' failed due to error: Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.<br> at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)<br> at System.Activities.WorkflowInvoker.Invoke(Activity workflow, IDictionary`2 inputs, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)<br> at Microsoft.Crm.Workflow.SynchronousRuntime.SynchronousWorkflowActivityHost.ExecuteWorkflowUsingInvoker(Activity workflow, ICommonWorkflowContext context)<br> at Microsoft.Crm.Workflow.SynchronousRuntime.SynchronousWorkflowActivityHost.StartWorkflow(WorkflowActivationData activationData, ICommonWorkflowContext context)</font></em><br></p> <p align="left">As you might know, SLA works as workflows behind the scenes. As soon as you create SLA’s there are some background processes (workflows) created which represent the SLA you have created. Some of these workflows are synchronous and it turns out that if your case has no title, the workflow fails with the above error message preventing the creation of the case. This can be easily fixed by either using the case title or placing some dummy value in it. In our case we simply copied the ticket number into the case title. However, one must admit that it is difficult to arrive to such conclusion from the error message above, that is why I’m sharing this finding, I hope you find it useful!</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com1tag:blogger.com,1999:blog-518307109112772972.post-32412408367895784292015-09-14T15:36:00.000-04:002015-09-14T15:36:00.126-04:00CRM 2016 announced! My favorite features to look out for<p> </p> <p>You might have seen the press release (<a title="http://news.microsoft.com/2015/09/08/microsoft-reinvents-productivity-with-upcoming-release-of-customer-engagement-solution/" href="http://news.microsoft.com/2015/09/08/microsoft-reinvents-productivity-with-upcoming-release-of-customer-engagement-solution/">http://news.microsoft.com/2015/09/08/microsoft-reinvents-productivity-with-upcoming-release-of-customer-engagement-solution/</a>), CRM 2016 is no longer under NDA and will be released by Q4 of this calendar year! Below is a very quick summary of favorite features I have extracted very high-level (in <b>bold are my biased favorite features!</b>) but if you’d like to read more about it, I suggest you review the release preview guide: <a href="http://download.microsoft.com/download/D/5/D/D5D38A09-6A5C-4DC0-8649-3A4F39A8F8F3/Microsoft_Dynamics_CRM_2016_Release_Preview_Guide.pdf">http://download.microsoft.com/download/D/5/D/D5D38A09-6A5C-4DC0-8649-3A4F39A8F8F3/Microsoft_Dynamics_CRM_2016_Release_Preview_Guide.pdf</a> <h4> </h4> <h4> </h4> <h4>MOBILE</h4> <p><br><strong>1. Full offline support to create, update and delete records while offline. *</strong><br>2. Open SharePoint/OneDrive documents from CRM mobile app.<br>3. App-to-app deep linking<br>4. New UI controls and ability to bind data fields to UI controls<br><strong>5. Task bases experiences: Show relevant data from multiple records in a single screen for a given task*</strong><br>6. Preview phone/tablet experience from browser while designing forms.<br>7. Full web resource (IFRAME) support in mobile.<br><strong>8. Integration with Intune for secure CRM data with Mobile Application Management (MAM) in BYOD scenarios.</strong></p> <p><strong><font size="3"></font></strong> </p> <p><strong><font size="3">CRM ONLINE*</font></strong><br>1. Data encryption for CRM server<br><strong>2. New data centers in Canada (Toronto and Quebec City) and India<br>3. Bulk Data Loader tool</strong></p> <p><font size="3"><strong></strong></font> </p> <p><font size="3"><strong>PLATFORM</strong></font><br><strong>1. Enhanced modern API: REST, OAuth 2, JSON, OData v4.<br>2. Next Generation Search: Multi-entity search with relevance ranking*<br>3. Azure Machine Learning: Easily configure ML integration for product cross sell recommendations*</strong></p> <p><font size="3"><strong></strong></font> </p> <p><font size="3"><strong>SALES / SERVICE / MARKETING</strong></font><br><strong>1. CRM app for Oultook (PC / Mac / any browser) *</strong><br>2. Excel integration including mobile/tablets and Excel templates for viewing sales data<br>3. Consolidated document storage integration from SharePoint, OneDrive and O365 groups.<br>4. Document Generation: in one click generate Word/Excel documents from CRM data (orders, invoices, etc.)<br>5. Cortana integration*<br>6. Single- and Multi-stream service hubs dashboards<br>7. Re-designed forms for cases, accounts, contacts and activities with reduced clicks, wall filtering, quick actions on related records.<br><strong>8. External Party Access: Give access to your CRM data to third party (customers, partners, etc.)</strong><br>9. Enhanced knowledge management curation process.<br><strong>10. CRM surveys sent to external people (email/web). Results and analysis captured in CRM. (requires Azure to host feedback page)</strong><br><strong>11. Field Service Capabilities (FieldOne acquisition)</strong> </p> <p><strong><font size="3"></font></strong> </p> <p><strong><font size="3">MDM*</font></strong><br>1. Enabled SMS channel for marketing campaigns (inbound/outbound) in MDM<br>2. HTML email rich editor in MDM</p> <p><font size="3"><strong></strong></font> </p> <p><font size="3"><strong>USD</strong></font><br>1. Centralized auditing configuration<br>2. OOB integration with Azure 3rd party systems like HDInsights<br>3. Stability and Performance enhancements<br>4. Simplified install & upgrade experience. Custom DLLs distributed through CRM server configuration.<br>5. PowerBI dashboard</p> <p><font size="3"><strong></strong></font> </p> <p><font size="3"><strong>MSE*</strong></font><br>1. Feeds from custom sources (RSS) and non-public sources (Yammer)<br>2. “Intelligent Social”: Target accounts and share content, detect potential leads vs. cases<br>3. Improved sentiment analysis by domain-specific models.<br>4. Groups and custom roles<br>5. Social Center: Post to Facebook/Twitter with rich multimedia content from social center.</p> <p>* Only available in CRM Online.</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com0tag:blogger.com,1999:blog-518307109112772972.post-4880789973393177882015-08-24T01:45:00.000-04:002015-08-24T01:45:00.465-04:00Agile CRM development with SCRUM<p><em>I recently worked in an agile CRM project as the scrum master and was able to learn some good tips of using scrum for CRM development.</em> <p>I am of the belief that the waterfall days are coming to an end, mostly due to its unsustainable release cadence, inefficiency when reacting to the 'unknown' and the large amount of waste that goes towards tight controls and project management as opposed to focusing effort on product quality and continuous business value generation. In the world of CRM implementations this becomes even more evident because of various factors such as the ability of the platform to quickly produce business value and adapt in an iterative manner, even when the final outcome is yet to be discovered. CRM can be configured very quickly which allows you to achieve valuable work (increment) in iterations (sprints) that can be as short as 1-2 weeks. The fact that you can have stakeholders provide early feedback continuously (sprint reviews) allows you to adapt very quickly to changing demands and gives your users a chance to get familiar with CRM early on. Continuous feedback leads to more valuable user feedback because users have something tangible to review instead of having to imagine what everything will look like. If you are reading this you probably already understand the value of agile and I don't need to convince you further. Now let's look at the typical challenges we find with agile frameworks like scrum and some ideas from my experience that can make your life easier. <p><strong><font color="#000000">1. Your customer needs to fund the project and to do so they need the cost, scope and schedule upfront.</font></strong> This has been the hardest challenge, specially in the consulting world as organizations are used to think that to manage risk means to transfer to someone else (you) the risk of changes in scope, cost and schedule. This is an antiquated mentality when it comes to complex software development because you cannot plan for everything in advance and expect that the requirements you document today is exactly what you are going to need. The result is typically that consulting companies add a large amount of contingency and charge you more for any change in your original requirements (change requests). The end result is that your customer ends up paying more and not getting the optimal solution. What is worse, is that they will not know that the solution is not really doing what they need until it is too late, too much has been wasted and the budget cannot be increased. This is by far the main reason I see waterfall projects fail. But what can you do about it if your customer's culture is waterfall and they want a fixed-everything upfront before they can "fund the project"? I think the magic word here is 'trust'. The best you can do (other than finding another customer that understands that we are not in the 90s) is to earn their trust. Certainly this does not happen overnight but if you are able to build this trust you might eventually be able to propose 'giving it a try' and educating your customer on modern software development practices. The good thing about agile is that you see the results very quickly and you can start by funding a single sprint to see how things go (the bad part is that the first sprint is always the hardest!). One important tip is to make sure your customer trusts you enough to not think that you are just trying to make more money with them, this will not go well and is very far from your objective. In reality your objective is to save them money and increase their probability of success, they need to see that intention clearly. Regarding funding, you can help them switch from the 'fund a project' mentality to the mentality of 'fund a team that is going to give you optimal business value for your project(s)'. <p><font color="#000000"><strong>2. You are not sure how to manage the CRM development and test environments for an agile project</strong></font>. This is one where you might have to try a few things and see what works best with your development team. My suggestion is to start with something as simple as possible and empower your development team to decide if they want to make changes to this during sprint retrospectives. The simplest is to have one (yes, one!) shared environment (CRM organization/tenant) for development and test. <a href="http://gonzaloruizcrm.blogspot.com/2012/01/setting-up-your-development-environment.html">I have blogged in the past here</a> about how to manage multiple dev and test CRM environments and some good practices about that, but I invalidate my own post when it comes to agile. You want to reduce waste and not have to spend time migrating code, merging solutions and fixing environment synchronization issues. This is why you should at least try to have everyone working on the same environment. You will certainly need some rules such as one developer cannot block the environment for debugging while everybody else needs it; but this is why the daily scrum exists. Make sure you have a daily scrum and that it is actually a 15 min maximum daily scrum and not some status report or defect logging meeting, do not invite anybody outside of your development team to the scrum meeting, this time should be wisely used so developers can synchronize and coordinate activities to be the most efficient. Developers can always have their own sandbox environments but these are not development environments as you should not promote code from sandbox to development. <p>Regarding test environment I initially had a separate test environment but the development team quickly realized that it was causing too much overhead with very little value. Instead, try to get everyone to test during the sprint (add testing to your definition of done). A very hard thing to do is to bring acceptance testing as part of the sprint and not an ad-hoc activity after the sprint. Get your product owner engaged to do the necessary acceptance testing prior to sprint review, and even consider incorporating UAT as part of your sprint. This is not easy but will give you increased agility and faster release cycles. If you succeed bringing acceptance testing as part of your sprint then it is more clear why you would not need a separate test environment. <p><font color="#000000"><strong>3. You don't know how long your sprints should be and how much PBIs you should forecast for a given sprint.</strong></font> I think this is one of the places where CRM has an advantage over custom development, you can do a lot in very little time. Therefore, I would suggest to start with shorter sprints (maybe 2 weeks is a good start). Never do a sprint over 4 weeks or you might end up just doing waterfall! At first you will most likely underestimate and not complete all the PBIs by the end of the first sprint. This is not ideal but is acceptable as long as you don't "extend" the sprint (sprints are time boxed, never extend the end date of a sprint, no matter what). The reality is that at first you might struggle with sprint planning, this is normal, try to forecast how much you can complete in your first sprint by keeping in mind your definition of done (make sure you have one!). So think about how much time it will take you not just to code/configure but also to design, analyze, unit test, document, etc.) each PBI. Additionally you need to work with the product owner to select the most urgent PBIs while keeping a balance for the skills required to deliver the sprint. For example, if you have a dev team of 6 but only 1 person knows SSRS, then you cannot realistically include only SSRS reports in the sprint, even if those reports are the most urgent PBIs. You need to make sure your team skill set is proportionate to the skill set required to deliver the sprint otherwise you might end up with 1-2 very busy individuals while others have a hard time finding how to contribute. Additionally when you forecast your sprint make sure you reserve a 10% of total sprint time to do backlog refinement (most teams fail to do this). There are multiple estimation techniques such as planning poker, however, when forecasting your sprint always ask your team at the end to do a 'gut check' (are we confident to be able to deliver these PBIs within next sprint?). I have found gut checks to be very useful and accurate, everyone must keep in mind the time spent in meetings, backlog refinement, sprint reviews and retrospectives, etc. because all these activities are also part of your sprint, not just the development activities. So in reality you spend only about 80% of your sprint capacity developing PBIs (note that development includes design, code, unit test, etc). One more thing about sprints, try to keep the same duration of sprints and the same start and end day of the week. Eliminate any time between sprints (yes this can be hard). Sprint n+1 should start the day after sprint n finishes, this will give you greater agility, as opposed to having "ventilation" time between sprints to do activities that should actually have been done during the sprint. <p><strong><font color="#000000">4. You are uncertain about how you can incorporate UAT and deployment into the scrum process.</font></strong> This is one of those places where reality sometimes crashes with scrum. If you can get your customer to perform UAT during the sprint and you make that work then you are probably a scrum super ninja that is not learning anything new from this post. So that's the ideal scenario, but we know for various reasons this does not always work. In this case you will need to do as much acceptance testing with the product owner during the sprint as possible and then release to UAT as part of your definition of done. The problem is that your team will probably get interruptions for UAT support as the UAT testers are usually in a completely different cycle as your sprint cycles. You will have to deal with this and account for this during sprint planning, but again the more you can test earlier the better because you will get less UAT defects and distractions. In this case you might need a separate environment for UAT and you would need to forecast in your sprint planning the activities for UAT release. Regarding deployment to production, I have often heard the misunderstanding that in scrum you are supposed to deploy to production at the end of every sprint, that's not correct. You can release to production as often as you want, from multiple times per sprint to once very number of sprints. However, at the end of each sprint, your work should be "potentially releasable" according to the scrum guide, so you shouldn't have broken critical functionality at the end of the sprint, even if you don't plan to release to production. It is a good idea to release as often as you can, try to incorporate the DevOps approach and bring development and operations together as much as possible, this will help you achieve a faster release cycle and a smooth transition to production. Of course this is not something that you will get right from one day to the next, DevOps requires multiple changes and great amount of practice and experience to become efficient (beyond the scope of this post). <p> <p>Scrum is an empirical process, so at its core it has transparency, inspection and adaptation. Make sure that you pay attention to these 3 values during your various events, the key is not how to successfully setup a scrum project but more about your agility to inspect and adapt your process to produce business value faster and reducing waste, that is your success criteria! There are certainly many other challenges and suggestions, I might blog more about this in the future, but I would conclude with one famous quote from scrum that couldn't be more accurate: scrum is a framework that is easy to understand but very hard to master.</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com4tag:blogger.com,1999:blog-518307109112772972.post-49013805540090779442015-07-21T09:15:00.001-04:002015-07-21T09:15:11.208-04:00How to cascade activate / deactivate (SetState) in CRM<p><em>CRM allows for an easy configuration of the cascade behavior for delete, assign, reparent, merge and share. But how can we cascade the status of a record to its children?</em></p> <p>This is a problem that I find in many CRM implementations. A typical example is that you want to deactivate all contacts related to an account whenever the account is deactivated. Similarly, if an inactive account is re-activated you might want to re-activate its child records such as the child contacts.</p> <p>The good news is that I have updated the <a href="https://mscrmworkflowutilities.codeplex.com/">MSCRMWorkflowUtilities</a> to handle this for you without having to write any code (currently only supported for CRM 2015+). Simply download and install the solution. Then you can configure any number of cascade behavior for the SetState operation. </p> <p>You will first need to define a workflow based on the parent entity. In the example above, we would define a workflow on the account entity in order to cascade its status to the child contacts. Then you can insert conditions and as many cascade steps as you want. In my example, I will check if the account is active then activate all the child contacts and if the account is inactive then inactivate all the child contats. However, note that you could insert additional steps if for example you want to deactivate all child contacts and activities then you need to insert one step for each child entity.</p> <p><a href="http://lh3.googleusercontent.com/-sieS9atoJNc/Va5F07a8s_I/AAAAAAAAAhc/Px_skPPJeDo/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://lh3.googleusercontent.com/-f0sNymSeLGM/Va5F1I7X5zI/AAAAAAAAAhg/nqYcXpXvasA/image_thumb%25255B1%25255D.png?imgmax=800" width="370" height="169"></a></p> <p> </p> <p>For each “Cascade SetState” step you insert, you need to configure 3 parameters:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpAcxnPl3zQkSTA_L6reW10FWvmUX1cisR_sIsaHiGT74zXADmo4TQhzM4Gh6FJ84MqY4nlHaLrVVxxGl9KHbjKMkUHmKHb4nUrA-IFSqg2beYYN6Ie28MVDfjIwNYW_Xj1io8h2yY4Ga9/s1600-h/image%25255B11%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://lh3.googleusercontent.com/-G2oHJCLq77w/Va5F1_eHccI/AAAAAAAAAhw/SJrHelj2bFw/image_thumb%25255B5%25255D.png?imgmax=800" width="532" height="152"></a></p> <p>1. Child Entity Name: The schema name of the child entity to which you wand to cascade the SetState operation. In this case it will be “contact”. You can obtain the schema name from the entity definition:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0qwxMO_UhsHL6GlgscKeto8bDReevWlClzJsxU0PQkqjDOylIHIKHzmfoRNyiwzhhw4XMZ2kxWzFc4OSME3Zwo7yguVzbhOGPGC7sDx97frJ7oXBKoGHcfqwnK6zmfj88qLnuboyPVIeS/s1600-h/image%25255B7%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://lh3.googleusercontent.com/-mU68-FWgm4k/Va5F2nWyMRI/AAAAAAAAAiE/_0Zslmqa_U8/image_thumb%25255B3%25255D.png?imgmax=800" width="401" height="121"></a></p> <p>2. Child Lookup Attribute To Parent: This is the schema name of the attribute (field) of the child entity that links to the parent entity via a lookup. In this case it is “parentcustomerid”. You can obtain this name from the child entity definition by finding the lookup attribute to the parent entity:</p> <p><a href="http://lh3.googleusercontent.com/-FTtl8ucnXTU/Va5F3N2380I/AAAAAAAAAiI/GWLDmA5cn3k/s1600-h/image%25255B15%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="http://lh3.googleusercontent.com/-VMT7twHEcQ0/Va5F3sDXK-I/AAAAAAAAAiQ/H3vSY0dd_rM/image_thumb%25255B7%25255D.png?imgmax=800" width="669" height="105"></a></p> <p>3. Target Child State Code: The target state/status to cascade to the child entity. For most cases “1” is for deactivate and “0” is to activate. <u>Note some special entities like case and opportunity are not supported because they don’t use a SetState message</u>. All custom entities are supported.</p> <p>You can imagine that there can be a performance problem when you try to deactivate a record which has a large number of child records. Therefore, it is recommended that your workflow be asynchronous instead of synchronous. Have fun!</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com2tag:blogger.com,1999:blog-518307109112772972.post-22636901472658485302015-06-01T13:30:00.000-04:002015-06-01T13:30:00.084-04:00Scalability considerations for CRM / SharePoint integration<em>SharePoint is probably the most natural and easiest to implement solution
for document management for CRM records. However, what happens when you have
millions of records in CRM which might have documents?</em><br />
<br />
I recently worked on fairly large document migration solution to be able to
associate documents to CRM records (e.g. cases, opportunities). There were a few
hundreds of thousands of files amounting to about 50GB of data. So here are a
few things to consider<br />
<br />
<br />
<br />
<strong>1. FOLDER HIERARCHY</strong><br />
<br />
<a href="file:///C:/Users/gonzalo.ruiz/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesBA628099/image[2].png"></a><br />
<br />
With the out of the box integration you have the choice to structure your
folders based on accounts/contacts or simply to create a new library for each
entity at the root of the site. It might be more convenient to navigate
SharePoint if you can start from an account and then find which are the related
cases/opportunities rather than starting with a global list of cases and try to
find which cases are related to a specific account. In general, this structure
is more convenient, until you consider security segregations.<br />
<br />
If you need to segregate SharePoint security in such a way that some users
can access documents of a given entity type (e.g. cases) but not be able to
access documents of another entity type (e.g. opportunities) then the
account/contact hierarchy becomes a problem because you cannot set the security
at the library level (all documents would be on the same library) and you would
have to do some acrobatics with folder security and inheritance breaking which
would be a nightmare to maintain. Instead, if you have each entity type have its
own SharePoint library then you can easily grant/deny access to documents of a
given entity type.<br />
<br />
Nonetheless, remember that with the out of the box CRM/SharePoint
integration, there is no security synchronization between CRM and SharePoint.
You need to keep this in mind throughout your design. Think for example that a
user might not be able to access any opportunities in CRM but the user (if
malicious) can always find a way to see the documents associated to CRM
opportunities by going to SharePoint (unless you block the user completely from
the given SharePoint library).<br />
<br />
Also consider that changing the folder hierarchy after would be a major data
migration effort so you should really think about what hierarchy makes more
sense you’re your situation and consider current or future security
requirements.<br />
<br />
<a href="file:///C:/Users/gonzalo.ruiz/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesBA628099/image[6].png"></a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGbvHG5PDp-lPDVs21esL8FAUeemmCy0ZwDo0219a9sXMZt4RnKwwzX1dbxQKw6PMw0VtvE8ESzZDIVSB3DwynrJRUJbUyhwkmUsBU_5G2SnNceUv0yqy86IqNwG7ZERTWIvnufxEfkQeL/s1600/1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGbvHG5PDp-lPDVs21esL8FAUeemmCy0ZwDo0219a9sXMZt4RnKwwzX1dbxQKw6PMw0VtvE8ESzZDIVSB3DwynrJRUJbUyhwkmUsBU_5G2SnNceUv0yqy86IqNwG7ZERTWIvnufxEfkQeL/s1600/1.JPG" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimpe4nb31wYMGL5genWZEDtdmKbj38JmrkZGNmb_mo_aapjR0w-dPCQ11N0_-M4gassQ56dtZpX0qCO_-CIX5seclYOCthFBAzTIAFcoR_9SA0-3GgkT5kfS3XVLlPpXT0aKUaVaHgp1w0/s1600/2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimpe4nb31wYMGL5genWZEDtdmKbj38JmrkZGNmb_mo_aapjR0w-dPCQ11N0_-M4gassQ56dtZpX0qCO_-CIX5seclYOCthFBAzTIAFcoR_9SA0-3GgkT5kfS3XVLlPpXT0aKUaVaHgp1w0/s1600/2.JPG" /></a></div>
<br />
<a href="file:///C:/Users/gonzalo.ruiz/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesBA628099/image[7].png"></a><br />
<a href="file:///C:/Users/gonzalo.ruiz/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesBA628099/image[6].png"></a><br />
<br />
<br />
<strong>2. NUMBER OF ITEMS PER FOLDER / LIBRARY</strong><br />
<br />
Imagine that you have 100K cases in your system and you have enabled
SharePoint integration for case entity. And perhaps every case has at least one
document. In this case, your case library/folder will contain 100K items flat on
the same list. This goes well beyond what SharePoint recommendations are for
scalability and performance. It is not recommended to go beyond 5000 items (even
that is already quite high). Of course you can always implement some sort of
archiving or use multiple SharePoint sites depending on some criteria so you
split this load. Another [reasonably] easy solution is to further structure your
folders by year, quarter or month (or all of these). This way you will not end
up with 100K folders under the “cases” folder. Instead, the maximum number of
folders you will have under a single folder will be the maximum amount of cases
that can be opened in a given month/quarter which might be a more reasonable
number.<br />
<br />
<a href="file:///C:/Users/gonzalo.ruiz/AppData/Local/Temp/WindowsLiveWriter1286139640/supfilesBA628099/image[15].png"></a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH7SzZ0UqJpuwSJ13_2CmNBNHPdiTkcl6eVrH_I1Z4qNIKQPqBMMJddPUaAHePoA-hCZ1VkF80xlaiN2bW9HQpEejHohugmCNfAwtMkd7bx6ZhlKu9GBH_hyKTT30R3_jKPrA1bw0ymFbl/s1600/3.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH7SzZ0UqJpuwSJ13_2CmNBNHPdiTkcl6eVrH_I1Z4qNIKQPqBMMJddPUaAHePoA-hCZ1VkF80xlaiN2bW9HQpEejHohugmCNfAwtMkd7bx6ZhlKu9GBH_hyKTT30R3_jKPrA1bw0ymFbl/s1600/3.JPG" /></a></div>
<br />
<br />
<br />
In the example above, we have 2 new layers: Year and Month. These correspond
to the date on which the case was opened. By adding these additional layers, we
can now guarantee that there will be no folder with more than a few hundred
sub-folders, since we know that we only open a few hundred cases per month.<br />
<br />
The downside is that this cannot be done by simple configuration or out of
the box integration. This structure would require that you register a plugin on
create of case, which will create the document locations and the SharePoint
folder for the case being created. Some other disadvantages of this approach is
that: <br />
<br />
- Every case will now have a folder, even if the case has no documents. This
should not be a problem if you know that anyway all cases have documents<br />
<br />
- You are creating the SharePoint folder at the same time as you create the
case, instead of the OOB behavior which is to create the folder on demand the
first time document library is accessed in CRM. This is not necessarily a bad
thing though.<br />
<br />
- If your plugin is sync and it fails (e.g. SharePoint is down) then it will
prevent the creation of the case in CRM. If your plugin is async and it fails,
then you need to have a way to recover and create the correct folder when the
user tries to access document library for this record.<br />
<br />
<br />
<br />
<br />
<br />
<strong>3. SIZE AND GROWTH OF YOUR SHAREPOINT SITE</strong><br />
<br />
For sites with large volume of documents, large document sizes or rapidly
growing volumes, you might also need to consider how long you have until you
start having a performance or limitation problem with things like the max size
of your content database, max number of files in the library or simply the
maximum size of your site. O365 also has some limitations that you need to
review. If you identify that this could be a potential problem you should
consider implementing some sort of archival solution which will allow you to
keep a link between CRM records and SharePoint files while at the same time
optimizing for current and mostly used records. I have heard of people simply
changing the CRM site every now and then and creating a new site once the old
site is getting too large. I guess any of these strategies would work as long as
you have defined a process to scale your site and monitor the volumes regularly.
The most important thing is to consider this during your design phase and have
identified your approach to handle scalability, even if you might not implement
a long term maintenance strategy right away.Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com7tag:blogger.com,1999:blog-518307109112772972.post-86239251251314918122015-04-07T13:33:00.000-04:002015-04-07T13:33:00.338-04:00CRM 2011 Workflow Utilities now available for CRM 2013 and 2015<p><em>By popular demand, I have upgraded the CRM 2011 workflow utilities and rebranded it to be compatible with CRM 2013 and CRM 2015.</em></p> <p>You might be familiar with the CRM 2011 Workflow Utilities project that I released to Codeplex a few years ago (<a href="https://crm2011workflowutils.codeplex.com/">https://<b>crm2011workflow</b>utils.codeplex.com/</a>). By popular demand I have released version 3.0 for CRM 2015 (both for Online and On Premises). You can download the CRM 2015 version here: <a title="http://mscrmworkflowutilities.codeplex.com/releases/view/613024" href="http://mscrmworkflowutilities.codeplex.com/releases/view/613024">http://mscrmworkflowutilities.codeplex.com/releases/view/613024</a></p> <p>Note that if you are still on CRM 2011 or CRM 2013, you must continue to use the release version 2.0.2.0 available here: <a title="http://mscrmworkflowutilities.codeplex.com/releases/view/102880" href="http://mscrmworkflowutilities.codeplex.com/releases/view/102880">http://mscrmworkflowutilities.codeplex.com/releases/view/102880</a> which has 2 solutions, one for CRM Online and another for CRM OnPremises.</p> <p>Additionally, I have rebranded the tool from “CRM 2011 Workflow Utilities” to “MSCRM Workflow Utilities” since it supports multiple CRM versions (and hopefully future versions too!).</p> <p>Workflow utilities is a CRM solution that allows you to extend the CRM workflow design experience by providing the following custom workflow steps available directly from the CRM process designer:</p> <ul> <li>Delete record</li> <li>Share or “unshare” a record</li> <li>Insert hyperlink to a CRM record</li> <li>Qualify lead (convert to account/contact/opportunity)</li> <li>Bulk activate / deactivate records (no record count limit)</li></ul> <p> </p> <p>For more details on how to install and use the solution please visit the <a href="http://mscrmworkflowutilities.codeplex.com/">Codeplex</a> site. Thanks for all the feedback received, enjoy!</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com5tag:blogger.com,1999:blog-518307109112772972.post-85552109675804198532015-03-02T13:16:00.000-05:002015-03-02T13:16:00.604-05:00Should I register my plugins in Sandbox or no isolation?<p><em>As you are probably aware CRM plugins and custom workflow activities can either execute in isolation (sandbox) or without isolation. This post explores when you have that choice and what good practices you should consider for making that choice.</em></p> <p>The CRM Sandbox is a feature released in CRM 2011 whose objective was to address one problem: How can we trust custom code to execute in a server that hosts multiple customers? Certainly, the core scenario is for CRM Online in which you have different tenants (customers) sharing the same infrastructure. You can imagine Microsoft would not allow any random .Net code to execute on their data centers without some controls. The same would apply if you are a partner who hosts tenants for a third party: You have to be careful about the code that executed in your infrastructure.</p> <p>So in order to execute someone else’s code in a <em>secure</em> manner you must make sure the code executes in a sandbox environment which has certain limitations. For example:</p> <p><strong><u>1. Code is running in partial trust.</u></strong> This prevents operations like accessing the local file system, registry, event log and many more. This is managed by .Net Code Access Security feature, you can read more here: <a title="https://msdn.microsoft.com/en-us/library/930b76w0(v=vs.80).aspx" href="https://msdn.microsoft.com/en-us/library/930b76w0(v=vs.80).aspx">https://msdn.microsoft.com/en-us/library/930b76w0(v=vs.80).aspx</a> . Note that by default sandbox plugins can sent HTTP or HTTPS messages to external endpoints, such as custom web services or Windows Azure. It is possible to overwrite this default to prevent sandbox plugins from calling external endpoints (other than localhost). You can overwrite this default in the registry (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\SandboxWorkerOutboundUriPattern). More info on this <a href="https://msdn.microsoft.com/en-us/library/gg334752.aspx">here</a>.</p> <p><strong><u>2. Throttling is applied to your plugin if it exceeds a given CPU/memory threshold.</u></strong> You can imagine this being useful to prevent someone’s code hijacking all the server resources. The process which executes your plugin can be killed if your plugin exceeds certain thresholds or becomes unresponsive.</p> <p><strong><u>3. Certain statistics are calculated for your sandbox plugins.</u></strong> These statistics typically include data such as how many times a given plugin type has crashed and what is the average execution time. These statistics are useful for providing throttling to someone else’s plugins.</p> <p>The exact mechanisms used by Sandbox are not all fully documented, this is perhaps because it reduces the risk of vulnerability of someone exploiting sandbox in CRM Online. However, what we do know is that sandbox plugins are executed in a special sandbox worker process that supports partial trust. Each CRM organization has its own sandbox worker process. Therefore, if someone’s malicious code results in the process getting killed, nobody else will be affected. Furthermore, we know there is a sandbox host process which you can see running as one of the windows services of CRM.</p> <p>If you are working on CRM Online you have no choice and you must register all your plugins and custom workflow activities in isolation (sandbox mode) because of security reasons. However, if you are on premises, you have the choice, so which one should you pick?</p> <p>You might hear that it is a good practice to always register on sandbox because if is more <em>secure</em> and you get free plugin statistics. Additionally, your solution will work for an Online customer or an on-premises customer without any modifications. While it is true that if you are an ISV and you sell solutions in the marketplace, it makes it easier to manage if your plugins are always in sandbox so you don’t need to release SKU-specific solutions (one solution for Online customers and another for on-premise customers). However, I disagree with anyone telling me that registering in sandbox is a good practice. It makes sense for the ISV scenario, but if you are simply writing a custom solution for your on-premise customer, you are not really getting any advantage by using sandbox. On the contrary, you are losing valuable system performance by doing so (more on this later).</p> <p>So unless I have a real requirement to compute and regularly report my plugin statistics, I don’t see this feature adding much value to my on-premises customer. If your plugins crash or fail then you have a bigger problem to solve than looking at statistics. Perhaps measuring how long your plugins take to execute can be useful in some scenarios, but if you have never looked at these (or you don’t know of the existence of these statistics) then it is a good indication that you don’t need them. You might find them valuable in some cases, but at the end of the day, you are losing so many points for the slow performance of sandbox plugins that you really need to think twice what you value more: Some statistics you might never look at or a faster performance of your CRM?</p> <p>The reason I talk about performance is because sandbox plugins are slower to execute that non-isolated plugins. I would have loved to have some data to show you but this is an easy test you can do, or I might do it in the future. Instead, I will explain why you can expect slower performance when using sandbox. Whenever CRM needs to execute a sandbox plugin, the process executing the operation (either IIS w3wp.exe or CrmAsyncService.exe) will need to do the following:</p> <blockquote> <p>1. Temporarily suspend the current transaction (there is a time limit for this suspension)</p></blockquote> <blockquote> <p>2. Serialize the entire exeuction context of the current transaction and send a request to the sanbox host process to execute a given plugin on the current context</p></blockquote> <blockquote> <p>3. The sandbox host process will then deserialize this information and will then again serialize the information in order to delegate to the assigned “worker” process to execute the plugin.</p></blockquote> <blockquote> <p>4. The worker process then deserializes the current execution context and runs the plugin code in partial trust.</p></blockquote> <blockquote> <p>5. The result is then sent back to the sandbox host process.</p></blockquote> <blockquote> <p>6. The result is then sent back to the original process who requested a sandbox plugin (w3wp.exe or CrmAsyncService.exe). </p></blockquote> <p>Below are some diagrams to help you understand the flow in each scenario:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg66flZcFBFLE9LjxvfKoBpovc18L8eT8UCigCckbSz7ddK-s8RzW7KtTiA9r85qC27nxE3JJussesRo5iJznBZQA3jNAB5odC-xHvT6TZZGD-BfoaGw_eqFAY_vfhMaXu9lRNABKxsIJuI/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/-9ZNVbAwRHjI/VPPBQdB2hvI/AAAAAAAAAfw/Jx_z8HnqNY4/image_thumb%25255B1%25255D.png?imgmax=800" width="539" height="467"></a></p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH67VNtS1fdL6uLYCmHIEwVb15R8h2nBtP4VQKyAHSP8_nYlJPD1wOOhVDIkH-p7jGfip2eB6dbQfAuHuY5JW9UtExO0Xak_QPRETpJGvrsyRdHv1jspUDgZuxtYbE8_2Df27myNOMBVqz/s1600-h/image%25255B7%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/-ueMCAfljF70/VPPBRDmme-I/AAAAAAAAAgA/SOH_pFtGtqc/image_thumb%25255B3%25255D.png?imgmax=800" width="546" height="466"></a></p> <p> </p> <p><a href="http://lh6.ggpht.com/-gSPfztxVkek/VPPBRdGlF5I/AAAAAAAAAgI/em0BMVbdF44/s1600-h/image%25255B15%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/-BJBw0zYrudI/VPPBR412vQI/AAAAAAAAAgQ/Ir1AK7RSwHU/image_thumb%25255B7%25255D.png?imgmax=800" width="481" height="425"></a></p> <p>As you can see, this requires a lot of chatter between various processes and app domains, which is not exactly something you get for free. Additionally more room for errors in your infrastructure that can occur when you require multiple processes to be synchronized. Note that your plugin always executes in the sandbox worker process when registered in sandbox. If your plugin is not registered in isolation then it will be executed directly by the w3wp.exe process or the CrmAsynService.exe process (for async plugins and async workflow activities).</p> <p>Therefore my conclusion is that you should not unnecessarily sacrifice system performance by registering your plugins in sandbox unless you have a good reason to. If you work with CRM Online you have no choice and if you are an ISV it might make your life easier to use sandbox; but I suggest you always make non-sandbox (no isolation) your default choice unless you have specific requirements or limitations that force you to register your plugin in sandbox mode. Additionally, you need to consider the limitations of sandbox plugins. For example, if you desire your plugin to use the event log or a local file system or shared drive, you cannot register your plugin in sandbox.</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com6tag:blogger.com,1999:blog-518307109112772972.post-33906772581503638812015-01-30T09:33:00.000-05:002015-01-30T09:33:00.366-05:00How to send an email via workflow without using the Send Email step (use create step instead)<p><em>You might already wonder: why the heck wouldn’t I just use the awesome “Send Email” step available in the workflow designer?! This post explains why but how to easily work around it.</em></p> <p>We recently came across a scenario in which we wanted to automate some emails via workflows. However, we also needed to add some attachments to those emails based on some logic. Unfortunately in the current CRM workflow designer you cannot have any logic to add dynamic attachments to your email when you use the “Send Email” step of a workflow definition. However, we knew we could develop a simple custom workflow activity that could add attachments to the email. The problem was: If you use a Send Email step, the email is sent immediately and you have no way modify the email with attachments. </p> <p>So the easy work-around is to first use the “Create” step in the workflow designer to first create an instance of the email an populate all the fields as desired. Then you can add additional steps that take actions on those emails, for example, a custom step that adds attachments to it. Because the records from the “Create” steps are available in all subsequent steps in the workflow, you can always reference it back and update it as many times as you want.</p> <p>Once you have manipulated your email and added the relevant attachments with a custom step you are finally ready to send it, and this is the tricky part. How can you add a step to send an existing draft email? Well, certainly you could develop another custom workflow activity to do so. However, the solutions turns out to be much easier: All you need to do is set the status of the email to “Pending Send” and set the No. of Delivery Attempts to 0. Once you do this, then the email router will be ready to deliver the email (of course this is assuming you have configured your email router!). </p> <p>So with a simple “Change Status” step and a subsequent “Update” step you can force the workflow to send the email you created!</p> <p><a href="http://lh4.ggpht.com/-cPommMvpLu4/VMrfhvFHPzI/AAAAAAAAAfE/q3GY_zopoHc/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/--9UGiwucYfk/VMrfiTwlHBI/AAAAAAAAAfM/A5jodFcxBg4/image_thumb%25255B1%25255D.png?imgmax=800" width="378" height="114"></a></p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidbT5T6P8iWmJfBiV-IDWmf1w26gtMxPNCM129ArRtxPNWOZmRped-6j76MAbl4_-pGGZMh-p2i_QI6lXMKbvKKtac72K_-GBDJNebibuGMZfAezpc9Vk4JXBT7iGxuMisi_KdIR78J-be/s1600-h/image%25255B7%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh6.ggpht.com/-P3_dsihOpFc/VMrfj21s6kI/AAAAAAAAAfc/9CtzvVwd6lk/image_thumb%25255B3%25255D.png?imgmax=800" width="231" height="32"></a></p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com5tag:blogger.com,1999:blog-518307109112772972.post-42893916959157005352014-11-19T14:06:00.000-05:002014-11-19T14:06:00.227-05:00Avoiding form reload when switching CRM forms based on a field<p><em>Quite often we define different forms for a given entity and we do a form switch based on a field rather than based on the security role of the user. The problem we observe with this approach is that there is often a double form load because when the wrong form loads then record re-loads using the appropriate form. This post offers a solution for avoiding the double form load in this scenario</em>.</p> <p>Say you have different types of opportunity which are identified by an “Opportunity Type” field and each opportunity type has its own form and BPF. The traditional approach would be to have a JavaScript on load of the form which will navigate to the appropriate form. The issue is that CRM web application loads by default the form that was <em>last used</em> by the given user. So if the user was looking at an opportunity of type “New Sale” and then opens an opportunity of type “New Service Contract” then the service contract opportunity will first load using the “new sale” form and after that form is loaded it will open the correct “service contract” form. The effect is bad user experience because the form loads twice so it doubles the form load time.</p> <p>I have come across a solution that prevents this double form loading from happening which was suggested by a colleague. The solution is based on the premise that we can change which is the <em>last viewed form</em> for a given user on the fly when the user is retrieving a record. So you can intercept the retrieve operation from a plugin and then look at the type of opportunity that the user is retrieving and then update the user’s last viewed form to be the correct form for the opportunity type that is just getting retrieved. This way, the opportunity is always loaded in the correct form without a form switch.</p> <p>The solution is a two-step process for the retrieve plugin:</p> <p>1. In the pre-operation you must make sure to include the “opportunity type” field in the ColumnSet so that it is available later on for you to decide which is the appropriate form for the record.</p> <p>2. In the post-operation you will have the “opportunity type” value and then you can update the user’s last viewed form to the correct form for the given opportunity type. To do so you simply need to modify the UserEntityUISettings entity for the given user and set the lastviewedformxml field.</p> <p>Here is some sample code to use from your Retrieve plugin:</p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:d351be1e-09c3-484d-8aac-327228f2d576" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">on Retrieve plugin</div> <div style="background: #fff; max-height: 500px; overflow: auto"> <ol style="background: #ffffff; margin: 0; padding: 0 0 0 5px;"> <li><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> pluginContext = (</span><span style="background:#ffffff;color:#2b91af">IPluginExecutionContext</span><span style="background:#ffffff;color:#000000">)context;</span></li> <li style="background: #f3f3f3"><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (pluginContext.IsPreOperationStage())</span></li> <li><span style="background:#ffffff;color:#000000">{</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> columns = (ColumnSet)pluginContext.InputParameters[</span><span style="background:#ffffff;color:#a31515">"ColumnSet"</span><span style="background:#ffffff;color:#000000">];</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (!columns.Columns.Contains(OpportunityTypeAttributeName))</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">columns.AddColumn(OpportunityTypeAttributeName);</span></li> <li><span style="background:#ffffff;color:#000000">}</span></li> <li style="background: #f3f3f3"><span style="background:#ffffff;color:#0000ff">else</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (pluginContext.IsPostOperationStage())</span></li> <li><span style="background:#ffffff;color:#000000">{</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> currentEntity = context.GetEntityFromContext();</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (currentEntity == </span><span style="background:#ffffff;color:#0000ff">null</span><span style="background:#ffffff;color:#000000">)</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">return</span><span style="background:#ffffff;color:#000000">;</span></li> <li> </li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">SetForm(currentEntity.ToEntity<Opportunity>(), service, context.UserId, tracingService);</span></li> <li><span style="background:#ffffff;color:#000000">}</span></li> </ol> </div> </div> </div> <p> </p> <p> <div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:7d7a5b2c-e7c8-4a43-93c1-42c4c0ed6728" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px"> <div style="border: #000080 1px solid; color: #000; font-family: 'Courier New', Courier, Monospace; font-size: 10pt"> <div style="background: #000080; color: #fff; font-family: Verdana, Tahoma, Arial, sans-serif; font-weight: bold; padding: 2px 5px">Set the correct form</div> <div style="background: #fff; max-height: 500px; overflow: auto"> <ol style="background: #ffffff; margin: 0; padding: 0 0 0 5px;"> <li><span style="background:#ffffff;color:#0000ff">private</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#0000ff">void</span><span style="background:#ffffff;color:#000000"> SetForm(</span><span style="background:#ffffff;color:#2b91af">Opportunity</span><span style="background:#ffffff;color:#000000"> opp, </span><span style="background:#ffffff;color:#2b91af">IOrganizationService</span><span style="background:#ffffff;color:#000000"> service, </span><span style="background:#ffffff;color:#2b91af">Guid</span><span style="background:#ffffff;color:#000000"> userId)</span></li> <li style="background: #f3f3f3"><span style="background:#ffffff;color:#000000">{</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> query = </span><span style="background:#ffffff;color:#0000ff">new</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#2b91af">QueryExpression</span><span style="background:#ffffff;color:#000000">(</span><span style="background:#ffffff;color:#2b91af">UserEntityUISettings</span><span style="background:#ffffff;color:#000000">.EntityLogicalName);</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">query.Criteria.AddCondition(</span><span style="background:#ffffff;color:#a31515">"ownerid"</span><span style="background:#ffffff;color:#000000">, </span><span style="background:#ffffff;color:#2b91af">ConditionOperator</span><span style="background:#ffffff;color:#000000">.Equal, userId);</span></li> <li> <span style="background:#ffffff;color:#000000">query.Criteria.AddCondition(</span><span style="background:#ffffff;color:#a31515">"objecttypecode"</span><span style="background:#ffffff;color:#000000">, </span><span style="background:#ffffff;color:#2b91af">ConditionOperator</span><span style="background:#ffffff;color:#000000">.Equal, </span><span style="background:#ffffff;color:#2b91af">Opportunity</span><span style="background:#ffffff;color:#000000">.EntityTypeCode);</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">query.ColumnSet = </span><span style="background:#ffffff;color:#0000ff">new</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#2b91af">ColumnSet</span><span style="background:#ffffff;color:#000000">(</span><span style="background:#ffffff;color:#a31515">"lastviewedformxml"</span><span style="background:#ffffff;color:#000000">);</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> settings = service.RetrieveMultiple(query).Entities;</span></li> <li style="background: #f3f3f3"> </li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#008000">// Some users such as SYSTEM have no UserEntityUISettings, so skip.</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (settings == </span><span style="background:#ffffff;color:#0000ff">null</span><span style="background:#ffffff;color:#000000"> || settings.Count != 1 || opp.pwc_OpportunityType == </span><span style="background:#ffffff;color:#0000ff">null</span><span style="background:#ffffff;color:#000000">) </span><span style="background:#ffffff;color:#0000ff">return</span><span style="background:#ffffff;color:#000000">;</span></li> <li> </li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> setting = settings[0].ToEntity<</span><span style="background:#ffffff;color:#2b91af">UserEntityUISettings</span><span style="background:#ffffff;color:#000000">>();</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">string</span><span style="background:#ffffff;color:#000000"> formToUse;</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">switch</span><span style="background:#ffffff;color:#000000"> ((</span><span style="background:#ffffff;color:#2b91af">pwc_contractedengineservice</span><span style="background:#ffffff;color:#000000">)opp.pwc_OpportunityType.Value)</span></li> <li> <span style="background:#ffffff;color:#000000">{</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">case</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#2b91af">pwc_contractedengineservice</span><span style="background:#ffffff;color:#000000">.NewSale:</span></li> <li> <span style="background:#ffffff;color:#000000">formToUse = </span><span style="background:#ffffff;color:#2b91af">String</span><span style="background:#ffffff;color:#000000">.Format(</span><span style="background:#ffffff;color:#a31515">"<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>"</span><span style="background:#ffffff;color:#000000">, AdHocEngineServiceSaleFormId);</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">break</span><span style="background:#ffffff;color:#000000">;</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">case</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#2b91af">pwc_contractedengineservice</span><span style="background:#ffffff;color:#000000">.ServiceContract:</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">formToUse = </span><span style="background:#ffffff;color:#2b91af">String</span><span style="background:#ffffff;color:#000000">.Format(</span><span style="background:#ffffff;color:#a31515">"<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>"</span><span style="background:#ffffff;color:#000000">, ContractedEngineServiceSaleFormId);</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">break</span><span style="background:#ffffff;color:#000000">;</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">default</span><span style="background:#ffffff;color:#000000">:</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">return</span><span style="background:#ffffff;color:#000000">;</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">}</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">if</span><span style="background:#ffffff;color:#000000"> (!formToUse.Equals(setting.LastViewedFormXml, </span><span style="background:#ffffff;color:#2b91af">StringComparison</span><span style="background:#ffffff;color:#000000">.InvariantCultureIgnoreCase))</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">{</span></li> <li> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#008000">// Only update if the last viewed form is not the one required for the given opportunity type</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000"></span><span style="background:#ffffff;color:#0000ff">var</span><span style="background:#ffffff;color:#000000"> s = </span><span style="background:#ffffff;color:#0000ff">new</span><span style="background:#ffffff;color:#000000"> </span><span style="background:#ffffff;color:#2b91af">UserEntityUISettings</span><span style="background:#ffffff;color:#000000"> { Id = setting.Id, LastViewedFormXml = formToUse };</span></li> <li> <span style="background:#ffffff;color:#000000">service.Update(s);</span></li> <li style="background: #f3f3f3"> <span style="background:#ffffff;color:#000000">}</span></li> <li><span style="background:#ffffff;color:#000000">}</span></li> </ol> </div> </div> </div></p> <p> </p> <p>Regarding the supportability of this approach it seems to be a grey area. Technically you are doing operations via the SDK and using regular plugins which seems like a supported approach. The only problem is that the lastviewedformxml field of the UserEntityUISettings which is not documented in the SDK (although the entity seems valid for update). </p> <p>A few things to consider is that if you are able to make this work with simple security roles then it would be simpler to configure the forms using security roles and you would not need this work-around. Consider as well whether you really need multiple forms or you can have other approaches such as JavaScript to hide/show sections depending on the type of record. I have posted some guidance regarding the different options available: <a title="http://gonzaloruizcrm.blogspot.ca/2014/07/different-entity-flavours-new-entity.html" href="http://gonzaloruizcrm.blogspot.ca/2014/07/different-entity-flavours-new-entity.html">http://gonzaloruizcrm.blogspot.ca/2014/07/different-entity-flavours-new-entity.html</a></p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com23tag:blogger.com,1999:blog-518307109112772972.post-32543466704874808742014-10-14T13:11:00.000-04:002014-10-14T13:11:00.325-04:00Error when importing CRM solution 0x8004F658: The label {}, id: {} already exists. Supply unique labelid values.<p><em>I usually don’t post on CRM bugs but this one made me lose so much time that I figured I might share my experience.</em></p> <p>We are in the middle of a CRM 2013 upgrade project and while transporting our main solution with all the entities we keep getting this error message: <em>The label '{0}', id: '{1}' already exists. Supply unique labelid values </em>while importing entity forms<em>. </em>I first thought that somehow we messed up the labels since we have a bilingual English and French system. However, after looking at the LocalizedLabel tables I could not find any duplicates of what the error message was talking about.</p> <p>Then I decided to do a search in all the system forms in the target environment and check where the labelid occurred. I never really found a duplicate but I noiticed that in some cases the label already existed in the target environment in the formXML. This was as expected but to get over the problem I deleted the problematic forms in the target environment (since they will get overwritten by the new solution anyway). Some forms were the system forms so I could not really delete them, therefore I just stripped them down completely to remove all tabs, sections and fields as much as possible to make sure the custom labels would have been deleted.</p> <p>Now I publish all the customizations and try importing again. Half of the failures are gone but I still get the same error message on some entity forms. I find it weird since the “duplicate” labels are nowhere to be found. I also validated that in my custoizations.xml the labelid was not duplicated, no, each GUID appeared only once.</p> <p>With no time to call Microsoft support I had to find a work-around. So here’s what I do (not proud of the solution but it got me out of trouble). I open the customizations.xml file and locate the labelid which solution import is complaining about. And I simply generate a new GUID and replace it:</p> <p><a href="http://lh5.ggpht.com/-pcXCWpMUTk0/VDwyFrsCn6I/AAAAAAAAAec/nOHn0DMZ4GE/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/-yfWNSGhckXk/VDwyGP7oLtI/AAAAAAAAAek/Lz2BrJ6gUhc/image_thumb%25255B1%25255D.png?imgmax=800" width="617" height="118"></a></p> <p>In some other cases there was not even a “labelid” attribute on the tab so I just added it with a new Guid. This happened in some tab, section and cell nodes, it was all over the place for some reason.</p> <p>So after assigning a new Guid to all these sections I was able to import successfully. I never really had the time to go deeper into the root cause, but after talking to a colleague we suspect that the issue stems from the fact that we used the “Merge Forms” button that is available in CRM 2013 to help you upgrade your forms. The issue only occurred for the entities for which we used that feature so therefore our suspicion. </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQt6QeyVqDVh2n5rNV6Ttp87LgiiMHwHPM9zbGBpqUAZahrutKi8SUZYOkcV58S9c1qn6J8pMXLaDyXK-OYbS_JSUH9zZaeIYo3hs2VKIUQcB7RvKteJwfWKu6E5tfLbpWeFa2-fsCjeKf/s1600-h/image%25255B7%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/-KB2_lEY2UqM/VDwyHqp659I/AAAAAAAAAe0/SxJuREDFMsU/image_thumb%25255B3%25255D.png?imgmax=800" width="383" height="168"></a></p> <p>I will never be sure but I will try to avoid using that button until I have time to get back to the root cause analysis of this issue, lot of time wasted.</p> Gonzhttp://www.blogger.com/profile/02521457712101956774noreply@blogger.com3