Handling a booking

This tutorial is intended for developers who are involved in the development of (Planning-)software that is responsible for responding to bookings by creating and executing trips that are a result of the booking. Creating a booking is described in tutorial Creating a booking

In the previous tutorial, we saw how easy it is to create a new booking. In this tutorial we discuss how the booking should be handled. Only Transporters are allowed to accept or reject a booking. Accepting or rejecting can be done by calling the Actions Booking.Accept and Booking.Reject respectively. There is no body to create or a need for content-type. Just execute a POST request to a specific URI and that’s it.

When a booking is accepted, its StatusName changes from "New" to "Confirmed" and one or more Trips are created, having at least one Movement.

When a booking is rejected, its StatusName changes from "New" to "Declined" and no Trips are created. This is a final state, nothing can be done anymore to the booking.

Let's move on and POST the following function to accept the booking

/Bookings(3220)/Accept

The server responds with a result 200 (OK) and includes in the body the newly created trip. The trip is in the form of an object of type TripSummary. Summary-types contain the fields of the related entity plus several extra fields of related child entities, so you don't need to perform extra requests.

{
  "odata.metadata" : "https://www.cabmanonline.nl/CCPService/DataServiceCCP.svc/$metadata#TripsSummary/@Element",
  "TripId" : 438778,
  "PlannedStartingTime" : "2014-12-04T10:59:50.387",
  "PlannedEndingTime" : "2014-12-04T10:59:50.387",
  "StartingTime" : null,
  "EndingTime" : null,
  "RouteNumber" : null,
  "Description" : "",
  "PlannedDistance" : 0.0,
  "TravelerCount" : 1,
  "WheelchairCount" : 0,
  "AttendantCount" : 0,
  "StatusName" : "Planned",
  "DayCode" : "E0001",
  "FirstMovementId" : 1126813,
  "LastMovementId" : 1126813,
  "LicensePlate" : null,
  "FirstLocationDescr" : null,
  "LastLocationDescr" : null,
  "Remark" : null,
  "Reference" : null,
  "FlightNumberPickUp" : null,
  "FlightNumberDropOff" : null,
  "PaymentTypeName" : null,
  "VehicleKindName" : "Taxi",
  "CostCenterDescription" : null,
  "DatacommunicationTripId" : null,
  "VehicleNr" : null,
  "VehicleUnitName" : null,
  "InternalTripNumber" : null,
  "StartOdoMeter" : null,
  "EndOdoMeter" : null,
  "AmountCash" : null,
  "AmountBill" : null,
  "LastUpdated" : "2014-12-04T10:03:38.323",
  "CalcTravelerCount" : 1,
  "CalcAbsenceCount" : 0,
  "TransporterName" : "Taxi TEST",
  "AssignedTransporterName" : null,
  "DriverName" : "-",
  "ShiftId" : null,
  "VehicleId" : null,
  "DriverId" : null,
  "ContractorId" : null,
  "BookingId" : 3220,
  "TransporterId" : 1,
  "AssignedTransporterId" : null,
  "FirstLocationId" : null,
  "LastLocationId" : null,
  "PreviousTripId" : null,
  "ReturnTripId" : null,
  "VehicleTypeId" : null,
  "Deleted" : false,
  "FirstAddress" : {
    "Street" : "Wilhelminapark",
    "Number" : 36,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : "TILBURG",
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : 51.565360000000005,
      "Longitude" : 5.0777600000000005,
      "FormatName" : "GeoDecimal"
    }
  },
  "LastAddress" : {
    "Street" : null,
    "Number" : null,
    "Addition" : null,
    "PostalCode" : null,
    "State" : null,
    "Town" : null,
    "CountryId" : null,
    "Coordinates" : {
      "Latitude" : null,
      "Longitude" : null,
      "FormatName" : null
    }
  }
}

What happens next?

  • The Trip(s) that are now created can be handled by the planning team, that can specify a Driver and a Vehicle for the Trip.
  • Once editing to the Trip is complete, it can be sent to the dataterminal to be executed.
  • The vehicle starts to move to the pickup location of the Trip. It's StateName should change to "InTransit".
  • When the vehicle arrives at the pickup location and travelers are picked up, and the Trip has been started on the dataterminal, it's StateName should change from "Planned" to "Active". The StatusName of the underlying Movements should change as well, from "Pending" to "Active" (traveler picked up) or to "NoShow" (traveler not present at the pickup location).
  • When the traveler(s) is/are transported to their destination, the StatusName of their Movement(s) should change to "DroppedOff". When no more Movements are pending, the Trip can be closed as well, by changing it's StatusName to "Done".

All this information about Trips and Movements can be used to provide feedback to the booking software. Especially the "InTransit" can be useful, as an indication to the waiting traveler that the vehicle is on its way

Canceling or modifying a booking

It can happen that a booking has to be cancelled or details have to be changed on the booking, after the booking has been created. The following rules apply:

  • As long as the StatusName of the booking is "New", the booking can be updated of cancelled without further consequences.
    The software that is used to let the user Accept or Reject the booking should always present a refreshed booking, so the possible changes are reflected as well.
  • Bookings that have the StatusName "Declined" cannot be updated nor cancelled.
  • Bookings that have the StatusName "Confirmed" can only be updated or cancelled when there are no conflicting TripRules, which is explained in detail below.
  • It is not allowed to modify the StatusName-field of the Booking. The StatusName changes automatically as a result of the Actions that will be executed.

Trip rules

Bookings that are already accepted (StatusName = "Confirmed") can only be cancelled or updated if none of the possible TripRules are violated. There are separate rules for cancelling or updating. You can edit and create TripRules using Cabman Online or using the OData-interface of CCP. A POST to create a typical TripRule could look like this:

POST /CCPService/DataServiceCCP.svc/TripRules HTTP/1.1
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 17.0
MaxCabmanCloudPlatformVersion: 17.0
{
  "TripRuleTypeName": "Cancel",
  "TripRuleConstraintName": "TimeBeforePlannedTime",
  "DayOfWeekName": null,
  "Time": "36000000000",
}

TripRuleTypeName could either be "Cancel" or "Update".

TripRuleConstraintName could be one of the following values:

  • "FixedTime": Cancelling or updating is only allowed before a the given time of day
  • "TimeBeforePlannedTime": Cancelling or updating is only allowed before the given time before the planned start of the Trip

DayOfWeekName could either be null (rule applies to all days in the week) or one of the following values: "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" or "Saturday". This gives you the opportunity to specify different rules for each day of the week.

Time is expressed in ticks (one tick = 100ns). The value 36000000000 x 100ns = 3600000000μs = 3600000ms = 3600s = 1 hour.

When an accepted Booking is cancelled (and no TripRules are being violated), its StatusName changes to "Cancelled", and it's related Trip(s) are cancelled, by setting their StatusName to "Canceled" (note: one "l" here!). When trips are synchronized (Trips where CCP is responsible for datacommunication to Cabman Terminals), they are withdrawn from the terminal.

When an accepted Booking is updated (and no TripRules are being violated), its StatusName changes to "New", and it's related Trips(s) are cancelled as well. This is because the Transporter, that has to perform the Trip, should validate and accept (or reject) the Booking again.

Synchronization

The Transporter that handles the Booking should be informed about new bookings or changes in existing bookings. The best way to achieve this, is using synchronisation, described in the guide Synchronisation. By periodically checking/syncing the entitysets Movements, Trips, Bookings, Travelers, etc. the software keeps up-to-date about new, changed or deleted entities.

Using email notification

Using Cabman Online the Transporter can perform a number of Email Settings, which also has the option to receive email on new or changed bookings. These setting can also be performed by the OData interface, using the generic Settings and SettingValues entitysets.

To get an overview of all possible settings you could perform the following GET request:

GET /CCPService/DataServiceCCP.svc/Settings HTTP/1.1
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 17.0
MaxCabmanCloudPlatformVersion: 17.0
The response should look like:
{
  "odata.metadata": "http://localhost/CCPService/DataServiceCCP.svc/$metadata#Settings",
  "value": [
    ::
    {
      "SettingId": 7,
      "Name": "DefaultLanguage",
      "SettingTypeName": "String",
      "DefaultValue": "nl",
      "EntitySetId": 1,
      "Deleted": false,
      "LastUpdated": "2015-09-10T11:32:23.813"
    },
    ::
    {
      "SettingId": 11,
      "Name": "NotifyNewOrChangedBooking",
      "SettingTypeName": "String",
      "DefaultValue": "-",
      "EntitySetId": 32,
      "Deleted": false,
      "LastUpdated": "2015-09-10T11:32:23.813"
    },
    ::
  ]
}

We are particulary interested in SettingId 7, DefaultLanguage and SettingId 11, NotifyNewOrChangedBooking at which we are going to create a SettingValue.

First we need to check if there is already an existing setting value:

GET /SettingValues?$filter=SettingId eq 11&$select=SettingValueId HTTP/1.1
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Accept: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 17.0
MaxCabmanCloudPlatformVersion: 17.0

Result when the setting could be found:

{
  "odata.metadata": "http://localhost/CCPService/DataServiceCCP.svc/$metadata#SettingValues&$select=SettingValueId",
  "value": [
    {
      "SettingValueId": 34
    }
  ]
}

When no SettingValue could be found, the "value"-array will be empty. Now you can use a MERGE (to modify the existing SettingValue) or a POST (to create a new SettingValue). In the next example we are modifying the existing SettingValue (having SettingValueId = 34, obtained from the previous request), by sending a MERGE-request:

MERGE /CCPService/DataServiceCCP.svc/SettingValues(34) HTTP/1.1
Authorization: Basic dGVzdEB0ZXN0LmNvbTp0ZXN0MTIz
Content-Type: application/json
DataServiceVersion: 3.0
MaxDataServiceVersion: 3.0
CabmanCloudPlatformVersion: 17.0
MaxCabmanCloudPlatformVersion: 17.0

having the following body:

{
  "Value": "user@euphoria-it.nl"
}

Using the same kind of requests we can also set SettingId 7 DefaultLanguage, to specify in which language we want to receive the emails (currently: "ne", "en" or "de").

Now when a new booking is created or when an existing booking has been updated, the user will receive an email containing details about the created/updated booking.