Copyright © 2005-2020

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

Overview

This guide describes the RESTful API of the OpenWMS.org WMS Inventory Service module and its usage. Some general terms and definitions are explained and declared in the first part of the document whereas in the second part the usage of the API is shown in a more use-case-driven approach.

Representation Formats

Basically JSON is used as representation format by default if not otherwise requested or mentioned below. XML format is supported for the index pages as well, but must be requested explicitly. Furthermore a vendor specific JSON format is used to represent resource representations. Therefore it is absolutely required that the Accept header denotes the demanded type. Find the type in the examples below.

Dates in JSON

Date or datetime fields are not treated specially in JSON. Within the scope of this API a date or datetime field is always expected and rendered as JSON String in ISO8601 format with timezone information and milliseconds: yyyy-MM-dd’T’HH:mm:ss.SSSTZD.

Embedded Entities

For the sake of convenience some response entities may included embedded entities or even parts of it. A reference to the actual entity is provided as HAL link as well.

Error Responses

Beside the actual representation of resources, the server may result an error response in JSON format that contains basic information about the error occurred. This information is additional to the standard HTTP status code and may help clients to identify the error cause in order to re-phrase and send the request again.

Currently there are two types of errors with their own response formats.

Server Declined Errors

This kind of errors are sent by the server runtime environment even before the request had a chance to be processed.

An example response looks like this:

{
    "timestamp": 1512400854941,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "org.ameba.oauth2.InvalidTokenException",
    "message": "JWT expired at 2017-12-04T15:04:43Z. Current time: 2017-12-04T15:20:54Z, ...",
    "path": "/v1/transport-units?bk=00000000000000004711"
}

The JSON structure contains the following fields.

Property Name Description

timestamp

When the error occurred on server side

status

The http status of the error

error

A short error text

exception

Internal class name of the Java exception type

message

A more descriptive error text describing the error in detail

path

The part of the URI for the REST resource that was queried

API Declined Errors

Those errors are thrown within the REST API validation and processing logic. For example, if a client request does not match the expected format or has produced an error on server side, the API will decline the request and return a response with status client-side error (4xx).

The structure of the response is aligned to the RFC7808. An example response looks like this:

{
    "message": "LocationGroup with name [NOT_EXISTS] not found",
    "messageKey": "COMMON.LOCATION_GROUP_NOT_FOUND",
    "obj" : [ "NOT_EXISTS" ],
    "httpStatus" : "404",
    "class" : "String"
}

The JSON structure contains the following fields.

Property Name Description

message

A short error text

messageKey

An unique identifier across the API that can be used to identify and translate the error message on the client side

obj

An array of possible passed arguments to the message

httpStatus

The http status of the error

class

The arguments type

Following message keys are currently used:

Message Key Description Action

not.found

The requested resource has not been found

The resource does not exist anymore or has never existed. The resource identifier must be verified

COMMON.BARCODE_NOT_FOUND

The TransportUnit does not exist

Verify the identifying attribute passed to the API

COMMON.LOCATION_NOT_FOUND

The requested Location does not exist

Verify the identifying attribute passed to the API

COMMON.LOCATION_GROUP_NOT_FOUND

The requested LocationGroup does not exist

Verify the identifying attribute passed to the API

COMMON.TRANSPORT_UNIT_TYPE_NOT_FOUND

The requested TransportUnitType does not exist

Verify the identifying attribute passed to the API

Resources

A overview of all used API resources with a description of their attributes. More information about the resource itself and the exposed functionality is mentioned in the chapters below.

vo
Table 1. ProductVO
Attribute Description

pKey

Unique identifier of the Product

sku

The Product’s SKU

label

Could be an identifying barcode label or RFID tag

baseUnit

The default UOM the Product is managed, i.e. PC (Piece)

description

A descriptive text

classification

An arbitrary information to classify the Product, i.e. a hazardous product

group

An attribute to group products

stockZone

Where this Product should be stored in stock

