Fluent Commerce Logo
Sign In

Manual Fulfillment Reassignment

How-to Guide
Extend

Authors:

Alan Jackson, Sergey Chebotarev, Randy Chan, Valery Kornilovich

Changed on:

4 July 2024

Key Points

  • This article gives System Integrator (SI) Partners and Businesses a high-level idea of where new features can be built on top of the OMS reference solution to support the customer's requirement within Fluent OMS.
  • This idea/solution will provide a new feature where the Store User can enter the rejection reason code during the Pick Confirm Process.
  • This feature allows customer service to reroute an existing
    `fulfilment`
    to a specific location that can fulfill the order.

Steps

Step arrow right iconUse Case

As a Customer Service agent, I want to be able to manually reroute an existing fulfillment to a new fulfillment location.

Step arrow right iconSolution Approach

The ability to override the standard sourcing logic for

`fulfilment`
.  This means selecting an alternative location to fulfill.  The reason may be that it is out of stock, and the store manager knows that it is available somewhere else.  It could also be that the customer service agent wants to expedite the customer's transaction so they may move the
`fulfilment`
from the warehouse to a specific store.

The store manager should have visibility into the inventory position of a specific location when deciding whether to fulfill from another location.  For example, store 1 has 5 inventory, and store 2 has 1 inventory position. The store manager should select the location with more inventory.

By default, locations that are near the existing location that was chosen for

`fulfilment`
should be displayed. There should be a manual override to enter a zip code to determine alternative locations that can be selected if they are too far away.


The following functional aspects need to be considered:

  • There is functionality to select an alternative location to fulfill the already existing
    `fulfilment`
    in the order.
  • Alternative locations that are near the existing location that was chosen for
    `fulfilment`
    should be displayed.
  • There should be a manual override to enter a zip code to determine alternative locations that can be selected if they are too far away.

Step arrow right iconTechnical Design Overview

To support the business solution design, the following technical areas need to be enhanced:

  • A new custom UI component (by using Component SDK) -
    `StoreSelector`
  • A new custom rule (by using Rule SDK) -
    `AssignFulfilmentToLocation`
  • Add the new ruleSet:
    `ManuallyReassign`
    into Order workflows

Step arrow right iconCustomised UI Component: StoreSelector

In the file: 

`StoreSelector.tsx`

1const searchVirtualInventoryQuery = `query availability($productQuantityInput: [ProductQuantityInput!]!, $locationRef: String!, $lat: Float!, $lng: Float!, $virtualCatalogueRef: String!) {
2    searchVirtualInventory(
3      virtualCatalogue: { ref: $virtualCatalogueRef },
4      productQuantities: $productQuantityInput,
5      excludedLocationRefs: [$locationRef],
6      orderByProximity: { longitude: $lng, latitude: $lat }
7    ) {
8      edges {
9        node {
10            location {
11                ref
12                name
13                primaryAddress {
14                    postcode
15                }
16            }
17          virtualPositions { quantity }
18        }
19      }
20    }
21  }`;
22  
23  
24  -- then create a function whihc need to contain below and this function is part of the useEffect:
25  --
26      await getQuery<SLResult>(searchLocationQuery, {
27      fulfilmentId: entityContext?.[0].entity.id,
28    }).then((locationWithOrder) => {
29      locationWithOrder.data?.fulfilmentById.order.items.edges.map(
30        (item: any) => {
31          orderItems.push({
32            productRef: item.node.ref,
33            quantity: item.node.quantity,
34          });
35        },
36      );
37      latitude = locationWithOrder.data?.fulfilmentById.fromAddress.latitude;
38      longitude = locationWithOrder.data?.fulfilmentById.fromAddress.longitude;
39    });
40
41    const stores: storeInterface[] = [];
42
43    await getQuery<SVIResult>(searchVirtualInventoryQuery, {
44      productQuantityInput: orderItems ? orderItems : '',
45      locationRef: auth.context.current.details?.ref,
46      lat: latitude,
47      lng: longitude,
48      virtualCatalogueRef: `BASE:${auth.user.primaryRetailer?.id}`,
49    })
50      .then((storesData) => {
51        storesData.data?.searchVirtualInventory.edges.map((item) => {
52          stores.push({
53            name: item.node.location.name,
54            ref: item.node.location.ref,
55            postcode: item.node.location.primaryAddress.postcode,
56          });
57        });
58        setLoading(false);
59      })
60      .catch(() => {
61        setLoading(false);
62      });
63
64const searchLocationQuery = `query fulfilment($fulfilmentId: ID!){
65    fulfilmentById(id: $fulfilmentId) {
66      fromAddress {
67        latitude
68        longitude
69      }
70      order {
71        items {
72          edges {
73            node {
74              ref
75              quantity
76            }
77          }
78        }
79      }
80    }
81 }`;

