All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.cq.commerce.common.promotion.PerfectPartnerPromotionHandler Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2012 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.cq.commerce.common.promotion;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.commerce.api.CommerceException;
import com.adobe.cq.commerce.api.CommerceSession;
import com.adobe.cq.commerce.api.CommerceSession.CartEntry;
import com.adobe.cq.commerce.api.PriceInfo;
import com.adobe.cq.commerce.api.Product;
import com.adobe.cq.commerce.api.promotion.Promotion;
import com.adobe.cq.commerce.api.promotion.PromotionHandler;
import com.adobe.cq.commerce.common.CommerceHelper;
import com.adobe.cq.commerce.common.PriceFilter;
import com.day.cq.i18n.I18n;
import com.day.cq.wcm.api.PageManager;

/**
 * PerfectPartnerPromotionHandler applies a configured discount
 * (percentage or absolute) on a product, if the product itself and its partner
 * product have been added to the cart.
 *
 * @deprecated in 6.1 (moved to info.geometrixx.commons.impl.PerfectPartnerPromotionHandler)
 */
@Deprecated
public class PerfectPartnerPromotionHandler implements PromotionHandler {

    private final Logger log = LoggerFactory.getLogger(PerfectPartnerPromotionHandler.class);

    @Reference
    private ResourceResolverFactory resolverFactory = null;

    Map promoCache = new HashMap();

    private PriceInfo calcDiscount(CartEntry cartEntry, String discountType, BigDecimal discountValue) throws CommerceException {
        BigDecimal discount = BigDecimal.ZERO;
        final PriceInfo unitPrice = cartEntry.getPriceInfo(new PriceFilter("UNIT")).get(0);
        if (discountType.equals("percentage")) {
            discount = discount.add(unitPrice.getAmount().multiply(discountValue.divide(new BigDecimal(100.0))));
        } else if (discountType.equals("absolute")) {
            discount = discount.add(discountValue);
        }
        discount = discount.multiply(new BigDecimal(cartEntry.getQuantity()));
        return new PriceInfo(discount, unitPrice.getLocale());
    }

    synchronized public PriceInfo applyCartEntryPromotion(CommerceSession commerceSession, Promotion promotion, CartEntry cartEntry) throws CommerceException {
        ValueMap config = promotion.getConfig();
        String discountType = config.get("discountType", "");
        BigDecimal discountValue = config.get("discountValue", BigDecimal.ZERO);

        String promoPath = promotion.getPath();
        validateCache(promoPath);
        if (promoCache.get(promoPath).getCompanion(cartEntry.getProduct(), commerceSession) != null) {
            // first product also in cart, so apply discount
            return calcDiscount(cartEntry, discountType, discountValue);
        }
        return null;
    }

    public PriceInfo applyOrderPromotion(CommerceSession commerceSession, Promotion promotion) throws CommerceException {
        return null;
    }

    public PriceInfo applyShippingPromotion(CommerceSession commerceSession, Promotion promotion) throws CommerceException {
        return null;
    }

    @Deprecated
    synchronized public String getMessage(SlingHttpServletRequest request, CommerceSession commerceSession, Promotion promotion) throws CommerceException {
        return getDescription(request, commerceSession, promotion);
    }

