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

com.adobe.cq.commerce.common.DefaultJcrPlacedOrder Maven / Gradle / Ivy

There is a newer version: 6.5.21
Show newest version
package com.adobe.cq.commerce.common;

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2013 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.
 **************************************************************************/

import com.adobe.cq.commerce.api.CommerceException;
import com.adobe.cq.commerce.api.CommerceSession;
import com.adobe.cq.commerce.api.PlacedOrder;
import com.adobe.cq.commerce.api.PriceInfo;
import com.adobe.cq.commerce.api.promotion.PromotionInfo;
import com.adobe.cq.commerce.api.promotion.Voucher;
import com.adobe.cq.commerce.api.promotion.VoucherInfo;
import com.adobe.cq.commerce.impl.promotion.JcrVoucherImpl;
import com.adobe.granite.security.user.UserProperties;
import com.day.cq.commons.LanguageUtil;
import com.day.cq.personalization.UserPropertiesUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.util.ISO9075;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.NodeIterator;
import javax.jcr.Session;
import javax.jcr.query.Query;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Currency;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * The default implementation for a {@link PlacedOrder} stored in a JCR repository, as used by
 * {@link AbstractJcrCommerceSession}.
 *
 * Implementations requiring customization should extend this class and override
 * {@link AbstractJcrCommerceSession#newPlacedOrderImpl(String)} to return said extension.
 *
 * NB: models shopper records in ~/commerce/orders/, not vendor records in /var/commerce/orders/.
 * For access to vendor records, see {@link VendorJcrPlacedOrder}.
 */
public class DefaultJcrPlacedOrder implements PlacedOrder {
    protected static final Logger log = LoggerFactory.getLogger(AbstractJcrCommerceSession.class);

    protected Resource order;

    protected Map details;
    protected List prices;
    protected List entries;

    private AbstractJcrCommerceSession abstractJcrCommerceSession;

    /**
     * Instantiate a new, read-only PlacedOrder record.
     *
     * @param abstractJcrCommerceSession The owning AbstractJcrCommerceSession
     * @param orderId An ID uniquely identifying the placed order.  Can either be the orderId property, or
     *                the path to the order node.  (The path is obviously quicker, but the orderId might
     *                be used to track the order in external systems, such as fulfillment systems.)
     */
    public DefaultJcrPlacedOrder(AbstractJcrCommerceSession abstractJcrCommerceSession, String orderId) {
        this.abstractJcrCommerceSession = abstractJcrCommerceSession;
        order = getPlacedOrder(orderId);
    }

    @Override
    public String getOrderId() throws CommerceException {
        if (details == null) {
            lazyLoadOrderDetails();
        }
        return (String) details.get(abstractJcrCommerceSession.PN_ORDER_ID);
    }

    @Override
    public Map getOrder() throws CommerceException {
        if (details == null) {
            lazyLoadOrderDetails();
        }
        return details;
    }

    //
    // On-demand loads the details member variable from the order data.
    //
    private void lazyLoadOrderDetails() throws CommerceException {
        details = new HashMap();
        if (order != null) {
            final SimpleDateFormat dateFmt = new SimpleDateFormat("dd MMM, yyyy");

            details.put("orderPath", order.getPath());

            ValueMap orderProperties = order.getValueMap();
            for (Map.Entry entry : orderProperties.entrySet()) {
                String key = entry.getKey();
                if ("cartItems".equals(key)) {
                    // returned by getPlacedOrderEntries()
                } else {
                    Object property = entry.getValue();
                    if (property instanceof Calendar) {
                        // explode date into 'property' and 'propertyFormatted'
                        details.put(key, property);
                        details.put(key + "Formatted", dateFmt.format(((Calendar) property).getTime()));
                    } else {
                        details.put(key, property);
                    }
                }
            }

            Resource orderDetailsChild = order.getChild("order-details");
            if (orderDetailsChild != null) {
                ValueMap orderDetailProperties = orderDetailsChild.getValueMap();
                for (ValueMap.Entry detailProperty : orderDetailProperties.entrySet()) {
                    String key = detailProperty.getKey();
                    Object property = detailProperty.getValue();
                    if (property instanceof Calendar) {
                        // explode date into 'property' and 'propertyFormatted'
                        details.put(key, property);
                        details.put(key + "Formatted", dateFmt.format(((Calendar) property).getTime()));
                    } else {
                        details.put(key, property);
                    }
                }
            }

            details.put("orderStatus", abstractJcrCommerceSession.getOrderStatus((String) details.get(abstractJcrCommerceSession.PN_ORDER_ID)));
        }
    }

    @Override
    public List getCartPriceInfo(Predicate filter) throws CommerceException {
        if (prices == null) {
            lazyLoadPriceInfo();
        }

        List filteredPrices = new ArrayList();
        CollectionUtils.select(prices, filter, filteredPrices);
        return filteredPrices;
    }

    @Override
    public String getCartPrice(Predicate filter) throws CommerceException {
        if (prices == null) {
            lazyLoadPriceInfo();
        }

        PriceInfo price = (PriceInfo) CollectionUtils.find(prices, filter);
        if (price != null) {
            return price.getFormattedString();
        } else {
            return "";
        }
    }

    //
    // On-demand loads the prices member variable from the order data.
    //
    protected void lazyLoadPriceInfo() throws CommerceException {
        prices = new ArrayList();

        if (order != null) {
            ValueMap orderMap = order.getValueMap();

            String languageTag = orderMap.get("jcr:language", String.class);
            Locale locale = languageTag != null ? LanguageUtil.getLocale(languageTag) : abstractJcrCommerceSession.getLocale();

            String currencyCode = orderMap.get("currencyCode", String.class);
            if (!currencyCode.equals(Currency.getInstance(locale).getCurrencyCode())) {
                log.error("Currency for locale has changed since order was saved.  Unable to load prices.");
                return;
            }

            //
            // Note: order is important; non-fully-specified price requests will get the first match.
            //

            PriceInfo price = new PriceInfo(orderMap.get("orderTotalPrice", BigDecimal.class), locale);
            price.put(PriceFilter.PN_TYPES, new HashSet(Arrays.asList("ORDER", "TOTAL", currencyCode)));
            prices.add(price);

            price = new PriceInfo(orderMap.get("orderTotalTax", BigDecimal.class), locale);
            price.put(PriceFilter.PN_TYPES, new HashSet(Arrays.asList("ORDER", "TAX", currencyCode)));
            prices.add(price);

            price = new PriceInfo(orderMap.get("cartSubtotal", BigDecimal.class), locale);
            price.put(PriceFilter.PN_TYPES, new HashSet(Arrays.asList("CART", "PRE_TAX", currencyCode)));
            prices.add(price);

            price = new PriceInfo(orderMap.get("orderShipping", BigDecimal.class), locale);
            price.put(PriceFilter.PN_TYPES, new HashSet(Arrays.asList("SHIPPING", currencyCode)));
            prices.add(price);
        }
    }

    @Override
    public List getCartEntries() throws CommerceException {
        if (entries == null) {
            lazyLoadCartEntries();
        }
        return entries;
    }

    //
    // On-demand loads the entries member variable from the order data.
    //
    protected void lazyLoadCartEntries() throws CommerceException {
        entries = new ArrayList();

        if (order != null) {
            String[] serializedEntries = order.getValueMap().get("cartItems", String[].class);
            for (String serializedEntry : serializedEntries) {
                try {
                    CommerceSession.CartEntry entry = abstractJcrCommerceSession.deserializeCartEntry(serializedEntry, entries.size());
                    entries.add(entry);
                } catch (Exception e) {     // NOSONAR (catch any errors thrown attempting to parse/decode entry)
                    log.error("Unable to load product from order: {}", serializedEntry);
                }
            }
        }
    }

    @Override
    public List getPromotions() throws CommerceException {
        List infos = new ArrayList();

        if (order != null) {
            String[] records = order.getValueMap().get("promotions", new String[]{});
            for (String record : records) {
                try {
                    String[] fields = record.split(";", 3);
                    String path = "null".equals(fields[0]) ? null : fields[0];
                    Integer entryIndex = "null".equals(fields[1]) ? null : Integer.parseInt(fields[1]);
                    String message = "null".equals(fields[2]) ? null : fields[2];
                    infos.add(new PromotionInfo(path, "", PromotionInfo.PromotionStatus.FIRED, "", message, entryIndex));
                } catch (Exception e) {     // NOSONAR (catch any errors thrown attempting to parse/decode entry)
                    log.error("Unable to load promotion from order: {}", record);
                }
            }
        }

        return infos;
    }

    @Override
    public List getVoucherInfos() throws CommerceException {
        List infos = new ArrayList();

        if (order != null) {
            String[] records = order.getValueMap().get("vouchers", new String[]{});
            for (String record : records) {
                String[] fields = record.split(";", 3);
                try {
                    if (fields.length == 1) {
                        // 5.6 only wrote out the path of JCR vouchers.  Since we have no way of knowing if the
                        // Voucher has materially changed since the order was placed (or even if the Voucher
                        // still exists, for that matter), just attempt to fetch the voucher code out of it.
                        String path = fields[0];
                        Voucher voucher = new JcrVoucherImpl(order.getResourceResolver().getResource(path));
                        infos.add(new VoucherInfo(voucher.getCode(), voucher.getPath(), "", "", true, ""));
                    } else {
                        // 6.0 serializes code;path;message
                        String code = fields[0].equals("null") ? null : fields[0];
                        String path = fields[1].equals("null") ? null : fields[1];
                        String message = fields[2].equals("null") ? null : fields[2];
                        infos.add(new VoucherInfo(code, path, "", "", true, message));
                    }
                } catch (Exception e) {     // NOSONAR (catch any errors thrown attempting to parse/decode entry)
                    log.error("Unable to load voucher from order: {}", record);
                }
            }
        }

        return infos;
    }

    //
    // Finds the node associated with a placed order.
    //
    protected Resource getPlacedOrder(String orderId) {
        try {
            Session userSession = abstractJcrCommerceSession.resolver.adaptTo(Session.class);
            final UserProperties userProperties = abstractJcrCommerceSession.request.adaptTo(UserProperties.class);
            if (userProperties != null && !UserPropertiesUtil.isAnonymous(userProperties)) {
                UserManager um = ((JackrabbitSession) userSession).getUserManager();
                Authorizable user = um.getAuthorizable(userProperties.getAuthorizableID());

                //
                // Quick way -- orderId is already a path:
                //
                if (orderId.startsWith("/")) {
                    Resource orderResource = abstractJcrCommerceSession.resolver.getResource(orderId);
                    if (orderResource != null && orderId.startsWith(user.getPath() + AbstractJcrCommerceSession.USER_ORDERS_PATH)) {
                        return orderResource;
                    }
                    return null;
                }

                //
                // Slow way -- orderId needs to be looked up in the user's home directory:
                //
                // example query: /jcr:root/home/users/geometrixx/[email protected]/commerce/orders//element(*)[@orderId='foo')]
                StringBuilder buffer = new StringBuilder();
                buffer.append("/jcr:root")
                        .append(ISO9075.encodePath(user.getPath() + AbstractJcrCommerceSession.USER_ORDERS_PATH))
                        .append("/element(*)[@orderId = '")
                        .append(Text.escapeIllegalXpathSearchChars(orderId).replaceAll("'", "''"))
                        .append("']");

                final Query query = userSession.getWorkspace().getQueryManager().createQuery(buffer.toString(), Query.XPATH);
                NodeIterator nodeIterator = query.execute().getNodes();
                if (nodeIterator.hasNext()) {
                    return abstractJcrCommerceSession.resolver.getResource(nodeIterator.nextNode().getPath());
                }
            }
        } catch (Exception e) {     // NOSONAR (fail-safe for when the query above contains errors)
            log.error("Error while searching for order history with orderId '" + orderId + "'", e);
        }
        return null;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy