Fluent Commerce Logo
Docs
Essential knowledge

Intended Audience:

Technical User

Authors:

Holger Lierse, Kirill Gaiduk

Changed on:

7 Aug 2026

Overview

The `SourcingUtils` class in the `util-sourcing` bundle is the main utility class that orchestrates the entire sourcing process within a rule. It provides core helper methods such as loading a sourcing profile, initializing the sourcing context, and executing the sourcing logic configured in the profile. Each stage supports multiple customization points.

Key points

  • Main Orchestrator: Central utility class that coordinates all sourcing operations from a rule
  • Strategy Evaluation: Evaluates and applies sourcing strategies based on business rules
  • Plan Generation: Generates sourcing plans for order fulfillment
  • Chunk Loading: Locations are evaluated in incremental chunks to support large sourcing networks. An early availability check skips expensive permutation search when the order cannot be fully sourced from the available stock
  • Multi-Attempt Sourcing: `getPlanFinder` provides a single entry point for primary and fallback sourcing, generating multiple ranked plans without reloading data

Core Methods

`getPlanFinder()`
The preferred single entry point for running primary or fallback sourcing from the same parameters object. Returns a `SourcingPlanFinder` that exposes:
  • `nextFullPlan()` for primary-strategy sourcing 
  • `nextFallbackPlan()` for fallback-strategy sourcing
The underlying finder for each variant is initialized lazily on the first call and reused on subsequent calls, so sourcing data is loaded at most once per variant.
1SourcingExecutionParameters params = SourcingExecutionParameters.builder()
2        .context(context)
3        .sourcingContext(sourcingContext)
4        .profile(sourcingProfile)
5        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
6        .build();
7
8SourcingPlanFinder finder = SourcingUtils.getPlanFinder(params);
9
10// Primary sourcing: call multiple times to produce alternative
11// plans without reloading sourcing data
12SourcingPlan plan = finder.nextFullPlan();
13
14// Fallback sourcing from the same parameters object
15SourcingPlan fallbackPlan = finder.nextFallbackPlan();
`findPlanBasedOnStrategies()`
Finds the best sourcing plan for an order based on sourcing strategies defined in a sourcing profile.Location evaluation uses a `ChunkLoadingPlanFinder` that loads locations in incremental chunks sized proportionally to the order's item count. 
  • A single-location search is run after each chunk
  • A multi-split search runs across the full evaluated set if no single-location plan is found
  • An early availability check is performed before entering permutation search - if any item cannot be covered by the combined quantities across candidate locations, permutation search is skipped
1SourcingPlan plan = SourcingUtils.findPlanBasedOnStrategies(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .profile(profile)
6        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
7        .build()
8);
`getBasedOnStrategiesPlanFinder()`
Returns a `SourcingPlanFinder` configured for primary sourcing strategies.`findPlanBasedOnStrategies(SourcingExecutionParameters)` is equivalent to `getBasedOnStrategiesPlanFinder(sourcingExecutionParameters).nextPlan()`.Use this when you need to call `findPlanForAllItems` multiple times without reloading sourcing data from scratch - for example, when generating a ranked list of fulfillment plans.
1SourcingPlanFinder planFinder = SourcingUtils.getBasedOnStrategiesPlanFinder(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .profile(profile)
6        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
7        .build()
8);
9// Call nextPlan() for each attempt without reloading sourcing data
10SourcingPlan plan = planFinder.nextPlan();
`findPlanBasedOnFallbackStrategies()`
Finds the sourcing plan for unfulfilled items using fallback sourcing strategies when no primary strategy fully satisfies the sourcing request.
  • Only one fallback strategy is used, specifically the first that satisfies the sourcing conditions
  • Supports partial sourcing, where fulfillments may not completely satisfy the order
  • Location ratings are recalculated after each allocation iteration using remaining unfulfilled items, ensuring each location selection reflects actual demand at that point in the iteration
1SourcingPlan plan = SourcingUtils.findPlanBasedOnFallbackStrategies(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .profile(profile)
6        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
7        .build()
8);
`getBasedOnFallbackStrategiesPlanFinder()`
Returns a `SourcingPlanFinder` configured for fallback sourcing strategies.`findPlanBasedOnFallbackStrategies(SourcingExecutionParameters)` is equivalent to `getBasedOnFallbackStrategiesPlanFinder(sourcingExecutionParameters).nextPlan()`.Wrap the returned finder (for example, for multi-attempt location exclusion) before calling `findPartialFulfilmentPlan` in a loop.
1SourcingPlanFinder fallbackFinder = SourcingUtils.getBasedOnFallbackStrategiesPlanFinder(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .profile(profile)
6        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
7        .build()
8);
9// Wrap finder for multi-attempt location exclusion, then call nextPlan()
10SourcingPlan partialPlan = fallbackFinder.nextPlan();
`buildRejectedFulfilment()`
Builds a rejected fulfillment for all remaining unfulfilled items in the sourcing context.
1// Build system rejected fulfillment for unfulfillable items
2Fulfilment rejectedFulfilment = SourcingUtils.buildRejectedFulfilment(
3    context, 
4    sourcingContext, 
5    context.getProp(PROP_SYSTEM_REJECTED_LOC_REF)
6);

Supporting Methods

