Self-Managed Commerce 8.7.x Upgrade Notes
Upgrade notes
The upgrading Elastic Path guide provides general instructions on upgrading Elastic Path projects. This page provides details about specific changes that may be required to your customizations when upgrading to Self-Managed Commerce 8.7.
Changes to webapp startup instructions for local development
There are several changes to the instructions for starting the Self-Managed Commerce webapps on a local development machine:
- The command to start the webapps is now
mvn clean package cargo:run
instead ofmvn clean tomcat8:run-war
. - The Commerce Manager webapp is now started from the
extensions/cm/ext-cm-modules/ext-cm-webapp
folder instead ofextensions/cm/ext-cm-modules/ext-cm-webapp-runner
. - JVM parameters passed to the
mvn clean package cargo:run
command are no longer passed to the webapps.- To enable remote debugging, add the
-Pdebug
option. - To enable
automation-id
HTML attributes for Selenium Tests, add the-PenableUITests
option to the Commerce Manager webapp startup command. - To add other JVM parameters, add them to the
systemProperties
section of thecargo-maven3-plugin
configuration in the webapppom.xml
file.
- To enable remote debugging, add the
- Webapps (including ActiveMQ) must be restarted after rebuilding the source code. They will stop working properly if the source code is rebuilt without restarting.
- Wait at least 30 seconds after stopping a webapp before restarting it, otherwise you may receive a "Port already in use" error at startup.
For more details, see Running Applications.
Jakarta migration
The Self-Managed Commerce 8.7 Jakarta migration has been released in three commits:
8.7.0.20250730203654-29b4ea
: This commit contains all Self-Managed Commerce 8.7 changes EXCEPT the Jakarta migration.Jakarta migration in replacer output
: This commit contains the required Maven POM changes to support the Jakarta migration, plus plugins that modify the source code at build time to support the Jakarta migration. This commit exists mainly to make it easier for developers to review the changes that have been made to support the Jakarta migration, without the boilerplate "import" updates that make it hard to review.Jakarta migration in committed source code
: This commit contains all the required changes to the source code to support the Jakarta migration.
note
This functionality is being delivered in three commits to improve the upgrade experience for developers. Do not deploy to production unless the Jakarta migration in committed source code
commit (or later) has been consumed.
We recommend following this process to upgrade your custom Self-Managed Commerce repository to Self-Managed Commerce 8.7:
note
Make sure to install the Self-Managed Commerce Upgrader tool before following this process.
Merge the
release/8.7.x-pre-jakarta
branch into your custom Self-Managed Commerce repo branch.smc-upgrader 8.7.x-pre-jakarta
Follow all steps (except Production Cutover) of the Upgrade Guide. This allows you to ensure that all of your customizations are compatible with the Self-Managed Commerce 8.7 updates, before applying the Jakarta migration.
Merge the
release/8.7.x
branch into your custom Self-Managed Commerce repo branch.smc-upgrader 8.7.x
Run a build with the
mvn clean install -DskipAllTests -Pjakarta-source-migration
command, which will run the replacer plugins which apply many of the changes to your custom source code to support the Jakarta migration.Review the changes and ensure that the source compiles without errors. Commit the changes to your custom Self-Managed Commerce repo branch.
Follow all steps of the Upgrade Guide after the "Merge and fix compilation issues" step.
Review the sections below for advice about common issues that you may encounter when applying the Jakarta migration commits:
Automated Jakarta Source Migration
Some parts of your custom code (Java, Groovy, and XML files) will be automatically migrated to use the Jakarta namespace when you build using the jakarta-source-migration
profile. This includes:
import com.fasterxml.jackson.jaxrs.base
-->import com.fasterxml.jackson.jakarta.rs.base
import com.fasterxml.jackson.jaxrs.json
-->import com.fasterxml.jackson.jakarta.rs.json
import com.sun.xml.bind.annotation.XmlLocation
-->import org.glassfish.jaxb.core.annotation.XmlLocation
import com.sun.xml.bind.Locatable
-->import org.glassfish.jaxb.core.Locatable
import javax.activation
-->import jakarta.activation
import javax.annotation
-->import jakarta.annotation
import javax.inject
-->import jakarta.inject
import javax.jms
-->import jakarta.jms
import javax.json
-->import jakarta.json
import javax.mail
-->import jakarta.mail
import javax.persistence
-->import jakarta.persistence
import javax.servlet
-->import jakarta.servlet
import javax.transaction
-->import jakarta.transaction
import javax.validation
-->import jakarta.validation
import javax.ws.rs
-->import jakarta.ws.rs
import javax.xml.bind
-->import jakarta.xml.bind
import javax.xml.soap
-->import jakarta.xml.soap
import org.apache.commons.mail.Email
-->import org.apache.commons.mail2.jakarta.Email
import org.apache.commons.mail.EmailAttachment
-->import org.apache.commons.mail2.jakarta.EmailAttachment
import org.apache.commons.mail.EmailException
-->import org.apache.commons.mail2.core.EmailException
import org.apache.commons.mail.HtmlEmail
-->import org.apache.commons.mail2.jakarta.HtmlEmail
import org.apache.commons.mail.SimpleEmail
-->import org.apache.commons.mail2.jakarta.SimpleEmail
javax.jms.ConnectionFactory
-->jakarta.jms.ConnectionFactory
javax.jms.JMSException
-->jakarta.jms.JMSException
javax.persistence.FlushModeType
-->jakarta.persistence.FlushModeType
javax.persistence.PersistenceException
-->jakarta.persistence.PersistenceException
javax.persistence.Query
-->jakarta.persistence.Query
javax.validation.constraints.NotNull.message
-->jakarta.validation.constraints.NotNull.message
javax.validation.constraints.Size.message
-->jakarta.validation.constraints.Size.message
javax.validation.Validator
-->jakarta.validation.Validator
Elastic Path hosted dependencies
Some Maven dependencies required by Self-Managed Commerce 8.7 do not exist in Maven Central. Therefore, we are hosting these dependencies in the Elastic Path public Nexus repository. The following third-party dependencies are hosted by Elastic Path:
org.jvnet.mock-javamail:mock-javamail:2.2
org.eclipse.gemini.blueprint:gemini-blueprint-core:4.0.0-atlassian-5
org.eclipse.gemini.blueprint:gemini-blueprint-io:4.0.0-atlassian-5
org.eclipse.gemini.blueprint:gemini-blueprint-extender:4.0.0-atlassian-5
Jakarta transformed dependencies
Some Maven dependencies required by Self-Managed Commerce 8.7 do not support the Jakarta namespace. Therefore, these dependencies have been wrapped in a custom artifact that is transformed to use the Jakarta namespace using the org.eclipse.transformer:transformer-maven-plugin
Maven plugin. The following modules have been transformed:
org.apache.activemq:activemq-broker
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-broker
org.apache.activemq:activemq-jms-pool
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-jms-pool
org.apache.activemq:activemq-pool
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-pool
org.apache.activemq:activemq-web-console
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-web-console
org.apache.solr:solr-core
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.solr.solr-core
org.apache.velocity.tools:velocity-tools-view
-->com.elasticpath.jakarta.transformed.org.apache.velocity.tools.velocity-tools-view
org.eclipse.sisu:org.eclipse.sisu.inject.extender
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject.extender
(within theapi-platform
project)org.eclipse.sisu:org.eclipse.sisu.inject
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject
(within theapi-platform
project)org.ops4j:peaberry
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.ops4j.peaberry
(within theapi-platform
project)
JSON dependency in bundles
If you encounter a ClassNotFoundException
on a org.json
class, you may need to make the following changes:
Add this dependency to your bundle pom.xml
:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
Add org.json
to the list of Import-Package
instructions in the maven-bundle-plugin
definition of your bundle pom.xml
.
OSGi dependencies
Any modules that declare a dependency on org.osgi.core
or org.osgi.annotation
should be updated to use the osgi.core
and osgi.annotation
dependencies instead.
For example:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>${osgi.core.version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.annotation</artifactId>
<version>${osgi.core.version}</version>
</dependency>
Becomes:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
<version>${osgi.core.version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.annotation</artifactId>
<version>${osgi.core.version}</version>
</dependency>
The "EP Core Tool Selenium Cucumber Tests" module has been moved to extensions
The commerce-engine/core/ep-core-tool-itests/selenium
module has been moved to extensions/system-tests/ep-core-tool-itests/selenium
. Any custom tests in this module should be moved to the new location.
Spring 5.x to 6.x changes
All @Autowired
annotations with corresponding @Named
annotations should use with @Qualifier
instead.
For example:
@Autowired
@Named("cachedModifierFieldValidationService")
private ModifierFieldValidationService modifierFieldValidationService;
Becomes:
@Autowired
@Qualifier("cachedModifierFieldValidationService")
private ModifierFieldValidationService modifierFieldValidationService;
Previously, beans declared using constructor injection could skip the definition of some or all of the constructor arguments if a bean was found in the Spring context with the same name as the constructor parameter. However, in Spring 6.x, all the constructor arguments must be defined explicitly, and they must be defined in the same order as the class constructor parameters.
For example, the following bean definition works in Spring 5.x even though the PaymentAPIWorkflowImpl
class has three constructor parameters:
<bean id="paymentAPIWorkflow" class="com.elasticpath.provider.payment.workflow.impl.PaymentAPIWorkflowImpl">
<constructor-arg name="beanFactory" ref="coreBeanFactory"/>
</bean>
However, in Spring 6.x, the bean must be defined as follows:
<bean id="paymentAPIWorkflow" class="com.elasticpath.provider.payment.workflow.impl.PaymentAPIWorkflowImpl">
<constructor-arg name="beanFactory" ref="coreBeanFactory"/>
<constructor-arg name="paymentProviderService" ref="paymentProviderService" />
<constructor-arg name="paymentProviderConfigurationService" ref="paymentProviderConfigurationService"/>
</bean>
If you have custom EhCache bean definitions, Spring 6.x has removed the org.springframework.cache.ehcache.EhCacheFactoryBean
class, so you will need switch to our legacy class definition:
Update any bean definitions that use org.springframework.cache.ehcache.EhCacheFactoryBean
to use com.elasticpath.cache.ehcache.spring.legacy.EhCacheFactoryBean
. For example:
<bean id="shippingCalculationResultCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
...etc...
</bean>
Becomes:
<bean id="shippingCalculationResultCache" class="com.elasticpath.cache.ehcache.spring.legacy.EhCacheFactoryBean">
...etc...
</bean>
Any org.springframework.cache.ehcache
imports in the list of Import-Package
instructions in the maven-bundle-plugin
definition of your bundle pom.xml
s will need to be replaced with com.elasticpath.cache.ehcache.spring.legacy
.
Camel 3.x to 4.x changes
Some Camel dependencies in your pom.xml
files will need to be updated to use the new Camel 4.x dependencies:
- Replace
org.apache.camel:camel-swagger-java
withorg.apache.camel:camel-openapi-java
. - Replace
org.apache.camel:camel-test
withorg.apache.camel:camel-test-junit5
. - Replace
org.apache.camel:camel-test-spring
withorg.apache.camel:camel-test-spring-junit5
.
Within your test class, replace all org.apache.camel.test.junit4.CamelTestSupport
imports with org.apache.camel.test.junit5.CamelTestSupport
.
Additionally, you will need to migrate all tests in modules using the camel-test
dependency to JUnit 5. See Added support for JUnit 5 for more information.
For any test classes extending CamelTestSupport
, the following changes are required:
- Rename the
setUp
method withsetUpTest
andtearDown
method withtearDownTest
. Also remove the@Override
annotation from these methods. - If your test uses
JUnitRuleMockery
to define a Camel route, you will need to remove this field from your class and instead define your route by overriding thecreateRouteBuilder
method.
The signature of the Camel DataFormat
interface has changed, so any mock expectations around DataFormat
method calls will need to be updated:
when(eventMessageDataFormat.unmarshal(any(Exchange.class), any(InputStream.class)))
Becomes:
when(eventMessageDataFormat.unmarshal(any(Exchange.class), any(Object.class)))
verify(eventMessageDataFormat).unmarshal(any(), any());
Becomes:
verify(eventMessageDataFormat).unmarshal(any(Exchange.class), any(Object.class));
given(eventMessageDataFormat.unmarshal(any(Exchange.class), any(InputStream.class)))
Becomes:
given(eventMessageDataFormat.unmarshal(any(Exchange.class), any(Object.class)))
verify(eventMessageDataFormat).unmarshal(any(), any());
Becomes:
verify(eventMessageDataFormat).unmarshal(any(Exchange.class), any(Object.class));
Remote Application Platform 3.5 to 4.2 changes
Any custom Commerce Manager modules will require some changes to the Require-Bundle
definitions in the MANIFEST.MF
files.
- Replace
org.eclipse.ui.*
withorg.eclipse.rap.ui.*
(where*
represents any package in theorg.eclipse.ui
package hierarchy). - Replace
org.eclipse.jface.*
withorg.eclipse.rap.jface.*
(where*
represents any package in theorg.eclipse.jface
package hierarchy). - Replace
javax.servlet
withjakarta.servlet-api
.
Additionally, any uses of the org.eclipse.core.databinding.beans.BeansObservables
class must be replaced with org.eclipse.core.databinding.beans.typed.BeanProperties
. You will also need to add org.eclipse.core.databinding.property
to your bundle's Require-Bundles
definitions.
Hamcrest 1.3 to 2.2 changes
Any dependencies on org.hamcrest:hamcrest-core:1.3
should be replaced with org.hamcrest:hamcrest:2.2
.
For example:
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>compile</scope>
</dependency>
Becomes:
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>compile</scope>
</dependency>
Dependency updates required
Some dependencies in your pom.xml
files will need to be updated to use the new dependencies:
javax.annotation:javax.annotation-api
-->jakarta.annotation:jakarta.annotation-api
javax.inject:javax.inject
-->jakarta.inject:jakarta.inject-api
javax.interceptor:javax.interceptor-api
-->jakarta.interceptor:jakarta.interceptor-api
javax.json:javax.json-api
-->jakarta.json:jakarta.json-api
javax.servlet:javax.servlet-api
-->jakarta.servlet:jakarta.servlet-api
javax.transaction:javax.transaction-api
-->jakarta.transaction:jakarta.transaction-api
javax.validation:validation-api
-->jakarta.validation:jakarta.validation-api
javax.ws.rs:javax.ws.rs-api
-->jakarta.ws.rs:jakarta.ws.rs-api
org.apache.activemq:activemq-broker
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-broker
org.apache.activemq:activemq-client
->org.apache.activemq:activemq-client-jakarta
org.apache.activemq:activemq-jms-pool
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-jms-pool
org.apache.activemq:activemq-pool
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-pool
org.apache.activemq:activemq-web-console
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-web-console
org.apache.commons:commons-email
-->org.apache.commons:commons-email2-core
org.apache.solr:solr-core
-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.solr.solr-core
org.apache.velocity.tools:velocity-tools-view
-->com.elasticpath.jakarta.transformed.org.apache.velocity.tools.velocity-tools-view
org.eclipse.sisu:org.eclipse.sisu.inject.extender
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject.extender
org.eclipse.sisu:org.eclipse.sisu.inject
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject
org.glassfish:jakarta.el
-->org.glassfish.expressly:expressly
org.glassfish:javax.el
-->org.glassfish.expressly:expressly
org.glassfish:javax.json
-->jakarta.json:jakarta.json-api
andorg.eclipse.parsson:parsson
org.ops4j:peaberry
-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.ops4j.peaberry
Additionally, the following dependencies can be removed:
com.sun.xml.bind:jaxb-core
- its functionality is provided bycom.sun.xml.bind:jaxb-impl
com.sun.activation:jakarta.activation
Java and bundle import changes required
Within your Java and Groovy classes, some imports will need to be updated to use the new imports. Additionally, Import-Package
declarations in your pom.xml
files will need to be updated to use the new imports. Some of these will be handled automatically by the Automated Jakarta Source Migration.
com.fasterxml.jackson.jaxrs.base
-->com.fasterxml.jackson.jakarta.rs.base
com.fasterxml.jackson.jaxrs.json
-->com.fasterxml.jackson.jakarta.rs.json
com.sun.xml.bind.annotation.XmlLocation
-->org.glassfish.jaxb.core.annotation.XmlLocation
com.sun.xml.bind.Locatable
-->org.glassfish.jaxb.core.Locatable
javax.activation
-->jakarta.activation
javax.annotation
-->jakarta.annotation
javax.inject
-->jakarta.inject
javax.jms
-->jakarta.jms
javax.json
-->jakarta.json
javax.mail
-->jakarta.mail
javax.persistence
-->jakarta.persistence
javax.servlet
-->jakarta.servlet
javax.transaction
-->jakarta.transaction
javax.validation
-->jakarta.validation
javax.ws.rs
-->jakarta.ws.rs
javax.xml.bind
-->jakarta.xml.bind
javax.xml.soap
-->jakarta.xml.soap
org.apache.commons.mail.Email
-->org.apache.commons.mail2.jakarta.Email
org.apache.commons.mail.EmailAttachment
-->org.apache.commons.mail2.jakarta.EmailAttachment
org.apache.commons.mail.EmailException
-->org.apache.commons.mail2.core.EmailException
org.apache.commons.mail.HtmlEmail
-->org.apache.commons.mail2.jakarta.HtmlEmail
org.apache.commons.mail.SimpleEmail
-->org.apache.commons.mail2.jakarta.SimpleEmail
org.springframework.mock.jndi.SimpleNamingContextBuilder
-->org.springframework.mock.jndi.legacy.SimpleNamingContextBuilder
Dropped certification for MySQL 5.7
Starting with Self-Managed Commerce 8.7.0, MySQL 5.7 is no longer officially supported. MySQL 5.7 reached its end-of-life on October 31, 2023. We recommend that customers upgrade to MySQL 8.0 or MySQL 8.4.
For a complete list of supported MySQL versions, see Supported Technologies.
Custom email templates
If your project has customized email templates, you will need to update them to use the new entity classes.
Responsibility for rendering and delivery of notifications is now handled by the Notification Events extension point. By default, the embedded VelocitySMTPEmailSender
extension renders emails using Apache Velocity and use SMTP to send out the emails, in the same way as previous versions. However, the properties that are passed into the email templates have changed from JPA domain classes to Extension Point Framework entity classes.
The table below shows the changes to the properties that are passed into the email templates:
Old Property | Old Property Type | New Property | New Property Type |
---|---|---|---|
locale | Locale | locale | Locale |
store | StoreImpl | store | XPFStore |
customer | CustomerImpl | customer | XPFCustomer |
order | OrderImpl | order | XPFOrder |
orderHolds | List<OrderHold> | order.holds | List<XPFOrderHold> |
orderItemFormBeanMap | Map<Long, List<? extends OrderItemPresentationBean>> | order.shipments.iterator.next.lineItems | List<XPFOrderItem> |
orderItemFormBeanList | List<? extends OrderItemPresentationBean> | shipment.lineItems | List<XPFOrderItem> |
orderReturn | OrderReturnImpl | orderReturn | XPFOrderReturn |
orderShipment | OrderShipmentImpl | shipment | XPFOrderShipment |
The classes responsible for injecting properties into each email template are shown in the table below:
Notification Type | Converter Class |
---|---|
CM_USER_PASSWORD_RESET | CmUserPasswordResetNotificationContextConverter |
CM_USER_PASSWORD_CHANGED | CmUserPasswordChangedNotificationContextConverter |
CM_USER_CREATED | CmUserCreatedNotificationContextConverter |
CUSTOMER_PASSWORD_CHANGED | CustomerPasswordChangedNotificationContextConverter |
CUSTOMER_PASSWORD_FORGOTTEN | CustomerPasswordForgottenNotificationContextConverter |
CUSTOMER_REGISTRATION | CustomerRegistrationNotificationContextConverter |
CUSTOMER_ANONYMOUS_REGISTRATION | CustomerAnonymousRegistrationNotificationContextConverter |
IMPORT_JOB | ImportJobCompletedNotificationContextConverter |
GIFT_CERTIFICATE | GiftCertificateNotificationContextConverter |
ORDER_CONFIRMATION | OrderConfirmationNotificationContextConverter |
ORDER_CANCELLATION | OrderCancellationNotificationContextConverter |
ORDER_SHIPMENT_SHIPPED | OrderShipmentShippedNotificationContextConverter |
ORDER_SHIPMENT_RELEASE_FAILED | OrderShipmentReleaseFailedNotificationContextConverter |
ORDER_RETURN_EXCHANGE | OrderReturnExchangeNotificationContextConverter |
ORDER_ON_HOLD | OrderOnHoldNotificationContextConverter |
Additionally, the GRMA.txt.vm
and GRMA.html.vm
templates have been removed, and are now handled by the RMA.txt.vm
and RMA.html.vm
templates, which can differentiate between physical and non-physical returns using the orderReturn.physicalReturn
property.
The Velocity template engine has also been configured to use runtime.references.strict
mode. This means that Velocity will now throw an exception if a reference such as a property or method cannot be found. If you have any invalid references in your templates, these will need to be corrected.
If you have created custom email types, the way this is implemented will need to be updated. See Adding a new notification type for more details.
For a complete list of the available notification types and the properties they support, see Notification Events extension point.
Custom promotion actions and conditions
Custom promotion rule delegate action methods
Previously, there was an inconsistency in how the promotion rule actions were applied. For shopping cart promotions, discount classes were used (extending AbstractDiscountImpl
), but for catalog and shipping promotions, special methods were implemented in PromotionRuleDelegateImpl
. Now, all discount action methods have been moved out of PromotionRuleDelegateImpl
and into their own discount type classes. Therefore, any custom discount action methods within PromotionRuleDelegateImpl
or an extension of this class will need to be moved into custom discount type classes.
For example,
@Override
public void applyCatalogCurrencyDiscountAmount(final long ruleId, final long actionId, final Price price, final String cartCurrencyCode,
final String ruleCurrencyCode, final String amount) {
if (LOG.isTraceEnabled()) {
LOG.trace("applyCatalogCurrencyDiscountAmount rule {} price {} cartCurrency {} ruleCurrency {} amount {}", ruleId, price,
cartCurrencyCode, ruleCurrencyCode, amount);
}
if (!cartCurrencyCode.equals(ruleCurrencyCode)) {
return;
}
final Money discount = stringAmountToMoney(amount, Currency.getInstance(cartCurrencyCode));
// Apply the discount to the product
discountPriceByAmount(ruleId, actionId, discount.getAmount(), price);
}
Becomes:
/**
* Applies a discount of a specified amount to a catalog.
*/
public class CatalogAmountDiscountImpl extends AbstractDiscountImpl<CatalogDiscountItemContainer> {
private static final long serialVersionUID = -4521436188919978665L;
private static final Logger LOG = LoggerFactory.getLogger(CatalogAmountDiscountImpl.class);
private static final PromotionActionParameters[] PARAMETER_KEYS = new PromotionActionParameters[] { PromotionActionParameters.DISCOUNT_AMOUNT,
PromotionActionParameters.RULE_CURRENCY_CODE};
private final String amount;
private final String ruleCurrencyCode;
/**
* Constructor which accepts a map of discount parameters.
* This constructor extracts values from the provided parameter map using predefined keys.
*
* @param discountParameters a map containing discount parameter values keyed by
* {@link PromotionActionParameters#getDataValue()} values.
*/
public CatalogAmountDiscountImpl(final Map<String, Object> discountParameters) {
super(discountParameters);
validateParameters(discountParameters);
this.amount = (String) discountParameters.get(PromotionActionParameters.DISCOUNT_AMOUNT.getDataValue());
this.ruleCurrencyCode = (String) discountParameters.get(PromotionActionParameters.RULE_CURRENCY_CODE.getDataValue());
}
@Override
protected BigDecimal doApply(final boolean actuallyApply, final CatalogDiscountItemContainer discountItemContainer) {
if (LOG.isTraceEnabled()) {
LOG.trace("applyCatalogCurrencyDiscountAmount rule {} price {} cartCurrency {} ruleCurrency {} amount {}",
getRuleGuid(), discountItemContainer.getPrice(),
discountItemContainer.getPrice().getCurrency(), ruleCurrencyCode, amount);
}
if (!discountItemContainer.getPrice().getCurrency().getCurrencyCode().equals(ruleCurrencyCode)) {
return BigDecimal.ZERO;
}
BigDecimal discountAmount = new BigDecimal(amount);
// Apply the discount to the product
for (PriceTier priceTier : discountItemContainer.getPrice().getPriceTiers().values()) {
final BigDecimal prePromotionAmount = priceTier.getPrePromotionPrice();
final BigDecimal discountedAmount = prePromotionAmount.subtract(discountAmount);
final DiscountRecord discountRecord =
new CatalogItemDiscountRecordImpl(getRuleGuid(), getActionId(), discountAmount);
if (priceTier.setComputedPriceIfLower(discountedAmount)) {
priceTier.clearDiscountRecords();
priceTier.addDiscountRecord(discountRecord);
}
}
return discountAmount;
}
/**
* Return the array of the required parameter keys for the rule.
*
* @return an array of String of the required parameter keys for the rule.
*/
@Override
public PromotionActionParameters[] getParameterKeys() {
return Stream.concat(Arrays.stream(super.getParameterKeys()), Arrays.stream(PARAMETER_KEYS))
.toArray(PromotionActionParameters[]::new);
}
}
The following methods in PromotionRuleDelegateImpl
were moved into their own discount type classes:
applyCatalogCurrencyDiscountAmount
-->CatalogAmountDiscountImpl
applyCatalogCurrencyDiscountPercent
-->CatalogPercentDiscountImpl
applyShippingDiscountAmount
-->ShippingAmountDiscountImpl
applyShippingDiscountPercent
-->ShippingPercentDiscountImpl
Custom discount types
Existing custom discount type classes (extending AbstractDiscountImpl
) for shopping cart promotions, will need to be updated.
First, you will need to define a generic type parameter for the AsbtractDiscountImpl
class that is being extended. The generic type parameter should either be set to ShoppingCartDiscountItemContainer
(for shopping cart discounts) or CatalogDiscountItemContainer
(for catalog discounts).
For example:
public class CartSkuAmountDiscountImpl extends AbstractDiscountImpl {
Becomes:
public class CartSkuAmountDiscountImpl extends AbstractDiscountImpl<ShoppingCartDiscountItemContainer> {
Secondly, the constructor parameters need to be updated to support the AbstractDiscountImpl
constructor parameter changes: long ruleId
was changed to String ruleGuid
and long actionId
was changed to String actionId
.
For example:
public CartSkuAmountDiscountImpl(final String ruleElementType,
final long ruleId, final long actionId, final String skuCode, final String amount,
final String exceptions, final int availableDiscountQuantity) {
super(ruleElementType, ruleId, actionId, availableDiscountQuantity);
this.amount = amount;
this.exceptionStr = exceptions;
this.skuCode = skuCode;
}
Becomes:
public CartSkuAmountDiscountImpl(final String ruleElementType,
final String ruleGuid, final String actionId, final String skuCode, final String amount,
final String exceptions, final int availableDiscountQuantity) {
super(ruleElementType, ruleGuid, actionId, availableDiscountQuantity);
this.amount = amount;
this.exceptionStr = exceptions;
this.skuCode = skuCode;
}
Similarly, any discount type classes with a ShoppingItem shoppingItem
parameter need to be updated to String shoppingItemGuid
. They can then retrieve the ShoppingItem
within the doApply
method by calling discountItemContainer.getShoppingItemByGuid(shoppingItemGuid);
.
Additionally, the doApply
method needs to be updated to accept the generic type parameter instead of DiscountItemContainer
.
For example:
public BigDecimal doApply(final boolean actuallyApply, final DiscountItemContainer discountItemContainer) {
Becomes:
public BigDecimal doApply(final boolean actuallyApply, final ShoppingCartDiscountItemContainer discountItemContainer) {
Also, a new getParameterKeys
method needs to be defined. This should return an array of PromotionActionParameters
that are required for the discount type.
Example:
@Override
public PromotionActionParameters[] getParameterKeys() {
return Stream.concat(Arrays.stream(super.getParameterKeys()), Arrays.stream(PARAMETER_KEYS))
.toArray(PromotionActionParameters[]::new);
}
Finally, for each custom discount type class, extend the DiscountImplementationType
extensible enum class to define the discount type and link it to the discount type class. This will be used by your custom rule action class to determine which discount type to use.
For example:
/** Catalog amount discount ordinal. */
public static final int CATALOG_AMOUNT_DISCOUNT_ORDINAL = 1;
/** Catalog amount discount. */
public static final DiscountImplementationType CATALOG_AMOUNT_DISCOUNT = new DiscountImplementationType(
CATALOG_AMOUNT_DISCOUNT_ORDINAL, "catalogAmountDiscount", CatalogAmountDiscountImpl.class);
For more information about extensible enum classes, see Extensible Enums.
Custom rule actions
Existing custom rule action classes (extending AbstractRuleActionImpl
) will need to be updated.
The way that the getRuleCode
method is implemented needs to be updated to use the new createRuleCode
method from the AbstractRuleActionImpl
class, instead of generating the Drools Rule Language code using a StringBuilder
. As a result, these methods only need to worry about passing the correct parameters that will be injected into the XPFPromotionAction
class.
For example:
@Override
@Transient
@SuppressWarnings({"PMD.ConsecutiveLiteralAppends", "PMD.AppendCharacterWithChar"})
public String getRuleCode() throws EpDomainException {
validate();
StringBuilder sbf = new StringBuilder();
sbf.append("\n\t applicableDiscounts.add(new CartAnySkuAmountDiscountImpl(\"").append(RULE_ELEMENT_TYPE).append("\", ");
sbf.append("\"").append(this.getRuleGuid()).append("\", \"");
sbf.append(this.getUidPk()).append("\", \"");
sbf.append(this.getParamValue(RuleParameter.DISCOUNT_AMOUNT_KEY));
sbf.append("\", \"").append(this.getExceptionStr());
sbf.append("\", delegate.calculateAvailableDiscountQuantity(cart, \"");
sbf.append(this.getRuleGuid()).append("\", ");
sbf.append(this.getParamValue(RuleParameter.NUM_ITEMS_KEY));
sbf.append(")));\n");
return sbf.toString();
}
Becomes:
@Override
@Transient
@SuppressWarnings({"PMD.ConsecutiveLiteralAppends", "PMD.AppendCharacterWithChar"})
public String getRuleCode() throws EpDomainException {
validate();
Map<String, String> parameters = Map.of(
PromotionActionParameters.DISCOUNT_AMOUNT.getDataValue(), addQuotes(this.getParamValue(RuleParameter.DISCOUNT_AMOUNT_KEY)),
PromotionActionParameters.EXCEPTION_STRING.getDataValue(), addQuotes(this.getExceptionStr()),
PromotionActionParameters.AVAILABLE_DISCOUNT_QUANTITY.getDataValue(), getRuleCodeForCalculateAvailableDiscountQuantity());
return createRuleCode(parameters);
}
Additionally, the AbstractRuleActionImpl
abstract class now requires that extending classes implement a getDiscountImplementationType
method. This method should return the DiscountImplementationType
that is used by the AbstractRuleActionImpl
class.
@Override
@Transient
public DiscountImplementationType getDiscountImplementationType() {
return DISCOUNT_IMPLEMENTATION_TYPE;
}
Custom rule conditions
Existing custom rule condition classes (implementing RuleCondition
), will need to be updated, as well as any extensions of the PromotionRuleDelegateImpl
class.
The Drools engine context no longer has access to the discountItemContainer
object. Therefore, the getRuleCode
method needs to be updated to remove this parameter from any calls to delegate
methods and the corresponding methods in extensions of the PromotionRuleDelegateImpl
class need to be updated to remove the discountItemContainer
parameter. The most common uses of the discountItemContainer
parameter were to call the cartItemEligibleForPromotion
or calculateSubtotalOfDiscountableItemsExcluding
methods, which are not available on the PromotionRuleDelegateImpl
class.
Additionally, the variables passed into the Drools engine context have been changed from domain classes to Extension Point Framework entity classes.
Specifically, for shopping cart promotion rules:
cart
has been changed fromShoppingCart
toXPFShoppingCart
shoppingItem
has been changed fromShoppingItemImpl
toXPFShoppingItem
For catalog promotion rules:
product
has been changed fromProductImpl
toXPFProduct
productSku
has been changed fromProductSkuImpl
toXPFProductSku
As a result, your extension of PromotionRuleDelegateImpl
will need have its method signatures updated to accept the Extension Point Framework entity classes instead of the domain entity classes.
Finally, if your custom rule condition classes need access to fields in extended domain classes, you will need to extend the corresponding domain to Extension Point Framework entity converter class to inject these custom field values into the Extension Point Framework entity customData
map. Then your extension of PromotionRuleDelegateImpl
can access those fields using the getCustomData
method on the corresponding Extension Point Framework entity.
Custom HTTP Request Tag Set Populator extensions
Extensions implementing the HttpRequestTagSetPopulator
interface should be modified to call XPFHttpTagSetContext#getHttpServletRequest()
instead of the deprecated HttpServletRequestWrapper#getHttpRequest()
.
For more information, see Removed dependency on javax.servlet-api
.
Custom payment plugins
For payment plugins that specify the @BillingAddressRequired
annotation, the behavior has been changed in a couple of ways.
First, Cortex will still prompt the user to enter billing address fields on the requestinstructionsform
and paymentinstrumentform
forms, but these fields will not be validated automatically. It is now the responsibility of the payment plugin to validate the billing address fields as required. This allows the plugin to match validation with the requirements of the payment gateway. It also allows the plugin to require the billing address fields on the requestinstructionsform
but not the paymentinstrumentform
, or vice-versa.
Second, Cortex no longer saves the specified billing address on the customer's profile.
Custom Camel route builders
As previously recommended, custom Camel route builders should extend CRSCEnabledRouteBuilder
instead of RouteBuilder
. However, it is also critical that the configure
method of these route builders invoke super.configure()
, or else the core request-scoped cache will not be cleared as expected.
To ensure that this is not missed, any route builder that extends CRSCEnabledRouteBuilder
but does not invoke super.configure()
will now throw an exception at runtime. Example:
IllegalStateException: The configure method of com.elasticpath.eventprocessing.customerevents.messaging.CustomerEventRouteBuilder must call super.configure().
Custom batch jobs and custom catalog syndication builders
Previously, we had complex transactional problems with the Catalog Syndication builders which use Spring Batch. To fully resolve the issues, the Spring Batch functionality in Catalog Syndication has been replaced with the simpler AbstractBatchJob
functionality that is already used for batch jobs launched by Quartz. Additionally, some enhancements were made to the AbstractBatchJob
class to support the functionality required by the Catalog Syndication builders, and the Spring Batch dependency has been removed from the platform.
Therefore, if your code includes custom batch jobs or custom catalog syndication builders, some changes will be required:
- All imports and
extends
ofcom.elasticpath.batch.jobs.AbstractBatchJob
need to be replaced with one of the following:com.elasticpath.batch.processing.AbstractBatchJob
: If thefetchBatchOfRecords
andgetBatchProcessor
methods are being overridden.com.elasticpath.batch.processing.AbstractJPANamedQueryBatchJob
: If thegetJPANamedQuery
andgetJPQNamedQueryParameters
methods are being overridden.com.elasticpath.batch.processing.AbstractJPANamedQueryListParameterBatchJob
: If thegetJPANamedQuery
,getJPQNamedQueryParameters
, andgetJPANamedQueryListParameters
methods are being overridden.
- All imports of
com.elasticpath.batch.jobs.impl.AbstractEnableJob
need to be replaced withcom.elasticpath.batch.processing.AbstractJPANamedQueryBatchJob
. - All calls to
AbstractBatchJob#setEnabledProvider
need to be replaced withAbstractBatchJob#setEnabledProvider
. - All calls to
AbstractBatchJob#setConfigBatchSize
need to be replaced withAbstractBatchJob#setBatchSizeProvider
. - All overrides of
AbstractJPANamedQueryBatchJob#getBatchJPQLQuery
need to be renamed toAbstractJPANamedQueryBatchJob#getJPANamedQuery
. - All overrides of
AbstractJPANamedQueryBatchJob#getParameters
need to be renamed toAbstractJPANamedQueryBatchJob#getJPANamedQueryParameters
. - All imports of
com.elasticpath.batch.jobs.AbstractBatchProcessor
need to be replaced withcom.elasticpath.batch.processing.AbstractBatchProcessor
. - All imports of
com.elasticpath.catalog.batch.processing.ExecuteBatchJob
need to be replaced withcom.elasticpath.batch.processing.ExecuteBatchJob
. - All imports of
com.elasticpath.catalog.batch.processing.OpenCloseOperations
need to be replaced withcom.elasticpath.batch.processing.OpenCloseOperations
. - All imports of
com.elasticpath.catalog.batch.CatalogJobRunner
need to be replaced withcom.elasticpath.batch.processing.BatchJobRunner
. - All imports of
org.springframework.batch.item.ItemProcessor
need to be replaced withcom.elasticpath.batch.processing.ItemProcessor
. - All imports of
org.springframework.batch.item.ItemWriter
need to be replaced withcom.elasticpath.batch.processing.ItemWriter
. - All imports of
org.springframework.batch.item.ExecutionContext
need to be replaced withcom.elasticpath.batch.processing.impl.ExecutionContext
.
Additionally, for catalog syndication builders, the definition of the Spring Batch job needs to be refactored from a batch:job
definition into a ExecuteCleanupAndBuildBatchJob
class definition.
This is best explained through example. Here's an example of the original definition of the BUILD_ALL_OPTIONS
job:
<batch:job id="BUILD_ALL_OPTIONS" job-repository="catalogJobRepository">
<batch:step id="cleanUpOptions" next="buildAllOptions" parent="baseStep">
<batch:tasklet>
<batch:chunk reader="jpaPagingOptionProjectionReader" writer="projectionRemover"/>
</batch:tasklet>
</batch:step>
<batch:step id="buildAllOptions" parent="baseStep">
<batch:tasklet>
<batch:chunk reader="jpaPagingOptionReader" processor="skuOptionItemProcessor" writer="projectionWriter"/>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="jpaPagingOptionProjectionReader" class="com.elasticpath.catalog.batch.job.JpaPagingItemReaderExtended" scope="step">
<property name="cleanUpDatabase" value="#{jobParameters['cleanUpDatabase']}" />
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="transacted" value="false"/>
<property name="queryString"
value="select s.guid from ProjectionEntity s where s.projectionId.type='option' and s.guid is not null order by s.guid"/>
<property name="pageSize" value="1000"/>
</bean>
<bean id="jpaPagingOptionReader" class="org.springframework.batch.item.database.JpaPagingItemReader">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="transacted" value="false"/>
<property name="queryString" value="select s from SkuOptionImpl s order by s.uidPk"/>
<property name="pageSize" value="1000"/>
</bean>
Here is an example of the refactored definition of the BUILD_ALL_OPTIONS
job:
<bean id="catalogSyndicationBuildAllOptions" class="com.elasticpath.catalog.batch.processing.ExecuteCleanupAndBuildBatchJob">
<property name="cleanupJob">
<bean parent="parentCatalogSyndicationCleanupBatchJob">
<property name="jobName" value="catalogSyndicationCleanupOptions"/>
<property name="jpqlQuery" value="select s.guid from ProjectionEntity s where s.projectionId.type='option' and s.guid is not null order by s.guid"/>
</bean>
</property>
<property name="buildJob">
<bean parent="parentCatalogSyndicationBuildBatchJob">
<property name="jobName" value="catalogSyndicationBuildOptions"/>
<property name="jpqlQuery" value="select s from SkuOptionImpl s order by s.uidPk"/>
<property name="springBatchProcessor">
<bean parent="batchTxProxyTemplate">
<property name="target">
<bean parent="parentCatalogSyndicationBatchProcessor">
<property name="processor" ref="skuOptionItemProcessor"/>
<property name="writer" ref="projectionWriter"/>
</bean>
</property>
</bean>
</property>
</bean>
</property>
</bean>
Finally, the bean ID needs to be referenced in the jobs
property of the catalogSyndicationBuildAllProjections
bean, and the sourceMap
property of the catalogSyndicationBatchJobsParent
bean.
Trusted Header Authentication
The -DenableTrustedSubjectHeaderMode
system property has been removed and replaced with the -Dauth.mode
system property. If you previously had the -DenableTrustedSubjectHeaderMode
system property set, change it to -Dauth.mode=TRUSTED_HEADER
.
Additionally, if you have the Cortex Trusted Header Mode Accelerator wired into your project, you should remove it. To remove the accelerator, review the Merge Preview and undo the changes shown.
New static analysis checks
Additional static analysis checks have been added to the Self-Managed Commerce build process. This may cause your build to fail if your code violates these checks:
- Enforce Upper-Bound Dependencies: Ensures that if a dependency is declared with a version that is lower than the version required by a transitive dependency, the build will fail.
- Analyze Exclusions: Analyzes the exclusions defined on dependencies in this project and fails the build if any of them are unneeded.
For more information, see Static analysis.
Changes to FollowLocation error handling
When Cortex POST
requests are made with the FollowLocation
query parameter, Cortex actually processes operations against two resources:
- The origin resource: The resource that the Cortex
POST
was made against. - The target resource: The resource that Cortex would normally redirect to, but which actually provides the response if the
FollowLocation
query parameter is included.
When doing a POST
to an origin resource with the FollowLocation
query parameter, if the origin resource was successful but the target resource was unsuccessful, Cortex would fall back to the origin resource response (which is a 2XX response with a location header).
This behavior is summarized in the following table:
Origin Resource Response | Target Resource Response | HTTP status returned | HTTP Location header returned | HTTP body returned |
---|---|---|---|---|
201 | 200 | 201 | Target resource location | Target resource body |
200 | 200 | 200 | Target resource location | Target resource body |
4XX or 5XX | N/A | 4XX or 5XX | None | Origin resource body |
201 | 4XX or 5XX | 201 | Target resource location | Origin resource body |
200 | 4XX or 5XX | 200 | Target resource location | Origin resource body |
This was confusing for clients, since the HTTP status code could indicate that the operation was successful, but the body of the response was empty. This was especially confusing for resources like lookups and search, which don’t create anything but just generate a URL for a specific resource,
To reduce confusion but maintain backwards compatibility, the behavior has been changed slightly. The returned HTTP status code is still determined by the origin resource. If the origin resource succeeds but the target resource operation fails, Cortex acts as if the FollowLocation
request was not present and returns the origin resource response with a location header for the target resource. Cortex also returns a X-Ep-Follow-Location-Status
header so the client can determine the result of the target resource operation. Additionally, if both resources succeed, the location header is no longer returned with the response, because we’re returning the target resource body with the response and we don't want the client to redirect to the location header value.
The new behavior is summarized in the following table:
Origin Resource Response | Target Resource Response | HTTP status returned | HTTP X-Ep-Follow-Location-Status status returned | HTTP Location header returned | HTTP body returned |
---|---|---|---|---|---|
201 | 200 | 201 | 200 | None | Target resource body |
201 | 200 | 201 | 200 | None | Target resource body |
4XX or 5XX | N/A | 4XX or 5XX | N/A | None | Origin resource body |
201 | 4XX or 5XX | 201 | 4XX or 5XX | Target resource location | Origin resource body |
200 | 4XX or 5XX | 200 | 4XX or 5XX | Target resource location | Origin resource body |
note
In the table above, the changes from the previous behavior are highlighted in bold.
For more information, see FollowLocation.
Camel dependencies in OSGi bundles
Previously, using Apache Camel in OSGi bundles was very challenging. Simply adding org.apache.camel
dependencies to a bundle would usually cause it to fail with the following error:
javax.xml.bind.JAXBException: "org.apache.camel.model.config" doesnt contain ObjectFactory.class or jaxb.index
This could be worked around by embedding Apache Camel dependencies into the bundle, but this was not ideal.
To make this easier, a new ep-camel-bundle
bundle has been created that provides the necessary Camel packages automatically. If you have extension bundle modules that define org.apache.camel
runtime dependencies and camel-*
embed-dependency
instructions, these can now be simply removed. At runtime, OSGi should import the required Camel packages from the ep-camel-bundle
bundle automatically.
OSGi split package detection
A new test has been added to the webapp-smoketests
module that will fail if an OSGi split package situation is detected. If you have multiple bundles in Cortex that export the same package, this test will fail.
For more information, see Webapp Smoketests.
Updated IntelliJ code style configuration
The IntelliJ code style configuration in devops/intellij/code-style.xml
has been updated. final
modifiers are now automatically applied to local variables and method parameters generated by IntelliJ. Additionally, jakarta
packages are now grouped alongside java
and javax
packages when imports are optimized.
Ensure that all developers update their IntelliJ code style configuration by following the setup code formatting instructions.
Added support for JUnit 5
Junit 5 (Jupiter) is now supported in all Self-Managed Commerce modules. The JUnit 4 dependencies are still available, and can be used without changing any of your existing tests. JUnit 4 and JUnit 5 dependencies can be used together in the same module without any issues.
To convert a JUnit 4 test to JUnit 5, make the following changes:
Remove the
junit
dependency from your modulepom.xml
. (This is optional; it can be left in place if you still have JUnit 4 tests in the module.)Add the following dependencies to your module
pom.xml
:<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> </dependency>
Within your test class, replace all
org.junit.*
imports withorg.junit.jupiter.api.*
.If using Mockito for mocking, replace
@RunWith(MockitoJUnitRunner.class)
with@ExtendWith(MockitoExtension.class)
.Any JUnit 4 annotations will need to be converted to JUnit 5 annotations:
@BeforeClass
->@BeforeAll
@AfterClass
->@AfterAll
@Before
->@BeforeEach
@After
->@AfterEach
@Ignore
->@Disabled
Commerce Engine changes
- The transient
promotionCodes
field onShoppingCartImpl
was changed fromMap<String, Set<String>>
(representing a map of rule GUIDs to set of coupon codes) toSet<String>
(representing a simple set of coupon codes). - Renamed
ShoppingCartPromotionImporterImpl
toPromotionImporterImpl
and renamedShoppingCartPromotionExporterImpl
toPromotionExporterImpl
. - Several changes have been made to the
CustomerService
interface:- Added
boolean isCustomerExistsByGuidAndCustomerType(String guid, CustomerType customerType)
. This allows you to determine if a customer exists using the GUID and customer type. - Removed
boolean isCustomerGuidExists(String guid)
. Useboolean isCustomerExistsByGuidAndCustomerType(String guid, CustomerType customerType)
instead. - Added
boolean boolean isCustomerExistsBySharedIdAndCustomerType(String sharedId, CustomerType customerType)
. This allows you to determine if a customer exists using the shared ID and customer type. - Deprecated
boolean isCustomerExistsBySharedId(String sharedId, CustomerType customerType)
. Useboolean isCustomerExistsBySharedIdAndCustomerType(String sharedId, CustomerType customerType)
instead. - Removed
boolean isRegisteredCustomerExistsBySharedIdAndCustomerType(Customer customer)
. Useboolean isCustomerExistsBySharedIdAndCustomerType(String sharedId, CustomerType customerType)
instead. - Added
String findCustomerGuidBySharedIdAndCustomerType(String sharedId, CustomerType customerType)
. This allows you to retrieve a customer's GUID using the shared ID and customer type. - Deprecated
String findCustomerGuidBySharedId(String sharedId, CustomerType customerType)
. UseString findCustomerGuidBySharedIdAndCustomerType(String sharedId, CustomerType customerType)
instead.
- Added
- The
loadingDefaultSku
field (and related getter/setter) has been removed fromProductLoadTunerImpl
. This field was confusing and unnecessary. Developers should use theloadingSkus
field to specify that product SKUs (including the default sku) should be loaded when retrieving a product from the database.
Import/Export changes
Moved condition rules into the promotions schema
Previously, the promotions.xml
Import/Export schema contained Drools actions and selling context conditions, but not Drools conditions. Instead, these needed to be specified in a separate condition_rules.xml
file, which referenced each promotion by code. This was inconvenient and confusing.
Now, the promotions.xml
Import/Export schema includes all promotion details, including Drools conditions. The conditions can be specified as in the following example:
<conditions>
<and>
<condition>
<kind>Condition</kind>
<type>productInCartCondition</type>
<parameters>
<parameter>
<key>numItems</key>
<value>1</value>
</parameter>
<parameter>
<key>numItemsQuantifier</key>
<value>AT_LEAST</value>
</parameter>
<parameter>
<key>productCode</key>
<value>triggerprodforfreeshippingpromo</value>
</parameter>
</parameters>
<exceptions/>
</condition>
</and>
</conditions>
For backwards compatibility, promotions can still be imported without condition rules, and the condition_rules.xml
file is still supported. However, when exporting promotions, the condition rules will be automatically included in the promotions.xml
schema.
For more information, see Promotions.