# Embedded / Fulfilment

# Shopfront Embedded Bridge Fulfilment API

Shopfront's Embedded Bridge allows you to easily plug into Shopfront's fulfilment system so stores don't have to
leave Shopfront to process orders and prevents manual processing errors.

> This page describes the Embedded Bridge's Fulfilment API, for in-depth examples on how to use the API,
> check out the [How To Guide](/documentation/How%20To/Fulfilment).

## Emitable Actions

The fulfilment API is made up a number of emitable actions that can be sent to Shopfront at any time.

### RegisterIntent

Whilst optional, this should be sent to Shopfront as soon as your application is ready to opt-in to the Fulfilment API.
This enabled the option on the sell screen to view the fulfilment page and display your application as connected.

| Field           | Type    | Description                                                                        |
|-----------------|---------|------------------------------------------------------------------------------------|
| requireApproval | boolean | Whether sales waiting to process require approval before they're available to pack |

**RegisterIntent**

```javascript
import { Fulfilment, Bridge } from "@shopfront/bridge";

const application = Bridge.createApplication({ /* your options */ });

application.addEventListener("READY", async () => {
    await application.send(new Fulfilment.RegisterIntent({
        requireApproval: true,
    }));
});
```

### Options

Update the fulfilment options previously provided on opt-in.

| Field           | Type    | Description                                                                        |
|-----------------|---------|------------------------------------------------------------------------------------|
| requireApproval | boolean | Whether sales waiting to process require approval before they're available to pack |

**Options**

```javascript
import { Fulfilment } from "@shopfront/bridge";

application.send(new Fulfilment.Options({
    requireApproval: false,
}));
```

### OrdersSync

Synchronise a number of orders with Shopfront, this is typically called on a schedule or when your application receives
an event from your server.

| Field  | Type                            | Description                                                                                                    |
|--------|---------------------------------|----------------------------------------------------------------------------------------------------------------|
| orders | Array<OrderCreateDetails> | The orders to send to Shopfront                                                                                |
| merge  | boolean                         | Whether the orders should be merged into the currently available orders, or if they should override the orders |

> Setting `merge` to `true` doesn't merge individual orders, it just prevents the removal of all previous orders for the integration.
> Updates to orders should use the `OrderUpdate` action.

#### OrderCreateDetails

