Missing Competitor Field in Dynamics 365 Opportunity: Debugging the “Close as Lost” Button

In a recent project involving Dynamics 365 for Sales, I encountered a peculiar issue where the “Close as Lost” button on an opportunity didn’t show the Competitor field, which is usually expected when accessing the close dialog. There were no custom plugins or workflow assemblies that could have contributed to this problem. So, I embarked on a journey to debug the issue, ultimately uncovering some hidden configuration that caused this behavior.

Problem Description

The typical “Close as Lost” dialog in Dynamics 365 provides a field to specify the competitor who won the deal. However, in this particular instance, the competitor field was conspicuously absent (as shown in the image below. This was confusing because no customizations should have interfered with this standard feature.

Initial Investigations

After verifying that no unusual plugins or workflows were active, I decided to dive deeper into the client-side logic. I inspected the JavaScript behind the “Close as Lost” button to see if there were any parameters or conditions being set that would hide the competitor field. I came across a crucial piece of code that read a specific parameter controlling whether the competitor field would be displayed or not.

Here’s a snippet from the code:

if (ClientUtility.DataUtil.isNullOrUndefined(hideCompetitorFieldAttribute)) {
   hideCompetitorField = hideCompetitorFieldAttribute.getValue();
}

This code was checking if the competitor field should be hidden, and in this case, it was set to “true.”

The Hidden Culprit: An Extra Boolean Parameter

Finally, I stumbled upon the root cause when reviewing the ribbon configuration in Ribbon Workbench. As it turns out, the “Close as Lost” button had an extra Boolean parameter configured by a previous consultant. This Boolean parameter was set to true, effectively hiding the competitor field.

This additional Boolean parameter was unnecessary and had inadvertently caused the competitor field to be hidden during the closure process. Once this parameter was removed or set to false, the competitor field reappeared as expected in the “Close as Lost” dialog.

Resolution Steps

Using Ribbon Workbench, I found an unnecessary Boolean parameter that had been added to the button’s configuration.
Fix: I updated the configuration by either removing the Boolean parameter or setting its value to false.


Conclusion

This issue highlights the importance of double-checking even the smallest customizations in Dynamics 365. A minor oversight, like an extra Boolean parameter, can have significant effects on user experience and business processes. Always ensure that customizations are documented and reviewed, especially when they affect standard features like opportunity closure.

Have you ever encountered a similar issue with missing fields in Dynamics 365? Share your experience in the comments below!

Overcoming FetchXML Limitations: Calculating “Newer-Than-X-Days” with Microsoft Formula Columns

Introduction:

In the realm of Dynamics 365 and FetchXML queries, users often find themselves grappling with limitations, especially when it comes to date filtering. A date filter allowing the display of records “newer” than 90 days from today is currently unavailable. The absence of a straightforward “newer-than-x-days” option in FetchXML has led many to seek alternative solutions. Fortunately, developers like Jonas Rapp and Mark Carrington have proposed workarounds that involve leveraging outer-join functionalities. However, these methods may not simple to implement by end-users. 

The Calculated Field Conundrum: 

Attempting to create a calculated field to bridge the gap presents its own set of challenges. The error message, “This operation cannot be performed on values which are of different Date Time Behaviors,” is a roadblock, particularly when dealing with “Date Only” fields like “Estimated Close Date.” This limitation can be frustrating, hindering the seamless execution of desired date calculations.

A Glimmer of Hope: Microsoft Formula Columns: 

Enter Microsoft formula columns, this alternative method provides a way to perform date calculations without running into the Date Time Behavior mismatch issue. By employing a straightforward formula, users can effortlessly calculate the number of days in the future from the current date.

The Solution: DateDiff Function:

The key to overcoming the FetchXML limitations lies in the DateDiff function provided by Microsoft formula columns. By utilizing this function, users can perform date calculations efficiently. Let’s take a look at a simple example:

DateDiff(UTCToday(), 'Estimated Close Date')

This formula calculates the difference in days between the current date (UTCToday()) and the value in the “Estimated Close Date” field. By using this approach, you can easily filter records based on a “newer-than-x-days” criterion.

Conclusion: 

While FetchXML may present certain limitations, the innovative use of Microsoft formula columns provides a robust solution to the challenge of date filtering. By embracing these alternative methods, users can enhance their Dynamics 365 experience and gain more flexibility in managing and querying data. So, the next time you find yourself grappling with date-related queries, remember the power of Microsoft formula columns to streamline your processes and make your Dynamics 365 journey smoother.

Revolutionizing Business Efficiency: Migrating from a “Broken” On-Premise CRM to Dynamics 365 in the Cloud

Introduction

In the fast-paced world of modern business, digital transformation is the key to staying competitive and relevant. An essential component of this transformation involves transitioning from outdated on-premise systems, such as traditional Customer Relationship Management (CRM) software, to advanced cloud-based solutions. This article explores the journey of migrating from a “broken” CRM system hosted on-premise to Microsoft Dynamics 365 in the cloud. Additionally, it emphasizes the critical role of strategy mapping to ensure alignment between the end system and business processes.

The Legacy of On-Premise Systems

Once considered groundbreaking, legacy on-premise CRM systems often turn into operational hindrances for various reasons:

  1. Maintenance and Upgrades: Maintaining and upgrading legacy systems comes with high costs and disruptions.
  2. Scalability Constraints: Expanding or downsizing an on-premise system is complex and time-intensive, hindering agility.
  3. Accessibility Limitations: On-premise systems restrict accessibility, inhibiting remote work and real-time collaboration.
  4. Integration Complexities: Integrating outdated systems with newer applications can be intricate and costly.
  5. Security Vulnerabilities: Aging systems are susceptible to security breaches and data vulnerabilities.

Elevating Business with Dynamics 365 in the Cloud

Migration to Microsoft Dynamics 365 in the cloud presents a multitude of advantages to address these challenges:

  1. Operational Costs: Cloud-based solutions eliminate upfront hardware investments and ongoing maintenance costs.
  2. Scalability and Flexibility: Dynamics 365 on the cloud adapts resources to demand, ensuring optimal performance.
  3. Remote Accessibility: Cloud platforms enable anytime, anywhere access, fostering remote work and bolstering productivity.
  4. Streamlined Integration: Dynamics 365 offers seamless integration capabilities, connecting with other applications effortlessly.
  5. Robust Security: Microsoft’s cloud services provide robust security features, including encryption and regular updates.

Strategy Mapping: Fusing Technology with Business Ambitions

A triumphant migration to Dynamics 365 hinges on more than just technological decisions. Strategy mapping is pivotal to ensure that the new CRM system aligns with business processes and objectives.

  1. Process Assessment: Before migration, analyze existing processes to identify pain points and opportunities for enhancement.
  2. Clear Goals: Define the CRM migration’s objectives—be it enhancing customer engagement, optimizing sales procedures, or refining data analytics.
  3. Stakeholder Involvement: Engage key stakeholders from various departments to shape the new system according to their needs.
  4. Customized Adaptations: Tailor Dynamics 365 to match your organization’s workflows, accommodating specific processes.
  5. Change Management: Prepare employees for the transition through training and communication about the system’s benefits. Address concerns for smooth adoption.
  6. Continuous Refinement: Regularly evaluate the system’s performance against objectives post-migration. Make necessary adjustments to maintain alignment with business goals.

Conclusion

Transitioning from a “broken” on-premise CRM system to Microsoft Dynamics 365 in the cloud is a strategic move that can rejuvenate business operations. The benefits of cost-effectiveness, scalability, accessibility, integration, and security are compelling reasons to embrace this transformation. Yet, the key to success lies in meticulous strategy mapping that ensures Dynamics 365 aligns seamlessly with business processes and ambitions. By intertwining technological innovation with a comprehensive business strategy, organizations can realize a seamless and impactful digital transformation.

Dynamics 365 WebAPI Asynchronous Calls

Column Editing Text Editor Editing Dynamics 365 development is a constantly evolving field, with the JavaScript API being no exception. Exciting advancements have been made in this space, such as the introduction of WebApi’s method, which allows for basic CRUD operations (Link).

Gone are the days of worrying about including JSON files or calling jQuery libraries just to make simple API calls. The platform now provides straightforward methods for performing basic CRUD operations.

To illustrate this, here’s a simple example. I utilized the fantastic REST Builder tool to generate the code. This code searches for an email address and retrieves the full name of the corresponding contact record.

function FindContactRecord(email) {
    Xrm.WebApi.online.retrieveMultipleRecords("contact", "?$select=fullname&$filter=(emailaddress1 eq '" + email + "')&$top=1").then(
        function success(results) {
            var result = results.entities[0];
            var fullname = result["fullname"];
            ProcessData(fullname);
        },
        function (error) {
            console.log(error.message);
        }
    );
}

The above code works great for a single record, since the ProcessData function gets invoked in the success function of the WebAPI call. When we start to do bulk operations, the code starts executing asynchronously. Here is an example of calling the function multiple times. This code is only for illustrations purposes. If we were to query multiple contact records, our best practice is to batch multiple email addresses into a single WebAPI call.

function FindContactRecords(EmailArray) {
    var contactFullnames = new Array();
    for (var i = 0; i < EmailArray.length; i++) {
        contactFullnames.push(FindContactRecord(email[i].trim()));
    }
    return contactFullnames;
}

function FindContactRecord(email) {
    Xrm.WebApi.online.retrieveMultipleRecords("contact", "?$select=fullname&$filter=(emailaddress1 eq '" + email + "')&$top=1").then(
        function success(results) {
            var result = results.entities[0];
            var fullname = result["fullname"];
            return (fullname);
        },
        function (error) {
            console.log(error.message);
        }
    );
}

//Main function 
var contactFullnames = FindContactRecords(EmailArray);
ProcessData(contactFullnames);

The main issue we face with the above code is that the contactFullname array will not return elements since JavaScript engine is executing the WebAPI calls asynchronously for performance reasons.

So how do we fix this problem? By using async, await and then statements, JavaScript will return a promise which will not execute subsequent code until the async code completes processing. The changes in blue illustrates the required changes.

async function FindContactRecords(EmailArray){
    var contactFullnames = new Array();
    for (var i = 0; i < EmailArray.length; i++) {
       await contactFullnames.push(FindContactRecord(email[i].trim()));
    }
    return contactFullnames;
}

async function FindContactRecord(email) {
    await Xrm.WebApi.online.retrieveMultipleRecords("contact", "?$select=fullname&$filter=(emailaddress1 eq '" + email + "')&$top=1").then(
        function success(results) {
            var result = results.entities[0];
            var fullname = result["fullname"];
            return (fullname);
        },
        function (error) {
            console.log(error.message);
        }
    );
}

//Main function 
FindContactRecords(EmailArray).then(
    contactFullnames => ProcessData(contactFullnames)
);

There are many articles that discusses async/await, but how to applies to Dynamics 365 is not something well covered. I hope this  article helps.