To view the current price of an item, all of the items in the Shopping Cart must be accessed and all of the correct price information for each of these items must be accessed.

The component located at /atg/commerce/order/OrderHolder is responsible for maintaining all of the customer’s orders. (ATG Consumer Commerce allows a customer to have more than one shopping cart where she can store orders. See the Working With Purchase Process Objects chapter of the ATG Commerce Programming Guide for more information on the OrderHolder component.) The Pioneer Cycling store supports a single Shopping Cart that is referenced in the OrderHolder component at /atg/commerce/order/OrderHolder.current. We accessed this component through the /atg/commerce/order/ShoppingCartModifier component. The ShoppingCartModifier component has an order property that is the current order from the OrderHolder component. We used the following JSP to reference the customer’s current Order object:

<dsp:valueof bean="ShoppingCartModifier.Order"/>

On the Shopping Cart page, the contents of the customer’s order are displayed with a ForEach component that iterates through each ShippingGroup in the order and then iterates through the items in each. It would be possible to simply have the ForEach iterate through each CommerceItem in the order directly. However, we decided that each ShippingGroup and its items should be displayed separately. The following code snippet, taken from cart.jsp, shows the nested iteration.

<% atg.servlet.DynamoHttpServletRequest dRequest =
      atg.servlet.ServletUtil.getDynamoRequest(request);
%>

. . .

<dsp:droplet name="ForEach">
 <dsp:param bean="ShoppingCartModifier.Order.ShippingGroups" name="array"/>
 <dsp:param name="elementName" value="ShippingGroup"/>
 <dsp:param name="indexName" value="shippingGroupIndex"/>
 <dsp:oparam name="output">
  <core:switch value='<%=
        dRequest.getParameter("ShippingGroup.shippingGroupClassType") %>'>

   <core:case value="electronicShippingGroup">

    <dsp:droplet name="ForEach">
     <dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/>
     <dsp:param name="elementName" value="CiRelationship"/>
     <dsp:param name="indexName" value="index"/>
     <dsp:oparam name="outputStart">
      <tr><td colspan=13>&nbsp;<Br>Electronic delivery to
      <dsp:valueof param="ShippingGroup.emailAddress">
           unknown email address</dsp:valueof>:
      <hr size=0></td></tr>
     </dsp:oparam>
     <dsp:oparam name="output">
       <dsp:setvalue param="itemName" value='<%= "item" +
           request.getParameter("shippingGroupIndex") + ":"
           +request.getParameter("index")%>'/>
       <%@ include file="CartLineItem.jspf" %>
     </dsp:oparam>
    </dsp:droplet>
   </core:case>

   <core:defaultCase>

    <dsp:droplet name="IsGiftShippingGroup">
     <dsp:param name="sg" param="ShippingGroup"/>
     <dsp:oparam name="false">
     <dsp:droplet name="ForEach">
      <dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/>
      <dsp:param name="elementName" value="CiRelationship"/>
      <dsp:param name="indexName" value="index"/>
      <dsp:oparam name="outputStart">
       <tr><td colspan=13>&nbsp;<br>Shipping to you:<hr size=0></td></tr>
      </dsp:oparam>
      <dsp:oparam name="output">
       <dsp:setvalue param="itemName" value='<%="item" +
            request.getParameter("shippingGroupIndex") + ":"
            +request.getParameter("index")%>'/>
       <%@ include file="CartLineItem.jspf" %>
      </dsp:oparam>
     </dsp:droplet>
    </dsp:oparam>
    <dsp:oparam name="true">

     <dsp:droplet name="ForEach">
      <dsp:param name="array" param="ShippingGroup.CommerceItemRelationships"/>
      <dsp:param name="elementName" value="CiRelationship"/>
      <dsp:param name="indexName" value="index"/>
      <dsp:oparam name="outputStart">
       <tr><td colspan=13>&nbsp;<br>Gifts
       <dsp:droplet name="GiftlistLookupDroplet">
        <dsp:param name="id" param="giftlistId"/>
        <dsp:param name="elementName" value="giftlist"/>
        <dsp:oparam name="output">
         for
         <dsp:valueof param="giftlist.owner.firstName"/>
         <dsp:valueof param="giftlist.owner.lastName"/>
        </dsp:oparam>
       </dsp:droplet>:
       <hr size=0></td></tr>
       </dsp:oparam>
       <dsp:oparam name="output">
         <dsp:setvalue param="itemName" value='<%="item" +
              request.getParameter("shippingGroupIndex") + ":"
              +request.getParameter("index")%>'/>
         <%@ include file="CartLineItem.jspf" %>
       </dsp:oparam>
      </dsp:droplet>

     </dsp:oparam>
    </dsp:droplet>

  </core:defaultCase>
  </core:switch>
 </dsp:oparam>
