| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| *******************************************************************************/ |
| package org.ofbiz.order.shoppingcart.product; |
| |
| import java.math.BigDecimal; |
| import java.math.MathContext; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.servlet.ServletRequest; |
| import javax.servlet.http.HttpServletRequest; |
| |
| import javolution.util.FastList; |
| import javolution.util.FastMap; |
| |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.UtilGenerics; |
| import org.ofbiz.base.util.UtilMisc; |
| import org.ofbiz.base.util.UtilNumber; |
| import org.ofbiz.base.util.UtilValidate; |
| import org.ofbiz.entity.Delegator; |
| import org.ofbiz.entity.GenericEntity; |
| import org.ofbiz.entity.GenericEntityException; |
| import org.ofbiz.entity.GenericValue; |
| import org.ofbiz.entity.util.EntityUtil; |
| import org.ofbiz.order.shoppingcart.ShoppingCart; |
| import org.ofbiz.order.shoppingcart.ShoppingCartItem; |
| import org.ofbiz.product.catalog.CatalogWorker; |
| import org.ofbiz.product.category.CategoryWorker; |
| import org.ofbiz.product.product.ProductWorker; |
| |
| |
| public class ProductDisplayWorker { |
| |
| public static final String module = ProductDisplayWorker.class.getName(); |
| |
| /* ========================================================================================*/ |
| |
| /* ============================= Special Data Retrieval Methods ===========================*/ |
| |
| public static List<GenericValue> getRandomCartProductAssoc(ServletRequest request, boolean checkViewAllow) { |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| HttpServletRequest httpRequest = (HttpServletRequest) request; |
| ShoppingCart cart = (ShoppingCart) httpRequest.getSession().getAttribute("shoppingCart"); |
| |
| if (cart == null || cart.size() <= 0) return null; |
| |
| List<GenericValue> cartAssocs = null; |
| try { |
| Map<String, GenericValue> products = FastMap.newInstance(); |
| |
| Iterator<ShoppingCartItem> cartiter = cart.iterator(); |
| |
| while (cartiter != null && cartiter.hasNext()) { |
| ShoppingCartItem item = cartiter.next(); |
| // Collection upgradeProducts = delegator.findByAnd("ProductAssoc", UtilMisc.toMap("productId", item.getProductId(), "productAssocTypeId", "PRODUCT_UPGRADE"), null, true); |
| List<GenericValue> complementProducts = delegator.findByAnd("ProductAssoc", UtilMisc.toMap("productId", item.getProductId(), "productAssocTypeId", "PRODUCT_COMPLEMENT"), null, true); |
| // since ProductAssoc records have a fromDate and thruDate, we can filter by now so that only assocs in the date range are included |
| complementProducts = EntityUtil.filterByDate(complementProducts); |
| |
| List<GenericValue> productsCategories = delegator.findByAnd("ProductCategoryMember", UtilMisc.toMap("productId", item.getProductId()), null, true); |
| productsCategories = EntityUtil.filterByDate(productsCategories, true); |
| if (productsCategories != null) { |
| for(GenericValue productsCategoryMember : productsCategories) { |
| GenericValue productsCategory = productsCategoryMember.getRelatedOne("ProductCategory", true); |
| if ("CROSS_SELL_CATEGORY".equals(productsCategory.getString("productCategoryTypeId"))) { |
| List<GenericValue> curPcms = productsCategory.getRelated("ProductCategoryMember", null, null, true); |
| if (curPcms != null) { |
| for(GenericValue curPcm : curPcms) { |
| if (!products.containsKey(curPcm.getString("productId"))) { |
| GenericValue product = curPcm.getRelatedOne("Product", true); |
| products.put(product.getString("productId"), product); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (UtilValidate.isNotEmpty(complementProducts)) { |
| for(GenericValue productAssoc : complementProducts) { |
| if (!products.containsKey(productAssoc.getString("productIdTo"))) { |
| GenericValue product = productAssoc.getRelatedOne("AssocProduct", true); |
| products.put(product.getString("productId"), product); |
| } |
| } |
| } |
| } |
| |
| // remove all products that are already in the cart |
| cartiter = cart.iterator(); |
| while (cartiter != null && cartiter.hasNext()) { |
| ShoppingCartItem item = cartiter.next(); |
| products.remove(item.getProductId()); |
| } |
| |
| // if desired check view allow category |
| if (checkViewAllow) { |
| String currentCatalogId = CatalogWorker.getCurrentCatalogId(request); |
| String viewProductCategoryId = CatalogWorker.getCatalogViewAllowCategoryId(delegator, currentCatalogId); |
| if (viewProductCategoryId != null) { |
| List<GenericValue> tempList = FastList.newInstance(); |
| tempList.addAll(products.values()); |
| tempList = CategoryWorker.filterProductsInCategory(delegator, tempList, viewProductCategoryId, "productId"); |
| cartAssocs = FastList.newInstance(); |
| cartAssocs.addAll(tempList); |
| } |
| } |
| |
| if (cartAssocs == null) { |
| cartAssocs = FastList.newInstance(); |
| cartAssocs.addAll(products.values()); |
| } |
| |
| // randomly remove products while there are more than 3 |
| while (cartAssocs.size() > 3) { |
| int toRemove = (int) (Math.random() * cartAssocs.size()); |
| cartAssocs.remove(toRemove); |
| } |
| } catch (GenericEntityException e) { |
| Debug.logWarning(e, module); |
| } |
| |
| if (UtilValidate.isNotEmpty(cartAssocs)) { |
| return cartAssocs; |
| } else { |
| return null; |
| } |
| } |
| |
| public static Map<String, Object> getQuickReorderProducts(ServletRequest request) { |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| HttpServletRequest httpRequest = (HttpServletRequest) request; |
| GenericValue userLogin = (GenericValue) httpRequest.getSession().getAttribute("userLogin"); |
| Map<String, Object> results = FastMap.newInstance(); |
| |
| if (userLogin == null) userLogin = (GenericValue) httpRequest.getSession().getAttribute("autoUserLogin"); |
| if (userLogin == null) return results; |
| |
| try { |
| Map<String, GenericValue> products = UtilGenerics.checkMap(httpRequest.getSession().getAttribute("_QUICK_REORDER_PRODUCTS_")); |
| Map<String, BigDecimal> productQuantities = UtilGenerics.checkMap(httpRequest.getSession().getAttribute("_QUICK_REORDER_PRODUCT_QUANTITIES_")); |
| Map<String, Integer> productOccurances = UtilGenerics.checkMap(httpRequest.getSession().getAttribute("_QUICK_REORDER_PRODUCT_OCCURANCES_")); |
| |
| if (products == null || productQuantities == null || productOccurances == null) { |
| products = new HashMap<String, GenericValue>(); |
| productQuantities = new HashMap<String, BigDecimal>(); |
| // keep track of how many times a product occurs in order to find averages and rank by purchase amount |
| productOccurances = new HashMap<String, Integer>(); |
| |
| // get all order role entities for user by customer role type : PLACING_CUSTOMER |
| List<GenericValue> orderRoles = delegator.findByAnd("OrderRole", UtilMisc.toMap("partyId", userLogin.get("partyId"), "roleTypeId", "PLACING_CUSTOMER"), null, false); |
| Iterator<GenericValue> ordersIter = UtilMisc.toIterator(orderRoles); |
| |
| while (ordersIter != null && ordersIter.hasNext()) { |
| GenericValue orderRole = ordersIter.next(); |
| // for each order role get all order items |
| List<GenericValue> orderItems = orderRole.getRelated("OrderItem", null, null, false); |
| Iterator<GenericValue> orderItemsIter = UtilMisc.toIterator(orderItems); |
| |
| while (orderItemsIter != null && orderItemsIter.hasNext()) { |
| GenericValue orderItem = orderItemsIter.next(); |
| String productId = orderItem.getString("productId"); |
| if (UtilValidate.isNotEmpty(productId)) { |
| // for each order item get the associated product |
| GenericValue product = orderItem.getRelatedOne("Product", true); |
| |
| products.put(product.getString("productId"), product); |
| |
| BigDecimal curQuant = productQuantities.get(product.get("productId")); |
| |
| if (curQuant == null) curQuant = BigDecimal.ZERO; |
| BigDecimal orderQuant = orderItem.getBigDecimal("quantity"); |
| |
| if (orderQuant == null) orderQuant = BigDecimal.ZERO; |
| productQuantities.put(product.getString("productId"), curQuant.add(orderQuant)); |
| |
| Integer curOcc = productOccurances.get(product.get("productId")); |
| |
| if (curOcc == null) curOcc = Integer.valueOf(0); |
| productOccurances.put(product.getString("productId"), Integer.valueOf(curOcc.intValue() + 1)); |
| } |
| } |
| } |
| |
| // go through each product quantity and divide it by the occurances to get the average |
| for (Map.Entry<String, BigDecimal> entry : productQuantities.entrySet()) { |
| String prodId = entry.getKey(); |
| BigDecimal quantity = entry.getValue(); |
| Integer occs = productOccurances.get(prodId); |
| BigDecimal nqint = quantity.divide(new BigDecimal(occs), new MathContext(10)); |
| |
| if (nqint.compareTo(BigDecimal.ONE) < 0) nqint = BigDecimal.ONE; |
| productQuantities.put(prodId, nqint); |
| } |
| |
| httpRequest.getSession().setAttribute("_QUICK_REORDER_PRODUCTS_", new HashMap<String, GenericValue>(products)); |
| httpRequest.getSession().setAttribute("_QUICK_REORDER_PRODUCT_QUANTITIES_", new HashMap<String, BigDecimal>(productQuantities)); |
| httpRequest.getSession().setAttribute("_QUICK_REORDER_PRODUCT_OCCURANCES_", new HashMap<String, Integer>(productOccurances)); |
| } else { |
| // make a copy since we are going to change them |
| products = new HashMap<String, GenericValue>(products); |
| productQuantities = new HashMap<String, BigDecimal>(productQuantities); |
| productOccurances = new HashMap<String, Integer>(productOccurances); |
| } |
| |
| // remove all products that are already in the cart |
| ShoppingCart cart = (ShoppingCart) httpRequest.getSession().getAttribute("shoppingCart"); |
| if (UtilValidate.isNotEmpty(cart)) { |
| for(ShoppingCartItem item : cart) { |
| String productId = item.getProductId(); |
| products.remove(productId); |
| productQuantities.remove(productId); |
| productOccurances.remove(productId); |
| } |
| } |
| |
| // if desired check view allow category |
| //if (checkViewAllow) { |
| //Set prodKeySet = products.keySet(); |
| String currentCatalogId = CatalogWorker.getCurrentCatalogId(request); |
| String viewProductCategoryId = CatalogWorker.getCatalogViewAllowCategoryId(delegator, currentCatalogId); |
| if (viewProductCategoryId != null) { |
| for (Map.Entry<String, GenericValue> entry : products.entrySet()) { |
| String productId = entry.getKey(); |
| if (!CategoryWorker.isProductInCategory(delegator, productId, viewProductCategoryId)) { |
| products.remove(productId); |
| productQuantities.remove(productId); |
| productOccurances.remove(productId); |
| } |
| } |
| } |
| //} |
| |
| List<GenericValue> reorderProds = FastList.newInstance(); |
| reorderProds.addAll(products.values()); |
| |
| /* |
| //randomly remove products while there are more than 5 |
| while (reorderProds.size() > 5) { |
| int toRemove = (int)(Math.random()*(double)(reorderProds.size())); |
| reorderProds.remove(toRemove); |
| } |
| */ |
| |
| // sort descending by new metric... |
| BigDecimal occurancesModifier = BigDecimal.ONE; |
| BigDecimal quantityModifier = BigDecimal.ONE; |
| Map<String, Object> newMetric = FastMap.newInstance(); |
| for (Map.Entry<String, Integer> entry : productOccurances.entrySet()) { |
| String prodId = entry.getKey(); |
| Integer quantity = entry.getValue(); |
| BigDecimal occs = productQuantities.get(prodId); |
| //For quantity we should test if we allow to add decimal quantity for this product an productStore : if not then round to 0 |
| if(! ProductWorker.isDecimalQuantityOrderAllowed(delegator, prodId, cart.getProductStoreId())){ |
| occs = occs.setScale(0, UtilNumber.getBigDecimalRoundingMode("order.rounding")); |
| } |
| else { |
| occs = occs.setScale(UtilNumber.getBigDecimalScale("order.decimals"), UtilNumber.getBigDecimalRoundingMode("order.rounding")); |
| } |
| productQuantities.put(prodId, occs); |
| BigDecimal nqdbl = quantityModifier.multiply(new BigDecimal(quantity)).add(occs.multiply(occurancesModifier)); |
| |
| newMetric.put(prodId, nqdbl); |
| } |
| reorderProds = productOrderByMap(reorderProds, newMetric, true); |
| |
| // remove extra products - only return 5 |
| while (reorderProds.size() > 5) { |
| reorderProds.remove(reorderProds.size() - 1); |
| } |
| |
| results.put("products", reorderProds); |
| results.put("quantities", productQuantities); |
| } catch (GenericEntityException e) { |
| Debug.logWarning(e, module); |
| } |
| |
| return results; |
| } |
| |
| public static List<GenericValue> productOrderByMap(List<GenericValue> values, Map<String, Object> orderByMap, boolean descending) { |
| if (values == null) return null; |
| if (values.size() == 0) return UtilMisc.toList(values); |
| |
| List<GenericValue> result = FastList.newInstance(); |
| result.addAll(values); |
| |
| Collections.sort(result, new ProductByMapComparator(orderByMap, descending)); |
| return result; |
| } |
| |
| static class ProductByMapComparator implements Comparator<Object> { |
| private Map<String, Object> orderByMap; |
| private boolean descending; |
| |
| ProductByMapComparator(Map<String, Object> orderByMap, boolean descending) { |
| this.orderByMap = orderByMap; |
| this.descending = descending; |
| } |
| |
| public int compare(java.lang.Object prod1, java.lang.Object prod2) { |
| int result = compareAsc((GenericEntity) prod1, (GenericEntity) prod2); |
| |
| if (descending) { |
| result = -result; |
| } |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private int compareAsc(GenericEntity prod1, GenericEntity prod2) { |
| Object value = orderByMap.get(prod1.get("productId")); |
| Object value2 = orderByMap.get(prod2.get("productId")); |
| |
| // null is defined as the smallest possible value |
| if (value == null) return value2 == null ? 0 : -1; |
| return ((Comparable<Object>) value).compareTo(value2); |
| } |
| |
| @Override |
| public boolean equals(java.lang.Object obj) { |
| if ((obj != null) && (obj instanceof ProductByMapComparator)) { |
| ProductByMapComparator that = (ProductByMapComparator) obj; |
| |
| return this.orderByMap.equals(that.orderByMap) && this.descending == that.descending; |
| } else { |
| return false; |
| } |
| } |
| } |
| } |