Online orders and fulfilment

Pick, ship and track fulfillments, including Store Pickup.

A fulfillment is how Smooth POS tracks getting items to where they need to go: from a warehouse to a customer, from one warehouse to another, or to a store for the customer to pick up. Online orders usually arrive as sales with a fulfillment attached, and your warehouse picks, packs and ships against it.

Types and statuses

TypeUsed for
Warehouse To CustomerShipping an order to the customer's address.
Warehouse To WarehouseSending stock to another warehouse.
Store PickupThe customer collects from a store.
StatusMeaning
OpenWaiting to be picked.
In ProgressSome items have been sent, but not all.
Awaiting PickupStore Pickup only: everything is at the store, ready for the customer.
Integrated RefundStore Pickup only: some items couldn't be fulfilled and need refunding. See below.
ClosedDone.
Canceled, InactiveNo longer being worked on.

In the app

Inventory → Fulfillments lists your fulfillments. Open one to see its lines, shipments and returns.

Create a fulfillment

When an order comes in from another system, create the sale (Create a sale) and then create a fulfillment for it:

{
  "sale_id": 880123,
  "reference": "WEB-10023",
  "type": "Warehouse To Customer",
  "source_warehouse_id": 3,
  "destination_address": {"first_name": "Jane", "last_name": "Citizen", "address1": "1 George Street", "city": "Sydney"},
  "lines": [
    {"item_id": 5521, "sale_line_id": 99120, "requested_quantity": 2}
  ]
}

requested_quantity is how many to send. As you ship, actual_quantity counts what's gone out.

Pick

Generate a pick list with GET /fulfillment/generate-pick.json?ids=101,102,103. It combines what's still to send across those fulfillments and tells you which location to pick each item from, with alternate_locations when one location doesn't have enough. All the fulfillments have to come from the same source warehouse.

Ship

Ship a fulfillment with POST /fulfillment/{id}/ship.json. Each call records one parcel: what's in it, where it was picked from, and how it's travelling.

{
  "carrier_id": 4,
  "box_id": 2,
  "tracking_code": "AP123456789AU",
  "tracking_url": "https://auspost.com.au/mypost/track/#/details/AP123456789AU",
  "label_url": "https://example.com/labels/AP123456789AU.pdf",
  "sent_items": [
    {"item_id": 5521, "location_id": 17, "quantity": 2}
  ]
}

After each shipment, Smooth POS works out the status:

  • Warehouse To Customer and Warehouse To Warehouse: Closed once every line is fully sent, otherwise In Progress.
  • Store Pickup: Awaiting Pickup once every line is at the store, otherwise Integrated Refund.

Shipping fires fulfillment_ship, plus fulfillment_status_change when the status changes. Fix tracking details later with Update a fulfillment's shipment.

📘

Let Smooth POS move the stock

Shipping a fulfillment updates stock levels for you. Don't adjust stock yourself as well, or it'll be counted twice.

Change the status by hand

These change data even though they're GET requests. They only work while the fulfillment is Open, In Progress or Awaiting Pickup.

Store Pickup and Integrated Refund

If a Store Pickup is shipped to the store with some items missing, it goes to Integrated Refund rather than Awaiting Pickup. That's on purpose: those items can't be collected, so the customer needs a refund for them.

  • For a sale from Shopify, refund the missing items in Shopify. A Glue job on fulfillment_status_change can do that for you (see the example below).
  • For a sale made in Smooth POS, refund them at the register. See Returns and exchanges.

Returns against a fulfillment

When a customer sends items back, Return a fulfillment records them against it, linked to the return sale with return_sale_id and return_line_id:

{"returned_items": [{"item_id": 5521, "quantity": 1, "return_sale_id": 880456, "return_line_id": 99877}]}

It fires fulfillment_return, and fulfillment_status_change if the status changes.

Example: Flag Store Pickups that need a refund

Trigger: System Hook, fulfillment_status_change.

  1. API Request: Get Private Data for your Slack webhook, Response variable slack_res.

  2. Script: Read the change

    var change = JSON.parse($.default);
    var f = change.after;
    $.fulfillment_id = f.id;
    $.sale_id = f.sale_id;
    $.needs_refund = change.before.status !== "Integrated Refund" && f.status === "Integrated Refund";
    $.missing = f.lines
      .filter(function (l) { return l.requested_quantity > l.actual_quantity; })
      .map(function (l) { return (l.sku || l.item_id) + " x " + (l.requested_quantity - l.actual_quantity); })
      .join(", ");
    var pd = JSON.parse($.slack_res.body);
    $.slack_url = pd[Object.keys(pd)[0]].trim();
  3. If Condition: If it needs a refund, Condition $.needs_refund === true.

  4. Send Notification: Tell the team, nested under THEN: Channel Slack, Webhook URL {{ slack_url }}, Title Refund needed for sale {{ sale_id }}, Message Store Pickup {{ fulfillment_id }} is missing: {{ missing }}.

For Shopify orders, swap the notification for a Shopify GraphQL step that issues the refund.


Did this page help you?