January 7, 2019

Open CRM Report with Javascript

To run a specific report against selected items with Javascript, use the code below.
Create a button in a ribbon and pass the following parameters: SelectedControl, SelectedControlItemIds, SelectedEntityTypeName

Beware that this is totally unsupported way of doing a this.

function openReport(gridControl, recordIds, selectedEntityType)
{
    var reportId = "{reportGuid}";
    var filterTable = true;
    var reportType = 1;
    var fileName = "REPORT NAME.rdl"
    
    Mscrm.RunReportFromGrid.$j(gridControl, recordIds, filterTable, reportId, reportType, fileName);
}

January 8, 2018

Get CRM Reports statistics

If you want to see how may times a certain report has been executed, run this query on the ReportServer database. First replace the [CRM DATABASE NAME_MSCRM] with the correct CRM database name. If the CRM database is not on the same server, then you can't simply joint the tables in one query, but still you can get the report Id list from the Reps (Name column).

;WITH Reps AS
(
SELECT c.Name,
       c.[Path],
       COUNT(*) AS TimesRun,
       MAX(l.TimeStart) AS [LastRun]
FROM [ReportServer].[dbo].[ExecutionLog](NOLOCK) AS l
INNER JOIN [ReportServer].[dbo].[Catalog](NOLOCK) AS c ON l.ReportID = C.ItemID
WHERE c.Type = 2 -- Only show reports 1=folder, 2=Report, 3=Resource, 4=Linked Report, 5=Data Source
GROUP BY l.ReportId,
         c.Name,
         c.[Path]
)
SELECT CRM.Name, CRM.ReportId, Reps.TimesRun, Reps.LastRun, Reps.Path FROM Reps JOIN [CRM DATABASE NAME_MSCRM]..Report CRM ON Reps.Name = '{' + CAST(CRM.reportid AS CHAR(36)) + '}'


Be aware that the statistic is there for a limited time. Execute this to get the minimum date: SELECT MIN(ExecutionLog.TimeStart) FROM [ReportServer].[dbo].ExecutionLog

August 17, 2017

Executing SQL queries in CRM database on behalf of another user

If you are using filtered views in SSMS it is not possible to see what data will be returned for other users. But there is a way!

The GUID of the user is stored in the SQL CONTEXT_INFO. All you have to do is to run this piece of code:

DECLARE @binUserGuid VARBINARY(128)
DECLARE @userGuid UNIQUEIDENTIFIER
SET @userGuid = 'GUID of the user you want to test'
SET @binUserGuid = CAST(@userGuid AS VARBINARY(128))

SET CONTEXT_INFO @binUserGuid

From now on you will be able to query the data as if you would be logged in as another user.

To clear the context info simply execute

SET CONTEXT_INFO 0x

I hope this helps those who are testing SQL queries for the CRM reports.

July 17, 2017

Have some respect!

Over the past five years I have been more and more involved as a speaker at the conferences. I had my first session at the meeting of Slovenian Developer Group (sloDUG), then soon my first session in English language happened at SQLSaturday Slovenia.

For those who don't know SQLSaturday events, these are FREE 1-day SQL training events made possible first by the organizers, who devote their free time to organize the whole event from scratch. Then there are sponsors who provide necessary funds for the event (renting the venue,  coffee and lunch for attendees, evening meal for speakers). And the last, but not least, there are the speakers who arrive from all over the globe, live on their own expenses and speak for free. Yes that's right! Free of charge. Even some sponsors are doing this just because they like the idea. All just for you knowledge thirsty enthusiasts. It's called giving back to the community. I too was once at the other end of the room listening to the speakers and admiring their enthusiasm.

Yes I do it for free, but I get paid with more than money, and I am sure that I am speaking on behalf of vast majority speakers at SQLSaturday. First there is this great feeling of spreading the knowledge when I deliver a good session and receive good feedback. Even better are the emails or tweets I receive after where people are commenting on the session or asking questions. But the best part of this whole thing are amazing friendships I have made with amazing people. We live in different countries and this is one of the ways we are using to meet again and hang together for a few days. Personally I always try to make this also into a short tourist trip, therefore I arrive at least a day early and leave at least a day later so I can explore the city and the country where I am speaking at.

Why am I writing this you ask? It all started with this tweet:


