svn commit: r798500 - in /ofbiz/trunk: applications/order/data/ applications/order/servicedef/ applications/order/src/org/ofbiz/order/order/ applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ applications/product/config/ applications/pro...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r798500 - in /ofbiz/trunk: applications/order/data/ applications/order/servicedef/ applications/order/src/org/ofbiz/order/order/ applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ applications/product/config/ applications/pro...

lektran
Author: lektran
Date: Tue Jul 28 12:13:28 2009
New Revision: 798500

URL: http://svn.apache.org/viewvc?rev=798500&view=rev
Log:
Implemented a services to keep track of products purchased together by creating ProductAssoc records of type ALSO_BOUGHT.  
These associated products are then shown to the customer on the product pages as "Customers who bought this item also bought: ..." sorted by popularity.

Modified:
    ofbiz/trunk/applications/order/data/OrderScheduledServices.xml
    ofbiz/trunk/applications/order/servicedef/services.xml
    ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java
    ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java
    ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ProductDetail.groovy
    ofbiz/trunk/applications/product/config/ProductUiLabels.xml
    ofbiz/trunk/applications/product/servicedef/services_view.xml
    ofbiz/trunk/applications/product/src/org/ofbiz/product/product/ProductServices.java
    ofbiz/trunk/specialpurpose/ecommerce/webapp/ecommerce/catalog/productdetail.ftl

Modified: ofbiz/trunk/applications/order/data/OrderScheduledServices.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/data/OrderScheduledServices.xml?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/data/OrderScheduledServices.xml (original)
+++ ofbiz/trunk/applications/order/data/OrderScheduledServices.xml Tue Jul 28 12:13:28 2009
@@ -29,5 +29,6 @@
     <JobSandbox jobId="8005" jobName="Extend expired Subscriptions" runTime="2000-01-01 03:00:00.000" serviceName="runSubscriptionAutoReorders" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
     <JobSandbox jobId="8006" jobName="Cancels all orders after date" runTime="2009-12-03 03:00:00.000" serviceName="cancelAllBackOrders" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
     <JobSandbox jobId="8007" jobName="Replacement Held Order Auto-Cancel" runTime="2000-01-01 00:00:00.000" serviceName="autoCancelReplacementOrders" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
+    <JobSandbox jobId="8008" jobName="Create Also Bought Product Associations" runTime="2000-01-01 00:00:00.000" serviceName="createAlsoBoughtProductAssocs" poolId="pool" runAsUser="system" tempExprId="MIDNIGHT_DAILY" maxRecurrenceCount="-1"/>
 
 </entity-engine-xml>

Modified: ofbiz/trunk/applications/order/servicedef/services.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/servicedef/services.xml?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/servicedef/services.xml (original)
+++ ofbiz/trunk/applications/order/servicedef/services.xml Tue Jul 28 12:13:28 2009
@@ -1008,4 +1008,25 @@
         <attribute name="shipGroupSeqId" type="String" mode="IN" optional="false"/>
         <attribute name="giftMessage" type="String" mode="IN" optional="true"/>
     </service>
+    <service name="createAlsoBoughtProductAssocs" engine="java" auth="true"
+            location="org.ofbiz.order.order.OrderServices" invoke="createAlsoBoughtProductAssocs">
+        <description>
+            Cycles through all newly created sales orders and creates ProductAssoc records (of type ALSO_BOUGHT) for products
+            that were purchased together.  If a ProductAssoc record already exists then the quantity field is incremented by one.
+            Newly created orders are determined by looking for orders that were created after the JobSandbox.startDateTime of the
+            previous async execution of this service, alternatively the service can be supplied with a orderEntryFromDateTime
+            parameter which will process all orders placed after that date/time or as a final option processAllOrders can be set
+            to true to force a calculation of all orders ever placed with orderEntryFromDateTime being ignored.
+        </description>
+        <attribute name="orderEntryFromDateTime" mode="IN" type="Timestamp" optional="true"/>
+        <attribute name="processAllOrders" mode="IN" type="Boolean" optional="true"/>
+    </service>
+    <service name="createAlsoBoughtProductAssocsForOrder" engine="java" auth="true"
+            location="org.ofbiz.order.order.OrderServices" invoke="createAlsoBoughtProductAssocsForOrder">
+        <description>
+            Creates ProductAssoc records (of type ALSO_BOUGHT) for products that were purchased together in the Order.  If a ProductAssoc record already exists then the quantity field is incremented by one.  If a variant product has
+            been ordered then the association is made to its parent product.
+        </description>
+        <attribute name="orderId" mode="IN" type="String" optional="false"/>
+    </service>
 </services>
