Custom Page – Opportunity Close Solution Download

Initially, I was just going to explore some possibilities with Custom Pages, but decided to make a solution out of it instead. This way you can just download the solution and do the required modifications for your project (GIPHY API key), and you are good to go😁

I am really excited to hear your feedback after seeing/trying this out, and hopefully, I will be able to make it a lot better in next revisions of the Custom Page!

Go to my GitHub to get a hold of the solution. It’s a pretty “thin” solution that should be easy to configure and easy to delete if you no longer need it.

💾 DOWNLOAD 💾

I have also included a video displaying how you install and use the solution

Installation and setup

Because of ribbon changes, I was forced to create a managed solution for this. The only way it wouldn’t overwrite the current opportunity ribbon.

If you want to play around with the solution, download the unmanaged. Just make sure you don’t have any other ribbon customizations on Opportunity. If you do back it up first!!

For everyone else, you have to download the managed solution to be on the safe side.

On my Gihub page I have written what components get installed, so you can easily remove it all at a later point in time.

Custom Page – GIF for the win🏆

Most of you might know by now that I am a huge fan of GIF’s. I have written about GIF’s earlier when trying to motivate sales people, and I thought I would turn it up one notch!

Last post I wrote was about the opportunity close dialog. It was functional, but not exciting.

It is still 100% more feedback friendly than the Microsoft OOTB functionality, but it’s a little boring now that we have the option to be creative. This is why I thought I would add my earlier GIF post together with this to create a solution for instant response to the salespeople.

We all know that there is no better feeling than finding the correct GIF! 😂

So let’s start off by including a flow that I have written earlier with the Custom Page Confirmation dialog to make it all a little better than the thumbs up that my last post was showcasing.

RUN Instant Flow when completing sales

In the last post we covered the Power Automate action when the confirmation button was pressed. Now we reopen it to add a few more steps. The steps are identical to the steps I have linked to above in the article that coveres GIF connector.

First let’s query for a WIN gif.

Go to Giphy and setup a developer account for free. I have written about it in the GIF post that I have on top here. Just replace the api_key value in the string:
https://api.giphy.com/v1/gifs/random?api_key=**********&tag=win

Parse the return of this query (SORRY.. This is a bit long!)

{
    "type": "object",
    "properties": {
        "data": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                },
                "url": {
                    "type": "string"
                },
                "slug": {
                    "type": "string"
                },
                "bitly_gif_url": {
                    "type": "string"
                },
                "bitly_url": {
                    "type": "string"
                },
                "embed_url": {
                    "type": "string"
                },
                "username": {
                    "type": "string"
                },
                "source": {
                    "type": "string"
                },
                "title": {
                    "type": "string"
                },
                "rating": {
                    "type": "string"
                },
                "content_url": {
                    "type": "string"
                },
                "source_tld": {
                    "type": "string"
                },
                "source_post_url": {
                    "type": "string"
                },
                "is_sticker": {
                    "type": "integer"
                },
                "import_datetime": {
                    "type": "string"
                },
                "trending_datetime": {
                    "type": "string"
                },
                "images": {
                    "type": "object",
                    "properties": {
                        "downsized_large": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_height_small_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "original": {
                            "type": "object",
                            "properties": {
                                "frames": {
                                    "type": "string"
                                },
                                "hash": {
                                    "type": "string"
                                },
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_height_downsampled": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "downsized_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_height_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "downsized_medium": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "downsized": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "preview_webp": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "original_mp4": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_height_small": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_height": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "downsized_small": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "preview": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_width_downsampled": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_width_small_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_width_small": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "original_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_width_still": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "looping": {
                            "type": "object",
                            "properties": {
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                }
                            }
                        },
                        "fixed_width": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "mp4": {
                                    "type": "string"
                                },
                                "mp4_size": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "webp": {
                                    "type": "string"
                                },
                                "webp_size": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "preview_gif": {
                            "type": "object",
                            "properties": {
                                "height": {
                                    "type": "string"
                                },
                                "size": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                }
                            }
                        },
                        "480w_still": {
                            "type": "object",
                            "properties": {
                                "url": {
                                    "type": "string"
                                },
                                "width": {
                                    "type": "string"
                                },
                                "height": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                },
                "user": {
                    "type": "object",
                    "properties": {
                        "avatar_url": {
                            "type": "string"
                        },
                        "banner_image": {
                            "type": "string"
                        },
                        "banner_url": {
                            "type": "string"
                        },
                        "profile_url": {
                            "type": "string"
                        },
                        "username": {
                            "type": "string"
                        },
                        "display_name": {
                            "type": "string"
                        },
                        "description": {
                            "type": "string"
                        },
                        "is_verified": {
                            "type": "boolean"
                        },
                        "website_url": {
                            "type": "string"
                        },
                        "instagram_url": {
                            "type": "string"
                        }
                    }
                }
            }
        },
        "meta": {
            "type": "object",
            "properties": {
                "msg": {
                    "type": "string"
                },
                "status": {
                    "type": "integer"
                },
                "response_id": {
                    "type": "string"
                }
            }
        }
    }
}

