Broadleaf Microservices
  • v1.0.0-latest-prod

Upgrade to 2.3.0

Table of Contents

Security

Important

This release contains several security-related fixes. It is strongly recommended that you review the security-related content for these releases at Broadleaf Security. You will need your login credentials originally provided for accessing the Broadleaf nexus.

Requirements

  • Java 17 is required since 2.0.0-GA.

New Services

Introduced new Workflow Service
Introduced new Audit Service
Introduced Subscription Lifecycle & Billing Functionality
  • Impacted Services: CatalogServices, PricingServices, OfferServices, CartOperationServices, OrderOperationServices, SubscriptionOperationServices, BillingServices, WorkflowServices, AuditServices

  • Highlights:

    • Subscription Purchase & Fulfillment: Out-of-box support for handling subscription-based products directly within the cart & checkout.

    • Subscription Management: Manage the full subscription lifecycle with configurable edit, upgrade, downgrade, and cancellation scenarios. Includes advanced support for sandboxing and delayed prepaid actions.

    • Recurring Billing: A highly configurable recurring billing engine that works in conjunction with Broadleaf’s Catalog, Pricing, and Offer Engines to support complex or unique recurring billing needs.

    • CSR Interactions: Allow CSRs to manage subscriptions price changes and add discounts to existing subscriptions.

Introduction of Chase Payment integration module

New and Notable Changes

Support for Kafka KRaft and Strimzi Operator
Support for Fees and Fee Calculation for Cart Pricing
Support Retail Delivery Fees from Avalara Tax
Added Domain Support for Default Frequency for Products with Recurring Pricing
Microservice Gateways Performance Improvements
  • Impacted Services: Microservices Gateways

  • Highlights:

    • Rewrote OAuth2ClientCredentialsGatewayFilterFactory to use a fully non-blocking mechanism for all logic related to obtaining a new access token.

    • Rewrote ApplicationTokenGatewayFilterFactory and ExternalApplicationResolverService to move blocking cache interactions off of the event loop thread and into worker threads.

  • Links: Please refer to Gateways 2.0.5

Confirmed Node 22 and 24 Support for Frontend Projects
New Components and Component Enhancements
Introduced TenantService Component to make interactions with Tenant Microservice extensible
Removed Bootstrap dependency
Added support for customizing access token cache key
  • Impacted Services: Auth SDK (@broadleaf/auth-web)

  • Links: Please refer to Auth JS SDK 1.6.5

Support Specifying Variant Hydration Behavior in Product Details Requests
Introduced Lightweight Inventory Availability Index Based on Lite Variant Details
Introduced New API for Consolidated Returnable Items and Fees Information
Support for Consolidated OMS Refund Transactions
Support for Two-Tier Pricing using Attributes and Characteristics
Added new Price List Types
Introduced Pricing Categories
Introduced Variant Pricing Strategy
Added support for Caffeine and Ehcache as Spring Cache options
  • Impacted Services: All services implementing caching

  • Links: Please refer to Caching for more information.

Breaking Changes Requiring Action

ProductCharacteristic domain separation

Notable Bug Fixes

Fixed RecordTooLargeException from Changing Status of a Large Fulfillment
Fixed Various Refund Bugs

Frontend Compatibility and Release Notes

Unified Admin Release Notes for 1.10.13

Features & Enhancements

Support Retail Delivery Fee Refunds
  • Introduced the ability to optionally request the refund of order fees (esp. Retail Delivery Fees) when requesting creating a ReturnAuthorization.

Important Updates

  • Now supports Node 22 and 24. We recommend upgrading to Node 24 as soon as possible since Node 20 reaches end of life in April 2026.

    • Upgrading Node is not required to use this update.

Introduced Badges for Tile and List Grids

Added support for badges in both Tile and List grid views within the Unified Admin interface. This enhancement allows developers to define badges that can be displayed on individual items in these grids, providing additional context or status information at a glance.

Badges are rendered through BadgeColumn and BadgeTileComponent components, which can be configured in the metadata for the respective grid views. This feature enhances the visual representation of data and can be particularly useful for highlighting important attributes or statuses of items in a list or tile format. These badge components are specifically designed to pull from a hydration endpoint based on a field in the entity. The main current use case is to show AdvancedTags associated with a product in product grids.

Introduced a click event handler for links with href attributes matching the pattern modal(modalId). This handler intercepts such clicks and opens a modal dialog with the specified ID, allowing users to interact without leaving the current page. The handler searches both the current metadata and the new global components metadata for the modal definition and opens it if found.

This enhancement’s primary use case is to facilitate the opening of modals from links within a fields' help text, improving user experience by providing context-sensitive information without navigating away from the current view.

Introduced ModalFormView Component

A new ModalFormView component has been added to the Unified Admin interface. This component is designed to render forms within modal dialogs primarily triggered by links in help text or other interactive elements. These modal forms can handle both creation and editing of entities, providing a seamless user experience.

Introduced Divider Form Component

Introduced new Divider form component type to render a horizontal rule between fields. The divider component can be targeted using the .Divider css class. It takes a width attribute, indicating the width of the horizontal rule—default is sm indicating 1px. It also takes a spacing attribute indicating the spacing above and below the rule in rem—default is md indicating 2rem.

Tip
Other values are defined in metadata, see Divider#Width and Divider#Spacing enums.
Introduced CheckboxField Form Component

A new CheckboxField form component type has been added to render a checkbox input. This component is useful for boolean fields, where users want to have a simple checkbox instead of the default toggle switch.

Multi-line Truncation using line-clamp in LargeContentColumn

Multi-line truncation has been made available by including the @tailwindcss/line-clamp dependency in order to access the tw-line-clamp class name. The LargeContentColumn component has been updated to use tw-line-clamp to dictate the number of lines to truncate to for the column display value. The number of lines is expected to be set in the metadata for the colum via the lines attribute, and is set to 1 by default. By default, the available classes are tw-line-clamp-1 to tw-line-clamp-6 (numbers 1 through 6), and tw-line-clamp-none.

Introduced NavMenuFooterMenuItems Sub-Component

An overridable sub-component NavMenuFooterMenuItems has been created for the purpose of containing all menu items and links in the footer of the navigation menu. This sub-component can be overridden by registering a new component in the ComponentRegistrar with the ID blblNavMenuFooterMenuItems. By default, NavMenuFooterMenuItems contains the Developer Settings menu item.

Introduced ModalLinkColumn Component

A new component ModalLinkColumn has been created that renders a column with a link that when clicked, opens a modal containing fields and other components configured through the column metadata. If the ModalLinkColumn metadata has no configured sub-components such fields, groups, or grids, then the link rendered in the column will not open a modal when it is clicked. The modal also displays a Submit button and expects by default that the ModalLinkColumn metadata has a configured SUBMIT endpoint that it will submit the form data to.

This component also supports showing a confirmation message before invoking a "delete" submission action. When a "Delete" action is triggered the first time, this message will be displayed and must be acknowledged before actually invoking the endpoint.

private void example() {
    Externals.grid()
        .addColumn(SHARED_CODE, Columns.modalLink()
            .label("Modal Link Column")
            .addField("field1", Fields.string()
                .label("First Field")
                .order(1000))
            .closeButtonLabel("Close")
            .submitEndpoint(Endpoints.put()
                .uri("/my-endpoint")
                .scope("MY_SCOPE"))
            .submitLabel("Submit")
            .addEndpoint(EndpointTypes.DELETE, Endpoints.delete()
                .uri("/my-endpoint")
                .scope("MY_SCOPE")
                .attribute(ModalFormAction.Attributes.SUBMIT_COLOR,
                        ModalFormAction.SubmitColors.DANGER)
                .attribute(ModalFormAction.Attributes.SUBMIT_LABEL, "Delete")));
}
Introduced FormFieldColumn Grid component
  • Introduced a new FormFieldColumn component that allows for the columns in ActionListGrid to render a form field for inline editing of the row entity.

    • Added a separate form state context to external grids to allow modifying the row’s state inline and submitting updates to a separate endpoint from the main entity.

    • When the user clicks away from a field in the row after making changes, these changes are saved automatically via the update endpoint specified in the grid metadata.

      • Custom column types that seek similar behavior can expect the update submission to be triggered via an onBlur prop passed as a prop

    • Updated the components for the supported field types (BooleanField, DateField, DecimalField, EnumSelectField, IntegerField, LongField, LookupField, MoneyField, PhoneField, and StringField) to support calling the onBlur callback prop when available.

Tip
See the Metadata 2.0.8 release notes for an example of how to configure a grid with this new type of column.

A new click event handler has been added to the admin that listens for anchor tag clicks specifically targeting a href with the pattern href="modal(id)" where id maps to the id of a modal defined in metadata. This allows reusable modals to be defined and triggered from links on multiple pages.

Define a global component ModalView in the metadata
registry.addGlobalComponent("test-modal", Views.modalView()
       .label("Terms and Conditions")
       .addField("testField", Fields.string()
            .label("Test Field")));

...

form.addField("someField", Fields.string()
       .label("some-field.label")
       .hint("some-field.hint-text"));
Add a modal link in a hint text message
some-field.hint-text=Click <a href="modal(test-modal)">here</a> for more info.

The test-modal modal view will be triggered when the link on the hint is clicked.

Support alternative UPDATE endpoint to be configured for EntityView per EntityViewForm

Updated EntityFormView to be able to configure an UPDATE endpoint on the form itself that overrides the parent View’s UPDATE endpoint