It happens and people tweet about it, no big deal. Some find it funny, some are feeling sorry for the speaker, and there are also some who take things like this personally.

Let's make it clear. Free event or not, speakers should prepare their sessions and themselves professionally. Go through the slides, the demos, see how much time you need and always think of what can you improve to make it better next time.

I'm not defending anyone here and I don't want to point any fingers, but then this tweet happened:

Wait what? Have some respect? How about you showing some respect? This "incompetent" speaker is one of the best professionals in his area of expertise and has taken his free time, paid for the trip and came to present to you for free so you could call him incompetent!

I just happened to be at this session. The speaker followed all the rules in the unwritten presenters rulebook. Two most important of them are: turn off all applications not needed for the session and prepare the scripts, do not type.

In the beginning everything went OK, but then scripts just failed to execute at one point. Then he started to type the commands 😐Of course this didn't work and at the end he decided to restart the computer. While restarting the computer he killed the time by explaining some things where demos were not need and had a short Q&A session. Basically he did everything he could do to make things better. Of course precious time was lost and he could not show everything he wanted to show in his session.

Boo-hoo, so what - it happens. Shit happens and it is called "The wrath of the demo gods". No matter how well you prepare yourself, there is always a chance that something will fail and eventually it will. The worst are online demos. The probability of connectivity problems in unimaginably high at the conferences. I myself have experienced "The wrath" twice. Both times there was something with SQL Management Studio. I restarted it and voila! Problem solved. In this case the restart of computer was needed, so it took a little longer to do so. Here the "incompetence" of the speaker is on display. Some would freeze and just wait in awkward silence for the computer to restart, some would try to kill the time with bad jokes 😬, but here the session actually continued.

I am sure that the author of the tweet above has seen better sessions but please don't judge people before you've walked a mile in their shoes.

So please have some respect for the speakers!

March 23, 2016

Error when opening an inactive entity

After migrating from CRM 2015 to CRM 2016 I experienced strange behavior at some inactive entities. Immediately after opening it I got an error:

The object cannot be updated because it is read-only

Wait what? I did not change any data! Why this error? Disabled all javascript, disabled all plugins, the error was still there.



We introduced business process flow some months ago in this entity. The old entities had no process and stage ID defined. CRM 2016 wants to fix this immediately when dealing with an entity that has no process or stage defined and sets the default values.

But there is a bug in CRM. The update is made on inactive entities as well. Thus the error.

The only solution is to update the values directly in the database: 

UPDATE ENTITY SET ProcessId = PROCESSGUID , StageId = STAGEGUID  WHERE (ProcessId is null or StageId IS NULL) AND StateCode = 1


March 5, 2016

Do not name the action parameter "Error"

If you create an output action parameter named "Error" and you assign a value to it using "Assign Value step" the action will be published normally, but when trying to execute it, you will get the message:

The following errors were encountered while processing the workflow tree:
'DynamicActivity': The private implementation of activity '1: DynamicActivity' has the following validation error:   Compiler error(s) encountered processing expression 

Just rename the parameter and the error will be gone.

March 3, 2016

CRM 2016 Update lookup field bug

I encountered a strange behavior after upgrading on premise CRM 2015 to 2016 version. 

In the account form after I set the value of the custom lookup field (lookup to custom Country entity) I got javascript error: 

SCRIPT5007: Unable to get property 'trim' of undefined or null reference

After some hours of debugging I removed all javascript code. Then I copied the example from SDK, the error was still there.

Investigating the setValue method I discovered, that something like "internalOnChange" was called. I suspect this method was called because there are some filtered lists in the page that depend on the value of this field.

Long story short, there are two ways to fix this:

Method 1:
Go to Settings-Administration-System settings. All the way down at the end of the page enable "Use legacy form rendering".

This will definitely slow down form loading.

Method 2:
The code deep down in the out-of-the-box CRM javascript libraries the script wants to parse the attribute type, but it is not there. The value should be "lookup" and you should pass it in the object when calling setValue.

//Create the lookup value object
var lookupReference = [];
lookupReference[0] = {};
lookupReference[0].id = "{a0c68c89-9752-e311-93f9-00155d78043f}";
lookupReference[0].entityType = "new_entityname";
lookupReference[0].name = "Display name";