At the end we have to respond to the app with the GIF URL that we want to use. There are many variables that have the same name “URL”, so make sure you find the one that I am using below.

Back to the Custom Page

The “Confirm Win” button now has to do a few more things, so here is the added code.

//Patch the Oppty fields
Patch(
    Opportunities,
    LookUp(
        Opportunities,
        Opportunity = GUID(VarOppportunity.opportunityid)
    ),
    {
        'Actual Close Date': EstClosingDate.Value,
        'Actual Revenue': Int(EstimatedRevenue.Value)
    }
);

//Update Opportunity Close entity and retreive GIF
Set(varFlow, CloseOpptyPostTeams.Run(VarOppportunity.Opportunity));
Set(varGIF, varFlow.urlgif);

//Hide input boxes and show confirmation
Set(
    varConfirmdetails,
    false
);
Set(
    varCongratulations,
    true
);

Mostly the only difference here is adding 2 variables. 1 that is the actual response from Power Automate (all of the fields), and the other one is setting a variable “varGIF” for use in our new image component that we add on the sales confirmation box.

Let’s check out that GIF!

Add an image to the Congratulations group, and reference the varGIF

Final Product 💘

When the sales person now closes the dialog, they get a 100% more awesome experience than the OOTB version!!! 🏆👍🎉

Custom Page – Close Dialog Logic

In the last post, we created a new close dialog, but we didn’t add any logic to the buttons.

Logic – Fields and Buttons

The most important parameter we send in via JavaScript last time was the GUID of the record that we are going to work with.

The first thing we do is add an onload to the app and perform a lookup as the very first step. This will give us all of the data for that given Opportunity that we can use within the Power App. We store the whole record in a variable “varOpportunity”.

A little clever step here is actually the “First(Opportunities)”. For testing purposes, this will open up the first Opportunity in the DB if you open the app without the GUID from Dynamics, and from here you can test the app make.powerapps.com studio without having to pass a parameter to the Custom Page 👍

ONLOAD

Set(VarOppportunity,
If(IsBlank(Param("recordId")),
First(Opportunities),
LookUp(Opportunities, Opportunity = GUID(Param("recordId"))))
)

Fields

Fields can now be added via the “varOpportunity” that contains all of the data to the first opportunity in the system.

BUTTONS

The cancel button only has “back()” as a function to close out the dialog, but the “Confirm WIN” has a patch statement for Opportunity.

//Patch the Opportunity fields
Patch(
    Opportunities,
    LookUp(
        Opportunities,
        Opportunity = GUID(VarOppportunity.opportunityid)
    ),
    {
        'Actual Close Date': EstClosingDate.Value,
        'Actual Revenue': Int(EstimatedRevenue.Value)
    }
);
//Hide input boxes and show confirmation
Set(
    varConfirmdetails,
    false
);
Set(
    varCongratulations,
    true
);

HIDE/SHOW

Because of some challenges I met with multiple screens, I had to use a single Screen with hide/show logic. Therefore I added all the fields to Groups and will hide Show based on groups.

The Congratulations group looks like this.

Closing the Opportunity Challenge

If this were a custom entity we could close the opportunity by setting the Status and Status Reason values. Unfortunately, the Opportunity has a function for closing the Opportunity that will create a Case close dialog. In order for this to work, we have to call a custom service for closing the Case. This does get a bit tricky.

We now have to call an action from Power Automate to close the opportunity as WON. At the moment of writing the blog, the process of calling the Microsoft action in Power Automate wasn’t working, so I created my own action. I will show you how, and honestly maybe even recommend doing it this way for now. It works all of the time and uses the technology that has been working in CRM since 4.0.

Custom Action