\ No newline at end of file

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java Tue Jul 28 12:13:28 2009
@@ -75,7 +75,7 @@
 
     protected GenericValue orderHeader = null;
     protected List orderItemAndShipGrp = null;
-    protected List orderItems = null;
+    protected List<GenericValue> orderItems = null;
     protected List adjustments = null;
     protected List<GenericValue> paymentPrefs = null;
     protected List orderStatuses = null;
@@ -1366,7 +1366,7 @@
     // ========== Order Item Methods ==========
     // ========================================
 
-    public List getOrderItems() {
+    public List<GenericValue> getOrderItems() {
         if (orderItems == null) {
             try {
                 orderItems = orderHeader.getRelated("OrderItem", UtilMisc.toList("orderItemSeqId"));

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderServices.java Tue Jul 28 12:13:28 2009
@@ -21,7 +21,6 @@
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.util.ArrayList;
-import com.ibm.icu.util.Calendar;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
@@ -31,6 +30,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 import javax.transaction.Transaction;
 
@@ -56,10 +56,10 @@
 import org.ofbiz.entity.condition.EntityCondition;
 import org.ofbiz.entity.condition.EntityConditionList;
 import org.ofbiz.entity.condition.EntityExpr;
-import org.ofbiz.entity.condition.EntityJoinOperator;
 import org.ofbiz.entity.condition.EntityOperator;
 import org.ofbiz.entity.transaction.GenericTransactionException;
 import org.ofbiz.entity.transaction.TransactionUtil;
+import org.ofbiz.entity.util.EntityFindOptions;
 import org.ofbiz.entity.util.EntityListIterator;
 import org.ofbiz.entity.util.EntityUtil;
 import org.ofbiz.order.shoppingcart.CartItemModifyException;
@@ -81,6 +81,8 @@
 import org.ofbiz.service.ModelService;
 import org.ofbiz.service.ServiceUtil;
 
+import com.ibm.icu.util.Calendar;
+
 /**
  * Order Processing Services
  */
@@ -91,8 +93,8 @@
     public static final String resource = "OrderUiLabels";
     public static final String resource_error = "OrderErrorUiLabels";
 
-    public static Map salesAttributeRoleMap = FastMap.newInstance();
-    public static Map purchaseAttributeRoleMap = FastMap.newInstance();
+    public static Map<String, String> salesAttributeRoleMap = FastMap.newInstance();
+    public static Map<String, String> purchaseAttributeRoleMap = FastMap.newInstance();
     static {
         salesAttributeRoleMap.put("placingCustomerPartyId", "PLACING_CUSTOMER");
         salesAttributeRoleMap.put("billToCustomerPartyId", "BILL_TO_CUSTOMER");
@@ -145,7 +147,7 @@
                     hasPermission = true;
                 } else {
                     // check sales agent/customer relationship
-                    List repsCustomers = new LinkedList();
+                    List<GenericValue> repsCustomers = new LinkedList<GenericValue>();
                     try {
                         repsCustomers = EntityUtil.filterByDate(userLogin.getRelatedOne("Party").getRelatedByAnd("FromPartyRelationship",
                                 UtilMisc.toMap("roleTypeIdFrom", "AGENT", "roleTypeIdTo", "CUSTOMER", "partyIdTo", partyId)));
@@ -5300,4 +5302,145 @@
         }
         return ServiceUtil.returnSuccess();
     }
+
+    public static Map<String, Object> createAlsoBoughtProductAssocs(DispatchContext dctx, Map context) {
+        GenericDelegator delegator = dctx.getDelegator();
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        // All orders with an entryDate > orderEntryFromDateTime will be processed
+        Timestamp orderEntryFromDateTime = (Timestamp) context.get("orderEntryFromDateTime");
+        // If true all orders ever created will be processed and any pre-existing ALSO_BOUGHT ProductAssocs will be expired
+        boolean processAllOrders = context.get("processAllOrders") == null ? false : (Boolean) context.get("processAllOrders");
+        if (orderEntryFromDateTime == null && !processAllOrders) {
+            // No from date supplied, check to see when this service last ran and use the startDateTime
+            EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toMap("statusId", "SERVICE_FINISHED", "serviceName", "createAlsoBoughtProductAssocs"));
+            EntityFindOptions efo = new EntityFindOptions();
+            efo.setMaxRows(1);
+            try {
+                GenericValue lastRunJobSandbox = EntityUtil.getFirst(delegator.findList("JobSandbox", cond, null, UtilMisc.toList("startDateTime DESC"), efo, false));
+                if (lastRunJobSandbox != null) {
+                    orderEntryFromDateTime = lastRunJobSandbox.getTimestamp("startDateTime");
+                }
+            } catch (GenericEntityException e) {
+                Debug.logError(e, module);
+            }
+            if (orderEntryFromDateTime == null) {
+                // Still null, process all orders
+                processAllOrders = true;
+            }
+        }
+        if (processAllOrders) {
+            // Expire any pre-existing ALSO_BOUGHT ProductAssocs in preparation for reprocessing
+            EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(
+                    EntityCondition.makeCondition("productAssocTypeId", "ALSO_BOUGHT"),
+                    EntityCondition.makeConditionDate("fromDate", "thruDate")
+            ));
+            try {
+                delegator.storeByCondition("ProductAssoc", UtilMisc.toMap("thruDate", UtilDateTime.nowTimestamp()), cond);
+            } catch (GenericEntityException e) {
+                Debug.logError(e, module);
+            }
+        }
+        EntityListIterator eli = null;
+        try {
+            List<EntityExpr> orderCondList = UtilMisc.toList(EntityCondition.makeCondition("orderTypeId", "SALES_ORDER"));
+            if (!processAllOrders && orderEntryFromDateTime != null) {
+                orderCondList.add(EntityCondition.makeCondition("entryDate", EntityOperator.GREATER_THAN, orderEntryFromDateTime));
+            }
+            EntityCondition cond = EntityCondition.makeCondition(orderCondList);
+            eli = delegator.find("OrderHeader", cond, null, null, UtilMisc.toList("entryDate ASC"), null);
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+            return ServiceUtil.returnError(e.getMessage());
+        }
+        if (eli != null) {
+            GenericValue orderHeader = null;
+            while ((orderHeader = eli.next()) != null) {
+                Map svcIn = FastMap.newInstance();
+                svcIn.put("userLogin", context.get("userLogin"));
+                svcIn.put("orderId", orderHeader.get("orderId"));
+                try {
+                    dispatcher.runSync("createAlsoBoughtProductAssocsForOrder", svcIn);
+                } catch (GenericServiceException e) {
+                    Debug.logError(e, module);
+                }
+            }
+            try {
+                eli.close();
+            } catch (GenericEntityException e) {
+                Debug.logError(e, module);
+            }
+        }
+        return ServiceUtil.returnSuccess();
+    }
+    
+    public static Map<String, Object> createAlsoBoughtProductAssocsForOrder(DispatchContext dctx, Map context) {
+        LocalDispatcher dispatcher = dctx.getDispatcher();
+        GenericDelegator delegator = dctx.getDelegator();
+        String orderId = (String) context.get("orderId");
+        OrderReadHelper orh = new OrderReadHelper(delegator, orderId);
+        List<GenericValue> orderItems = orh.getOrderItems();
+        // In order to improve efficiency a little bit, we will always create the ProductAssoc records
+        // with productId < productIdTo when the two are compared.  This way when checking for an existing
+        // record we don't have to check both possible combinations of productIds
+        TreeSet<String> productIdSet = new TreeSet<String>();
+        if (orderItems != null) {
+            for (GenericValue orderItem : orderItems) {
+                String productId = orderItem.getString("productId");
+                if (productId != null) {
+                    GenericValue parentProduct = ProductWorker.getParentProduct(productId, delegator);
+                    if (parentProduct != null) productId = parentProduct.getString("productId");
+                    productIdSet.add(productId);
+                }
+            }
+        }
+        TreeSet<String> productIdToSet = new TreeSet<String>(productIdSet);
+        for (String productId : productIdSet) {
+            productIdToSet.remove(productId);
+            for (String productIdTo : productIdToSet) {
+                EntityCondition cond = EntityCondition.makeCondition(
+                        UtilMisc.toList(
+                                EntityCondition.makeCondition("productId", productId),
+                                EntityCondition.makeCondition("productIdTo", productIdTo),
+                                EntityCondition.makeCondition("productAssocTypeId", "ALSO_BOUGHT"),
+                                EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, UtilDateTime.nowTimestamp()),
+                                EntityCondition.makeCondition("thruDate", null)
+                        )
+                );
+                GenericValue existingProductAssoc = null;
+                try {
+                    // No point in using the cache because of the filterByDateExpr
+                    existingProductAssoc = EntityUtil.getFirst(delegator.findList("ProductAssoc", cond, null, UtilMisc.toList("fromDate DESC"), null, false));
+                } catch (GenericEntityException e) {
+                    Debug.logError(e, module);
+                }
+                try {
+                    if (existingProductAssoc != null) {
+                        BigDecimal newQuantity = existingProductAssoc.getBigDecimal("quantity");
+                        if (newQuantity == null || newQuantity.compareTo(BigDecimal.ZERO) < 0) {
+                            newQuantity = BigDecimal.ZERO;
+                        }
+                        newQuantity = newQuantity.add(BigDecimal.ONE);
+                        ModelService updateProductAssoc = dctx.getModelService("updateProductAssoc");
+                        Map<String, Object> updateCtx = updateProductAssoc.makeValid(context, ModelService.IN_PARAM, true, null);
+                        updateCtx.putAll(updateProductAssoc.makeValid(existingProductAssoc, ModelService.IN_PARAM));
+                        updateCtx.put("quantity", newQuantity);
+                        dispatcher.runSync("updateProductAssoc", updateCtx);
+                    } else {
+                        Map<String, Object> createCtx = FastMap.newInstance();
+                        createCtx.put("userLogin", context.get("userLogin"));
+                        createCtx.put("productId", productId);
+                        createCtx.put("productIdTo", productIdTo);
+                        createCtx.put("productAssocTypeId", "ALSO_BOUGHT");
+                        createCtx.put("fromDate", UtilDateTime.nowTimestamp());
+                        createCtx.put("quantity", BigDecimal.ONE);
+                        dispatcher.runSync("createProductAssoc", createCtx);
+                    }
+                } catch (GenericServiceException e) {
+                    Debug.logError(e, module);
+                }
+            }
+        }
+        
+        return ServiceUtil.returnSuccess();
+    }
 }

