Fluent Commerce Logo
Docs

Order Module 2.2.0 - Greater Sourcing Control and More Reliable Re-Sourcing

Release

Author:

Kirill Gaiduk

Changed on:

13 Aug 2026

Target release date:2026-08-13
Release status:Released

Description

Order Module 2.2.0 extends the Responsive Sourcing Framework (RSF) with greater flexibility, foundational fulfillment options integration enhancements, and reliability improvements.Sourcing profiles now give you full control over the criteria applied during strategy evaluation. Previously, rejected-location exclusion behavior was enforced by the framework regardless of your configuration. With this release, rejected location exclusion criterion becomes a standard reference criterion. Add it to your strategies when you need it, or leave it out when you don’t.The sourcing utilities library introduces foundational enhancements for Responsive Sourcing integration with fulfillment options workflows, including:
  • ETA calculator injection support
  • Multi-attempt fulfillment plan generation
This release also addresses a set of reliability improvements in the sourcing process:
  • Concurrent re-sourcing from simultaneous fulfillment expiry no longer produces duplicate fulfillments or incorrect allocation quantities (requires `fc.api.fulfilment.uniqueness` configuration)
  • Fallback sourcing now selects locations based on the items still to be fulfilled at each step, not the full original order
  • All eligible locations in the sourcing network are now evaluated during primary sourcing
  • Primary sourcing exits early when the available stock across candidate locations cannot cover the order, reducing unnecessary processing time
  • Escalated fulfillments - routed to customer service when Responsive Sourcing cannot fulfill all items after primary and fallback strategies - now contain only unsourced items with their correct product references, keeping already allocated items separate
  • Locations with `AT_RISK` and `OUT_OF_STOCK` virtual position (VP) statuses are now included as sourcing candidates alongside `ACTIVE` VPs
  • Rejected fulfillments now carry the correct rejected quantity on their fulfillment items
🔎 See the Changelog for details.

Changelog

Framework & Sample Data Module
  • `RejectedLocationExclusionCriterion` is no longer injected automatically during sourcing strategy evaluation. Add it explicitly to the relevant sourcing profile strategies
  • Sample data sourcing profiles (`CC_DEFAULT`, `HD_DEFAULT`, `MULTI_DEFAULT`) now include `RejectedLocationExclusionCriterion` explicitly in both primary and fallback strategies
Settings
  • Updated the `fc.rubix.order.sourcing.criteria` setting to list `RejectedLocationExclusionCriterion` as an available reference criterion
  • Added the new `REF_ONLY` value to the `fc.api.fulfilment.uniqueness` setting
    When configured, the setting ensures fulfillment uniqueness based on the fulfillment reference. It deduplicates concurrent creation attempts that use the same reference so that only one fulfillment record is persisted. Existing setting values are unaffected
Workflows
  • Added the new `ReSourceOrder` ruleset to CC, HD, and Multi order workflows
    When triggered, it schedules two `SourceOrder` attempts using `ScheduleEventWithRandomOffset` with configurable delays and offset percentages. This prevents simultaneous sourcing executions for the same order when multiple fulfillments expire concurrently
  • Updated the `FulfilmentExpiry` ruleset to route to `ReSourceOrder` instead of directly to `SourceOrder`
    All other sourcing entry points (`PartiallyFulfilled`, `AllRejected`, `RejectFulfilment`, `ReassignRejections`) continue to route directly to `SourceOrder` and are unchanged