</dsp:droplet>
Getting Correct Price Information

Each CommerceItem maintains price information in its priceInfo property, which then in turn has an amount property that is the currently calculated cost of the commerce item.

The priceInfo object returned is an instance of ItemPriceInfo. It contains information on the current price of a CommerceItem such as list price, a list of discounts applied to the item, and the item’s onSale status.

The following JSP code demonstrates the simplest case of displaying an item’s price, assuming that the commerce item has a parameter named commerceItem.

<dsp:valueof converter="currency" param="commerceItem.priceInfo.amount"/>

Note that the currency TagConverter used here automatically formats the price so it is displayed in a particular localized format.

The following JSP code displays the prices:

The price of the commerce item is:
<dsp:valueof converter="currency" param="commerceItem.priceInfo.amount"/>

In the Pioneer store, we wanted to display both the sale price and the list price for an item. We used the compare servlet bean to determine if the item has been discounted, and if so, to display both the list price as well as the discounted price.

<%-- Figure out if there have been discounts applied to this item.  If
     there have then print out both the discounted and undiscounted price.
     else just print out the current total price.
--%>
<core:exclusiveIf>

  <core:ifEqual
     object1='<%= drequest.getParameter
("CiRelationship.commerceItem.priceInfo.amount") %>'
     object2='<%= drequest.getParameter
("CiRelationship.commerceItem.priceInfo.rawTotalPrice") %>'>
    <td></td><td></td>
    <td></td>
    <td align=right>
    <dsp:valueof converter="currency"
         param="CiRelationship.amountByAverage"/>
    </td>

    <td>
    </td>
  </core:ifEqual>
  <core:defaultCase>
    <td></td>
    <td align=right>
    <span class=strikeout><dsp:valueof converter="currency"
      param="CiRelationship.commerceItem.priceInfo.rawTotalPrice"/></span>

    </td>
    <td></td>
    <td align=right>
      <dsp:valueof converter="currency"
           param="CiRelationship.commerceItem.priceInfo.amount"/>
    </td>
  </core:defaultCase>
 </core:exclusiveIf>
Viewing Order Subtotal

The cart.jsp also displays the subtotal of the order: the sum of all the commerce items minus any order-level discounts. The ShoppingCartModifier accesses the subtotal. The priceInfo object of the Order object itself holds price-level information.

<dsp:valueof converter="currency"
     bean="ShoppingCartModifier.order.priceInfo.amount"/>

Again in the Pioneer Cycling, store, we wanted to show customers their subtotals both before discounts and after discounts.

We used the following JSP code to perform this functionality:

  <%--
       Obtain two subtotals.  One that represents the subtotal after
       promotions and then the one from before promotions.  This allows
       the end user to view the two different ones.
  --%>

<dsp:getvalueof id="subtotal"
                bean="ShoppingCartModifier.order.priceInfo.rawSubtotal">
