Hybris Promotion
分为Product Promotion、Order Promotion两种。
Product Promotion
产品级别促销例子:BOGOF(Buy One Get One Free)买一送一
定义 Promotion itemtype
Integer.valueOf(2) 定义属性:出发促销的数量 The number of products required in the cart to activate the promotion. (For standard BOGOF this is 2). Integer.valueOf(1) 定义属性:赠送的数量 The number of products within the cart to give away free. (For standard BOGOF this is 1). 发生促销时 要提示的信息 The message to show when the promotion has fired. varchar2(4000) text nvarchar(max) LONGVARCHAR varchar 存在潜在促销时 要提示的信息 The message to show when the promotion could have potentially fire. varchar2(4000) text nvarchar(max) LONGVARCHAR varchar
ProductBOGOFPromotion 继承 ProductPromotion 扩展一些所需字段,创建新的 Model 对象 ProductBOGOFPromotionModel,同时生成一个 java 类(里面的方法自己改自己的逻辑)
执行 ant all 生成促销逻辑 jalo class 如下面 ProductBOGOFPromotion.java
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package de.hybris.platform.promotions.jalo;import de.hybris.platform.jalo.SessionContext;import de.hybris.platform.jalo.c2l.Currency;import de.hybris.platform.jalo.order.AbstractOrder;import de.hybris.platform.promotions.jalo.GeneratedProductBOGOFPromotion;import de.hybris.platform.promotions.jalo.PromotionOrderEntryConsumed;import de.hybris.platform.promotions.jalo.PromotionResult;import de.hybris.platform.promotions.jalo.PromotionsManager;import de.hybris.platform.promotions.jalo.PromotionsManager.RestrictionSetResult;import de.hybris.platform.promotions.result.PromotionEvaluationContext;import de.hybris.platform.promotions.result.PromotionOrderView;import de.hybris.platform.promotions.util.Helper;import java.util.ArrayList;import java.util.Comparator;import java.util.Iterator;import java.util.List;import java.util.Locale;import org.apache.log4j.Logger;public class ProductBOGOFPromotion extends GeneratedProductBOGOFPromotion { private static final Logger LOG = Logger.getLogger(ProductBOGOFPromotion.class); public ProductBOGOFPromotion() { } //核心方法 @Override public Listevaluate(SessionContext ctx, PromotionEvaluationContext promoContext) { ArrayList results = new ArrayList(); //获取符合促销的产品,并应用任何限制 RestrictionSetResult restrictResult = this.findEligibleProductsInBasket(ctx, promoContext); //如果这些限制没有拒绝这项促销活动,并且在限制之后仍然允许使用产品 if(restrictResult.isAllowedToContinue() && !restrictResult.getAllowedProducts().isEmpty()) { //获取促销属性值 int qualifyingCount = this.getQualifyingCount(ctx).intValue(); int freeCount = this.getFreeCount(ctx).intValue(); PromotionsManager promotionsManager = PromotionsManager.getInstance(); //创建一个视图仅包含允许产品的订单 PromotionOrderView orderView = promoContext.createView(ctx, this, restrictResult.getAllowedProducts()); PromotionResult result1; //获取购物车中基本产品的实际数量 long realQuantity =orderView.getTotalQuantity(ctx) while(realQuantity >= (long)qualifyingCount) { promoContext.startLoggingConsumed(this); Comparator remainingCount = PromotionEvaluationContext.createPriceComparator(ctx); orderView.consumeFromTail(ctx, remainingCount, (long)(qualifyingCount - freeCount)); List freeItems = orderView.consumeFromHead(ctx, remainingCount, (long)freeCount); ArrayList certainty = new ArrayList(); Iterator consumed = freeItems.iterator(); while(consumed.hasNext()) { PromotionOrderEntryConsumed result = (PromotionOrderEntryConsumed)consumed.next(); result.setAdjustedUnitPrice(ctx, 0.0D); double adjustment = result.getEntryPrice(ctx) * -1.0D; certainty.add(promotionsManager.createPromotionOrderEntryAdjustAction(ctx, result.getOrderEntry(ctx), adjustment)); } //createPromotionResult()最后一个参数传入的是1.0F表示该Promotion Fired //createPromotionResult()最后一个参数传入的是0.75F表示该Promotion Could Fire. result1 = promotionsManager.createPromotionResult(ctx, this, promoContext.getOrder(), 1.0F); List consumed1 = promoContext.finishLoggingAndGetConsumed(this, true); result1.setConsumedEntries(ctx, consumed1); result1.setActions(ctx, certainty); results.add(result1); } long remainingCount1 = orderView.getTotalQuantity(ctx); if(orderView.getTotalQuantity(ctx) > 0L) { promoContext.startLoggingConsumed(this); orderView.consume(ctx, remainingCount1); float certainty1 = (float)remainingCount1 / (float)qualifyingCount; result1 = promotionsManager.createPromotionResult(ctx, this, promoContext.getOrder(), certainty1); result1.setConsumedEntries(promoContext.finishLoggingAndGetConsumed(this, false)); results.add(result1); } } return results; } //用于返回Promotion提示的消息 //根据该方法传入的promotionResult判断改Promotion是否Fired,根据结果返回相应的Message。 @Override public String getResultDescription(SessionContext ctx, PromotionResult promotionResult, Locale locale) { AbstractOrder order = promotionResult.getOrder(ctx); if(order != null) { Currency orderCurrency = order.getCurrency(ctx); Integer qualifyingCount = this.getQualifyingCount(ctx); Integer freeCount = this.getFreeCount(ctx); //结果为Fired显示该Promotion的MessageFired字段 if(promotionResult.getFired(ctx)) { double args2 = promotionResult.getTotalDiscount(ctx); Object[] args1 = new Object[]{qualifyingCount, freeCount, Double.valueOf(args2), Helper.formatCurrencyAmount(ctx, locale, orderCurrency, args2)}; return formatMessage(this.getMessageFired(ctx), args1, locale); } //结果为Could Fire显示该Promotion的MessageFired字段 if(promotionResult.getCouldFire(ctx)) { Object[] args = new Object[]{Long.valueOf(this.getQualifyingCount(ctx).longValue() - promotionResult.getConsumedCount(ctx, true)), qualifyingCount, freeCount}; return formatMessage(this.getMessageCouldHaveFired(ctx), args, locale); } } return ""; }}
编写hmc.xml文件,将自定义的Promotion加入促销组中用于后台管理及维护
将在hmc.xml中用到的key加入properties,实现国际化.
# =============================================================================# promotions/hmc/resources/de/hybris/platform/promotions/hmc/locales_zh.properties# BOGOF Promotion specific text# =============================================================================text.productbogofpromotion.detaileddescription=text.productbogofpromotion.qualifyingoverview= 在此处指定用户的购物车中必须存在的项目数量以及其中免费项目的数量。在典型的“买一送一”促销中,符合条件的数量是 2,而免费数是 1(&Q)。text.productbogofpromotion.qualifyingcount=要符合此促销的条件,用户的购物车中必须具有的单位数。text.productbogofpromotion.freecount=免费赠送的单位数。该值必须小于符合条件的数量。text.productbogofpromotion.messagefired=此促销开始后要显示的文本。text.productbogofpromotion.messagecouldhavefired=要求产品数未满足时显示的文本。text.productbogofpromotion.messagefiredargs= text.productbogofpromotion.messagecouldhavefiredargs=购买特定数量的商品,即可免费获得指定数量的低价商品。例如: 买一送一(也叫作 一件价买两件)、 两件价买三件或买就送产品的任何组合。商品必须都来自符合条件产品的范围。
# =============================================================================# promotions/hmc/resources/de/hybris/platform/promotions/hmc/locales_en.properties# BOGOF Promotion specific text# =============================================================================text.productbogofpromotion.detaileddescription= \\text.productbogofpromotion.qualifyingoverview= \Here you specify how many items the user must have in their cart and how \many of those will be free. In a classic "Buy One Get One Free" the qualifying count \is 2 and the free count is 1.text.productbogofpromotion.qualifyingcount=The number of units the user must have in their cart to qualify for this promotion.text.productbogofpromotion.freecount=The number of units to give away free of charge. This must be less than the qualifying count.text.productbogofpromotion.messagefired=Text displayed when this promotion fires.text.productbogofpromotion.messagecouldhavefired=Text displayed when the required number of products has not been met.text.productbogofpromotion.messagefiredargs= \ text.productbogofpromotion.messagecouldhavefiredargs= \Buy a certain number of items, get specified number of lowest valued items for free.\For example: Buy 1 get 1 free (also known as Buy 2 for the price of 1), \ Buy 3 for the price of 2 or any combination of paid for and free products.\The items must all be from the range of qualifying products.\
重新启动服务,并在hac中勾选下图选项,进行Update。
Order Promotion
订单级别促销例子:Order Threshold Discount Promotion 订单阈值固定折扣
The cart total value threshold in specific currencies. Fixed price for discount in specific currencies. The message to show when the promotion has fired. varchar2(4000) text nvarchar(max) LONGVARCHAR varchar The message to show when the promotion could have potentially fire. varchar2(4000) text nvarchar(max) LONGVARCHAR varchar
OrderThresholdDiscountPromotion继承OrderPromotion扩展一些所需字段,创建新的Model对象OrderThresholdDiscountPromotionModel,同时生成一个java类(里面的方法自己改自己的逻辑)
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package de.hybris.platform.promotions.jalo;import de.hybris.platform.jalo.ConsistencyCheckException;import de.hybris.platform.jalo.SessionContext;import de.hybris.platform.jalo.c2l.Currency;import de.hybris.platform.jalo.order.AbstractOrder;import de.hybris.platform.promotions.jalo.AbstractPromotionAction;import de.hybris.platform.promotions.jalo.GeneratedOrderThresholdDiscountPromotion;import de.hybris.platform.promotions.jalo.PromotionResult;import de.hybris.platform.promotions.jalo.PromotionsManager;import de.hybris.platform.promotions.result.PromotionEvaluationContext;import de.hybris.platform.promotions.util.Helper;import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;import java.util.List;import java.util.Locale;import java.util.Map;import org.apache.log4j.Logger;public class OrderThresholdDiscountPromotion extends GeneratedOrderThresholdDiscountPromotion { private static final Logger log = Logger.getLogger(OrderThresholdDiscountPromotion.class.getName()); public OrderThresholdDiscountPromotion() { } public void remove(SessionContext ctx) throws ConsistencyCheckException { deletePromotionPriceRows(ctx, this.getThresholdTotals(ctx)); deletePromotionPriceRows(ctx, this.getDiscountPrices(ctx)); super.remove(ctx); } public Listevaluate(SessionContext ctx, PromotionEvaluationContext promoContext) { ArrayList promotionResults = new ArrayList(); if(this.checkRestrictions(ctx, promoContext)) { Double threshold = this.getPriceForOrder(ctx, this.getThresholdTotals(ctx), promoContext.getOrder(), "thresholdTotals"); if(threshold != null) { Double discountPriceValue = this.getPriceForOrder(ctx, this.getDiscountPrices(ctx), promoContext.getOrder(), "discountPrices"); if(discountPriceValue != null) { AbstractOrder order = promoContext.getOrder(); double orderSubtotalAfterDiscounts = getOrderSubtotalAfterDiscounts(ctx, order); if(orderSubtotalAfterDiscounts >= threshold.doubleValue()) { if(log.isDebugEnabled()) { log.debug("(" + this.getPK() + ") evaluate: Subtotal " + orderSubtotalAfterDiscounts + ">" + threshold + ". Creating a discount action for value:" + discountPriceValue + "."); } PromotionResult certainty = PromotionsManager.getInstance().createPromotionResult(ctx, this, promoContext.getOrder(), 1.0F); double result = discountPriceValue.doubleValue(); if(result > orderSubtotalAfterDiscounts) { result = orderSubtotalAfterDiscounts; } certainty.addAction(ctx, PromotionsManager.getInstance().createPromotionOrderAdjustTotalAction(ctx, -result)); promotionResults.add(certainty); } else { if(log.isDebugEnabled()) { log.debug("(" + this.getPK() + ") evaluate: Subtotal " + orderSubtotalAfterDiscounts + "<" + threshold + ". Skipping discount action."); } float certainty1 = (float)(orderSubtotalAfterDiscounts / threshold.doubleValue()); PromotionResult result1 = PromotionsManager.getInstance().createPromotionResult(ctx, this, promoContext.getOrder(), certainty1); promotionResults.add(result1); } } } } return promotionResults; } public String getResultDescription(SessionContext ctx, PromotionResult result, Locale locale) { AbstractOrder order = result.getOrder(ctx); if(order != null) { Currency orderCurrency = order.getCurrency(ctx); Double threshold = this.getPriceForOrder(ctx, this.getThresholdTotals(ctx), order, "thresholdTotals"); if(threshold != null) { Double discountPriceValue = this.getPriceForOrder(ctx, this.getDiscountPrices(ctx), order, "discountPrices"); if(discountPriceValue != null) { if(result.getFired(ctx)) { Object[] orderSubtotalAfterDiscounts1 = new Object[]{threshold, Helper.formatCurrencyAmount(ctx, locale, orderCurrency, threshold.doubleValue()), discountPriceValue, Helper.formatCurrencyAmount(ctx, locale, orderCurrency, discountPriceValue.doubleValue())}; return formatMessage(this.getMessageFired(ctx), orderSubtotalAfterDiscounts1, locale); } if(result.getCouldFire(ctx)) { double orderSubtotalAfterDiscounts = getOrderSubtotalAfterDiscounts(ctx, order); double amountRequired = threshold.doubleValue() - orderSubtotalAfterDiscounts; Object[] args = new Object[]{threshold, Helper.formatCurrencyAmount(ctx, locale, orderCurrency, threshold.doubleValue()), discountPriceValue, Helper.formatCurrencyAmount(ctx, locale, orderCurrency, discountPriceValue.doubleValue()), Double.valueOf(amountRequired), Helper.formatCurrencyAmount(ctx, locale, orderCurrency, amountRequired)}; return formatMessage(this.getMessageCouldHaveFired(ctx), args, locale); } } } } return ""; }}