RulesSourcing Utilities
  • Renamed the rejected location exclusion criterion `type` identifier from `fc.sourcing.criterion.locationExclusion` to `fc.sourcing.criterion.rejectedLocationExclusion`
  • Added `getCriteria(SourcingStrategy)` as the new signature for retrieving criteria for a strategy. `getCriteria(strategy, defaultCriteria)` and `getDefaultCriteria()` are deprecated
  • Chunk loading architecture: location evaluation now uses a `ProfileStrategyPlanFinder``ChunkLoadingPlanFinder``AllItemsPlanFinder` chain
    `ChunkLoadingPlanFinder` loads locations in incremental chunks sized proportionally to the order's item count and runs a single-location search after each chunk. A multi-split search is run across the full evaluated set if no single-location plan is found
  • Early availability check: before entering permutation search, the combined available quantity for each required item is summed across all candidate locations. If any item cannot be covered, the permutation search is skipped and sourcing proceeds directly to the next strategy
  • Fallback sourcing: location ratings are now recalculated after each allocation iteration using remaining unfulfilled items, ensuring each location selection reflects actual demand at that point in the iteration
  • ETA Calculator: the injectable `EtaCalculator` function runs for each fulfillment plan and its fulfillments, with access to selected locations and event context. Additional GraphQL queries are supported within the calculator
  • Added the `availableQty` field to `FulfilmentItem` and `CreateFulfilmentPlanFulfilmentItemInput`. The value is saved on the fulfillment plan at sourcing time
  • Multi-attempt sourcing: `getPlanFinder` is the preferred entry point - it returns a `SourcingPlanFinder` exposing `nextFullPlan()` (primary) and `nextFallbackPlan()` (fallback), loading sourcing data at most once per variant so alternative plans can be generated without reloading. The lower-level factory methods `getBasedOnStrategiesPlanFinder` and `getBasedOnFallbackStrategiesPlanFinder` are available for decorator/wrapping scenarios
  • Extracted `findPartialFulfilmentPlan` as a public static method
  • Deterministic fulfillment reference format (`createFulfilments`): fulfillments are now created with a sequential one-based index reference `{orderId}-{fulfilmentChoiceId}-{index}`. The index starts at the number of existing fulfillments for the order plus one. This ensures that concurrent sourcing processes operating from the same starting state generate identical references, enabling deduplication via `fc.api.fulfilment.uniqueness`

Limitations

  • Duplicate fulfillment prevention during concurrent re-sourcing requires explicit configuration of `fc.api.fulfilment.uniqueness` = `REF_ONLY`. Without this setting, concurrent sourcing behavior is unchanged
  • The unique constraint on the combination of order and unique reference applies only to fulfillments created after the production release date. Pre-existing fulfillment records are not affected
  • The ReSourceOrder ruleset depends on the `ScheduleEventWithRandomOffset` rule, which is delivered in Core Reference Module v2.3.0. Core Reference Module v2.3.0 must be deployed alongside Order Reference Module v2.2.0 for staggered re-sourcing to function
  • Customers upgrading from a previous version who relied on automatic injection of `RejectedLocationExclusionCriterion` must add this criterion explicitly to their sourcing profile strategies before or after upgrading. Without this update, previously rejected locations will no longer be excluded from sourcing
Released capability depth:Enhancement
Release bundle / Capability type:Module

Use case

Prevent Duplicate Fulfillments During Concurrent Re-Sourcing

Problem
When a fulfillment expires, the workflow sends a re-sourcing event on the parent order. If the same order has multiple fulfillments that all share the same Pick/Pack deadline, all of them can expire exactly at the same time - triggering multiple re-sourcing attempts for the same order simultaneously. Each attempt independently loads the current unfulfilled item state of the order. Because all attempts load the same state before any of them has completed, each attempt calculates the same set of items to fulfill and tries to create fulfillments for all of them.Without a mechanism to prevent this, concurrent re-sourcing can produce the following failure modes:
  • Duplicate fulfillments created for the same items. Multiple concurrent attempts each try to create fulfillments for the same unfulfilled items. Without deduplication, all attempts succeed, resulting in more fulfillments than intended for the same order items.
  • Inventory reserved multiple times for the same demand. Each concurrent attempt that successfully creates fulfillments also triggers inventory reservations. The same stock is reserved multiple times, reducing availability for other orders beyond what the actual demand justifies.
  • Negative unallocated item quantities. Because each concurrent attempt subtracts from the same unfulfilled count, the combined effect of all completed attempts can drive the unallocated quantity below zero, producing an inconsistent order state.
  • Operational overhead to recover. Identifying and removing duplicate fulfillments, correcting reservation quantities, and restoring the order to a consistent state requires manual investigation that scales with order volume.
A fashion retailer processes an order that contains two items:
  • a jacket (Item A) 
  • a pair of boots (Item B)
