tisdag 25 augusti 2015

Mapping from opportunity through quote to salesorder

I've run into this issue a couple of times now so I thought it might be a good thing to collect what I've found about this issue. If you noticed that the order of the lines from an opportunity isn't copied from opportunity to quote and then on to salesorder. 

This probly ranks among the sort-of-supported, but it's not any SQL hacks involved so I think it's sort of kosher. 
All info is taken from https://community.dynamics.com/crm/f/117/t/140108 which in turn points to a bllog. This is a  summary of that.

First, run the following SQL command on your organisation db:
SELECT  EntityMapId
FROM    EntityMapBase
WHERE   TargetEntityName='quotedetail' AND SourceEntityName='opportunityproduct'
That will give you a guid to a mapping page which is interesting. When you want to do the same thing on quote to order mapping, change the target to "salesorderdetail" and the source to "quotedetail"

Now we use the guid provided by the nice database in the following URL: http://[x]/Tools/SystemCustomization/Relationships/Mappings/mappingList.aspx?mappingId=[y]. Change [x] to serverurl and organisation (myserver/testorg/) and the [y] to the guid just found in the database

This opens a mapping page where you can insert mapping from sequencenumber to sequencenumber and the products will be in the same order in both records.
I'm pretty much posting this to make it easier for me to find, maybe it's good for someone else too. Again, I haven't found out how you did this, just to make sure.

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se  
 

fredag 14 augusti 2015

Exchange rates per record, or any color you like

Since I started working with Dynamics CRM the "issue" of one exchange rate per currency has come up a couple of times. How do you handle it, when do you update, what do you do if you want to... 
A customer of ours wanted to have an exchange rate per record so how can you fix that and still be able to use the rest of the functionality.

This is by no means THE solution but it is one. I was fiddling around a bit with CRM when I got the problem. Could I write directly to the base currency, was there any other way around the issue, should we use some other field?

I started looking on the web for solutions and I found this short blog http://www.crmsoftwareblog.com/2012/11/dynamics-crm-2011-multiple-exchange-rates-for-currencies/ which pointed be in the direction of the RetrieveExchangeRate message. Having no idea, I had a look in the plugin registration and yes, there was indeed still such a message



















