Python SDK Sample: Fulfillment


# retrieve an auto order
import ultracart
from ultracart.rest import ApiException
from ultracart import ApiClient, FulfillmentShipment, FulfillmentInventory
from pprint import pprint
import datetime
from datetime import timedelta, timezone

config = ultracart.Configuration()
# this key is valid only in the UltraCart development system.  You need to supply a valid simple key here.
config.api_key['x-ultracart-simple-key'] \
    = '4256aaf6dfedfa01582fe9a961ab0100216d737b874a4801582fe9a961ab0100'
config.debug = True
config.verify_ssl = True  # Development only.  Set to True for production.

api_client = ApiClient(configuration=config, header_name='X-UltraCart-Api-Version', header_value='2017-03-01')
fulfillment_api = ultracart.FulfillmentApi(api_client)

distribution_center_code = 'DFLT'


# ==========================================================================
# this method is solely in case there are no unacknowledged orders in the system
# so that the third illustration has an order to operate on.  this method would
# not be part of any production system.
def find_any_order():
    order_api = ultracart.OrderApi(api_client)

    # to find any order, just submit a really old creation date begin.
    # for this example, we'll use three year ago.
    three_years = timedelta(days=365 * 3)
    some_really_early_date = datetime.datetime.now(timezone.utc) - three_years
    creation_date_begin = some_really_early_date.replace(microsecond=0).isoformat()

    orders_response = order_api.get_orders(creation_date_begin=creation_date_begin)
    orders = orders_response.orders
    # let's hope there's at least one order in the system.
    return orders[0]


# ==========================================================================


# This example contains the four main lifecycle events for a fulfillment center.
# 1) get orders, 2) acknowledge you've gotten the order, 3) acknowledge you've shipped it, 4) update inventory
# ideally each block would contain its own try/catch error handling, as well as testing call responses for success
# and empty/no data i.e. there is nothing to ship, etc.

try:

    # ==========================================================================
    #  STEP 1: GET ORDERS NEEDING SHIPPING
    #  Best practice: Use a webhook instead of polling getOrders here.  That will provide instant response.
    #  This call does take an expand parameter like many other getXXX calls.  The orders are returned with maximum
    #  expansion by default.  So all that's needed is the dc code.
    unacknowledged_orders_response = fulfillment_api.get_distribution_center_orders(distribution_center_code)
    # TODO test for unacknowledged_orders_response.success boolean and take appropriate steps.
    unacknowledged_orders = unacknowledged_orders_response.orders

    # ==========================================================================
    #  STEP 2: ACKNOWLEDGE YOU HAVE RECEIVED ORDERS
    #  store the unacknowledged orders off somewhere for processing. (not shown here)
    #  then, acknowledge that you've received them.
    #  if you have more than 100 orders to acknowledge, send them in batches.  limit is 100.
    acknowledged_order_ids = []
    for order in unacknowledged_orders:
        print(order.order_id)
        acknowledged_order_ids.append(order.order_id)

    # just in case there isn't any test data, add some.  in production, this if-then block would decide whether or
    # not to make the API call at all.
    if len(acknowledged_order_ids) == 0:
        acknowledged_order_ids.append('DEMO-123456789')

    # TODO process these orders in your system before acknowledging them...
    fulfillment_api.acknowledge_orders(distribution_center_code, acknowledged_order_ids)

    # ==========================================================================
    #  STEP 3: SHIP ORDERS
    #  Grab the first order.  This is just an example.  In a real situation, this block would be called
    # for each order after you have shipped it.
    if len(unacknowledged_orders) > 0:
        first_order = unacknowledged_orders[0]  # this will obviously fail if there are none...
    else:
        first_order = find_any_order()
    #  create one or more shipment objects
    fulfillment_shipment = FulfillmentShipment()
    fulfillment_shipment.order_id = first_order.order_id
    fulfillment_shipment.tracking_numbers = ['TrackingNo12345',
                                             'TrackingNo67890']  # these will come from your shipping software.

    shipments = []
    shipments.append(fulfillment_shipment)  # just a single shipment this time

    fulfillment_api.ship_orders(distribution_center_code, shipments)

    # ==========================================================================
    #  STEP 4: UPDATE INVENTORIES AS NEEDED
    #  update inventories as needed.
    first_inventory = FulfillmentInventory()
    first_inventory.item_id = 'BONE'
    first_inventory.quantity = 2500

    second_inventory = FulfillmentInventory()
    second_inventory.item_id = 'BONE'
    second_inventory.quantity = 2500

    inventory_updates = [first_inventory, second_inventory]

    #  limit is 500 inventory updates at a time.  batch them if you're going large.
    fulfillment_api.update_inventory(distribution_center_code, inventory_updates)

    pprint(unacknowledged_orders_response)
    pprint(unacknowledged_orders)
    print('Finished.')

except ApiException as e:
    print("Exception when calling Fulfillment API calls: %s\n" % e)