Fluent Commerce Logo
Docs

How Controls Utilities work

Topic

Authors:

Holger Lierse, Kirill Gaiduk

Changed on:

24 Aug 2026

Overview

The articles walks you through the Controls Utilities bundle (`util-controls`): a library that applies controls to catalog positions from within a Rule, so that inclusion, exclusion, buffer, limit and threshold logic is resolved and applied the same way everywhere it is used.Prerequisites

Controls Utilities Overview

Author:

Holger Lierse

Changed on:

26 Aug 2026

Overview

The Controls Utilities library (`util-controls`) applies controls to a catalog position in a consistent, reusable way. Rather than each rule reimplementing how to find and apply controls, a rule calls `ControlUtils.applyControls(...)`, which loads the required data from GraphQL, evaluates the applicable controls, and returns an updated position together with an audit of every change.

Key points

  • Prerequisites: Java, Maven, and JUnit; Rules SDK (Rules and the rule Context); Understanding of Fluent Inventory concepts and domain model
  • Single entry point: `ControlUtils.applyControls(...)` resolves and applies controls in one call
  • Standard control types: `ControlUtils` includes inclusion, exclusion, buffer, limit, and threshold controls by default
  • Specificity-aware: position/product/variant-level controls take precedence over group-level (bulk) controls
  • Auditable: the result includes a before/after snapshot for every control the utility applied
  • Extensible: supply custom control functions to support control types specific to your implementation

Value Proposition

The core benefit is consistency: the utility resolves and applies controls the same way everywhere.
  • Reusable logic: one implementation of control resolution, shared across every rule that needs it.
  • Self-contained: the utility loads the position and control data it needs directly from GraphQL; rules do not need to be chained to pass data.
  • Configurable: you pass in the set of control types to apply, and can extend them per implementation.

Key Concepts

  • Control: adjusts a position's `status` and/or `quantity` when its context matches the position.
  • Control group: a collection of controls associated with a catalog. A catalog normally has a single control group; multiple groups are supported via the catalog's `controlGroups` attribute.
  • Bulk (group-level) control: a control whose type is prefixed `BULK_`, applying across a group.
  • Position-level control: a control targeting a specific product or position; takes precedence over bulk controls.
  • Control function: the pairing of "does this control apply to this position?" and "how is it applied?". Build the standard set with `ControlTypes.buildControlFunctions()`.

Explanation Through an Example

Resolve and apply the standard controls to a position, then persist the result:The call returns`PositionUpdateResult` carrying the updated position and an audit of every control the utility applied.

Control Utils

Authors:

Holger Lierse, Kirill Gaiduk

Changed on:

24 Aug 2026

Overview

`ControlUtils` is the entry point of the Controls Utilities bundle. This page is the API reference for applying controls to a position from a rule.For the underlying concepts (control, control group, bulk vs position-level control) see Controls Utilities Overview. For the order in which the `ControlUtils` resolves and applies controls, and the behavior of the caches, see the Detailed Technical Description on the Controls Utilities package page.

Key points

  • Specificity-aware: position/product/variant-level controls take precedence over group-level (bulk) controls
  • Auditable: `ControlUtils` records every applied control with a before/after snapshot
  • Extensible: the position source and the set of control functions are both parameters you can supply

Applying Controls to a Position

`applyControls(context, inventoryCatalogueRef, inventoryPositionRef)`
The convenience overload. Loads the position with the default position supplier and applies the default set of control functions. Use this when the position maps one-to-one to the triggering event.
`applyControls(context, positionSupplier, controlFunctions)`
Applies the given control functions to the position produced by `positionSupplier`. Use this to control where the position comes from (for example, an aggregate across locations) and which control functions run.
`applyControls(context, positionSupplier, controlFunctions, defaultControls)`
Like the previous overload, but the `ControlUtils` adds `defaultControls` to the loaded controls where a control of that type is not already present. Use this to guarantee a baseline of behavior when no matching control exists.
`applyControlsWithDefaultBehavior(context, positionSupplier, controlFunctions)`
A shorthand for the overload above that supplies `ControlTypes.DEFAULT_THRESHOLD_CONTROL_ACTIVE` as the default control, so a position with no matching threshold control defaults to `ACTIVE`.

Position Suppliers

A position supplier is a `Supplier<Position>` that tells `applyControls` which position to operate on. `ControlUtils` provides two.
`defaultPositionSupplier(context, inventoryCatalogueRef, inventoryPositionRef)`
Loads a single inventory position and enriches it with the standard related product and location information.
`aggregatePositionSupplier(...)`
Produces a single position whose `quantity` is the sum of on-hand across multiple locations, for a product. `ControlUtils` provides three variants:
  • `aggregatePositionSupplier(context, inventoryCatalogueRef, productRef, activeStatuses)`
    aggregates the positions passed to it, counting only those whose status is in `activeStatuses`.
  • `aggregatePositionSupplier(context, inventoryCatalogueRef, productRef, networkRef, activeStatuses)`
    aggregates across every location in the given network.
  • `aggregatePositionSupplier(context, inventoryCatalogueRef, productRef, locationSupplier, activeStatuses)`
    aggregates across the locations returned by a custom `locationSupplier`.