//Magic happens here
lookupReference[0].type = "lookup";
    
//Update the lookup field   
Xrm.Page.getAttribute("new_entitynameid").setValue(lookupReference);

February 15, 2010

Sitemap error because of missing LCID

I accidentally discovered the following “feature”: All LCID Titles in SubArea must have matching LCID in Group Titles.

Add a group in sitemap, like this:

<Group Id="Demo">
  <Titles>
    <Title LCID="1033" Title="Demo"/>
  </Titles>
  <SubArea Id="DemoPage" Icon="iconUrl" Url="
someUrl">
    <Titles>
      <Title LCID="1033" Title="Demo Page"/>
      <Title LCID="1060" Title="Demo Stran"/>
    </Titles>
  </SubArea>    
</Group>

The sitemap will upload normally and pass all validation, but after switching to language 1060, the error will be shown. The cause is missing LCID 1060 in Group Titles.

The correct XML for this example is:

<Group Id="Demo">
  <Titles>
    <Title LCID="1033" Title="Demo"/>
    <Title LCID="1060" Title="Demo"/>
  </Titles>
  <SubArea Id="DemoPage" Icon="iconUrl" Url="someUrl">
    <Titles>
      <Title LCID="1033" Title="Demo Page"/>
      <Title LCID="1060" Title="Demo Stran"/>
    </Titles>
  </SubArea>    
</Group>

February 5, 2010

Problem fixed: Installing Rollup 8 for Data Migration Manager

I installed a fresh instance of Microsoft Dynamics CRM Data Migration Manager. Then I wanted to apply Rollup 8 to it.

Error: DMClient KB971782 is not found