Modified: ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ProductDetail.groovy
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ProductDetail.groovy?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ProductDetail.groovy (original)
+++ ofbiz/trunk/applications/order/webapp/ordermgr/WEB-INF/actions/entry/catalog/ProductDetail.groovy Tue Jul 28 12:13:28 2009
@@ -379,6 +379,9 @@
     }
 
     // get product associations
+    alsoBoughtProducts = dispatcher.runSync("getAssociatedProducts", [productId : productId, type : "ALSO_BOUGHT", checkViewAllow : true, prodCatalogId : currentCatalogId, bidirectional : true, sortDescending : true]);
+    context.alsoBoughtProducts = alsoBoughtProducts.assocProducts;
+
     obsoleteProducts = dispatcher.runSync("getAssociatedProducts", [productId : productId, type : "PRODUCT_OBSOLESCENCE", checkViewAllow : true, prodCatalogId : currentCatalogId]);
     context.obsoleteProducts = obsoleteProducts.assocProducts;
 

Modified: ofbiz/trunk/applications/product/config/ProductUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/config/ProductUiLabels.xml?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/config/ProductUiLabels.xml (original)
+++ ofbiz/trunk/applications/product/config/ProductUiLabels.xml Tue Jul 28 12:13:28 2009
@@ -7061,6 +7061,9 @@
         <value xml:lang="th">Allow USPS Addr (PO Box, RR, etc)</value>
         <value xml:lang="zh">允许USPS地址 (邮箱、 退货率等)</value>
     </property>
