Skip to content

OrderFlow Scripting Guide

Realtime Despatch Software Ltd

Document Version: 4.0.6

Document Built: 2020-10-12

This document and its content is copyright of Realtime Despatch Software Limited. All rights reserved.
You may not, except with our express written permission, distribute, publish or commercially exploit the content.
Any reproduction of part or all of the contents in any form is prohibited.

Batch Selection

Batch Selection

Batch Selection Script

The batch selection script is the script which is used to determine which shipments get picked together, and presented to the packer, in a group.

The batch selection script determines the type of the shipment batch, which in turn defines the grouping mechanism.

For more details on shipment batching, see the 'Batching' chapter in the OrderFlow Advanced Concepts Guide.

Batching Strategies

A wide range of strategies may be employed to determine the batch type of a shipment:

  • whether the order is single or multiline, or even the number of lines
  • the courier
  • the priority (e.g urgent vs normal)
  • products with a particular set of characteristics
  • order lines being picked from a particular area in the warehouse

The actual logic to be used will depend on business requirements, which may in turn either be driven by commitments to the end customer, or a drive for more efficient warehousing operations.

Batch Types

At a technical rather than business level, the purpose of the batch selection script is to identify the appropriate batch type to be used. Available batch types can be found from the Setup -> Batch Types menu, as shown below.

Batch Types

The identifier for the batch types is in the first column, e.g. singleline, multiline. A valid batch selection script will apply business rules to determine one of the available batch types.

If the batch type needed to implement the required business logic is not present, part of the batch selection implementation task will be to create the new batch type(s).

A Simple Example

The example below is a script which implements very simple logic to select the multiline batch type for multiline shipments, and the singleline batch type for single line shipments.

if (value.orderLineCount > 1) return 'multiline';
else return 'singleline';

Note that the return value for the batch selection script is important; the script needs to return the identifier for the required batch type.

Scripting Context

The following scripting variables are available for use in a batch selection script.

value

Holds an instance of the Shipment for which the courier selection is being performed.

values

Holds an instance of the context map for the courier selection script. Doesn't typically get used directly, but does hold additional referencable data as described next.

values.populators

Holds a reference to populators that can be used to further populate data already in the scripting context.

Consider for example, the shipment. The product associated with the first order line in the shipment can be found for each order line using code such as the following:

def orderLine = shipment.orderLines.iterator().next();
def product = orderLine.product;

However not all fields in the products are automatically populated for efficiency reasons. For example, product attributes would not be directly referencable.

In order to get access to product attributes, you would need to fully populate the product, using the following script.

def orderLine = shipment.orderLines.iterator().next();
def product = orderLine.product;
def populatedProduct = values.populators['product'].populate(product);

Note that the populator defined above will not necessarily be available in all scripting environments, but will be available for batch selection.

Useful Expressions

The following expressions and snippets are useful in batch selection scripts:

Priority

The following expression will retrieve the priority of the shipment, which can be used to make decisions on the shipment's urgency.

def priority = value.priority;
Courier

The courier and service reference are often used in batch selection scripts where shipments to go out with particular couriers need to be picked together.

def courier = value.courier?.externalReference;
def serviceCode = value.deliveryMethod?.serviceCode;
Line and Item Count

The following expressions can be used to determine whether a shipment has multiple lines or even multiple items. (A shipment with a single line with a quantity of 2 is considered multi-item.)

def multiLine = (value.orderLineCount > 1);
def multiQuantity = value.orderItemCount > 1;
Address Information

The following expressions retrieve the country code and post code for a shipment, using the implicitAddress expression for generality. The post code value is often used for shipments in more remote destinations on the British Isles.

def countryCode = value.implicitAddress?.countryCode?.toUpperCase();
def postCode = value.implicitAddress?.postCode?.toUpperCase();

A More Complex Example

The example below returns batch types according the following logic:

Batch Types

Batch Priority Quantity Destination
priority-singleitem High Single item GB and the Channel Islands
standard-singleitem Standard Single item GB and the Channel Islands
priority-multiitem High Multi-item GB and the Channel Islands
standard-multiitem Standard Multi-item GB and the Channel Islands
priority-singleitem-de High Single item Germany
standard-singleitem-de Standard Single item Germany
priority-multiitem-de High Multi-item Germany
standard-multiitem-de Standard Multi-item Germany
priority-singleitem-eu High Single item Rest of EU
standard-singleitem-eu Standard Single item Rest of EU
priority-multiitem-eu High Multi-item Rest of EU
standard-multiitem-eu Standard Multi-item Rest of EU

