How to enable Returns in Fluent OMS and Fluent Store
Author:
Fluent Commerce
Changed on:
8 July 2024
Overview
The Returns feature is available for any OMX web apps. The following guide will step through the areas of configuration to enable the Returns 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 workflow 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
Language: graphqlschema
Name: RETURN_ORCHESTRATED create GraphQL
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: RETURN_CONDITION create GraphQL
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: RETURN_REASON create GraphQL
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: DEFAULT_RETURN_DESTINATION_LOCATION create GraphQL
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: DEFAULT_TAX_TYPE create GraphQL
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: HOTFIX_NEW_APOLLO_CLIENT_ENABLED create GraphQL
Description:
[Warning: empty required content area]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
Language: graphqlschema
Name: Role Creation - INSTORE_RETURNS Role Creation
Description:
[Warning: empty required content area]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}
Language: graphqlschema
Name: Role Assignment
Description:
[Warning: empty required content area]Workflow Configuration
The following steps provide a reference solution of how workflows can be configured to enable Returns. 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 order workflows and create a return order workflow and a billing workflow.
1. Configuring the Order workflow
The following supplied Returns rules can be added to the order workflow to allow a Return order to be created from an existing order.
ValidateOrderQty
The
`ValidateOrderQty`
`ReturnOrder`
CreateReturnOrderFromOrder
The
`CreateReturnOrderFromOrder`
UpdateOrderItemAttribute
The
`UpdateOrderItemAttribute`
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 Order workflow.
If you are using the sample rulesets, please replace the
`subtype`
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}
Language: json
Name: Order Returns Rulesets
Description:
[Warning: empty required content area]The sample
`ReturnOrder`
`ChangeStateGQL`
`RETURN_CREATED`
`OrderReturnComplete`
`ChangeStateGQL`
`RETURN_COMPLETE`
1{
2 "name": "RETURN_CREATED",
3 "entityType": "ORDER",
4 "category": "DONE"
5},
6{
7 "name": "RETURN_COMPLETE",
8 "entityType": "ORDER",
9 "category": "DONE"
10}
Language: json
Name: Return Statuses
Description:
[Warning: empty required content area]2. Configuring the Return Order workflow
After creating a Return Order entity, you will need to create a workflow to manage it in its lifecycle. The following rules facilitate that process:
CreateReturnFulfilment
The
`CreateReturnFulfilment`
SendEventToUpdateOrderItemAttribute
The
`SendEventToUpdateOrderItemAttribute`
VerifyBillingAccount
The
`VerifyBillingAccount`
CreateBillingAccount
The
`CreateBillingAccount`
SendEventForReturnToBillingAccount
The
`SendEventForReturnToBillingAccount`
SendEventForReturnToOrder
The
`SendEventForReturnToOrder`
Example Return Order workflow
Using the above rules, we have assembled a sample Return Order Workflow. This workflow 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 workflow against.
3. Configuring the Billing Account Workflow
CreateCreditMemoFromReturnOrder
The
`CreateCreditMemoFromReturnOrder`
SendEventForCreditMemoToReturn
The
`SendEventForCreditMemoToReturn`
Example Billing Account workflow
Using the above rules, we have assembled a sample Billing Account Workflow. This workflow 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 workflow 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`
Add the Returns Plugin
Add the Returns Plugin alongside any others that exist within the manifest. See how to configure UX plugins here
`{`
"type": "url",
"src": "/_plugins/returns"
}
Add the Returns Reference Route
Add the Returns Reference Route alongside the others within the manifest. See Reference Routes for more information.
`{`
"type": "reference",
"settingName": "fc.mystique.manifest.store.fragment.returns"
}
Add the Returns Fragment
Create a new fragment called
`fc.mystique.manifest.store.fragment.returns`
`fc.mystique.manifest.store.json`
The most up-to-date content for the Returns fragment can be found in the Fluent Store getting started guide.
2. Configure Fluent OMS Manifests
Update OMS Base Manifest
Much like the Store manifest, the Returns plugin needs to be added to the base OMS manifest.
`{`
"type": "url",
"src": "/_plugins/returns"
}
Configure the Order Management Fragment
If you are not using the latest
`fc.mystique.manifest.oms.fragment.ordermanagement`
In the props of the
`orders/:id/:retailerId/:ref`
Optionally add
`overrides`
`lodgedLocation`
`pickupLocationtext`
In the following snippet lines 21-34 are the new user action configuration which should be added to the
`ordermanagement`
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 },
Language: json
Name: Returns ordermanagement fragment changes
Description:
[Warning: empty required content area]Configure a summary card
You can configure a summary component and the returns field with the Returns setting. Steps for configuring one can be found in the returns summary knowledge base guide