units

Alternative UOMs this Product can be packaged, i.e. PC (Piece), DOZ (Dozen)

Table 2. PackagingUnitVO
Attribute Description

product

The Product the PackagingUnit belongs to

quantity

The quantity of the Product this PackagingUnit contains

length

Current length of the PackagingUnit

width

Current width of the PackagingUnit

height

Current height of the PackagingUnit

weight

Current weight of the PackagingUnit

message

Some informational text stored with the PackagingUnit

loadUnitDistribution

Describes how many of this kind of PackagingUnits fit into the current LoadUnit

Table 3. LoadUnitDistributionVO
Attribute Description

piecesPerLoadUnitLayer

How many PackagingUnits fit into one layer in a LoadUnit

numberOfLoadUnitLayers

How many layers of the PackagingUnit fit into a LoadUnit

Table 4. UnitTypeVO
Attribute Description

amount

The actual amount of the unit of measure (UOM)

unit

The unit of the UOM

Table 5. MessageVO
Attribute Description

messageNo

An identifying number of the Message that can be used for translation

messageText

A descriptive message text

Index

The initial HTTP request to retrieve information about all available resources looks like the following. The Index page is a public available resource and does not need any authentication.

GET /index HTTP/1.1
Host: localhost:8080

The Index resource is returned in the response body with the response status of 200-OK. This main Index lists all primary resource entities to follow next.

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 303

{
  "_links" : {
    "product-index" : {
      "href" : "http://localhost:8080/v1/products/index"
    },
    "load-unit-index" : {
      "href" : "http://localhost:8080/v1/load-units/index"
    },
    "packaging-unit-index" : {
      "href" : "http://localhost:8080/v1/packaging-units/index"
    }
  }
}

A client application only needs to know about the agreed link names and follow the corresponding href link to navigate to further resources.

LoadUnit

A LoadUnit is the thing that actually carries an amount of Products. Mostly this is done with the abstraction of PackagingUnits so that a LoadUnit carries multiple PackagingUnits where each of those contains an amount of a Product. A LoadUnit can either be only a virtual separation of a TransportUnit or even a physical box or separated area on top of the TransportUnit.

LoadUnit Index (load-unit-index)

The index with all possible operations on LoadUnits can be retrieved with an GET request:

GET /v1/load-units/index HTTP/1.1
Host: localhost:8080

The response lists all the operations with a name and the corresponding href link:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 293

{
  "_links" : {
    "lu-divide" : {
      "href" : "http://localhost:8080/v1/transport-unit?transportUnitBK=transportUnitBK&loadUnitType=loadUnitType&parts=4"
    },
    "lu-put-product" : {
      "href" : "http://localhost:8080/v1/load-units/b65a7658-c53c-4a81-8abb-75ab67783f47"
    }
  }
}

Divide a TransportUnit into multiple LoadUnits (load-units-divide)

An existing TransportUnit can be split or divided into multiple LoadUnits in the simplest way by just giving the number of LoadUnits to create on top of the TransportUnit and the type of LoadUnit.

POST /v1/transport-unit HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

transportUnitBK=4712&loadUnitType=EURO&parts=2

If the server has fulfilled the operation successfully the response is:

HTTP/1.1 201 Created

If the server could not process the request successfully because the TransportUnit is already divided into multiple LoadUnits the response states this:

HTTP/1.1 409 Conflict
Content-Type: application/hal+json
Content-Length: 175

{
  "message" : "The TransportUnit with the Barcode [4711] is already divided, try to add other LoadUnits instead",
  "messageKey" : "already.exists",
  "httpStatus" : "409"
}

Reserve a LoadUnit for a Product (lu-put-product)

LoadUnits may contain different types of Products or are limited to carry only one type of Product - this is a matter of configuration. To assign or reserve a LoadUnit to a specific type of Product a client can send a POST request to request this assignment. If the system is configured in the way that only same Products are allowed on LoadUnits, the server may reject the request in case the LoadUnit is already assigned or carries PackagingUnits of a different type.