    synchronized public String getDescription(SlingHttpServletRequest request, CommerceSession commerceSession, Promotion promotion) throws CommerceException {
        String promoPath = promotion.getPath();
        validateCache(promoPath);
        PairingsCache pairingCache = promoCache.get(promoPath);

        List resolved = new ArrayList();
        Map potentials = new HashMap();
        pairingCache.characterizeCart(commerceSession, resolved, potentials);

        PageManager pageManager = request.getResourceResolver().adaptTo(PageManager.class);

        I18n i18n = new I18n(request);
        String description = "";
        for (String pathSet : resolved) {
            if (description.length() > 0) {
                description += "
"; } try { String paths[] = pathSet.split(";"); Product product1 = CommerceHelper.findCurrentProduct(pageManager.getPage(paths[0])); Product product2 = CommerceHelper.findCurrentProduct(pageManager.getPage(paths[1])); description += product1.getTitle() + " + " + product2.getTitle(); } catch (Exception e) { description += i18n.get("error fetching products"); } } for (Map.Entry potential : potentials.entrySet()) { String path1 = potential.getKey(); String path2 = potential.getValue(); if (description.length() > 0) { description += "
"; } try { Product product1 = CommerceHelper.findCurrentProduct(pageManager.getPage(path1)); Product product2 = CommerceHelper.findCurrentProduct(pageManager.getPage(path2)); description += i18n.get("{0} (suggest {1})", null, product1.getTitle(), product2.getTitle()); } catch (Exception e) { description += i18n.get("error fetching products"); } } if (description.length() == 0) { description = i18n.get("no pairings in cart"); } return description; } synchronized public Map getMessages(SlingHttpServletRequest request, CommerceSession commerceSession, Promotion promotion) throws CommerceException { Map messages = new HashMap(); ValueMap config = promotion.getConfig(); String discountType = config.get("discountType", ""); BigDecimal discountValue = config.get("discountValue", BigDecimal.ZERO); String messageTemplate = config.get("message", String.class); if (messageTemplate == null) { return messages; } String promoPath = promotion.getPath(); validateCache(promoPath); PairingsCache pairingCache = promoCache.get(promoPath); I18n i18n = new I18n(request); PageManager pageManager = request.getResourceResolver().adaptTo(PageManager.class); for (CartEntry entry : commerceSession.getCartEntries()) { Product product = entry.getProduct(); String companionPath = pairingCache.getCompanion(product, commerceSession); if (companionPath != null) { String companionTitle; try { Product companion = CommerceHelper.findCurrentProduct(pageManager.getPage(companionPath)); companionTitle = companion.getTitle(); } catch (Exception e) { companionTitle = i18n.get("unknown product"); } PriceInfo discount = calcDiscount(entry, discountType, discountValue); messages.put(entry.getEntryIndex(), MessageFormat.format(messageTemplate, "", companionTitle, discount.getFormattedString())); } } return messages; } synchronized public void getPotentials(CommerceSession commerceSession, Promotion promotion, Map potentials) throws CommerceException { String promoPath = promotion.getPath(); validateCache(promoPath); PairingsCache pairingCache = promoCache.get(promoPath); List resolved = new ArrayList(); pairingCache.characterizeCart(commerceSession, resolved, potentials); } synchronized public void invalidateCaches() { promoCache.clear(); } private class PairingsCache { // // Since a particular product might appear in multiple pairings (and on both sides), we // need to implement separate maps: // // 1) a map from firstProduct : list of secondProducts can be used to determine what // products to generate teasers for when the firstProduct appears in the cart // // 2) a map from secondProduct : list of firstProducts can be used to determine whether // or not to discount the second product // private Map> firstProductMap = new HashMap>(); private Map> secondProductMap = new HashMap>(); public void addPairing(String firstProductPath, String secondProductPath) { if (firstProductPath == null || secondProductPath == null) { return; } if (firstProductMap.containsKey(firstProductPath)) { firstProductMap.get(firstProductPath).add(secondProductPath); } else { List secondProductList = new ArrayList(); secondProductList.add(secondProductPath); firstProductMap.put(firstProductPath, secondProductList); } if (secondProductMap.containsKey(secondProductPath)) { secondProductMap.get(secondProductPath).add(firstProductPath); } else { List firstProductList = new ArrayList(); firstProductList.add(firstProductPath); secondProductMap.put(secondProductPath, firstProductList); } } public String getCompanion(Product product, CommerceSession commerceSession) throws CommerceException { // We want to know if a particular secondProduct should be discounted. So grab the list // of firstProducts which appeared with it in the pairings, and see if any of them are in the // cart. String secondProductPath = getProductPagePath(product); if (secondProductMap.containsKey(secondProductPath)) { List firstProductList = secondProductMap.get(secondProductPath); for (String firstProductPath : firstProductList) { if (productInCart(firstProductPath, commerceSession)) { return firstProductPath; } } } return null; } public void characterizeCart(CommerceSession commerceSession, List resolved, Map potentials) throws CommerceException { for (CartEntry entry : commerceSession.getCartEntries()) { String firstProductPath = getProductPagePath(entry.getProduct()); if (firstProductMap.containsKey(firstProductPath)) { List secondProductsList = firstProductMap.get(firstProductPath); boolean match = false; for (String secondProductPath : secondProductsList) { if (productInCart(secondProductPath, commerceSession)) { resolved.add(firstProductPath + ";" + secondProductPath); match = true; } } if (!match) { // TODO: it'd be nice to have the pairings ranked so we could be more discriminating // But for now, just grab the first: potentials.put(firstProductPath, secondProductsList.get(0)); } } } } // // Turn the product page URL into a simple path for comparison // private String getProductPagePath(Product product) { String href = product.getPagePath(); int extension = href.indexOf(".html"); if (extension > 0) { return href.substring(0, extension); } else { int fragment = href.indexOf('#'); if (fragment > 0) { return href.substring(0, fragment); } } return href; } private boolean productInCart(String productPath, CommerceSession commerceSession) throws CommerceException { for (CartEntry entry : commerceSession.getCartEntries()) { if (getProductPagePath(entry.getProduct()).equals(productPath)) { return true; } } return false; } } // // Make sure the requested promotion is in the cache. // synchronized private void validateCache(String promoPath) { if (!promoCache.containsKey(promoPath)) { ResourceResolver serviceResolver = null; try { PairingsCache pairingsCache = new PairingsCache(); promoCache.put(promoPath, pairingsCache); final Map authenticationInfo = new HashMap(); authenticationInfo.put(ResourceResolverFactory.SUBSERVICE, "frontend"); serviceResolver = resolverFactory.getServiceResourceResolver(authenticationInfo); Resource promoResource = serviceResolver.getResource(promoPath); Resource pairings = promoResource.getChild("jcr:content").getChild("config").getChild("pairings"); Iterator iterator = pairings.listChildren(); while (iterator.hasNext()) { ValueMap pair = ResourceUtil.getValueMap(iterator.next()); pairingsCache.addPairing(pair.get("firstProductPath", String.class), pair.get("secondProductPath", String.class)); } } catch (Exception e) { log.error("Couldn't construct promotion cache for: " + promoPath, e); } finally { if (serviceResolver != null && serviceResolver.isLive()) { serviceResolver.close(); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy