Skip to content

Pick-Drop Food Delivery

Business Scenario

In Singapore, kStore offers home appliance installation and repair services. There are four types of service orders for different products:

  • AC
  • TV
  • Kitchen (Appliances)
  • Plumber (Services)

There are also three different customer types:

  • KA (Key Accounts)
  • SMB (Small Medium Businesses)
  • Individual

Each worker (or technician, engineer) can have different combination of those service skills and may have access to different types of customers.

The orders are sent to Kandbox Dispatch platform via APIs continuously and dispatched to workers once a day.

The main difference between this guide and others is that we have to deal with complex business rules like "Skills", "Service Time Window", "Service Duration". Also the planning window may span accross multiple days.

All codes in this example can be found in repository: KDClient . The main programs/scripts for this example are:

  • python demo
  • HTTP scripts

Prerequisite

  1. You should already have a Kandbox Dispatch Account. If not, please go to kandbox dispatch to register one account.
  2. To run the sample python program, you should have python installed in your environment. We are building the client example by Python and Restful APIs. VSCode is our preferred IDE. The HTTP scripts are coded in rest-client format.

Execute the Example Python Program in one go

The first step is to clone the repository to your local computer:

sh
git clone https://github.com/li-jinbao/kdclient

Then install the required packages to your python environment:

sh
cd kdclient
pip install -r requirements.txt

Configure the required environment variables with the information you have registered to Kandbox Dispatch:

sh
# export server_url=http://_server_url_/kd/api/v1
# export email=_email_@email.com
# export password=_pass_
# export team_code=_team_

Then you can run the sg_multi_days demo program by:

sh
python ./src/sg_multi_days.py

After the program finish, you should be able to view the planned route in the Web UI.

Run APIs Step by Step

The previous Python program executes several steps in one go. To learn and test the APIs one by one, you can follow steps in this section. This sequence is also the sequence of API being used in the python program.

Create a team

To use the restful APIs, you should first use the Login API to get a token for each sub-sequent API calls.

http

POST {{hostUrl}}/auth/login
Content-Type: application/json

{"email": "{{user}}", "password": "{{pass}}"}
python
    url = f"{self.service_url}/auth/login"
    login_info = {"email": username, "password": password}

    response = self.requests.post(
        url, json=login_info, headers={"Content-Type": "application/json"}, 
    )

With the token, we will create our team at Singapore with this API call:

http

POST {{server_url}}/teams/
Authorization: Bearer {{jwtoken}}
Content-Type: application/json

{
    "code": "sgmd", "name": "sgmd", 
    "geo_longitude": 103.8, 
    "geo_latitude": 1.3, 
    "planner_service": {"code": "single"}, 
    "flex_form_data": {
        "fixed_horizon_flag": "1", 
        "env_start_datetime": "2023-11-13T00:00:00",
        "horizon_start_datetime": "2023-11-13T08:01:01",
        "nbr_minutes_planning_windows_duration": 1440,

        "enable_skills": "1",
    }
}

Here "enable_skills" will enable the skill check for the planning engine. By default it is disabled.

Create Workers

We can now create two workers with different skill sets one by one with this API call:

http

POST {{server_url}}/workers/   HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{jwtoken}}

{
    "code": "w1", "name": "w1",
    "team": {"code": "{{team_code}}"}, 
    "geo_longitude": 103.835, 
    "geo_latitude": 1.303, 
    "flex_form_data": {"area_code": "A", "capacity_volume": 0, "max_nbr_order": 0}, 
    "business_hour": {
        "monday": [{"open": "0005", "close": "2330", "id": "a0", "isOpen": true}], 
        "tuesday": [{"open": "0005", "close": "2330", "id": "a1", "isOpen": true}], 
        "wednesday": [{"open": "0005", "close": "2330", "id": "a2", "isOpen": true}], 
        "thursday": [{"open": "0005", "close": "2330", "id": "a3", "isOpen": true}], 
        "friday": [{"open": "0005", "close": "2330", "id": "a4", "isOpen": true}], 
        "saturday": [{"open": "0005", "close": "2330", "id": "a5", "isOpen": true}], 
        "sunday": [{"open": "0005", "close": "2330", "id": "a6", "isOpen": true}]
        }, 
    "auto_planning": true,
    "is_active": true
}

Then you can create the second worker by repeating previous call after replacing skills by "Kitchen" in the JSON body:

json
{
    "code": "w2", "name": "w2",
    "flex_form_data": {
        "skills":"Kitchen"
    }
}

Reset Planning Window

After we have created the workers, we should reset the planning window to initialize the planning engine.

http
POST {{server_url}}/planner_service/reset_planning_window/   HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{jwtoken}}

{"team_code":  "{{team_code}}"}

If resetting is successful, you should be able to view gantt chart with worker's empty working slot.

Create Jobs

Now we can add jobs to this team. In this guide, we should store the job and dispatch them in one batch. This is done by setting auto_planning=false.

We can create the jobs one by one with this API call:

http

###
# @name create_job_1

POST {{server_url}}/jobs/  HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{jwtoken}}


{
    "code":"A1-0",
    "team": {"code": "{{team_code}}"} ,
    "job_type": "visit",
    "auto_planning": false,
    "is_active": true,
    "planning_status": "U",
    "geo_longitude": 103.833, 
    "geo_latitude": 1.301, 
    "requested_start_datetime": "2023-11-13T07:06:40",
    "requested_duration_minutes": 0.5,
    "flex_form_data": {
        "area_code": "A",
        "skills":"TV"
    }
}

Then you can create the second worker by repeating previous call after replacing skills by "Kitchen" in the JSON body:

json
{
    "code":"A2-0",
    "geo_longitude": 103.833, 
    "geo_latitude": 1.315, 
    "flex_form_data": {
        "skills":"Kitchen"
    }
}

Run Batch Planning

Since we are using "auto_planning"=false, the order should not be planned after creation. It will wait till run_batch_optimizer call.

After you have created all the jobs, you can initiate the planning action by this API.

http
POST {{server_url}}/planner_service/run_batch_optimizer/   HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{jwtoken}}

{
    "team_code": "{{team_code}}"
}

Read the planning result

You can use this api to get the planned jobs for any worker.

http
POST {{server_url}}/planner_service/get_env_jobs/  HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{jwtoken}}

{
    "team_code": "{{team_code}}", 
    "reset_start_datetime": false,
    "active_only":false
}

You should see a result json like the following:

json

{
  "w2": [
    {
      "code": "A2-0",
      "scheduled_start_datetime": "2023-11-13T08:03:00",
      "prev_travel": 0.0,
      "scheduled_duration_minutes": 0.0
    }
  ],
  "w1": [
    {
      "code": "A1-0",
      "scheduled_start_datetime": "2023-11-13T08:03:00",
      "prev_travel": 2.0,
      "scheduled_duration_minutes": 0.0
    }
  ]
}

In this result job "A1-0" is assigned to worker "W1" and job "A2-0" is assigned to worker "W2". This was determined by the skills setting during the creation.

Review the planned result in the Web UI

After the planning is done, you can login to the Kandbox Dispatch platform. You should be able to see a Singapore map with planned workers and jobs on the map.