Language: tsx

Name: Use `getQuery` (from the hook) to get `searchVirtualInventory` and `fulfilmentById`.

Description:

Use getQuery (from the hook) to get searchVirtualInventory and fulfilmentById. 

1  return (
2    <>
3      {!loading ? (
4        <Autocomplete
5          options={stores}
6          getOptionLabel={(option) =>
7            `${option.name}, ${option.postcode} (${option.ref})`
8          }
9          onChange={(_event, newStore) => setSelectedOption(newStore)}
10          getOptionSelected={(option, value) => option.ref === value.ref}
11          renderInput={(params) => (
12            <TextField {...params} label="Stores" variant="outlined" />
13          )}
14        />
15      ) : (
16        <Loading />
17      )}
18    </>
19  );

Language: tsx

Name: The sample return snippet:

Description:

The sample return snippet:

Step arrow right iconCustom Rules

AssignFulfilmentToLocation

Property

Value

Plugin name

<yourPluginName>

Rule API Client

GraphQL

Rule Info Description

Assigns

`fulfilment`
to a new location passed in the event attribute

Supported Entities

FULFILMENT

Event Attributes

Attribute Name

Description

locationRef

Provided

`locationRef`
will be used for the new
`fulfilment`
`toLocation`
.

Step arrow right iconAdd a new ruleset in the ORDER workflow (ManuallyReassign)

Several rulesets should be added to Home Delivery (HD) and Click and Collect (CC) Order Workflows to activate the 'Reassign Fulfilment' feature. Every ruleset should contain three rules:

  • AssignFulfilmentToLocation: creates new
    `fulfilment`
    for the selected location
  • SetState: moves current
    `fulfilment`
    into MANUALLY_REASSIGNED status
  • SendEventToUpdateInventoryQuantity: initiates
    `UpdateInventoryQty`
    event with UNRESERVE operation for the current inventory catalog to process reservations for the current
    `fulfilment`
    .

Typical trigger statuses for the ruleset are AWAITING_WAVE, ASSESSMENT, REJECTED, and ESCALATED.

The ruleset must contain a user action to fill in the attribute

`locationRef`
using
`storeSelector`
component.

All rulesets have type FULFILMENT, but subtypes are different: CC_PFS, CC_PFDC, HD_PFS, HD_PFDC.

1{
2      "name": "ManuallyReassign",
3      "type": "FULFILMENT",
4      "subtype": "CC_PFDC",
5      "eventType": "NORMAL",
6      "rules": [
7        {
8          "name": "{{fluent.account.id}}.{packageName}.AssignFulfilmentToLocation",
9          "props": {}
10        },
11        {
12          "name": "{{fluent.account.id}}.core.SetState",
13          "props": {
14            "status": "MANUALLY_REASSIGNED"
15          }
16        },
17        {
18          "name": "{{fluent.account.id}}.order.SendEventToUpdateInventoryQuantity",
19          "props": {
20            "eventName": "UpdateInventoryQty",
21            "operation": "UNRESERVE",
22            "inventoryCatalogueRef": "DEFAULT:{{fluent.retailer.id}}"
23          }
24        }
25      ],
26      "triggers": [
27        {
28          "status": "AWAITING_WAVE"
29        },
30        {
31          "status": "ASSESSMENT"
32        },
33        {
34          "status": "REJECTED"
35        },
36        {
37          "status": "ESCALATED"
38        }
39      ],
40      "userActions": [
41        {
42          "context": [
43            {
44              "label": "MANUALLY REASSIGN",
45              "type": "SECONDARY",
46              "modules": [
47                "adminconsole",
48                "servicepoint"
49              ],
50              "confirm": false
51            }
52          ],
53          "attributes": [
54            {
55              "name": "locationRef",
56              "label": "Location Ref",
57              "type": "storeSelector",
58              "source": "",
59              "defaultValue": "",
60              "mandatory": false
61            }
62          ]
63        }
64      ]
65    },

Language: json

Name: ManuallyReassign ruleset in Order workflow

Description:

ManuallyReassign ruleset in Order workflow 

Step arrow right iconResult


The User Action would look like (closed/open states):

No alt provided


After the user action has been completed, the existing

`fulfilment`
should be set to
`MANUALLY_REASSIGNED`
status, inventory marked as released, and a new
`fulfilment`
created with a selected location.


Alan Jackson

Alan Jackson

Contributors:
Sergey Chebotarev
Randy Chan
Valery Kornilovich