Unable to set value as server-side script is not returning a response

I want to write server side and client side code on the ERPNext UI that can allow me to achieve the following scenario. In the Stock Entry doctype, I have added 4 new fields in the Stock Entry Detail which are custom_day_in, custom_day_out, custom_no_of_storage_days and custom_use_last_day. The custom_day_in is only accessible during a Stock Entry Type of ‘Material Receipt’ and it marks the day an item was received into the warehouse. The other 3 are accessible during a Stock Entry Type of ‘Material Issue’ and they are meant to mark the day an item was taken out of the warehouse(custom_day_out), the number of days the item has been in the warehouse(custom_no_of_storage_days) and the last is a check field(custom_use_last_day). Each stock entry detail item is also attached a batch_no. A batch_no can also be used multiple times. Based off of this, I’ve defined the below code that will allow me to calculate and set the custom_no_of_storage_days which will be the difference between either the first custom_day_in and the custom_day_out or the last custom_day_in and the custom_day_out which are of the same batch_no and use the use_last_day check to decide whether to use the first option or the second one.

Client-side:
frappe.ui.form.on(‘Stock Entry’, {
refresh: function(frm) {
// Refresh the field after setting it
frm.fields_dict.items.grid.refresh();
}
});

// Listen for changes in the child table (Stock Entry Detail)
frappe.ui.form.on(‘Stock Entry Detail’, {
custom_day_out: function(frm, cdt, cdn) {
calculate_storage_days(frm, cdt, cdn);
},
custom_use_last_day: function(frm, cdt, cdn) {
calculate_storage_days(frm, cdt, cdn);
}
});

// Function to calculate the storage days
function calculate_storage_days(frm, cdt, cdn) {
let row = locals[cdt][cdn]; // Get the specific row in the child table

if (frm.doc.stock_entry_type === 'Material Issue' && row.batch_no && row.custom_day_out) {
    // Call server method to fetch the first or last custom_day_in
    frappe.call({
        method: 'calculate_no_of_storage_days',  // This is the API Method defined in the Server Script
        args: {
            batch_no: row.batch_no,
            use_last_day: row.custom_use_last_day,
            day_out: row.custom_day_out
        },
        callback: function(r) {
            console.log("Server Response:", r);  // Debug: log the full response
            if (r.message) {
                row.custom_no_of_storage_days = r.message;
                frm.refresh_field('items');
            } else {
                console.log("No message received from server");
            }
        }
    });
}

}

Server-side:

def calculate_no_of_storage_days(batch_no, use_last_day, day_out):
frappe.log_error(“Batch No: {} | Use Last Day: {} | Day Out: {}”.format(batch_no, use_last_day, day_out), “Calculate Storage Days Debug”)

stock_entries = frappe.db.sql("""
    SELECT sed.custom_day_in
    FROM `tabStock Entry Detail` sed
    JOIN `tabStock Entry` se ON sed.parent = se.name
    WHERE sed.batch_no = %s
    AND sed.custom_day_in IS NOT NULL
    ORDER BY sed.custom_day_in {0}
    LIMIT 1
""".format('ASC' if not use_last_day else 'DESC'), (batch_no), as_dict=True)

if stock_entries:
    day_in = stock_entries[0].get('custom_day_in')

    if day_in and day_out:
        # Calculate the number of storage days
        no_of_storage_days = (getdate(day_out) - getdate(day_in)).days
        frappe.log_error("Day In: {} | Day Out: {} | Storage Days: {}".format(day_in, day_out, no_of_storage_days), "Calculate Storage Days Success")
        return no_of_storage_days
    else:
        frappe.log_error("Missing Day In or Day Out", "Calculate Storage Days Error")
else:
    frappe.log_error("No Stock Entries Found for Batch No: {}".format(batch_no), "Calculate Storage Days Error")

return 0  # Return 0 if no matching entry is found

No message Received from server is logged on the console but no messages are being received on the error logs. On the network tab on the Developer tools, I see the method is being called and the payload has the correct values.

Please check the video, if you want to apply the frappe.whitelist method on the server side.