The order is sourced and two separate fulfillments are created - one for each item - at two different store locations. Both fulfillments share the same Pick/Pack deadline of 9:00 AM on Monday. Both locations miss the deadline, and both fulfillments expire at exactly the same time.When a fulfillment expires, the workflow fires a re-sourcing event on the parent order. With two fulfillments expiring simultaneously, two re-sourcing attempts are triggered for the same order at the same moment. Both attempts load the current state of the order before either has made any changes. At that point, both Item A and Item B are still unfulfilled. Each attempt independently calculates that fulfillments need to be created for both items, and both proceed to create them.The result is four fulfillments instead of two - duplicate fulfillments for both Item A and Item B. Inventory is reserved twice for each item, and the order's unallocated quantity drops below zero. The order is now in an inconsistent state that requires manual correction.
Solution Overview
Reference order workflow addresses concurrent re-sourcing through two complementary mechanisms:
  • The first reduces the probability that two re-sourcing attempts execute at the same time
  • The second ensures that even when they do, only one set of fulfillments is persisted
How It Works at a glance
  • Stagger Concurrent Attempts with Two Scheduled Events
    Rather than triggering re-sourcing immediately when a fulfillment expires, the `ReSourceOrder` ruleset schedules two `SourceOrder` events with different configurable randomized delays:
    • The first event fires with a base delay of approximately 30 seconds, with a random offset applied around that value. When two fulfillments expire at the same time, the randomization makes it likely that their re-sourcing attempts will land in different execution windows. If the first attempt completes before the second begins, the second finds no unfulfilled items remaining and exits cleanly
    • The second event fires approximately 5 minutes after expiry. By that point, all in-flight attempts from the original expiry wave have either succeeded or failed. Any items that remain unfulfilled are picked up and processed. In the large majority of cases, this event fires, finds nothing to do, and exits. Its value lies in the edge cases where it is the only path to recovery
    • The two events serve distinct purposes:
      • the first reduces the probability of concurrent execution
      • the second provides a self-correcting fallback for the rare cases where the first is not sufficient
  • Deduplicate at the Database Level Using Deterministic References
    Scheduling alone reduces but does not eliminate the risk of concurrent execution. To handle the cases where two attempts do run simultaneously and both reach fulfillment creation, fulfillments in the reference workflow are assigned deterministic, index-based references rather than random identifiers. When two concurrent attempts start from the same loaded order state - meaning the same number of existing fulfillments - they calculate identical references for the new fulfillments they are about to create.

    When the `fc.api.fulfilment.uniqueness` setting is configured to `REF_ONLY`, the fulfillment's external reference is copied to its `unique_reference` field before persistence. A database-level unique constraint on the combination of order and unique reference then ensures that only one of the concurrent attempts can persist its records. The second attempt's identical references are rejected at the database level - no duplicate fulfillments are created, and no additional inventory is reserved.
  • This Pattern Applies Only to Fulfillment Expiry Re-Sourcing
    The `ReSourceOrder` ruleset and the `ScheduleEventWithRandomOffset` pattern are applied exclusively to the re-sourcing flow triggered by fulfillment expiry. All other sourcing entry points - initial sourcing at order creation, partial fulfillment sourcing, and sourcing triggered by rejection and reassignment - route directly to `SourceOrder`.

    Initial sourcing does not face the same concurrency risk. For CC and HD orders, a single sourcing event is triggered per order. For Multi orders, sourcing is triggered once per fulfillment choice. In both cases, there is no competing parallel execution path that would produce the same race condition.
Required Configuration
Both mechanisms must be in place for full protection. The scheduled delay pattern and the `fc.api.fulfilment.uniqueness` = `REF_ONLY` setting work together:
  • The delay reduces the likelihood of concurrent execution
  • The uniqueness constraint handles the cases where concurrent execution still occurs
Configuring only one without the other leaves a gap.
Intended Use
`ScheduleEventWithRandomOffset` is a targeted pattern for a specific concurrency risk. It is included in the reference workflow because fulfillment expiry is a known scenario where multiple fulfillments on the same order can expire simultaneously. Applying this pattern to other rulesets - particularly the initial sourcing flow or standard order lifecycle events - adds latency and complexity without addressing a real problem. Before using `ScheduleEventWithRandomOffset` outside the expiry context, confirm that a genuine concurrent execution risk exists for the scenario you are configuring.
Solution