| Field      | Type                                            | Description                                                                                                                                                                 |
|------------|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| id         | string                                          | Your ID for the order                                                                                                                                                       |
| customer   | { name: string, phone: string }                 | The customer who has performed the order                                                                                                                                    |
| courier    | { icon?: string, name: string, phone: string }? | (optional) The courier who will be picking up the order                                                                                                                     |
| comment    | string?                                         | (optional) Any comments for the order                                                                                                                                       |
| totalPrice | number                                          | The total price for the order                                                                                                                                               |
| expiry     | (string | Date)?                           | (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string |
| status     | OrderStatus?                                    | (optional) The status of the order                                                                                                                                          |
| finalLabel | string?                                         | (optional) The label to display as part of the final step in the UI                                                                                                         |
| createdAt  | string                                          | The time the order was created                                                                                                                                              |

#### OrderStatus

One of:

- `PENDING_APPROVAL`
- `WAITING_FOR_PACKING`
- `PACKED`
- `COLLECTED`
- `COMPLETED`

**OrdersSync**

```javascript
import { Fulfilment } from "@shopfront/bridge";

// onOrderRetrievalQueue is a presumed way that you would retrieve orders from your API
onOrderRetrievalQueue(async allOrders => {
    await application.send(new Fulfilment.OrdersSync({
        orders: allOrders,
        merge : false,
    }));
});
```

### OrderCreate

Create a single order to send to Shopfront's fulfilment system. This can be used instead of the `OrdersSync` action.

| Field | Type               | Description                    |
|-------|--------------------|--------------------------------|
| order | OrderCreateDetails | The order to send to Shopfront |

**OrderCreate**

```javascript
import { Fulfilment } from "@shopfront/bridge";

// onNewOrder is a presumed way that you would receive a new order notification from your API
onNewOrder(async order => {
    await application.send(new Fulfilment.OrderCreate({
        order,
    }));
});
```

### OrderUpdate

Update a single order that already exists within Shopfront's fulfilment system.

| Field | Type                | Description                    |
|-------|---------------------|--------------------------------|
| order | OrderSummaryDetails | The order to send to Shopfront |

#### OrderSummaryDetails

| Field      | Type                                              | Description                                                                                                                                                                 |
|------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| id         | string                                            | Your ID for the order                                                                                                                                                       |
| customer   | `{ name: string, phone: string }`                 | The customer who has performed the order                                                                                                                                    |
| courier    | `{ icon?: string, name: string, phone: string }?` | (optional) The courier who will be picking up the order                                                                                                                     |
| comment    | string?                                           | (optional) Any comments for the order                                                                                                                                       |
| totalPrice | number                                            | The total price for the order                                                                                                                                               |
| expiry     | (string | Date)?                             | (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string |
| status     | OrderStatus                                       | The status of the order                                                                                                                                                     |
| finalLabel | string?                                           | (optional) The label to display as part of the final step in the UI                                                                                                         |
| createdAt  | string                                            | The time the order was created                                                                                                                                              |

**OrderUpdate**

```javascript
import { Fulfilment } from "@shopfront/bridge";

// onOrderUpdate is a presumed way that you would receive an updated order from your API
onOrderUpdate(async order => {
    await application.send(new Fulfilment.OrderUpdate({
        order,
    }));
});
```

### OrderCancel

This cancels / removes an order within Shopfront's fulfilment system.

| Field | Type   | Description                   |
|-------|--------|-------------------------------|
| id    | string | The ID of the order to remove |

**OrderCancel**

```javascript
import { Fulfilment } from "@shopfront/bridge";

// onOrderCancel is a presumed way that you would receive a new order notification from your API
onOrderCancel(async id => {
    await application.send(new Fulfilment.OrderCancel({
        id,
    }));
});
```

## Receivable Events

Along with registering and sending order updates, your application will also need to listen to a number of events
and respond back to them with correctly formatted data.

> Any event can optionally return a promise.

### FULFILMENT_GET_ORDER

This event triggers when Shopfront requires the full details of a single order.

**Arguments**

| Field | Type   | Description                  |
|-------|--------|------------------------------|
| id    | string | The ID of the order          |

**Returns**

| Field      | Type                                            | Description                                                                                                                                                                 |
|------------|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| id         | string                                          | Your ID for the order                                                                                                                                                       |
| customer   | { name: string, phone: string }                 | The customer who has performed the order                                                                                                                                    |
| courier    | { icon?: string, name: string, phone: string }? | (optional) The courier who will be picking up the order                                                                                                                     |
| comment    | string?                                         | (optional) Any comments for the order                                                                                                                                       |
| totalPrice | number                                          | The total price for the order                                                                                                                                               |
| expiry     | (string | Date)?                           | (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string |
| status     | OrderStatus                                     | The status of the order                                                                                                                                                     |
| finalLabel | string?                                         | (optional) The label to display as part of the final step in the UI                                                                                                         |
| createdAt  | string                                          | The time the order was created                                                                                                                                              |
| items      | Array<OrderItem>                          | The items on the order                                                                                                                                                      |

#### OrderItem

| Field    | Type                  | Description                                                            |
|----------|-----------------------|------------------------------------------------------------------------|
| id       | string                | Your ID for the product                                                |
| image    | string?               | (optional) The image to use for the product                            |
| name     | string                | Your name for the product                                              |
| quantity | number                | The number of `packSize`s sold                                         |
| packSize | number\|PackSizeMatch | The outer size which was sold (e.g. a six-pack would return this as 6) |
| price    | number                | The total price for the line                                           |
| comment  | string?               | (optional) Any comment on the product to display                       |
| match    | OrderItemMatch?       | (optional) The matching for the product                                |

#### PackSizeMatch

This allows you to specify a different quantity depending upon how the item was matched.

| Field        | Type              | Description                                                                                |
|--------------|-------------------|--------------------------------------------------------------------------------------------|
| id           | number?           | For matching via Shopfront ID                                                              |
| barcode      | ("auto"\|number)? | For matching matching via barcode (if auto is specified, it will use the barcode quantity) |
| mdbId        | number?           | For matching via MDB ID                                                                    |
| supplierCode | number?           | For matching via Supplier Code                                                             |
| defaultTo    | number            | When a match can't be determined or a field isn't specified which was used to match        |

#### OrderItemMatch

All fields are optional, for details on how Shopfront performs matching, see the
[product matching algorithm section](#product-matching-algorithm).

| Field          | Type                 | Description                                                                   |
|----------------|----------------------|-------------------------------------------------------------------------------|
| id             | string?              | Shopfront's ID for the product                                                |
| barcodes       | Array<string>? | The barcodes of the product, these don't have to correspond to the `packSize` |
| mdbId          | number?              | The master database reference for the product                                 |
| supplier       | object?              | The supplier which this product belongs to                                    |
| supplier.id    | string?              | Shopfront's ID for the supplier                                               |
| supplier.mdbId | number?              | The master database reference for the supplier                                |
| supplierCodes  | Array<string>? | The supplier codes that belong to the product                                 |

**FULFILMENT_GET_ORDER**

```javascript
application.addEventListener("FULFILMENT_GET_ORDER", id => {
    return retrieveFullOrderFromAPI(id);
});
```

### FULFILMENT_VOID_ORDER

Triggers when Shopfront (typically on user interaction) voids / cancels an order. It's expected that you'll send this
information back to your server to handle any potential refund process and remove the order from the list of orders
available to sync to Shopfront.

**Arguments**

| Field | Type   | Description                  |
|-------|--------|------------------------------|
| id    | string | The ID of the order          |

**Returns**

- None

**FULFILMENT_VOID_ORDER**

```javascript
application.addEventListener("FULFILMENT_VOID_ORDER", id => {
    return cancelOrderViaAPI(id);
});
```

### FULFILMENT_PROCESS_ORDER

This occurs once the order has been processed and matched on Shopfront, moving to the `PACKED` stage. It's expected
you'll transform and process the sale on Shopfront when ready (the sale is not automatically processed on Shopfront).

**Arguments**

| Field | Type                                      | Description             |
|-------|-------------------------------------------|-------------------------|
| event | FulfilmentProcessEvent                    | The event that occurred |

**Returns**

- None

#### FulfilmentProcessEvent

| Field | Type                                      | Description             |
|-------|-------------------------------------------|-------------------------|
| id    | string                                    | The ID of the order     |
| sale  | [Sale](/documentation/Embedded/Sale#sale) | The sale from Shopfront |

**FULFILMENT_PROCESS_ORDER**

```javascript
application.addEventListener("FULFILMENT_PROCESS_ORDER", async (event) => {
    const id   = event.id;
    const sale = event.sale;

    await markSaleAsPackedAndSaveSale(id, sale);
});
```

### FULFILMENT_ORDER_APPROVAL

This triggers when the order is approved or declined on Shopfront, this will only be called if you have enabled
`requireApproval` when opting in to using the fulfilment system.

**Arguments**

| Field | Type                    | Description             |
|-------|-------------------------|-------------------------|
| event | FulfilmentApprovalEvent | The event that occurred |

**Returns**

- None

#### FulfilmentApprovalEvent

| Field    | Type    | Description                           |
|----------|---------|---------------------------------------|
| id       | string  | The ID of the order                   |
| approved | boolean | Whether the order was approved or not |

**FULFILMENT_ORDER_APPROVAL**

```javascript
application.addEventListener("FULFILMENT_ORDER_APPROVAL", async (event) => {
    if(!event.approved) {
        await cancelOrderViaAPI(event.id);
    } else {
        await approveOrder(event.id);
    }
});
```

### FULFILMENT_ORDER_COLLECTED

Triggers when the order is marked as collected through Shopfront.

**Arguments**

| Field    | Type    | Description                           |
|----------|---------|---------------------------------------|
| id       | string  | The ID of the order                   |

**Returns**

- None

**FULFILMENT_ORDER_COLLECTED**

```javascript
import { Sales } from "@shopfront/bridge";

application.addEventListener("FULFILMENT_ORDER_COLLECTED", async id => {
    await markOrderAsCollected(id);
    const sale = await retrieveSavedShopfrontSale(id);

    sale.addPayment(
        new Sales.SalePayment("<< id of payment method >> ", sale.getSaleTotal())
    );

    await sale.create(application);
});
```

### FULFILMENT_ORDER_COMPLETED

Triggers when the order is marked as completed within Shopfront

**Arguments**

| Field    | Type    | Description                           |
|----------|---------|---------------------------------------|
| id       | string  | The ID of the order                   |

**Returns**

- None

**FULFILMENT_ORDER_COMPLETED**

```javascript
application.addEventListener("FULFILMENT_ORDER_APPROVAL", async (id) => {
    await markOrderAsCompleted(id);
});
```

## Product Matching Algorithm

When the `FULFILMENT_GET_ORDER` event is triggered, it expects an object back that contains a list of products, each
of which can optionally contain an `OrderItemMatch` object.

> It's suggested to provide as many details in `OrderItemMatch` as you can as it'll allow for the most accurate
> matching and reduce the chances of algorithm changes affecting your application in the future.

Shopfront then attempts to match using the following algorithm:

1. Exact `id` match
    - If this matches, use the matching product
    - Otherwise continue
2. Match via `barcodes`
    - If a single product matches, use the matching product
    - If no products match, continue
    - If multiple products match, continue using the matching products as the base result set
3. Match via `mdbId`
    - If a single products matches, use the matching product
    - If no products match, continue
    - If multiple products match, continue using the matching products as the base result set
4. Match via `supplierCodes` using the `supplier.id` as the supplier
    - If a single product matches, use the matching product
    - If no products match, continue
    - If multiple products match, continue
5. Match via `supplierCodes` using the `supplier.mdbId` as the supplier
    - If a single product matches, use the matching product
    - If no products match, continue
    - If multiple products match, continue
6. Stop matching and review existing products
    - If multiple products exist which have moved through each of the previous steps, select the first one
    - If multiple products exist from one of the previous steps (in order from 2-5), select the first one
    - If no products match, an automatic match isn't possible

Even is an automatic match is possible, the user can still elect to change which product matches to your provided
product.