Поделиться через


Контекст формы для внутренних обработчиков отсутствует в формах настраиваемых бронированных ресурсов

Эта статья помогает устранить проблему, вызванную отсутствием контекста формы для внутренних обработчиков в формах бронируемых ресурсов в Microsoft Dynamics 365 Field Service.

Симптомы

При создании резервируемого ресурса в Службе полей Dynamics 365 при выборе пользователя из поля в форме может появиться следующее сообщение об ошибке:

Не удается прочитать свойства неопределённого объекта при попытке чтения 'getFormContext'.

Причина

Проблема возникает из-за того, что система использует настраиваемую форму, основанную на устаревшей версии формы бронируемого ресурса. Теперешние изменения внутренних обработчиков для событий onchange требуют передачи контекста выполнения из формы.

Резолюция

Используйте одно из перечисленных разрешений, чтобы убедиться, что контекст выполнения передается в качестве первого параметра.

Это важно

В следующих разрешениях предполагается, что ошибка скрипта ссылается на функцию Mscrm.userid_onchange . Если ошибка относится к другим полям или функциям, таким как Mscrm.accountid_onchange или Mscrm.contactid_onchange, измените шаги соответствующим образом.

Решение 1. Обновление формы в Power Apps

  1. Войдите в Power Apps и откройте решение, содержащее форму.

  2. Выберите поле "Пользователь" и перейдите к параметрам события.

  3. Найдите обработчик событий Mscrm.userid_onchange.

    Если обработчик не существует:

    1. Добавьте новое событие типа On Change.
    2. Выберите библиотеку Scheduling/BookableResource/BookableResource_main_system_library.js .
    3. Введите Mscrm.userid_onchange в поле "Функция ".
    4. Убедитесь, что установлены флажки Включено и Передавать контекст выполнения как первый параметр.

    Если обработчик существует:

    1. Измените обработчик и убедитесь, что флажок Передача контекста выполнения как первого параметра выбран.
  4. Сохраните и опубликуйте обновленную форму.

Решение 2. Проверка файла customizations.xml

  1. Откройте файлcustomizations.xml из решения, связанного с настраиваемой формой, которая отображает ошибку в редакторе.

  2. В элементе Handler функции Mscrm.userid_onchange убедитесь, что атрибут passExecutionContext установлен в true.

  3. Повторно опубликуйте решение.

Решение 3. Запуск скрипта в консоли браузера