A client request to ask for a Product assignment could look like this:

PUT /v1/load-units/1000 HTTP/1.1
Content-Type: application/json
Content-Length: 202
Host: localhost:8080

{
  "links" : [ ],
  "sku" : "ZS0002029",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  }
}

If the server has successfully assigned the Product to the LoadUnit the response looks like:

HTTP/1.1 200 OK

If the request could not been processed successfully because the server denied the assignment, the response looks like:

HTTP/1.1 502 Bad Gateway
Content-Type: application/hal+json
Content-Length: 142

{
  "message" : "The LoadUnit [1001] has already a Product [ZS0002029] assigned and cannot be loaded a second time.",
  "httpStatus" : "502"
}

PackagingUnit

A PackagingUnit describes an amount of a Product. This is a physical packet of an amount of an article, like screws or ar tires. Using PackagingUnits in a warehouse is always a benefit, even there is this extra resource that needs to be considered. A simple warehouse could also live with LoadUnits on TransportUnits but in most cases it makes sense to have an amount of PackagingUnits with a LoadUnit and perhaps multiple LoadUnits on top of the TransportUnit.

PackagingUnit Index (packaging-unit-index)

The index with all possible operations on PackagingUnits can be retrieved with an GET request:

GET /v1/packaging-units/index HTTP/1.1
Host: localhost:8080

The response lists all the operations with a name and the corresponding href link:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 475

{
  "_links" : {
    "packaging-units-create" : {
      "href" : "http://localhost:8080/v1/transport-units/transportUnitBK/load-units/luPos/packaging-units?loadUnitType=loadUnitType"
    },
    "packaging-units-findontu" : {
      "href" : "http://localhost:8080/v1/transport-units/transportUnitBK/load-units/packaging-units"
    },
    "packaging-units-findforproduct" : {
      "href" : "http://localhost:8080/v1/packaging-units?sku=sku&amount=amount&sort=sort"
    }
  }
}

Create a new PackagingUnit (packaging-units-create)

A PackagingUnit can be created and directly put into a LoadUnit of a TransportUnit. Therefor a client sends a POST request with the identifying information for the LoadUnit and the required PackagingUnit data to the server:

POST /v1/transport-units/4711/load-units/1/packaging-units?loadUnitType=EURO HTTP/1.1
Content-Type: application/json
Content-Length: 262
Host: localhost:8080

{
  "product" : {
    "links" : [ ],
    "sku" : "ZS0002029",
    "units" : [ ]
  },
  "quantity" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "links" : [ ]
}

If the server has created and attached the PackagingUnit successfully a 201-CREATED is returned with a LOCATION header to the new resource:

HTTP/1.1 201 Created
Location: http://localhost:8080/v1/transport-units/4711/load-units/1/packaging-units/e59b3e76-5b0b-4764-a879-02cc25a712b3/

Find all PackagingUnits on a TransportUnit (packaging-units-findontu)

To find all PackagingUnits in all LoadUnits that are currently assigned to a particular TransportUnit a client needs to query for the secondary resources of the TransportUnitLoadUnitPackagingUnit.

GET /v1/transport-units/4711/load-units/packaging-units HTTP/1.1
Host: localhost:8080

If the server has created and attached the PackagingUnit successfully a 201-CREATED is returned with a LOCATION header to the new resource:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1380

