Skip to content

Commit f010ae7

Browse files
authored
Updates from editor
1 parent 585142c commit f010ae7

File tree

2 files changed

+121
-121
lines changed

2 files changed

+121
-121
lines changed
Lines changed: 120 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
---
2-
title: Missing form context for internal handlers on customized bookable resource forms
3-
description: Address issues with customized forms based on the default bookable resource form for Dynamics 365 Field Service.
2+
title: Missing Form Context for Internal Handlers on Customized Bookable Resource Forms
3+
description: Addresses issues with customized forms that are based on the default bookable resource form for Dynamics 365 Field Service.
44
author: m-hartmann
55
ms.author: mhart
66
ms.reviewer: mhart
7-
ms.date: 03/31/2025
7+
ms.date: 04/08/2025
88
ms.custom: sap:Schedule Board
99
---
10-
# Missing form context for internal handlers on customized bookable resource forms
10+
# Form context for internal handlers is missing on customized bookable resource forms
1111

12-
This article helps resolve issues caused by missing form context for internal handlers on the bookable resource form in Microsoft Dynamics 365 Field Service.
12+
This article helps resolve an issue caused by missing form context for internal handlers on bookable resource forms in Microsoft Dynamics 365 Field Service.
1313

1414
## Symptoms
1515

16-
When you select a user from the field on the form while [creating a bookable resource](/dynamics365/field-service/set-up-bookable-resources) in Dynamics 365 Field Service, you might receive the following error message:
16+
While [creating a bookable resource](/dynamics365/field-service/set-up-bookable-resources) in Dynamics 365 Field Service, you might receive the following error message when you select a user from a field on the form:
1717

1818
> Cannot read properties of undefined (reading 'getFormContext')
1919
2020
## Cause
2121

22-
The issue occurs because the system uses a customized form based on an outdated version of the bookable resource form. A change in the internal handlers for `onchange` events now requires the execution context to be passed in from the form.
22+
The issue occurs because the system uses a customized form that is based on an outdated version of the bookable resource form. A change to the internal handlers for `onchange` events now requires the execution context to be passed in from the form.
2323

2424
## Resolution
2525

2626
Use one of the listed resolutions to ensure that the execution context is passed as the first parameter.
2727

2828
> [!IMPORTANT]
29-
> The following resolutions assume that the script error references the `Mscrm.userid_onchange` function. If the error refers to other fields or functions, such as `Mscrm.accountid_onchange` or `Mscrm.contactid_onchange`, adapt the steps accordingly.
29+
> The following resolutions assume that the script error references the `Mscrm.userid_onchange` function. If the error refers to other fields or functions, such as `Mscrm.accountid_onchange` or `Mscrm.contactid_onchange`, adjust the steps accordingly.
3030
3131
### Resolution 1: Update the form in Power Apps
3232

@@ -38,9 +38,9 @@ Use one of the listed resolutions to ensure that the execution context is passed
3838

3939
If the handler doesn't exist:
4040

41-
1. Add a new event of type **On Change**
41+
1. Add a new event of type **On Change**.
4242
1. Select the **Scheduling/BookableResource/BookableResource_main_system_library.js** library.
43-
1. Enter _Mscrm.userid_onchange_ in the **Function** field.
43+
1. Enter **Mscrm.userid_onchange** in the **Function** field.
4444
1. Ensure the **Enabled** and **Pass execution context as first parameter** checkboxes are selected.
4545

4646
If the handler exists:
@@ -59,232 +59,232 @@ Use one of the listed resolutions to ensure that the execution context is passed
5959

6060
### Resolution 3: Run a script in the browser console
6161

62-
To ensure this script has the permission to find and update the required information, you need to run it in a browser tab that has an active session with your enviornment. Additionally, your user account needs the permisssions to update the XML of the customized bookable resource form.
62+
To ensure this script has permission to find and update the required information, you need to run it in a browser tab that has an active session with your environment. Additionally, your user account needs permisssion to update the XML of the customized bookable resource form.
6363

6464
1. Open the environment in your browser. The following instructions use Microsoft Edge as an example.
6565

66-
1. Open **DevTools** by pressing F12 or navigating to ellipsis (…) > **More tools** > **Developer tools**
66+
1. Open **DevTools** by pressing <kbd>F12</kbd> or navigating to ellipsis (&hellip;) > **More tools** > **Developer tools**.
6767

68-
1. Select the **Console** tab in the DevTools and select **Clear Console**.
68+
1. Select the **Console** tab in DevTools and select **Clear Console**.
6969

7070
1. Copy and paste the following JavaScript code into the console.
7171

