Field Service: correct way to cancel a BookableResourceBooking via SDK/API? - dynamics-crm

The BookableResourceBooking entity is documented here:
https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/entities/bookableresourcebooking
I'd like to able to cancel a booking but I can't seem to find any SDK or API docs that explain how to do so. Would changing the bookingstatus value to "canceled" be sufficient to cancel a booking? Where would I input the reason code?

You can do this using SDK as well as API.
When you see Bookable Resource Booking in CRM, you can see Deactivate button. Clicking on it will deactivte the Bookable Resource Booking.
Now there is one more clean way to manage data, you can set Booking status to cancelled and then deactivate record in this way you can capture complete data as why Bookable Resource Booking record is cancelled/deactivated.
var entity = {};
entity["bookingstatus#odata.bind"] = "/bookingstatuses(bbda588b-013a-eb11-a813-000d3a25bbe9)"; /* cancelled booking status ID*/
entity.statecode = 1; /*Inactive*/
entity.statuscode = 2; /*Inactive*/
Xrm.WebApi.online.updateRecord("bookableresourcebooking", "bbda588b-013a-eb11-a813-000d3a25bbe9", entity).then(
function success(result) {
var updatedEntityId = result.id;
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);

Related

How to get query sys_id of current.sys_id Service Portal (ServiceNow)

I have a question regarding a small issue that I'm having. I've created a widget that will live on the Service Portal to allow an admin to Accept or Reject requests.
The data for the widget is pulling from the Approvals (approval_approver) table. Under my GlideRecord, I have a query that checks for the state as requested. (Ex. addQuery('state', 'requested'))
To narrow down the search, I tried entering addQuery('sys_id', current.sys_id). When I use this query, my script breaks and I get an error on the Service Portal end.
Here's a sample of the GlideRecord script I've written to Accept.
[//Accept Request
if(input && input.action=="acceptApproval") {
var inRec1 = new GlideRecord('sysapproval_approver');
inRec1.addQuery('state', 'requested');
//inRec1.get('sys_id', current.sys_id);
inRec1.query();
if(inRec1.next()) {
inRec1.setValue('state', 'Approved');
inRec1.setValue('approver', gs.getUserID());
gs.addInfoMessage("Accept Approval Processed");
inRec1.update();
}
}][1]
I've research the web, tried using $sp.getParameter() as a work-around and no change.
I would really appreciate any help or insight on what I can do different to get script to work and filter the right records.
If I understand your question correctly, you are asking how to get the sysId of the sysapproval_approver record from the client-side in a widget.
Unless you have defined current elsewhere in your server script, current is undefined. Secondly, $sp.getParameter() is used to retrieve URL parameters. So unless you've included the sysId as a URL parameter, that will not get you what you are looking for.
One pattern that I've used is to pass an object to the client after the initial query that gets the list of requests.
When you're ready to send input to the server from the client, you can add relevant information to the input object. See the simplified example below. For the sake of brevity, the code below does not include error handling.
// Client-side function
approveRequest = function(sysId) {
$scope.server.get({
action: "requestApproval",
sysId: sysId
})
.then(function(response) {
console.log("Request approved");
});
};
// Server-side
var requestGr = new GlideRecord();
requestGr.addQuery("SOME_QUERY");
requestGr.query(); // Retrieve initial list of requests to display in the template
data.requests = []; // Add array of requests to data object to be passed to the client via the controller
while(requestsGr.next()) {
data.requests.push({
"number": requestsGr.getValue("number");
"state" : requestsGr.getValue("state");
"sysId" : requestsGr.getValue("sys_id");
});
}
if(input && input.action=="acceptApproval") {
var sysapprovalGr = new GlideRecord('sysapproval_approver');
if(sysapprovalGr.get(input.sysId)) {
sysapprovalGr.setValue('state', 'Approved');
sysapprovalGr.setValue('approver', gs.getUserID());
sysapprovalGr.update();
gs.addInfoMessage("Accept Approval Processed");
}
...

Bring entities on draft mode Strapi

Scenario
I'm trying to get all entities from an endpoint, the ones on draft mode, and the ones on published mode.
I know that if I want to post on draft mode, I have to post published_at on null on the body request.
If I do:
/posts?published_at_null=true
that returns an empty array.
Question
How can I do to return ALL the posts?
It is on the documentation
https://strapi.io/documentation/developer-docs/latest/developer-resources/content-api/content-api.html#publication-state
But quick answer, URL + '?_publicationState=preview'
https://forum.strapi.io/t/draft-and-posted-entities/3576/2
you will have to create a custom control that will fetch all the entries.you cannot fetch draft data using the existing restapi urls.
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async findUnpublished(ctx) {
//getting all the existing articles, no meter if they have unpublished status
let result = await strapi.query('posts').find();
//sanitize them to hide all private fields
let articles = sanitizeEntity(result, {
model: strapi.models['posts'],
});
//return result to the /findUnpublished API
ctx.send(articles);
}
};

nativescript-phone prevents Nativescript-contacts from returning

I have an app where I want to select a person from contacts and then send a text to that person. It works as expected for the first user, but after that the app never receives control after the contact is selected. I've isolated the problem to the Nativescript-phone plugin. If you simply call phone.sms() to send a text, and then call contacts.getContact(), the problem occurs. I see this on both Android and iOS.
I've created a sample app that demos the problem at https://github.com/dlcole/contactTester. The sample app is Android only. I've spent a couple days on this and welcome any insights.
Edit 4/21/2020:
I've spent more time on this and can see what's happening. Both plugins have the same event handler and same request codes:
nativescript-phone:
var SEND_SMS = 1001;
activity.onActivityResult = function(requestCode, resultCode, data) {
nativescript-contacts:
var PICK_CONTACT = 1001;
appModule.android.on("activityResult", function(eventData) {
What happens is that after invoking phone.sms, calling contacts.getContact causes control to return to the phone plugin, and NOT the contacts plugin. I tried changing phone's request code to 1002 but had the same results.
So, the next step is to determine how to avoid the collision of the event handlers.
Instead of using activityResult event, nativescript-phone plugin overwrites the default activity result callback.
A workaround is to set the callback to it's original value after you are done with nativescript-phone.
exports.sendText = function (args) {
console.log("entering sendText");
const activity = appModule.android.foregroundActivity || appModule.android.startActivity;
const onActivityResult = activity.onActivityResult;
permissions.requestPermissions([android.Manifest.permission.CALL_PHONE],
"Permission needed to send text")
.then(() => {
console.log("permission granted");
phone.sms()
.then((result) => {
console.log(JSON.stringify(result, null, 4));
activity.onActivityResult = onActivityResult;
})
})
}

Get conversation ID in directline bot framework inline

I am using directline for webchat.
I want to include a refresh button on top of the chat and for that I need the conversation ID. How can I get the ID? Is it possible by using inline webchat ?
This the refresh button that I am trying to implement
I was facing the same problem, as I wanted to pass the conversation ID to my custom controller for initial authentication and correspondingly, push a custom auth data to the conversation stack of the bot framework relating to that specific conversation ID.
My hunting got me to this issue post on Github:
In the 3rd post by inmarktech he mentions the below code:
var params = BotChat.queryParams(location.search);
var my_token = params['my_token'];
var botConnection = new BotChat.DirectLine({
secret: 'DIRECTLINE_SECRET'
});
BotChat.App({
botConnection: botConnection
,user: { id: 'USER_ID', name: 'User' } // user.id auto updates after first user message
}, document.getElementById("bot"));
botConnection.connectionStatus$.subscribe(function (status) {
if (status == 2) { // wait for connection is 'OnLine' to send data to bot
var convID = botConnection.conversationId;
botConnection.postActivity({
from: { id: convID } // because first time user ID == conversation ID
,type: 'event'
,name: 'registerUserData' // event name as we need
,value: my_token // data attached to event
}).subscribe(function (activityId) {
// This subscription is a MUST
// If I remove this handler the postActivity not reaches the bot
});
}
});
as you can see he is subscribing to botConnection.connectionStatus$ and as and when the status property is equal to 2(Online) you can then fetch the Conversation ID from the botConnection object.
Hope that helps :)

GoogleCalendarAPI accept/decline event

I am working on GoogleCalendar API and using node.js as a platform to build my application.
I am able to create events using authentication procedure and creating the calendar event using the access token generated while authenticating.
My question is that suppose if we have any attendee in the event and I want to accept/decline the event using the calendar API from the attendee's side, how can we do that?
I have tried fetching the calendar event of the attendee and matching it with the iCalUID of the event which was originally created and then modifying the event using update event on the attendee's calendar.
Event creator or owner cannot modify the response of attendees. Only attendees can modify their status.
To update the status on the side of the user, You may use the Event.update API and provide value for 'attendees.responseStatus'. Attendee's response status has 4 (four) possible value (described below).
'needsAction' - has not responded to the invitation.
'declined' - has declined the invitation.
'tentative' - has tentatively accepted the invitation
'accepted' - has accepted the invitation.
In addition to this, You can use the word "primary" as value for the calendar id to represent the currently logged in user
CalendarId: Calendar identifier. To retrieve calendar IDs call the calendarList.list method. If you want to access the primary calendar of the currently logged in user, use the "primary" keyword. (string).
For the id, you need to use the "id" returned by the Events.list API not the "iCalUID". Those two are different as described here.
Other fields that you need to provide are the email (of the attendee), startdate and enddate.
For more information, you may view the official documentation, link below:
https://developers.google.com/google-apps/calendar/v3/reference/events
Here is an example in java, using PATCH. Create an event object with the just the information you want to change, in this case the attendee and the response status. This code is running as the attendee.
final Event event = new Event()
.setAttendees(Arrays.asList(new EventAttendee().setEmail(email)
.setResponseStatus("declined")));
try
getCalendarService(googleAccountCredential).events()
.patch(CALENDAR_PRIMARY, calendarEventId, event)
.setSendNotifications(true)
.setOauthToken(googleAccountCredential.getToken()).execute();
return true;
} catch (final Exception ex) {
...
return false;
}
}
Like Android Enthusiast discussed, only the attendee can update his or her calendar from the attendee's side. You should also check the documentation as he suggested. The answer below is a working example for node.js and python
To update the event, you need to have the eventId and the user email. Get the event from the calendar(with the eventID),
loop through all the attendees, change responseStatus for
that particular attendee and then update the google calendar
For node js using the google api
const { google } = require('googleapis');
const calendar = google.calendar({ version: 'v3', auth: 'YOUR-API-KEY-HERE' });
#get the event to be updated
let theEvent = calendar.events.get({ calendarId: 'primary', eventId: eventId })
#loop through the whole attendee
for (let i = 0, i < theEvent['atendees'].length; i++){
if (theEvent['atendees'][i]['email'] === userEmail){
theEvent['atendees'][i]['responseStatus'] = 'accepted'
}
}
#update the google event
calendar.events.update({ calendarId: 'primary', eventId: theEventId, body: theEvent}, function(err, event) {
  if (err) {
    console.log('there was an error');
    return;
  }
  console.log('Event updated');
});
For python using googleapiclient
from googleapiclient.discovery import build
calendar = build('calendar', 'v3', credentials=credential)
event = calendar.events().get(calendarId='primary', eventId='eventId').execute()
For attendee in event['atendees']:
if atendee['email'] == user_email:
attendee['responseStatus'] = 'accepted'
break
#update the google event
udated_event = calendar.events().update(calendarId='primary', eventId=eventId, body=event).execute()
Lets suppose that you already have the event payload with the attendees key, then you need to get the ID for the created event:
created_event = gcal_service.events().insert(
calendarId='primary', body=event_payload
).execute()
then copy the attendees in a new object
accepted_attendees = {}
accepted_attendees['attendees'] = event_payload['attendees'].copy()
and now what you need to do is submit a patch to the attendees calendar based on the event_id, like this:
for attendee in event_payload['attendees']:
attendee_pos = accepted_attendees['attendees'].index(attendee)
accepted_attendees['attendees'].pop(attendee_pos)
accepted_attendees['attendees'].append({
'email': attendee['email'],
'self': True,
'responseStatus': 'accepted',
'additionalGuests': 0,
})
gcal_service.events().patch(
calendarId='primary',
eventId=created_event['id'],
body=accepted_attendees
)
And that's all, all the other attendees, now have accepted the event, hope it helps.
To respond, you need to get the event with the same event id from the attendee's calendar and then perform a patch or an update operation changing the response status of this attendee from needsAction to accepted / declined.
A bit of documentation on how events are copied between attendees and organizers:
https://developers.google.com/google-apps/calendar/concepts/sharing
Here is an example in python for Google Calendar Api v3. You can either use update or patch. Both of them are working.
all_attendees = event['attendees']
event['attendees'] = [{
'email': 'you#example.com',
'self': True,
'responseStatus': 'accepted',
'additionalGuests': 0,
}]
updated_event = service.events().patch(calendarId=calendar_id, eventId=event_id, body=event).execute()
Have fun

Resources