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:runinstead ofmvn clean tomcat8:run-war. - The Commerce Manager webapp is now started from the
extensions/cm/ext-cm-modules/ext-cm-webappfolder instead ofextensions/cm/ext-cm-modules/ext-cm-webapp-runner. - JVM parameters passed to the
mvn clean package cargo:runcommand are no longer passed to the webapps.- To enable remote debugging, add the
-Pdebugoption. - To enable
automation-idHTML attributes for Selenium Tests, add the-PenableUITestsoption to the Commerce Manager webapp startup command. - To add other JVM parameters, add them to the
systemPropertiessection of thecargo-maven3-pluginconfiguration in the webapppom.xmlfile.
- 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-jakartabranch into your custom Self-Managed Commerce repo branch.smc-upgrader 8.7.x-pre-jakartaFollow 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.xbranch into your custom Self-Managed Commerce repo branch.smc-upgrader 8.7.xRun a build with the
mvn clean install -DskipAllTests -Pjakarta-source-migrationcommand, 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.baseimport com.fasterxml.jackson.jaxrs.json-->import com.fasterxml.jackson.jakarta.rs.jsonimport com.sun.xml.bind.annotation.XmlLocation-->import org.glassfish.jaxb.core.annotation.XmlLocationimport com.sun.xml.bind.Locatable-->import org.glassfish.jaxb.core.Locatableimport javax.activation-->import jakarta.activationimport javax.annotation-->import jakarta.annotationimport javax.inject-->import jakarta.injectimport javax.jms-->import jakarta.jmsimport javax.json-->import jakarta.jsonimport javax.mail-->import jakarta.mailimport javax.persistence-->import jakarta.persistenceimport javax.servlet-->import jakarta.servletimport javax.transaction-->import jakarta.transactionimport javax.validation-->import jakarta.validationimport javax.ws.rs-->import jakarta.ws.rsimport javax.xml.bind-->import jakarta.xml.bindimport javax.xml.soap-->import jakarta.xml.soapimport org.apache.commons.mail.Email-->import org.apache.commons.mail2.jakarta.Emailimport org.apache.commons.mail.EmailAttachment-->import org.apache.commons.mail2.jakarta.EmailAttachmentimport org.apache.commons.mail.EmailException-->import org.apache.commons.mail2.core.EmailExceptionimport org.apache.commons.mail.HtmlEmail-->import org.apache.commons.mail2.jakarta.HtmlEmailimport org.apache.commons.mail.SimpleEmail-->import org.apache.commons.mail2.jakarta.SimpleEmailjavax.jms.ConnectionFactory-->jakarta.jms.ConnectionFactoryjavax.jms.JMSException-->jakarta.jms.JMSExceptionjavax.persistence.FlushModeType-->jakarta.persistence.FlushModeTypejavax.persistence.PersistenceException-->jakarta.persistence.PersistenceExceptionjavax.persistence.Query-->jakarta.persistence.Queryjavax.validation.constraints.NotNull.message-->jakarta.validation.constraints.NotNull.messagejavax.validation.constraints.Size.message-->jakarta.validation.constraints.Size.messagejavax.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.2org.eclipse.gemini.blueprint:gemini-blueprint-core:4.0.0-atlassian-5org.eclipse.gemini.blueprint:gemini-blueprint-io:4.0.0-atlassian-5org.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-brokerorg.apache.activemq:activemq-jms-pool-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-jms-poolorg.apache.activemq:activemq-pool-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-poolorg.apache.activemq:activemq-web-console-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-web-consoleorg.apache.solr:solr-core-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.solr.solr-coreorg.apache.velocity.tools:velocity-tools-view-->com.elasticpath.jakarta.transformed.org.apache.velocity.tools.velocity-tools-vieworg.eclipse.sisu:org.eclipse.sisu.inject.extender-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject.extender(within theapi-platformproject)org.eclipse.sisu:org.eclipse.sisu.inject-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject(within theapi-platformproject)org.ops4j:peaberry-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.ops4j.peaberry(within theapi-platformproject)
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.xmls 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-javawithorg.apache.camel:camel-openapi-java. - Replace
org.apache.camel:camel-testwithorg.apache.camel:camel-test-junit5. - Replace
org.apache.camel:camel-test-springwithorg.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
setUpmethod withsetUpTestandtearDownmethod withtearDownTest. Also remove the@Overrideannotation from these methods. - If your test uses
JUnitRuleMockeryto define a Camel route, you will need to remove this field from your class and instead define your route by overriding thecreateRouteBuildermethod.
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.uipackage hierarchy). - Replace
org.eclipse.jface.*withorg.eclipse.rap.jface.*(where*represents any package in theorg.eclipse.jfacepackage hierarchy). - Replace
javax.servletwithjakarta.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-apijavax.inject:javax.inject-->jakarta.inject:jakarta.inject-apijavax.interceptor:javax.interceptor-api-->jakarta.interceptor:jakarta.interceptor-apijavax.json:javax.json-api-->jakarta.json:jakarta.json-apijavax.servlet:javax.servlet-api-->jakarta.servlet:jakarta.servlet-apijavax.transaction:javax.transaction-api-->jakarta.transaction:jakarta.transaction-apijavax.validation:validation-api-->jakarta.validation:jakarta.validation-apijavax.ws.rs:javax.ws.rs-api-->jakarta.ws.rs:jakarta.ws.rs-apiorg.apache.activemq:activemq-broker-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-brokerorg.apache.activemq:activemq-client->org.apache.activemq:activemq-client-jakartaorg.apache.activemq:activemq-jms-pool-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-jms-poolorg.apache.activemq:activemq-pool-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-poolorg.apache.activemq:activemq-web-console-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.activemq.activemq-web-consoleorg.apache.commons:commons-email-->org.apache.commons:commons-email2-coreorg.apache.solr:solr-core-->com.elasticpath.jakarta.transformed:com.elasticpath.jakarta.transformed.org.apache.solr.solr-coreorg.apache.velocity.tools:velocity-tools-view-->com.elasticpath.jakarta.transformed.org.apache.velocity.tools.velocity-tools-vieworg.eclipse.sisu:org.eclipse.sisu.inject.extender-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.inject.extenderorg.eclipse.sisu:org.eclipse.sisu.inject-->com.elasticpath.rest.jakarta.transformed:com.elasticpath.rest.jakarta.transformed.org.eclipse.sisu.injectorg.glassfish:jakarta.el-->org.glassfish.expressly:expresslyorg.glassfish:javax.el-->org.glassfish.expressly:expresslyorg.glassfish:javax.json-->jakarta.json:jakarta.json-apiandorg.eclipse.parsson:parssonorg.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-implcom.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.basecom.fasterxml.jackson.jaxrs.json-->com.fasterxml.jackson.jakarta.rs.jsoncom.sun.xml.bind.annotation.XmlLocation-->org.glassfish.jaxb.core.annotation.XmlLocationcom.sun.xml.bind.Locatable-->org.glassfish.jaxb.core.Locatablejavax.activation-->jakarta.activationjavax.annotation-->jakarta.annotationjavax.inject-->jakarta.injectjavax.jms-->jakarta.jmsjavax.json-->jakarta.jsonjavax.mail-->jakarta.mailjavax.persistence-->jakarta.persistencejavax.servlet-->jakarta.servletjavax.transaction-->jakarta.transactionjavax.validation-->jakarta.validationjavax.ws.rs-->jakarta.ws.rsjavax.xml.bind-->jakarta.xml.bindjavax.xml.soap-->jakarta.xml.soaporg.apache.commons.mail.Email-->org.apache.commons.mail2.jakarta.Emailorg.apache.commons.mail.EmailAttachment-->org.apache.commons.mail2.jakarta.EmailAttachmentorg.apache.commons.mail.EmailException-->org.apache.commons.mail2.core.EmailExceptionorg.apache.commons.mail.HtmlEmail-->org.apache.commons.mail2.jakarta.HtmlEmailorg.apache.commons.mail.SimpleEmail-->org.apache.commons.mail2.jakarta.SimpleEmailorg.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-->CatalogAmountDiscountImplapplyCatalogCurrencyDiscountPercent-->CatalogPercentDiscountImplapplyShippingDiscountAmount-->ShippingAmountDiscountImplapplyShippingDiscountPercent-->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:
carthas been changed fromShoppingCarttoXPFShoppingCartshoppingItemhas been changed fromShoppingItemImpltoXPFShoppingItem
For catalog promotion rules:
producthas been changed fromProductImpltoXPFProductproductSkuhas been changed fromProductSkuImpltoXPFProductSku
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
extendsofcom.elasticpath.batch.jobs.AbstractBatchJobneed to be replaced with one of the following:com.elasticpath.batch.processing.AbstractBatchJob: If thefetchBatchOfRecordsandgetBatchProcessormethods are being overridden.com.elasticpath.batch.processing.AbstractJPANamedQueryBatchJob: If thegetJPANamedQueryandgetJPQNamedQueryParametersmethods are being overridden.com.elasticpath.batch.processing.AbstractJPANamedQueryListParameterBatchJob: If thegetJPANamedQuery,getJPQNamedQueryParameters, andgetJPANamedQueryListParametersmethods are being overridden.
- All imports of
com.elasticpath.batch.jobs.impl.AbstractEnableJobneed to be replaced withcom.elasticpath.batch.processing.AbstractJPANamedQueryBatchJob. - All calls to
AbstractBatchJob#setEnabledProviderneed to be replaced withAbstractBatchJob#setEnabledProvider. - All calls to
AbstractBatchJob#setConfigBatchSizeneed to be replaced withAbstractBatchJob#setBatchSizeProvider. - All overrides of
AbstractJPANamedQueryBatchJob#getBatchJPQLQueryneed to be renamed toAbstractJPANamedQueryBatchJob#getJPANamedQuery. - All overrides of
AbstractJPANamedQueryBatchJob#getParametersneed to be renamed toAbstractJPANamedQueryBatchJob#getJPANamedQueryParameters. - All imports of
com.elasticpath.batch.jobs.AbstractBatchProcessorneed to be replaced withcom.elasticpath.batch.processing.AbstractBatchProcessor. - All imports of
com.elasticpath.catalog.batch.processing.ExecuteBatchJobneed to be replaced withcom.elasticpath.batch.processing.ExecuteBatchJob. - All imports of
com.elasticpath.catalog.batch.processing.OpenCloseOperationsneed to be replaced withcom.elasticpath.batch.processing.OpenCloseOperations. - All imports of
com.elasticpath.catalog.batch.CatalogJobRunnerneed to be replaced withcom.elasticpath.batch.processing.BatchJobRunner. - All imports of
org.springframework.batch.item.ItemProcessorneed to be replaced withcom.elasticpath.batch.processing.ItemProcessor. - All imports of
org.springframework.batch.item.ItemWriterneed to be replaced withcom.elasticpath.batch.processing.ItemWriter. - All imports of
org.springframework.batch.item.ExecutionContextneed 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
POSTwas made against. - The target resource: The resource that Cortex would normally redirect to, but which actually provides the response if the
FollowLocationquery 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
junitdependency 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
promotionCodesfield onShoppingCartImplwas 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
ShoppingCartPromotionImporterImpltoPromotionImporterImpland renamedShoppingCartPromotionExporterImpltoPromotionExporterImpl. - Several changes have been made to the
CustomerServiceinterface:- 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
loadingDefaultSkufield (and related getter/setter) has been removed fromProductLoadTunerImpl. This field was confusing and unnecessary. Developers should use theloadingSkusfield 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.