On Change Client Script with recursion problem

Guilherme Popo2
Tera Contributor

Hello everyone, could you help me with a problem?

 

I have a catalog item that has a field called "cr_due_date", in which the user can only select the last day of the month, however, if the last day of the month is a weekend (Saturday or Sunday) or a holiday, it should allow the user to enter the next business day.

 

I have this script that basically does this, however, what happens is that when I enter today's date, for example 11/29/2024, it should inform the user that the correct date to enter would be 12/02/2024. However, when I enter this date, it clears the field and says that the correct date to enter is 12/31/2024.

 

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }

    var currentDate = new Date(newValue);
    var currentMonth = currentDate.getMonth();
    var currentYear = currentDate.getFullYear();

    var lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0);

    if (lastDayOfMonth.getDay() === 6) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 2);

    } else if (lastDayOfMonth.getDay() === 0) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
	}

	

	var ga = new GlideAjax('x_itke_csc_latam.getHoliday');
	ga.addParam('sysparm_name','findHoliday');
	ga.addParam('sysparm_date', formattedDate);
	ga.getXML(callbackHoliday);

	function callbackHoliday(response){
		var resp = response.responseXML.documentElement.getAttribute('answer');
		if (resp == "true"){
			if(lastDayOfMonth.getDay() === 5){
				lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 3);
				formattedDate = lastDayOfMonth;
			}else{
				lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
			}
		}
	}

	var formattedDate = lastDayOfMonth.toISOString().split('T')[0];
	if (newValue !== formattedDate){
		g_form.clearValue('cr_due_date');
        g_form.showFieldMsg('cr_due_date', 'The date provided does not apply to payment rules. The correct date should be: ' + formattedDate, 'info');
	}
	
}

 Due Date 11.29.pngDue Date message 11.29.pngDue Date 12.02.pngDue Date message 12.02.png

1 ACCEPTED SOLUTION

Brad Bowman
Kilo Patron
Kilo Patron

This script runs fresh every time the Date is changed.  The issue is not recursion, rather that your logic doesn't permit the first business day of the month to be selected, even when the previous month ends on a non-working day.  Although formatted date ends up being declared before it is used due to hoisting, it would make more sense to follow best practices by declaring the variable at the beginning of the (onChange) function, setting the value before the GlideAjax call, then updating the value where you have it near the end.  This will get you closer:

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }

    var currentDate = new Date(newValue);
    var currentMonth = currentDate.getMonth();
    var currentYear = currentDate.getFullYear();
	var lastDayOfMonth = new Date();
	var formattedDate = '';
	var currentDayOfMonth = currentDate.getDate();
	
	if (currentDayOfMonth < 4) { //a date near the beginning of a month was selected, check if the previous month ends on a non-working day
		lastDayOfMonth = new Date(currentYear, currentMonth, 0); 
		
	} else { //check if selected month ends on a non-working day
		lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0);
	}
	
    if (lastDayOfMonth.getDay() === 6) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 2);
	
    } else if (lastDayOfMonth.getDay() === 0) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
    }

	formattedDate = lastDayOfMonth.toISOString().split('T')[0];

    var ga = new GlideAjax('x_itke_csc_latam.getHoliday');
    ga.addParam('sysparm_name', 'findHoliday');
    ga.addParam('sysparm_date', formattedDate);
    ga.getXML(callbackHoliday);

    function callbackHoliday(response) {
        var resp = response.responseXML.documentElement.getAttribute('answer');
        if (resp == "true") {
            if (lastDayOfMonth.getDay() === 5) {
                lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 3);
                formattedDate = lastDayOfMonth;
            } else {
                lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
            }
        }
    }

    formattedDate = lastDayOfMonth.toISOString().split('T')[0];
    if (newValue !== formattedDate) {
        g_form.clearValue('cr_due_date');
        g_form.showFieldMsg('cr_due_date', 'The date provided does not apply to payment rules. The correct date should be: ' + formattedDate, 'info');
    }

}

View solution in original post

2 REPLIES 2

Abhay Kumar1
Giga Sage

@Guilherme Popo2 Verify below aa looks issue with scope variable :

 Variable Scope Issue: The formattedDate variable is used before it is defined. You should define formattedDate before calling GlideAjax for the holiday check, because you use formattedDate in your ga.addParam() call.

 

Asynchronous GlideAjax: The GlideAjax call is asynchronous. This means that when the callback callbackHoliday runs, the logic following the call (like the comparison with newValue) will execute before the response is received. This will cause issues where the field is cleared or not updated correctly because formattedDate isn't updated yet when the field value is checked.

 

Hope this will help you.

 

To fix these issues, you should restructure your script to handle the asynchronous behavior properly. 

Brad Bowman
Kilo Patron
Kilo Patron

This script runs fresh every time the Date is changed.  The issue is not recursion, rather that your logic doesn't permit the first business day of the month to be selected, even when the previous month ends on a non-working day.  Although formatted date ends up being declared before it is used due to hoisting, it would make more sense to follow best practices by declaring the variable at the beginning of the (onChange) function, setting the value before the GlideAjax call, then updating the value where you have it near the end.  This will get you closer:

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue === '') {
        return;
    }

    var currentDate = new Date(newValue);
    var currentMonth = currentDate.getMonth();
    var currentYear = currentDate.getFullYear();
	var lastDayOfMonth = new Date();
	var formattedDate = '';
	var currentDayOfMonth = currentDate.getDate();
	
	if (currentDayOfMonth < 4) { //a date near the beginning of a month was selected, check if the previous month ends on a non-working day
		lastDayOfMonth = new Date(currentYear, currentMonth, 0); 
		
	} else { //check if selected month ends on a non-working day
		lastDayOfMonth = new Date(currentYear, currentMonth + 1, 0);
	}
	
    if (lastDayOfMonth.getDay() === 6) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 2);
	
    } else if (lastDayOfMonth.getDay() === 0) {
        lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
    }

	formattedDate = lastDayOfMonth.toISOString().split('T')[0];

    var ga = new GlideAjax('x_itke_csc_latam.getHoliday');
    ga.addParam('sysparm_name', 'findHoliday');
    ga.addParam('sysparm_date', formattedDate);
    ga.getXML(callbackHoliday);

    function callbackHoliday(response) {
        var resp = response.responseXML.documentElement.getAttribute('answer');
        if (resp == "true") {
            if (lastDayOfMonth.getDay() === 5) {
                lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 3);
                formattedDate = lastDayOfMonth;
            } else {
                lastDayOfMonth.setDate(lastDayOfMonth.getDate() + 1);
            }
        }
    }

    formattedDate = lastDayOfMonth.toISOString().split('T')[0];
    if (newValue !== formattedDate) {
        g_form.clearValue('cr_due_date');
        g_form.showFieldMsg('cr_due_date', 'The date provided does not apply to payment rules. The correct date should be: ' + formattedDate, 'info');
    }

}