<dsp:getvalueof id="amount" bean="ShoppingCartModifier.order.priceInfo.amount">
<core:exclusiveIf>
  <core:ifEqual object1="<%=subtotal%>"
                object2="<%=amount%>">
    <tr>
      <td colspan=9 align=right>Subtotal</td>
      <td></td>
      <td align=right>
       <b><dsp:valueof converter="currency"
           bean="ShoppingCartModifier.order.priceInfo.amount"/></b>
      </td>
    </tr>
  </core:ifEqual>
  <core:defaultCase>
    <tr>
      <td colspan=9 align=right>Subtotal with order discount</td>
      <td></td>
      <td align=right>
        <span class=strikeout>
          <dsp:valueof converter="currency"
               bean="ShoppingCartModifier.order.priceInfo.rawSubtotal"/>
        </span>
      </td>
      <td></td>
      <td align=right>
        <b><dsp:valueof converter="currency"
                bean="ShoppingCartModifier.order.priceInfo.amount"/></b>
      </td>
    </tr>
  </core:defaultCase>
</core:exclusiveIf>
</dsp:getvalueof>
</dsp:getvalueof>

We used the Compare servlet bean to check the actual amount of the order with the rawSubtotal of the order. The rawSubtotal is the subtotal before any order level discounts are applied.

Changing the Quantity Ordered of an Item

The customer can change the quantity of an item in the Shopping Cart by entering a new number into the quantity field and clicking Recalculate. cart.jsp then returns with the quantity reset. The ShoppingCartModifier component performs this function.

The new quantity is passed to the server via the catalogRefId parameter and then the ShoppingCartModifier method, handleSetOrder(), is invoked. If the quantity parameter passed in is different than the current quantity of the commerce item, the new quantity is set.

Here is the JSP code to indicate the quantity of a particular item:

<dsp:input type="text" size="4"
     bean="item.catalogRefId"
     beanvalue="item.quantity"/>

This generates a text box that is set to the current quantity of the commerce item.

To pick up the changed quantity, it is necessary to invoke the handleSetOrder method of the ShoppingCartModifier component by submitting the form to the handleSetOrder method. This is what the submit JSP tags look like.

<!-- RECALCULATE Order button: -->
<!------------------------------->
<dsp:input bean="ShoppingCartModifier.setOrderByRelationshipId" type="submit"
     value="Recalculate"/> &nbsp; &nbsp;

<!--  GoTo this URL if user pushes RECALCULATE button and
      there are no errors: -->
<dsp:input bean="ShoppingCartModifier.setOrderByRelationshipIdSuccessURL"
     type="hidden" value="cart.jsp"/> <!-- stay here -->

<!--  GoTo this URL if user pushes RECALCULATE button and
      there are errors: -->
<dsp:input bean="ShoppingCartModifier.setOrderByRelationshipIdErrorURL"
     type="hidden" value="cart.jsp"/> <!-- stay here -->

In the Pioneer Cycling store, we wanted a user to always return to the Shopping Cart page (cart.jsp) after he has submitted a form to display either error messages or the new cart contents. (Displaying error messages is discussed in detail later in this chapter.)

Deleting Items from the Cart

cart.jsp enables customers to delete items from their Shopping Cart either by checking the ‘X’ button or by setting the quantity of an item to zero.

Using the ‘X’ Checkbox

To remove items from the Shopping Cart by checking the ‘X’ button, two things must happen. First, the Ids of the items to be removed must be passed to the form handler. Second, the handleSetOrder() method in the ShoppingCartModifier must be invoked.

The ShoppingCartModifier component has a property named removalCatalogRefIds, which is an array property of the catalogRefIds that need to be removed from the cart. Thus, every item in this array is removed from the cart. The JSP for each checkbox looks like:

<dsp:input bean="ShoppingCartModifier.removalCatalogRefIds"
           paramvalue="item.catalogRefId " type="checkbox" checked="<%=false%>"/>

As the cart.jsp iterates through each commerce item, it sets the parameter item to the current commerce item and then references its catalogRefId. The catalogRefId is added to the array only if the box is checked. For more information on using form elements in JSP, please consult the Creating Forms chapter in the ATG Page Developer's Guide.

