Fluent Commerce Logo
Docs
Sign In

How to enable Returns in Fluent OMS and Fluent Store

Essential knowledge

Author:

Fluent Commerce

Changed on:

8 July 2024

Overview

The feature is available for any OMX web apps. The following guide will step through the areas of configuration to enable the reference solution.

Key points

  • A step-by-step guide to enable reference return functionality.
    • Settings setup
    • Create & Assign the Instore Return Role
    • Workflow Configuration (Order, returnOrder and Billing)
    • Manifest Changes for Fluent Store and Fluent OMS

Settings

The following settings are required as part of the Return setup:

1POST: {{fluentApiHost}}/graphql
2
3//create for both ACCOUNT and Retailer level:
4
5
6GraphQL Query:
7mutation CreateSetting {
8createSetting(input: {
9		name: "RETURN_ORCHESTRATED", 
10		valueType: "BOOLEAN", 
11		value: "TRUE", 
12		context: "RETAILER", 
13		contextId:$retailerId }) {
14    id
15    name
16  }
17}
18
19createSetting(input: {
20		name: "RETURN_ORCHESTRATED", 
21		valueType: "BOOLEAN", 
22		value: "TRUE", 
23		context: "ACCOUNT", 
24		contextId:0}) {
25    id
26    name
27  }
28}
29
1POST: {{fluentApiHost}}/graphql
2
3// create a postman environment variable:
4// Variable: json_value
5// initial val + current value: 
6[
7    { "label":"Reusable", "value":"GOOD" },
8    { "label":"Destroy", "value":"BROKEN" }
9]
10
11
12GraphQL variables:
13{
14	"retailerId": {{retailer_id}},
15    "lobValue" : {{json_value}}
16}
17
18
19GraphQL Query:
20mutation CreateSetting($retailerId:Int! , $lobValue:Json)  {
21createSetting(input: {
22		name: "RETURN_CONDITION", 
23		valueType: "JSON", 
24		lobValue:$lobValue , 
25		context: "RETAILER", 
26		contextId:$retailerId}) {
27    id
28    name
29  }
30}
1POST: {{fluentApiHost}}/graphql
2
3// create a postman environment variable:
4// Variable: json_value
5// initial val + current value: 
6[
7
8    {
9        "label": "Wrong Size",
10        "value": "WRONGSIZE"
11    },
12    {
13        "label": "Broken",
14        "value": "BROKEN"
15    },
16    {
17        "label": "Doesn't match the description",
18        "value": "WRONG_DESCRIPTION"
19    },
20    {
21        "label": "Change of mind",
22        "value": "CHANGE_MIND"
23    }
24]
25
26
27GraphQL variables:
28{
29	"retailerId": {{retailer_id}},
30    "lobValue" : {{json_value}}
31}
32
33
34GraphQL Query:
35mutation CreateSetting($retailerId:Int! , $lobValue:Json)  {
36createSetting(input: {
37		name: "RETURN_REASON", 
38		valueType: "JSON", 
39		lobValue:$lobValue , 
40		context: "RETAILER", 
41		contextId:$retailerId}) {
42    id
43    name
44  }
45}
1POST: {{fluentApiHost}}/graphql
2
3
4GraphQL Query:
5mutation CreateSetting {
6createSetting(input: {
7		name: "DEFAULT_RETURN_DESTINATION_LOCATION", 
8		valueType: "STRING", 
9		value: $retailerId, 
10		context: "RETAILER", 
11		contextId:$retailerId }) {
12    id
13    name
14  }
15}
1POST: {{fluentApiHost}}/graphql
2
3// create a postman environment variable:
4// Variable: DEFAULT_TAX_TYPE
5// initial val + current value: 
6{
7    "country": "AUS",
8    "group": "STANDARD",
9    "tariff": "GST"
10}
11
12
13GraphQL variables:
14{
15	"retailerId": {{retailer_id}},
16    "lobValue" : {{DEFAULT_TAX_TYPE}}
17}
18
19
20GraphQL Query:
21mutation CreateSetting($retailerId:Int! , $lobValue:Json)  {
22createSetting(input: {
23		name: "DEFAULT_TAX_TYPE", 
24		valueType: "JSON", 
25		lobValue:$lobValue , 
26		context: "RETAILER", 
27		contextId:$retailerId}) {
28    id
29    name
30  }
31}
1POST {{fluentApiHost}}/graphql
2
3mutation CreateSetting {
4   createSetting(input: {
5		name: "HOTFIX_NEW_APOLLO_CLIENT_ENABLED", 
6		valueType: "BOOLEAN", 
7		value: "TRUE", 
8		context: "ACCOUNT", 
9		contextId: 0}) {
10    id
11    name
12  }
13}



