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
| Type | Used for |
|---|---|
Warehouse To Customer | Shipping an order to the customer's address. |
Warehouse To Warehouse | Sending stock to another warehouse. |
Store Pickup | The customer collects from a store. |
| Status | Meaning |
|---|---|
| Open | Waiting to be picked. |
| In Progress | Some items have been sent, but not all. |
| Awaiting Pickup | Store Pickup only: everything is at the store, ready for the customer. |
| Integrated Refund | Store Pickup only: some items couldn't be fulfilled and need refunding. See below. |
| Closed | Done. |
| Canceled, Inactive | No 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 stockShipping 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.
- Mark a fulfillment as in progress:
GET /fulfillment/{id}/in-progress.json - Close a fulfillment:
GET /fulfillment/{id}/close.json(orclosed.json), for example when the customer has collected their order. GET /fulfillment/{id}/integrated-refund.jsonsets the status to Integrated Refund. See the reference.
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_changecan 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.
-
API Request: Get Private Data for your Slack webhook, Response variable
slack_res. -
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(); -
If Condition: If it needs a refund, Condition
$.needs_refund === true. -
Send Notification: Tell the team, nested under THEN: Channel Slack, Webhook URL
{{ slack_url }}, TitleRefund needed for sale {{ sale_id }}, MessageStore Pickup {{ fulfillment_id }} is missing: {{ missing }}.
For Shopify orders, swap the notification for a Shopify GraphQL step that issues the refund.
Updated about 22 hours ago
