Bhubaneswar, Odisha, India
+91-8328865778
support@softchief.com

Async Save Feature+ Xrm.WebAPI+ Prevent Save – Conditionally restrict save for xrm webapi retrievemultiple call on save event Dataverse/ Dynamics 365

Async Save Feature+ Xrm.WebAPI+ Prevent Save – Conditionally restrict save for xrm webapi retrievemultiple call on save event Dataverse/ Dynamics 365

In this blog post, we’ll walk through an asynchronous JavaScript function restrictAppointments_OnSave_Async with Xrm.WebAPI.ReatrieveMultipleRecord that accomplishes this by restricting the number of appointments a patient can have with the same doctor on the same day in a Dynamics 365 environment.

Problem Statement

The goal is to prevent a patient from booking more than two appointments with the same doctor on the same day. This validation needs to occur during the save event of the appointment form to ensure that no invalid data is saved.

Solution Code

First you need to enable Async OnSave Feature for your Model Driven App.

After this setting create a JS web resource and add the below function to the JS file and call the function as an event handler from Form on Save event.

JavaScript
async function restrictAppointments_OnSave_Async(executionContext) {
    try {
        
        // This method ensures that if an error occurs during the save event, 
        // the save operation will be prevented. This helps maintain data integrity 
        // by not allowing the save to proceed when there are errors in the script.
       
        executionContext.getEventArgs().preventDefaultOnError();

        var formContext = executionContext.getFormContext();
        
        var doctorSelected = formContext.getAttribute("seven_doctor").getValue();
        var patientSelected = formContext.getAttribute("seven_patient").getValue();
        var appointmentSlot = formContext.getAttribute("seven_appointmentslot").getValue();

        // Ensure all required values are present
        if (!doctorSelected || !doctorSelected[0] || !patientSelected || !patientSelected[0] || !appointmentSlot) {
            formContext.ui.setFormNotification("Doctor, patient, and appointment slot are required fields.", "ERROR", "REQUIRED_FIELDS");
            throw new Error("Doctor, patient and slot is required.");
        }

        var doctorID = doctorSelected[0].id;
        var patientID = patientSelected[0].id;
        var year = appointmentSlot.getFullYear();
        var month = appointmentSlot.getMonth() + 1;
        var day = appointmentSlot.getDate();
        var appointmentSlotValue = `${year}-${month}-${day}`;

        var fetchXML = `
            <fetch version='1.0' mapping='logical' no-lock='false' distinct='true'>
                <entity name='seven_doctorappointment'>
                    <attribute name='statecode'/>
                    <attribute name='seven_doctorappointmentid'/>
                    <attribute name='seven_name'/>
                    <attribute name='createdon'/>
                    <attribute name='seven_appointmentnumber'/>
                    <attribute name='seven_appointmentslot'/>
                    <attribute name='seven_doctor'/>
                    <attribute name='seven_patient'/>
                    <order attribute='seven_appointmentnumber' descending='false'/>
                    <order attribute='seven_appointmentslot' descending='false'/>
                    <attribute name='statuscode'/>
                    <filter type='and'>
                        <condition attribute='statecode' operator='eq' value='0'/>
                        <condition attribute='seven_doctor' operator='eq' value='${doctorID}' uitype='systemuser'/>
                        <condition attribute='seven_patient' operator='eq' value='${patientID}' uitype='contact'/>
                        <condition attribute='seven_appointmentslot' operator='on' value='${appointmentSlotValue}'/>
                    </filter>
                </entity>
            </fetch>`;

        var fetchXmlFinal = `?fetchXml=${encodeURIComponent(fetchXML)}`;

        let result = await Xrm.WebApi.retrieveMultipleRecords("seven_doctorappointment", fetchXmlFinal);
        var notificationID = "MYMSG";

        if (result.entities.length >= 2)
            throw new Error("You cannot create more than 2 appointments for the same patient and doctor on the same day.");

    } catch (error) {
        console.error("Error in restrictAppointments_OnSave_Async:", error.message);
        throw error;
    }
}


JavaScript

Here is the result.

Validation Failed Result

If no validation failed it allows saving the record without any message.

In case you want to make use of XMLHttp call instead of Xrm.WEBAPI Call for the same scenario, you can use below sample code. For this no need to use Async and await keyword.

JavaScript
function restrictAppointments(executionContext)
{
    var formContext = executionContext.getFormContext();

    var doctorSelected = formContext.getAttribute("seven_doctor").getValue();
    var doctorID = doctorSelected[0].id;
   
    var patientSelected = formContext.getAttribute("seven_patient").getValue();
    var patientID = patientSelected[0].id;

    var appointmentSlot = formContext.getAttribute("seven_appointmentslot").getValue();

    var year = appointmentSlot.getFullYear();
    var month = appointmentSlot.getMonth()+1;
    var day = appointmentSlot.getDate();
    
    var appointmentSlotValue = year+"-"+month+"-"+day;

    var fetchXML ="<fetch version='1.0' mapping='logical' no-lock='false' distinct='true'><entity name='seven_doctorappointment'><attribute name='statecode'/><attribute name='seven_doctorappointmentid'/><attribute name='seven_name'/><attribute name='createdon'/><attribute name='seven_appointmentnumber'/><attribute name='seven_appointmentslot'/><attribute name='seven_doctor'/><attribute name='seven_patient'/><order attribute='seven_appointmentnumber' descending='false'/><order attribute='seven_appointmentslot' descending='false'/><attribute name='statuscode'/><filter type='and'><condition attribute='statecode' operator='eq' value='0'/><condition attribute='seven_doctor' operator='eq' value='"+doctorID+"' uitype='systemuser'/><condition attribute='seven_patient' operator='eq' value='"+patientID+"' uitype='contact'/><condition attribute='seven_appointmentslot' operator='on' value='"+appointmentSlotValue+"'/></filter></entity></fetch>";

    console.log(fetchXML);

    var fetchXmlFinal = encodeURI(fetchXML);

    var query = "/api/data/v9.2/seven_doctorappointments?fetchXml="+fetchXmlFinal;

    var globalContext = Xrm.Utility.getGlobalContext();
    
    var finalpathwithquery = globalContext.getClientUrl() + query;
    
    var data = null;
    var isAsync = false;
    
    var req = null;
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } 
    else if (window.ActiveXObject) {
        req = new ActiveXObject("MSXML2.XMLHTTP.3.0");
    }
    
    req.open("GET",finalpathwithquery,isAsync);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var result = JSON.parse(this.response);
                data = result;
            } 
            else {
                Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
    
    if(data.value.length >=2)
    {
          Xrm.Utility.alertDialog("You cannot create more than 2 apppointments for same patient and same doctor on same date. There are total "+data.value.length+ " records retrieved");       
          executionContext.getEventArgs().preventDefault();        
    }
}
JavaScript

Hope this helps.