Create & Assign the Instore Return Role

1POST: {{fluentApiHost}}/graphql
2
3mutation {
4  createRole(input: {name: "INSTORE_RETURNS", 
5    permissions: [
6        {name: "ORDER_VIEW"}, 
7        {name: "CUSTOMER_VIEW"}, 
8        {name: "RETURN_VIEW"}, 
9        {name: "ORDERITEM_VIEW"}, 
10        {name: "PRODUCT_VIEW"}, 
11        {name: "VARIANTPRODUCT_VIEW"}, 
12        {name: "STANDARDPRODUCT_VIEW"}, 
13        {name: "GROUPPRODUCT_VIEW"}, 
14        {name: "CATEGORY_VIEW"}
15    ] 
16  }) {
17    name
18    permissions {
19      name
20    }
21  }
22}
23
1POST: {{fluentApiHost}}/graphql
2
3// Graphql Variables:
4{
5  "input": {
6    "id": {{userID}},
7    "roles": [
8    {
9        "contexts": [
10            {
11                "contextId": "{{retailerID}}",
12                "contextType": "RETAILER"
13            },
14                        {
15                "contextId": "{{locationID}}",
16                "contextType": "AGENT"
17            }
18        ],
19        "role": {
20            "name": "INSTORE_RETURNS"
21        }
22    }
23    ]
24  }
25}
26
27
28
29
30// QUERY:
31mutation updateUser ($input: UpdateUserInput) {
32    updateUser (input: $input) {
33        id
34        ref
35        username
36        title
37        firstName
38        lastName
39        primaryEmail
40        primaryPhone
41        type
42        status
43        department
44        country
45        timezone
46        promotionOptIn
47        createdOn
48        updatedOn
49        roles{
50            contexts{
51                contextId
52                contextType
53            }
54            role{
55                name
56            }
57        }
58    }
59}



Workflow Configuration

The following steps provide a reference solution of how workflows can be configured to enable . The below diagram serves as a visual guide of the logical flow of these rules and how they interact across workflows to manage a Return across its entire lifecycle.  Hence, we will update the workflows and create a return and a billing .

No alt provided

1. Configuring the Order workflow

The following supplied rules can be added to the to allow a Return to be created from an existing .

ValidateOrderQty

The `ValidateOrderQty`  validates if a `ReturnOrder` can be created based on the item quantity that is available to return.

CreateReturnOrderFromOrder

The `CreateReturnOrderFromOrder` creates a return to begin the of refunding the returned items.

UpdateOrderItemAttribute

The `UpdateOrderItemAttribute`  calculates the remaining returnable quantity for the items within the .


Example Order Rulesets using the Returns rules

Using the above rules, we have assembled a sample set of rulesets, including status updates, which can be implemented into an .

If you are using the sample rulesets, please replace the `subtype` value with the type you are implementing the rulesets against.

