Track and Trace Quick Start

The management of risk within a supply chain is crucial to control cost and ensure customer commitments are met. Visibility into supply chain activities and inventory handling is paramount in identifying risks and opportunities. For example, manufacturers may wish to identify patterns in shipping activities that have previously damaged goods. When supply chain logistics extended beyond a single organization to include shippers, retails, and other parties, this visibility can only be enabled through the sharing of data. Vendia unis provide a platform for sharing data and code with partners in real-time across geographies, cloud-accounts, and technology stacks.

In this Quick start, you will demonstrate how Vendia unis can easily be implemented to track and trace goods in a supply chain as they move from manufacturer to shipper and ultimately to retailers.

By the end of this Quick start you will have:

  • Created a universal Application (uni) with a pre-defined data-model
  • Defined a network of business partners and on-boarded them to the uni
  • Used the automatically generated HTTPS GraphQL API to interact with the uni
  • Quickly performed an analysis of supply chain data to answer business questions

Prerequisites

This Quick start uses the Vendia share-cli, a Command Line Interface (CLI) for creating and managing unis. We will be using the share-cli to deploy and manage our uni.

Command Line Installation

The share-cli can be installed using the nodeJS node Package Manager (NPM).

To install the CLI globally, run the following command:

npm install @vendia/share-cli -g

NOTE You can also install Vendia CLI inside a project (instead of globally).

For more information, please visit the @vendia/share-cli NPM package page or view the CLI commands list.

You will need to have a valid Vendia Share user in order to deploy this Quick start. Please sign up for Vendia Share if you have not already done so.

Step 1 - Prepare the Deployment Files for the uni

