Broadleaf Microservices
  • v1.0.0-latest-prod

Fulfillment Splitting

Overview

In some cases Broadleaf needs to split up an existing Order Fulfillment, whether it’s being split so that each vendor has its own fulfillment (see Order Submission), or only part of a fulfillment is changing to a new status (see Changing Status).

Input and Output

There are two inputs when splitting an Order Fulfillment:

  1. Original Fulfillment - what is the fulfillment we need to split?

  2. Item Quantities - which items do we want to "split out" from the original?

    1. This is a list, where each element represents one new fulfillment we want to "split out" from the original

    2. Each element contains a map of Order Fulfillment Item IDs to the amount of that item we want to apportion to this particular new fulfillment

    3. All item amounts not specified here will stay in the original fulfillment

Based on that, our output will be a list of Order Fulfillments containing:

  1. A fulfillment which used to be the original, now containing all leftover items not specifically requested to split into new fulfillments

  2. All the new fulfillments, each containing the item quantities requested to be split

Example

Fulfillment Split Item Quantities

In this example, we request to split Fulfillment 1 so that two new fulfillments are created - one with 20 of Item 2 and 10 of Item 3, and another with 10 of Item 3 and 40 of Item 4.

Two new fulfillments, Fulfillment 2 and Fulfillment 3, are created to hold those items as requested. Everything else remains in Fulfillment 1, which was 10 of Item 1, and the leftover 10 of Item 3.

Splitting Monetary Amounts

Handling monetary amounts when splitting fulfillments is tricky. Unit amounts, like merchandise unit prices, don’t need to change, but calculated monetary values such as fulfillment charges, totals, and taxes need to be distributed appropriately across the new fulfillments.

At a high level, the process for this splitting is:

  1. Split the items into separate lists based on requested item quantities

    1. If one item has been split up, distribute monetary amounts proportionally based on quantity, but do not round

  2. Create the new split fulfillments

  3. Distribute the original fulfillment-level monetary amounts to each fulfillment, but do not round

    1. Most are distributed based on the fulfillments' item merchandise totals

    2. Fulfillment-level fulfillment (shipping) tax is distributed based on the items' fulfillment (shipping) taxable totals

      1. The splitting proportion is dependent on the band field of the Fulfillment Calculator that was used. Out-of-box, this could be the item price or weight.

    3. The grand totals for the fulfillments are calculated from merchandise, fulfillment, and tax totals

  4. Round all the monetary amounts (more details below)

It’s important that all rounding waits until the end. Rounding each value in the middle of splitting could result in the new fulfillments having the incorrect monetary amounts due to the accumulation of up-rounding or down-rounding.

Rounding the Split Amounts

Even when we wait to round until the end, it can still be tricky.

Penny Rounding Issue

Consider a scenario where $10.00 needs to be split between four buckets in a ratio of 1 : 2 : 3 : 3. When we split the amount based on this proportion, we have approximately these values and the amounts they normally would round to:

  • $1.111111…​ → $1.11

  • $2.222222…​ → $2.22

  • $3.333333…​ → $3.33

  • $3.333333…​ → $3.33

However, this only adds up to $9.99, not $10.00. This is a classic case of a Penny Rounding Issue.

This situation is likely to occur during Order Fulfillment splitting, and is resolved in Broadleaf by this method:

  1. Round all the values down

  2. Calculate the difference from the original total

  3. Sort the split values by their sub-penny remainder

  4. Distribute a penny to the value with the highest sub-penny remainder

  5. Repeat

For our example, this resolves the problem by changing one of the $3.33 to $3.34.

Sum Totals and the Penny Rounding Issue

The Penny Rounding Issue becomes doubly complex due when we are splitting values that also sum into some other total.

Consider a different scenario where we want to split a simple fulfillment exactly in half. This fulfillment has a merchandise total of $2.00, a shipping charge of $0.95, and a tax of $0.15. Before splitting, the total cost is $3.10. Since we’re splitting it in half, each new half should have a total cost of $1.55.

The merchandise total splits into halves of $1.00 easily with no need for rounding.

Using the method of rounding described above, the shipping charge gets split into $0.48 for the first half and $0.47 for the second half.

Also using that method of rounding, the tax gets split into $0.08 for the first half and $0.07 for the second half.

Our rounded fulfillments look like this:

Table 1. Rounded Fulfillments Split in Half
Fulfillment 1 Fulfillment 2 Total

Merchandise Total

$1.00

$1.00

$2.00

Shipping Charge

$0.48

$0.47

$0.95

Tax

$0.08

$0.07

$0.15

Total

$1.56

$1.54

$3.10

Each value does add up to the amount from the original fulfillment, but the total sum within each fulfillment doesn’t match, even though we should have been able to reach $1.55 for each of these.

To solve this, the Broadleaf solution considers not only the penny distribution within a single split amount, but also across multiple amounts in order to add up to the expected total ($1.55 in this case). We must iterate over each of the values in the matrix above while keeping track of the penny difference between the current value-wise total and its expected amount, as well as the penny difference between the current fulfillment-wise total and its expected amount. Pennies are distributed only until each of those totals matches the expected amount.

Rounding Walkthrough

Calculate non-rounded split values Sum the non-rounded values Fill in expected totals Round split values down (note remainder) and sum current totals Calculate pennies off from expected totals Distribute penny to value in Fulfillment 1 which has pennies off Distribute penny to value in Fulfillment 2 which has pennies off Final rounded values with correct totals

Customization

The default implementation splits all applicable values when a fulfillment is split. In order to change that behavior, for example to always retain a fulfillment charge even when some items are cancelled, the DefaultFulfillmentSplittingService and the DefaultFulfillmentSplitRoundingService can be extended or replaced with a custom implementation.