1{
2    "name":"ReturnOrder",
3    "description":"Validate if the item can be returned and create the return order",
4    "type":"ORDER",
5    "subtype":"{{order_type}}",
6    "eventType":"NORMAL",
7    "rules":[
8        {
9            "name":"FLUENTRETAIL.base.ValidateReturnQty"
10        },
11        {
12            "name":"FLUENTRETAIL.base.CreateReturnOrderFromOrder"
13        },
14        {
15            "name":"FLUENTRETAIL.base.ChangeStateGQL",
16            "props":{
17                "status":"RETURN_CREATED"
18            }
19        }
20    ],
21    "triggers":[
22        {
23            "status":"COMPLETE"
24        },
25        {
26            "status":"RETURN_CREATED"
27        },
28        {
29            "status":"RETURN_COMPLETE"
30        }
31    ],
32    "userActions":[
33        {
34            "context":[
35                {
36                    "type":"PRIMARY",
37                    "label":"Submit Return",
38                    "confirm":false,
39                    "modules":[
40                        "adminconsole",
41                        "store",
42                        "servicepoint"
43                    ]
44                }
45            ],
46            "attributes":[
47                {
48                    "name":"lodgedLocation",
49                    "type":"STRING",
50                    "label":"Lodged Location",
51                    "source":"",
52                    "mandatory":false,
53                    "defaultValue":""
54                },
55                {
56                    "name":"pickupLocation",
57                    "type":"ADDRESS",
58                    "label":"Pickup Location",
59                    "source":"",
60                    "mandatory":false,
61                    "defaultValue":""
62                },
63                {
64                    "name":"returnItems",
65                    "type":"RETURN_ITEMS",
66                    "label":"Items",
67                    "source":"",
68                    "mandatory":true,
69                    "defaultValue":""
70                },
71                {
72                    "name":"type",
73                    "type":"STRING",
74                    "label":"Return Type",
75                    "source":"",
76                    "options":{
77                        "active":[
78                            {
79                                "name":"Default",
80                                "value":"DEFAULT"
81                            }
82                        ]
83                    },
84                    "mandatory":false,
85                    "defaultValue":""
86                }
87            ]
88        }
89    ]
90},
91{
92    "name":"UpdateReturnableQty",
93    "description":"This rule updates the returnableQty to the order items attributes",
94    "type":"ORDER",
95    "subtype":"{{order_type}}",
96    "eventType":"NORMAL",
97    "rules":[
98        {
99            "name":"FLUENTRETAIL.base.UpdateOrderItemAttribute"
100        }
101    ],
102    "triggers":[
103        {
104            "status":"RETURN_CREATED"
105        }
106    ],
107    "userActions":[
108
109    ]
110},
111{
112    "name":"OrderReturnComplete",
113    "description":"Updates the status of the Return Order",
114    "type":"ORDER",
115    "subtype":"{{order_type}}",    
116    "eventType":"NORMAL",
117    "rules":[
118        {
119            "name":"FLUENTRETAIL.base.ChangeStateGQL",
120            "props":{
121                "status":"RETURN_COMPLETE"
122            }
123        }
124    ],
125    "triggers":[
126        {
127            "status":"RETURN_CREATED"
128        }
129    ],
130    "userActions":[
131
132    ]
133}

The sample `ReturnOrder`  above has a `ChangeStateGQL`  which changes the status to `RETURN_CREATED`. Secondly, the `OrderReturnComplete`  also contains a `ChangeStateGQL`  which upon execution changes the status to `RETURN_COMPLETE`. To enable these rules to execute you will need to add the relevant statuses to the . The following snippet provides an example of that.

1{
2    "name": "RETURN_CREATED",
3    "entityType": "ORDER",
4    "category": "DONE"
5},
6{
7    "name": "RETURN_COMPLETE",
8    "entityType": "ORDER",
9    "category": "DONE"
10}


2. Configuring the Return Order workflow

After creating a Return , you will need to create a to manage it in its lifecycle. The following rules facilitate that :

CreateReturnFulfilment

The `CreateReturnFulfilment` creates a Return Fulfilment for all return items.

SendEventToUpdateOrderItemAttribute

The `SendEventToUpdateOrderItemAttribute` updates the returnable quantity amount for each returned item.

VerifyBillingAccount

