Implementing Locker Collection
Author:
Ashwin Anand
Changed on:
5 Sept 2025
Key Points
- A simple way to illustrate how to enable the locker collection option in the Click & Collect workflow
- Illustrates a way to expose the locker information to the order sales channel via the Fulfilment Options workflow
- Access to the lockers is assumed via the carrier company delivering to the locker location (i.e. there is no hardware integration)
Steps
The Problem
Retailers are constantly looking for ways to improve their delivery and collection options to their customers, whether it be same/next day delivery or curb side click and collect.
Depending upon the product range, enabling customers to collect their orders from lockers is an area that has seen some growth. The reasons could be as simple as additional convenience option for the customer, particularly in densely populated metro areas where locker banks operate 24/7. Another reason could be to increase their customer segment by going into areas where their retail footprint is lower due to the regional locality (i.e. outside of metro areas).
By consolidating multiple orders at a single location, lockers improve delivery efficiency, reduce failed deliveries and provide a secure, self-service option that enhances the overall customer experience. This flexible solution not only meets the growing demand for convenience but also helps retailers stay competitive in an evolving market.
Before you begin
Objective:
Locker-Based Pickup
Prerequisites (Fluent):
- Locker locations (stores) with required location attributes
- Existing network “Click & Collect” (CC_NETWORK)
- Existing “Fulfillment Options” and “Click & Collect” order workflow
Implementation/Configuration Steps:
To implement the physical collection locker system, leverage the existing “Fulfillment Options” and “Click & Collect” order workflows.
The “Fulfillment Options” workflow identifies the three nearest plans, options, or stores based on the customer’s address, allowing them to choose one before placing an order. Once the order is placed, the “Click & Collect” order workflow is triggered.
1. “Fulfillment options” Workflow Implementation/Configuration Steps:
Locker locations (Stores: VR01, VR02, VR03, VR04 & VR05) configuration in the Fluent OMS with required location attributes:
- This configuration represents physical locker locations of type Store in the Fluent OMS web application.
- In the back-end logic, utilise the locker store's parameters like ref, name, address, latitude and longitude to check distances from the address.

- Location attribute is used to represent the locker store. The attribute name
`IS_LOCKER _STORE`
represents the locker store. If the value of such an attribute is`true`
, it is treated as a locker store.

Network configuration in Fluent OMS:
- Utilise the click & collect related network, which is configured within the related workflow.
- In the back-end workflow, the logic accesses the network and the locations within it. Only locations that are part of the
`CC_NETWORK`
and have an`ACTIVE`
status are considered for the different rules checks of the CC order.

