Thngs, Collections, and Actions
This is the key section for setting up the supply chain products and getting data from them into the EVRYTHNG Platform. The Thngs represent the bottles, collection resources represent cases of bottles, and actions of various types record progress from factory to store as a series of discrete events.
Create Some Thngs
Each bottle of beer manufactured in the factory is given its own Thng resource, which is the principal data type within the EVRYTHNG Platform. Thngs represent single instances of an object class and are created with a link to the product resource representing their SKU. Create your first Thng that references the “Honeysuckle Zombie Brown Ale” product you created earlier by making a POST /thngs
request.
Like the product creation request, this one must be scoped to the correct project by using the project
query parameter. This time, the request must be made with the Application User API Key because the user associated with the key was the original creator of the bottle.
Substitutions: :projectId
, :productId
curl -H "Content-Type: application/json" \
-H "Authorization: $APPLICATION_USER_API_KEY" \
-X POST 'https://api.evrythng.com/thngs?project=:projectId' \
-d '{
"name": "HZBA #43897",
"description": "A single bottle of Honeysuckle Zombie Brown Ale",
"product": ":productId"
}'
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "UmFSm6PkVDPw9pRwRECx4cVa",
"createdAt": 1501666536785,
"updatedAt": 1501666536785,
"name": "HZBA #43897",
"description": "A single bottle of Honeysuckle Zombie Brown Ale",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb"
}
Finally, create a few more Thngs for bottles of beer to model a case, such as a case of six bottles. Increment the number in each name
value to make them unique.
Create a Case of Beer
Now that you've created multiple Thngs, the next step is to model a grouping of Thngs into a collection resource. The collection represents a case of beer that is shipped from the factory to the store. First, create a collection resource in the context of the project as the Application User by using a POST /collections
request:
Substitutions: :projectId
curl -H "Content-Type: application/json" \
-H "Authorization: $APPLICATION_USER_API_KEY" \
-X POST 'https://api.evrythng.com/collections?project=:projectId' \
-d '{
"name": "HZBA Case #7316",
"description": "A case of six HZBA beer bottles"
}'
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "UG2SGPSbqm8hhqRwwhaNMqke",
"createdAt": 1501666833381,
"updatedAt": 1501666833381,
"name": "HZBA Case #7316",
"description": "A case of six HZBA beer bottles"
}
Next, add the Thngs representing the individual bottles to the collection—the grouping of bottles inside the case. This is done by making a POST /collections/:collectionId/thngs
request in which you supply the IDs of the Thngs to add in the request body.
Substitutions: :collectionId
, multiple :thngId
s
curl -H "Content-Type: application/json" \
-H "Authorization: $APPLICATION_USER_API_KEY" \
-X PUT 'https://api.evrythng.com/collections/:collectionId/thngs' \
-d '[
":thng1Id", ":thng2Id", ":thng3Id",
":thng4Id", ":thng5Id", ":thng6Id"
]'
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "UG2SGPSbqm8hhqRwwhaNMqke",
"createdAt": 1501666833381,
"scopes": {
"users": [
"U3EQA4aDeXPwQpwRagPwMMhb"
],
"projects": [
"UGhQAe3eeXswQ5wwagWUNKaq"
]
},
"updatedAt": 1501666833381,
"name": "HZBA Case #7316",
"description": "A case of six HZBA beer bottles"
}
You can verify that the Thngs were added correctly using a GET /collections/:collectionId/thngs
request.
Substitutions: :collectionId
curl -H "Authorization: $APPLICATION_USER_API_KEY" \
-X GET 'https://api.evrythng.com/collections/:collectionId/thngs'
Each Thng now lists the collection ID as an item in its collections
field, indicating the list of collections that Thng belongs to. (Note that Thngs can belong to multiple collections.) The response has been edited here for brevity.
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "Um2bmrby6GshhqRwwhManKgd",
"name": "HZBA #43902",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
},
{
"id": "UmFx37wreMPN9NaawXeFXcMq",
"name": "HZBA #43901",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
},
{
"id": "U3FbmrRXqQteEPRawDbyVnyk",
"name": "HZBA #43900",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
},
{
"id": "UGkb3MQtVXsatpwawDRe3b6g",
"name": "HZBA #43899",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
},
{
"id": "Um2SmMtmVgsaQpaaRhfx4gHa",
"name": "HZBA #43898",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
},
{
"id": "UmFSm6PkVDPw9pRwRECx4cVa",
"name": "HZBA #43897",
"product": "U3EtU2k3BD8wQpwwR6EMXgKb",
"collections": [ "UG2SGPSbqm8hhqRwwhaNMqke" ]
}
]
Create Actions
With the data model detailing the products being shipped, we can begin to model their movements from factory to shop floor.
Actions are the Platform resource models for simple notifications and provide a lasting record of discrete events that can occur. They can also contain custom data fields to drive business logic. In our supply chain scenario, an action is created in a variety of situations to provide a history of locations, inventory scans, and other activities it's involved in.
Each action must have a type to describe its class. This is similar to the relationship between Thngs and products, but it's mandatory here. There are also four built-in action types, which you can view by making a GET /actions
request using the Operator API Key.
Create new action types to represent the case’s movements at various stages of the supply chain. You can draw inspiration for the names of these new action types from the GS1 Core Business Vocabulary Standard (CBVS). When creating new action types, the name must start with an underscore. The first action type is _Active
, which indicates an object has been introduced into the supply chain.
Substitutions: :projectId
curl -H "Content-Type: application/json" \
-H "Authorization: $OPERATOR_API_KEY" \
-X POST 'https://api.evrythng.com/actions?project=:projectId' \
-d '{
"name": "_Active"
}'
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "UGFbsEH5qm8EEMwawDNnBk5s",
"createdAt": 1501685675342,
"updatedAt": 1501685675342,
"name": "_Active"
}
Now that the action type is in place, it can be used to create actions of that type. For the collection representing the case of beer, the first action in its simulated life cycle will be of this newly created _Action
type. The action itself is created with the Application User’s API Key to represent the user who created the case. Perform a GET /collections
request if you don't know the case’s ID.
Substitutions: :projectId
, :collectionId
curl -H "Content-Type: application/json" \
-H "Authorization: $APPLICATION_USER_API_KEY" \
-X POST 'https://api.evrythng.com/actions/_Active?project=:projectId' \
-d '{
"type": "_Active",
"collection": ":collectionId"
}'
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "UGkSQwKcWc9n9Qwawg7Nmh7e",
"createdAt": 1501687986689,
"timestamp": 1501687986689,
"type": "_Active",
"user": "U3EQA4aDeXPwQpwRagPwMMhb",
"location": {
"latitude": 51.45,
"longitude": 0.2167,
"position": {
"type": "Point",
"coordinates": [
0.2167,
51.45
]
}
},
"locationSource": "geoIp",
"context": {
"city": "Dartford",
"region": "England",
"countryCode": "GB",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
"timeZone": "Europe/London"
},
"createdByProject": "UGhQAe3eeXswQ5wwagWUNKaq",
"createdByApp": "UGE9dWaxBXsRtKRwaDdWnepa",
"collection": "UG2SGPSbqm8hhqRwwhaNMqke"
}
And so the case has begun its journey. In a real integration, supply chain staff members would use the mobile app to scan and track the case as it enters and leaves warehouses, ships on trucks and freight trains, and finally arrives on the shop floor. These activities can be represented by the additional action types:
_In_Transit
- Item is being shipped between two location._Encoded
- An instance-level identifier has been written to a barcode or RFID (radio frequency identification) tag._Container_Closed
- Item has been loaded onto a container._Sellable_Accessible
- Item can be sold as-is to an end customer._Expired
- Item is past its expiration date._Destroyed
- Item has been fully rendered unusable.
The diagram below illustrates when each of these action types could occur in a real-life supply chain scenario. At each stage, the actions themselves contain additional data in their customFields
property about the case’s current location, which can be used to build a map of the case’s journey across the world.
Example Implementation
The Python script below demonstrates the creation of all these actions with historical timestamps to illustrate one case’s entire journey. After each action, the timestamp is wound back to the previous step.
You can run this script yourself to see it in action, but you must replace the Application User API Key and place resource IDs with those you have created thus far.
import requests
import time
APPLICATION_USER_API_KEY = '1FGqgM8xr13gg4Ay9lukS4H3EwK1dP1x2ds67s7d...'
PROJECT_ID = 'UGhQAe3eeXswQ5wwagWUNKaq'
COLLECTION_ID = 'UGFkECmDVD8wQKaaaEgF4Een'
# Place IDs
FACTORY = 'UmkFemTdVXPa9Kwwwg4Nqm4q'
WAREHOUSE = 'Um2FVGQGegsat5waagH7QtGg'
SHIPPING_CENTER = 'UGFFV4Gqeg8a9KwRwYKcxfCq'
DISTRIBUTION_CENTER = 'UGFFBK8cBD8RtKRRwESbXsNh'
RETAILER_WAREHOUSE = 'U3F2BMCTegsRQpwwwkAdTnGm'
RETAILER_STORE = 'UG2Fe6PSBD8RtpwwwFdEecRt'
DISPOSAL_CENTER = 'UG22BNfWegsRQpRRwE3Qqr5a'
g_timestamp = int(round(time.time() * 1000))
def create_action(action_type, place_id):
action = {
'type': action_type,
'collection': COLLECTION_ID,
'locationSource': 'place',
'location': { 'place': place_id },
'timestamp': g_timestamp
}
headers = {
'Content-Type': 'application/json',
'Authorization': APPLICATION_USER_API_KEY
}
url = 'https://api.evrythng.com/actions/{}?project={}'.format(action_type, PROJECT_ID)
print(requests.post(url, json=action, headers=headers).text)
def subtract_days(days):
global g_timestamp
g_timestamp -= (1000 * 60 * 60 * 24 * days)
def create_supply_chain_history():
create_action('_Destroyed', DISPOSAL_CENTER)
subtract_days(1)
create_action('_Expired', RETAILER_STORE)
subtract_days(14)
create_action('_Sellable_Accessible', RETAILER_STORE)
subtract_days(1)
create_action('_In_Transit', RETAILER_WAREHOUSE)
subtract_days(4)
create_action('_In_Transit', DISTRIBUTION_CENTER)
subtract_days(2)
create_action('_In_Transit', SHIPPING_CENTER)
subtract_days(1)
create_action('_Container_Closed', WAREHOUSE)
subtract_days(1)
create_action('_Encoded', WAREHOUSE)
subtract_days(1)
create_action('_In_Transit', FACTORY)
subtract_days(1)
create_action('_Active', FACTORY)
if __name__ == '__main__':
create_supply_chain_history()
In the real world, staff could create each of these actions in the inventory management mobile app whenever they see a case enter or leave a location, notice that it's expired, or when it's been destroyed. This could be done by manual entry or by using the Identifier Recognition feature of the EVRYTHNG Platform to scan each case for its collection ID and then create the actions with a Reactor script.
In the next section, you'll explore two other major features of the Platform that can enhance the tagged products use-case: roles and permissions, and data visualization on the EVRYTHNG Dashboard.
Updated almost 2 years ago