Actions work with the same logic as a Workflow, but they can be fired at any time from anywhere. They can receive inputs, and generate outputs. A workflow will only trigger from CRUD events, and work within the context of the record triggering the actual workflow. They are in many ways an underrated function in Dynamics / Dataverse.

It’s a pretty simple step updating the status of the opportunity to “won”, and by doing it this way the system will automatically do the correct calls in the API for Opportunity Close.

This is all you need for the action. After activation, we can go back to the custom page and create a instant flow (Power Automate).

In the Custom Page we now add a line to our “Confirm WIN” button. (Yes, I know we probably should add some logic for success/fail, but that will be a part of the final solution on Github).

//Patch the Opportunity fields
Patch(
    Opportunities,
    LookUp(
        Opportunities,
        Opportunity = GUID(VarOppportunity.opportunityid)
    ),
    {
        'Actual Close Date': EstClosingDate.Value,
        'Actual Revenue': Int(EstimatedRevenue.Value)
    }
);
//Hide input boxes and show confirmation
Set(
    varConfirmdetails,
    false
);
Set(
    varCongratulations,
    true
);
//Update Opportunity Close entity
CloseOpptyPostTeams.Run(VarOppportunity.Opportunity);

WINNER WINNER 🏆🥇

You should now be able to close the opportunity as won via a custom page. Just remember to publish the custom page AND publish the app again. If not it will now show. Do remember to give it a few moments before refreshing after a change.

Business Unit Name Change

Something small, but yet useful. Thank you Tanguy for the tip🤗

As you probably figured out the Default Business unit name is something you can’t change OOTB after you have created a CRM/Dataverse environment.

If you are not careful when creating a new environment the org name will be set for you, and that is also when the Business Unit Name is set.

This can result in the following main Business Unit, and it just doesn’t make any sense. Also you can’t change it because Parent Business needs to be entered. Problem is that there is no parent to the parent in this case 🤣

XrmToolBox – Bulk Data Update to the rescue😎

Open the view for active Business Units and find your result.

⛔NB!! Make sure your search only returns one business unit

Next thing you do is set a fixed value, add attribute to the bulk job, and the update the record.

Power Apps Pay As you GO!!💸

When Microsoft introduced Azure for the Microsoft public, it was a new way of thinking. We were suddenly paying for what we needed and when we needed it. Amazon had been there for a long while, but for Microsoft customers this was a new way of thinking. After a skeptical start, this model has really become somewhat of a system standard.

As of today Power Platform will be available on Azure subscription! It is being introduced as a “Pay as you go” model. It is important that you don’t mistake this for the same as Azure. In Azure you actually only pay for the compute time used (in most cases), but here you will pay for a license once you use an application.

WOW THIS IS SOOOO COOL … Well, is it really?

Let’s just think about the following first. Just a few weeks ago Microsoft dropped the prices to half of what they used to cost. They are now only 5$ and 20$ for the different plans. When you think about the value you get from a Dataverse OOTB that is a BARGAIN already.

So why am I not overly excited about the “Pay as you Go” PAYGO model? Well, I don’t really see the big impact yet. Most of my customers are on the CSP agreement, and can flex as much as they feel for. Planning ahead for apps is also hard, and is counter intuitive for innovation. By releasing a plan as PAYGO, you essentially need to plan financially for all users that might use an app, while you silently hope that not all users actually use the app that month. For every user that didn’t use the app, you save some money.

I am sure that the plan makes sense for many scenarios, but I just don’t really see them yet. The good thing is that “limitations/possibilities” for the new plan will be monitored closely in the beginning to find the correct levels for all types of use cases. Remember to voice your opinion if you see some great opportunity. Microsoft will be listening😀

Pricing comparison

Standard Pricing App and User Plan

Standard Pricing Storage

PAYGO Pricing app

https://docs.microsoft.com/en-us/power-platform/admin/powerapps-flow-licensing-faq#add-ons

PAYGO Storage

https://docs.microsoft.com/en-us/power-platform/admin/powerapps-flow-licensing-faq#add-ons

Personal Thoughts

The only thing that we know for sure is that licensing will always be a situation where we as consumers want changes. We want more more more, and want to pay less less less. Microsoft will continuously find new license models to adapt to our wishes while finding ways to keep profits. Don’t get me wrong. I am all about Microsoft being able to charge what they want. After all it’s a great product!!! I’m just saying that you need to look behind the shining stuff before you automatically assume that everything new is automatically better.