(Yes, I'm still using the 2011 plugin registration tool, it works and you can store multiple connections...)

So, having found this message and having no idea how to use it, I didn't find anything about it in the SDK either, so I started out with a pretty empty plugin which was called by the message which I step debugged in VS to see what was going on.

It would prove to be fired before everything else and the calling object was present as context.ParentContext. After that quick test I started getting stuff together. So, this is what I did:

IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
            Entity parentTarget = ((Entity)context.ParentContext.InputParameters["Target"]);
            IPluginExecutionContext parentContext = context.ParentContext;


The usual suspects, set stuff up.

if (parentTarget.LogicalName == "crmk_testentity")
            {


Run the code only when we are interested of the result. Now you can do pretty much what you want, in my case we had the exchange rate in a parent object so I had to fetch that, it could also be the case that the currency was the base currency and the plugin didn't need to do anything. This meant I needed to fetch the currency and the wanted exchange rate. 

The magic happens with the output from the RetrieveExchangeRate plugin,  
context.OutputParameters["ExchangeRate"] = exch; 

"exch" is a decimal and you can set it to whatever you want and then CRM behaves as if it just retrieved the exchange rate from the system. Since we're using a plugin message and not doing anything realy naughty, more than pretty much a man in the middle attack on the system itself, this should be supported.

Happy coding

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se  
 

onsdag 8 juli 2015

Company Name not available in lead views

I was editing system views for a customer today and had some issues with the company name field on the lead system views.

It seems that this is a bug which has been reported to Microsoft connect, so please upvote this. https://connect.microsoft.com/dynamicssuggestions/feedbackdetail/view/955886/cannot-add-company-name-column-in-existing-standard-view-in-lead-entity

I found this in CRM 2013 but it seems that it's an issue in CRM 2015 as well. Here's a thread where I found the connect issue; https://community.dynamics.com/crm/f/117/p/122564/405479#405479

Please vote on connect so we get rid of these mighty annoying bugs.

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se

tisdag 7 juli 2015

Part two of the time zone tombola

So, I did actually keep the "6 months timeframe" that I promised in the last part, just over two months is a bit longer than I planned this though. So how did I go on with our time zone issue?

The main idea is that we use the CRM time fields to keep track of stuff in UTC time. Those fields will always show the user when the meeting takes place in the time zone you have set in CRM.














As you can see in the picture I have set up two time blocks, "local meeting time" and "my time". The local meeting time is the time when the meeting takes place at the actual site. If we woul
d have a meeting in London, and this is my CRM running CET there would be a difference of one hour between the two where "my time" would be one hour ahead.

As you can see I also locked the datetime fields in my time so all editing happens in "local time".

Now on to JS madness.

Onload of the appointment form I fetch the starttime and endtime of the appointmen, if those aren't set I set them as new Date(), which I then make sure is on the hour of half hour. I also fetch the duration field, which is a tricky bastard I might add, and the custom timezone field.

The endtime is set as starttime plus duration if there are no values for start and endtime on the form.

Now things got a bit trickier, and here's why I don't like time zones. I use the same way that CRM uses the timezones for the script part so I set up a timezone setting array which looks something like this:
localSettings = [0, 10, 5, 0, 2, 3, 5, 0, 1, -60];

So, this is slightly greek but this is a settings array for UTC. The first element is timezone bias, which is 0 for UTC. To make it slightly worse it's negative for CET so you take the time i CET and add the bias to end up in UTC.
The second element is standard month, which is the month in which standard time is starting, no worries, well I might mention that January is 0 in JS. 
The third element is standard day, which could be a date but it isn't  since you change back to standard time on the fourth saturday after the fifth moon after midsommer, ish. That is which number of day this is, in this case the fifth time the day appears in a month.
The fourth element is standard day of week, which means which day we are lookin at, and since 0 is the first day of the week, it's a sunday.
The fifth element, standard hour, is what time of day you return the clock one hour. Which in this case means that in UTC you return the clock one hour at two in the morning.
 The following four elements are the same for DST and the final element is daylight bias, which again is negative.
I also fetch the users timezone settings from CRM to calculate UTC time.

With this info I calculate when DST starts and ends using the following code:
 var GetDst = function (inyear, month, day, dayofweek, hour,tzBias) {

    if (tzBias == 0)
        return new Date(2011, 1, 1, 12, 0, 0, 0);

    var returnDate = new Date(inyear, (month - 1), 1, hour);
    var testDate = new Date(inyear, (month - 1), 1, hour);
    var firstDayOfMonth = new Date(inyear, (month - 1), 1);
    var firstDay = firstDayOfMonth.getDay();

    if (firstDay != dayofweek) {
        if (dayofweek == 0)
            testDate.setDate(testDate.getDate() + (7 - firstDay));
        else
            testDate.setDate(testDate.getDate() + (dayofweek - firstDay));
    }

    testDate.setDate(testDate.getDate() + ((day-1) * 7));
    if(testDate.getMonth() != (month - 1))
    {
        testDate.setDate(testDate.getDate() - 7);
    }
    return testDate;
}


Here the inputs are the year from the appointment, in the first case I want DST start time for the year the appointment is happening, month, day, dayofweek and tzbias are from the settings array

First I set a date of the year, DST start month, the first day of that month and the hour that stuff happens. I alse set up en exact copy of that date. 
I the proceed with finding out which day of the week the first day of the month is and compare that to the daylight saving start day and if the don't match I find when the first "change day" of the month happens (say first sunday).
After this I apply the number of weeks that should be added and check if we're still in the same month, if not, remove one week. SIMPLE

The time presented in "My time" is set up by UTC time plus timezone difference and DST difference so to get UTC time from the form I convert that time with the info about 
I then check if the time we get out of CRM is within DST and apply the changes to get to UTC time. 

This is done since I can never be sure that the computer's timezone setting is the same as CRM settings so I need to calculate the time in the form based on the CRM settings, yes that made me feel a bit sick too.

After I've calculated what the "my time" times are in UTC time I can then apply the timezone info on that data and I know when the meeting is taking place at the local office.

What I also do is set the submitmode to never if it's anything else than a create form. You will also need onchange scripts that sets the submitmodes to relevant values when you change an appointment.

The duration field isn't really propagated in a way you would think when you change it so that has to be made dirty with code to be sent, very annoying and a source of errors I might add.

I hope this makes some sence. It's a bit tricky and I had to work quite hard to get it right. What started out as a "Oh, we just take whatever time CRM is giving us and add an hour and we're home free" ended up in a couple of hundred lines of scripts and a little less plugin code to handle the saves.

So as my commanding officers said after an order or information speech. Any questions, too late, dismissed!

Seriously, if you would stumble upon this and have similar issues, give me a holler and I'll try to help. I'm thinking of talking about the backend code sometime later, maybe in another two months :)

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se

onsdag 24 juni 2015

Email Configuration button missing

No biggie, and quite documented but I'd like to point this out anyway. 

If you're not having any email configuration button in the settings area, then you have probably upgraded from CRM 2011 and the button isn't part of the sitemap. The solution is to add the following to you sitemap right aftert the <SubArea ID="nav_audit"...> tag,

<SubArea Id="nav_social" ResourceId="Social_SubArea_Title" DescriptionResourceId="Social_SubArea_Description" Icon="/_imgs/area/16_social.png" Url="/tools/social/social_area.aspx" AvailableOffline="false" />

It's pretty logic that you should add the social nav to get email configuration. Read more at
http://blogs.msdn.com/b/emeadcrmsupport/archive/2014/03/07/dynamics-crm-2013-email-configuration-tab-is-missing-from-the-settings.aspx

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se

söndag 7 juni 2015

IFD/ADFS setup stumbles when you think you're done

I've been fiddling with IFD and ADFS a bit more than what's healthy the past couple of years and I still face the "What the h*** is wrong now"-moments from time to time. Admittedly they're a bit more rare and not quite as severe as they uses to be, thank god.

One thing I've been running into quite frequently the latest times I've been messing with IFD is that when you think you're done and you try to log on for the first time you get a "I can't connect to the ADFS-server"-message in the browser.
On the ADFS server the logs say "the element 'serviceIdentityToken' was fount to have invalid data", on the CRM server you have a "Exception information:
    Exception type: EncryptedTokenDecryptionFailedException
    Exception message: ID4036: The key needed to decrypt the encrypted security token could not be resolved from the following security key identifier " and the CRM trace tell you something like "Microsoft.IdentityModel.Tokens.EncryptedTokenDecryptionFailedException: Microsoft Dynamics CRM has experienced an error" (and by the way, yes, that's the coherent part of that error).

You think, probably at least since I have, What happened, I just connected to all these servers.
So, what needs to be done? I can't tell you exactly what makes things work but if you update the metadata in the ADFS server manager, restart ADFS-service, restart the IIS you should be fine.

If that doesn't work, start browsing Chris Cognettas blog.

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se

söndag 31 maj 2015

Which version of ADFS are you running?

When you are setting up an Internet Facing CRM you need to use an STS service which often means ADFS. So which version are you running?

 Now, CRM of different verisons supports different versions of ADFS and the complete list is in your implementation guide. But how do you check for which version you are running. It turns out you don't. But have no fear, Jorge is your man here. This blog https://jorgequestforknowledge.wordpress.com/2014/02/23/gathering-architectural-details-from-your-adfs-infrastructure-adfs-version/#comment-17198 helped me to determine which verison that was installed on a system. Really, you have to look at how the GUI looks and see if there are files available.

This feels so 2015.

Have fun and thanks Jorge!

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se