The `VerifyBillingAccount` verifies that a billing exists for the Customer Returning the items.

CreateBillingAccount

The `CreateBillingAccount` creates a Billing for the Customer.

SendEventForReturnToBillingAccount

The `SendEventForReturnToBillingAccount` sends an to the Billing associated with the Customer creating the Return.

SendEventForReturnToOrder

The `SendEventForReturnToOrder` sends an to the associated with the .

Example Return Order workflow

Using the above rules, we have assembled a sample Return Order Workflow. This sample assumes the use of the following environment variables.

  • `retailerID`
  • `version`

You may have to change these values to be in line with the Retailer and version you wish to implement the against.


3. Configuring the Billing Account Workflow

CreateCreditMemoFromReturnOrder

The `CreateCreditMemoFromReturnOrder` when executed creates a for the customer billing with the amount captured in the Return .

SendEventForCreditMemoToReturn

The `SendEventForCreditMemoToReturn` sends an from the to the return .

Example Billing Account workflow

Using the above rules, we have assembled a sample Billing Account Workflow. This sample assumes the use of the following environment variables.

  • `retailerID`
  • `version`

You may have to change these values to be in line with the Retailer and version you wish to implement the against.



Manifest Changes for Fluent Store and Fluent OMS

1. Configure Fluent Store Base Manifest

The following changes will be required to the `fc.mystique.manifest.store` manifest.

Add the Returns Plugin

Add the alongside any others that exist within the . See how to configure UX plugins here

`{`
   "type": "url",
   "src": "/_plugins/"
}

Add the Returns Reference Route

Add the Reference Route alongside the others within the . See Reference Routes for more information.

`{`
   "type": "reference",
   "settingName": "fc...store.fragment."
}

Add the Returns Fragment

Create a new fragment called `fc.mystique.manifest.store.fragment.returns` using the same naming as the reference route added to `fc.mystique.manifest.store.json` in the previous step.

The most up-to-date content for the fragment can be found in the Fluent Store getting started guide.


2. Configure Fluent OMS Manifests

Update OMS Base Manifest

Much like the Store , the needs to be added to the base OMS manifest.

`{`
   "type": "url",
   "src": "/_plugins/"
}

Configure the Order Management Fragment

If you are not using the latest `fc.mystique.manifest.oms.fragment.ordermanagement` reference fragment, then the following additions are required.

In the props of the `orders/:id/:retailerId/:ref` ( Details) page a primary is required for returnOrder.

Optionally add `overrides` to edit the `lodgedLocation` and `pickupLocationtext` text labels.

In the following snippet lines 21-34 are the new configuration which should be added to the `ordermanagement` fragment. The other lines serve to demonstrate where exactly the new and overrides are required.

Returns order management fragment changes

