/* * #%L * BroadleafCommerce Framework * %% * Copyright (C) 2009 - 2013 Broadleaf Commerce * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package org.broadleafcommerce.core.offer.service.processor; import org.broadleafcommerce.common.money.Money; import org.broadleafcommerce.core.offer.dao.CustomerOfferDao; import org.broadleafcommerce.core.offer.dao.OfferCodeDao; import org.broadleafcommerce.core.offer.dao.OfferDao; import org.broadleafcommerce.core.offer.domain.CandidateItemOffer; import org.broadleafcommerce.core.offer.domain.CandidateItemOfferImpl; import org.broadleafcommerce.core.offer.domain.Offer; import org.broadleafcommerce.core.offer.domain.OfferImpl; import org.broadleafcommerce.core.offer.domain.OfferQualifyingCriteriaXref; import org.broadleafcommerce.core.offer.domain.OfferTargetCriteriaXref; import org.broadleafcommerce.core.offer.domain.OrderItemAdjustment; import org.broadleafcommerce.core.offer.domain.OrderItemAdjustmentImpl; import org.broadleafcommerce.core.offer.domain.OrderItemPriceDetailAdjustment; import org.broadleafcommerce.core.offer.service.OfferDataItemProvider; import org.broadleafcommerce.core.offer.service.OfferServiceImpl; import org.broadleafcommerce.core.offer.service.OfferServiceUtilitiesImpl; import org.broadleafcommerce.core.offer.service.discount.CandidatePromotionItems; import org.broadleafcommerce.core.offer.service.discount.domain.PromotableCandidateItemOffer; import org.broadleafcommerce.core.offer.service.discount.domain.PromotableItemFactoryImpl; import org.broadleafcommerce.core.offer.service.discount.domain.PromotableOrder; import org.broadleafcommerce.core.offer.service.discount.domain.PromotableOrderItem; import org.broadleafcommerce.core.offer.service.type.OfferDiscountType; import org.broadleafcommerce.core.offer.service.type.OfferItemRestrictionRuleType; import org.broadleafcommerce.core.order.dao.FulfillmentGroupItemDao; import org.broadleafcommerce.core.order.dao.OrderItemDao; import org.broadleafcommerce.core.order.domain.DiscreteOrderItem; import org.broadleafcommerce.core.order.domain.FulfillmentGroupItem; import org.broadleafcommerce.core.order.domain.Order; import org.broadleafcommerce.core.order.domain.OrderItem; import org.broadleafcommerce.core.order.domain.OrderItemPriceDetail; import org.broadleafcommerce.core.order.domain.OrderMultishipOption; import org.broadleafcommerce.core.order.service.FulfillmentGroupService; import org.broadleafcommerce.core.order.service.OrderItemService; import org.broadleafcommerce.core.order.service.OrderMultishipOptionService; import org.broadleafcommerce.core.order.service.OrderService; import org.broadleafcommerce.core.order.service.call.FulfillmentGroupItemRequest; import org.easymock.EasyMock; import org.easymock.IAnswer; import java.util.ArrayList; import java.util.List; import java.util.TimeZone; import junit.framework.TestCase; /** * * @author jfischer * */ public class ItemOfferProcessorTest extends TestCase { protected OfferDao offerDaoMock; protected OrderItemDao orderItemDaoMock; protected OrderService orderServiceMock; protected OfferServiceImpl offerService; protected OrderItemService orderItemServiceMock; protected FulfillmentGroupItemDao fgItemDaoMock; protected OfferDataItemProvider dataProvider = new OfferDataItemProvider(); protected FulfillmentGroupService fgServiceMock; protected OrderMultishipOptionService multishipOptionServiceMock; protected OfferTimeZoneProcessor offerTimeZoneProcessorMock; protected ItemOfferProcessorImpl itemProcessor; @Override protected void setUp() throws Exception { CustomerOfferDao customerOfferDaoMock = EasyMock.createMock(CustomerOfferDao.class); OfferCodeDao offerCodeDaoMock = EasyMock.createMock(OfferCodeDao.class); offerDaoMock = EasyMock.createMock(OfferDao.class); orderItemDaoMock = EasyMock.createMock(OrderItemDao.class); orderServiceMock = EasyMock.createMock(OrderService.class); orderItemServiceMock = EasyMock.createMock(OrderItemService.class); fgItemDaoMock = EasyMock.createMock(FulfillmentGroupItemDao.class); fgServiceMock = EasyMock.createMock(FulfillmentGroupService.class); multishipOptionServiceMock = EasyMock.createMock(OrderMultishipOptionService.class); offerTimeZoneProcessorMock = EasyMock.createMock(OfferTimeZoneProcessor.class); OfferServiceUtilitiesImpl offerServiceUtilities = new OfferServiceUtilitiesImpl(); offerServiceUtilities.setOfferDao(offerDaoMock); offerServiceUtilities.setPromotableItemFactory(new PromotableItemFactoryImpl()); itemProcessor = new ItemOfferProcessorImpl(); itemProcessor.setOfferDao(offerDaoMock); itemProcessor.setOrderItemDao(orderItemDaoMock); itemProcessor.setOfferTimeZoneProcessor(offerTimeZoneProcessorMock); itemProcessor.setPromotableItemFactory(new PromotableItemFactoryImpl()); itemProcessor.setOfferServiceUtilities(offerServiceUtilities); offerService = new OfferServiceImpl(); OrderOfferProcessorImpl orderProcessor = new OrderOfferProcessorImpl(); orderProcessor.setOfferDao(offerDaoMock); orderProcessor.setPromotableItemFactory(new PromotableItemFactoryImpl()); orderProcessor.setOfferTimeZoneProcessor(offerTimeZoneProcessorMock); orderProcessor.setOrderItemDao(orderItemDaoMock); orderProcessor.setOfferServiceUtilities(offerServiceUtilities); offerService.setCustomerOfferDao(customerOfferDaoMock); offerService.setOfferCodeDao(offerCodeDaoMock); offerService.setOfferDao(offerDaoMock); offerService.setOrderOfferProcessor(orderProcessor); offerService.setItemOfferProcessor(itemProcessor); offerService.setPromotableItemFactory(new PromotableItemFactoryImpl()); offerService.setOrderService(orderServiceMock); } public void replay() throws Exception { EasyMock.expect(orderItemDaoMock.createOrderItemPriceDetail()).andAnswer(OfferDataItemProvider.getCreateOrderItemPriceDetailAnswer()).anyTimes(); EasyMock.expect(orderItemDaoMock.createOrderItemQualifier()).andAnswer(OfferDataItemProvider.getCreateOrderItemQualifierAnswer()).anyTimes(); EasyMock.expect(offerDaoMock.createOrderItemPriceDetailAdjustment()).andAnswer(OfferDataItemProvider.getCreateOrderItemPriceDetailAdjustmentAnswer()).anyTimes(); EasyMock.expect(fgServiceMock.addItemToFulfillmentGroup(EasyMock.isA(FulfillmentGroupItemRequest.class), EasyMock.eq(false))).andAnswer(OfferDataItemProvider.getAddItemToFulfillmentGroupAnswer()).anyTimes(); EasyMock.expect(orderServiceMock.removeItem(EasyMock.isA(Long.class), EasyMock.isA(Long.class), EasyMock.eq(false))).andAnswer(OfferDataItemProvider.getRemoveItemFromOrderAnswer()).anyTimes(); EasyMock.expect(orderServiceMock.save(EasyMock.isA(Order.class), EasyMock.isA(Boolean.class))).andAnswer(OfferDataItemProvider.getSaveOrderAnswer()).anyTimes(); EasyMock.expect(orderServiceMock.getAutomaticallyMergeLikeItems()).andReturn(true).anyTimes(); EasyMock.expect(orderItemServiceMock.saveOrderItem(EasyMock.isA(OrderItem.class))).andAnswer(OfferDataItemProvider.getSaveOrderItemAnswer()).anyTimes(); EasyMock.expect(fgItemDaoMock.save(EasyMock.isA(FulfillmentGroupItem.class))).andAnswer(OfferDataItemProvider.getSaveFulfillmentGroupItemAnswer()).anyTimes(); EasyMock.expect(multishipOptionServiceMock.findOrderMultishipOptions(EasyMock.isA(Long.class))).andAnswer(new IAnswer<List<OrderMultishipOption>>() { @Override public List<OrderMultishipOption> answer() throws Throwable { return new ArrayList<OrderMultishipOption>(); } }).anyTimes(); multishipOptionServiceMock.deleteAllOrderMultishipOptions(EasyMock.isA(Order.class)); EasyMock.expectLastCall().anyTimes(); EasyMock.expect(fgServiceMock.collapseToOneShippableFulfillmentGroup(EasyMock.isA(Order.class), EasyMock.eq(false))).andAnswer(OfferDataItemProvider.getSameOrderAnswer()).anyTimes(); EasyMock.expect(fgItemDaoMock.create()).andAnswer(OfferDataItemProvider.getCreateFulfillmentGroupItemAnswer()).anyTimes(); fgItemDaoMock.delete(EasyMock.isA(FulfillmentGroupItem.class)); EasyMock.expectLastCall().anyTimes(); EasyMock.expect(offerTimeZoneProcessorMock.getTimeZone(EasyMock.isA(OfferImpl.class))).andReturn(TimeZone.getTimeZone("CST")).anyTimes(); EasyMock.replay(offerDaoMock); EasyMock.replay(orderItemDaoMock); EasyMock.replay(orderServiceMock); EasyMock.replay(orderItemServiceMock); EasyMock.replay(fgItemDaoMock); EasyMock.replay(fgServiceMock); EasyMock.replay(multishipOptionServiceMock); EasyMock.replay(offerTimeZoneProcessorMock); } public void verify() { EasyMock.verify(offerDaoMock); EasyMock.verify(orderItemDaoMock); EasyMock.verify(orderServiceMock); EasyMock.verify(orderItemServiceMock); EasyMock.verify(fgItemDaoMock); EasyMock.verify(fgServiceMock); EasyMock.verify(multishipOptionServiceMock); EasyMock.verify(offerTimeZoneProcessorMock); } public void testFilterItemLevelOffer() throws Exception { replay(); List<PromotableCandidateItemOffer> qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); List<Offer> offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, null, null ); PromotableOrder order = dataProvider.createBasicPromotableOrder(); itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0)); //test that the valid order item offer is included - legacy format - no qualifier //since there's no qualifier, both items can apply // This line is commented out because we are no longer creating legacy offers. //assertTrue(qualifiedOffers.size() == 2 && qualifiedOffers.get(0).getOffer().equals(offers.get(0)) && qualifiedOffers.get(1).getOffer().equals(offers.get(0))); qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0)); //test that the valid order item offer is included //there is a qualifier and the item qualifying criteria requires only 1, therefore there will be only one qualifier in the qualifiers map //we don't know the targets yet, so there's only one CandidateItemOffer for now assertTrue(qualifiedOffers.size() == 1 && qualifiedOffers.get(0).getOffer().equals(offers.get(0)) && qualifiedOffers.get(0).getCandidateQualifiersMap().size() == 1); // Add a subtotal requirement that will be met by the item offer. qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); offers.get(0).setQualifyingItemSubTotal(new Money(1)); itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0)); //test that the valid order item offer is included //there is a qualifier and the item qualifying criteria requires only 1, therefore there will be only one qualifier in the qualifiers map //we don't know the targets yet, so there's only one CandidateItemOffer for now assertTrue(qualifiedOffers.size() == 1 && qualifiedOffers.get(0).getOffer().equals(offers.get(0)) && qualifiedOffers.get(0).getCandidateQualifiersMap().size() == 1); // Add a subtotal requirement that will not be met by the item offer. qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); offers.get(0).setQualifyingItemSubTotal(new Money(99999)); itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0)); // Since the item qualification subTotal is not met, the qualified offer size should // be zero. assertTrue(qualifiedOffers.size() == 0); qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); itemProcessor.filterItemLevelOffer(order, qualifiedOffers, offers.get(0)); //test that the invalid order item offer is excluded assertTrue(qualifiedOffers.size() == 0); verify(); } public void testCouldOfferApplyToOrder() throws Exception { replay(); PromotableOrder order = dataProvider.createBasicPromotableOrder(); List<Offer> offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, null, null ); boolean couldApply = itemProcessor.couldOfferApplyToOrder(offers.get(0), order, order.getDiscountableOrderItems().get(0), order.getFulfillmentGroups().get(0)); //test that the valid order item offer is included assertTrue(couldApply); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()==0", OfferDiscountType.PERCENT_OFF, null, null ); couldApply = itemProcessor.couldOfferApplyToOrder(offers.get(0), order, order.getDiscountableOrderItems().get(0), order.getFulfillmentGroups().get(0)); //test that the invalid order item offer is excluded assertFalse(couldApply); verify(); } public void testCouldOrderItemMeetOfferRequirement() throws Exception { replay(); PromotableOrder order = dataProvider.createBasicPromotableOrder(); List<Offer> offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); OfferQualifyingCriteriaXref xref = offers.get(0).getQualifyingItemCriteriaXref().iterator().next(); boolean couldApply = itemProcessor.couldOrderItemMeetOfferRequirement(xref.getOfferItemCriteria(), order.getDiscountableOrderItems().get(0)); //test that the valid order item offer is included assertTrue(couldApply); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); xref = offers.get(0).getQualifyingItemCriteriaXref().iterator().next(); couldApply = itemProcessor.couldOrderItemMeetOfferRequirement(xref.getOfferItemCriteria(), order.getDiscountableOrderItems().get(0)); //test that the invalid order item offer is excluded assertFalse(couldApply); verify(); } public void testCouldOfferApplyToOrderItems() throws Exception { replay(); PromotableOrder order = dataProvider.createBasicPromotableOrder(); List<Offer> offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); List<PromotableOrderItem> orderItems = new ArrayList<PromotableOrderItem>(); for (PromotableOrderItem orderItem : order.getDiscountableOrderItems()) { orderItems.add(orderItem); } CandidatePromotionItems candidates = itemProcessor.couldOfferApplyToOrderItems(offers.get(0), orderItems); //test that the valid order item offer is included //both cart items are valid for qualification and target assertTrue(candidates.isMatchedQualifier() && candidates.getCandidateQualifiersMap().size() == 1 && candidates.getCandidateQualifiersMap().values().iterator().next().size() == 2 && candidates.isMatchedTarget() && candidates.getCandidateTargetsMap().size() == 1 && candidates.getCandidateTargetsMap().values().iterator().next().size() == 2); offers = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test5\"), MVEL.eval(\"toUpperCase()\",\"test6\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ); candidates = itemProcessor.couldOfferApplyToOrderItems(offers.get(0), orderItems); //test that the invalid order item offer is excluded because there are no qualifying items assertFalse(candidates.isMatchedQualifier() && candidates.getCandidateQualifiersMap().size() == 1); verify(); } private int checkOrderItemOfferAppliedCount(Order order) { int count = 0; for (OrderItem item : order.getOrderItems()) { for (OrderItemPriceDetail detail : item.getOrderItemPriceDetails()) { count = count + (detail.getOrderItemPriceDetailAdjustments().size() * detail.getQuantity()); } } return count; } private int checkOrderItemOfferAppliedQuantity(Order order, Offer offer) { int count = 0; for (OrderItem item : order.getOrderItems()) { for (OrderItemPriceDetail detail : item.getOrderItemPriceDetails()) { for (OrderItemPriceDetailAdjustment adjustment : detail.getOrderItemPriceDetailAdjustments()) { if (adjustment.getOffer().getId().equals(offer.getId())) { count += detail.getQuantity(); } } } } return count; } private int countPriceDetails(Order order) { int count = 0; for (OrderItem item : order.getOrderItems()) { count = count + item.getOrderItemPriceDetails().size(); } return count; } public void testApplyAllItemOffers() throws Exception { replay(); Order order = dataProvider.createBasicOrder(); Offer offer1 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); offer1.setId(1L); List<Offer> offers = new ArrayList<Offer>(); offers.add(offer1); List<PromotableCandidateItemOffer> qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offers, order); assertTrue(order.getTotalAdjustmentsValue().getAmount().doubleValue() > 0); order = dataProvider.createBasicOrder(); qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); offer1.setApplyDiscountToSalePrice(false); ((DiscreteOrderItem) order.getOrderItems().get(0)).getSku().setSalePrice(new Money(1D)); ((DiscreteOrderItem) order.getOrderItems().get(1)).getSku().setSalePrice(new Money(1D)); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offers, order); assertTrue(order.getTotalAdjustmentsValue().getAmount().doubleValue() == 0); verify(); } public void testApplyAdjustments() throws Exception { replay(); Order order = dataProvider.createBasicOrder(); Offer offer1 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); offer1.setId(1L); OfferQualifyingCriteriaXref xref = offer1.getQualifyingItemCriteriaXref().iterator().next(); xref.getOfferItemCriteria().setQuantity(2); offer1.setCombinableWithOtherOffers(false); Offer offer2 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); offer2.setId(2L); List<Offer> offerListWithOneOffer = new ArrayList<Offer>(); offerListWithOneOffer.add(offer1); List<Offer> offerListWithTwoOffers = new ArrayList<Offer>(); offerListWithTwoOffers.add(offer1); offerListWithTwoOffers.add(offer2); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithOneOffer, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 1); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 1); // Add the second offer. The first was nonCombinable so it should still be 1 order = dataProvider.createBasicOrder(); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithTwoOffers, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 0); // Reset offer1 to combinable. Now both should be applied. offer1.setCombinableWithOtherOffers(true); order = dataProvider.createBasicOrder(); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithTwoOffers, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 0); // Offer 1 back to nonCombinable but don't allow discount to the sale price // and make the sale price a better overall offer offer1.setCombinableWithOtherOffers(false); offer1.setApplyDiscountToSalePrice(false); order = dataProvider.createBasicOrder(); ((DiscreteOrderItem) order.getOrderItems().get(1)).getSku().setSalePrice(new Money(10D)); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithOneOffer, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 0); // Try again with two offers. The second should be applied. order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithTwoOffers, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 2); // Trying with 2nd offer as nonCombinable. offer1.setCombinableWithOtherOffers(true); ((DiscreteOrderItem) order.getOrderItems().get(1)).getSku().setSalePrice(null); offer2.setCombinableWithOtherOffers(false); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithOneOffer, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 1); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithTwoOffers, order); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 0); // Set qualifying criteria quantity to 1 // Set qualifying target criteria to 2 order = dataProvider.createBasicOrder(); xref = offer1.getQualifyingItemCriteriaXref().iterator().next(); xref.getOfferItemCriteria().setQuantity(1); OfferTargetCriteriaXref targetXref = offer1.getTargetItemCriteriaXref().iterator().next(); targetXref.getOfferItemCriteria().setQuantity(2); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithOneOffer, order); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 2); // Reset both offers to combinable and the qualifiers as allowing duplicate QUALIFIERs // and Targets offer1.setCombinableWithOtherOffers(true); offer2.setCombinableWithOtherOffers(true); offer1.setOfferItemQualifierRuleType(OfferItemRestrictionRuleType.QUALIFIER_TARGET); offer1.setOfferItemTargetRuleType(OfferItemRestrictionRuleType.QUALIFIER_TARGET); offer2.setOfferItemQualifierRuleType(OfferItemRestrictionRuleType.QUALIFIER_TARGET); offer2.setOfferItemTargetRuleType(OfferItemRestrictionRuleType.QUALIFIER_TARGET); order = dataProvider.createBasicOrder(); order.updatePrices(); offerService.applyAndSaveOffersToOrder(offerListWithTwoOffers, order); assertTrue(checkOrderItemOfferAppliedCount(order) == 4); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 2); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer1) == 2); verify(); } public void testApplyItemQualifiersAndTargets() throws Exception { replay(); List<PromotableCandidateItemOffer> qualifiedOffers = new ArrayList<PromotableCandidateItemOffer>(); Offer offer1 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); offer1.setId(1L); Offer offer2 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); offer2.setId(2L); OfferTargetCriteriaXref targetXref = offer2.getTargetItemCriteriaXref().iterator().next(); targetXref.getOfferItemCriteria().setQuantity(4); offer2.getQualifyingItemCriteriaXref().clear(); offer2.setOfferItemTargetRuleType(OfferItemRestrictionRuleType.TARGET); Offer offer3 = dataProvider.createItemBasedOfferWithItemCriteria( "order.subTotal.getAmount()>20", OfferDiscountType.PERCENT_OFF, "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))", "([MVEL.eval(\"toUpperCase()\",\"test1\"), MVEL.eval(\"toUpperCase()\",\"test2\")] contains MVEL.eval(\"toUpperCase()\", discreteOrderItem.category.name))" ).get(0); PromotableOrder promotableOrder = dataProvider.createBasicPromotableOrder(); itemProcessor.filterItemLevelOffer(promotableOrder, qualifiedOffers, offer1); assertTrue(qualifiedOffers.size() == 1 && qualifiedOffers.get(0).getOffer().equals(offer1) && qualifiedOffers.get(0).getCandidateQualifiersMap().size() == 1); itemProcessor.filterItemLevelOffer(promotableOrder, qualifiedOffers, offer2); assertTrue(qualifiedOffers.size() == 2 && qualifiedOffers.get(1).getOffer().equals(offer2) && qualifiedOffers.get(1).getCandidateQualifiersMap().size() == 0); itemProcessor.filterItemLevelOffer(promotableOrder, qualifiedOffers, offer3); assertTrue(qualifiedOffers.size() == 3 && qualifiedOffers.get(2).getOffer().equals(offer3) && qualifiedOffers.get(2).getCandidateQualifiersMap().size() == 1); // Try with just the second offer. Expect to get 4 targets based on the offer having no qualifiers required // and targeting category test1 or test2 and that the offer requires 4 target criteria. Order order = dataProvider.createBasicOrder(); List<Offer> offerList = new ArrayList<Offer>(); offerList.add(offer2); offerService.applyAndSaveOffersToOrder(offerList, order); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 4); assertTrue(countPriceDetails(order) == 3); // Now try with both offers. Since the targets can be reused, we expect to have 4 targets on offer2 // and 1 target on offer1 order = dataProvider.createBasicOrder(); offerList.add(offer1); // add in second offer (which happens to be offer1) offerService.applyAndSaveOffersToOrder(offerList, order); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer2) == 4); assertTrue(countPriceDetails(order) == 3); // All three offers - offer 3 is now higher priority so the best offer (offer 2) won't be applied order = dataProvider.createBasicOrder(); offerList.add(offer3); // add in second offer (which happens to be offer1) offer3.setPriority(-1); offerService.applyAndSaveOffersToOrder(offerList, order); assertTrue(checkOrderItemOfferAppliedQuantity(order, offer3) == 2); assertTrue(countPriceDetails(order) == 4); verify(); } public class Answer implements IAnswer<CandidateItemOffer> { @Override public CandidateItemOffer answer() throws Throwable { return new CandidateItemOfferImpl(); } } public class Answer2 implements IAnswer<OrderItemAdjustment> { @Override public OrderItemAdjustment answer() throws Throwable { return new OrderItemAdjustmentImpl(); } } }