A script which implements this logic is show below:

def multiQuantity = value.orderItemCount > 1;
def countryCode = value.implicitAddress?.countryCode?.toUpperCase();
def domesticCountryCodes = ['GB','UK','JE','GG','IM'];

def priority = shipment.priority;

def suffix = '';
if (countryCode == 'DE') {
    suffix = '-de';
} else if (!domesticCountryCodes.contains(countryCode)) {
    suffix = '-eu';
}

if (priority >= 10) {
    if (multiQuantity) { 
        return 'priority-multiitem'+suffix; 
    } else {   
        return 'priority-singleitem'+suffix;
    }
} else {
    if (multiQuantity) { 
        return 'standard-multiitem'+suffix; 
    } else {   
        return 'standard-singleitem'+suffix;
    }
}

Unit Testing

A Note on Unit Testing

This section requires the that you have in place the OrderFlow integration and scripting environment. If you are interested in having this set up in your environment to enable you to write your own unit tests, please contact the Realtime Despatch support team.

For complex batch selection scripts, a accompanying unit test is highly recommended (and mandatory if implemented by Realtime Despatch Technical Staff).

Writing a unit test should generally be done at the same time or even before the batch selection is written, in line with the principles of Test Driven Development.

Write Test

Unit testing of batch selection can be done using a unit test which extends BaseBatchSelectionScriptTest.

public class OrderFlowBatchSelectionScriptTest extends BaseBatchSelectionScriptTest {

    private Address address;

    protected String getScriptPackageName() {
        String packageName = "rtd.orderflow.batch";
        return packageName;
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        address = new Address();
        address.setLine1("line1");
        shipment.setAddress(address);
    }

    public void testGBShipment() throws Exception {
        address.setCountryCode("GB");

        shipment.getDeliveryMethod().setServiceCode("first");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-singleitem", shipment, "orderflow.batch.selection.script");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-multiitem", shipment, "orderflow.batch.selection.script");
    }

    public void testDEShipment() throws Exception {
        address.setCountryCode("De");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-singleitem-de", shipment, "orderflow.batch.selection.script");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-multiitem-de", shipment, "orderflow.batch.selection.script");
    }

    public void testEUShipment() throws Exception {
        address.setCountryCode("fr");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-singleitem-eu", shipment, "orderflow.batch.selection.script");

        shipment.addOrderLine(newOrderLine());
        expect("orderflow-multiitem-eu", shipment, "orderflow.batch.selection.script");
    }

    private void expect(String expected, Shipment shipment, String scriptFile) {
        assertEquals(expected, run(scriptFile, shipment, expected, false));
    }

    private String run(final String scriptFile, Shipment shipment, String expected, boolean highVolume) {
        Map<String,Object> parameters = new HashMap<String, Object>();
        parameters.put("expected", expected);
        parameters.put("highVolume", highVolume);
        return run(scriptFile, shipment, parameters);
    }

}

The BaseBatchSelectionScriptTest defines a run(scriptFile, shipment, parameters) method which is useful for setting up the scripting context for the batch selection script.

Note that the run() method returns the batch selection returned from the batch selection script, which can be compared with an expected result.

The responsibility of the script developer is to return identify all of the scenarios that need to be covered in the selection script, and ensure that the correct value is returned for each scenario.

Write Script

The script developer will then write a script for which all of the tests pass. The script corresponding to the above unit test is shown below:

def multiLine = (value.orderLineCount > 1);
def multiQuantity = value.orderItemCount > 1;
def countryCode = value.implicitAddress?.countryCode?.toUpperCase();
def domesticCountryCodes = ['GB','UK','JE','GG','IM'];

def suffix = '';
if (countryCode == 'DE') {
    suffix = '-de';
} else if (!domesticCountryCodes.contains(countryCode)) {
    suffix = '-eu';
}

if (multiLine || multiQuantity) { 
    return 'orderflow-multiitem'+suffix; 
}

return 'orderflow-singleitem'+suffix;
IDE Setup

A screenshot showing these in an Eclipse IDE project environment is shown below.

Batch Selection

Note the use of the test package naming convention, which follows the along the lines: ... In line with this convention, the test and script itself a re contained in the batch subpackage.