1{
2    "type": "page",
3    "path": "orders/:id/:retailerId/:ref",
4    "component": "fc.page",
5    "data": {
6        "query": "query ($id: ID!, $items_after: String, $items_first: Int, $fulfilments_first: Int, $comments_first: Int, $financialTransactions_first: Int, $returnOrders_first: Int, $ref: String!, $retailerId: ID!) {\n  orderById(id: $id) {\n    id\n    __typename\n    ref\n    status\n    type\n    createdOn\n    updatedOn\n    retailer {\n      id\n      tradingName\n    }\n    customer {\n      id\n      ref\n      firstName\n      lastName\n      primaryEmail\n      primaryPhone\n      timezone\n    }\n    items(after: $items_after, first: $items_first) {\n      edges {\n        node {\n          id\n          ref\n          price\n          quantity\n          totalPrice\n          currency\n          product {\n            name\n          }\n        }\n      }\n    }\n    fulfilmentChoice {\n      id\n      pickupLocationRef\n      deliveryAddress {\n        id\n        name\n        street\n        city\n        state\n        postcode\n        country\n        longitude\n        latitude\n        companyName\n      }\n      deliveryInstruction\n      deliveryType\n      fulfilmentPrice\n    }\n    fulfilments(first: $fulfilments_first) {\n      edges {\n        node {\n          id\n          deliveryType\n          status\n          eta\n          createdOn\n          fromAddress {\n            ref\n            companyName\n            name\n          }\n          toAddress {\n            ref\n            companyName\n            name\n          }\n        }\n      }\n    }\n    financialTransactions(first: $financialTransactions_first) {\n      edges {\n        node {\n          ref\n          createdOn\n          status\n          type\n          cardType\n          paymentMethod\n          total\n        }\n      }\n    }\n    attributes {\n      name\n      type\n      value\n    }\n  }\n  comments(first: $comments_first, entityId: [$id], entityType: \"ORDER\") {\n    edges {\n      node {\n        id\n        text\n        entityId\n        entityType\n        text\n        createdOn\n        updatedOn\n      }\n    }\n  }\n  returnOrders(\n    first: $returnOrders_first\n    order: {ref: $ref, retailer: {id: $retailerId}}\n  ) {\n    edges {\n      node {\n        id\n        ref\n        type\n        status\n        createdOn\n        retailer {\n          id\n        }\n      }\n    }\n  }\n}\n",
7        "variables": {
8            "id": "{{params.id}}",
9            "ref": "{{params.ref}}",
10            "retailerId": "{{params.retailerId}}",
11            "fulfilments_first": 100,
12            "items_first": 100,
13            "comments_first": 100,
14            "financialTransactions_first": 100,
15            "returnOrders_first": 100
16        }
17    },
18    "props": {
19        "title": "{{orderById.ref}} - {{orderById.status}}",
20        "actions": {
21            "primary": [
22                {
23                    "type": "userAction",
24                    "name": "ReturnOrder",
25                    "overrides": {
26                        "lodgedLocation": {
27                            "label": "i18n:fc.om.orders.detail.userAction.returnOrder.lodgedLocation.label"
28                        },
29                        "pickupLocation": {
30                            "label": "i18n:fc.om.orders.detail.userAction.returnOrder.pickupLocation.label"
31                        }
32                    }
33                }
34            ],
35            "secondary": [
36                {
37                    "type": "mutation",
38                    "label": "i18n:fc.om.orders.detail.userAction.addComment",
39                    "name": "createComment",
40                    "overrides": {
41                        "entityId": {
42                            "defaultValue": "{{orderById.id}}"
43                        },
44                        "entityType": {
45                            "defaultValue": "i18n:fc.om.orders.index.userAction.entityType.order"
46                        }
47                    }
48                },
49                {
50                    "type": "userAction",
51                    "name": "OrderAddressChange",
52                    "overrides": {
53                        "deliveryAddress": {
54                            "defaultValue": {
55                                "companyName": "{{orderById.fulfilmentChoice.deliveryAddress.companyName}}",
56                                "name": "{{orderById.fulfilmentChoice.deliveryAddress.name}}",
57                                "street": "{{orderById.fulfilmentChoice.deliveryAddress.street}}",
58                                "city": "{{orderById.fulfilmentChoice.deliveryAddress.city}}",
59                                "postcode": "{{orderById.fulfilmentChoice.deliveryAddress.postcode}}",
60                                "state": "{{orderById.fulfilmentChoice.deliveryAddress.state}}",
61                                "country": "{{orderById.fulfilmentChoice.deliveryAddress.country}}"
62                            }
63                        }
64                    }
65                }
66            ]
67        },
68        "backButtons": [
69            {
70                "path": "orders",
71                "menuLabel": "i18n:fc.om.orders.detail.breadcrumb.backToOrders"
72            },
73            {
74                "path": "customers/{{orderById.customer.id}}/{{orderById.customer.ref}}",
75                "menuLabel": "i18n:fc.om.orders.detail.breadcrumb.viewCustomer"
76            }
77        ]
78    },


Configure a summary card

You can configure a summary component and the field with the setting. Steps for configuring one can be found in the returns summary knowledge base guide