+    <property key="ProductAlsoBought">
+        <value xml:lang="en">Customers who bought this item also bought:</value>
+    </property>
     <property key="ProductAlternate">
         <value xml:lang="de">Alternative</value>
         <value xml:lang="en">Alternate</value>

Modified: ofbiz/trunk/applications/product/servicedef/services_view.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/servicedef/services_view.xml?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/servicedef/services_view.xml (original)
+++ ofbiz/trunk/applications/product/servicedef/services_view.xml Tue Jul 28 12:13:28 2009
@@ -63,12 +63,19 @@
     </service>
     <service name="getAssociatedProducts" engine="java"
             location="org.ofbiz.product.product.ProductServices" invoke="prodFindAssociatedByType">
-        <description>Finds associated products by the defined type.</description>
+        <description>
+            Finds associated products by the defined type.  Only one of either productId or productIdTo can be supplied,
+            not both.  If bidirectional is set to true then the passed in productId will be treated as both a productId
+            and a productIdTo (defaults to false).  If sortDescending is true then assocProducts will be returned sorted
+            by sequenceNum descending (defaults to false).
+        </description>
         <attribute name="productId" type="String" mode="IN" optional="true"/>
         <attribute name="productIdTo" type="String" mode="IN" optional="true"/>
         <attribute name="checkViewAllow" type="Boolean" mode="IN" optional="true"/>
         <attribute name="prodCatalogId" type="String" mode="IN" optional="true"/>
         <attribute name="type" type="String" mode="IN"/>