` In this Quick start, we have provided sample files including a data-model for your use that describes four collections of data:

TypeDescription
InventoryInventory records provide information about products including their Name, SKU, Location, and Quantity on Hand.
ShipmentShipment records reflect the movement of products to and from locations in the supply chain.
OrderOrder records reflect a purchase of inventory from the manufacturer that will require a shipment for delivery to a retailer.
WarehouseWarehouse records detail the locations along the supply chain where partners may store goods throughout the manufacturing, shipping, and receiving activities.

Save the Quick start files below

The registration.json, schema.json, and initial-state.json will be referenced by the CLI later. Please save these files locally by expanding each node and copying the contents.

The registration.json file defines the uni name, location of the schema, and the node configuration representing partners in the supply chain network. In this example, you will define a manufacturer, a shipper, and two retailers.

NOTE You will need to provide your Vendia Share userId when defining your nodes. For the quick start, please use your same userId across all nodes.

ANOTHER NOTE Pick a unique name for your uni - by default all unis share a common namespace so here is your chance to get creative.

Save this file as registration.json on your local workstation.

Registration file - save as registration.json
{
  "name": "test-track-and-trace",
  "schema": "schema.json",
  "initState": "initial-state.json",
  "nodes": [{
      "name": "manufacturer",
      "userId": "example@vendia.net",
      "region": "us-east-2"
    },
    {
      "name": "shipper",
      "userId": "example@vendia.net",
      "region": "us-east-2"
    },
    {
      "name": "retailer01",
      "userId": "example@vendia.net",
      "region": "us-west-2"
    },
    {
      "name": "retailer02",
      "userId": "example@vendia.net",
      "region": "us-west-2"
    }
  ]
}

The schema.json file defines the data model for the uni and is used to automatically generate the API that will be used later to interact with the system. This file defines the record types previously mentioned. Save it as schema.json on your local workstation in the same directory as the registration.json file you saved prior.

Schema file - save as schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://vendia.net/schemas/demos/track-and-trace.json",
  "title": "Track and Trace",
  "description": "Track and trace shipments among a manufacturer, shipper, and retailer",
  "type": "object",
  "properties": {
    "Inventory": {
      "description": "Inventory information",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "description": "Product name",
            "type": "string"
          },
          "sku": {
            "description": "Product SKU",
            "type": "string"
          },
          "warehouse": {
            "description": "Manufacturer warehouse code",
            "type": "string"
          },
          "availableInventory": {
            "description": "Available inventory",
            "type": "integer"
          }
        }
      }
    },
    "Shipment": {
      "description": "Shipment information",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "originWarehouse": {
            "description": "Shipment origin",
            "type": "string"
          },
          "destinationWarehouse": {
            "description": "Shipment destination",
            "type": "string"
          },
          "orderId": {
            "description": "Order ID",
            "type": "string"
          },
          "created": {
            "description": "When the order was placed",
            "type": "string",
            "format": "date-time"
          },
          "lastUpdated": {
            "description": "When the order was last updated",
            "type": "string",
            "format": "date-time"
          },
          "location": {
            "description": "Current Lat/long of the shipment",
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "uniqueItems": true
          },
          "delivered": {
            "description": "Delivery status",
            "type": "boolean"
          }
        }
      }
    },
    "Order": {
      "description": "Order information",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "orderId": {
            "description": "Order ID",
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sku": {
                  "description": "Product SKU in order",
                  "type": "string"
                },
                "quantity": {
                  "description": "Quantity of product in order",
                  "type": "integer"
                }
              }
            }
          },
          "manufacturerWarehouseCode": {
            "description": "Manufacturer warehouse that holds the inventory",
            "type": "string"
          },
          "manufacturerWarehouseLocation": {
            "description": "Lat/long of manufacturer warehouse",
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "uniqueItems": true
          },
          "retailerWarehouseCode": {
            "description": "Retailer warehouse that placed the order",
            "type": "string"
          },
          "retailerWarehouseLocation": {
            "description": "Lat/long of retailer warehouse",
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "uniqueItems": true
          },
          "delivered": {
            "description": "Order status",
            "type": "boolean"
          },
          "created": {
            "description": "When order was placed",
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "description": "When order was last updated",
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "Warehouse": {
      "description": "Warehouse information",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "companyName": {
            "description": "Company name",
            "type": "string"
          },
          "code": {
            "description": "Warehouse code",
            "type": "string"
          },
          "street1": {
            "description": "Warehouse Street Address",
            "type": "string"
          },
          "street2": {
            "description": "Warehouse Warehouse Street Address Continued",
            "type": "string"
          },
          "city": {
            "description": "Warehouse City",
            "type": "string"
          },
          "state": {
            "description": "Warehouse State",
            "type": "string"
          },
          "postalCode": {
            "description": "Warehouse Postal Code",
            "type": "string"
          },
          "country": {
            "description": "Warehouse Country Code",
            "type": "string"
          },
          "phone": {
            "description": "Warehouse Phone Number",
            "type": "string"
          },
          "fax": {
            "description": "Warehouse Fax Number",
            "type": "string"
          },
          "created": {
            "description": "When the Warehouse record was created",
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "description": "When Warehouse record was last updated",
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
The Initial State file allows for the population of pre-existing data when creating a uni. In this example, the initial-state.json file is pre-populated with Inventory, Warehouse, Order, and Shipment data.

Save this file as initial-state.json in the same directory as the prior files.

Initial State file - save as initial-state.json
{
  "Inventory": [{
      "name": "Widget 123 - 1 count",
      "sku": "WMl237ABC",
      "warehouse": "manufacturer",
      "availableInventory": 100
    },
    {
      "name": "Widget 123 - 5 count",
      "sku": "WMl237DEF",
      "warehouse": "manufacturer",
      "availableInventory": 12
    },
    {
      "name": "Widget 123 - 10 count",
      "sku": "WMl237GHI",
      "warehouse": "manufacturer",
      "availableInventory": 83
    },
    {
      "name": "Widget 123 - 50 count",
      "sku": "WMl237JKL",
      "warehouse": "manufacturer",
      "availableInventory": 20
    },
    {
      "name": "Widget 456 - 1 count",
      "sku": "WMl237MNP",
      "warehouse": "manufacturer",
      "availableInventory": 10
    },
    {
      "name": "Widget 456 - 5 count",
      "sku": "WMl237QRS",
      "warehouse": "manufacturer",
      "availableInventory": 350
    },
    {
      "name": "Widget 456 - 10 count",
      "sku": "WMl237TUV",
      "warehouse": "manufacturer",
      "availableInventory": 1250
    },
    {
      "name": "Widget 456 - 50 count",
      "sku": "WMl237WXY",
      "warehouse": "manufacturer",
      "availableInventory": 49
    }
  ],
  "Warehouse": [{
      "companyName": "Manufacturing Company",
      "code": "manufacturer",
      "street1": "3543 Queens Lane",
      "city": "Keysville",
      "state": "VA",
      "postalCode": "23947",
      "country": "US",
      "phone": "4345553444",
      "fax": "4345553445",
      "created": "2020-12-01T14:10:02Z",
      "updated": "2020-12-01T14:10:02Z"
    },
    {
      "companyName": "Retailer Inc",
      "code": "retailer01",
      "street1": "558 Wilkinson Ct",
      "street2": "ste 1402",
      "city": "Maple Valley",
      "state": "WA",
      "postalCode": "98038",
      "country": "US",
      "phone": "4255551314",
      "fax": "3605553213",
      "created": "2020-12-01T14:10:02Z",
      "updated": "2020-12-01T14:10:02Z"
    },
    {
      "companyName": "Outlet Shops LLC",
      "code": "retailer02",
      "street1": "3449 Wolf Pen Rd",
      "city": "San Francisco",
      "state": "CA",
      "postalCode": "94107",
      "country": "US",
      "phone": "6505559544",
      "fax": "4155556254",
      "created": "2020-12-01T14:10:02Z",
      "updated": "2020-12-01T14:10:02Z"
    }
  ],
  "Order": [{
      "orderId": "manufacturer-01",
      "items": [{
        "sku": "WMl237TUV",
        "quantity": 10
      }],
      "manufacturerWarehouseCode": "manufacturer",
      "manufacturerWarehouseLocation": [37.04827632907081, -78.48873356671945],
      "retailerWarehouseCode": "retailer01",
      "retailerWarehouseLocation": [47.37441523471446, -122.03460569672072],
      "delivered": true,
      "created": "2020-12-02T14:10:02Z",
      "updated": "2020-12-02T14:10:02Z"
    },
    {
      "orderId": "manufacturer-02",
      "items": [{
        "sku": "WMl237JKL",
        "quantity": 6
      }],
      "manufacturerWarehouseCode": "manufacturer",
      "manufacturerWarehouseLocation": [37.04827632907081, -78.48873356671945],
      "retailerWarehouseCode": "retailer02",
      "retailerWarehouseLocation": [37.72443674615711, -122.49047216194155],
      "delivered": false,
      "created": "2020-12-02T14:10:02Z",
      "updated": "2020-12-02T14:10:02Z"
    },
    {
      "orderId": "manufacturer-03",
      "items": [{
          "sku": "WMl237DEF",
          "quantity": 2
        },
        {
          "sku": "WMl237MNP",
          "quantity": 4
        }
      ],
      "manufacturerWarehouseCode": "manufacturer",
      "manufacturerWarehouseLocation": [37.04827632907081, -78.48873356671945],
      "retailerWarehouseCode": "retailer02",
      "retailerWarehouseLocation": [37.72443674615711, -122.49047216194155],
      "delivered": false,
      "created": "2020-12-02T14:10:02Z",
      "updated": "2020-12-02T14:10:02Z"
    }
  ],
  "Shipment": [{
      "originWarehouse": "manufacturer",
      "destinationWarehouse": "retailer01",
      "orderId": "manufacturer-01",
      "created": "2020-12-02T14:10:02Z",
      "lastUpdated": "2020-12-02T14:10:02Z",
      "location": [47.37441523471446, -122.03460569672072],
      "delivered": true
    },
    {
      "originWarehouse": "manufacturer",
      "destinationWarehouse": "retailer02",
      "orderId": "manufacturer-02",
      "created": "2020-12-02T14:10:02Z",
      "lastUpdated": "2020-12-02T14:10:02Z",
      "location": [40.20448090000001, -74.5759279],
      "delivered": false
    },
    {
      "originWarehouse": "manufacturer",
      "destinationWarehouse": "retailer01",
      "orderId": "manufacturer-03",
      "created": "2020-12-02T14:10:02Z",
      "lastUpdated": "2020-12-02T14:10:02Z",
      "location": [40.19557037731903, -74.56602928070187],
      "delivered": false
    }
  ]
}

Step 2 - Command Line Deployment

Once the files are saved, deploy the uni using the share-cli.

If not already logged in to the share service do so by running share login:

share login

The share uni create command can be used to deploy our uni.

share uni create --config registration.json

Check on uni Status

The uni deployment will take approximately 4 minutes. The status of the uni deployment can be viewed by running the share get command.

note: your uni-name should differ from the example. set the value of the --uni argument accordingly to match the Name property in registration.json

share get --uni test-track-and-trace

Please refer to the Vendia Share CLI documentation for more detailed information.

Step 3 - Preparing to interact with uni Data

The easiest way to work with uni data is via the Graphql API. You can access the GraphQL API with a web-browser by visiting the GraphQL Explorer for you uni.

You can find the GraphQL Explorer by viewing your uni on the uni Dashboard

Dashboard Image

Open the details page for your uni and click on the Open GraphQL Explorer button for any node.

Remember, uni data is available in real-time from any node, so you can explore the data from any node created in this Quick start.

GraphQL Explorer Button

Run your first Query

The GraphQL Explorer window will be pre-populated with an example query. Delete this query and replace it with the sample below to list the pre-loaded inventory information. Run the query by pressing the play button and review the list of products.

query listInventorys {
  listInventorys {
    Inventorys {
      id
      name
      sku
      availableInventory
      warehouse
    }
  }
}

Step 4 - Track Products in the supply chain network

Now that the uni is operational and data has been loaded, the GraphQL Explorer can be used to further inspect the state of orders and shipments.

Where is my delivery?

Imagine that Retailer Inc., represented by the code retailer01 in our network, has called regarding a delivery. The retailer provides the order id manufacturer-01.

Using the following query in the GraphQL Explorer, determine the Delivery status and Location of the related shipment.

query manufacturing0102Shipment {
  listShipments(filter: {
      orderId: {eq: "manufacturer-01"}
    }
  ) {
        Shipments {
            id
            orderId
            delivered
            originWarehouse
            destinationWarehouse
            location
            created
            lastUpdated
        }
    }
}

Example Output
GraphQL Results

A quick review of the results show the order is not Delivered and was last reported at the location 47.37441523471446, -122.03460569672072.

Note the ID of the shipment record returned. That will be useful in the next step.

A quick review of the location on a map suggests this package may be irretrievably lost.

Map Image

While the storage and retrieval of order data is useful, it may become necessary to review the history of the shipment data for deeper insights. For the purposes of this example, assume the package was retrieved from Lake Wilderness successfully and re-routed to its intended destination.

Progress the shipment by updating the shipment with new location data. Copy and Paste the following mutation to update the shipment.

Important As the ID of each record in the initial-state is generated by the uni when we create it, you must update the ID!= value on line one to match the ID of the shipment record you noted in the previous step. This value is specific to your uni.

Note: While you can apply mutations from any node in the uni in this Quick start, each mutation will be written with metadata regarding its source. For demonstration purposes, imagine that you are acting on behalf of the Shipper and run the mutations from the Shipper node


mutation ($shipmentId: ID!="YOUR SHIPMENT ID") {

   updateShipment_async (id: $shipmentId, input: {
       created: "2020-12-02T14:10:02Z",
       delivered: false,
       destinationWarehouse: "retailer01",
       lastUpdated: "2020-12-02T16:10:02Z",
       location: [47.37481841937031, -122.04123612233782],
       orderId: "manufacturer-01",
       originWarehouse: "manufacturer"}) {
     result{
       tx_id
    }
  }
}

Example Output
GraphQL Results

Once the mutation is executed, a tx_id value will be returned.

Query the shipment again to verify its current location

query manufacturing0102Shipment {
  listShipments(filter: {
      orderId: {eq: "manufacturer-01"}
    }
  ) {
        Shipments {
            id
            orderId
            delivered
            originWarehouse
            destinationWarehouse
            location
            created
            lastUpdated
        }
    }
}

Apply another mutation with new location data

mutation ($shipmentId: ID!="YOUR SHIPMENT ID") {
   updateShipment_async(id: $shipmentId, input: {created: "2020-12-02T14:10:02Z", delivered: false, destinationWarehouse: "retailer01", lastUpdated: "2020-12-02T20:10:02Z", location: [47.37610590795778, -122.08528339383143], orderId: "manufacturer-01", originWarehouse: "manufacturer"}) {
     thirdStepResults: result{
      tx_id
    }
  }
}

List the shipment again

query manufacturing0102Shipment {
  listShipments(filter: {
      orderId: {eq: "manufacturer-01"}
    }
  ) {
        Shipments {
            id
            orderId
            delivered
            originWarehouse
            destinationWarehouse
            location
            created
            lastUpdated
        }
    }
}

Notice that the record only contains the most recent location. This is very useful as anyone querying from any of the nodes in the uni can quickly obtain the current accurate state of the shipment. It may be useful however, to understand historically where the shipment has been located.

Because they are backed by an immutable distributed ledger, Vendia unis contain the history of records as they are updated. You can query the ledger history with GraphQL to review past mutations.

query Ledger {
  list_Blocks {
    _Blocks {
      _TX {
        Mutations
        Owner
        TxId
        Version
      }
      CommitTime
    }
  }
}

Example Output
GraphQL Results

Note that each block in the ledger is recorded with the Mutation data and metadata regarding the node that submitted the mutation. This can be very useful when researching the lineage of specific data such as when working to identify when and where the Shipment record was updated.

Step 5 - Trace issues with package handling

Sharing scalar data such tracking updates for shipments is easy with Vendia Share but it is only part of the data-sharing story. Another feature that logistics customers value is the ability to share files with nodes inside a uni.

Imagine that the shipment with which you have been working finally makes it to its intended destination. Upon inspection however, the retailer reports the package has been damaged. Using Vendia Share, we can begin to add physical evidence to the activities in the supply chain to identify where such damage occurs.

Vendia's Glenn Dierkes details a solution for gathering such evidence in a recent Blog Article. This solution takes advantage of Vendia Share's file-sharing feature to allow partners to upload photos of products as they traverse the supply-chain. These photos can then later be correlated to reports of damage to identify loss patterns.

Adding photos to the supply chain

Using the GraphQL Explorer, simulate the delivery of the shipment while providing a photo of the damaged item.

mutation ($shipmentId: ID! = "YOUR SHIPMENT ID FROM STEP 4") {
  setDeliveredToTrue: updateShipment_async(id: $shipmentId, input: {
      created: "2020-12-02T14:10:02Z",
      delivered: true,
      destinationWarehouse: "retailer01",
      lastUpdated: "2020-12-02T22:10:02Z",
      location: [47.357449210866776, -122.02730719806772],
      orderId: "manufacturer-01",
      originWarehouse: "manufacturer"}) {
    deliveryResult: result {
      tx_id
    }
  }
  addAPhoto: add_File_async(input: {
      SourceBucket: "coryatvendia",
      SourceKey: "public_objects/damaged_pasta.png",
      SourceRegion: "us-east-2",
      DestinationKey: "shipment_9371a86c-5fc2-11eb-b7d4-4f20ed4c7445_delivery.jpg"}) {
    photoResult: result {
      tx_id
    }
  }
}

This mutation has posted an update to the shipment record and added a file to the uni at shipment/9371a86c-5fc2-11eb-b7d4-4f20ed4c7445/delivery.jpg. This file can be shared with other nodes and presented directly via cloud-based storage services. As the nodes in this uni are deployed to AWS, the file is accessible via Amazon S3.

Run the following query in GraphQL Explorer to retrieve a temporary URL for the added file

query ListFiles {
  list_Files{
    _Files{
      id
      TemporaryUrl
      DestinationKey
    }
  }
}

The returned TemporaryUrl can be opened from a web browser to inspect the photo. In this example, it appears the box was left unattended and thus was damaged.

Step 6 - Clean up

It is important that the uni created in this Quick start is destroyed to prevent any unexpected charges. You can destroy the uni from the Share Website or with the CLI command below.

share uni delete --uni test-track-and-trace

Note: the --uni argument should be adjusted to reflect the name of your uni as defined by the Name property in the registration.json file.

Summary and Next Steps

This Quick start demonstrated the ease and speed of which Vendia Share can be leveraged to support supply chain logistics. With zero-code, data was quickly accessible between multiple parties and updates could be recorded and validated in real-time across parties.Vendia Share integrations and smart-contracts could be leveraged to further automate data collection and review. For example, email notifications for deliveries could be modeled against Shipment data and links to view progress on a map could be dynamically generated. If you would like to hear more about how logistics and inventory management customers use Vendia Share, reach out today. We would enjoy discussing this exciting platform with you in more detail.

Show table of contents
Edit this page