Author:
Holger Lierse
Changed on:
5 Sept 2025
`util-sourcing`), designed to simplify implementing complex sourcing logic and reduce repetitive code.PrerequisitesBefore diving in, make sure you have:Author:
Holger Lierse
Changed on:
25 Sept 2025
`util-sourcing` library is a comprehensive collection of utility functions designed to minimize the overhead and complexity of writing sourcing logic in your Fluent Commerce rules.PrerequisitesThese articles assumes you're familiar with:`SourcingUtils`): Orchestrates the sourcing process and provides helper methods to load a Sourcing Profile`SourcingContextUtils`): Loads and manages Sourcing Context `OrderUtils`): Performs order-specific operations such as fulfillment creation`LocationUtils`): Provides location-based helpers including distance calculations and caching1// Load the sourcing profile for the current context
2SourcingProfile sourcingProfile = SourcingUtils.loadSourcingProfile(context);1// Create sourcing context with unfulfilled order items
2SourcingContext sourcingContext = SourcingContextUtils.loadSourcingContext(
3 context,
4 SourcingUtils::getUnfulfilledItems
5);1// Find the best sourcing plan based on strategies
2SourcingPlan plan = SourcingUtils.findPlanBasedOnStrategies(
3 context,
4 sourcingContext,
5 sourcingProfile,
6 ImmutableList.of(Constants.Status.ACTIVE), // Consider only ACTIVE positions
7 this::customInventoryProcessor
8);1// The system generates a sourcing plan that might look like:
2SourcingPlan optimalPlan = new SourcingPlan();
3optimalPlan.addFulfilment(
4 SourcingUtils.Fulfilment.builder()
5 .location(Location.builder().name("Manhattan Store").build())
6 .items(Arrays.asList(
7 Fulfilment.FulfilmentItem.builder().ref("2x Laptops").build(),
8 Fulfilment.FulfilmentItem.builder().ref("3x Mice").build())
9 )
10 .build()
11);
12optimalPlan.addFulfilment(
13 SourcingUtils.Fulfilment.builder()
14 .location(Location.builder().name("NJ Warehouse").build())
15 .items(Arrays.asList(
16 Fulfilment.FulfilmentItem.builder().ref("3x Mice").build())
17 )
18 .build()
19);1// Set fulfillment types
2OrderUtils.fillFulfilmentType(sourcingContext, fulfilments);
3
4// Create fulfillments from the plan
5List<Fulfilment> fulfilments = OrderUtils.createFulfilments(
6 context,
7 sourcingContext,
8 plan.getFulfilments()
9);Author:
Holger Lierse
Changed on:
22 Sept 2025
`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, initialise the Sourcing Context, and trigger the execution of the sourcing logic configured in the profile. Each stage supports multiple customisation points throughout the process.`findPlanBasedOnStrategies()`1SourcingPlan plan = SourcingUtils.findPlanBasedOnStrategies(
2 context,
3 sourcingContext,
4 profile,
5 ImmutableList.of(Constants.Status.ACTIVE),
6 null // No custom inventory processor
7);`findPlanBasedOnFallbackStrategies()` 1SourcingPlan plan = SourcingUtils.findPlanBasedOnFallbackStrategies(
2 context,
3 sourcingContext,
4 profile,
5 ImmutableList.of(Constants.Status.ACTIVE),
6 null // No custom inventory processor
7);
8 `buildRejectedFulfilment()`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);`findPlanForAllItems()``findPlanBasedOnStrategies` method to identify a plan for an order based on the Sourcing Strategies. It loads candidate locations and their stock positions, ranks them 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. If no valid combination exists, it returns an empty plan.1// Load virtual positions
2List<LocationAndPositions> locationAndPositions = SourcingUtils.loadPositions(
3 context,
4 sourcingContext,
5 sourcingProfile,
6 strategy,
7 ImmutableList.of(Constants.Status.ACTIVE)
8);
9
10// Get strategy configuration
11String networkRef = SourcingUtils.getNetworkRef(sourcingProfile, strategy);
12String virtualCatalogueRef = SourcingUtils.getVirtualCatalogueRef(sourcingProfile, strategy);
13Integer maxSplit = SourcingUtils.getMaxSplit(sourcingProfile, strategy);
14
15// Generate sourcing plan
16SourcingPlan plan = SourcingUtils.findPlanForAllItems(
17 context,
18 sourcingContext,
19 locationAndPositions,
20 maxSplit,
21 null // Use default scoring
22);`findHighestValuePartialFulfilment()``findPlanBasedOnFallbackStrategies` method to find the highest-value partial fulfillment. It filters out excluded locations, compares each candidate’s rating, 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}`loadPositions()``findPlanForAllItems` and `findPlanBasedOnFallbackStrategies`) to load inventory.1List<LocationAndPositions> locationAndPositions = SourcingUtils.loadPositions(
2 context,
3 sourcingContext,
4 sourcingProfile,
5 sourcingStrategy,
6 ImmutableList.of(Constants.Status.ACTIVE)
7);`loadSourcingProfile()`1SourcingProfile profile = SourcingUtils.loadSourcingProfile(context);
2
3if (profile != null) {
4 // Use the profile for sourcing operations
5 SourcingPlan plan = SourcingUtils.findPlanBasedOnStrategies(
6 context,
7 sourcingContext,
8 profile,
9 ImmutableList.of(Constants.Status.ACTIVE),
10 null
11 );
12}`getUnfulfilledItems()`1List<OrderItem> unfulfilledItems = SourcingUtils.getUnfulfilledItems(context);`getNetworkRef()`1String networkRef = SourcingUtils.getNetworkRef(profile, strategy);`getVirtualCatalogueRef()`1String catalogueRef = SourcingUtils.getVirtualCatalogueRef(profile, strategy);`getMaxSplit()`1Integer maxSplit = SourcingUtils.getMaxSplit(profile, strategy);Author:
Holger Lierse
Changed on:
17 Sept 2025
`SourcingContextUtils` in the `util-sourcing` is a utility class for managing sourcing context and data loading operations. It provides methods to create and populate sourcing contexts with order details, unfulfilled items, and supporting data required for sourcing decisions.`loadSourcingContext()`1SourcingContext sourcingContext = SourcingContextUtils.loadSourcingContext(
2 context,
3 SourcingUtils::getUnfulfilledItems
4);Author:
Holger Lierse
Changed on:
20 Sept 2025
`OrderUtils` class in the `util-sourcing` is a utility class that provides order-specific utilities for sourcing operations. It handles order-related sourcing operations including fulfillment creation, fulfillment type determination, and order item management.`createFulfilments()``SourcingPlan`, allocating order items to locations and assigning fulfillment types.1// Create fulfillments from the sourcing plan
2List<Fulfilment> createdFulfilments = OrderUtils.createFulfilments(
3 context,
4 sourcingContext,
5 plan.getFulfilments()
6);`fillFulfilmentType()` 1// Set fulfillment types based on business rules
2OrderUtils.fillFulfilmentType(sourcingContext, fulfilments);`itemsMinusFulfilments()`1// Get unfulfilled items
2List<OrderItem> unfulfilledItems = SourcingUtils.getUnfulfilledItems(context);
3
4// Find partial fulfillment
5Optional<Fulfilment> partialFulfilmentOpt = SourcingUtils.findHighestValuePartialFulfilment(
6 locationAndPositions,
7 unfulfilledItems,
8 Collections.emptySet() // No excluded locations
9);
10
11// Calculate remaining items after partial fulfillment
12if (partialFulfilmentOpt.isPresent()) {
13 Fulfilment partialFulfilment = partialFulfilmentOpt.get();
14 List<OrderItem> remainingItems = OrderUtils.itemsMinusFulfilments(
15 unfulfilledItems,
16 Arrays.asList(partialFulfilment)
17 );
18}Author:
Holger Lierse
Changed on:
17 Sept 2025
`LocationUtils` class in the `util-sourcing` is a utility class that provides utilities for location-based sourcing decisions. It handles location-specific sourcing logic including distance calculations, location availability checks, and provide location-based caching optimization.`getLocationByRef()`1Location location = LocationUtils.getLocationByRef(context, "store-123");`getLocationsInNetwork()` 1List<Location> networkLocations = LocationUtils.getLocationsInNetwork(
2 context,
3 "network-001"
4);`getLocationsInNetworks()`1List<String> networkRefs = Arrays.asList("network-001", "network-002", "network-003");
2List<Location> allLocations = LocationUtils.getLocationsInNetworks(context, networkRefs);`distanceInMetres()`1Location storeLocation = LocationUtils.getLocationByRef(context, "store-123");
2Location customerLocation = LocationUtils.getLocationByRef(context, "customer-location");
3
4if (storeLocation != null && customerLocation != null &&
5 storeLocation.getPrimaryAddress() != null && customerLocation.getPrimaryAddress() != null) {
6
7 double distance = LocationUtils.distanceInMetres(
8 storeLocation.getPrimaryAddress().getLatitude(),
9 storeLocation.getPrimaryAddress().getLongitude(),
10 customerLocation.getPrimaryAddress().getLatitude(),
11 customerLocation.getPrimaryAddress().getLongitude()
12 );
13
14 // Check if within delivery radius (e.g., 50km)
15 if (distance <= 50000) {
16 context.action().log("Location is within delivery radius");
17 }
18}