+        <attribute name="bidirectional" type="Boolean" mode="IN" optional="true"/>
+        <attribute name="sortDescending" type="Boolean" mode="IN" optional="true"/>
         <attribute name="assocProducts" type="java.util.Collection" mode="OUT" optional="true"/>
     </service>
     <service name="getProductFeatures" engine="java"

Modified: ofbiz/trunk/applications/product/src/org/ofbiz/product/product/ProductServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/src/org/ofbiz/product/product/ProductServices.java?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/applications/product/src/org/ofbiz/product/product/ProductServices.java (original)
+++ ofbiz/trunk/applications/product/src/org/ofbiz/product/product/ProductServices.java Tue Jul 28 12:13:28 2009
@@ -43,6 +43,8 @@
 import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.condition.EntityCondition;
+import org.ofbiz.entity.condition.EntityJoinOperator;
 import org.ofbiz.entity.util.EntityUtil;
 import org.ofbiz.product.image.ScaleImage;
 import org.ofbiz.product.catalog.CatalogWorker;
@@ -423,8 +425,12 @@
         String errMsg = null;
 
         Boolean cvaBool = (Boolean) context.get("checkViewAllow");
-        boolean checkViewAllow = (cvaBool == null ? false : cvaBool.booleanValue());
+        boolean checkViewAllow = (cvaBool == null ? false : cvaBool);
         String prodCatalogId = (String) context.get("prodCatalogId");