Чтобы этот скрипт получил разрешение на поиск и обновление необходимых сведений, его необходимо запустить на вкладке браузера с активным сеансом с вашей средой. Кроме того, учетной записи пользователя необходимы разрешения для обновления XML настраиваемой формы бронируемого ресурса.

  1. Откройте среду в браузере. Следующие инструкции используют Microsoft Edge в качестве примера.

  2. Откройте средства разработки , нажав клавишу F12 или перейдя к многоточию (...) >Дополнительные средства>Средства разработчика.

  3. Выберите вкладку "Консоль " в DevTools и нажмите кнопку "Очистить консоль".

  4. Скопируйте и вставьте следующий код JavaScript в консоль.

    const ORG = "<YOUR-ENVIRONMENT-URL>"; // for example "contoso.crm.dynamics.com"
    
    async function fixBookableResourceForms() {
    
        console.log("Starting Bookable Resource Form fetch process...");
    
        let publishXML = false;
    
        try {
    
            const response = await fetch(`https://${ORG}/api/data/v9.2/systemforms?$filter=objecttypecode eq 'bookableresource'`);
    
            if (!response.ok) {
    
                throw new Error(`Error fetching resources: ${response.statusText}`);
    
            }
    
            const data = await response.json();
    
            const forms = data.value;
    
            if(forms.length !== 0) {
    
                for (const form of forms) {
    
                    console.log(`  Checking form ${form.name} (${form.formid})...`);
    
                    const formXML = form.formxml;
    
                    const parser = new DOMParser();
    
                    const xmlDoc = parser.parseFromString(formXML, "text/xml");
    
                    const handlers = xmlDoc.getElementsByTagName("Handler");
    
                    const userIdHandlers = []
    
                    for (let i = 0; i < handlers.length; i++) {
    
                        const handler = handlers[i];
    
                        if (
    
                            handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
    
                            handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js"
    
                        ) {
    
                            userIdHandlers.push(handler);
    
                        }
    
                    }
    
                    if (userIdHandlers.length > 1) {
    
                        console.warn(`Form ${form.name} (${form.formid}) has more than 1 Mscrm.userid_onchange event handlers (has ${userIdHandlers.length}).`);
    
                    }
    
                    else if (userIdHandlers.length === 0) {
    
                        console.warn(`Form ${form.name} (${form.formid}) has 0 Mscrm.userid_onchange event handlers.`);
    
                        continue;
    
                    }
    
                    await Promise.all(userIdHandlers.map(async handler => {
    
                        if (
    
                            handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
    
                            handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
    
                            handler.getAttribute("passExecutionContext") === "true"
    
                        ) {
    
                            console.log(`    Form ${form.name} (${form.formid}) has the correct Mscrm.userid_onchange event handler.`);
    
                        }
    
                        else if (
    
                            handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
    
                            handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
    
                            (handler.getAttribute("passExecutionContext") === "false" || handler.getAttribute("passExecutionContext") === null)
    
                        ) {
    
                            console.log(`    Form ${form.name} (${form.formid}) has the Mscrm.userid_onchange event handler but the passExecutionContext attribute is not set to true. Setting it to true...`);
    
                            handler.setAttribute("passExecutionContext", "true");
    
                            const serializer = new XMLSerializer();
    
                            const updatedXml = serializer.serializeToString(xmlDoc);
    
                            await updateBookableResourceForms(form, updatedXml);
    
                            publishXML = true;
    
                        }
    
                        return Promise.resolve();
    
                    }));
    
                }
    
            } else {
    
                console.log("No Bookable Resource forms found. Nothing was done");
    
            }
    
        } catch (error) {
    
            console.error(error.message);
    
        }
    
        if(publishXML) {
    
            console.log("Publishing changes...");
    
            await publishChanges();
    
            publishXML = false;
    
        }
    
        console.log("Finished");
    
    }
    
    async function updateBookableResourceForms(form, formxml) {
    
        console.log(`    Updating Bookable Resource Form ${form.name} (${form.formid}) ...`);
    
        try {
    
            const reqBody = JSON.stringify({formxml: formxml});
    
            const updateResponse = await fetch(`https://${ORG}/api/data/v9.2/systemforms(${form.formid})`, {
    
                method: 'PATCH',
    
                headers: {
    
                    'Content-Type': 'application/json'
    
                },
    
                body: reqBody
    
            });
    
            if (!updateResponse.ok) {
    
                throw new Error(`Error updating form ${formid}: ${updateResponse.statusText}`);
    
            }
    
            console.log(`      Form ${form.name} (${form.formid}) updated successfully.`);
    
        } catch (error) {
    
            console.error(error.message);
    
        }
    
        console.log(`    Done updating Bookable Resource Form ${form.name} (${form.formid}).`);
    
    }
    
    async function publishChanges() {
    
        console.log("  Starting publish XML process...");
    
        const publishResponse = await fetch(`https://${ORG}/api/data/v9.2/PublishXml`, {
    
            method: 'POST',
    
            headers: {
    
                'Content-Type': 'application/json'
    
            },
    
            body: '{"ParameterXml": "<importexportxml><entities><entity>bookableresource</entity></entities></importexportxml>"}'
    
        });
    
        if (!publishResponse.ok) {
    
            throw new Error(`Error publishing XML: ${publishResponse.statusText}`);
    
        }
    
        console.log("  Done publish XML process.");
    
    }
    
    // Call the function
    
    fixBookableResourceForms()
    
  5. Обновите константу ORG в скрипте с URL-адресом вашей среды, например, contoso.crm.dynamics.com.

  6. Запустите скрипт и просмотрите выходные данные, чтобы подтвердить обновления.