72-
1. Update the `ORG` constant in the script with your environment URL, for example, `contoso.crm.dynamics.com`.
73-
74-
1. Run the script and review the output to confirm the updates.
72+
```JavaScript
73+
const ORG = "<YOUR-ENVIRONMENT-URL>"; // for example "contoso.crm.dynamics.com"
7574

76-
```JavaScript
77-
const ORG = "<YOUR-ENVIRONMENT-URL>"; // for example "contoso.crm.dynamics.com"
75+
async function fixBookableResourceForms() {
7876

79-
async function fixBookableResourceForms() {
77+
console.log("Starting Bookable Resource Form fetch process...");
8078

81-
console.log("Starting Bookable Resource Form fetch process...");
79+
let publishXML = false;
8280

83-
let publishXML = false;
81+
try {
8482

85-
try {
83+
const response = await fetch(`https://${ORG}/api/data/v9.2/systemforms?$filter=objecttypecode eq 'bookableresource'`);
8684

87-
const response = await fetch(`https://${ORG}/api/data/v9.2/systemforms?$filter=objecttypecode eq 'bookableresource'`);
85+
if (!response.ok) {
8886

89-
if (!response.ok) {
87+
throw new Error(`Error fetching resources: ${response.statusText}`);
9088

91-
throw new Error(`Error fetching resources: ${response.statusText}`);
89+
}
9290

93-
}
91+
const data = await response.json();
9492

95-
const data = await response.json();
93+
const forms = data.value;
9694

97-
const forms = data.value;
95+
if(forms.length !== 0) {
9896

99-
if(forms.length !== 0) {
97+
for (const form of forms) {
10098

101-
for (const form of forms) {
99+
console.log(` Checking form ${form.name} (${form.formid})...`);
102100

103-
console.log(` Checking form ${form.name} (${form.formid})...`);
101+
const formXML = form.formxml;
104102

105-
const formXML = form.formxml;
103+
const parser = new DOMParser();
106104

107-
const parser = new DOMParser();
105+
const xmlDoc = parser.parseFromString(formXML, "text/xml");
108106

109-
const xmlDoc = parser.parseFromString(formXML, "text/xml");
107+
const handlers = xmlDoc.getElementsByTagName("Handler");
110108

111-
const handlers = xmlDoc.getElementsByTagName("Handler");
109+
const userIdHandlers = []
112110

113-
const userIdHandlers = []
111+
for (let i = 0; i < handlers.length; i++) {
114112

115-
for (let i = 0; i < handlers.length; i++) {
113+
const handler = handlers[i];
116114

117-
const handler = handlers[i];
115+
if (
118116

119-
if (
117+
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
120118

121-
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
119+
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js"
122120

123-
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js"
121+
) {
124122

125-
) {
123+
userIdHandlers.push(handler);
126124

127-
userIdHandlers.push(handler);
125+
}
128126

129127
}
130128

131-
}
129+
if (userIdHandlers.length > 1) {
132130

133-
if (userIdHandlers.length > 1) {
131+
console.warn(`Form ${form.name} (${form.formid}) has more than 1 Mscrm.userid_onchange event handlers (has ${userIdHandlers.length}).`);
134132

135-
console.warn(`Form ${form.name} (${form.formid}) has more than 1 Mscrm.userid_onchange event handlers (has ${userIdHandlers.length}).`);
133+
}
136134

137-
}
135+
else if (userIdHandlers.length === 0) {
138136

139-
else if (userIdHandlers.length === 0) {
137+
console.warn(`Form ${form.name} (${form.formid}) has 0 Mscrm.userid_onchange event handlers.`);
140138

141-
console.warn(`Form ${form.name} (${form.formid}) has 0 Mscrm.userid_onchange event handlers.`);
139+
continue;
142140

143-
continue;
141+
}
144142

145-
}
143+
await Promise.all(userIdHandlers.map(async handler => {
146144

147-
await Promise.all(userIdHandlers.map(async handler => {
145+
if (
148146

149-
if (
147+
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
150148

151-
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
149+
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
152150

153-
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
151+
handler.getAttribute("passExecutionContext") === "true"
154152

155-
handler.getAttribute("passExecutionContext") === "true"
153+
) {
156154

157-
) {
155+
console.log(` Form ${form.name} (${form.formid}) has the correct Mscrm.userid_onchange event handler.`);
158156

159-
console.log(` Form ${form.name} (${form.formid}) has the correct Mscrm.userid_onchange event handler.`);
157+
}
160158

161-
}
159+
else if (
162160

163-
else if (
161+
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
164162

165-
handler.getAttribute("functionName") === "Mscrm.userid_onchange" &&
163+
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
166164

167-
handler.getAttribute("libraryName") === "Scheduling/BookableResource/BookableResource_main_system_library.js" &&
165+
(handler.getAttribute("passExecutionContext") === "false" || handler.getAttribute("passExecutionContext") === null)
168166

169-
(handler.getAttribute("passExecutionContext") === "false" || handler.getAttribute("passExecutionContext") === null)
167+
) {
170168

171-
) {
169+
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...`);
172170

173-
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...`);
171+
handler.setAttribute("passExecutionContext", "true");
174172

175-
handler.setAttribute("passExecutionContext", "true");
173+
const serializer = new XMLSerializer();
176174

177-
const serializer = new XMLSerializer();
175+
const updatedXml = serializer.serializeToString(xmlDoc);
178176

179-
const updatedXml = serializer.serializeToString(xmlDoc);
177+
await updateBookableResourceForms(form, updatedXml);
180178

181-
await updateBookableResourceForms(form, updatedXml);
179+
publishXML = true;
182180

183-
publishXML = true;
181+
}
184182

185-
}
183+
return Promise.resolve();
186184

187-
return Promise.resolve();
185+
}));
188186

189-
}));
187+
}
188+
189+
} else {
190+
191+
console.log("No Bookable Resource forms found. Nothing was done");
190192

191193
}
192194

193-
} else {
195+
} catch (error) {
194196

195-
console.log("No Bookable Resource forms found. Nothing was done");
197+
console.error(error.message);
196198

197199
}
198200

199-
} catch (error) {
200-
201-
console.error(error.message);
201+
if(publishXML) {
202202

203-
}
203+
console.log("Publishing changes...");
204204

205-
if(publishXML) {
205+
await publishChanges();
206206

207-
console.log("Publishing changes...");
207+
publishXML = false;
208208

209-
await publishChanges();
209+
}
210210

211-
publishXML = false;
211+
console.log("Finished");
212212

213213
}
214214

215-
console.log("Finished");
215+
async function updateBookableResourceForms(form, formxml) {
216216

217-
}
217+
console.log(` Updating Bookable Resource Form ${form.name} (${form.formid}) ...`);
218218

219-
async function updateBookableResourceForms(form, formxml) {
219+
try {
220220

221-
console.log(` Updating Bookable Resource Form ${form.name} (${form.formid}) ...`);
221+
const reqBody = JSON.stringify({formxml: formxml});
222222

223-
try {
223+
const updateResponse = await fetch(`https://${ORG}/api/data/v9.2/systemforms(${form.formid})`, {
224224

225-
const reqBody = JSON.stringify({formxml: formxml});
225+
method: 'PATCH',
226226

227-
const updateResponse = await fetch(`https://${ORG}/api/data/v9.2/systemforms(${form.formid})`, {
227+
headers: {
228228

229-
method: 'PATCH',
229+
'Content-Type': 'application/json'
230230

231-
headers: {
231+
},
232232

233-
'Content-Type': 'application/json'
233+
body: reqBody
234234

235-
},
235+
});
236236

237-
body: reqBody
237+
if (!updateResponse.ok) {
238238

239-
});
239+
throw new Error(`Error updating form ${formid}: ${updateResponse.statusText}`);
240240

241-
if (!updateResponse.ok) {
241+
}
242242

243-
throw new Error(`Error updating form ${formid}: ${updateResponse.statusText}`);
243+
console.log(` Form ${form.name} (${form.formid}) updated successfully.`);
244244

245-
}
245+
} catch (error) {
246246

247-
console.log(` Form ${form.name} (${form.formid}) updated successfully.`);
247+
console.error(error.message);
248248

249-
} catch (error) {
249+
}
250250

251-
console.error(error.message);
251+
console.log(` Done updating Bookable Resource Form ${form.name} (${form.formid}).`);
252252

253253
}
254254

255-
console.log(` Done updating Bookable Resource Form ${form.name} (${form.formid}).`);
255+
async function publishChanges() {
256256

257-
}
257+
console.log(" Starting publish XML process...");
258258

259-
async function publishChanges() {
259+
const publishResponse = await fetch(`https://${ORG}/api/data/v9.2/PublishXml`, {
260260

261-
console.log(" Starting publish XML process...");
261+
method: 'POST',
262262

263-
const publishResponse = await fetch(`https://${ORG}/api/data/v9.2/PublishXml`, {
263+
headers: {
264264

265-
method: 'POST',
265+
'Content-Type': 'application/json'
266266

267-
headers: {
267+
},
268268

269-
'Content-Type': 'application/json'
269+
body: '{"ParameterXml": "<importexportxml><entities><entity>bookableresource</entity></entities></importexportxml>"}'
270270

271-
},
271+
});
272272

273-
body: '{"ParameterXml": "<importexportxml><entities><entity>bookableresource</entity></entities></importexportxml>"}'
273+
if (!publishResponse.ok) {
274274

275-
});
275+
throw new Error(`Error publishing XML: ${publishResponse.statusText}`);
276276

277-
if (!publishResponse.ok) {
277+
}
278278

279-
throw new Error(`Error publishing XML: ${publishResponse.statusText}`);
279+
console.log(" Done publish XML process.");
280280

281281
}
282282

283-
console.log(" Done publish XML process.");
283+
// Call the function
284284

285-
}
285+
fixBookableResourceForms()
286+
```
286287

287-
// Call the function
288+
1. Update the `ORG` constant in the script with your environment URL, for example, `contoso.crm.dynamics.com`.
288289

289-
fixBookableResourceForms()
290-
```
290+
1. Run the script and review the output to confirm the updates.

0 commit comments

Comments
 (0)