[ {
  "product" : {
    "links" : [ ],
    "pKey" : "2",
    "sku" : "ZS0002030",
    "baseUnit" : {
      "@class" : "org.openwms.core.units.api.Piece",
      "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
      "magnitude" : 1
    },
    "stockZone" : "A",
    "units" : [ ]
  },
  "quantity" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  },
  "links" : [ ],
  "length" : 15,
  "width" : 12,
  "height" : 10,
  "weight" : {
    "@class" : "org.openwms.core.units.api.Weight",
    "unitType" : [ "org.openwms.core.units.api.WeightUnit", "G" ],
    "magnitude" : 400
  }
}, {
  "product" : {
    "links" : [ ],
    "pKey" : "2",
    "sku" : "ZS0002030",
    "baseUnit" : {
      "@class" : "org.openwms.core.units.api.Piece",
      "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
      "magnitude" : 1
    },
    "stockZone" : "A",
    "units" : [ ]
  },
  "quantity" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  },
  "links" : [ ],
  "length" : 15,
  "width" : 12,
  "height" : 10,
  "weight" : {
    "@class" : "org.openwms.core.units.api.Weight",
    "unitType" : [ "org.openwms.core.units.api.WeightUnit", "G" ],
    "magnitude" : 400
  }
} ]

Find available PackagingUnits on a TransportUnit (packaging-units-findforproduct)

A client might want to find PackagingUnits on a TransportUnit that are available and not allocated by any other orders or tasks in order to reserve them for allocation. Therefore the client sends a GET request with the TransportUnit’s identifier and the Product SKU to search for. The client may also limit the number of returned values by giving an amount parameter and a sort direction.

The request to the server may look like this:

GET /v1/transport-units/4711/load-units/packaging-units?sku=ZS0002029&amount=10&sort=DESC HTTP/1.1
Host: localhost:8080

The server responds with a list of entities currently not blocked in allocation:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1380

[ {
  "product" : {
    "links" : [ ],
    "pKey" : "2",
    "sku" : "ZS0002030",
    "baseUnit" : {
      "@class" : "org.openwms.core.units.api.Piece",
      "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
      "magnitude" : 1
    },
    "stockZone" : "A",
    "units" : [ ]
  },
  "quantity" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  },
  "links" : [ ],
  "length" : 15,
  "width" : 12,
  "height" : 10,
  "weight" : {
    "@class" : "org.openwms.core.units.api.Weight",
    "unitType" : [ "org.openwms.core.units.api.WeightUnit", "G" ],
    "magnitude" : 400
  }
}, {
  "product" : {
    "links" : [ ],
    "pKey" : "2",
    "sku" : "ZS0002030",
    "baseUnit" : {
      "@class" : "org.openwms.core.units.api.Piece",
      "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
      "magnitude" : 1
    },
    "stockZone" : "A",
    "units" : [ ]
  },
  "quantity" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  },
  "links" : [ ],
  "length" : 15,
  "width" : 12,
  "height" : 10,
  "weight" : {
    "@class" : "org.openwms.core.units.api.Weight",
    "unitType" : [ "org.openwms.core.units.api.WeightUnit", "G" ],
    "magnitude" : 400
  }
} ]

Product

The Product is a representation of an article known and shipped within a warehouse. A Product is just a definition but not the physical article. In the real world a Product exists in a number of items, dimensions, liters or other units. Hence a Product is physically always tied to an amount, see PackagingUnits for more information.

Product Index (product-index)

The index with all possible operations can be retrieved with an GET request:

GET /v1/products/index HTTP/1.1
Host: localhost:8080

The response lists all the operations with a name and the corresponding href link:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 405

{
  "_links" : {
    "products-create" : {
      "href" : "http://localhost:8080/v1/products"
    },
    "products-findall" : {
      "href" : "http://localhost:8080/v1/products"
    },
    "products-findbysku" : {
      "href" : "http://localhost:8080/v1/products?sku=ZS0002029"
    },
    "products-findbyskuanduom" : {
      "href" : "http://localhost:8080/v1/products?sku=ZS0002029&uom=PC"
    }
  }
}

Find all Products (products-findall)

To retrieve a list of all existing Products a client must send a GET request to the server:

GET /v1/products HTTP/1.1
Host: localhost:8080

The response contains all existing Products:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 534