Resolution: First install  Rollup 7 update for DM Client (http://www.microsoft.com/downloads/details.aspx?FamilyID=A4893988-7804-4E23-AB58-740441CC696E&displaylang=en) and then you are able to install Rollup 8.

October 23, 2009

Error: The specified type was not recognized: name='usersettings'

Simple task: Try to retrieve user settings of a CRM user.

Simple solution:
RetrieveUserSettingsSystemUserRequest request = new RetrieveUserSettingsSystemUserRequest();
request.EntityId = systemUserId;
request.ColumnSet = new AllColumns();

RetrieveUserSettingsSystemUserResponse response= (RetrieveUserSettingsSystemUserResponse)crmService.Execute(request);

NOT!

The specified type was not recognized: name='usersettings', namespace='http://schemas.microsoft.com/crm/2007/WebServices', at <BusinessEntity xmlns='http://schemas.microsoft.com/crm/2006/WebServices'>.

 

Real solution:

Add the following line: request.ReturnDynamicEntities = true;

September 9, 2009

Microsoft CRM 4.0 – Applications certification and SQL 2008

On Microsoft learning page (http://www.microsoft.com/learning/en/us/certification/dynamics-crm-professional.aspx#tab2) it is said that exam 70-431 - Microsoft SQL Server 2005 – Implementation and Maintenance is elective for Microsoft Certified Business Management Solutions Professional – Applications for Microsoft Dynamics CRM 4.0 certification.

There is nothing said about SQL 2008 exams on this page.

So I sent an email to Microsoft and here is their response:

Dear Dejan,
Thank you for your email.
We are glad to inform you that the Exam 70-432: TS: Microsoft SQL Server 2008, Implementation and Maintenance can be counted as an elective for Microsoft Certified Business Management Solutions Professional – Applications for Microsoft Dynamics CRM 4.0 certification.
As that new exam has been released recently, it has not been added to the certification path yet.
Please note that the update of the Certification Tree has been planned for the end of September 2009.
Then, the exam 70-431 will be visible in the certification path for
Applications for Microsoft Dynamics CRM4.0 certification on the
Microsoft Learning website, as that certification will be uploaded to the Transcripts of all customers who have completed the certification requirements by passing exam 70-432.
We hope that provided information is of assistance.
If you have any further questions please get in touch with us and we will be happy to assist you in any way we can.

September 7, 2009

Link to custom entity – use of etn instead of etc argument

If you want to create a direct link to an out-of-a-box entity it is a very simple task: Right-click on one instance (let’s say account) and select “Copy Shortcut”.

image

Then just just change the GUID in the URL to open desired account. For the Account this URL looks like this: http://SERVER:PORT/OrganizationName/sfa/accts/edit.aspx?id={GUID}

Custom entities

All custom entities have the same URL structure: http://SERVER:PORT/OrganizationName/userdefined/edit.aspx?id={GUID}&etc=EntityTypeCode

Notice the entity type code? This code is created when creating the entity. But for some reason Entity Type Code can be (in most cases) different on every CRM instance. This means that if you are creating a plugin that needs to create a link to the entity, you will have to query metadata to get the code or enter that code in configuration part.

But there is a better way:

Instead of “etc” argument you can use “etn” (Entity Name) in the query string. Then use the logical name of the entity. Logical name is the same on all instances.

http://SERVER:PORT/OrganizationName/userdefined/edit.aspx?id={GUID}&etn=EntityName

http://testsrv:5555/Contoso/userdefined/edit.aspx?id={08B91EBF-525A-DE11-94A6-002354F5ABA2}&etn=new_testentity

August 28, 2009

Get a list of searchable attributes from database

(The final solution is at the bottom)

Today my colleague wanted to check which attributes were marked as searchable in a specific entity.

One way to do this is to open each attribute in MSCRM and check if it is marked as searchable. A long way if you have an entity with 220 attributes.

So I went to the database and checked the MetadataSchema.Attribute table. There is a column named DisplayMask. SDK says that display masks are following (ValidForAdvancedFind = Searchable):

Name Value Description
None 1 Specifies no restrictions.
ObjectTypeCode 4 Specifies that the attribute is an entity type code. The name of the entity is displayed.
PrimaryName 2 Specifies to display the attribute as the primary name.
RequiredForForm 0x40 Specifies that the attribute must be shown on a form.
RequiredForGrid 0x80 Specifies that the attribute can be shown in a grid view.
ValidForAdvancedFind 8 Specifies that the attribute can be shown on the Advanced Find form. This attribute is shown as Searchable in the UI.
ValidForForm 0x10 Specifies that the attribute can be shown on a form.
ValidForGrid 0x20 Specifies that the attribute can be shown in a grid view.

But the value in the database is 469762048 (this attribute is searchable)! Bitwise comparison fails: 469762048 & 8 = 0 WTF!!!

After some googling and 30 minutes of using reflector if found that the former values are meant to be used only when working with metadata web service, like in this example: http://msdn.microsoft.com/en-us/library/cc151072.aspx

Thank you Reflector!

Microsoft.Crm.Platform.Sdk.dll (found it in GAC) contains DisplayMasks enumeration in Microsoft.Crm.Metadata namespace.

This is what the reflector says:

public enum DisplayMasks
{
ActivityPointerRegardingName = 0x80,
ActivityRegardingName = 1,
BitMask = 0x1000,
DynamicFormatCode = 0x4000,
None = 0,
ObjectTypeCode = 0x400,
PrimaryName = 0x100,
QueueItemPriority = 0x10,
QueueItemSender = 0x20,
QueueItemState = 4,
QueueItemStatus = 8,
QueueItemTitle = 2,
QueueItemToRecipients = 0x40,
RequiredForForm = 0x20000000,
RequiredForGrid = 0x40000000,
ResourcePointerName = 0x200,
ReturnedTypeCode = 0x800,
StateCode = 0x2000,
ValidForAdvancedFind = 0x4000000,
ValidForForm = 0x8000000,
ValidForGrid = 0x10000000
}


This means that the ValidForAdvancedFind has value of 0x4000000! I tested it with the previous example and it works! Hooray!


Solution

So the final answer to the problem of this post is:

select * from MetadataSchema.Attribute
where DisplayMask & 0x4000000 > 0 and EntityId = @entityId

Of course you can use all other values as well.

What I haven’t tested yet is whether you can change this mask value directly in the database - definitely not supported by Microsoft :)

July 16, 2009

Show custom notifications on Microsoft Dynamics CRM - upgrade

Marco Amoedo created cool javascript function that displays custom warning messages in Microsoft Dynamics CRM 4.0. http://marcoamoedo.com/blog/show-custom-warning-messages-on-microsoft-dynamics-crm/

I liked the idea so much, that I took the script and upgraded pimped it.

What it does?

The function simply adds a notification on top of the form, like in the picture below.

image

You can add multiple notifications with different icons.

image

Update or delete previously added notifications or add new ones. This image shows (after Name onChange event) changed error text, updated third notification to info style and a new warning. Compare it with previous picture.

image

How to use it?

Step 1: Insert this code into form OnLoad event.

/*============== addNotification function =============






Adds a message on the top of the entity form using



the same visual style as Microsoft CRM.



If function is called more than once,



the messages are added at the bottom of the list



If the message with same ID already exists,



then the message is updated and not added to a list.



It also supports deleting of previously added notifications.






Put this function in the OnLoad event,



so you can use it in all form and field events.






Parameters:



message - message to be shown to the user



messageType - Type of the message: 1 - critical, 2 - info, 3 - warning



notificationId - ID of the notification (needed for updating purposes)



    



Created by:



Marco Amoedo (http://marcoamoedo.com) - the idea and initial function



Dejan Dular (http://a-crm.blogspot.com) - upgraded functionality (different icons, updating and deleting notifications)



    



=======================================================*/



 



addNotification = function(message, messageType, notificationId)



{



    var notificationsArea = document.getElementById('Notifications');



    



    if (notificationsArea == null)



    {



        //Don't display message when deleting a notification.



        if (messageType != 4)



        {



            //Sorry. Notifications are not possible on this form.



            //Display a message box instead of notification.



            alert(message);



        }



        return;



    }



    



    var notificationDIV = document.getElementById('NotificationDiv_' + notificationId);



    



    //Delete the notification



    if (messageType == 4)



    {



        if (notificationDIV != null)



        {



            //Remove the notification



            notificationsArea.removeChild(notificationDIV);



        }



        return;



    }



    



    //Get the notification image. The default is info image.



    var notificationImage;



    switch (messageType)



    {



        case 1:



            notificationImage = '/_imgs/error/notif_icn_crit16.png';



            break;



        case 2:



            notificationImage = '/_imgs/error/notif_icn_info16.png';



            break;    



        case 3:



            notificationImage = '/_imgs/error/notif_icn_warn16.png';



            break;    



        default:



            notificationImage = '/_imgs/error/notif_icn_info16.png';



    }



 



    //Create notification



    var notificationTable = '<TABLE cellSpacing="0" cellPadding="0"><TBODY><TR><TD vAlign="top"><IMG class="ms-crm-Lookup-Item" alt="" src="' + notificationImage + '" /></TD><TD><SPAN>' + message +'</SPAN></TD></TR></TBODY></TABLE>';



    



    //Check if the notification with same ID already exists



    if (notificationDIV == null)



    {    



        //Create a new notification



        var notificationHTML = '<DIV class="Notification" ID="NotificationDiv_' + notificationId + '">' + notificationTable + '</DIV>';



        notificationsArea.innerHTML += notificationHTML;



        notificationsArea.style.display = 'block';



    }



    else



    {



        //Update the notification.



        notificationDIV.innerHTML = notificationTable;



    }



}



/*============= END addNotification function ===========*/




 



Step 2: Run the following function when there is a need for a notification.




addNotification(string message, int messageType, string notificationId)







Parameters:




  • message – Notification message displayed


  • messageType – Type of the notification (the icon) (1 – critical, 2 – info, 3 – warning, 4 – delete the notification)


  • notificationId – Any string that identifies specific notification. If a function is called later with existing notificationId, the notification will be updated. Otherwise a new notification is added at the bottom of the list. If you want to delete a specific notification, just set messageType = 4 and use correct notificationId.



Thanks again to Marco Amoedo.



 



Originally published by Dejan Dular

March 27, 2009

Reindex whole database

EXEC sp_MSforeachtable @command1="print '?' DBCC DBREINDEX ('?', ' ', 80)"
GO

July 2, 2008

Button that starts a workflow

My client wanted to have a button that starts a specific workflow on the form. So after analyzing some javascript I ended with this code:

var a = new Array(crmFormSubmit.crmFormSubmitId.value);
var sIds = crmFormSubmit.crmFormSubmitId.value+";";
var sEntityTypeCode = "10004"; //Replace this with your entity type code
var sWorkflowId = "{BE7E47C0-2452-4F44-8CCA-425B83A7A8FB}"; //Replace this with your actual workflow ID
var iWindowPosX = 500; //Modal dialog position X
var iWindowPosY = 200; //Modal dialog position Y

var oResult = openStdDlg(prependOrgName("/_grid/cmds/dlg_runworkflow.aspx")+"?iObjType=" + CrmEncodeDecode.CrmUrlEncode(sEntityTypeCode) + "&iTotal=" +
CrmEncodeDecode.CrmUrlEncode(a.length) + "&wfId=" + CrmEncodeDecode.CrmUrlEncode(sWorkflowId)+ "&sIds=" + CrmEncodeDecode.CrmUrlEncode(sIds) , a, iWindowPosX, iWindowPosY);


Of course this is not supported by Microsoft. You use it at your own risk.

June 9, 2008

Generic SQL error

WOW! I'm sooooo happy when I receive such deterministic errors.

After saving an account, this message popped up. And you guessed it - it appeared just SOMETIMES!

After a little of SQL profiling i found the reason:
I extended the account's address1_line1 field from 50 to 100 characters. After saving the account, CRM automatically creates/updates also the customeraddress entity.

Solution:
Extend the customeraddress entity, extend attribute line1, match the length to length of the account address line.

May 30, 2008

Cool SQL query that deletes the duplicates

Found this on Sql Server Central:

CREATE TABLE #new(ID INT NULL, KeyValue VARCHAR(2))
INSERT INTO #new(ID, KeyValue) VALUES (1,'aa')
INSERT INTO #new(ID, KeyValue) VALUES (2,'bb')
INSERT INTO #new(ID, KeyValue) VALUES (1,'aa')
INSERT INTO #new(ID, KeyValue) VALUES (1,'aa')


SELECT * FROM #new;


Now let's delete the duplicates. The semicolon at the beginning is recommended if you are not using it after every SQL sentence. This tells the parser to begin with new statement. Without it you will probably get a syntax error.

;WITH Numbered AS (SELECT RowNo=ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID), ID, KeyValue FROM #new) DELETE FROM Numbered where RowNo>1;

SELECT * FROM #new;


Cleanup...

DROP TABLE #new;

March 3, 2008

Snippet for CRM workflow activity parameter

If you are writing custom workflow activities for workflows in CRM 4.0 you will find this snippet handy.

Copy the code below into a new file and name it CRMParameter.snippet. Save the file into Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets folder. It works in VS2008 too.
The shortcut for the snippet is cwp.

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>CRM workflow property snippet</Title>
<Shortcut>cwp</Shortcut>
<Description>Code snippet for crm property </Description>
<Author>Dejan Dular</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>PropertyInputOutputType</ID>
<ToolTip>Input/output property</ToolTip>
<Default>CrmInput</Default>
</Literal>
<Literal>
<ID>PropertyDisplayName</ID>
<ToolTip>Property display name</ToolTip>
<Default>Display Name</Default>
</Literal>
<Literal>
<ID>PropertyName</ID>
<ToolTip>Property Name</ToolTip>
<Default>name</Default>
</Literal>
<Literal>
<ID>Type</ID>
<ToolTip>Property Type</ToolTip>
<Default>string</Default>
</Literal>
<Literal>
<ID>Class</ID>
<ToolTip>Class</ToolTip>
<Default>ClassName</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[#region $PropertyDisplayName$
[$PropertyInputOutputType$("$PropertyDisplayName$")]
public $Type$ $PropertyName$
{
get { return ($Type$)base.GetValue($PropertyName$Property); }
set { base.SetValue($PropertyName$Property, value); }
}
public static DependencyProperty $PropertyName$Property = DependencyProperty.Register("$PropertyName$", typeof($Type$), typeof($Class$));
#endregion
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

February 28, 2008

Error at decimal field display format (4.0)

When upgrading a German version of CRM 3.0 to version 4.0, a strange thing happened. All decimal, float or money fields behaved very strange.

Example: User enters a value 2. After clicking save button, the number changes to 200.00 and then immediately to 20.000.00.

Then I noticed that a decimal separator and a thousands separator were the same. Therefore I checked organization settings where everything looked fine.

The problem lied in user settings where both separators were set to dot (.)

SOLUTION:
Change must be made for each user. Set the decimal separator to comma (,).
Update the CRM database to affect all users: UPDATE UserSettingsBase SET DecimalSymbol = ','