What you need to do as a customer is get help to assess assess your licensing situation. Not only is licensing complex from a rules perspective, but the applications can be modified to adapt to licensing changes. I am not saying PAYGO is bad, but I’m not jumping on the PAYGO train quite yet. Most of my customers are CSP customers and have a lot of freedom with licensing (Up and Down). Just going to see what happens first 😁

I might also have misunderstood quite a lot in regards to the benefits received from this model, and if so I would love feedback to learn new ways of thinking!👌

Dynamics 365 App for Outlook button

Tiny blog for a tiny button 😂 This is only relevant if you have users that work in Outlook Web. Every now and then I do encounter a few Apple users that prefer the Outlook Web, even though it works well with Outlook for Mac.

Outlook client

If you use the Outlook client you know the button from the ribbon. Click the button do load the client.

Outlook Web.

OOTB the Dynamics client is hidden once it is deployed for the user. Only way to find it is to open the actual email and choose the ellipsis

Great thing is that we can change the order of the buttons:)

Solution

Open the Outlook Web settings and choose “View All Outlook Settings”.

Find the Dynamics 365 button in the Customize Actions and click save.

You now have the button easily accessible 🤗

Dynamics 365 Teams Document Locations – Where art thou?🤷‍♂️

Even though we all wish it wasn’t so, Document Locations still rule the integrations between Dynamics 365 and SharePoint. I’m not saying that I have a better idea what would be a smarter way of solving it, but it all seems a bit “2011” ish.

Last week I encountered a problem with the Document Locations for Teams, and I was surprised when I couldn’t find them in the Document Locations at first. The list only contained the SharePoint sites that the standard SharePoint connector uses.

In this list I was missing all of the Teams locations. Turns out that the view is only showing Active SHAREPOINT locations.. hehe

All you have to do is add the “MS TEAMS” to the search, and you should see all of the document locations for that also

Dynamics 365 + Teams integration error

Recently ran into a problem with the Teams integration OOTB, where the integration continuously threw an error after connecting the Dynamics record to the Teams.

This is a pretty Vanilla environment, so I couldn’t quite figure out what was wrong. I obviously could see that the URL was wrong, but I didn’t understand WHY it was wrong.

https://**.sharepoint.com/sites/SuperCards/Shared%20Documents/General <- Nothing really wrong with this URL at first site.

After a lot of painful digging I finally found the issue. Someone had decided to install SharePoint in Norwegian when they first setup the tenant!!! hehe. This meant that the URL the SharePoint URL was wrong.

Wrong URL ⛔

Correct URL ✅

Solution?

Microsoft Support didn’t see a fix in the near future for language support, so I guess it’s time for a small work around 🙂 Not really exciting fix, but you need to create a Workflow or Power Automate on create to change the name of the document location to your local language.

Why a workflow you may ask? When is the last time a workflow failed you I answer 😎

Dynamics / Power Apps + Office App Launcher

Launching a new app or launching a new CRM system always leaves the users with the same question. Where do I find the application? At first I didn’t really understand the question, because I thought it was natural to bookmark the URL to your application ie https://www.company.crm4.dynamics.com/***** etc.

Eventually I realized that most users are actually using the waffle menu in office 365 when navigating to applications that they don’t use continuously.

They were expecting to see the application in the list when you clicked the waffle menu, because this would save them time.

Luckily this is not a problem 😊 Open the all apps, and locate the app you are looking for

And just like that you now have a quick navigation to your CRM or Power App application in the Microsoft 365 app launcher👊

Dynamics 365 form or advanced find images error

This post is almost not relevant any more due to the fact that we all will be pushed into the make.powerapps experience in “the fullness of time”, but I will still be using the classic viewer for the foreseeable future 🙂

From time to the images can get distorted due to unknown reasons. This is not a big deal as the buttons still work, but can be annoying for the users. You might have seen this in the 2 places:

Advanced find:

Advanced Find Menu Error – Microsoft Dynamics CRM Forum Community Forum

Form configuration:

Fix

There seem to be several ways to solve this issue, but not all seem to work the same. I have seen some options describing a cache clear in the console of the browser, but for some reason none of those worked for me. Lately I have had to open the developer tools and delete a bit more manual.

Click F12 in the browser where you see the error

Open the Application tab on the dev tools

Clear or delete anything within the storage related to dynamics.

Try to restart the browser, and hopefully that should do the trick:)