[ {
  "links" : [ ],
  "pKey" : "1",
  "sku" : "ZS0002029",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "stockZone" : "A",
  "units" : [ "1 PC", "1 DOZ" ]
}, {
  "links" : [ ],
  "pKey" : "2",
  "sku" : "ZS0002030",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "stockZone" : "A",
  "units" : [ ]
} ]

Find a Product by SKU (products-findbysku)

To find and return a Product identified by its SKU a client needs to query the resource by sku as query parameter in a GET request:

GET /v1/products?sku=ZS0002029 HTTP/1.1
Host: localhost:8080

The response contains either the Product if it exists:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 255

{
  "pKey" : "1",
  "sku" : "ZS0002029",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "stockZone" : "A",
  "units" : [ "1 PC", "1 DOZ" ]
}

or returns with a 404-NOT FOUND error response in case no Product with the given SKU exists:

HTTP/1.1 404 Not Found
Content-Type: application/hal+json
Content-Length: 115

{
  "message" : "Product with SKU [UNKNOWN] does not exist",
  "messageKey" : "not.found",
  "httpStatus" : "404"
}

Find a Product by SKU and UOM (products-findbyskuanduom)

A Product may exist in different measurement flavors. One Product with one SKU but packed in different packaging types. For example, car tires with the SKU = ZS0002029 may exist in packages of always one piece and additionally in packages with 3x4 pieces (a Dozen). A client could be interested in finding the correct Product by SKU and UOM by sending a GET request with both as request parameters:

GET /v1/products?sku=ZS0002029&uom=1+DOZ HTTP/1.1
Host: localhost:8080

If this Product with such an UOM exists, the server returns a 200-OK with the Product and all existing UOM relations.

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 255

{
  "pKey" : "1",
  "sku" : "ZS0002029",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "stockZone" : "A",
  "units" : [ "1 PC", "1 DOZ" ]
}

If the given UOM is not a valid one, like in this example Camels, the server responds with 404-NOT FOUND:

HTTP/1.1 404 Not Found

Create one or more Products (products-create)

New Products can be added to the catalog of existing Products by sending a POST request to the server with the details of each Product. Products are often created by the ERP system, indirectly through the appropriate ERP Hostconnector, and it is not unusual that this happens in a bulk operation with thousands of records at a time.

A request could look like this:

POST /v1/products HTTP/1.1
Content-Type: application/json
Content-Length: 241
Host: localhost:8080

[ {
  "links" : [ ],
  "sku" : "NEW_PRODUCT",
  "baseUnit" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1
  },
  "units" : [ "8 PC", "1 DOZ" ]
} ]

If the server has successfully created the demanded Product the response looks like:

HTTP/1.1 201 Created
Location: http://localhost:8080/v1/products/d041a779-cdeb-4b53-a6ef-7625d8a3473b/

Notice that the server sends back the LOCATION header to the created Product resource in case only one new Product has been requested. If multiple Products have been created, the LOCATION header is kept empty.

If the request could not been processed because of missing or invalid request data the server responds with:

HTTP/1.1 400 Bad Request
Content-Type: application/hal+json
Content-Length: 116

{
  "message" : "Error on validation [baseUnit]",
  "messageKey" : "core.validation.error",
  "httpStatus" : "400"
}

Allocate Products

A demanded amount of a Product can be reserved or allocated for picking or goods out. To do so, a client must call the allocation API to get back an array of objects that contain the TransportUnit, the LoadUnit and available quantity of the Product.

POST /allocation/transport-units HTTP/1.1
Content-Type: application/json
Content-Length: 183
Host: localhost:8080

{
  "sku" : "ZS0002030",
  "qty" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  }
}

The server responds with an array:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 270

[ {
  "transportUnitBK" : "00000000000000004711",
  "loadUnitPosition" : "1",
  "sku" : "ZS0002030",
  "qtyAllocated" : {
    "@class" : "org.openwms.core.units.api.Piece",
    "unitType" : [ "org.openwms.core.units.api.PieceUnit", "PC" ],
    "magnitude" : 1000
  }
} ]