When the form is submitted to the handleSetOrder method, the method iterates through the catalogRefId objects in the removalCatalogRefIds property and removes them from the current order.

Invoking the handleSetOrder method is described in the section Changing the Quantity Ordered of An Item.

Setting Quantity to Zero

Removing an item by setting its quantity to zero is the same as changing an item order to any quantity. The ShoppingCartModifier detects if the quantity equals zero. When this happens, the item is automatically removed from the cart.

About cart_edit.jsp

The cart_edit.jsp gives the customer the ability to

Deleting items from the order is the simplest feature here and depends on the handleRemoveItemFromOrder method of the ShoppingCartModifier. The other two features utilize the handleRemoveAndAddItemToOrder method.

Deleting Items

The previous sections discussed one way to remove items from an order—setting the removalCatalogRefIds property and then invoking the handleSetOrder method on the form submit. However, if it is only necessary to remove items in this call, without also changing quantities, this can be accomplished by setting the removalCommerceIds property and then invoking the handleRemoveItemFromOrder method.

The following JSP adds an item to the removalCatalogRefIds property.

<dsp:input bean="ShoppingCartModifier.removalCommerceIds"
     beanvalue="ShoppingCartModifier.commerceItemToEdit.Id" type="hidden"/>

This always adds the ID of the commerce itemToEdit to the removalCommerceIds property. If the form is submitted to the handleRemoveItemFromOrder method using the following JSP code, the item is removed.

<!-- Delete the item from the cart -->
<dsp:input bean="ShoppingCartModifier.RemoveItemFromOrder" type="submit"
           value="Delete"/>
Changing the Quantity or SKU Instance of an Item

This page provides one place where a user can change the quantity or the SKU instance of a commerce item. Since this functionality has to be provided by one form method, the handler needs to be general enough to handle both situations. For more information on how the handleRemoveAndAddItemToOrder method works, see the section on the ShoppingCartModifier component in the Configuring Merchandising Services chapter in the ATG Commerce Programming Guide.

To change either the quantity or the SKU instance of an item, the item is actually deleted and a new one is added. Therefore, to change either, the quantity and the catalogRefId are the minimum pieces of information necessary to create a new commerce item. The removalCommerceId always is set as described in the previous section. The quantity is set by setting a form element to the quantity property of the ShoppingCartModifier.

<!-- Display the current quantity of the item -->
<dsp:input bean="ShoppingCartModifier.quantity"
     beanvalue="ShoppingCartModifier.commerceItemToEdit.quantity" size="4"
     type="text"/>

The following JSP sets the catalogRefId that will be added:

<br>Model Selection</b><br>

<!-- Set the product id, so this item can be added to the cart if necessary -->
<dsp:input bean="ShoppingCartModifier.productId"
           beanvalue="ShoppingCartModifier.commerceItemToEdit.auxiliaryData
.productRef.repositoryId"
           type="hidden"/>

<!-- Display all the child SKUs for this particular SKUs parent product -->
<!-- When displaying info to the user, grab the displayableSKUAttributes-->
<!-- property from the product and iterate through the list getting-->
<!-- the SKU property for each one-->
<dsp:select bean="ShoppingCartModifier.catalogRefIds">
<dsp:droplet name="/atg/dynamo/droplet/ForEach">
 <dsp:param bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData
.productRef.childSKUs" name="array"/>
 <dsp:param name="elementName" value="sku"/>
 <dsp:param name="indexName" value="skuIndex"/>
 <dsp:oparam name="output">
  <dsp:droplet name="/atg/commerce/catalog/DisplaySkuProperties">
 <dsp:param name="delimiter" value=" | "/>
 <dsp:param name="sku" param="sku"/>
 <dsp:param bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData
.productRef" name="product"/>
 <dsp:param name="displayElementName" value="outputString"/>
 <dsp:oparam name="output">
  <dsp:droplet name="/atg/dynamo/droplet/Switch">
  <dsp:param name="value" param="sku.repositoryId"/>

  <!-- Current Sku object is the same as one in cart, make it default selected -->
  <dsp:getvalueof id="nameval2"
                  bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData.