+        Boolean bidirectional = (Boolean) context.get("bidirectional");
+        bidirectional = bidirectional == null ? false : bidirectional;
+        Boolean sortDescending = (Boolean) context.get("sortDescending");
+        sortDescending = sortDescending == null ? false : sortDescending;
 
         if (productId == null && productIdTo == null) {
             errMsg = UtilProperties.getMessage(resource,"productservices.both_productId_and_productIdTo_cannot_be_null", locale);
@@ -462,11 +468,28 @@
 
         try {
             List<GenericValue> productAssocs = null;
+            
+            List<String> orderBy = FastList.newInstance();
+            if (sortDescending) {
+                orderBy.add("sequenceNum DESC");
+            } else {
+                orderBy.add("sequenceNum");
+            }
 
-            if (productIdTo == null) {
-                productAssocs = product.getRelatedCache("MainProductAssoc", UtilMisc.toMap("productAssocTypeId", type), UtilMisc.toList("sequenceNum"));
+            if (bidirectional) {
+                EntityCondition cond = EntityCondition.makeCondition(
+                        UtilMisc.toList(
+                                EntityCondition.makeCondition("productId", productId),
+                                EntityCondition.makeCondition("productIdTo", productId)
+                        ), EntityJoinOperator.OR);
+                cond = EntityCondition.makeCondition(cond, EntityCondition.makeCondition("productAssocTypeId", type));
+                productAssocs = delegator.findList("ProductAssoc", cond, null, orderBy, null, true);
             } else {
-                productAssocs = product.getRelatedCache("AssocProductAssoc", UtilMisc.toMap("productAssocTypeId", type), UtilMisc.toList("sequenceNum"));
+                if (productIdTo == null) {
+                    productAssocs = product.getRelatedCache("MainProductAssoc", UtilMisc.toMap("productAssocTypeId", type), orderBy);
+                } else {
+                    productAssocs = product.getRelatedCache("AssocProductAssoc", UtilMisc.toMap("productAssocTypeId", type), orderBy);
+                }
             }
             // filter the list by date
             productAssocs = EntityUtil.filterByDate(productAssocs);

Modified: ofbiz/trunk/specialpurpose/ecommerce/webapp/ecommerce/catalog/productdetail.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/ecommerce/webapp/ecommerce/catalog/productdetail.ftl?rev=798500&r1=798499&r2=798500&view=diff
==============================================================================
--- ofbiz/trunk/specialpurpose/ecommerce/webapp/ecommerce/catalog/productdetail.ftl (original)
+++ ofbiz/trunk/specialpurpose/ecommerce/webapp/ecommerce/catalog/productdetail.ftl Tue Jul 28 12:13:28 2009
@@ -696,13 +696,20 @@
 
     <div class="productsummary-container">
     <#list assocProducts as productAssoc>
+        <#if productAssoc.productId == product.productId>
+            <#assign assocProductId = productAssoc.productIdTo/>
+        <#else/>
+            <#assign assocProductId = productAssoc.productId/>
+        </#if>
         <div>
-          <a href="<@ofbizUrl>${targetRequest}/<#if categoryId?exists>~category_id=${categoryId}/</#if>~product_id=${productAssoc.productIdTo?if_exists}</@ofbizUrl>" class="buttontext">
-            ${productAssoc.productIdTo?if_exists}
+          <a href="<@ofbizUrl>${targetRequest}/<#if categoryId?exists>~category_id=${categoryId}/</#if>~product_id=${assocProductId}</@ofbizUrl>" class="buttontext">
+            ${assocProductId}
           </a>
-          - <b>${productAssoc.reason?if_exists}</b>
+        <#if productAssoc.reason?has_content>
+          - <b>${productAssoc.reason}</b>
+        </#if>
         </div>
-      ${setRequestAttribute("optProductId", productAssoc.productIdTo)}
+      ${setRequestAttribute("optProductId", assocProductId)}
       ${setRequestAttribute("listIndex", listIndex)}
       ${setRequestAttribute("formNamePrefix", formNamePrefix)}
       <#if targetRequestName?has_content>
@@ -723,6 +730,8 @@
 <#assign listIndex = 1>
 ${setRequestAttribute("productValue", productValue)}
 <div id="associated-products">
+    <#-- also bought -->
+    <@associated assocProducts=alsoBoughtProducts beforeName="" showName="N" afterName="${uiLabelMap.ProductAlsoBought}" formNamePrefix="albt" targetRequestName=""/>
     <#-- obsolete -->
     <@associated assocProducts=obsoleteProducts beforeName="" showName="Y" afterName=" ${uiLabelMap.ProductObsolete}" formNamePrefix="obs" targetRequestName=""/>
     <#-- cross sell -->