Broadleaf Microservices
  • v1.0.0-latest-prod

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.