How to Replicate Close Opportunity in Dynamics 365 using Alert.js

Adam Murchison, 25 July 2019

Dynamics 365 provides un-customizable functionality out of the box such as the Close Opportunity functionality. If you want to customize or recreate this functionality, it has been time-consuming to replicate the pop-up… until now. Introducing Alert.js 3.0, the stylish Dynamics 365 solution to help you create your aesthetically pleasing pop-ups.

What is Alert.js?

Alert.js is a product created by Magnetism Solutions to create nice, simplistic alerts that replicate the Dynamics 365 UI. We use this for many of our clients to quickly create tailored popups for their purposes. Documentation for Alert.js can be found here.

This blog is an insight on how to use Alert.js and to showcase its capabilities with minimal code.

Installing Alert.js

You can download a trial version of Alert.js here or you can purchase a full license for Alert.js here. After this you will have to import the managed solution into your Dynamics 365 instance and reference the Alert.js file in your form or ribbon.

Referencing Alert.js in the Form and Ribbon

You simply add Alert.js to the form as shown below, just like any other web resource:

image

To add it in the ribbon you simply navigate to the ribbon work bench and reference it as shown below:

image

This is how you reference it and use it in other JavaScript files. Ensure that it sits above the function your button will execute.

Code for the Close Opportunity popup

Below is the code for the pop up, validation and sending the request to close the opportunity. Use this as you want and modify to fit your needs!

// Called from ribbon button, passing through first primary record ID
function showCloseAsWon(opportunityId) {
    var alertButtons = [
        // Keep the alert open to allow for validation of required fields
        new Alert.Button("OK", function (responses) { closeOpportunityAsWon(responses, opportunityId); }, true, true),
        new Alert.Button("CANCEL")
    ];

    // Create the options for the alert
    var alertOptions = {
        title: "Close Opportunity",
        message: "Provide the following information about why this opportunity is being closed",
        height: 490,
        width: 450,
        id: "closeOpportunity",
        buttons: alertButtons
    };

    // Create the options for each of the fields. You can add in custom HTML to the label
    var statusOptions = {
        id: "statuscode",
        label: "Status Reason" + "<span style='color:red'>*</span>",
        value: 3, // Default value
        options: [{
            text: "Won",
            value: 3
        }]
    };

    var actualRevenueOptions = {
        id: "actualrevenue",
        label: "Actual Revenue" + "<span style='color:red'>*</span>",
        value: 0.0,
        type: "number"
    };

    // Specify additional HTML attributes to add to the field
    var actualRevenueExtra = {
        step: "0.01"
    };

    var closedDateOptions = {
        id: "closeddate",
        label: "Closed Date" + "<span style='color:red'>*</span>",
        value: new Date(), // Default to today
        type: "date"
    };

    var competitorOptions = {
        id: "competitor",
        label: "Competitor",
        entityTypes: ["competitor"]
    };

    var descriptionOptions = {
        id: "description",
        label: "Description"
    };

    // Create all the fields with their specified options and attributes
    var fields = [
        new Alert.OptionSet(statusOptions),
        new Alert.Input(actualRevenueOptions, actualRevenueExtra),
        new Alert.Input(closedDateOptions),
        new Alert.Lookup(competitorOptions),
        new Alert.MultiLine(descriptionOptions)
    ];

    new Alert(alertOptions).showPrompt(fields);
}

// This is the callback for the OK button
function closeOpportunityAsWon(promptResponse, opportunityId) {
    // promptResponse is an array, containing the entered values, e.g.:
    // 0: { id: "statuscode", value: 0 }
    // 1: { id: "actualrevenue", value: null }
    // 2: { id: "closeddate", value: null }
    // 3: { id: "competitor", value: null }
    // 4: { id: "description", value: null }

    // Get the values from the prompt responses
    var status = promptResponse.filter(a => a.id == "statuscode")[0].value;
    var actualRevenue = promptResponse.filter(a => a.id == "actualrevenue")[0].value;
    var closedDate = promptResponse.filter(a => a.id == "closeddate")[0].value;
    var competitor = promptResponse.filter(a => a.id == "competitor")[0].value;
    var description = promptResponse.filter(a => a.id == "description")[0].value;

    // Check if any of the required fields are not entered, then display an error
    if (status == null || actualRevenue == null || closedDate == null) {
        var requiredFields = [];
        if (status == null) { requiredFields.push("Status Code"); }
        if (actualRevenue == null) { requiredFields.push("Actual Revenue"); }
        if (closedDate == null) { requiredFields.push("Closed Date"); }

        var options = {
            title: "Validation Error",
            message: "The following fields are required to close the opportunity: " + requiredFields.join(", "),
            icon: "ERROR",
            id: "errorAlert"
        };

        new Alert(options).show();
    }
    else {
        // Close the main alert once all required fields are validated
        new Alert({ id: "closeOpportunity" }).hide();

        // Call the WebApi to win the opportunity
        // Use the example by Microsoft found here to make a request similar to this: 
        var opportunityClose = {
            "opportunityid@odata.bind": "/opportunities(" + cleanIdField(opportunityId) + ")",
            "description": description,
            "subject": "Subject here...",
            "actualrevenue": actualRevenue,
            "actualend": closedDate
        }

        var winOpportunityReq = {
            OpportunityClose: opportunityClose,
            Status: 3, // Won
            getMetadata: function () {
                return {
                    boundParameter: null,
                    parameterTypes: {
                        "OpportunityClose": {
                            "typeName": "mscrm.opportunityclose",
                            "structuralProperty": 5 // Entity Type
                        },
                        "Status": {
                            "typeName": "Edm.Int32",
                            "structuralProperty": 1 // Primitive Type
                        }
                    },
                    operationType: 0, // Action
                    operationName: "WinOpportunity",
                }
            }
        };

        Xrm.WebApi.online.execute(winOpportunityReq).then(
            function (result) {
                if (result.ok) {
                    console.log("Status: %s %s", result.status, result.statusText);
                    // Perform other operations as required
                }
            },
            function (error) {
                console.log(error.message);
                // Handle error conditions
            });
    }
}

// Strip off the curly braces for WebApi requests
function cleanIdField(id) {
    id = id.replace("{", "");
    id = id.replace("}", "");
    return id;
}

Alert.js Won Opportunity Screen

image

With the custom popup that tells you a required field is missing:

image

Microsoft’s out of the box Won Opportunity screen:

image

Summary:

Alert.js is a simple and easy to use solution to replicate popups by Microsoft, this is one example of millions that you could create. I suggest you download the trial and let it work its magic!