`locationsByNetworkSupplier(context, networkRef)`
Returns`Supplier<List<LocationReference>>` of the locations in a network. Pass it to the `locationSupplier` variant of `aggregatePositionSupplier` when you want to aggregate over a network but supply the locations yourself.

Supplying Custom Control Functions

The control functions passed to `applyControls` determine which control types `ControlUtils` recognizes and how each applies. `ControlTypes.buildControlFunctions()` returns the standard set; pass your own list to add or override behavior. The example below assumes you have statically imported the relevant members of `ControlTypes` (`ControlFunction``CONTROL_FUNCTION_*``appliesToPosition``EXCLUSION_TYPE`):

The Result: `PositionUpdateResult`

Every overload returns`PositionUpdateResult`:
  • `getPosition()` - the updated position (`status` and `quantity` after all controls were applied).
  • `getPositionBefore()` - the original position, before any control was applied.
  • `getAudit()` - an ordered list of `PositionUpdate` entries, one per applied control.
  • `getMessage()` - a human-readable summary, for example `"2 controls were applied"`.
Each `PositionUpdate` in the audit exposes `getBefore()``getAfter()` and `getControl()`, giving the position on either side of that single control and the control  that applied the change.

Control Types

Authors:

Holger Lierse, Kirill Gaiduk

Changed on:

24 Aug 2026

Overview

This page describes how to configure a control: the structure of the control data and the values each standard control type expects. For what each type does and how the utility resolves and applies controls, see the Controls Utilities Overview and the Detailed Technical Description on the Controls Utilities page.

Key points

  • Behavior is chosen by the `type` suffix: the suffix of `type` selects the control function of `type` (`...INCLUSION``...EXCLUSION``...BUFFER``...LIMIT``...THRESHOLD`).
  • Group-level controls use the `BULK_` prefix: Control Utils loads a control as group-level (bulk) only when its `type` begins with `BULK_`, for example `BULK_BUFFER`.
  • Every control needs a `context:` the `context` value decides which positions a control applies to. A control with no `context` never applies; a `CATALOGUE` context always matches.
  • Only `ACTIVE` controls apply: the utility only loads controls whose top-level status is `ACTIVE`.
  • Order is explicit: `executionOrder` orders controls within a group in ascending order; lower runs first.

Anatomy of a Control

Each control belongs to a control group. When the utility loads a control, it has the following structure:
  • `type` - selects the control function by its suffix (`...INCLUSION``...EXCLUSION``...BUFFER``...LIMIT``...THRESHOLD`). Group-level (bulk) controls are loaded when `type` begins with `BULK_`; Position, Product and Variant level controls are matched by `ref`.
  • `status` - only controls with a `status` of `ACTIVE` are loaded.
  • `executionOrder` - orders controls within a group (ascending); the lowest value is applied first. When several controls of the same type can match one position only the first is applied, so give those controls distinct orders. 
  • `values` - the configuration. Every control carries a `context` value that decides which positions it applies to, plus any type-specific values described below.

Targeting with `context`

The `context` value is an array of matcher objects. All objects in the array must match for the control to apply to a position; where an individual object's `value` is itself an array, any one of those values may match.
  • `type` - what to match against: `PRODUCT``VARIANT``LOCATION``CATEGORY``NETWORK` or `POSITION``CATALOGUE` is a special case that always matches (used for catalog-wide controls).
  • `property` - the path within that entity, for example `ref``status`, or an attribute via `attributes.byName.<attributeName>`.
  • `value` - the expected value at that path. A single value, or an array of values of which any one may match.  
The structure of a `context` matcher:
A catalog-wide control (applies to every position in the catalog):Multiple matchers are combined with AND. The following applies only to product `AH8050` in network `F_NSW`:

Control Types

Inclusion

Sets the matched position's `status` to `ACTIVE`. Carries only a `context`.

Exclusion

Sets the matched position's `status` to `INACTIVE`. Carries only a `context`.

Buffer (Absolute)

Adjusts the position `quantity` by a fixed amount. A negative `quantity` reduces available stock; a positive one increases it.

Buffer (Percentage)

A buffer control uses one of two values: a `quantity` value (the absolute buffer shown above) or a `percentage` value. When the control has no `quantity` value, the utility calculates the buffer as the `percentage` of the position's current quantity. A positive percentage increases the quantity; a negative one reduces it.

Limit

Clamps the position `quantity` so it is never below `min` nor above `max`.

Threshold

Sets the position `status` when the `quantity` is at or below `threshold`. It changes status only, never quantity.

Combined Example

A control group typically holds several controls of different types. The util applies them in a fixed order - inclusion, then exclusion, then buffer, then limit, then threshold - and the effect is cumulative: each type sees the quantity and status left by the previous one. Within a single type, the utility applies the most specific matching control (position or product level before group level).The controls below belong to one group scoped to category `ABC`: an absolute buffer and a two-tier threshold.