catalogRef.repositoryId" idtype="java.lang.String">
<dsp:oparam name="<%=nameval2%>">
   <dsp:getvalueof id="option131" param="sku.repositoryId"
                   idtype="java.lang.String">
<dsp:option selected="<%=true%>" value="<%=option131%>"/>
</dsp:getvalueof><dsp:valueof param="sku.displayName"/>
  </dsp:oparam>
</dsp:getvalueof>

  <!-- The current Sku object does NOT match one is the shopping cart, don't make
       selected -->
  <dsp:oparam name="default">
   <dsp:getvalueof id="option139" param="sku.repositoryId"
                   idtype="java.lang.String">
<dsp:option value="<%=option139%>"/>
</dsp:getvalueof><dsp:valueof param="sku.displayName"/>
  </dsp:oparam>
  </dsp:droplet>
 </dsp:oparam>
 <dsp:oparam name="empty">
  <dsp:droplet name="/atg/dynamo/droplet/Switch">
  <dsp:param name="value" param="sku.repositoryId"/>

  <!-- Current Sku object is the same as one in cart, make it default selected -->
  <dsp:getvalueof id="nameval2"
                  bean="ShoppingCartModifier.commerceItemToEdit.auxiliaryData
.catalogRef.repositoryId" idtype="java.lang.String">
<dsp:oparam name="<%=nameval2%>">
   <dsp:getvalueof id="option157" param="sku.repositoryId"
                   idtype="java.lang.String">
<dsp:option selected="<%=true%>" value="<%=option157%>"/>
</dsp:getvalueof><dsp:valueof param="sku.displayName"/>
  </dsp:oparam>
</dsp:getvalueof>

  <!-- Current Sku object does NOT match one is the shopping cart, don't make
       selected -->
  <dsp:oparam name="default">
   <dsp:getvalueof id="option165" param="sku.repositoryId"
                   idtype="java.lang.String">
<dsp:option value="<%=option165%>"/>
</dsp:getvalueof><dsp:valueof param="sku.displayName"/>
  </dsp:oparam>
  </dsp:droplet>
 </dsp:oparam>
 <dsp:oparam name="empty">
  <dsp:getvalueof id="option177" param="sku.repositoryId"
                  idtype="java.lang.String">
<dsp:option selected="<%=true%>" value="<%=option177%>"/>
</dsp:getvalueof><dsp:valueof param="sku.displayName"/>
 </dsp:oparam>
   </dsp:droplet>
  </dsp:oparam>
</dsp:droplet>

</dsp:select>

The catalogRefId of the selected items is set to the catalogRefId property of the ShoppingCartModifier. Inside the select tags, all the child SKUs of a particular product are listed out as a select box by the ForEach component in conjunction with the productRef.childSkus property. For each of these child SKUs, the DisplaySkuProperties servlet bean returns the displayable SKU properties or, if it returns nothing, shows the sku.displayName.

The submit of this form then goes to the method handleRemoveAndAddItemToOrder method.

In Pioneer Cycling, the entire order is repriced on the Shopping Cart page because a customer may have accumulated promotions while navigating the site. Repricing the order causes all the price changes to be resaved and therefore SQL is sent to the database. Even if there are no new promotions, the order is repriced. As a performance optimization, you may decide to remove the JSP code snippet that forces the order to be repriced when the customer simply views the Shopping Cart page. If the customer changes something in the cart, or moves to the confirmation page, the order is still repriced.

The line of JSP to remove is:

<dsp:setvalue bean="ShoppingCartModifier.repriceOrder" value="ORDER_SUBTOTAL"/>

If you remove this line, when a customer looks at the Shopping Cart page, the order is not repriced, and the subsequent SQL is not be generated. Another option is to tie the repricing functionality to a submit button (such as “recalculate”) to optimize performance.

Please note that SQL is only generated for registered users, not for anonymous users.

 
loading table of contents...