Ruleset related to network (Workflow: “Fulfilment options: Default”):
1{ "name": "GetLocations",
2 "description": "This ruleset will attempt to Fulfilment Plans.",
3 "type": "FULFILMENT_OPTIONS",
4 "eventType": "NORMAL",
5 "rules": [{
6 "name": "<project-namespace>.base.GetLocationsForNetworkFO",
7 "props": {
8 "eventName": "FilterByLocationsRangedToProductsFO",
9 "networkRef": "CC_NETWORK",
10 "locationStatuses": [ "ACTIVE"]}}],
11 "triggers": [{"status": "RECEIVED"}],
12 "userActions": []}
13
Code sample: Rule: `GetLocationsForNetworkFO`
1final String eventName = context.getProp(PROP_EVENT_NAME);
2final String networkRef = context.getProp(PROP_NETWORK_REF);
3List<String> locationStatuses = context.getPropList(PROP_LOCATION_STATUSES, String.class);
4final List<ProductAttributesDto> productAttributesDtoList = context.getEvent().getAttributeList(EVENT_ATTRIBUTE_PRODUCT_FILTER_CRITERIA, ProductAttributesDto.class);
5List<String> selectedLocationList = context.getEvent().getAttributeList(EVENT_ATTRIBUTE_SELECTED_LOCATIONS, String.class);
6
7
8//Created a list and adding the active location from the network
9LocationService locationService = new LocationService(context);
10List<LocationEntity> locationEntityList = locationService.loadLocationsForNwWithLocRefList(
11 networkRef,
12 selectedLocationList,
13 locationStatuses );
14List<LocationEntity> lockerLocationEntityList = new ArrayList<>();
15for (LocationEntity locationEntity : locationEntityList) {
16if(CollectionUtils.isNotEmpty(locationEntity.getAttributes())) { for (AttributeEntity attributeEntity : locationEntity.getAttributes()) { if (attributeEntity.getName().equalsIgnoreCase(Constants.IS_LOCKER_STORE) && attributeEntity.getValue().toString().equalsIgnoreCase(Constants.TRUE)) { lockerLocationEntityList.add(locationEntity);
17}}}}
18Map<String, Object> attributes = new HashMap<>();
19attributes.put(EVENT_ATTRIBUTE_LOCATIONS, locationEntityList);
20attributes.put(EVENT_ATTRIBUTE_LOCKER_LOCATIONS, lockerLocationEntityList);
21attributes.put(EVENT_ATTRIBUTE_PRODUCT_FILTER_CRITERIA,productAttributesDtoList);
Add locker locations (VR01, VR02, VR03, VR04 & VR05) in network (`CC_NETWORK`
):
- Only locations that are part of the
`CC_NETWORK`
and have an`ACTIVE`
status are considered for the different rules checks of the CC order. - The logic employs locker locations (VR01, VR02, VR03, VR04, and VR05) within the
`GetLocationsForNetworkFO`
rules’ Java class, utilizing the network specified in the fulfillment option workflow.
Inventory checks (Virtual Position):
- Since most of the business requirements related to locker collection points have no inventory check related constraint, inventory check can be disabled to avoid the stock related checks.
- The code sample below gives the view of how the inventory check is bypassed in such a scenario.
Ruleset related to inventory checks (Workflow: “Fulfillment options: Default”):
1 { "name": "SearchInventoryAtLocations",
2 "description": "This ruleset will attempt to Fulfilment Plans.",
3 "type": "FULFILMENT_OPTIONS",
4 "eventType": "NORMAL",
5 "rules": [{
6 "name": "<project- namespace>.base.SearchInventoryAtLocationsFO",
7 "props": {
8 "eventName": "IdentifyFulfilmentPlans",
9 "virtualCatalogueRef": "BASE:1"}}],
10 "triggers": [{"status": "RECEIVED"}],
11 "userActions": []}
12
Code sample: Rule: `SearchInventoryAtLocationsFO`
1//Fetching the locker location in the list
2List<LocationEntity> lockerLocations = context.getEvent().getAttributeList(EVENT_ATTRIBUTE_LOCKER_LOCATIONS, LocationEntity.class);
3
4
5VirtualService virtualService = new VirtualService(context);
6
7List<VirtualPositionEntity> virtualPositions;
8
9List<String> productRefs = fulfilmentOptionEntity.getProducts().stream()
10 .map(FulfilmentOptionProductEntity::getProductRef)
11 .collect(Collectors.toList());
12
13
14//Checking whether locker locations are part of the list and if locker location is present in the list adding the locker location ref in the newly created list
15List<String> lockerLocationRefs = new ArrayList<>();
16
17if(lockerLocations != null && lockerLocations.size() > 0) {lockerLocationRefs = lockerLocations.stream().map(BaseEntity::getRef).collect(Collectors.toList());
18}
19
20
21//If the locker location ref is present in the “lockerLocationRefs” list setting the inventory quantity to infinity (999999999)
22if(lockerLocationRefs.size() > 0) {
23 for (String locationRef : lockerLocationRefs) {
24 for (String productRef : productRefs) {
25 VirtualPositionEntity virtualPositionEntity = new VirtualPositionEntity();
26 virtualPositionEntity.setProductRef(productRef);
27 virtualPositionEntity.setQuantity(Constants.INFINITE_INVENTORY_QUANTITY);
28virtualPositionEntity.setGroupRef(locationRef);
29virtualPositions.add(virtualPositionEntity);
30}}}
31
Fulfillment Plans:
- From the filtered locations based on all the above-mentioned checks the logic proposes the 3 nearest fulfillment plans/locations from the address so customers can select any of them and place an order.
- Since locker stores serve as collection points without their own inventory, orders for these lockers are fulfilled from the nearest store or location. Therefore, if the fulfillment plan includes locker collection points, those orders are always fulfilled from the stores or locations associated with these pickup points.
- In the back-end logic, the workflow refers to the properties that denote the relation between the locker Pick-up Store ID and their corresponding fulfillment store (where the actual inventory will be fulfilled from). Based on the corresponding tagging to stores, the appropriate locker fulfillment store is assigned to the locker collection location as the fulfillment store.
- So for example, Locker Collection Points: VR01, VR02, VR03, VR04 & VR05 delivery are tagged to
`FULFILMENT_REF = “VC63”`
store. Similarly, Locker Collection Points: SR01, SR02 & SR03 delivery are tagged to`FULFILMENT_REF = “SC03”`
store for their respective fulfillment.
Ruleset related to fulfillment plans (Workflow: “Fulfillment options: Default”):
1{ "name": "CreateFulfilmentPlans",
2 "description": "This ruleset will attempt to Fulfilment Plans.",
3 "type": "FULFILMENT_OPTIONS",
4 "eventType": "NORMAL",
5 "rules": [{
6 "name": "<project- namespace>.base.CreateFulfilmentPlans",
7 "props": {
8 "lockerFulfilmentDTCVICRegion": "VC63",
9 "lockerFulfilmentDTCSA01Region": "SC03",
10 "lockerFulfilmentDTCSA02Region": "SC15",
11 "lockerCollectionPointsVICRegion":
12 ["VR01","VR02","VR03","VR04","VR05"],
13 "lockerCollectionPointsSA01Region":
14 ["SR01","SR02","SR03"],
15 "lockerCollectionPointsSA02Region": ["SR05","SR06"]}}, {
16 "name": "FLUENTRETAIL.base.ChangeState",
17 "props": {"status": "OPTIONS_PROVIDED"}}],
18 "triggers": [{"status": "RECEIVED"}],
19 "userActions": []}
20
Code sample: Rule: `CreateFulfilmentPlans`
1//Iterating the attribute in the map
2Map<String, FulfilmentPlanFulfilmentEntity> fulfilmentPlanFulfilmentMap = context.getEvent().getAttribute(EVENT_ATTRIBUTE_PROPOSED_FULFILMENT_PLANS, Map.class);
3
4Map<String, ProposedFulfilment> proposedFulfilments = context.getEvent().getAttribute(EVENT_ATTRIBUTE_PROPOSED_FULFILMENTS, Map.class);
5
6
7//locker collection points list and locker order fulfilment DTC from rule props
8final String lockerFulfilmentDTCVICRegion = context.getProp(PROP_LOCKER_FULFILMENT_DTC_VIC_REGION);
9
10final String lockerFulfilmentDTCSA01Region = context.getProp(PROP_LOCKER_FULFILMENT_DTC_SA01_REGION);
11
12final String lockerFulfilmentDTCSA02Region = context.getProp(PROP_LOCKER_FULFILMENT_DTC_SA02_REGION);
13
14final List<String> lockerCollectionPointsVICRegion = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_VIC_REGION, String.class);
15
16final List<String> lockerCollectionPointsSA01Region = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_SA01_REGION, String.class);
17
18final List<String> lockerCollectionPointsSA02Region = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_SA02_REGION, String.class);
19
20
21//Iterating the fulfilment plan entry set
22int sortCount = 0;
23if (fulfilmentPlanFulfilmentMap != null && fulfilmentPlanFulfilmentMap.size() > 0) {
24for (Map.Entry<String, FulfilmentPlanFulfilmentEntity> entry : fulfilmentPlanFulfilmentMap.entrySet()) {
25
26LocationService locationService = new LocationService(context);
27Map<String,Boolean> paramsToInclude = new HashMap<>();
28 paramsToInclude.put(LOCATION_INCLUDE_ATTRIBUTES,Boolean.TRUE); paramsToInclude.put(LOCATION_PRIMARY_ADDRESS,Boolean.TRUE);
29String fulfilmentRef = entry.getValue().getLocationRef();
30GetLocationByRefQuery.Location location= locationService.getLocationByRef(fulfilmentRef,paramsToInclude);
31if(CollectionUtils.isNotEmpty(location.attributes())) {
32List<GetLocationByRefQuery.Attribute> locationAttributeList = location.attributes();
33
34
35//Iterating the location attribute
36for(GetLocationByRefQuery.Attribute attribute : locationAttributeList){
37
38//If input location from fulfilment plan is LOCKER and present in the list of LOCKER collection points then accordingly assigning LOCKER fulfilment store if(attribute.name().equalsIgnoreCase(Constants.IS_LOCKER_STORE) && attribute.value().toString().equalsIgnoreCase(Constants.TRUE)){
39if (lockerCollectionPointsVICRegion.contains(fulfilmentRef))
40fulfilmentRef = lockerFulfilmentDTCVICRegion;
41else if lockerCollectionPointsSA01Region.contains(fulfilmentRef))
42fulfilmentRef = lockerFulfilmentDTCSA01Region;
43else if lockerCollectionPointsSA02Region.contains(fulfilmentRef))fulfilmentRef = lockerFulfilmentDTCSA02Region;}}}
44
45
46//Creating a list and adding the required fulfilment plan inputs
47List<CreateFulfilmentPlanFulfilmentItemInput> fulfilmentPlanFulfilmentItems = new ArrayList<>();
48for (FulfilmentPlanFulfilmentItemEntity fulfilmentPlanFulfilmentItem : entry.getValue().getItems()) { CreateFulfilmentPlanFulfilmentItemInput fulfilmentPlanFulfilmentItemInput =
49CreateFulfilmentPlanFulfilmentItemInput.builder() .availableQuantity(fulfilmentPlanFulfilmentItem.getAvailableQuantity()) .requestedQuantity(fulfilmentPlanFulfilmentItem.getRequestedQuantity()) .productRef(fulfilmentPlanFulfilmentItem.getProductRef()) .catalogueRef(fulfilmentPlanFulfilmentItem.getCatalogueRef()).build(); fulfilmentPlanFulfilmentItems.add(fulfilmentPlanFulfilmentItemInput);}
50CreateFulfilmentPlanFulfilmentInput fulfilmentPlanFulfilmentInput = CreateFulfilmentPlanFulfilmentInput.builder()
51 .fulfilmentType(PROPOSED)
52 .locationRef(entry.getKey())
53 .items(fulfilmentPlanFulfilmentItems)
54 .build();
55
56List<CreateFulfilmentPlanFulfilmentInput> fulfilmentPlanInputs = new ArrayList<>();
57fulfilmentPlanInputs.add(fulfilmentPlanFulfilmentInput);
58
59
60List<AttributeInput> fulfilmentPlanAttributes = new ArrayList<>();
61sortCount++;
62AttributeInput attributeInput = AttributeInput.builder()
63 .name(SORT_POSITION)
64 .type("String")
65 .value(String.valueOf(sortCount))
66 .build();
67fulfilmentPlanAttributes.add(attributeInput);
68
69attributeInput = AttributeInput.builder()
70 .name(FULFILMENT_REF)
71 .type("String")
72 .value(String.valueOf(fulfilmentRef))
73 .build();
74fulfilmentPlanAttributes.add(attributeInput);
75
76attributeInput = AttributeInput.builder()
77 .name(DISTANCE_METRE)
78 .type("String") .value(String.valueOf(proposedFulfilments.get(entry.getKey()).getDistance()))
79 .build();
80fulfilmentPlanAttributes.add(attributeInput);
81
82//Mutation to create a plan and mapping it to the fulfilment option
83context.action().mutation(CreateFulfilmentPlanMutation.builder().input(CreateFulfilmentPlanInput.builder() .fulfilmentOptionId(FulfilmentOptionId.builder().id(context.getEvent().getEntityId()).build())
84 .ref(UUID.randomUUID().toString())
85 .retailerId( Integer.parseInt(context.getEvent().getRetailerId()))
86 .type(TYPE_DEFAULT)
87 .attributes(fulfilmentPlanAttributes)
88 .fulfilments(fulfilmentPlanInputs).build())
89 .build());
90}}
91
2. “Order: Click & Collect” Workflow Implementation/Configuration Steps:
The “Click & Collect” workflow executes when a customer places a CC order.
On the eCommerce checkout screen, customers can view three plans or stores as outlined in the fulfillment plans and select one before placing a Click & Collect order. Each plan has a unique plan ID. Fluent Commerce receives the plan ID of the selected location. After the order is placed, it is fulfilled from the fulfillment store associated with the chosen locker location or store.
Create fulfillment based on the fulfillment plan for the CC order:
- If the fulfillment plan exists and the pickup/collection point is “Locker location/store”:
- If a fulfillment plan exists for the order and the fulfillment location reference in the plan details is a locker location, the order is fulfilled from the locker fulfillment store. For locker orders, the “TO Location” is one of the locker locations (VR01, VR02, VR03, VR04, or VR05), and the “FROM Location” is always “VC63.”
- This ruleset calls the event
`UpdateOrderAttributeForCollectionPoint`
as the next ruleset.
Ruleset related to the create fulfillment for CC order (Workflow: “Order: Click & Collect”):
1{ "name": "CreateFulfilment",
2 "description": "create a fulfilment using the fulfilment plan.",
3 "type": "ORDER",
4 "eventType": "NORMAL",
5 "rules": [{
6 "name": "<project- namespace>.base.CreateFulfilmentCC",
7 "props": {
8 "lockerFulfilmentDTCVICRegion": "VC63",
9 "lockerFulfilmentDTCSA01Region": "SC03",
10 "lockerFulfilmentDTCSA02Region": "SC15",
11 "lockerCollectionPointsVICRegion":
12 ["VR01","VR02","VR03","VR04","VR05"],
13 "lockerCollectionPointsSA01Region":
14 ["SR01","SR02","SR03"],
15 "lockerCollectionPointsSA02Region": ["SR05","SR06"]}}, {
16 "name": "<project-namespace>.base.SendEventIfLocationIsCollectionPoint",
17 "props": {
18 "eventName": "UpdateOrderAttributeForCollectionPoint"}}],
19 "triggers": [{"status": "BOOKED"}], "userActions": []}
20
Code sample: Rule: `CreateFulfillmentCC`
1//locker collection points from rule props
2final String lockerFulfilmentDTCVICRegion = context.getProp(PROP_LOCKER_FULFILMENT_DTC_VIC_REGION);
3final String lockerFulfilmentDTCSA01Region = context.getProp(PROP_LOCKER_FULFILMENT_DTC_SA01_REGION);
4final String lockerFulfilmentDTCSA02Region = context.getProp(PROP_LOCKER_FULFILMENT_DTC_SA02_REGION);
5//locker order fulfilment DTC from rule props
6final List<String> lockerCollectionPointsVICRegion = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_VIC_REGION, String.class);
7final List<String> lockerCollectionPointsSA01Region = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_SA01_REGION, String.class);
8final List<String> lockerCollectionPointsSA02Region = context.getPropList(PROP_LOCKER_COLLECTION_POINTS_SA02_REGION, String.class);
9
10
11//Iterating the fulfilment plan ID from the order
12OrderService orderService = new OrderService(context);
13OrderEntity order = orderService.getOrderByIdOrFail(context.getEvent().getEntityId());
14FulfilmentPlanService fulfilmentPlanService = new FulfilmentPlanService(context);
15FulfilmentPlanEntity fulfilmentPlan =
16fulfilmentPlanService.getFulfilmentPlanByIdOrFail((String) order.getAttributes().get(FULFILMENT_PLAN_ID));
17
18
19//Iterating the fulfilment plan details
20for(FulfilmentPlanFulfilmentEntity fulfilmentPlanFulfilment : fulfilmentPlan.getFulfilments()){
21List<CreateFulfilmentItemWithFulfilmentInput> items = orderService.createFulfilmentItemListCC(fulfilmentPlanFulfilment,order);
22String orderFulfilmentStore = fulfilmentPlanFulfilment.getLocationRef();
23LocationService locationService = new LocationService(context);
24Map<String,Boolean> paramsToInclude = new HashMap<>();
25paramsToInclude.put(LOCATION_INCLUDE_ATTRIBUTES,Boolean.TRUE);
26paramsToInclude.put(LOCATION_PRIMARY_ADDRESS,Boolean.TRUE);
27GetLocationByRefQuery.Location location= locationService.getLocationByRef(fulfilmentPlanFulfilment.getLocationRef(),paramsToInclude);
28if(CollectionUtils.isNotEmpty(location.attributes())) {
29List<GetLocationByRefQuery.Attribute> locationAttributeList = location.attributes();
30
31//Iterating the location attribute
32for(GetLocationByRefQuery.Attribute attribute : locationAttributeList){
33
34//If the input location from the fulfilment plan is locker location and present in the list of locker collection points then accordingly assign locker fulfilment store if(attribute.name().equalsIgnoreCase(Constants.IS_LOCKER_STORE) && attribute.value().toString().equalsIgnoreCase(Constants.TRUE)){
35if(lockerCollectionPointsVICRegion.contains(orderFulfilmentStore))
36orderFulfilmentStore = lockerFulfilmentDTCVICRegion;
37else if (lockerCollectionPointsSA01Region.contains(orderFulfilmentStore))
38orderFulfilmentStore = lockerFulfilmentDTCSA01Region;
39else if (lockerCollectionPointsSA02Region.contains(orderFulfilmentStore))
40orderFulfilmentStore = lockerFulfilmentDTCSA02Region;}}}
41
42//Creating the fulfilment for a CC order based on the assigned fulfilment store
43orderService.createFulfilment(order,orderFulfilmentStore,items,null);}
44
Add a new ruleset `UpdateOrderAttributeForCollectionPoint`
for the “Order: Click & Collect” Workflow:
- Add the ruleset
`UpdateOrderAttributeForCollectionPoint`
to differentiate the locker collection point orders from the other orders. - The order includes an attribute field, and by applying this new rule, a new attribute is added to identify the locker collection point order.
- This rule updates the order attribute for locker collection point orders as below:
`"attributeName": "IS_LOCKER_ORDER", `
`"attributeType": "STRING", `
`"attributeValue": "TRUE"`
Ruleset `UpdateOrderAttributeForCollectionPoint`
(Workflow: “Order: Click & Collect”):
1{ "name": "UpdateOrderAttributeForCollectionPoint",
2 "description": "add IS_LOCKER_ORDER attribute to order level",
3 "type": "ORDER",
4 "eventType": "NORMAL",
5 "rules": [{
6 "name": "<project- namespace>.base.UpdateOrderAttributeForCollectionPoint",
7 "props": {
8 "attributeName": "IS_LOCKER_ORDER",
9 "attributeType": "STRING",
10 "attributeValue": "TRUE"}}],
11 "triggers": [{"status": "BOOKED"}],
12 "userActions": []}
13
Code sample: Rule: `UpdateOrderAttributeForCollectionPoint`
1//Iterating attribute name, type & value from rule props
2final String attributeName = context.getProp(PROP_ATTRIBUTE_NAME, String.class);
3final String attributeType = context.getProp(PROP_ATTRIBUTE_TYPE, String.class);
4final String attributeValue = context.getProp(PROP_ATTRIBUTE_VALUE, String.class);
5
6
7//For missing attribute props throwing an exception
8if(StringUtils.isEmpty(attributeName) || StringUtils.isEmpty(attributeType) || StringUtils.isEmpty(attributeValue) ){
9throw new IllegalArgumentException("Rule is missing the mandatory properties attributeName: " + attributeName + ", attributeType: " + attributeType + ", attributeValue: " + attributeValue);}
10
11//Add attribute information in a list format
12List<AttributeInput> orderAttributeList = new ArrayList<>();
13AttributeInput orderAttribute = AttributeInput.builder()
14.name(attributeName).type(attributeType).value(attributeValue).build();
15orderAttributeList.add(orderAttribute);
16
17//Update the Order with the attribute information
18UpdateOrderInput orderInput = UpdateOrderInput.builder()
19.id(context.getEvent().getEntityId()).attributes(orderAttributeList).build();
20UpdateOrderMutation orderMutation = UpdateOrderMutation.builder().input(orderInput).build();
21context.action().mutation(orderMutation);
Conclusion:
- After implementing this feature, new stores can be added and designated as lockers using the location attribute. The attribute
`IS_LOCKER_STORE`
is used to identify locker stores, and if its value is set to “true,” the store is treated as a locker collection point. - The “Fulfillment Options: Default” workflow includes a ruleset for the distance radius threshold. Locations within “x” kms from the address are considered for the fulfillment option plan or subsequent checks, allowing for store selection based on proximity.
- From the filtered locations that pass all “Fulfillment Options: Default” workflow checks, customers can view the three nearest fulfillment plans, options, or stores on the front-end screen when placing a Click & Collect order. They can then select one of these options before finalizing their order, ensuring they can pick up their order from the nearest and most convenient locker collection store.
Except as otherwise stated in the Extend Knowledge Content site policy, the content on this page is licensed under the Creative Commons Attribution 4.0 Licence, and any code samples that appear on this page are licensed under the Apache 2.0 Licence, unless any code sample forms part of the Fluent Order Management Platform code. Neither of these licences apply to any content on any other page that can be reached via a link on this page unless otherwise specified on that other page. If you wish to use any of the Extend Knowledge Content, you must do so in compliance with the licenses referred to above and the Extend Knowledge Content site policy, including attribution in the manner set out on this page.