`findPlanForAllItems()`
This helper method is used by the `findPlanBasedOnStrategies` method to identify a plan for an order based on the sourcing strategies. It ranks candidate locations using the provided sourcing criteria, and searches for the best combination of locations that can cover the full order within the allowed split limit. Fewer-location plans are always preferred.Before entering permutation search, an early availability check sums available quantities for each required item across all candidate locations. If any item cannot be covered, permutation search is skipped. If no valid combination exists, it returns an empty plan.
1String networkRef = SourcingUtils.getNetworkRef(sourcingProfile, strategy);
2String virtualCatalogueRef = SourcingUtils.getVirtualCatalogueRef(sourcingProfile, strategy);
3Integer maxSplit = SourcingUtils.getMaxSplit(sourcingProfile, strategy);
4List<Location> locations = LocationUtils.getLocationsInNetwork(context, networkRef);
5
6SourcingPlan plan = SourcingUtils.findPlanForAllItems(
7    SourcingExecutionParameters.builder()
8        .context(context)
9        .sourcingContext(sourcingContext)
10        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
11        .build(),
12    virtualCatalogueRef,
13    locations,
14    sourcingCriteria,
15    maxSplit
16);
`findHighestValuePartialFulfilment()`
This helper method is used by the `findPlanBasedOnFallbackStrategies` method to find the highest-value partial fulfillment. It filters out excluded locations, compares each candidate's rating based on the remaining unfulfilled items at that iteration, and checks whether the location can cover at least part of the remaining items. The method returns the best fulfillment found or none if no positive-value option exists.
1Set<String> excludedLocations = Set.of("warehouse-001", "store-002");
2Optional<Fulfilment> partialFulfilmentOpt = SourcingUtils.findHighestValuePartialFulfilment(
3    locationAndPositions,
4    unfulfilledItems,
5    excludedLocations
6);
7
8if (partialFulfilmentOpt.isPresent()) {
9    Fulfilment partialFulfilment = partialFulfilmentOpt.get();
10    
11    // Calculate remaining items after partial fulfillment
12    List<OrderItem> remainingItems = OrderUtils.itemsMinusFulfilments(
13        unfulfilledItems,
14        Arrays.asList(partialFulfilment)
15    );
16    
17    // Create the partial fulfillment
18    OrderUtils.fillFulfilmentType(sourcingContext, Arrays.asList(partialFulfilment));
19    OrderUtils.createFulfilments(context, sourcingContext, Arrays.asList(partialFulfilment));
20    
21    context.action().log("Created partial fulfillment with {} items", 
22        partialFulfilment.getItems().size());
23}
`findPartialFulfilmentPlan()`
Builds a greedy partial fulfillment plan by repeatedly calling `findHighestValuePartialFulfilment` until the `maxSplit` limit is reached or all unfulfilled items are covered. Location ratings are recalculated after each allocation iteration.Use `getBasedOnFallbackStrategiesPlanFinder` to obtain a pre-loaded `SourcingPlanFinder`, then wrap it (for example, for multi-attempt location exclusion) before calling `findPartialFulfilmentPlan` in a loop.
1SourcingPlanFinder fallbackFinder = SourcingUtils.getBasedOnFallbackStrategiesPlanFinder(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .profile(profile)
6        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
7        .build()
8);
9SourcingPlan partialPlan = SourcingUtils.findPartialFulfilmentPlan(
10    sourcingContext,
11    fallbackStrategy,
12    unfulfilledItems,
13    locationsWithPositions,
14    criteria,
15    locationLimit,
16    maxSplit
17);
`loadPositions()`
Loads virtual positions for sourcing operations. This method is used by both core methods (`findPlanForAllItems` and `findPlanBasedOnFallbackStrategies`) to load inventory.
1List<LocationAndPositions> locationAndPositions = SourcingUtils.loadPositions(
2    SourcingExecutionParameters.builder()
3        .context(context)
4        .sourcingContext(sourcingContext)
5        .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
6        .build(),
7    sourcingCriteria,
8    virtualCatalogueRef,
9    locations
10);
`loadSourcingProfile()`
Loads the sourcing profile for the current context.
1SourcingProfile profile = SourcingUtils.loadSourcingProfile(context);
2if (profile != null) {
3    SourcingPlan plan = SourcingUtils.findPlanBasedOnStrategies(
4        SourcingExecutionParameters.builder()
5            .context(context)
6            .sourcingContext(sourcingContext)
7            .profile(profile)
8            .positionStatuses(ImmutableList.of(Constants.Status.ACTIVE))
9            .build()
10    );
11}
`getUnfulfilledItems()`
Computes outstanding order items after accounting for allocated but non-rejected quantities.
1List<OrderItem> unfulfilledItems = SourcingUtils.getUnfulfilledItems(sourcingContext);
`getNetworkRef()`
Gets the network reference from a sourcing profile or strategy.
1String networkRef = SourcingUtils.getNetworkRef(profile, strategy);
`getVirtualCatalogueRef()`
Gets the virtual catalog reference from a sourcing profile or strategy.
1String catalogueRef = SourcingUtils.getVirtualCatalogueRef(profile, strategy);
`getMaxSplit()`
Gets the maximum split value from a sourcing profile or strategy.
1Integer maxSplit = SourcingUtils.getMaxSplit(profile, strategy);