Example usage of overriding the UPDATE endpoint per form
UpdateEntityView view = Views.entityViewUpdate()
        .label("Update View")
        .submitUrl("/main-entity", Scopes.SCOPE)
        // ...
        .addForm("separateEntityForm", Views.entityForm()
                .label("Form with External Related Entity")
                .updateUrl("/external-entity", Scopes.EXTERNAL_SCOPE);
Improved support for updating fields within an ExternalFieldGroup

This work was done in support of allowing a fields within an ExternalFieldGroup to be updated using a form’s override, update endpoint where the actual parent entity’s state wasn’t being modified and wouldn’t be returned from the submit endpoint, which was in a different microservice.

  • Added attributes map to Endpoints metadata with relevant helper methods

  • Added Endpoint attribute, responseIsPartialState: Indicates that this endpoint’s response will be a partial state that should be merged with the existing entity state, similar to a PATCH request. This is useful for APIs that do not support PATCH requests directly or that are backing external field groups on a different entity’s form. In those cases, the parent entity state should be merged with the new state returned by the endpoint to maintain the full view state.

  • Fixed Groups not getting their IDs set when using Form#addGroup(String type, Group<?> group)

  • Expanded javadocs on various metadata DSL interfaces

  • Added attribute to ExternalFieldGroup, useParentFormState. Indicates that the state for the fields in this group will be backed by the parent form’s.

  • If false, then a nested form state will be created to hold the field values in isolation from the parent. Note that this currently makes the group effectively read-only.

  • If true, then users should ensure that the field names are prefixed with the Group.getId() since this is used in the admin to ensure that the values do not collide with other fields in the parent form. TransformBody and MappingList can be used to map the form state to the appropriate structure expected by the form or entity submit endpoint (if any) and vice versa.

public void exampleUsage() {
    form.updateEndpoint(Endpoints.post()
            .responseIsPartialState()
            .uri("/dto-endpoint")
            .transformRequest(t -> t.mappings(transformEntityToRequestDto("externalGroupId")))
            .transformResponse(t -> t.mappings(transformEntityFromRequestDto("externalGroupId")))
            .scope("DTO"))
            .addGroup("externalGroupId", getExternalGroup());
}

public static MappingList transformEntityToRequestDto(String groupId) {
    return new MappingList(Arrays.asList(
            Mappings.mapValue("%s.%s".formatted(groupId, "field1"), "field1"),
            Mappings.mapValue("%s.%s".formatted(groupId, "field2"), "field2"),
            Mappings.mapValue("%s.%s".formatted(groupId, "field3"), "field3")));
}

public static MappingList transformEntityFromRequestDto(String groupId) {
    return new MappingList(Arrays.asList(
            Mappings.mapValue("field1", "%s.%s".formatted(groupId, "field1")),
            Mappings.mapValue("field2", "%s.%s".formatted(groupId, "field2")),
            Mappings.mapValue("field3", "%s.%s".formatted(groupId, "field3")));
}
Introduced TenantService to make interactions with Tenant Microservice extensible

This allows the API calls made by the Admin to Tenant Service to be overridden and customized.

  • Refactored code to use new Tenant Service

  • Introduced new TenantServiceContext to allow overrides by passing in custom TenantService object

Example Override:
// 1. Define your custom getApplications function
const customGetApplications = async (
  tenantId: string,
  applicationId: string
) => {
  console.log('Using custom getApplications with customParam!');
  const defaultService = DefaultTenantService;
  const applications = await defaultService.getApplications(
    tenantId,
    applicationId
  );
  // You can modify params or response here, for example:
  // const response = await axios.get(getTenantApplicationsUrl(), {
  //   ...restrictByTenant(tenantId, applicationId),
  //   params: {
  //     active: true,
  //     forward: true,
  //     offset: 0,
  //     size: getTenantApplicationsPageSize(),
  //     customParam: 'my-custom-value' // adding a custom parameter
  //   }
  // });
  return applications;
};

// 2. Create a custom TenantService implementation
const customTenantService: TenantService = {
  ...DefaultTenantService,
  getApplications: customGetApplications
};

// 3. Create a component that uses the TenantService
const MyComponent = () => {
  const tenantService = useTenantService();

  useEffect(() => {
    // Now this will call the custom getApplications function
    tenantService.getApplications('my-tenant-id', 'my-app-id');
  }, [tenantService]);

  return <div>My Component</div>;
};

// 4. Wrap your component with the TenantServiceProvider
const TenantServiceOverride = () => {
  return (
    <TenantServiceProvider service={customTenantService}>
      <MyComponent />
    </TenantServiceProvider>
  );
};

export default TenantServiceOverride;
Removed Bootstrap dependency

The dependency on Bootstrap CSS has been removed from the Unified Admin interface as it is no longer referenced in our styles. If you are relying on Bootstrap for custom code, or this removal results in any styling issues in your implementation, we recommend re-adding it as a dependency and importing it in your custom code to restore the previous styles.

Miscellaneous Improvements
  • Introduced new SimpleComponentTypeRenderer to handle simple components that do not fall into the existing classifiers like Field, Group, External, and View.

  • Allow for top-level menu items (menu items that have a configured URL without child menu items) to be rendered and displayed in the Navigation menu.

    • Additionally, if a parent menu item only has one child menu item visible, the menu items will be flattened so that the child menu item is displayed at the root level and the parent menu item is hidden. This behavior is controlled by the VITE_ENABLE_NAVIGATION_FLATTEN_SINGLE_MENU_ITEM environment variable, which is true by default.

  • Single-value fields were updated to be able to render raw data according to their types. If the field’s metadata has the displayOnly attribute set to true, then the field will be displayed as a plain or raw text representation. There is also a placeholder value to be displayed when there is no value for the field.

    • The following single-value field components can display fields as raw data:

      • BooleanField

      • DateField

      • DecimalField

      • IntegerField

      • LongField

      • MoneyField

      • PhoneField

      • StringField

      • TextAreaField

    • Updated FieldDecorations to not display change highlights for display-only fields

  • Added the ability to "Select All" tiles from the Product Browse Tile Grid similar to how it is supported on the list grid view. Used for bulk operations.

  • Handle pre-selected items in grids by looking for $selected on the row

  • Handle paginated results when using a transformMapper to map data from the response. Previously, it expected the response to be an array. Added support for checking the content if it exists.

  • This has no impact on any grids unless a transformResponse is added to the grids metadata

  • Introduced a MoneyTileComponent for TileGrid to display money fields

  • Introduced a ToggleTileComponent for TileGrid elements to display boolean fields as toggles.

  • Introduced a description prop to CollapsibleGroup components.

  • Introduced a overrideMaxWidth prop to SlideOver components to allow overriding the max width that is calculated by default.

  • Added mobile breakpoints for TileGrid components to allow for better responsiveness on smaller screens

  • Added ModalFormView that handles Update, Create and simple ModalViews

  • Support targeting Auth token claims in field conditionals

    • Auth claims can be targeted by prefixing $authClaims to the name of the claims, e.g., $authClaims.3pidp_client_registration_id

    • Example usage: Make an Admin User’s email read-only if they authenticated with Google.

  • Enhanced display of display-only Rule Builders so that they are more human-readable

    • This allows a FieldArrayBlock to be marked as displayOnly and for that to be inherited by its child field components.

    • This also improves the look of displayOnly RuleBuilderFields.

      Display-Only Rule Builder Example
  • Introduced support for Computed Dates in DateField component.

    • Takes in metadata-provided computedFromDate, computedFromDuration, and computedFromDurationUnit attributes to dynamically compute the value of the read-only date field.

  • Introduced a new DateRangeField component to display dates in a range format, e.g., "Start Date - End Date".

  • Introduced new DurationField and DurationColumn components to display duration values given specific base units, e.g., "2 weeks".

  • Added support for displaying a success toast notification message when a new entity is created. This is driven by the successNotificationOnCreate attribute. Additionally, a custom message can be set via the successNotificationMessage attribute, otherwise it will fall back to a default success message.

  • Updated the ActionListGridSelectAction to display a toast notification to indicate when the sort action is in process, and another to notify that the sort action was applied successfully.

  • Updated components to support displaying metadata-driven icons in action buttons:

    • Updated the ActionListGridAdvancedSearch component to display a button with a filter icon instead of the original "Filters" label. This icon can be modified via the filterIconName metadata attribute.

    • Updated the ActionListGridModalFormButton component to support displaying a button with a custom icon instead of the action definition’s label. This is driven by the iconName metadata attribute, and if it is not set, it will fall back to displaying the label as before.

    • Updated the custom Export and Import views to also display icons in their buttons and allow for customization via the iconName metadata attribute. By default, these views will display an upload icon and download icon, respectively.

  • Updated the BetterSearchInput component to display an explicit search button, instead of relying on the user to press enter after typing in their search query. This also allows for better accessibility.

  • Added a new selectableWhenMutableOnly metadata attribute to the ActionListGrid component. This is helpful to disable row selection and thus prevent the use of actions on entities that are not mutable.

  • Added a new forceCatalogSelector metadata attribute to Entity Forms that forces displaying the Catalog Selector, overriding the default behavior where it is only shown in Create forms.

  • The Rule Builder is now able to render lookups that are dependent upon the values from the parent form. To achieve this, the $parent prop is now being passed to the RuleBuilderQueryBuilder component.

  • The Grid Create Action now supports refetching on action success based on the new readOnSuccess metadata attribute.

  • Introduced components to support the new Interdependent Grid Concept

  • Implemented custom behaviors in the FieldArrayGrid component to support custom messages for Boolean Attribute Choices, defaulting the choice labels to "Yes" and "No".

  • Updated the main navigation to display the Application Logo instead of the Application Portrait and added environment properties to provide better control over this selection:

    • VITE_USE_PORTRAIT_ASSET_IN_MAIN_NAV: reverts to using the Application Portrait in the main navigation when set to true. This property is false by default.

    • VITE_USE_PORTRAIT_ASSET_IN_APPLICATION_SELECTOR: allows using the first letter of the Application Name instead of the portrait asset as the portrait in the application selector when set to false. This property is true by default.

  • Allowed metadata for entity forms to define a isCatalogOverrideFallbackHintField attribute that can be used as a fallback to determine if the current entity is a catalog override in the current context, even if its ContextState data does not indicate that it is an override.

    • The exemplifying use-case here is a Product from parentCatalog which has characteristics defined in childCatalog. If viewing the entity from the childCatalog context, Product.contextState may not have field changes and will not seem like an override, but the new characteristicsOverriddenHint field in CatalogServices will be used as a fallback to ensure the 'undo overrides' button still appears.

  • isProductionCatalogEntity is now exported as a public method out of CatalogUtils to allow its direct invocation in Delete.tsx

Bug Fixes

  • Fixed extra translations being submitted on entity forms where the values were the same as the default, untranslated values.

    • When in translation mode, an extra call will now be made to fetch the entity’s data without the language header in order to maintain in state the untranslated values for comparison when the TranslateModeService builds out the list of translations to submit to the backend APIs.

  • Added support for metadata attribute default_selected_component_id in TargetKeyLookup component.

    • When it is set, that value will be used to find a matching component and use it as a default selection if no component is found, the first component will be used.

  • Fixed typo on showInAdmin for badge columns/tile components (attribute name changed on the metadata)

  • Fixed TimeZoneProvider not making requests with appropriate scope and against the right endpoints.

    • Previously, this would not work for users without ALL_ADMIN_USER permissions.

  • Application selector will be hidden when no applications are available

  • Fixed mistakenly including the sandboxId in external grid requests when the grid isn’t sandboxable

  • Fixed an issue where the list grid rows are not selectable when they are displayed in a modal popup

  • Fixed an issue with validation for fields with multiple conditions

  • Fixed a bug where the 'Undo Overrides' action was being offered to users even when the entity was not specifically overridden in the current catalog context. This enabled the possibility of users making requests to undo overrides inherited from parent catalogs. Going forward, the action will only appear if the current catalog matches the entity’s override catalog.

  • Added handling in the FieldArrayGrid component to prevent modification of attribute choices that are derived from enum characteristics.

  • Fixed a bug where the 'Undo Overrides' action was being offered to users even when the entity was not specifically overridden in the current catalog context. This enabled the possibility of users making requests to undo overrides inherited from parent catalogs. Going forward, the action will only appear if the current catalog matches the entity’s override catalog.

  • Fixed a bug in LookupColumn where it failed to detect that the lookup value should be hydrated and instead displayed the entity id.

  • Added support for handling strings with single quotes in SpEL expressions, which were previously stripped or would result in errors at evaluation.

  • Replaced polling in the useSummaryGrid in favor of a refresh button.

    • This fixes an issue where continuous polling would clear the state and cause selected rows in the change summary grids to become unselected.


Auth JS SDK Release Notes for 1.6.5

Enhancements & Notable Features

  • Verified support for Node 22 and 24

  • Verified support for React 19

Added support for customizing access token cache key
  • Added new protected method to AuthClient, _saveAuthToken to allow easier customization of the token cache key

  • Added new protected method to AuthClient, _debugLog to allow adding debug logging.

    • To enable, pass in enableDebug as an option when instantiating the AuthClient

Example AuthClient Override
class MyAuthClient extends AuthClient {
  newParam?: string;

  constructor(options: { newParam?: string } & AuthClientOptions) {
    super(options);
    this.newParam = options.newParam;
  }

  // setter for newParam
  setNewParam(newParam: string) {
    this.newParam = newParam;
  }

  /**
   * Add new param to authorize request.
   */
  _buildAuthorizeUrl(
    params: { newParam?: string } & AuthorizeParams
  ): string {
    params.newParam = this.newParam;
    const authUrl = `${this.baseURL}/oauth/authorize?${qs.stringify(params, {
      skipNull: true,
      skipEmptyString: true,
    })}`;

    this._debugLog(`Built authorize URL: ${authUrl}`);

    return authUrl;
  }

  /**
   * Builds a token cache key.
   */
  _buildTokenCacheKey(scope?: string): DefaultTokenCacheKey {
    scope = scope || this.scope;
    if (this.useRefreshTokens && scope.indexOf('OFFLINE_ACCESS') === -1) {
      scope = `${scope} OFFLINE_ACCESS`;
    }
    const key = new DefaultTokenCacheKey({
      accountId: this.accountId,
      clientId: this.clientId,
      scope: scope || this.scope,
      additionalArguments: [this.newParam],
    });
    this._debugLog(
      `Building token cache key with newParam: ${key.stringify()}`
    );
    return key;
  }

  /**
   * Saves the auth token to the token cache.
   */
  _saveAuthToken(
    result: RequestAccessTokenResult,
    clientId?: string,
    scope?: string
  ): void {
    const args = {
      accountId: this.accountId,
      accessToken: result.accessToken,
      clientId: clientId || this.clientId,
      scope: scope ?? this.scope,
      session: result.session,
      additionalKeyArguments: [this.newParam],
    };
    this._debugLog(
      `Saving auth token with newParam: ${JSON.stringify(args)}`
    );
    this.tokenCache.save(args);
  }
}

const AuthProviderInner: FC<AuthProviderInnerProps> = props => {
  // ...truncated for example
  const { state } = useContext(MyContext);
  const myParamValue = state?.someValue;
  const initializeClient = useCallback(
    (options: AuthClientOptions) => {
      const client = new MyAuthClient({ ...options });
      setNewParam(client);
      if (myParamValue) {
        client.setNewParam(myParamValue);
      }
      return client;
    },
    [myParamValue]
  );

  return (
    <AuthProvider
      initializeClient={initializeClient}
    >
      <AuthSessionManager>{children}</AuthSessionManager>
    </AuthProvider>
  );
};

Commerce SDK Release Notes for 1.7.2

Requirements

  • Introduced the RETAIL_DELIVERY_FEE cart item type & order item type

Features & Enhancements

  • Verified support for Node 22 and 24

  • Added attributes map field to the PaymentMethodOptionResponse.

  • Introduce new DefaultProductVariantHydrationBehavior to define the default supported behaviors for hydrating the direct Variants of a requested Product when constructing a product response.

  • Added variantIds, hydratePromotionProducts, skipInventory, skipPricing, and variantHydrationBehavior fields to the ProductListFetchParams.

  • Added skipInventory, skipPricing, and variantHydrationBehavior fields to the BrowseEntityRequest.

  • Added hydrateVariantsOnProducts to the CategoryListParams.

  • Introduced support for recurring payments and subscriptions

    • Added new and updated existing types to align with the new feature

    • Added support for pricing terms and frequencies

    • Added support for filtering alternative payment options by existing subscription term

    • Added an ability to specify the payment strategy in AddItemRequest specifically when payment is billed for a recurring price

    • Introduce ADD_TO_BILL payment type

    • Added support for the subscription Cancellation Policy

    • Added support for new create account member endpoint to the account client


Commerce Quote Microfrontend Release Notes for 1.1.2

Tip

This release is compatible with Release Trains starting in the 2.2.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

  • Verified Node 22 and 24 support

  • Verified React 19 support


Commerce Shared React Microfrontend Release Notes for 1.0.2

Tip

This release is compatible with Release Trains starting in the 2.2.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

  • Verified Node 22 and 24 support

  • Verified React 19 support

Bug Fixes

  • Fixed PreviewProvider not using baseHost to build key in local storefront with remote backend environment


Commerce Subscription React Microfrontend Release Notes for 1.0.2

Tip

This release is compatible with Release Trains starting in the 2.2.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

  • Verified Node 22 and 24 support

  • Verified React 19 support

  • Added support for edit, upgrade, downgrade, and cancel subscription actions

  • Added support for account subscription management

    • Add User

    • Review User

    • Edit Subscription Access for User


Payment JS SDK Release Notes for 1.3.5

Features & Enhancements

  • Verified Node 22 and 24 support

  • Verified React 19 support

  • Modified all usages of Customer Access Tokens to pass in limited scopes when fetching the access tokens.


Payment JS SDK Release Notes for 1.4.0

Features & Enhancements

  • Dropped Node 18 support

  • Verified Node 22 and 24 support

  • Verified React 19 support

  • Modified all usages of Customer Access Tokens to pass in limited scopes when fetching the access tokens.

  • Modified all usages of Customer Access Tokens to pass in limited scopes when fetching the access tokens.

  • Updates for PayPal Complete Payments & server-side shipping callbacks

    • Upgraded PayPal-JS dependency to version 8.2

    • Added PayPalPaymentServicesClient to send requests to our new PayPal APIs.

    • Updated the PayPal CreateOrderRequest to include shipping information, contact information, app switch preference, and callback configuration.

    • Added the Purchase Unit ReferenceID as part of the payload to support shipping callbacks.

  • Added new PayPal Checkout React SDK

    • Added hooks to create and fetch the PayPal order.


Commerce Next.js Starter Release Notes for 1.6.6

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/commerce-nextjs-starter:1.6.6

Features & Enhancements

Miscellaneous
  • Modified all usages of Customer Access Tokens to pass in limited scopes when fetching the access tokens.

Bug Fixes

  • Fix bug where Quote pricing was being overridden by a CSR after making changes, removing any customer-segment based offers that apply to the quote owner.

    • Include the X-Customer-Ref header in any requests to modify a cart when isCsrSelf(user) is true.

  • Fixed bug where the type ahead dropdown did not open if there were product suggestions but no category suggestions.


Commerce Next.js Starter Release Notes for 1.6.7

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/commerce-nextjs-starter:1.6.7

Features & Enhancements

Bug Fixes

  • Fixed bug where the Clone Cart button in the CSR Ribbon shown when impersonating a customer was being displayed even when the cart was already cloned or otherwise marked as a CSR cart.


Commerce Next.js Starter Release Notes for 2.0.0

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/commerce-nextjs-starter:2.0.0

Features & Enhancements

  • Upgraded to Next 16 and React 19

  • Verified support for Node 22 and 24

  • Dropped support for Node 18

Features & Enhancements

  • Updated the Cart Summary to include the Fee Total and list Fee items. See the Pricing Services 2.2.0 Release Notes for details on the new Fee feature.

  • Updated PayPal SDK integration to support new PayPal Complete Payments features. More details on these features can be found on the PayPal 3.0.3 Release notes for PPCP.

  • Fixed bug where the Clone Cart button in the CSR Ribbon shown when impersonating a customer was being displayed even when the cart was already cloned or otherwise marked as a CSR cart.


Telco Next.js Starter Release Notes for 1.0.2

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/telco-nextjs-starter:1.0.2

Features & Enhancements

  • Verified support for Node 22 and 24

  • Pass product when fetching additional pricing targets

  • Made a PDP specific to products with recurring pricing

    • Handles a new display template name, BLC_SUBSCRIPTION, meant to handle demo subscription products (Office 365 and VPNs)

    • The DefaultRecurringPricingPdp is the exact same copy of DefaultPdp except that DefaultPdp now does NOT use the AdditionalPricingOptions while DefaultRecurringPricingPdp does

      • This is to prevent /price-targets requests from being called unnecessarily on the DefaultPdp

    • Products using the PAY_AS_YOU_GO_PHONE template will use the DefaultPdp as they do not have recurring pricing.

  • Added Fee total to Cart Summary and list fees underneath

  • Added generic recurring pricing template and logic to reprice a cart before submitting checkout for virtual or non-shipping fulfillment groups

    • Added condition for GENERIC_RECURRING_PRICING display template

    • Reprice cart right before checkout for nonShippingFulfillment

      • Since they need the BillingAddress to calculate taxes and everything

    • Now reprices the cart right before checkout for fulfillments that have DefaultFulfillmentType of VIRTUAL or NONE. This is done so that taxes and other pricing mechanisms can be run after the BillingAddress has been populated.taxes and everything

Payment Offers Support
  • Add a useAddAttributeToCart hook to facilitate requests to cartClient#addAttributeToCart.

  • Add a useRemoveAttributeFromCart hook to facilitate requests to cartClient#removeAttributeFromCart.

  • The Passthrough Payment Form now uses the useAddAttributeToCart hook to update the PAYMENTS_LIST when a new payment is submitted or removed.

    • The AddAttributeRequest contains invalidatePricing: true, so that the cart will be repriced once the attribute has been updated.

    • The PAYMENTS_LIST attribute is used by the backend to apply offers based on the type of payment. For more details visit the Cart Operation Service release notes.

Bug Fixes

  • Fixed issues with telco product initial pricing and products with recurring prices added to cart

    • Use frequency when adding to cart, fix term messaging to allow 1-year terms

    • Update to pass frequency when adding to cart with additional pricing options

    • Add logic to reset Product PriceInfo to basePrice before fetching additional pricing options

      • The product at this point is already priced, but we don’t want the already-determined bestPrice to be used as basePrice when pricing these additional targets

    • Make sure to pass frequency for dependent items


Telco Next.js Starter Release Notes for 1.1.0

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/telco-nextjs-starter:1.1.0

Features & Enhancements

  • Upgraded to Next 16 and React 19

  • Dropped support for Node 18

  • Verified support for Node 22 and 24

  • Pass product when fetching additional pricing targets

  • Made a PDP specific to products with recurring pricing

    • Handles a new display template name, BLC_SUBSCRIPTION, meant to handle demo subscription products (Office 365 and VPNs)

    • The DefaultRecurringPricingPdp is the exact same copy of DefaultPdp except that DefaultPdp now does NOT use the AdditionalPricingOptions while DefaultRecurringPricingPdp does

      • This is to prevent /price-targets requests from being called unnecessarily on the DefaultPdp

    • Products using the PAY_AS_YOU_GO_PHONE template will use the DefaultPdp as they do not have recurring pricing.

  • Added Fee total to Cart Summary and list fees underneath

  • Added generic recurring pricing template and logic to reprice a cart before submitting checkout for virtual or non-shipping fulfillment groups

    • Added condition for GENERIC_RECURRING_PRICING display template

    • Reprice cart right before checkout for nonShippingFulfillment

      • Since they need the BillingAddress to calculate taxes and everything

    • Now reprices the cart right before checkout for fulfillments that have DefaultFulfillmentType of VIRTUAL or NONE. This is done so that taxes and other pricing mechanisms can be run after the BillingAddress has been populated.taxes and everything === Payment Offers Support

  • Add a useAddAttributeToCart hook to facilitate requests to cartClient#addAttributeToCart.

  • Add a useRemoveAttributeFromCart hook to facilitate requests to cartClient#removeAttributeFromCart.

  • The Passthrough Payment Form now uses the useAddAttributeToCart hook to update the PAYMENTS_LIST when a new payment is submitted or removed.

    • The AddAttributeRequest contains invalidatePricing: true, so that the cart will be repriced once the attribute has been updated.

    • The PAYMENTS_LIST attribute is used by the backend to apply offers based on the type of payment. For more details visit the Cart Operation Service release notes.

Bug Fixes

  • Fixed issues with telco product initial pricing and products with recurring prices added to cart

    • Use frequency when adding to cart, fix term messaging to allow 1-year terms

    • Update to pass frequency when adding to cart with additional pricing options

    • Add logic to reset Product PriceInfo to basePrice before fetching additional pricing options

      • The product at this point is already priced, but we don’t want the already-determined bestPrice to be used as basePrice when pricing these additional targets

    • Make sure to pass frequency for dependent items

Services

Admin Navigation Release Notes for 2.3.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

  • Updated seed data to support the new Fee domain added to Pricing Services. See Pricing Services 2.2.0 Release Notes for more information.

    • Added Admin menu item for Fees management

  • Added standardized Cancellation Policy metadata and Navigation menu item labels & URLs

  • Updated seed data & metadata labels to add Admin menu items for the following features:

  • Implemented SimpleRoutePredicate and added predicates onto ComponentRoutes based on their Route counterparts in order to allow restriction of access to certain Admin Navigation menu items based on the identified predicates


Admin User Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

Bug Fixes

  • Fixed a bug where the Admin UI would allow adding augmentations on the AdminUser Views despite the AdminUser domain not supporting them by default.

  • Updated ExternalAdminUserModificationListener to honor tenant/app access values received on initial user creation.

    • Previously, the values for these fields were not initialized unless broadleaf.adminuser.access-level.manage-tenant-app-access-externally was enabled. However, this meant values set by AuthenticationServices (both before and after AuthenticationServices version 2.3.0) would be ignored when a user was first created. This was previously acceptable, as Auth and AdminUser would always set the same default initial values in their respective local states. However, since Auth 2.3.0, the initial values sent by AuthenticationServices may differ (and may potentially be values defined internally rather than on the 3PIDP configuration). For this reason, the listener has been updated to honor any value for those fields on initial user creation, regardless of whether broadleaf.adminuser.access-level.manage-tenant-app-access-externally is enabled or disabled.

  • Included all Global Components to the metadata response for augmented container components

  • Fixed an issue where fields in the User Profile Preferences form would be overridden by null values if they were not updated by the user.

    • This was due to the fact that the update endpoint for user profile preferences was implemented as a PATCH operation, and the incoming request body would only include fields that were updated by the user.

    • Taking into account that null values could be passed in to indicate that fields are emptied (Phone and Default Application ID can be emptied), a new update endpoint using a PUT operation was added to allow for a full update of the user profile preferences, and the PATCH endpoint is now deprecated.


Asset Release Notes for 2.0.5-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Features & Notable Changes

  • Added support for Caffeine and Ehcache as Spring Cache options.

    • Deprecated AssetServiceCacheAutoConfiguration (Ignite cache auto-configuration) in favor of these standard Spring Cache implementations.

    • For more details, see the Caching documentation.

  • Updated ImageMagick detector to support a warning message when using deprecated commands.


Auth Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • Spring Authorization Server 1.5.6+ is required for this version of AuthenticationServices, and beyond

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

Spring Authorization Server Upgrade

AuthenticationServices is built upon and customizes various functionality from Spring Authorization Server.

In typical release cycles, the underlying version of Spring Authorization Server is upgraded via the standard Broadleaf base dependencies upgrades, and does not necessarily result in any changes to AuthenticationServices itself.

In this release, however, a multitude of classes in AuthenticationServices have been updated to reach full parity with the recently released Spring Authorization Server 1.5.6 version.

The overwhelming majority of changes around this are just simple internal refactorings to align with similar changes in Spring Authorization Server itself. From a functional perspective, there should be no significant differences in the behavior of the application.

With that being said, there are a handful of new features in Spring Authorization Server (SAS) such as OAuth2 Demonstrating Proof of Possession (DPoP) and Pushed Authorization Requests (PAR). While Broadleaf does not explicitly leverage or customize these flows, at the very least, the changes in this release give the relevant Broadleaf components the foundation for future support.

Below is a list of key affected classes. In all of them, there have been changes made to respond to SAS’s refactoring and eliminate redundant code.

  • OIDAwareOAuth2RefreshTokenAuthenticationProvider

    • DPoP support

  • PublicRefreshOAuth2AuthorizationCodeAuthenticationProvider

    • Updated comments/Javadocs to explain the customizations in context of the new SAS

    • DPoP support

  • ScopeNarrowingOAuth2AuthorizationCodeRequestAuthenticationProvider

    • PAR support

    • Native OIDC prompt support

  • ScopeNarrowingOAuth2ClientCredentialsAuthenticationProvider

    • DPoP support

    • The base SAS class introduced a new authenticationValidator concept to validate scopes, but our customizations require a different approach, so this is disabled by default.

  • PublicRefreshPublicClientAuthenticationProvider

    • Aligns with stricter PKCE enforcement around codeVerifier from newer SAS versions

  • PublicRefreshCodeVerifierAuthenticator

    • Aligns with stricter PKCE enforcement around codeVerifier from newer SAS versions

    • Aligns with fail-fast behavior from SAS when code was missing in an authorization_code flow

  • AdvancedOAuth2RefreshTokenConfigurerUtils

    • SAS introduced some new 'default' token customizers relate to DPoP and Token Exchange delegation, which are now successfully registered in addition to the standard token customizers from Broadleaf.

  • DefaultClientScopeAuthorizationCodeRequestConverter

    • PAR support

  • DefaultOAuth2AuthorizationCodeRequestAuthenticationValidator

    • Updated with new methods matching changes in SAS for use in other components

  • EmbeddedLoginCodeAuthenticationProvider

    • Updated to maintain full parity with updates in PublicRefreshOAuth2AuthorizationCodeAuthenticationProvider and the latest SAS

Third Party Login Improvements
  • Introduced a new OAuth2LoginSystemAuthenticationFailureExceptionMapping bean responsible for handling system (not user) errors that occur on 3PIDP login requests.

    • This means if the 3PIDP setup is misconfigured in any way (ex: invalid client secret, etc), the end user will now see an accurate error message stating there is an internal system error rather than an issue with the user’s input.

    • The handler will also emit error logs detailing the issue, since such logs were not already being emitted by the Spring components implementing the login flow.

  • When a third party IDP client registration is defined in both the DB and in properties, the system will now disallow admin (API) requests to modify the DB-based registration. Property-defined client registration information now always takes precedence when specified, so mutating the DB-based representation would not have any effect anyway. In order to mutate the DB representation, any system configuration properties related to that registration must be fully removed from the environment. This validation prevents users from being confused about their changes to the DB representation not actually taking effect.

  • Excluded issuerUri from OAuthClientRegistration equals/hashcode, as the database representation never has a value for this field, and thus causes unnecessary mismatches to be detected between the property and DB representations of a client registration

  • Introduced changes to allow 3PIDP configurations to specify initial values for tenant/app access when auto-registering users whose tenant/app access is not managed externally

    • Introduced new provider-specific configuration properties to allow AuthenticationServices to set initial values for tenant/app access on auto-registered admin users when external management is disabled. Please see the configuration properties documentation for more details

      • Previously this was only per app type (e.g., only per Admin or Application).

    • Updated AbstractExternalAdminUserHandler to defer to these internally set properties when external management is disabled. If the external property values are not set, access defaults to no-access just as before.

      Example Configuration
      broadleaf:
        auth:
          user:
            web:
              authorization:
                identity-provider:
                  admin:
                    # original setting, applies to all admin apps
                    # auto-register: true
                    providers:
                      okta:
                        # new per provider setting
                        auto-register: true
                        icon: /img/okta-icon.svg
                      google:
                        # new per provider setting
                        auto-register: false
                        icon: /img/google-icon.svg
  • Added 3rd party IDP claims to access token

    • Previously they were only on the session token.

    • This allows them to be known in the UI and acted on such as to make a user’s email read-only if they signed in with Google.

  • Added support for syncing Account numbers to Auth

    • This is a modification to the CustomerAccount domain.

Miscellaneous
  • Property broadleaf.auth.token.support-refresh-token-rotation was removed and whether refresh token support is enabled is now solely based on AuthorizedClient/RegisteredClient grant types(REFRESH_TOKEN)

    • For example: registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)

  • Property broadleaf.auth.token.support-refresh-token-cleanup removed.

  • Introduced the new property broadleaf.auth.token.enable-oauth2-authorization-cleanup. This property would default to true. This property controls whether a clean-up job to prune obsolete OAuth2Authorizations (runs periodically) is enabled. This property supersedes the now-removed broadleaf.auth.token.support-refresh-token-cleanup property.

  • Added an event listener that will prune the blc_password_token table as it can grow large over time.

  • Added official support for Caffeine and Ehcache as alternative caching providers for many microservices.

    • This includes new auto-configuration and properties for managing heap and off-heap budgets, as well as weights and estimated sizes for various service components.

    • For more details on configuring these providers, see the Caching Configuration documentation.

  • Updated scheduled jobs to support more user-friendly duration formats (e.g., 2d, 12h) in addition to ISO-8601.

    • Additionally, these jobs now emit started and completed events to provide better visibility into their execution status.

  • Introduced lots of new diagnostic trace logs in OAuth2 flows to make it easier to identify the root cause that failed a request. You can now enable TRACE logs in any of the following classes, and more detailed information will appear as the request moves through the flow.

    Note
    This is primarily intended only for use in lower environments, and in scenarios where testing a new integration may temporarily require extra diagnostic output. It’s not recommended to enable these logs in production, as they will be very noisy.
    Note
    The API responses returned to the external caller have not changed - the messages there still remain vague to prevent leaking information to potential attackers.
    • com.broadleafcommerce.auth.authorization.security.embedded.code.EmbeddedLoginCodeAuthenticationConverter

    • com.broadleafcommerce.auth.authorization.security.embedded.code.EmbeddedLoginCodeAuthenticationProvider

    • com.broadleafcommerce.auth.authorization.security.spring.OIDAwareOAuth2RefreshTokenAuthenticationProvider

    • com.broadleafcommerce.auth.authorization.security.spring.PublicRefreshCodeVerifierAuthenticator

    • com.broadleafcommerce.auth.authorization.security.spring.PublicRefreshOAuth2AuthorizationCodeAuthenticationProvider

    • com.broadleafcommerce.auth.authorization.security.spring.PublicRefreshPublicClientAuthenticationProvider

    • com.broadleafcommerce.auth.authorization.security.spring.ScopeNarrowingOAuth2AuthorizationCodeRequestAuthenticationProvider

    • com.broadleafcommerce.auth.authorization.security.spring.ScopeNarrowingOAuth2ClientCredentialsAuthenticationProvider

  • Refactored AuthenticationFailureExceptionMapping/DefaultExceptionMappingAuthenticationFailureHandler to support more complex code-based error handling determinations rather than purely relying on an exception name. Furthermore, AuthenticationFailureExceptionMapping can now define a bean order, which allows defining a priority of the mapping recognized by DefaultExceptionMappingAuthenticationFailureHandler

    • Existing AuthenticationFailureExceptionMapping beans will continue to work as expected for backward compatibility.

  • Add explicit AuthenticationFailureExceptionMapping bean for the UsernameNotFoundException to produce a more useful error message when that exception is thrown

  • The Application’s logo is now displayed on the login form if available

  • Add logic to filter out questionable permissions for customers in auth. See Customer Authority Filter

  • Introduce cache(oauth2IdentityProviders) for oauth2 identity providers instead of Map. You can control its ttl with property broadleaf.auth.cache.oauth2-identity-providers-cache, default is 5 mins.

  • Now when a DB(blc_oauth_client_registration) and a property file(spring.security.oauth2.client.registration.xxx) have the same entry for client registration, the one from a property file will be used.

  • Removed Yugabyte liquibase generation support, as it is no longer a supported DB.

  • Added support for user restrictions to be discriminated by account ID.

  • Added support to be able to display an Application’s logo on the login form.

  • Optimized the space efficiency of restrictions / restricted authorities in access tokens.

  • Added support to be able to update a user’s restrictions, restricted roles, and restricted permissions upon receiving an AccountMemberRestrictionUpdateEvent event.

  • Added support to persist a user in the Authentication Service from the Account User Creation Flow.

    • Updated Customer representation to properly deserialize the ID and ContextState when emitted from Customer Service with the actual Customer object (as opposed to PersistenceMessage payload)

    • Introduce CustomerPersistenceUtil to handle some common logic related to customer persistence

    • Added consumer events and bindings to listen for events emitted from Customer Service related to customer persistence, such as when a new customer is created and needs to be persisted in Auth

    • Note that the reason why the event is named AccountUserManuallyCreatedEvent instead of just AccountUserCreatedEvent is to be explicitly clear that this is specifically emitted & processed when an account user is manually created by another account user, rather than by the system through the account invite flow

  • Added support to emit audit events for user lifecycle changes such as user creation, deletion, and login/logout events.

New Seed Data
  • Added permissions/scopes for the new Fee domain added to Pricing Services. See Pricing Services 2.2.0 Release Notes for more information. These changelogs are included in the auth.starter.required.data.changelog.xml file.

    • Introduced FEE permissions and scopes

    • Granted the cartsopsclient the ALL_FEE permission/scopes

    • Added openapi client scope and permission for FEE

    • Granted the FEE user permissions to default Admin roles such as FULL_ACCESS, PARTIAL_ACCESS, APPLICATION_ACCESS, ROLE_MARKETPLACE_OPERATOR, ROLE_VENDOR_ADMIN, and ROLE_VENDOR_MERCHANDISER

  • Added permissions/scopes for the new fulfillment callback endpoints in Cart Operation Services and Payment Transaction Services. These changelogs are included in the auth.starter.required.data.changelog.xml file.

    • Introduced FULFILLMENT_PAYMENT_CALLBACK and EXTERNAL_FULFILLMENT_CALLBACK permissions and scopes

    • Granted the cartopsclient the FULFILLMENT_PAYMENT_CALLBACK scope

    • Granted the anonymous client the EXTERNAL_FULFILLMENT_CALLBACK scope

    • Added openapi client scope and permission for FULFILLMENT_PAYMENT_CALLBACK and EXTERNAL_FULFILLMENT_CALLBACK

  • Granted the cartsopsclient the SYSTEM_SAVED_PAYMENT_MANAGEMENT scope and READ_SYSTEM_SAVED_PAYMENT_MANAGEMENT permission

    • This is needed to read the available saved payment methods summary to validate the subscription payment by CartPaymentMethodValidationActivity during checkout

  • Added permissions/scopes for the new concepts of Cost and Adjustment price lists.

    • Introduced COST_LIST and ADJUSTMENT_LIST permissions and scopes.

    • Added an openapi client scope and permission for COST_LIST and ADJUSTMENT_LIST.

    • Granted the COST_LIST and ADJUSTMENT_LIST user permissions to default Admin roles such as FULL_ACCESS, PARTIAL_ACCESS, APPLICATION_ACCESS, and ROLE_MARKETPLACE_OPERATOR

  • Added missing security scopes and permission mappings for Customer operations such as CUSTOMER_RETURN, CUSTOMER_ORDER, and CUSTOMER_ENTITLEMENT.

  • Created a separate permission to allow Admins to modify their own preferences.

    • Introduced ADMIN_USER_PROFILE permissions and scopes.

    • Added an openapi client scope and permission for ADMIN_USER_PROFILE.

    • Granted the ADMIN_USER_PROFILE user permissions to default Admin roles such as FULL_ACCESS, PARTIAL_ACCESS, APPLICATION_ACCESS, ROLE_MARKETPLACE_OPERATOR, ROLE_VENDOR_ADMIN, and ROLE_VENDOR_MERCHANDISER

  • Added scopes and permissions to restrict which users can modify Cart internal attributes.

    • Introduced CART_INTERNAL_ATTRIBUTE permissions and scopes.

    • Added an openapi client scope and permission for CART_INTERNAL_ATTRIBUTE.

  • Added an authorized client & permissions/scopes for operations related to the newly introduced Billing Services and Subscription Operations Services.

    • Introduced a new billingclient authorized client mapped with the ACCOUNT_SUBSCRIPTION, BILLING, CUSTOMER_SUBSCRIPTION, REFUND_TRANSACTION_RESULTS, SAVED_PAYMENT_METHOD_EXECUTE_AUTHORIZE_AND_CAPTURE, SAVED_PAYMENT_METHOD_TRANSACTION_RESULTS, SUBSCRIPTION, SYSTEM_SAVED_PAYMENT_MANAGEMENT, and SYSTEM_SUBSCRIPTION and scopes & permissions.

    • Introduced a new subscriptionopsclient authorized client mapped with the ADMIN_SCOPED_SERVICE_CLIENT, CART_INTERNAL_ATTRIBUTE, CUSTOMER, CUSTOMER_SEGMENT, OFFER, ORDER, SUBSCRIPTION, SYSTEM_SUBSCRIPTION scopes & permissions.

    • Mapped the SUBSCRIPTION_PRICING, SYSTEM_SAVED_PAYMENT_MANAGEMENT and SYSTEM_SUBSCRIPTION scopes & permissions to the cartopsclient authorized client.

    • Mapped the ADMIN_SCOPED_SERVICE_CLIENT, BILLING, DELETE_SUBSCRIPTION, OFFER, PRICE_LIST, SUBSCRIPTION, and SYSTEM_SUBSCRIPTION scopes & permissions to the workflowclient authorized client.

    • Granted the BILLING, SUBSCRIPTION, and ACCOUNT_SUBSCRIPTION user permissions to default Admin roles such as FULL_ACCESS and PARTIAL_ACCESS.

    • Granted the CUSTOMER_SUBSCRIPTION user permission to the default Customer User role.

  • Added permissions/scopes for access to the Cancellation Policy feature.

    • Introduced CANCELLATION_POLICY permissions and scopes.

    • Added an openapi client scope and permission for CANCELLATION_POLICY.

    • Granted the CANCELLATION_POLICY user permission to default Admin roles such as FULL_ACCESS and PARTIAL_ACCESS.

    • Mapped the CANCELLATION_POLICY scope & permission to the cartopsclient and `authorized clients.

  • Added permissions/scopes for the pricingclient and notificationclient to read order and order fulfillment data.

    • Granted the pricingclient the ORDER_FULFILLMENT scope and READ_ORDER_FULFILLMENT permission.

    • Granted the notificationclient the ORDER and ORDER_FULFILLMENT scopes and READ_ORDER and READ_ORDER_FULFILLMENT permissions.

Bug Fixes

  • AuthServiceUserWebAutoConfiguration.addResourceHandlers was previously misconfigured with invalid resource paths, which caused failures starting in Spring Boot 3.5 due to stricter Spring validations. The resource configuration has been reworked with corrected syntax and more granular mappings. From an external perspective, nothing has changed, and resources can still be accessed at the same paths as before (so templates will be unaffected).

  • Fixed a compilation issue related to the Thymeleaf upgrade.

  • Fixed a channel name typo in PurgePasswordTokenJobListener.

  • Fixed an error when the AccountMemberRoleChangeRequest#userId or AccountMemberRestrictionUpdateEvent#userId is null.

  • Fixed bug in AccountMemberRestrictionChangeRequestHandler where the restrictions field was incorrectly being cleared instead of the actual roles and permissions fields

    • When restricted roles or restricted permissions are empty in the event payload, the restrictions field is being cleared instead of the appropriate user field for roles or permissions.

  • Fixed message for the UserNotActiveException thrown when a user is deactivated while already logged in.

  • Fixed an issue where the creation of an Authorized Client with a name longer than 36 characters would fail due to the name being too long for the database column.


Bulk Operations Release Notes for 1.0.3-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Notable Enhancements

  • Added DataRouteByKey annotation to BulkOperationsEndpoint


Cart Services Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

  • Introduce CartItem#hasOverriddenSubtotal flag to support overriding the CartItem#getSubtotalWithDependentItems() calculation when portions of the subtotal don’t scale with the item quantity

  • Add support for subscription Cancellation Policy identification

  • Add support for customer context tracking on cart

    • Cart entity tracking types were updated to FullTracking to support customer context. This was changed because application tracking isn’t sufficient in all scenarios for Carts. An example is business having a web app and a mobile app. These may have different application IDs for discrimination of certain things (e.g. content), but need to share customers, catalogs, carts, orders, etc. With the application discrimination of Cart, there was no straightforward way to share Carts between these two applications. Customer context discrimination (which can be shared across applications) solves this need.

Bug Fixes

  • Fix issue where CartCrudEntityHelper was not sending out mutation events since it wasn’t configured properly as a spring bean in order to accept the autowire dependency for event publisher

Upgrade Guide

Add new columns to the BLC_CART_ACTION_AUDIT table
ALTER TABLE cart.blc_cart_action_audit
    ADD COLUMN IF NOT EXISTS trk_catalog_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_catalog_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_catalog_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_catalog_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_parent_cat_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_composite_sort_level NUMERIC(11,5),
    ADD COLUMN IF NOT EXISTS trk_customer_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_field_overrides VARCHAR(10485760);
Add new columns to the BLC_CART_NOTE table
ALTER TABLE cart.blc_cart_note
    ADD COLUMN IF NOT EXISTS trk_catalog_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_catalog_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_catalog_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_catalog_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_parent_cat_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_composite_sort_level NUMERIC(11,5),
    ADD COLUMN IF NOT EXISTS trk_customer_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_field_overrides VARCHAR(10485760);
Add new columns to the BLC_CART table
ALTER TABLE cart.blc_cart
    ADD COLUMN IF NOT EXISTS trk_catalog_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_catalog_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_catalog_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_catalog_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_level BIGINT,
    ADD COLUMN IF NOT EXISTS trk_parent_cat_locale VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_parent_cat_name VARCHAR(255),
    ADD COLUMN IF NOT EXISTS trk_composite_sort_level NUMERIC(11,5),
    ADD COLUMN IF NOT EXISTS trk_customer_context_id VARCHAR(36),
    ADD COLUMN IF NOT EXISTS trk_field_overrides VARCHAR(10485760);
Add new columns to the BLC_CART table
ALTER TABLE cart.blc_cart
    ADD COLUMN trk_catalog_context_id VARCHAR(36),
    ADD COLUMN trk_catalog_level BIGINT,
    ADD COLUMN trk_catalog_locale VARCHAR(255),
    ADD COLUMN trk_catalog_name VARCHAR(255),
    ADD COLUMN trk_parent_cat_context_id VARCHAR(36),
    ADD COLUMN trk_parent_cat_level BIGINT,
    ADD COLUMN trk_parent_cat_locale VARCHAR(255),
    ADD COLUMN trk_parent_cat_name VARCHAR(255),
    ADD COLUMN trk_composite_sort_level NUMERIC(11,5),
    ADD COLUMN trk_customer_context_id VARCHAR(36),
    ADD COLUMN trk_field_overrides VARCHAR(10485760);
Add new indexes for the trk_customer_context_id
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx7b28qu6ygegxyv9u30fvfch6m
ON cart.blc_cart(trk_customer_context_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idxh49b7b4j012hmjcgv13c4hf92
ON cart.blc_cart_action_audit(trk_customer_context_id);

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx3779sx8swarwd9a7t0mf2bmvj
ON cart.blc_cart_note(trk_customer_context_id);

Cart Operation Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

Fees
Tip
Since Cart Operation Service 2.3.0, Release Train 2.3.0

This release includes support for Fees, which is a new domain addition to Pricing Services. See Pricing Services 2.2.0 Release Notes for more information on the domain.

There is also relevant documentation that catalogs the updates to Cart Operations Pricing, such as the changes made to the Pricing Provider, Cart Pricing Service, and the new Fee Pricing Service.

  • Implemented Fee calculation logic when a Cart is being priced.

    • Created the FeePricingService that handles fee retrieval, calculation, and applying the matching fee items onto the cart.

    • Fees are fetched by Application (retrieved via the ContextInfo) from the Pricing Service and are cached by application to facilitate easier retrieval and eliminate redundant calls to the Pricing Service.

  • Created CartOperationCacheAutoConfiguration and CartOperationCacheProperties to enable caching for the Cart Operation Service and to be able to cache Fees by Application

  • Added more CartItemAttributeConstants that are applicable for Fee-type CartItems

    • ITEM_TYPE = The type of the cart item, which is typically a DefaultCartItemTypes enum

    • FEE_CODE = The code of the fee that the cart item represents

    • IS_FEE_REFUNDABLE = Whether this cart item that represents a fee is refundable

  • Added new domains and enum types

    • Added Fee domain to represent the fees to be applied onto a Cart

    • Added enum types DefaultFeeCalculationMethod which represents the calculation method to be used when applying the fee to a cart and DefaultFeeCalculationOperation which represents the operation to be used when calculating the fee to be applied to a cart

  • Resolved an issue that limited quantity validation to ItemChoice-dependent cart items. All dependent items are now validated correctly.

  • Tax Handling

    • Introduced clear separation in the DefaultDelegatingTaxService between COD Fees and Extra (e.g., service) Fees.

    • Create a separate TaxCalculationGroup for extra fees by default.

    • By default for tax purposes, extra fees the address of the first fulfillment group on the cart

      • See DefaultDelegatingTaxService#mapFulfillmentGroupsToExtraFees to control how fees are related to fulfillment groups

    • Protected methods removed and replaced for code and usage correctness:

      • addFeeItemsTaxCalculationGroup: Renamed includeFeesInTaxRequest

      • findTaxAddressSourceHandlerForFeeTaxCalculation: Renamed findTaxAddressSourceHandlerForCODFeeTaxCalculation

Include Variant IDs in Catalog Info requests after Add to Cart

The ProductDetailsRequest now (Catalog Service 2.3.0) supports specifying variantIds to fetch, not just Product IDs and URIs, in order to make add-to-cart more efficient. In support of this, ExternalCatalogProvider#retrieveCatalogItems will now include the IDs of Variants in the collection of CatalogItemRequests as parameters if present. The variant ID will be used in place of the product ID in these cases as the parent product can be retrieved by Catalog Service once the Variant is resolved automatically. Additionally, #retrieveCatalogItems will specify to hydrate none of the Product’s other variants.

  • The protected methods #getRetrieveProductsUrl(Collection<String>) and #getRetrieveProductsUrl(Collection<String>, ContextInfo) have been replaced with #getRetrieveProductsUrl(Collection<CatalogItemRequest>, ContextInfo) to provide more information and greater flexibility when build the URL with various parameters.

  • Added protected method #customizeRetrieveProductsUrl(UriComponentsBuilder, Collection<CatalogItemRequest>, ContextInfo) to provide a clear hookpoint for custom parameters without having to copy the base method that produces a stringified URL.

  • Variants are never hydrated by default. Use broadleaf.cartoperation.catalogprovider.variant-hydration-behavior to modify this behavior.

    • This takes values of NONE (new default), ALL (old behavior), or DEFAULT_ONLY (i.e., return the Product with its default Variant hydrated only).

    • It is not expected that any but NONE will be needed since only a specific variant is ever added to the cart.

Payment Fulfillment Callback Endpoint
Tip
Since Cart Operation Service 2.3.0, Release Train 2.3.0

This release includes support for fulfillment callbacks initiated from a payment gateway. This feature was added with an implementation for PayPal server-side shipping callbacks as part of our updates to PayPal Complete Payment (PPCP). See PayPal Payment Library 3.0.3 Release Notes for more information on the Complete Payment updates.

  • Added a new endpoint for payment gateway-initiated fulfillment callbacks

    • Calls Payment Transaction Services to understand the payload from the Payment Gateway and validate the callback

    • Updates the fulfillment information and reprices the Cart

    • Calls Payment Transaction Services to build the response payload to return back to the Payment Gateway

Retail Delivery Fees
  • Added support for Retail Delivery Fees from the Avalara Tax Module

    • Updated DefaultDelegatingTaxService to build RETAIL_DELIVERY_FEE type CartItems from the RDF tax infos returned on the TaxResponse.

      • Ensures that the applicable fulfillment groups have an attribute set to track any RDF cart items.

      • Ensures that for a given RDF, it is only applied one time to the cart as a fee item, regardless of the number of fulfillment groups.

    • Added checkout validation for RDF cart items to ensure that if a fulfillment group is expecting an RDF item, then that RDF cart item exists on the cart.

    • Marked the RDF cart items as non-removable from the cart, excluded the RDF cart items from stale cart item removal considerations, and ensured the RDF items were not added as fulfillment items.

    • Ensured the RDF cart items are not included in the calls to Catalog, Offer, or Pricing services.

    • Clears RDF cart items before applying tax response to get the latest retail delivery fees supplied by the tax provider.

    • For details on the Avalara RDF implementation, see Avalara Retail Delivery Fees documentation.

Feature: Added mechanism to filter out sensitive data from cart payloads

See the CatalogBrowse-counterpart of this mechanism in Catalog Browse Services 2.2.0 Release Notes.

Introduced an OncePerRequest filter guarded by configured URIs patterns that will remove the baseCost property from the Cart response payloads.

This mechanism can be configured by the following properties and is enabled by default (see CartOperationSensitiveDataRemovalProperties):

broadleaf:
  cartoperation:
    sensitive-data-removal:
      enabled: true
      url-patterns:
        - /cart/*
        - /checkout/*
        - ... etc.
      fields-to-remove:
        - baseCost      # default
Miscellaneous
  • Updated ExternalCatalogProvider to now include a skipInventory=true request parameter in the request to retrieve products. This parameter became available in CatalogBrowseServices 2.2.0.

    • This reduces unnecessary load on the system and improves performance, as CartOps does not ever use the availabilitySummary information returned by CatalogBrowse. Any inventory related checks are done via separate, intentional inventory check/reserve calls during the add-to-cart flows.

  • In order to support OMS consolidated refunds, the TransactionExecutionRequest and similar DTOs have been modified to support multiple source entity IDs.

  • Extracted duplicate logic from DefaultCartItemCatalogInformationService#populatePricingInfo and DefaultCartPricingService#applyPricesToCart for setting price infos in Cart Item attributes from Products and Variants into CartItemPricingUtils#populatePricingInfoForItem

    • Also extracted #cleanPriceInfo and #getPricingKey into CartItemPricingUtils as public methods.

  • Support for subscription payment validation during checkout

Bug Fixes

  • DefaultInventoryAvailabilityService.getRequestedQuantitiesPerSku was indiscriminately including dependent cart items in availability checks during add-to-cart flows. Now, inventory checks will only be performed for dependent items if their inventoryCheckStrategy is ADD_TO_CART, making it consistent with the behavior in place for top-level cart items.

  • Fixed an issue where CartItemValidationActivity was not leveraging CartOperationServiceProperties#isVariantIdUsedToIdentifyCatalogItemRequests to identify duplicate CatalogItemRequests, and so would be removing cart items during checkout if multiple variants of the same Product were in a cart.

    • Part of the solution was to consolidate the logic to identify duplicate CatalogItemRequests into a new CartItemConfigurationService#catalogItemRequestAlreadyExists method that can be used across the service.


Catalog Services Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

  • This version now requires DataTracking 2.0.7+

  • This version now requires TranslationCommon 2.0.5+

New Features & Notable Changes

Warning
This release involves breaking changes in multiple areas, most notably in the ProductCharacteristic domain separation and reindexing changes to ProductCharacteristics. Please review those sections carefully, as some action may be required.
Feature: Catalog Characteristic Value Narrowing
  • Added support for "narrowing" characteristic values in child catalogs.

  • Introduced a new narrowingEnabled property for product characteristics:

    • Allows characteristic values (e.g., Color, Size) to be selectively narrowed for child catalogs.

    • Only values marked as "active" in the child catalog are available; inactive values are excluded.

  • UI and API changes:

    • New boolean field for "Should this characteristic’s values narrow in child catalogs?"

    • Tooltip and help text provided for user guidance.

  • Hydration logic and backend services updated:

    • Core product hydration flow applies narrowing when enabled.

    • New CharacteristicHydrationService and default implementation to handle narrowing logic.

  • Database schema:

    • Added is_narrowing_enabled column to the blc_characteristic table in all supported databases.

  • Backward compatible; default is disabled unless explicitly set.

Notes
  • Useful for scenarios where child catalogs should only see a subset of parent characteristic values.

  • See JavaDocs and tooltips for details on usage and configuration.

Feature: Placeholder Characteristics
  • Introduced a new property placeholder on the Characteristic domain model.

    • This property determines whether this characteristic is allowed to have no values set. If false, at least one value must be specified.

    • This is primarily used in advanced scenarios where a characteristic is being used as a placeholder for values that will be defined later, e.g., by an external system.

Feature: Tiled Grid View for Product Browse in Admin
  • Updated the out of box metadata configuration to make use of the new Tiled Grid View for the product browse view in the Admin.

  • Added support for assigning "targetKey" (augmentation target keys) to characteristics in BusinessTypeCharacteristic and Characteristic domain models.

  • Introduced new DSL extension: TargetKeyLookup and its implementation DefaultTargetKeyLookup, enabling form section/group placement of characteristics.

  • Extended metadata auto-configuration to include "targetKey" controls in admin forms for characteristics.

Support Specifying Variant IDs in Product Details Requests

Added support in ProductDetailsRequests to specify the IDs of Variants to specifically fetch and to exclude hydrating other direct Product Variants. This is in support of improving the performance of Add-to-Cart requests since Cart Operations Service calls the ProductDetailsEndpoint to retrieve Catalog information for the items requested to be added to the cart.

  • New Field on ProductDetailsRequest

    • variantIds: The set of Variant IDs to specifically retrieve. Their parent products will also be returned as ProductDetails in the response.

  • New Field on ProductDetailsList

    • variantIdsForMissingEntities: The set of requested Variant IDs that were not resolved.

Support Specifying Variant Hydration Behavior in Product Details Requests

Added support in ProductDetailsRequests to specify the system’s behavior when hydrating Variants onto ProductDetails. By default previously, all direct variants were fetched for all products specified in the ProductDetailsRequest by ID or URI. Now, the request can specify whether to hydrate all, none, or just the default variant using the param variantHydrationBehavior. The parameter is optional, and, if not present, broadleaf.catalog.product.details.variant-hydration-behavior will determine the behavior, which remains ALL by default.

This configurable behavior has also been extended to CategoryDetailsRequests when CategoryProducts are added to the response. This likewise previously would hydrate all direct variants of the category products. However, now, the default behavior is to hydrate NONE of them. To modify the default system behavior, set broadleaf.catalog.category.details.variant-hydration-behavior.

  • New Field on BrowseDetailsRequest, CategoryDetailsRequest, and ProductDetailsRequest

    • variantHydrationBehavior: String (Enum). This is blank by default on the request. The default behavior for the system is determined by broadleaf.catalog.product.details.variant-hydration-behavior whose default is ALL for backwards compatibility. The supported behaviors are:

      • ALL: All direct variants are returned.

      • NONE: No direct variants are returned unless specifically requested, see ProductDetailsRequest#variantIds.

      • DEFAULT_ONLY: Only the default Variant is hydrated. This is typically the Variant whose SKU matches the parent Product’s.

Important
We removed the deprecated overloaded versions of ProductDetailsService#getDetailsForProducts in favor of simplified ProductDetailsService#getDetailsForProducts(Collection<Product>, ProductDetailsRequest, ContextInfo). The previous Set<String> arguments for the missing Product IDs and URIs are instead expected to be set as transient fields of the ProductDetailsRequest in order to avoid expanding the number of inputs further for variantIdsForMissingEntities and other future extensions. This will cause compilation errors for extensions of the older methods.
Propagation Enhancements
  • In DataTracking 2.0.7, a new batch-optimized catalog propagation implementation was introduced. For projects that enable this feature, the standard propagators in DataTracking are insufficient to handle Product.tags, and thus JpaProductSimpleTagsUpdatePropagator has been introduced (along with new methods in JpaCustomizedProductRepository) in CatalogServices to handle that field.

  • ProductCharacteristic instances are no longer subject to catalog propagation by default, and components have been introduced to explicitly skip this process for that domain. For more details, please see the Javadocs of JpaProductCharacteristic, as well as Product Characteristic Propagation Properties.

    • For projects that have the new batch-optimized catalog propagation enabled, JpaProductCharacteristicDisabledHierarchyPropagationHandler handles skipping characteristic propagation.

    • For the existing, standard propagation flow, JpaProductCharacteristicDisabledPropagationHandler handles skipping characteristic propagation.

Product Characteristics Domain Separation
Warning
This is a breaking change involving a data migration. Please read this entire section carefully.
  • Refactored Product.characteristics from an embedded @OneToMany collection in JpaProduct to a fully separate entity in the blc_product_characteristic_ext table.

    • This change improves inheritance handling by allowing individual product characteristics to be tracked separately.

    • Previously, overriding a product in a child catalog duplicated all characteristics, breaking inheritance for unchanged ones. Now, modifying one characteristic does not break inheritance for the others.

    • When a product is saved, the supplied characteristics are compared to any existing characteristics and only changes are persisted to ensure inheritance is preserved.

  • Database Changes:

    • The blc_product_characteristic table is now obsolete and has been replaced by the new blc_product_characteristic_ext table, which stores the externalized collection.

    • Migration scripts are included to automatically move data from the old table to the new one via Liquibase (e.g., db/changelog/jpa/migration/migrate-product-characteristics.postgresql.sql). Scripts are provided for all supported databases.

      Warning

      Sandbox Limitation: The migration scripts do not migrate sandbox records. It is strongly recommended to promote and deploy all pending changes involving Product Characteristics before upgrading to ensure minimal data loss.

      Note

      Override Detection: The script logic attempts to detect when a characteristic was truly overridden versus when it was simply duplicated from the parent (a side effect of the previous architecture). Only detected overrides are migrated. This helps restore inheritance for values that match the parent.

  • API & Persistence Behavior:

    • No New CRUD Endpoints: There is no new standalone endpoint for Product Characteristics. They continue to be managed via the Product endpoints (POST/PUT) within the Product.characteristics field.

    • Persistence Semantics: The characteristics collection on the Product API now follows "Create or Update" semantics.

      • No Implicit Delete: Omitting a characteristic from the collection in a request does not delete it.

      • Matching Logic: Incoming characteristics are matched to existing ones first by ID, and then by Field Name. If a match is found, it is updated; otherwise, a new characteristic is created.

      • No Unnecessary Persistence: If a characteristic value provided in the API payload exactly matches the default value inherited from the parent BusinessType, there will be no ProductCharacteristic persisted for that element. This avoids unnecessary persistence, and also allows for the value to be dynamically updated at the BusinessType level and automatically flow-through to all downstream products.

    • Characteristics Now Provided During Product Creation

      • Previously, the Broadleaf Admin frontend did not allow API callers to specify ProductCharacteristic values in the create form of Product itself. This led to the possibility of products being persisted in a broken/incomplete state, since business types may intend to require their characteristics be further refined/configured at the product level.

      • With the latest iteration, the Broadleaf Admin now includes the characteristics fields for the specified business type in the product create form. Furthermore, the backend ProductValidator now more strictly validates that the expectations of the parent business type are met before allowing the product to be persisted. The net result is that products can no longer be persisted in a 'broken' state, either via the admin or via standard API calls.

        • To ensure the Broadleaf Admin frontend knows which characteristics/options form fields to render, it calls upon a new GET /products/creationTemplate endpoint, which accepts the businessType as a request parameter and receives a response containing the relevant characteristics/options for that type. This is used in a new ENTITY_HYDRATOR field to populate the form state.

  • Backend Services & Components:

    • New Components for ProductCharacteristic domain management: Introduced ProductCharacteristicService and ProductCharacteristicRepository to manage the persistence and retrieval of the externalized characteristics.

    • Product CRUD Operation Updates

      • A variety of key changes have been made in DefaultProductService to continue to facilitate the management of ProductCharacteristics as an "embedded" collection on Product, despite the fact that these instances are now external.

      • DefaultProductService has been updated to orchestrate the persistence flow. It delegates to ProductCharacteristicService to handle the complexity of characteristic management during Product create and update operations. There are also specialized changes to ensure that during product delete/clear-override operations, the relevant characteristics are deleted or have their overrides cleared.

        Note

        The semantics are as such: if the input payload to ProductService contained requested changes for characteristics, the Product in the immediate method response will contain the latest state of all the directly assigned characteristics for that product after updates are applied. This guarantees the caller (even without hydration involved) can at least see their request was honored. If the input payload to ProductService does not contain any changes for characteristics, the Product in the immediate response will also not contain any characteristic data.

      • There is a new ProductCrudEntityHelper bean, which extends the default CrudEntityHelper specifically in the context of DefaultProductService. The primary motivation for this is to ensure validations against ProductCharacteristics are executed as expected. Despite being separated into an external entity, ProductCharacteristics continue to be validated as part of ProductValidator. ProductCrudEntityHelper has customizations that ensure the updated characteristics values are always passed through to the validator. There is also logic to ensure validation is not short-circuited when there are ProductCharacteristic changes, but the Product itself is detected as not having any dirty changes.

      • ProductValidator has been updated to add a few more validations around ProductCharacteristic

        • Added validation to guarantee a ProductCharacteristic cannot be created without a source Characteristic ID, and that the source Characteristic ID must actually exist in the system

        • Added validation to guarantee the ProductCharacteristic field name must match with the source Characteristic field name

        • Added validation to guarantee the Product specifies values for all characteristics required to be configured by the parent business type

    • DefaultCloneProductService has been updated to explicitly clone ProductCharacteristic instances. This is not something the API caller can opt out of, as product characteristics are integral to the identity and composition of a product, and thus cloning without them would be invalid.

      Note

      This involves a mild breaking change to the return type of the DefaultCloneProductService.generateClone method. It now returns a DTO containing both the persisted domain representation of the generated clone, and the hydrated/modified business instance representation. Moving to the new signature should be a very straightforward/easy change.

  • Domain & Hydration:

    • From an external API perspective, the Product projection domain remains essentially unchanged and continues to hold a collection for hydrated characteristics.

    • Because characteristics are now no longer available directly on JpaProduct (and subsequently Product at fetch time), a separate hydration step is now required when retrieving products due to the separation of characteristics.

      Note

      Important for Custom Code: Most typical Product API endpoints already invoke ProductHydrationService.hydrate on the Product before returning the results, so characteristics will be available in those results. However, if custom client code retrieves products just via ProductService and is relying on characteristics being present, an additional call to ProductHydrationService#hydrateCharacteristics will be required to populate this collection.

      • The ProductHydrationService has been updated to execute this characteristic hydration. The standard hydrate method now hydrates characteristics and their translations as part of its logic, and there is also a dedicated new hydrateCharacteristics method that is available for scenarios where only characteristic hydration (and not hydration of any other entities) is needed.

        • As part of this flow, the service computes the value of a new characteristicsOverriddenHint field on Product. The Javadocs of the field describe its purpose further, but in essence this is a field leveraged by the Broadleaf Admin frontend as the isCatalogOverrideFallbackHintField to drive whether the 'Undo Overrides' action is shown as an option on the product edit page.

        • There is a mild breaking change to rename the protected hydrateConfigurableProductCharacteristics method to syncOptionForConfigurableProductCharacteristic. This is only a naming change to better align with the actual implementation of the method.

      • The mechanism to apply translations for ProductCharacteristic has changed significantly on the backend.

        • The ProductBaseCharacteristicTranslationPostMapperMember has been deleted, as characteristics will no longer be available during the mapping process of Product and therefore the mapper member cannot function correctly. Its functionality has been migrated into a new CharacteristicsTranslationService.

        • This new CharacteristicsTranslationService component is invoked by ProductHydrationService during characteristic hydration to find and apply translation values as part of its flow. ResolvedProductReferences has been updated to hold the fetched translations. The semantics of how translations are found/applied remains the same as before (ProductCharacteristic translations directly defined under Product.characteristics remain the highest priority, and if no translations are found, translations from the base Characteristic are applied).

        • Introduced a new ProductCharacteristicsFieldTranslationHandler implementation to skip the standard field path transformation logic on Product characteristics that typically happens during translation updates. This transformation is fully irrelevant for characteristic translations, as we use the name to translate rather than the ID or index.

        • Introduced a new ProductCharacteristicSkippingTranslationApplier implementation to skip characteristic translations from happening in TranslationPostMapperMember, as characteristics are no longer available at the time a product is read and mapped. As noted earlier, this logic is now in CharacteristicsTranslationService

  • Import/Export:

    • Import and Export components have been updated to support the new architecture.

    • Syntax: The CSV syntax for defining characteristics generally remains consistent, but the underlying processing now routes through the new service components.

  • User Impact:

    • Functionality remains unchanged from a user perspective; this is primarily a backend architectural improvement.

Product Characteristic Reindexing
  • Updated reindexing logic to support the new product characteristic structure via a new ProductCharacteristicsConsolidationContributor

  • Refactored the abstract ContextualProductConsolidationContributor to make its methods more granular, visible, and extensible

  • Introduced a dedicated field for characteristics on ConsolidatedProduct to ensure correct indexing.

  • Optimized hydration and consolidation processes to correctly handle characteristic inheritance and narrowing during reindexing.

  • Ensured that getCharacteristicsValues is correctly populated in the final indexed document.

  • Introduced a new mechanism for BusinessType + ancestor + characteristic hydration that works in ignore-narrowing contexts like reindexing. This is implemented in a few new methods: BusinessTypeService.readAllByTypeKeyInWithAncestorsForConsolidation, BusinessTypeAncestryHydrationService.fetchRawBusinessTypesAndAncestors, and processed further in ConsolidationBusinessTypeHydrationUtil.

  • Characteristics are the largest field in ConsolidatedProduct, and we were serializing them twice due to the two separate characteristics and characteristicValues fields. The characteristicValues fields are now removed.

    Warning
    Breaking change: any FieldDefinition in SearchServices that is currently referencing characteristicValues will no longer work unless the path is updated to point to the characteristics field. Alternatively, clients can introduce an extension of ConsolidatedProduct with a custom getter for characteristicValues to reintroduce that field in the JSON payload if absolutely necessary - but this approach is not recommended as it reintroduces the larger payload size.
  • Added a dedicated new method in ProductConsolidationService to eliminate/trim ConsolidatedProduct fields, with new logic for cleaning out bulky ProductCharacteristic and Characteristic values. Endpoints returning ConsolidatedProduct have been updated to call this method.

Feature: Default Frequency for Products with Recurring Pricing
  • Added domain support to specify default frequency to use for initial product pricing (e.g. PDP and PLP views) for products with recurring pricing

    • For existing products with recurring pricing, it’s recommended to populate the default frequency, so that the PDP and PLP can show the initial pricing as expected (see Data Migration for more details).

Feature: Two-Tier Pricing
  • The out-of-box metadata for the Price Data form shown on the Product page has been updated to support the Two-Tier Pricing feature introduced in Release Train 2.3.0.

Lite Variants API
  • Introduced new LiteVariant DTO to represent the simplified variant, containing the following fields:

    • contextId - the ID of the variant product

    • productId - the ID of the base product

    • sku - the SKU of the variant

    • optionValues - a map of option attribute names to their corresponding option value.

  • Added new readAllLiteVariantsByProductId and readAllLiteVariantsByIdOrProductId methods to the VariantService and provided a default implementation for each.

    • These implementations cache the result of the method calls using catalogCacheByLiteVariant

  • Added new findAllLiteVariantsByProductContextId method to the CustomizedVariantRepository with a Jpa implementation to retrieve and build the list of LiteVariant instances for a given product ID.

  • Introduced a new GET /lite-variants/{productId} endpoint to retrieve a lightweight list of variants that are related to a given product, returning only essential information for performance optimization in scenarios where the full variant details are not necessary.

  • Introduced support for hydration behavior where only Lite Variant information is hydrated onto a product. This is useful for commerce flows where the full variant details are not necessary and can thus be omitted for performance optimization.

    • Added LITE_ONLY enum value to the DefaultProductVariantHydrationBehavior enum to specify that only lite variant information should be hydrated for the product.

    • Added liteVariants field to the ProductDetails DTO.

    • Added relatedLiteVariants field to the ProductDetailsContext DTO.

    • Added relatedLiteVariants field to the CategoryDetailsContext DTO.

    • Added new components to contribute lite variant information onto their respective DTOs:

      • RelatedLiteVariantsProductContextContributorProductDetailsContext#resolvedProduct#relatedLiteVariants

      • RelatedLiteVariantsCategoryContextContributor#relatedLiteVariantsCategoryDetailsContext#relatedProducts

      • LiteVariantsProductDetailsContributorProductDetails#liteVariants

Feature: Variant Pricing Strategy

Variant Pricing Strategy was introduced to the Product domain to assist users who price Variants based on the Product’s SKU rather than the Variants' own. By default, Broadleaf sends PriceableTargets to the Pricing Service for every Variant as well as the parent Product. In this case, the Product’s pricing (whether by SKU or pricing key) acts as the fallback if a Variant doesn’t have a price specified.

However, there are cases where users never specify variant-specific pricing, using other mechanisms to vary pricing such as the new Two-Tier pricing targeting Characteristic values. Consequently, performance can be improved by skipping sending the PriceableTargets for the Variants and instead only using the Product’s price. This is additionally useful in such cases when there are also a very large number of variants.

Therefore, a new field has been added to Product, variantPricingStrategy, to determine whether to price by the product only.

The supported values out-of-box:
  • PRODUCT: All the variants will always use product-level pricing, e.g., catalog’s product-level pricing, price data targeting pricing key, etc., useful to if variant-level pricing isn’t needed.

  • VARIANT: Variant-level pricing will take precedence, e.g., price data targeting variant SKU or pricing key. If variant-level pricing isn’t available, product-level pricing will still be used.

    • This is the previous behavior.

  • DEFAULT: Delegates to the system setting to determine whether Product or Variant pricing strategy should be used.

    • Set the system behavior with broadleaf.common.default-configurations.product.variant-pricing-strategy.

Other Enhancements
  • Characteristics admin form improvements

    • When Characteristic is of Date type, the translatable option is not shown

    • When Characteristic is of Variant Options type, the cardinality option is not shown

    • Tooltip was added for Min Cardinality field

  • Required validation is now enforced for product characteristics with minimum cardinality greater than 0 on a storefront.

  • BusinessTypeEndpoint readAll operations now support pagination

    • Introduced new GET /all-paginated operation. Full url /product-types/all-paginated. Will return a paginated list of BusinessType instances, including support for out-of-box default types and template types.

    • Introduced new GET /component-mappings-paginated operation. Full url /product-types/component-mappings-paginated. Will allow callers to request a page of component mappings returned as a map structure keyed by business type key

    • Deprecate GET /all, use /all-paginated instead

    • Deprecate GET /component-mappings, use /component-mappings-paginated instead

    • The admin metadata for Business Types has been updated to leverage the new paginated endpoint.

      • If you have customized or overridden this metadata, you may want to consider updating the logic to match the following:

        .addPrimaryAction(Actions.action()
                .label("product.browse.actions.create")
                .type("CREATE_CHOICE")
                .addEndpoint(Endpoints.pageableGet()
                        .narrowedPaging()
                        .type("EXTERNAL_CHOICES")
                        .uri("/catalog/product-types/all-paginated")
                        .param("q", "${filter.q}")
                        .param("cq", "${filter.cq}")
                        .param("includeDefaults", "true")
                        .param("includeTemplates", "false")
                        .scope(BusinessTypeScopes.BUSINESS_TYPE))
                .scope(BusinessTypeScopes.BUSINESS_TYPE)
                .attribute("createPathTemplate", "/products/${typeKey}/create")
                .scope(ProductScopes.PRODUCT))
  • Product options derived from characteristics are now filtered to only include active characteristic values before being sent to the frontend.

  • Added color and showInAdmin properties to AdvancedTag domain model.

    • color allows specifying a color for the tag, which is used in admin UI displays.

    • showInAdmin determines if the tag should be visible in admin interfaces.

  • Form field and group ordering improvements in admin characteristic forms.

  • Implemented Business Type Characteristics Inheritance

    • If a Business Type’s productType key is not any of the DefaultProductType enums (meaning it is not one of the default product types that are included out-of-box), then it is pointing to another Business Type whose typeKey matches the productType key. This indicates that this Business Type can inherit certain attributes from its parent Business Type (also known as its ancestor), such as Characteristics.

    • When updating a Product whose Business Type has a parent Business Type, the Product is hydrated with all inherited characteristics from its ancestor Business Types. See BusinessTypeAncestryHydrationService to see how this hydration is performed.

    • The Business Type Characteristics grid displays each Characteristic’s Inheritance Type which determines how the Characteristic was assigned to the Business Type:

      • OVERRIDDEN - Characteristics that were initially INHERITED but were edited by a user. These Characteristics are persisted as part of the Business Type’s characteristics field.

      • CUSTOM - Characteristics that are independently created by users. These Characteristics are persisted as part of the Business Type’s characteristics field.

      • INHERITED - Characteristics that are inherited from ancestor Business Types. These Characteristics are not persisted as part of the Business Type, but instead are hydrated based on the Business Type ancestors field.

  • Since DefaultProductService now manages both Product and ProductCharacteristic persistence within its CRUD methods, changes had to be made to ensure SingleIndexRequest is only emitted for the Product after all the persistence for ProductCharacteristic elements is complete. This ensures the product will never be reindexed in an incomplete state - it will only be reindexed once both the Product and all of its ProductCharacteristic instances have finished persisting. Several new components are involved in achieving this result:

    • SingleIndexRequestNotificationStateBuildContext is leveraged to ensure the notification state for SingleIndexRequest is still built, even when SuppressNotificationContext is used to suppress the eager initial message send attempt.

    • ProductPersistedDomainCollectingMapperMember and CollectedProductPersistedDomainContext are used to allow DefaultProductService to have access to the persisted-domain instances, which it passes to NotificationManager to invoke the eager SingleIndexMessage send after persistence finishes.

  • Replaced ModelMapper-based cloning logic with a new Kryo-based ObjectDeepCopyUtility for significantly improved cloning performance during product hydration and reindexing

    • Introduced a new Kryo-based ObjectDeepCopyUtility bean which can 'deep clone' any input object

    • Updated CategoryProductConsolidationContributor, ContextualProductConsolidationContributor, and DataDrivenEnumConsolidationContributor to use this new utility for cloning, replacing the previous ModelMapper 'clone mapper' approach

  • Some flows (notably the ContextualProductConsolidationContributor components) were constructing RSQL Node instances by first building a filter string and then parsing it. Many of these flows now manually construct the Node filters directly, improving performance.

  • Updated the out-of-box metadata for the Price Data Form in the Product View to allow explicitly defining a pricing key for Standard, Variant-Based, and Bundle products.

    • Updated the pricingKey field conditionals in the price data form to be visible for these product types.

    • Renamed the "SKU" column to "SKU or Pricing Key" and updated the Price Data grid to also render price data that is targeting the product via a pricing key.

    • Added new /products/{productId}/target-ids endpoint to retrieve all target IDs (SKUs and Pricing Keys) associated with a product. This is used to render them as filter options in the Price Data grid.

  • Updated the "Allowed Values" section in the Characteristic and Product Option forms to only be visible when the value type is set to Variant Option, Enumeration, or Boolean. This ensures a cleaner user interface by hiding irrelevant configuration fields for simple data types (e.g., String, Integer, Date).

    • When a Boolean Characteristic that is used in a Product Type is set to be configurable, the allowed values section that is displayed in the Product Options for a Product only displays "True" and "False" as options, since those are the only two valid options for a Boolean Characteristic. The labels for these options are updatable and can also have translations provided for them, but the actual values are fixed to be "True" and "False" in order to prevent any invalid values from being entered for Boolean Characteristics.

  • Removed product characteristics from the general "Characteristics" section of the Product form that are marked as configurable, since those will appear in the "Add-Ons and Options" section.

  • Updated Product Choice product selection metadata filters to target the Product.productType field instead of Product.businessType field when fetching Products, and updated the relevant Product endpoint to support filtering by Product Type as well.

  • Enhanced the AddOn product pricing management for the SpecificItemChoice in the admin view

Enhancement: Added support to show/hide recurring pricing fields by properties

Instead of always showing the recurring pricing metadata fields (e.g. terms and frequency), the metadata is now controlled by a different property:

broadleaf:
  catalog:
    metadata:
      display-recurring-pricing-fields: true
Note
Enabling subscription via broadleaf.subscription.enabled implicitly enables the recurring pricing fields too

Bug Fixes

  • Fix a bug where products would go missing from search results if a product inherited from a tenant context had a related entity (ex: product tag) defined in an application context

    • DefaultConsolidatedProductPostProcessor was failing to calculate the right creatingApplicationId for synthetically generated ConsolidatedProduct instances during the indexing flow. The new implementation leverages the new ContextState.creatingApplicationId field made available in DataTracking 2.0.7 to accurately populate this value. All the consolidation contributors have also been updated to populate the new creatingApplicationId and overrideCatalogLevel fields on ContextState to ensure consistency.

  • Fixed an issue where immutable products from a child catalog are not disabled when reordering category products.

  • Added validation to ensure that active OptionTemplate entities that are directly referenced cannot be deleted.

  • Cart Item Attribute Product Option fixes

    • Fixed an issue in the admin form where the required toggle could get stuck in the "not required" position, even after setting the attribute as required.

    • Fixed an issue where integer values could be incorrectly flagged as invalid

    • Fixed an issue where the add to cart button could be disabled even after all required cart attributes are satisfied

  • Fixed untranslatable characteristics like booleans being included in translation requests

    • Instead of using a conditional on the translatable field, we use a dynamic field where the translatable flag is hidden and defaulted to false for untranslatable Characteristic types like Boolean.

  • Fixed that a characteristic created from the business type form will be placed into the basic information field group instead of the characteristic group

    • For that purpose the new metadata attribute defaultSelectedComponentId for TargetKeyLookup component was introduced

  • Fixed the issue when after product import of a business type with enum characteristics, the product further editing returns an error

  • Improved hydration logic so configurable characteristics aren’t unnecessarily recreated, preventing false overrides and propagation issues. “By Reference” option templates also benefit from this fix.

  • Updated characteristics metadata so that the characteristic’s allowed values are read-only when derived from enumeration type characteristics.

  • Fixed NPE when trying to generate SKUs from variant option characteristics when no characteristic actually exists.

  • Fixed issue related to modifying a collection in a stream causing duplicated product options to be returned during the product hydration process.

  • Fix a bug in ContextualProductConsolidationContributor where ContextState was not being properly deep copied during product override generation, and therefore any subsequent mutation to that instance would affect all cloned instances.

  • Fixed an issue where minimumQuantity of the Selector product option was updated even when unchanged.

  • Fixed an issue where overlapping message.properties files would cause incorrect message/label resolution for services that have their own message.properties file in addition to the main one in Catalog.

  • Fixed an issue where DefaultCloneProductService would clone with translated data for the caller’s locale instead of the raw values. The flow has been updated to leverage ContextInfo.isIgnoreTranslation to ensure only raw values are cloned. This primarily affected entities related to Product (such as Variant) rather than Products themselves, but the issue is now fixed for all entities in the flow.

  • Fixed a bug in the Category Product grid where attempts to sort products inherited from a parent catalog would fail. The grid metadata was updated to include a selectableWhenMutableOnly attribute that makes the sort action inaccessible to immutable products.

  • Fixed a bug where JpaCatalogQueryHelper#countNonProductionRecordsByProductIdsInTenant did not exclude sandbox-archived records in its query criteria, potentially resulting in false-positive validation errors during entity deletion

  • Fixed a bug where the CatalogCurrencyContextInfoCustomizer could throw an IllegalStateException when not in a request context. A check was added to prevent interacting with the CurrencyHolder outside of a request context.

Data Migration

Populating Default Frequency for Products with Recurring Pricing

For existing products with recurring pricing, it’s recommended to populate the default frequency, so that the PDP and PLP can show the initial pricing as expected.

Most of the time, you can just populate MONTHLY frequency for the product with recurring pricing with the following SQL:

UPDATE blc_product SET default_recurring_frequency = '{"recurringPeriodFrequency":1,"recurringPeriodType":"MONTHLY"}' WHERE context_id IN ('some-ids', 'some-ids-2');

Alternatively, you can use terms to determine what records to update instead of context_ids:

UPDATE blc_product SET default_recurring_frequency = '{"recurringPeriodFrequency":1,"recurringPeriodType":"MONTHLY"}' WHERE terms IS NOT NULL AND terms != '[]';

Note: For this option, you will still need to update the default recurring frequency using the ids for recurring products with no terms.


Catalog Browse Release Notes for 2.2.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

Add Ehcache and Caffeine cache support
  • Added support for Ehcache and Caffeine caches for dynamic customer segments.

  • For more information on configuring these caches, see the Caching Configuration guide.

  • Accept a new skipInventory parameter in /browse/products (aka /products/details) to skip inventory availability checks. Internally, the backend flow already supported honoring ProductDetailsRequest.skipInventory, but this setting was not exposed via the API.

  • Updated the pricing logic to use product’s default frequency when doing initial product pricing (e.g. PDP and PLP views) for products with recurring pricing

Feature: Added mechanism to filter out sensitive data from browse product payloads

See the CartOperation-counterpart of this mechanism in Cart Operation Services 2.3.0 Release Notes.

Introduced an OncePerRequest filter guarded by configured URIs patterns that will remove the baseCost property from the CatalogBrowse response payloads.

This mechanism can be configured by the following properties and is enabled by default (see CatalogBrowseSensitiveDataRemovalProperties):

broadleaf:
  catalogbrowse:
    sensitive-data-removal:
      enabled: true
      url-patterns:
        - /browse/*
        - /products/details
        - ... etc.
      fields-to-remove:
        - cost          # default
        - baseCost      # default
Feature: Variant Pricing Strategy
Important
Requires Catalog Services 2.3.0
Feature: Variant Pricing Strategy

Variant Pricing Strategy was introduced to the Product domain to assist users who price Variants based on the Product’s SKU rather than the Variants' own. By default, Broadleaf sends PriceableTargets to the Pricing Service for every Variant as well as the parent Product. In this case, the Product’s pricing (whether by SKU or pricing key) acts as the fallback if a Variant doesn’t have a price specified.

However, there are cases where users never specify variant-specific pricing, using other mechanisms to vary pricing such as the new Two-Tier pricing targeting Characteristic values. Consequently, performance can be improved by skipping sending the PriceableTargets for the Variants and instead only using the Product’s price. This is additionally useful in such cases when there are also a very large number of variants.

Therefore, a new field has been added to Product, variantPricingStrategy, to determine whether to price by the product only.

The supported values out-of-box:
  • PRODUCT: All the variants will always use product-level pricing, e.g., catalog’s product-level pricing, price data targeting pricing key, etc., useful to if variant-level pricing isn’t needed.

  • VARIANT: Variant-level pricing will take precedence, e.g., price data targeting variant SKU or pricing key. If variant-level pricing isn’t available, product-level pricing will still be used.

    • This is the previous behavior.

  • DEFAULT: Delegates to the system setting to determine whether Product or Variant pricing strategy should be used.

    • Set the system behavior with broadleaf.common.default-configurations.product.variant-pricing-strategy.

Other Enhancements
  • Characteristics admin form improvements

    • When Characteristic is of Date type, the translatable option is not shown

    • When Characteristic is of Variant Options type, the cardinality option is not shown

    • Tooltip was added for Min Cardinality field

  • Required validation is now enforced for product characteristics with minimum cardinality greater than 0 on a storefront.

  • BusinessTypeEndpoint readAll operations now support pagination

    • Introduced new GET /all-paginated operation. Full url /product-types/all-paginated. Will return a paginated list of BusinessType instances, including support for out-of-box default types and template types.

    • Introduced new GET /component-mappings-paginated operation. Full url /product-types/component-mappings-paginated. Will allow callers to request a page of component mappings returned as a map structure keyed by business type key

    • Deprecate GET /all, use /all-paginated instead

    • Deprecate GET /component-mappings, use /component-mappings-paginated instead

    • The admin metadata for Business Types has been updated to leverage the new paginated endpoint.

      • If you have customized or overridden this metadata, you may want to consider updating the logic to match the following:

        .addPrimaryAction(Actions.action()
                .label("product.browse.actions.create")
                .type("CREATE_CHOICE")
                .addEndpoint(Endpoints.pageableGet()
                        .narrowedPaging()
                        .type("EXTERNAL_CHOICES")
                        .uri("/catalog/product-types/all-paginated")
                        .param("q", "${filter.q}")
                        .param("cq", "${filter.cq}")
                        .param("includeDefaults", "true")
                        .param("includeTemplates", "false")
                        .scope(BusinessTypeScopes.BUSINESS_TYPE))
                .scope(BusinessTypeScopes.BUSINESS_TYPE)
                .attribute("createPathTemplate", "/products/${typeKey}/create")
                .scope(ProductScopes.PRODUCT))
  • Product options derived from characteristics are now filtered to only include active characteristic values before being sent to the frontend.

  • Added color and showInAdmin properties to AdvancedTag domain model.

    • color allows specifying a color for the tag, which is used in admin UI displays.

    • showInAdmin determines if the tag should be visible in admin interfaces.

  • Form field and group ordering improvements in admin characteristic forms.

  • Implemented Business Type Characteristics Inheritance

    • If a Business Type’s productType key is not any of the DefaultProductType enums (meaning it is not one of the default product types that are included out-of-box), then it is pointing to another Business Type whose typeKey matches the productType key. This indicates that this Business Type can inherit certain attributes from its parent Business Type (also known as its ancestor), such as Characteristics.

    • When updating a Product whose Business Type has a parent Business Type, the Product is hydrated with all inherited characteristics from its ancestor Business Types. See BusinessTypeAncestryHydrationService to see how this hydration is performed.

    • The Business Type Characteristics grid displays each Characteristic’s Inheritance Type which determines how the Characteristic was assigned to the Business Type:

      • OVERRIDDEN - Characteristics that were initially INHERITED but were edited by a user. These Characteristics are persisted as part of the Business Type’s characteristics field.

      • CUSTOM - Characteristics that are independently created by users. These Characteristics are persisted as part of the Business Type’s characteristics field.

      • INHERITED - Characteristics that are inherited from ancestor Business Types. These Characteristics are not persisted as part of the Business Type, but instead are hydrated based on the Business Type ancestors field.

  • Since DefaultProductService now manages both Product and ProductCharacteristic persistence within its CRUD methods, changes had to be made to ensure SingleIndexRequest is only emitted for the Product after all the persistence for ProductCharacteristic elements is complete. This ensures the product will never be reindexed in an incomplete state - it will only be reindexed once both the Product and all of its ProductCharacteristic instances have finished persisting. Several new components are involved in achieving this result:

    • SingleIndexRequestNotificationStateBuildContext is leveraged to ensure the notification state for SingleIndexRequest is still built, even when SuppressNotificationContext is used to suppress the eager initial message send attempt.

    • ProductPersistedDomainCollectingMapperMember and CollectedProductPersistedDomainContext are used to allow DefaultProductService to have access to the persisted-domain instances, which it passes to NotificationManager to invoke the eager SingleIndexMessage send after persistence finishes.

  • Replaced ModelMapper-based cloning logic with a new Kryo-based ObjectDeepCopyUtility for significantly improved cloning performance during product hydration and reindexing

    • Introduced a new Kryo-based ObjectDeepCopyUtility bean which can 'deep clone' any input object

    • Updated CategoryProductConsolidationContributor, ContextualProductConsolidationContributor, and DataDrivenEnumConsolidationContributor to use this new utility for cloning, replacing the previous ModelMapper 'clone mapper' approach

  • Some flows (notably the ContextualProductConsolidationContributor components) were constructing RSQL Node instances by first building a filter string and then parsing it. Many of these flows now manually construct the Node filters directly, improving performance.

  • Updated the out-of-box metadata for the Price Data Form in the Product View to allow explicitly defining a pricing key for Standard, Variant-Based, and Bundle products.

    • Updated the pricingKey field conditionals in the price data form to be visible for these product types.

    • Renamed the "SKU" column to "SKU or Pricing Key" and updated the Price Data grid to also render price data that is targeting the product via a pricing key.

    • Added new /products/{productId}/target-ids endpoint to retrieve all target IDs (SKUs and Pricing Keys) associated with a product. This is used to render them as filter options in the Price Data grid.

  • Updated the "Allowed Values" section in the Characteristic and Product Option forms to only be visible when the value type is set to Variant Option, Enumeration, or Boolean. This ensures a cleaner user interface by hiding irrelevant configuration fields for simple data types (e.g., String, Integer, Date).

    • When a Boolean Characteristic that is used in a Product Type is set to be configurable, the allowed values section that is displayed in the Product Options for a Product only displays "True" and "False" as options, since those are the only two valid options for a Boolean Characteristic. The labels for these options are updatable and can also have translations provided for them, but the actual values are fixed to be "True" and "False" in order to prevent any invalid values from being entered for Boolean Characteristics.

  • Removed product characteristics from the general "Characteristics" section of the Product form that are marked as configurable, since those will appear in the "Add-Ons and Options" section.

  • Updated Product Choice product selection metadata filters to target the Product.productType field instead of Product.businessType field when fetching Products, and updated the relevant Product endpoint to support filtering by Product Type as well.

  • Enhanced the AddOn product pricing management for the SpecificItemChoice in the admin view

Enhancement: Added support to show/hide recurring pricing fields by properties

Instead of always showing the recurring pricing metadata fields (e.g. terms and frequency), the metadata is now controlled by a different property:

broadleaf:
  catalog:
    metadata:
      display-recurring-pricing-fields: true
Note
Enabling subscription via broadleaf.subscription.enabled implicitly enables the recurring pricing fields too

Bug Fixes

  • Fix a bug where products would go missing from search results if a product inherited from a tenant context had a related entity (ex: product tag) defined in an application context

    • DefaultConsolidatedProductPostProcessor was failing to calculate the right creatingApplicationId for synthetically generated ConsolidatedProduct instances during the indexing flow. The new implementation leverages the new ContextState.creatingApplicationId field made available in DataTracking 2.0.7 to accurately populate this value. All the consolidation contributors have also been updated to populate the new creatingApplicationId and overrideCatalogLevel fields on ContextState to ensure consistency.

  • Fixed an issue where immutable products from a child catalog are not disabled when reordering category products.

  • Added validation to ensure that active OptionTemplate entities that are directly referenced cannot be deleted.

  • Cart Item Attribute Product Option fixes

    • Fixed an issue in the admin form where the required toggle could get stuck in the "not required" position, even after setting the attribute as required.

    • Fixed an issue where integer values could be incorrectly flagged as invalid

    • Fixed an issue where the add to cart button could be disabled even after all required cart attributes are satisfied

  • Fixed untranslatable characteristics like booleans being included in translation requests

    • Instead of using a conditional on the translatable field, we use a dynamic field where the translatable flag is hidden and defaulted to false for untranslatable Characteristic types like Boolean.

  • Fixed that a characteristic created from the business type form will be placed into the basic information field group instead of the characteristic group

    • For that purpose the new metadata attribute defaultSelectedComponentId for TargetKeyLookup component was introduced

  • Fixed the issue when after product import of a business type with enum characteristics, the product further editing returns an error

  • Improved hydration logic so configurable characteristics aren’t unnecessarily recreated, preventing false overrides and propagation issues. “By Reference” option templates also benefit from this fix.

  • Updated characteristics metadata so that the characteristic’s allowed values are read-only when derived from enumeration type characteristics.

  • Fixed NPE when trying to generate SKUs from variant option characteristics when no characteristic actually exists.

  • Fixed issue related to modifying a collection in a stream causing duplicated product options to be returned during the product hydration process.

  • Fix a bug in ContextualProductConsolidationContributor where ContextState was not being properly deep copied during product override generation, and therefore any subsequent mutation to that instance would affect all cloned instances.

  • Fixed an issue where minimumQuantity of the Selector product option was updated even when unchanged.

  • Fixed an issue where overlapping message.properties files would cause incorrect message/label resolution for services that have their own message.properties file in addition to the main one in Catalog.

  • Fixed an issue where DefaultCloneProductService would clone with translated data for the caller’s locale instead of the raw values. The flow has been updated to leverage ContextInfo.isIgnoreTranslation to ensure only raw values are cloned. This primarily affected entities related to Product (such as Variant) rather than Products themselves, but the issue is now fixed for all entities in the flow.

  • Fixed a bug in the Category Product grid where attempts to sort products inherited from a parent catalog would fail. The grid metadata was updated to include a selectableWhenMutableOnly attribute that makes the sort action inaccessible to immutable products.

  • Fixed a bug where JpaCatalogQueryHelper#countNonProductionRecordsByProductIdsInTenant did not exclude sandbox-archived records in its query criteria, potentially resulting in false-positive validation errors during entity deletion

  • Fixed a bug where the CatalogCurrencyContextInfoCustomizer could throw an IllegalStateException when not in a request context. A check was added to prevent interacting with the CurrencyHolder outside of a request context.

Data Migration

Populating Default Frequency for Products with Recurring Pricing

For existing products with recurring pricing, it’s recommended to populate the default frequency, so that the PDP and PLP can show the initial pricing as expected.

Most of the time, you can just populate MONTHLY frequency for the product with recurring pricing with the following SQL:

UPDATE blc_product SET default_recurring_frequency = '{"recurringPeriodFrequency":1,"recurringPeriodType":"MONTHLY"}' WHERE context_id IN ('some-ids', 'some-ids-2');

Alternatively, you can use terms to determine what records to update instead of context_ids:

UPDATE blc_product SET default_recurring_frequency = '{"recurringPeriodFrequency":1,"recurringPeriodType":"MONTHLY"}' WHERE terms IS NOT NULL AND terms != '[]';

Note: For this option, you will still need to update the default recurring frequency using the ids for recurring products with no terms.

Improved support for AddOn pricing and the UX for its configuration
Important
Requires Catalog Services 2.3.0
  • Updated the pricing comparator to prioritize as follows:

    • VariantPricingOverride if it exists

    • PriceData targeting the add-on’s pricing key

    • Direct price set on the add-on

    • Underlying entity match (follows existing logic for priority (e.g. price data, price lists, direct pricing)

  • Removed logic that always builds priceableTarget for defaultProduct and defaultVariant when building for options

    • This target is redundant because we are already building the targets for all the specific choices, which the default product or variant is already in there too

  • Added logic to build the PriceableTargets for underlying product’s variants for specific item choices

  • Updated the ProductOptionsProductPriceableTargetsBuilder to return empty set of targets early if override price exist for the ItemChoice or SpecificItemChoice

    • No need to build and price the underlying products/variants if the ItemChoice or SpecificItemChoice already has an explicit price override

  • Added support for variant based products to be a product’s add-on

Inventory Availability Index

Introduced a new GET /inventory-availability-index/{productId} endpoint that returns basic variant information along with inventory availability information for a given product. This endpoint is intended to be used in commerce flows to retrieve inventory availability information for all variants of a product in a single request and replace hydrating full VariantDetails onto the ProductDetails. The index uses the variant option values as the keys to allow fast lookup in the frontend to be used alongside option selector components to hide/grey-out unavailable options and the add-to-cart button.

Example Payload
type InventoryAvailabilityIndex = {
  variants: {
    "gold:XL": {
      id: string;
      sku: string;
      optionValues: Record<string, string>;
      available: boolean;
    }
  }
}
Primary Use Case

Using this endpoint rather than hydrating the full variant details is particularly useful for products with large numbers (e.g., hundreds) of variants to improve performance and time to first paint. To maximize its effect, set default variant hydration behavior for product details in Catalog Service to NONE and have the frontend make a separate call to fetch the inventory availability index in asynchronously such as in a useEffect or server function. This allows the main content to load and be painted more quickly while the availability index is retrieved before the user can actually interact with the page.

Disable Variant Hydration for Product Details
broadleaf:
  catalog:
    product:
      details:
        variant-hydration-behavior: NONE
Other Implementation Details
  • Added new InventoryAvailabilityIndexService to orchestrate calls to the Catalog and Inventory providers to fetch lite variant and inventory availability information for all variants of a product.

  • Added a new LiteVariant DTO, which is a lightweight representation of a Variant.

  • Added new fetchLiteVariantsForProduct method to the ExternalCatalogProvider

  • Added new getInventoryAvailabilityForSkus method to the ExternalInventoryProvider

    • The default implementation builds a collection of SkuInventoryAvailabilityRequests and calls the bulk availability API in Inventory Services.

  • The map key for AvailabilityInfo#variants takes the variant option values and joins each value with a semicolon in alphabetical order by key. For example, given the following option values

    {
      "size": "XL",
      "color": "gold"
    }

    The key would be gold:XL. ** To modify this behavior, extend DefaultInventoryAvailabilityIndexService#buildOptionValuesKey

Bug Fixes

  • ExternalInventoryProvider.getInventorySummaries was previously making a request to inventory services even when there were no items to provide in the request payload (for example, all requested products had inventory check/reserve strategy settings set to NEVER). There is now a check in this flow to examine whether the requested item list is empty before proceeding to make a request to inventory services, reducing unnecessary system load.

  • Fixed an issue where the "Pay In Full" button appears twice for Products configured with recurring pricing and a default frequency that is not "One-Time" on the Product Details Page.

  • Fixed an issue where incorrect and redundant Purchasing Options are being displayed for Variant Products whose Base Price is different from the variant’s price.


Content Release Notes for 2.0.9-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

  • Added support for Caffeine and Ehcache caching implementations, including new auto-configurations and properties for fine-grained cache management.

  • Updated Content Item minification to properly handle nested content and clear unnecessary context state from model fields.

  • Updated the admin metadata to hide the translatable flag for Asset-typed Content Model Fields

Bug Fixes

  • Improved hydration validation to correctly account for custom fields that support multiple values.

  • Added defensive checks to prevent a potential NullPointerException when Content Field attributes are null during hydration.

  • Fixed a bug where the Admin UI would allow adding augmentations on the ContentItem Views despite the ContentItem domain not supporting them by default

  • Fixed a bug where the image field on a Content Item will show as changed any time any field on the Content Item is changed — updated the logic to consistently map the ContentField data for the image field so that the comparison logic can properly determine if the image field has actually changed or not

  • Added validation to prevent missing value for the custom CONTENT_FIELD, which will cause errors


Customer Release Notes for 2.1.0

New Features & Notable Changes

  • Add support for managing a Customer’s Accounts from the Customer screen via the Accounts tab

    • Allows the user to update the Customer’s Account Member role, add them to other Customer Accounts, and remove them from Customer Accounts they are members of.

  • Introduced enhancement to disallow users from editing the User ID attribute field in the Customer form, with validation to prevent updates to the User ID.

  • Add Query Builder filters to the Customer Browse grid to allow users to filter entries using advanced search features.

    • Fields that are not indexed in Solr can be searchable in case that Customer Internal Search is enabled.

  • `AccountUserManuallyCreatedProducer', 'AccountMemberRoleChangeProducer', 'AccountMemberRestrictionChangeProducer' will first attempt to use durable sender if found in the context.

  • Introduced support for Recommendation Engine Services via Customer Modified messaging.

    • The Customer Modified messaging configuration is featured in its own separate class to support the balanced configuration setup.

  • Included Account#accountNumber in AccountUpdateRequest handled by Auth service to track the mapping of users to accounts.

  • Add support for account member restrictions and account member creation by another account member. See Add Account Member Flow.

  • Added support to view a Customer’s or Account’s subscriptions in the Admin

  • Update the UserRegistrationListener#buildCustomer method to receive UserCreatedEvent for better extension point

  • Added a new endpoint CustomerSegmentEndpoint#readAccountsBySegment to support filtering requested accounts by customer segments

  • Remove unnecessary Owned interface implementation from JpaAccount

Miscellaneous
  • Set default status for the RegisterAccountMemberRequest to ACTIVE, allowing API requests to override the value.

Bug fixes

  • Fixed an issue where account member permission updates were not reflected, making some sections (e.g., saved payments) inaccessible.

  • Fixed AccountMember change event gaps

    • Fixed an issue where the AccountMember-related fields where not updated due to missing userId in the AccountMemberRoleChangeRequest event

    • Send an AccountMemberRestrictionUpdateEvent event when the account member is deleted

Upgrade Guide

Method Signature Changes
Class/Method Description

DefaultAccountService.notifyAccountChange

Now takes in Account and ContextInfo instead of String accountId, String parentAccountId, String status, ContextInfo context, so that the Account can be used to populate details in AccountUpdateRequest


Fulfillment Services Release Notes for 2.0.6-GA

Enhancements

  • Make name field on Fulfillment Destination and Fulfillment Origin required

Note

If the name fields are left blank, then it does not show up on the Admin listgrid, and cannot be selected in the fulfillment calculator configuration.

To help avoid this scenario, we’ve included a Liquibase script that will automatically populate these values with "UNNAMED_DESTINATION" or "UNNAMED_ORIGIN", if they’re currently blank. We recommend that you rename these fulfillment destinations & origins to give them a more meaningful name.


Import Release Notes for 2.1.1-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

  • Added new iconName DSL method to allow setting a custom icon to be displayed by the StartImportAction component.

  • Added import audit logging for each import request with tracking information — see Advanced Audit for details on using the audit features.

Bug Fixes

  • Updated the maximum length for 'Product Characteristics' in CompleteProductImportSpecification to allow more than 255 characters while importing.


Inventory Release Notes for 2.1.2

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

Reintroduce Index on blc_sku_inventory.sku_code

The introduction of Serialized Inventory in Inventory Service 1.7.13-GA saw the removal of several indices including one on sku_code (id IDXk4k1x05sf4w59lia0c819pv20). Analysis since shows that the latter is an essential index to maintain for anyone not using Serialized Inventory. Therefore, we have reintroduced it by default, and it will be present for all new projects on the 2.1.x and subsequent release trains.

We advise all users to verify that this index is in place. If missing, we recommend adding it as it avoids table scans of the blc_sku_inventory table in typical usage.

CREATE INDEX IDXK4K1X05SF4W59LIA0C819PV20 ON BLC_SKU_INVENTORY (SKU_CODE);
Default Sku Inventory Summary Service Improvements
  • Updated DefaultSkuInventorySummaryService to include the quantity in the summary result and refactored the object creation logic into a common method for improved maintainability.

Bug Fixes

  • Updated the FulfillmentStatusChange listeners to read Order and OrderFulfillment by ids, to accommodate the new message payload that only contains IDs to avoid RecordTooLargeException for large fulfillments

    • The listeners will still check for the full Order and OrderFulfillment payload in the message for the sake of backward compatibility, but the logic is subject to removal in a future release

Configuration Properties

Added Properties
  • broadleaf.inventory.orderprovider.url

  • broadleaf.inventory.orderprovider.order-uri

    • Description: The URI path for basic singular order retrieval

    • Default value: /orders

  • broadleaf.inventory.orderprovider.fulfillment-uri

    • Description: The URI path for basic singular fulfillment retrieval

    • Default value: /order-fulfillments

  • broadleaf.inventory.orderprovider.service-client

    • Description: The service client to use when calling order services

    • Default value: inventoryclient


Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Features & Notable Changes

  • Added support for Caffeine and Ehcache as Spring Cache options.

    • Standard Spring Cache implementations are now available alongside Apache Ignite.

    • For more details, see the Caching documentation.


Admin Metadata Release Notes for 2.3.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

Notable Changes

  • Added globalComponents to the AugmentedContainer DTO to support global component augmentations.

    • These components are not tied to a specific container instance and can be reused across multiple containers.

  • Added support for "characteristics" on BusinessType DTO:

    • Introduced BusinessTypeCharacteristic and Characteristic DTOs.

    • BusinessType now includes a characteristics list.

  • Enhanced augmentation handling:

    • Added repository/service methods to find and manage augmentations by containerKey and characteristic data.

    • Implemented logic to create or update characteristic augmentations in MetadataProductBusinessTypeModifiedHandler.

  • Minor refactoring and documentation update.


Notification Release Notes for 2.1.3-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

New Features & Notable Changes

  • Added support for Retail Delivery Fees

    • Included fee totals in order total breakdown of customer emails

Bug Fixes

  • Updated the FulfillmentStatusChange listeners to read Order and OrderFulfillment by ids, to accommodate the new message payload that only contains IDs to avoid RecordTooLargeException for large fulfillments

    • The listeners will still check for the full Order and OrderFulfillment payload in the message for the sake of backward compatibility, but the logic is subject to removal in a future release

Configuration Properties

Added Properties
  • broadleaf.notification.orderprovider.url

  • broadleaf.notification.orderprovider.order-uri

    • Description: The URI path for basic singular order retrieval

    • Default value: /orders

  • broadleaf.notification.orderprovider.fulfillment-uri

    • Description: The URI path for basic singular fulfillment retrieval

    • Default value: /order-fulfillments

  • broadleaf.notification.orderprovider.service-client

    • Description: The service client to use when calling order services

    • Default value: notificationclient


Offer Release Notes for 3.2.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

Enhancements

Added support to show/hide subscription offer fields by properties

Instead of always showing the subscription offer metadata fields (e.g. subscription offer templates, begin period, etc.), the metadata is now controlled by a different property:

broadleaf:
  offer:
    metadata:
      enable-subscription-offers: true
Note
Enabling subscription via broadleaf.subscription.enabled implicitly enables the subscription offer fields too
New Offer fields
  • Added sharedCode field onto the Offer entity to provide an alternative code that can be used to opt in to an offer, in addition to the mapped codes that are already supported. This is useful for cases where there are multiple codes that can be used to opt in to the same offer, or when there needs to be a single code that can be used across multiple offers.

  • Added includeFreeShipping field onto the Offer entity to indicate whether the offer that is applying to an Order Item or Order should qualify for free shipping.

  • Updated Offer metadata to add an implicit filter to exclude rule-based categories from Target Item Criteria rule builder. This is to provide better clarity on how the offer is intended to be used and can be used to drive different behaviors in the future if needed based on how the offer is accessed by customers. This is also to prevent rule-based categories from showing up as selectable criteria, since they are dynamically generated based on rules and cannot be reliably used as criteria for offers. This change is to improve the usability of the Offer edit page and prevent confusion for users who may not understand why certain categories are not working as expected when used as Offer criteria.

  • Added accessMethod field onto the Offer entity to indicate whether how the offer is accessed by customers, whether through a code that they need to enter or through an automatic application (i.e. automatically considered).

  • Added the ability to generate a single-use code for an offer that can be used for a single opt-in and redeemed by a single customer, in addition to the existing ability to generate multiple codes for an offer that can be used for multiple opt-ins and redeemed by multiple customers.

  • Added support to emit audit events for edits to Offer fields.

Subscription Offer Enhancements
  • Added Payment Period Frequency and Payment Period Type to the list of targetable fields in the Offer Target Item Criteria rule-builder for subscription based order-item offers.

  • Added flow field onto the Offer entity to indicate what subscription flow the offer is to be applied to, if any, to provide better support for subscription offers that are only intended to be applied during a specific part of the subscription lifecycle (e.g. only during the subscription creation, editing, upgrade, or downgrade).

  • Added support for an offer to target a new customer subscribing for the first time. The first time subscription offer can be targeted against a specific product or any products, and can be used to incentivize new customers to subscribe by offering them a discount on their first subscription order.

Bug Fixes

  • Fixed an issue where the discount is incorrect when maxSavingsPerOrder is set and two offers are stacked and applied to the same item

    • Previously, the discount for the offer that is applied second was calculated based on the item’s original price, rather than the price after applying the first discount, causing the final total discounts to be incorrect

  • Fixed a bug where the Admin UI would allow adding augmentations on the Campaign Views despite the Campaign domain not supporting them by default


Order Services Release Notes for 2.2.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

  • This version now requires DataTracking 2.0.3+

New Features & Notable Changes

Retail Delivery Fees
  • Added support for Retail Delivery Fees

    • Added support for optionally refunding RDFs in OMS views

      • Added property to gate enabling RDF refund choice for CSRs in the Admin OMS views (broadleaf.order.metadata.enable-refunds-on-retail-delivery-fees - false by default)

      • Added metadata attribute on the return action to pass the property value to AdminWeb.

    • Added new fee domain to the JpaReturnAuthorization

  • Support Admin Workflow UI for Order Fulfillments

    • New properties

      • broadleaf.order.metadata.support-views-as-body-content-for-fulfillment-view-enabled=false: Enables FulfillmentView to have subview sections like OrderView rather than only having a single hardcoded view

      • broadleaf.order.metadata.workflow-service-integration-enabled=false: Enables a workflow view section for Fulfillments to integrate with an external workflow service

    • Added FulfillmentViewSection to allow FulfillmentView to have sub-views like OrderView. Analogous to OrderViewSection

    • Added FulfillmentSummaryViewSection to be a FulfillmentViewSection representing the default view previously hardcoded into FulfillmentView. Use in combination with the property specified above

    • Added FulfillmentMainView interface to include shared configuration options between FulfillmentSummaryViewSection and FulfillmentView to reduce duplication

    • Added FulfillmentWorkflowViewSection to represent the workflow from the workflow service for the Fulfillment. Use in combination with the property specified above

    • Added FulfillmentWorkflowCommandAction to represent the commands that can be issued against a Workflow

    • Added OMS metadata DSL support for inventoryLookupEndpoint on ItemsOrderViewSection to replace manual construction

    • Added DSL helper util FulfillmentActions for initializing workflow command class instances

    • Added DSL helper util FulfillmentViews for initializing instances of difference Fulfillment related classes

    • Added workflow view related messages to messages/workflow.properties

  • Added Cancellation Policy ref fields to the JpaOrderItem

New API for Consolidated Returnable Items and Fees Information
  • Added new endpoint to read a ReturnableFulfillmentInfo object that contains both the returnable items and refundable fees to avoid multiple calls in the UI

  • This replaces the returnable items endpoint, which is now deprecated

  • Also added IncludedFees to the OrderFulfillment domain for persistence

    • This is a JSON array with summaries of each fee captured or canceled along with an OrderFulfillment

    • Column INCLUDED_EXTRA_FEES

Table 1. New Endpoints
Purpose HTTP Method Path Response

Get returnable info for entire order

GET

/orders/{id}/returnable-info

ReturnableFulfillmentInfo

Get returnable info for an order fulfillment

GET

/orders/{orderId}/fulfillments/{id}/returnable-info

ReturnableFulfillmentInfo

Table 2. Deprecated Endpoints
Purpose HTTP Method Path Response

Get returnable fulfillment items for entire order

GET

/orders/{id}/returnable-items

List<OrderFulfillmentItem>

Get returnable fulfillment items for an order fulfillment

GET

/orders/{orderId}/fulfillments/{id}/returnable-items

List<OrderFulfillmentItem>

ReturnableFulfillmentInfo
{
  "returnableItems": OrderFulfillmentItem[];
  "returnableFees": IncludedFee[];
}
Schema Changes
  • Added new columns to the blc_order_item table

    • CANCEL_POLICY_REF_ATTR

    • CANCEL_POLICY_REF_ID

    • CANCEL_POLICY_REF_SRC_ID

    • CANCEL_POLICY_REF_SRC_TYPE


Order Operation Release Notes for 2.2.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features

Retail Delivery Fees
  • Added support for Retail Delivery Fees

    • Added support for mapping RDF attributes during Order & Order Fulfillment generation

    • Added support for fee amount capture & refunds in OMS

      • Added fees to the ReturnRequest

      • Populated the FeeRefund on the ReturnAuthorization from the OrderItems based on the fees included in the ReturnRequest

      • Include fee refund totals in the refund estimated totals

      • Ensure the fee is only refundable once, keeps track across all the order’s ReturnAuthorizations

      • Builds PaymentRefundPackages for the fees, and uses those when calling PaymentTransactionServices to issue refunds

Subscriptions

For more information on Subscription Operation Service, please refer to the Subscription Operation Service 1.0.0 release notes.

  • Added support for provisioning Subscriptions & fulfilling subscription-based orders via the newly-created Subscription Operation Service. This support includes the following capabilities:

    • Processing subscription-related orders and order items.

    • Creating net-new Subscriptions based on orders and order items.

    • Splitting fulfillments based on the subscription items’s fulfillmentWorkflow

    • Managing subscription locks to avoid multiple processes manipulating the same subscription at the same time

    • Initiating subscription workflows based on the fulfillmentWorkflow and subscriptionActionFlow to fulfill each type of subscription action (create, edit, upgrade, downgrade, cancel)

Note
The environment variable broadleaf.subscription.enabled must be set to true to enable this support for Subscriptions in the Order Operation Service.
Consolidated OMS Refunds
  • Return Authorization refunds can now be consolidated into a single transaction per payment.

    • When confirming multiple items for a refund, instead of executing a REFUND transaction per item, the item totals are aggregated and a single REFUND transaction request is sent to the payment gateways.

    • In order to support this feature, the TransactionExecutionRequest and similar DTOs have been modified to support multiple source entity IDs.

    • This consolidated refund flow is now enabled by default in the OMS. To disable this flow, set the broadleaf.orderoperation.service.payment.consolidate-refund-transactions property to false.

Bug Fixes

  • Fixed RecordTooLargeException for large fulfillments by only using Order and OrderFulfillment ids for FulfillmentStatusChangeEvents

    • The AbstractFulfillmentStatusChangeHandler is updated to only emit Order and OrderFulfillment ids.

    • The FulfillmentStatusChange listeners are updated to read Order and OrderFulfillment by ids, to accommodate the new message payload that only contains IDs to avoid RecordTooLargeException for large fulfillments.

      • The listeners will still check for the full Order and OrderFulfillment payload in the message for the sake of backwards compatibility, but the logic is subject to removal in a future release.

  • Ensured ReturnConfirmedEvent is sent with a unique message idempotency key, fixing a bug where multiple quickly-submitted confirmations on the same return would not be successfully processed by consumers.

  • Fixed an issue where incorrect refund amounts were being calculated when refunding products with add-ons in quantities greater than 1 — updated the calculation of refund amounts to include parent Order Item quantity for dependent items such as add-ons.

  • Fixed an issue where building Order Line Items from a given Order was skipping the succeeding layer of dependent cart items (such as add-ons) — updated the logic to recursively build Order Line Items for all layers of dependent cart items, ensuring that all items are included in the Order Line Items list regardless of how many layers of dependencies there are.

  • Fixed an issue where a user could not refund the full remaining amount of an Order after overriding any item total to an amount exceeding its original amount during partial confirmation — the DefaultConfirmReturnRequestValidator was updated to check when the total to refund for items exceeds available funds especially when the total to refund is being overridden, and throw an appropriate error message to the user. Additionally, the DefaultReturnConfirmationGenerationService was updated to use the overridden refund amount when calculating the remaining total refund amount for the confirmation, ensuring that the correct total refund amount is used when confirming a return with overridden item totals.

Miscellaneous

  • Updated existing and introduced new kafka bindings:

    • Added orderOperationsTaxHandlingOnOrderCreatedInput and orderOperationsVoucherCreationOnOrderCreatedInput for orderCreated topic

    • Added orderOperationsFulfillmentRefundTaxOnReturnConfirmedInput for returnConfirmed topic

    • Fixed FulfillmentRefundTaxHandler to subscribe to correct binding ReturnConfirmedFulfillmentRefundTaxConsumer.CHANNEL

    • Fixed VoucherCodeGenerationOrderCreatedListener to subscribe to OrderCreatedVoucherGenerationConsumer.CHANNEL

  • Fixed a bug where calculation of refund amounts didn’t include the parent order item’s quantity for Add-On Order Items, resulting in Add-On Order Items to only refund one quantity


Payment Transaction Release Notes for 2.0.9-GA

Requirements

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

Subscription & Billing Support
  • Enhanced SavedPaymentMethod features to support Billing lifecycle

    • Added API functionality to read and execute payments for a SavedPaymentMethod to integrate with BillingServices.

      • Introduce endpoint for gathering SavedPaymentMethods for a set of owningUserIds.

      • Introduce endpoints to execute payment transactions based on a SavedPaymentMethod.

      • Introduce endpoints for looking up and recording SavedPaymentMethod transaction results by requestId.

    • Added SavedPaymentMethodTransactionExecutionRequest to handle transactions made against a SavedPaymentMethod.

    • Added validation ensuring that the request user matches the saved payment method

    • Updated logic creating a Payment based on a SavedPaymentMethod to declare the type & gatewayType from the SavedPaymentMethod.

    • Added logic to archive SavedPaymentMethods if a hard decline is encountered while using the payment method.

  • Added hook point in DefaultTransactionExecutionUtil to populate TransactionExecutionDetail#getAdditionalResponseAttributes.

  • Updated the Payment domain.

    • Added simple field, shouldArchivePaymentDueToFailedInitialTransaction, to optionally declare whether the payment should be archived when the initial Auth or AuthAndCapture transaction fails.

    • Added simple field, requestId, to track the request that created the payment.

      • Added Liquibase scripts to populate this field by default in the database.

  • Enhanced gateway error handling.

    • Added new transaction status, REQUEST_NOT_RECEIVED_BY_GATEWAY, to track transactions that failed before gateway connection.

    • Interpret InvalidCreatePaymentRequestException as InvalidTransactionExecutionRequestException for SavedPaymentMethodTransactionExecutionEndpoint interactions.

  • Added isShouldSavePaymentForFutureUse field to the payment summary.

  • Added new Payment Owner type, PAYMENT_ACCOUNT_PROCESSING_REQUEST, to represent a Billing event.

  • Added Payment#getAttributes to the PaymentRequest#getAdditionalFields during request creation.

Other Features
  • Fixed bug that allowed customers to create payments for submitted orders.

    • Added validation to prevent creating new Payments through the CustomerPaymentManagementEndpoint when an existing Payment for the same cart has a CUSTOMER_MUTABILITY_BLOCKED status.

  • Updated DefaultTransactionExecutionService#getTransactionReferenceId to invoke the new PaymentGatewayTransactionService#identifyTransactionReferenceIdOverrideForSubsequentTransactions method from PaymentGatewayCommon. This ensures transaction reference ID can be overridden for non-initial transactions.

  • Updated DefaultPaymentManagementService to populate PaymentInfo.addressByType from the equivalent field in Payment

  • Updated DefaultPaymentManagementService to invoke the new PaymentGatewayPaymentModificationService#modifyFullPaymentForCreate method from PaymentGatewayCommon. This allows gateway-specific overrides of all PaymentInfo fields, instead of being limited to PaymentGatewayPaymentModificationService#modifyPaymentMethodProperties like before.

  • Updated DefaultPaymentRequestService.createPaymentRequest to include a breakdown of order subtotal, adjustments total, shipping total, and tax total on the PaymentRequest, conditional on whether the new broadleaf.paymenttransaction.service.discriminated.include-transaction-amount-breakdown property is enabled.

  • In order to support OMS consolidated refunds, the PaymentTransaction domain, as well as TransactionExecutionRequest and similar DTOs have been modified to support multiple source entity IDs.

  • Added new PaymentCheckoutCompletionListener that will listen for checkout completion events, and sends out its own paymentPostCheckout events that can be listened to.

  • Added payment gateway reference to the payment attributes map.

  • Added new endpoints to support handling of payment gateway fulfillment callback responses.

  • Added property discriminated per payment gateway to determine whether or not to include the breakdown of transaction amounts.

  • Added isShouldSavePaymentForFutureUse field to the PaymentSummary


Pricing Release Notes for 2.2.0-GA

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

New Features & Notable Changes

Fees
Tip
Since Pricing Service 2.2.0, Release Train 2.3.0

This release includes support for Fees, which is a new domain addition to Pricing Services. For more information on this new domain and feature, such as how it is used and how fees are calculated, see the Pricing Services Fees documentation.

  • Added FeeMetadataAutoConfiguration to configure Fee metadata for the Fee Browse grid and Fee create and update pages

    • The Fee metadata is controlled by setting broadleaf.pricing.metadata.fee.enable-fee-metadata to either enable or disable the Fee feature being accessible via the Admin Portal.

  • Added new domains and enum types

    • Added Fee domain to represent the fees to be applied onto a Cart

    • Added JpaFee domain to persist Fee objects

    • Added enum types FeeCalculationMethod which represents the calculation method to be used when applying the fee to a cart and FeeCalculationOperation which represents the operation to be used when calculating the fee to be applied to a cart

  • Updated PricingCacheProperties and PricingCacheAutoConfiguration to be able to cache Fees by Application

  • Added FeeService for Fee-related operations and methods

    • Fees can be fetched by Application and are cached by Application to facilitate easier retrieval and eliminate redundant calls to the data store

  • Added FeeEndpoint for CRUD (create, read, update, and delete) operations

  • Added FeeValidator to apply custom validations for Fees

  • Added Broadleaf Translation Common library dependency and implemented an endpoint to be able to update translations for Fee labels === Updated PriceInfos Retrieval Logic for Recurring Prices Prior to this release, the DefaultPriceInfoService logic for recurring prices (prices with frequency and term) would only check if the PriceData term matches the given PriceableTarget’s term.

In this release, the check against frequency was added to ensure consistent matching behavior. This affects the CatalogBrowse pricing logic for PDP and PLP, as CatalogBrowse needs to know the frequency to pass when doing the initial pricing for products.

Added support to show/hide recurring pricing fields by properties

Instead of always showing the recurring pricing metadata fields (e.g. terms and frequency), the metadata is now controlled by a different property:

broadleaf:
  pricing:
    metadata:
      display-recurring-pricing-fields: true
Note
Enabling subscription via broadleaf.subscription.enabled implicitly enables the recurring pricing fields too
Two-Tier Pricing

This feature allows configuration of a new "tier" pricing strategy that involves consideration of a specific target attribute and its quantity to determine the applicable price tier. By using this "two-factor" approach, we are able to handle more complex pricing scenarios where the tier adjustment amount can further vary depending on the quantity of the specified attribute. In practice, the attribute needs to match either an attribute or characteristic assigned to the PriceableTarget (e.g., Product) for the tier to apply.

  • Updated PriceData domain to include tierPricingStrategy and tierAttributeName fields.

  • Updated PriceDataTier domain to include attributeMinValue field.

  • Added TierPricingStrategyOptionEnum to represent the available tier pricing strategy options:

    • NONE

    • SIMPLE

    • TWO_FACTOR

  • Updated the DefaultPriceInfoService to handle two-tier pricing logic when calculating prices based on the defined strategy.

    • When using the TWO_FACTOR strategy, the service will filter for any price data tiers linked to the PriceableTarget’s attribute or characteristic matching the tierAttributeName, leveraging newly introduced filterPriceDataTiers and filterTiersByAttributeQuantity method

    • Updates were also made to ensure price list modifications and adjustments are also applied to the tiered pricing related to a price detail, by calculating adjustments and applying formulas to tiered prices.

  • Updated the metadata for the Price Data Form to replace the pricing strategy fields to support the new pricing strategy.

    • Replaced the tiers control group for a tierPricingStrategy dropdown.

    • When TWO_FACTOR is selected, a new tierAttributeName text field will appear to allow specifying the attribute name to be used for tier filtering.

    • When TWO_FACTOR is selected, the Price Data Tiers grid will allow defining and attributeMinValue for each row.

Example Use Case

When configuring pricing tiers for a product with an attribute or characteristic of variable quantity (e.g., "Storage Size"), the two-tier pricing strategy can be used to define price adjustments based on both the quantity purchased and the characteristic/attribute value.

For example, a product may have the following tiers defined:

  1. $50 per item with quantity ≤10 and 64 GB memory

  2. $45 per item with quantity from 11-20 and 64 GB memory

  3. $40 per item with quantity >20 and 64 GB memory

  4. $80 per item with quantity ≤10 and 128 GB memory

  5. $75 per item with quantity from 11-20 and 128 GB memory

  6. $70 per item with quantity >20 and 128 GB memory

New Price List Types

Added new Price List types: Costs Price List and Adjustment Price List, in addition to the existing Price List types.

  • Cost Price Lists are used to define alternate cost prices for products, which can then be referenced to calculate "markup" prices based on the defined cost for the SKU.

  • Adjustment Price Lists are used to define price adjustments for existing price data, allowing for modifications to be applied to prices without having to modify the original price data, which can be useful for temporary promotions or discounts.

Introduced Pricing Categories

Iin addition to the Price Modifiers concept, Pricing Categories can be used to override the cost or amount specified in a Price Modifier by referencing a Pricing Category that has a different cost or amount defined, allowing for more flexible and dynamic pricing configurations.

Miscellaneous Enhancements

  • Added new Price Modifiers: Percent Markup and Target Margin - these modifiers can be used in Price Lists to calculate prices based on a percentage markup or target margin, respectively, instead of a fixed amount.

    • For Percent Markup, the price is calculated by applying a percentage increase to the base price (e.g., a 20% markup on a $100 base price would result in a final price of $120).

    • For Target Margin, the price is calculated to achieve a specific profit margin based on the cost price (e.g., if the cost price is $80 and the target margin is 25%, the final price would be calculated as $80 / (1 - 0.25) = $106.67).

  • To complement an enhancement in Catalog Services 2.3.0, the JpaCustomizedPriceDataRepository#findByPriceListIdAndTarget has been updated to separate the targetType predicate from the targetIds predicate in its query.

    • This solves an issue where querying for multiple target IDs of different types (SKUs and Pricing Keys) would fail as the previous implementation only supported querying by target IDs when a type was also specified.

  • Added caching to newly introduced PriceList and PriceData related queries.

  • Added the ability to prefer contract prices even when they are higher — this can be enabled by setting broadleaf.pricing.price-info.prefer-contract-prices to true, as the property is false by default. This can be for use cases where the contract price is more relevant to show than comparable lower standard or sales prices, even if it is higher, such as when a contract price reflects a negotiated price that the customer has agreed to pay.

Bug Fixes

  • Fixed a bug where the Admin UI would allow adding augmentations on the PriceList Views despite the PriceList domain not supporting them by default.

  • Updated the FulfillmentStatusChange listeners to read Order and OrderFulfillment by ids, to accommodate the new message payload that only contains IDs to avoid RecordTooLargeException for large fulfillments

    • The listeners will still check for the full Order and OrderFulfillment payload in the message for the sake of backward compatibility, but the logic is subject to removal in a future release

  • Fixed a bug where Price Lists could be augmented in the admin. Added @NonAugmentable to the Price List domain to prevent illegal augmentation.

  • Fixed a bug where DefaultPriceDataService.populatePriceListCurrencyMap would only populate the result with one entry instead of all different price lists

  • Fixed a performance issue where DefaultPriceDataService.fixCurrency was not considering priceListCurrencyMap when mapping from the persisted domain, causing individual price list queries for each item in the result

  • Refactored all string literal definitions of "priceListCurrencyMap" to use a shared String constant DefaultPriceDataService.CURRENCY_BY_PRICE_LIST_ID_MAP

  • Fixed a bug where the PricingCatalogCurrencyContextInfoCustomizer could throw an IllegalStateException when not in a request context. A check was added to prevent interacting with the CurrencyHolder outside of a request context.

Configuration Properties

Added Properties
  • broadleaf.pricing.orderprovider.url

  • broadleaf.pricing.orderprovider.order-uri

    • Description: The URI path for basic singular order retrieval

    • Default value: /orders

  • broadleaf.pricing.orderprovider.fulfillment-uri

    • Description: The URI path for basic singular fulfillment retrieval

    • Default value: /order-fulfillments


Sandbox Release Notes for 2.1.5-GA

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

  • Track approver and deployer through sandbox transition lifecycle

Schema Changes
  • Added new column to the blc_change_summary table

    • APPROVER

Bugs Fixed

  • Added prevention for "Author cannot be null!" errors for the existing change summary events


Scheduled Job Release Notes for 2.1.5-GA

New Features & Notable Changes

Scheduled Job Execution Details cleanup
  • The cleanup functionality was added to keep only a configured number of execution details

    • You can configure it on a per-job basis - configuration property Execution Details to Keep was added in the admin view

      • It accepts values from 1 to 20 and indicates the last X number of execution details records to keep deleting the rest. The "last" is identified by the triggerTime column. The cleanup is happening when the job is executed and track execution details is enabled.

      • This requires the DB schema update to add a new column, check Upgrade Guide

Upgrade Guide

Note
If using liquibase to pull in base Broadleaf changelogs, then it is not necessary to manually run this script.
Add new column to the BLC_SCHEDULED_JOB table
ALTER TABLE blc_scheduled_job
ADD COLUMN number_of_exec_details_to_keep INT4;

UPDATE blc_scheduled_job
SET number_of_exec_details_to_keep = 10
WHERE number_of_exec_details_to_keep IS NULL;

ALTER TABLE blc_scheduled_job
ALTER COLUMN number_of_exec_details_to_keep SET DEFAULT 10;

Search Services Release Notes for 2.2.2-GA

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

  • Note that since 2.2.1-GA of SearchService, it requires CatalogServices 2.2.1+ documented in the 2.2.1 Catalog Release Notes

Important
After upgrading to this version, a full reindex is required for the changes to take effect.

Important Updates

New Features & Notable Changes

  • When a reindex triggered with "Index Filters" selected, you can see what filters were selected

Bug Fixes

  • Prevent partial reindex if the enablePartialIndexWithFilters is false even if the reindex request contains additional filters (startDate, endDate etc).

  • Updated the Reindex CreateEntityView metadata to define a successNotificationOnCreate message to notify users of successful reindex creation.

Miscellaneous
  • Fixed an issue where the sort order of a Facet within a Facet Group was not honored by the search engine.

    • Any Facets that are sorted within a Facet Grouping will be returned by their specified sort order. Any Facets that are marked unsorted will default to the Facet’s global display order, if set.

  • Fixed issue where only one of the translations get indexed if there are multiple translations with the same entity field name, even if the translations are for separate entities

    • For example, consider a Product in a Category. If both the Product and Category have a "name" field that is translated, only one of those fields would be indexed. Now both will be indexed.

  • Increased specificity for the Product IndexableTypeDocumentBuilder

    • This fixes an issue that can arise when using @ConditionalOnMissingBean and its interpretation of interfaces, particularly when a class implements multiple interfaces. For example, a bean was not getting registered after introducing an IndexableTypeDocumentBuilder for a different indexable type altogether. Using the more specific concrete class as the return type of the bean method prevents ambiguity.

  • Update reindex logic so that reindex for the same indexable type can’t be running in parallel on the same node, potentially causing the index operation context to be polluted.

  • Fixed an issue where search entities didn’t delete their reference entities

    • For example, with this fix when a Facet is deleted, the FacetGroupFacet references will also be deleted removing the Facet from any FacetGroups it was assigned to.

    • This was implemented for Facets, SortOptions, RelevancyRules, FacetGroups, SortGroups, and RelevancyRuleGroups

  • Expose a new deleteByQuery method in ReindexProvider and update SolrReindexService.deleteAll() to use it for more efficient bulk write requests, improving performance of single and batch index flows.

  • Fixed SpringCloudStream binding misconfiguration that caused Kafka consumer lag even after multiple iterations of successful processing

    • Update SpringCloudStream bindings for singleIndexRequestInputProduct, batchIndexRequestInputProduct, and fullIndexMessageInputProduct indexing to each use their own groups

Upgrade Guide

Liquibase Change Sets

The database schema has changed as part of this version.

Creates and Updates

Create/update changes (new tables, new columns, etc) are automatically included in the updated *changelog-master.xml after you upgrade to the new Authentication Services JAR. The new changesets inside will run automatically to migrate existing data.

Database Platform Create/Update Changelog File Name

PostgreSQL

db/changelog/auth.postgresql.changelog-master.xml

MariaDB

db/changelog/auth.mariadb.changelog-master.xml

MySQL

db/changelog/auth.mysql.changelog-master.xml

Oracle

db/changelog/auth.oracle.changelog-master.xml and db/changelog/auth.oracle.short.changelog-master.xml


Tenant Services Release Notes for 2.1.6-GA :toc:

Tip
The 2.x versions are Spring Boot 3 compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, all microservices have been upgraded to support Spring Boot 3.3 & 3.5.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

Bug Fixes

  • Fixed an issue where ApplicationEndpoint would return results for applications when an admin user doesn’t have application access.

  • Fixed an issue where an implicit catalog’s currency would not get updated after updating its parent catalog.


Workflow Services Release Notes 1.0.0-GA

1.0.0-GA

  • Initial release of the service.

Main Components
  • com.broadleafcommerce.workflow.web.endpoint.WorkflowEndpoint

    • API for managing Workflow instances and execution

  • com.broadleafcommerce.workflow.service.WorkflowService

    • Main service-layer interface for all standard workflow related logic flows

  • com.broadleafcommerce.workflow.service.WorkflowLauncher

    • Management component that spawns SimpleWorkflow instances on a limited thread pool

  • com.broadleafcommerce.workflow.service.WorkflowRunner

    • Worker component (in a thread pool - see WorkflowLauncher) that handles calling SimpleWorkflow#start, or handles reconstitution of workflow state via replay

Release Train Compatibility

Compatible with all Release Trains after 2.2.0 unless otherwise noted.

Payment Integration Libraries

Braintree Release Notes for 2.0.2-GA

Miscellaneous

  • Updated the partner BN code.


Chase Payment Release Notes for 1.0.0

Initial release of Broadleaf’s Chase Payment integration module, including support for:

  • Chase Hosted Payment Solution (HPS) Order Abstraction Flows for Orbital profile creation and usage

  • Apple Pay

  • Authorize, AuthorizeAndCapture, ReverseAuthorize, Capture, & Refund transactions against the Orbital JSON API


Checkout.com Release Notes for 2.0.4

New Features & Notable Changes

  • Introduced new default CheckoutComApplePayCertificateAndKeyProvider: StringBase64CheckoutComApplePayCertificateAndKeyProvider. Check details

    • It expects apple certificate and private key in the form of base64 encoded string and uses new properties for that. Optionally specify property broadleaf.checkout-com.apple-pay.certificate-and-key-provider-impl=BASE64_STRING

  • Deprecated old default CheckoutComApplePayCertificateAndKeyProvider: DefaultCheckoutComApplePayCertificateAndKeyProvider

    • To continue using it, specify property broadleaf.checkout-com.apple-pay.certificate-and-key-provider-impl=CLASSPATH_RESOURCE

  • Package com.broadleafcommerce.vendor.checkoutcom.service.sesseion was renamed to com.broadleafcommerce.vendor.checkoutcom.service.session


Broadleaf PayPal Integration Release Notes for 3.0.3

New Features & Notable Changes

  • Upgraded PayPal to support new Complete Payment features including the Contact & Shipping Modules

    • Updated PayPal Order payload to include:

      • Venmo Payment Source

      • Order Update Callback Config

      • App Switch Preference

      • Contact Preference

      • Shipping Options

    • Added the BN code as a header to every PayPal API call

    • Added handling of shipping callbacks to the PayPalCheckoutHostedService

    • Added Assertion Header to the create PayPal Order API call

    • Added endpoints to create and fetch orders from PayPal

    • Added a HMAC security token on the callback config to verify the returned shipping callback

Upgrade Notes
Authentication Data

The calls to PayPal create and fetch order have been moved to the backend. To support the new endpoint, new authentication scripts have been added to the environment setup steps. Please review the new data in the environment setup.

PayPal Frontend Libraries

Additionally, to support the new functionality and backend PayPal v2 endpoint support, changes have been made to the paypal-checkout-js library and a new paypal-checkout-react library has been added.

See paypal-checkout-js and paypal-checkout-react documentation for more details.

Added Configuration Properties
  • broadleaf.paypal-checkout.api.merchant-id

    • Description: The Account ID, also known as the Partner-Merchant-Id or PayPal Merchant ID in the PayPal dashboard. Used when building PayPal Assertion headers.

  • `broadleaf.paypal-checkout.api.populate-shipping-on-order

    • Whether the shipping details should be populated if it’s already known when the PayPal Order is created or updated.

      • Default is false.

      • This should be true if setting the shipping_preference to SET_PROVIDED_ADDRESS in the frontend.

  • `broadleaf.paypal-checkout.api.populate-items-on-order

    • Whether the purchase unit items should be populated if they’re already known when the PayPal Order is created or updated.

      • Default is false.

      • This should be true if setting the shipping_preference to SET_PROVIDED_ADDRESS in the frontend.

  • broadleaf.paypal-checkout.gateway.callback-token-hmac-key

    • The private key used to generate an HMAC token for shipping callback validation.

  • broadleaf.paypal-checkout.gateway.callback-token-hmac-algorithm

    • The algorithm used to generate an HMAC token for shipping callback validation.

    • Default is HmacSHA256.

  • broadleaf.paypal-checkout.gateway.valid-order-callback-statuses

    • The PayPal Order statuses that are valid during shipping callback validation.

    • Default is ["CREATED", "SAVED", "APPROVED", "PAYER_ACTION_REQUIRED"].

  • broadleaf.paypal-checkout.gateway.gateway-reference-for-transaction-id

    • The name of the Payment attribute that the transaction reference ID should be saved as, mapped by transaction type.

    • Default is { AUTHORIZE: "PAYPAL_CUSTOM_ID", AUTHORIZE_AND_CAPTURE: "PAYPAL_CUSTOM_ID" }.

Bug Fixes

  • Updated the Authorize & Capture payment response to include the transactionReferenceId.

Common Libraries

Common Libraries

Cart Client Release Notes

2.0.5-GA

  • Introduced the RETAIL_DELIVERY_FEE cart item type

  • Add support for Cancellation Policy identification

  • Add new NON_CATALOG_ITEM to the DefaultCartItemTypes enum for generic non-catalog backed items that also aren’t fees

    • The NON_CATALOG_ITEM type is used for an item that does not exist in a catalog. This allows for custom usage of the CartItem domain and tracking of its instances as line items. These items are included in the cart subtotal and excluded from promotions.

  • Introduce CartItem#hasOverriddenSubtotal flag to support overriding the CartItem#getSubtotalWithDependentItems() calculation when portions of the subtotal don’t scale with the item quantity

  • Added CANCEL_CART to the DefaultCartActionTypes enum

Customer Client Release Notes

2.0.4-GA

Spring Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Features & Notable Changes
  • Added support for Caffeine and Ehcache as Spring Cache options.

    • For more details, see the Caching documentation.

  • Added support for specifying the X-Customer-Ref header in the service client.

Data Tracking Release Notes

Data Tracking Release Notes for 2.0.7-GA

  • The 2.x versions are Spring Boot 3 compatible.

Spring Boot Upgrade

  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Introduce Support for Optimized Catalog Hierarchy Propagation

When changes to a catalog-discriminated entity (ex: Product) are deployed to production, there is a 'propagation' process executed to cascade update any downstream catalog overrides with the changes within (unless the downstream record has already overridden the changed fields).

This is an expensive operation, and in projects with large numbers of child catalogs and child catalog overrides, this can become a performance hotspot.

With this release, we are introducing optional support for a different propagation design that can more efficiently propagate over large quantities of override records.

Please see Optimized Hierarchy Propagation documentation for more details.

  • Introduce a completely new alternative set of components called HierarchyPropagationHandler. Unlike PropagationHandler, which focuses on propagating a single record at a time (and relies on DefaultPropagationManager to do the work of recursing through the hierarchy), these implementations are capable of completing the full end-to-end propagation process for the entire hierarchy all on their own. This API contract enables much more powerful handler implementations that can batch propagate through entire ancestries much more quickly than the current flow.

  • Support for HierarchyPropagationHandler components is disabled by default. However, if enabled, DefaultPropagationManager will find the first such component which can handle the flow and delegate to it if available. If no HierarchyPropagationHandler is available, propagation falls back to the existing standard propagation flow.

  • Introduce a SimpleDeleteHierarchyPropagationHandler which delegates to a new method in JpaTrackableRepository to batch update entities as archived

  • Introduce a new SimpleCreateHierarchyPropagationHandler which short-circuits create propagation as a no-op

  • Introduce a new PerFieldUpdateHierarchyPropagationHandler which propagates changes for each field individually, delegating to FieldUpdatePropagator components to do so. By default, the only such propagator is SimpleFieldUpdatePropagator, which leverages new methods on JpaTrackableRepository to detect if a field is a simple field (can be easily compared and set in a single UPDATE statement) and execute an update statement to propagate changes on that field if it’s simple. If SimpleFieldUpdatePropagator is insufficient for a particular client’s needs, a new FieldUpdatePropagator can easily be registered with higher precedence to handle the specific field on the specific entity.

  • To support the simple field update propagation, JpaTrackableRepository was updated to now inject the NativeSqlStrategyManager to allow delegation to NativeSqlStrategy beans.

  • Defined new NativeSqlStrategy beans to implement the native SQL generation for the interesting fields. These are supported by new HibernateColumnResolverUtility for column mapping detection, as well as new methods in EntityUtils to detect entity field definition information, including any converter that might be being used for the field.

Bug Fixes

  • Updated JpaTrackableRepository.findByContextIdAndCatalog to detect if the current entity is sandbox-discriminated, and if so, add filters to ensure only production and non-sandbox-archived records are returned.

    • This fixes a bug with the default catalog propagation logic, which used this method to locate instances to propagate changes to. Without these additional filters, the result of the query was not deterministic and could result in propagation being applied to the wrong instance.

  • Fixed an issue where the incorrect tracking level causes the creation of a redundant notification state for the ChangeSummaryProducer#TYPE and an event with a null author and sandbox

    • NOTE: The method com.broadleafcommerce.data.tracking.core.mapping.TrackableDomainMapperMemberSupport.buildTracking is overloaded, and a new parameter is added. This may cause a breaking change if the method is overridden. The old method will no longer be called by the com.broadleafcommerce.data.tracking.core.transition.VendorSandboxPropagationHandler. If you have any customizations, please make sure to update them.

  • Added a cache invalidator to clear application cache to fix an issue where products from an unassigned and deleted catalog can still be searched and accessed on a storefront

  • Fixed an issue where methods in DefaultTrackablePolicyUtils that use Assert.notNull to check when catalogs are archived would throw an error instead of gracefully handling the policy validation failure

  • Fixed an issue where 'Undo Overrides' could be requested on an inherited entity and directly mutate the instance in the parent catalog

    • DefaultTrackableDomainMapperMemberSupport.handleDelete has been updated to reject 'clear override' requests if the target entity is not directly located in the catalog defined in the context request

Miscellaneous

  • Introduced a mechanism to allow the NotificationState for SingleIndexRequest messages to be built, even if SuppressNotificationContext is enabled

    • Previously, when SuppressNotificationContext was active for SingleIndexRequest during a CRUD flow, SingleIndexRequestNotificationStateMapperMember would always cease to build the NotificationState for it. This is still the default behavior, but now, there is a new SingleIndexRequestNotificationStateBuildContext ThreadLocal that can be activated to force the NotificationState to still be built in those flows. Using this will mean that on a CRUD operation, the NotificationState will still be built, but the first 'eager' send attempt of that message will not happen until the retry handler picks it up later.

    • Example of usage

      return SingleIndexRequestNotificationStateBuildContext.doWithForceBuildEnabled(() -> {
          return SuppressNotificationContext.exec(() -> {
              // invoke CRUD method that engages the mapping + persistence flows
              return replace(id, businessInstance, context);
          }, SingleIndexRequestProducer.TYPE);
      });
  • Updated various methods in CrudEntityHelper to be protected instead of private, improving extensibility of the class

  • Updated SortPositionRequest in accordance with Jackson library upgrade to enable seamless JSON deserialization

  • Enhanced performance of standard recursive catalog propagation flow

    • Introduced a net new PropagationCandidateFinderRepositoryFragment interface with a new bulk findByContextIdAndCatalogIn method.

    • Updated JpaTrackableRepository to implement that interface, and updated JpaTrackableRepositoryFactory to explicitly expose that interface on the final repository proxy instance

    • Updated DefaultPropagationManager to utilize the new batch query instead of querying for override records one child catalog at a time

      Note
      Please see Propagation Manager Properties for configuration options on the batch size.
  • Updated DefaultPropagationHandler to gate the emission of PERSISTENCE notifications on a new property. The default is still enabled for backward compatibility, but this feature now allows clients to disable that process for less noise and improved performance.

    Note
    Please see Default Propagation Handler Properties for configuration options.
  • Introduced new overrideCatalogLevel and creatingApplicationId fields to ContextState, and updated ContextStateBuilder to initialize them

    • overrideCatalogLevel is not serialized in API responses by default, but serves as a mechanism for internal service components to know the level of the catalog in overrideCatalogId, if set

    • creatingApplicationId is serialized in API responses and is also available for internal service components to know which application owns the entity (if the entity is in an application-owned catalog)

Fulfillment Common Release Notes

Extension Common Release Notes

Release Notes for 2.0.7

Ignite Metrics Collection Changes
  • Ignite metrics collection is resource-expensive and has been disabled by default. Previously, there was no mechanism available to disable this metrics collection. New properties have been introduced to control whether metrics collection is enabled.

  • Ignite metrics collection has been fully reworked with a new implementation.

    • This fixes a variety of bugs, including value miscalculations, overly aggressive metrics gathering in a looping structure, lazily instantiated caches being omitted from metrics, and large numbers of background tasks that continually run and consume resources. Hit percentage is no longer reported, as this can be calculated by the tooling gathering the data.

  • New Caffeine and Ehcache options for Spring Cache. See Caching for more information.

Tomcat Metrics Collection Enhancements

Import Consumer Release Notes

2.0.3

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Notable Changes
  • Fixed a bug that occurred when importing a product update with no changes, the product didn’t show up in the list grid.

  • Updated AbstractImportBatchHandler to replace usages of the deprecated property userSandboxPersistenceNotificationEnabled with the new property persistenceNotificationEnabled.

JPA Common Release Notes

Version 2.0.5-GA

Spring Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Features & Notable Changes
  • Improved memory efficiency during classpath scanning for Liquibase changelogs.

  • Fixed intermittent classloader collisions when reading YAML files during concurrent schema loading.

Metadata Release Notes

2.0.8-GA

Spring Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Features/Notable Changes
  • Added new attribute augmentable to Component that allows to disable form modification for views. Defaults to true.

    • This attribute cascades to child components overriding their settings.

  • Introduce Global Components to the registry

    • Global components are reusable components that can be shared across multiple containers. For instance, defining a modal once that can be triggered by multiple links in one or more forms.

      Example Usage of Global component
      registry.addGlobalComponent("test-modal", Views.modalView()
             .label("Terms and Conditions")
             .addField("testField", Fields.string()
                  .label("Test Field")));
  • Added new DSL for defining Modal Views.

    • Introduced ModalView interface and Views.modalView() builder method.

    • ModalView is a specialized form view designed for modal dialog presentations.

    • ModalViewCreate and ModalViewUpdate interfaces can be used for entity creation and update scenarios.

  • Introduced new DSL for CheckboxField component.

    • Introduced CheckboxField interface and Fields.checkbox() builder method.

    • CheckboxField is used to represent a boolean value as a checkbox in forms.

  • Added new DSL for BadgeColumn and BadgeTileComponent.

    • Introduced BadgeColumn interface and Columns.badge() builder method.

    • Introduced BadgeTileComponent interface and TileComponents.badge() builder method.

    • BadgeColumn and BadgeTileComponent are used to display badges in grid views.

  • Introduced new Simple classifier for components to handle simple components that don’t require or support all of the configuration options of Form Components, Groups, Views, Externals, etc.

    • Simple components can be added to other components using Component#addSimpleComponent, which will autogenerate the key to be in the form simple:{type}:{order}. Generally this will be sufficient, but they can also be added like normal components and supplied a manually defined key.

  • Introduced a Divider component to render a horizontal rule within forms or groups.

    • Takes a width attribute, indicating the width of the horizontal rule—default is sm indicating 1px.

    • Takes a spacing attribute indicating the spacing above and below the rule in rem—default is md indicating 2rem.

      Example usage of Simple component
      Groups.basic()
              .label("product.groups.basic-information")
              .addField(ProductProps.NAME, Fields.string()
                      .label("product.fields.name")
                      .required()
                      .translatable()
                      .readOnly(!isCreate)
                      .hidden(!isCreate)
                      .order(1000))
              .addSimpleComponent(SimpleComponents.divider()
                      .order(1500)
                      .width(Divider.Width.SMALL)
                      .spacing(Divider.Spacing.MEDIUM))
  • Added a new DSL for LargeContentColumn and its default implementation DefaultLargeContentColumn. This renders a column for the LARGE_CONTENT type with large contents like embedded collections, which displays the content as truncated strings. The column value is clickable, which will show a modal with the full contents formatted similar to YAML style.

    • This component can take a lines attribute to configure the number of lines to truncate to for the column value. Out of box, the default value is 1 and the maximum value is 6.

    • There is also a clearLines() method that will clear the lines attribute from the column.

      Example usage of LargeContentColumn
      Fields.residentGrid()
              .label("sandbox.grid.change-summaries")
              .addColumn(SandboxProps.DETAILS, Columns.largeContentColumn()
                      .label("sandbox.columns.details")
                      .lines()
                      .order(1000))
  • Added a new DSL for ModalLinkColumn and its default implementation DefaultModalLinkColumn. This renders a column for the MODAL_LINK type whose column value is clickable, and upon clicking will show a modal form with the fields and components configured via metadata.

    • Aside from being able to add fields and components, the endpoint to submit the modal form to can be configured via metadata as well.

      Example usage of ModalLinkColumn
      Externals.grid()
              .label("product.externals.promotional-product")
              .scope(ProductScopes.PRODUCT)
              .sandboxTrackable(CatalogChangeContainers.PROMOTIONAL_PRODUCT)
              .catalogTrackable()
              .sortable()
              .addColumn(ProductProps.Marketing.TYPE, Columns.modalLink()
                      .label("Update Promotional Product")
                      .order(1000)
                      .sandboxTrackable(CatalogChangeContainers.PROMOTIONAL_PRODUCT)
                      .catalogTrackable()
                      .submitEndpoint(Endpoints.put()
                              .uri(ProductPaths.PROMOTIONAL_PRODUCT)
                              .scope(ProductScopes.PRODUCT))
                      .addField(ProductProps.Marketing.TYPE, Fields.select()
                              .label("Type")
                              .required()
                              .defaultValue(PromotionalProductTypeOptionEnum.FEATURED)
                              .options(PromotionalProductTypeOptionEnum.toOptions())
                              .order(1000))
                      .addField(ProductProps.Marketing.RELATED_PRODUCT_ID,
                              ProductLookupHelpers.createProductIdLookup()
                                      .label("Select a Product")
                                      .required()
                                      .order(2000))
                      .addField(ProductProps.Marketing.PROMOTION_MESSAGE, Fields.string()
                              .label("Promotional Message")
                              .translatable()
                              .order(3000)));
  • Added displayOnly and displayOnly(boolean displayOnly) attribute methods to the Field DSL in order to display it as a plain or raw text representation of the field.

  • Introduce FORM_FIELD column type.

    • This column type allows for inline editing of the row entity by displaying an editable form field in the column.

    • Changes are submitted on blurring the field (moving focus away from it or just toggling for a boolean).

    • It supports the following field types:

      • BOOLEAN

      • DATE

      • DECIMAL

      • INTEGER

      • LONG

      • LOOKUP

      • MONEY

      • PHONE

      • SELECT

      • STRING

        Example Usage of FORM_FIELD column
        externalGrid
            // define an update endpoint to perform the save operation
            .updateEndpoint(endpoint -> endpoint
                .uri(EntityPaths.ENTITY)
                .scope(EntityScopes.ENTITY))
            // add a form field column
            .addColumn(EntityProps.DATE, Columns.formField()
                .label("Date")
                .field(Fields.bool()
                    .name("date")
                    // required to avoid displaying decorations inside the grid cell
                    .decorated(false))
                .order(3000))
  • Added new DSL for MoneyTileComponent to display monetary amount fields on tile grids

  • Added new DSL for CheckboxField component type.

  • Updated EntityFormView to be able to configure an UPDATE endpoint on the form itself that overrides the parent View’s UPDATE endpoint

    Example usage of overriding the UPDATE endpoint per form
    UpdateEntityView view = Views.entityViewUpdate()
            .label("Update View")
            .submitUrl("/main-entity", Scopes.SCOPE)
            // ...
            .addForm("separateEntityForm", Views.entityForm()
                    .label("Form with External Related Entity")
                    .updateUrl("/external-entity", Scopes.EXTERNAL_SCOPE);
  • Improved support for ExternalFieldGroup

    Note
    This work was done in support of allowing a fields within an ExternalFieldGroup to be updated using a form’s override update endpoint where the actual parent entity’s state wasn’t being modified and wouldn’t be returned from the submit endpoint, which was in a different microservice.
    • Added explicit DSL support for ExternalFieldGroup

    • Updated ExternalTypes & Externals to be able to expose the DefaultExternalFieldGroup DSL for metadata configuration

    • Added attributes map to Endpoints metadata with relevant helper methods

    • Added Endpoint attribute, responseIsPartialState: Indicates that this endpoint’s response will be a partial state that should be merged with the existing entity state, similar to a PATCH request. This is useful for APIs that do not support PATCH requests directly or that are backing external field groups on a different entity’s form. In those cases, the parent entity state should be merged with the new state returned by the endpoint to maintain the full view state.

    • Fixed Groups not getting their IDs set when using Form#addGroup(String type, Group<?> group)

    • Expanded javadocs on various metadata DSL interfaces

    • Added attribute to ExternalFieldGroup, useParentFormState. Indicates that the state for the fields in this group will be backed by the parent form’s.

    • If false, then a nested form state will be created to hold the field values in isolation from the parent. Note that this currently makes the group effectively read-only.

    • If true, then users should ensure that the field names are prefixed with the Group.getId() since this is used in the admin to ensure that the values do not collide with other fields in the parent form. TransformBody and MappingList can be used to map the form state to the appropriate structure expected by the form or entity submit endpoint (if any) and vice versa.

      Example Usage of ExternalFieldGroup
      public void exampleUsage() {
          form.updateEndpoint(Endpoints.post()  // <-- overrides the View's endpoint
                  .responseIsPartialState()
                  .uri("/dto-endpoint")
                  .transformRequest(t -> t.mappings(transformEntityToRequestDto("externalGroupId")))
                  .transformResponse(t -> t.mappings(transformEntityFromRequestDto("externalGroupId")))
                  .scope("DTO"))
                  .addGroup("externalGroupId", getExternalGroup());
      }
      
      public static MappingList transformEntityToRequestDto(String groupId) {
          return new MappingList(Arrays.asList(
                  Mappings.mapValue("%s.%s".formatted(groupId, "field1"), "field1"),
                  Mappings.mapValue("%s.%s".formatted(groupId, "field2"), "field2"),
                  Mappings.mapValue("%s.%s".formatted(groupId, "field3"), "field3")));
      }
      
      public static MappingList transformEntityFromRequestDto(String groupId) {
          return new MappingList(Arrays.asList(
                  Mappings.mapValue("field1", "%s.%s".formatted(groupId, "field1")),
                  Mappings.mapValue("field2", "%s.%s".formatted(groupId, "field2")),
                  Mappings.mapValue("field3", "%s.%s".formatted(groupId, "field3")));
      }
  • Added DateRangeColumn class to define a type of column that takes in a start date and end date to display a date range.

  • Added a DurationColumn class to define a type of column that takes in an integer value and displays it in a user-friendly format.

    • Includes a baseUnits attribute to specify the base units of the given duration.

    • Introduced DurationBaseUnits utility class to define the available base units:

      • DAY

      • WEEK

      • MONTH

      • YEAR

        Example Usage:
        grid
            .addColumn(DURATION, Columns.duration()
                    // The duration will be displayed in days
                    .baseUnits(DurationBaseUnits.DAY));
  • Added support for date fields to be computed based on another date field and a duration, e.g., allow active end date to be computed as the active start date plus 30 days.

    • Added computedFromDate, computedFromDuration, and computedFromDurationUnit attributes to the DateField, along with corresponding DSL methods.

      Example Usage:
      form
          // Add a duration field that will be used to compute the end date
          .addField(DURATION, Fields.duration())
          // Add a start date field
          .addField(ACTIVE_START_DATE, Fields.date())
          // Add an end date field that will be computed based on the start date and duration field
          .addField(ACTIVE_END_DATE, Fields.date()
                  .computedFrom(
                          ACTIVE_START_DATE,
                          OFFER_DURATION,
                          // Specify the base units for the duration field
                          DurationBaseUnits.DAY
                      )
                  );
  • Added new DSL methods to CreateEntityView to configure toast notifications to display on succesful create of an entity:

    • Introduced successNotificationOnCreate() method to configure a success notification with a default message.

    • Introduced successNotificationOnCreate(String message) method to configure a success notification with a custom message.

    • Introduced successNotificationOnCreate(InternationalizedMessage message) method to configure a success notification with a custom internationalized message.

  • Added new iconName DSL method to allow setting a custom icon to be displayed by the following components:

    • FilterAction

    • CommonStartExportAction

    • ExportGridAction

    • ImportGridAction

    • See Import Services 2.1.1 for StartImportAction component changes.

  • Added ComputedField & DefaultComputedField DSL support for Form components to allow defining fields whose values are computed based on other field values using a specified function. .Example Usage:

form
    .addField("shopPrice", Fields.money()
            .label("Shop Price")
            .validationSchema(ValidationSchemas.money()
                    .method(ValidationMethods.positive(
                            "Must be positive")))
            .order(1000))
    .addField("shopMarkup", Fields.money()
            .label("Shop Markup")
            .validationSchema(ValidationSchemas.money()
                    .method(ValidationMethods.positive(
                            "Must be positive")))
            .order(2000))
    .addField("productMarkup", Fields.money()
            .label("Product Markup")
            .validationSchema(ValidationSchemas.money()
                    .method(ValidationMethods.positive(
                            "Must be positive")))
            .order(3000))
    .addField("finalPrice", Fields.computed()
            .label("Final Price")
            .computationType(ComputedField.ComputationTypes.SUM)
            .fieldsToCompute(List.of("shopPrice", "shopMarkup", "productMarkup"))
            .order(4000));
  • Added ToggleTileComponent as a new type of TileComponent that represents a boolean value as a toggle switch on a tile grid. .Example Usage:

browseComponent.getDefaultGrid()
        .addTileComponent("thumbnail", TileComponents.thumbnail()
                .toggle(TileComponents.toggle()
                        .name("featured")
                        .label("product.browse.columns.featured")
                        .icon("star")
                        .submitEndpoint(endpoint -> endpoint
                                .uri(ProductPaths.TOGGLE_FEATURED_TAG)
                                .scope(ProductScopes.PRODUCT)
                                .param("toggleValue", "${toggleValue}"))
                        .order(1000)));
  • Added skipSandboxing attribute to GridExternal. This allows grids that pull data from external sources to opt out of sandboxing if the external source is not expected to be affected by sandboxing or if the grid is intended to show unsandboxed data for some reason.

  • Introduced InterdependentExternalGridsGroup concept to allow grouping together multiple instances of GridExternal that are related to each other and have interdependent data such that they should have their state changes tracked together in the admin and be displayed together in the UI.

    • For instance, this can be used for a group of grids that all pull from the same external source and should be tracked together in the admin since they will all be affected by the same external state changes, or if a change in one grid should trigger a refresh in the other grids since they are related and likely displayed together in the UI.

    • The following methods & attributes were added to support this feature:

      • listensFor(Map<String, List<String>>) & listensForGridsAndOperations: This method adds the listensForGridsAndOperations attribute and configures the other external grids within the InterdependentExternalGridsGroup that this grid will be listening for. This means that if a change is made to any of the configured grids, then this grid will be refreshed (i.e. the configured READ endpoint for the grid will be called). Operations can also be defined for each dependent grid to allow for conditional updates, but are not required. The operations are expected to be GridExternal.GridListenerOperations so that this grid is refreshed depending on the operation done on the grid being listened to. .Example Usage:

private Group<?> getPromotionsGroup() {
        return Groups.interdependentExternalGrids()
        .addExternal("CURRENT_PROMOTIONS", Externals.grid()
                .id("CURRENT_PROMOTIONS")
                .label("Current Promotions")
                ...
                .listensFor(Map.of("UPCOMING_PROMOTIONS", List.of())))
        .addExternal("UPCOMING_PROMOTIONS", Externals.grid()
                .id("UPCOMING_PROMOTIONS")
                .label("Upcoming Promotions")
                ...
                .listensFor(Map.of("CURRENT_PROMOTIONS", List.of(GridExternal.GridListenerOperations.ON_CREATE))));
}
  • Added the readOnSuccess attribute to ModalFormAction to specify whether a re-fetch should be executed after the modal form action is successfully executed. The read endpoint that is configured for the grid will be used to trigger this re-fetch.

Messaging Common Release Notes

2.0.5

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Notable Changes
  • Introduced RoundRobinRetryScheduler to manage retry handlers more efficiently.

    • Addresses resource exhaustion by using a fixed-size thread pool instead of creating a dedicated thread per handler.

    • In the past, thread counts for retry scheduling could get up to ~2600. This should now be reduced to a constant of 30 (the default).

    • Ensures fairness via a round-robin scheduling algorithm, preventing "noisy" handlers from monopolizing resources.

    • Implements a non-blocking design where the scheduler skips full pools and retries on the next cycle.

  • Added "Burst Mode" support for high-load scenarios.

    • Allows handlers to request immediate execution on a dedicated "Burst Pool" when a full page of records is processed.

    • Helps drain large backlogs quickly without starving other handlers in the main fair pool.

  • Added new configuration properties for tuning the retry scheduler and burst mode behavior.

Offer Client Release Notes

2.0.4-GA

  • Add support for offer targeting a specific subscription flow. See Subscription Offer Configuration.

  • Add Adjustment#isExistingAdjustment flag to indicate if it’s an existing adjustment for post-purchase subscription flows. Defaults to false.

  • Add new Dtos to support targeting former and active subscribers

  • Record discount method type and amount on OfferRef

Order Client Release Notes

2.0.4-GA

  • Introduced the RETAIL_DELIVERY_FEE order item type

  • Introduced fee refund concepts to the ReturnAuthorization domain

  • Forward-compatibility to support new concepts from Order Services 2.2.0, Order Operation Services 2.2.0

    • Introduced IncludedFee to represent a fee that is included in actions against an OrderFulfillment such as capturing funds or cancelling (refunding).

      • Stored as OrderFulfillment#includedExtraFees

  • Added CancellationPolicyRef to the OrderItem domain. See Subscription Cancellation for more information on cancellation policies.

  • Added support for the Spring boot 3.3/3.5

Order Common Release Notes

2.0.4-GA

  • Introduced the CancellationPolicyRef domain to represent the data structure describing a reference to a Cancellation Policy and where it came from

  • Added helper method to DefaultFreeTrialLengthUnits to convert FreeTrialLengthUnits to ChronoUnit

  • Added a Adjustment#isExistingAdjustment flag to indicate an existing offer

  • Introduced CurrencyConversionDetail domain to represent the adjustment details of the exchange rate used to convert a MonetaryAmount from one currency to another

  • Added the new attributes to the OfferRef domain to better describe the style of offer, its discount rate, & how it interacts with other offers

Payment Gateway Common Release Notes

2.0.5-GA

  • Introduced new fields on PaymentInfo

    • addressByType

    • displayAttributes

  • Introduced new method in PaymentGatewayPaymentModificationService

    • modifyFullPaymentForCreate: When a payment is being newly created, the request information will be copied to a PaymentInfo and passed into this method. The implementation can then customize the values as needed depending on the gateway-specific requirements. All values from the modified PaymentInfo will then be mapped back onto the payment, essentially as a full replacement. For backwards compatibility, this method is called after modifyPaymentMethodProperties. Default implementation in the interface is a no-op.

  • Introduced new method in PaymentGatewayTransactionService

    • identifyTransactionReferenceIdOverrideForSubsequentTransactions: Hook used to potentially gather a transactionReferenceId override value for the payment’s subsequent transactions after the initial Authorize or Authorize-Capture transaction. Default interface implementation returns null, effectively serving as a no-op.

  • Added new ShippingCallbackValidationException to handle fulfillment callback validation errors.

  • New interface methods in PaymentGatewayConfiguration for mapping a name to the gateway’s reference ID.

    • These methods determine the name of the attribute added to the Payment for the transaction reference ID. Typically an implementation of these methods would be used to name the attribute specific to the gateway’s naming convention, i.e. ORDER_ID or CUSTOM_ID.

Pricing Client Release Notes

2.0.4-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Notable Changes
  • Forward-compatibility to support new concepts from PricingServices 2.2.0.

    • Added domain support for adjustment price details

    • Added support to store and summate adjustment data on price info

    • Added domain support to store the derived field type for price details, price info, and priceable targets

    • Added support for simpler access to tiered pricing information on price details and price info

    • Added support for pricing categories on priceable targets

    • Added a transient property to store requested currency on the PriceInfoContext domain to allow for better support of multi-currency use cases

    • Added a new price source for adjustment to a direct field

    • Added support for different frequencies each payment term

    • Added the DefaultPaymentStrategy enum which is the strategy used when paying for a target using a given price detail, whether the beginning or end of the billing period

    • Added a new domain ExchangeRateDetail to represent the details of the exchange rate used to convert a given amount from one currency to another, and added support to include this information on price details and price info when currency conversion is involved

    • Added a CancellationPolicy reference for price details and price info

    • Added priceTargetCurrencies to PriceContext

    • Allow for filtering price data by payment strategy via the PriceInfoContext

Security Common Release Notes

2.0.5-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Notable Changes
  • Optimized the space efficiency of restrictions / restricted authorities in access tokens.

Tax Common Release Notes

2.0.4-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 2.3.0-GA, support for Spring Boot 3.3 & 3.5 for all common libraries has been added.

Notable Changes
  • Introduced the feeAmount field to the TaxInfo representation.

Translation Common Release Notes

2.0.5-GA

  • Extracted the default updateField translation-applying logic out of TranslationPostMapperMember into a new reusable TranslationHelper bean. The existing updateField method is marked deprecated and unused. Clients should move any overrides of the updateField-related methods out of their TranslationPostMapperMember customizations and into an extension of TranslationHelper.

  • Introduced a new EntityTranslationApplier concept that can be used to override the default translation-applying logic on a per-entity, per-field basis. TranslationPostMapperMember will delegate to these if available.

  • Introduced a new findByEntityTypeAndEntityFieldStartsWithAndEntityIdInAndLocaleIn method in TranslationRepository and TranslationEntityService to allow fetching translations matching a certain field

  • Introduced a new TranslationFieldTransformerHandler concept that can be used to override the default 'path transformation' logic executed in DefaultTranslationEntityService.bulkReplaceTranslationsForEntityInLocale on a per-entity, per-field basis

  • Updated various methods in DefaultTranslationEntityService to protected instead of private for the sake of extensibility in the rare circumstance a client may wish to directly extend that service

Workflow Client Release Notes

1.0.0-GA

  • Initial Release

Main Components
  • com.broadleafcommerce.orchestration.service.provider.external.WorkflowProvider

    • Support remote REST API calls to WorkflowServices

  • com.broadleafcommerce.orchestration.service.provider.domain.WorkflowRequest

    • Embodies data related to requesting a remote workflow execution

  • com.broadleafcommerce.orchestration.client.WorkflowInformation

    • Reference information about the currently executing workflow

  • com.broadleafcommerce.orchestration.client.SimpleWorkflow

    • Responsible for launching execution of activities at any point in the list

  • com.broadleafcommerce.orchestration.client.SimpleActivity

    • Prescribed interface to support initiation of a business task as part of an ordered/orchestrated workflow

Utility Services

Microservices Gateways Release Notes for 2.0.5

Features/Notable Changes

  • Added URIs, predicates, and filters for Audit and Workflow Services for Admin and Commerce Gateways.

  • Advance base dependencies version with the spring boot 3.5 upgrade

Bug Fixes

  • Rewrote OAuth2ClientCredentialsGatewayFilterFactory to use a fully non-blocking mechanism for all logic related to obtaining a new access token

    • Previously, this filter would defer to a worker thread and execute synchronized/blocking logic to fetch the access token. In scenarios where clients had attached the Elastic APM agent to the gateway for instrumentation, the agent would cause the blocking work to be executed in an event loop thread, which would throw an error on the blocking logic.

    • With the rewrite, the flow is now fully non-blocking, and the Elastic APM agent can be used with the gateway.

    • This change should also produce a positive impact on performance for all anonymous storefront requests, as a variety of inefficiencies have now been eliminated in this flow.

  • Fixed issue where caching could not be turned off in the commerce gateway due to a lack of null-checks against CacheStateManager

  • Fixed a bug where the commerce gateway’s default properties were setting the cache type to 'simple', even though the actual configuration uses and configures Ignite. The cache type property is now removed, preventing a confusing contradictory combination of defaults.

  • Rewrote the application token resolution logic in commerce gateway to fix a few different bugs

    • Since ThreadLocals are unsafe in reactive flows, removed the use of CacheContext and added a custom CacheStateConfigurer to force-set the cache enabled value to true for the relevant app token cache flow

    • Rewrote the ApplicationTokenGatewayFilterFactory and ExternalApplicationResolverService to move blocking cache interactions off of the event loop thread and into worker threads. Furthermore, added strong concurrency control to enforce only one thread at a time will make a request for any given application token, fixing a bug where multiple requests could have previously been fired off all at once to the downstream service.

  • Enabled ApplicationToken filter for services that were missing them

    • audit

    • billing

    • catalog

    • notification

    • workflow

  • Enabled Anonymous filter for services that were missing them

    • fulfillment

    • offer

    • orderoperation

    • pricing

Security

This release contains security updates. For more details, please visit Broadleaf Security and review the security advisories page.