Order Fulfillment

The order fulfillment part of the application will display the order based on the contents of the user's shopping basket. This order includes the price and quantity of each book, along with additional charges for taxes and for shipping. Once the user views the order, they can choose to go back and modify the contents of their basket, or they can proceed to providing payment information.

Application Component

The application component is responsible for creating what is called the TransactionDetails. The TransactionDetails basically represent the line items in the final order. Each line in the order corresponds to either a book that was purchased, with its quantity and price, or a charge for shipping or for taxes. The information that the application component builds can then be displayed for the user for verification before proceeding to payment.

Public Function BuildTransactionDetails(ByVal vOrderID As Variant) As Variant
  On Error GoTo BuildTransactionDetailsErr
    
  Dim vResponse As Variant
…Declare a bunch of variables here…
  Dim vOrderItem As Collection

The BuildTransactionDetails method will take a valid OrderID as its input parameter and return a variant that contains the TransactionDetails in a collection of information. The next step is to declare all of the variables that will be used in the method. Since there are a lot of them, we will omit most of them to save space. So if later in the method you see a variable that wasn't declared, we omitted it on purpose.

The variable that will hold the total amount of the order will be initialized to 0. The shipping cost for the order is initialized to $4. This is an implementation of one of the business rules. The business rule for computing shipping costs is $4 per order, plus an additional $1.50 per book. We are hard-coding this amount for simplicity, but storing this value externally so that it can be changed without modifying the application component would make more sense in a production application. The next step is to retrieve the contents of the shopping basket by using the Baskets data component. This component, which was introduced in the previous chapter, has a method that returns a collection containing all of the items in the user's shopping basket.

      
  vOrderTotalPrice = 0
  vShippingPrice = 4
Set vResponse = oDBaskets.GetBasketItems(vOrderID)
  If Not vResponse("error") Then
  
    Set vBasket = vResponse("items")
         

The next step is to iterate through each item in the basket. To make the code a bit more readable, the elements of the BasketItem collection will be read into local variables. These four variables will be used to calculate the selling price of a single item, as well as the total line item price.

    For Each vBasketItem In vBasket
      vPrice = vBasketItem("price")
      vPromotionCode = vBasketItem("promotioncode")
      vPromotionEndDate = vBasketItem("promotionenddate")
      vNumItems = vBasketItem("numitems")

To calculate the selling price of an item, will use the same method we used in the previous chapter. The AdjustTodaysPrice method will calculate the actual selling price of the item based on any promotions that may be in effect. This calculated price would then be formatted for display using the VB method FormatCurrency. This information will be added to the collection that holds all of the information about the line item.

      vTodaysPrice = AdjustTodaysPrice(vPrice, vPromotionCode, vPromotionEndDate)
      vTodaysPrice = Format(vTodaysPrice, "$###,##0.00")
      vBasketItem.Add vTodaysPrice, Key:="todaysprice"
      
      vTotalPrice = vNumItems * vPrice
      vTotalPrice = Format(vTotalPrice, "$###,##0.00")
      vBasketItem.Add vTotalPrice, Key:="totalprice"

Once we have calculated the actual selling price for one item, we need to then calculate the price for this line in the sale. This is determined by multiplying the selling price for one item by the quantity of that item ordered. This amount is calculated then formatted into a currency format. The formatted amount is added to the basket item collection as the totalprice key.

As we are iterating through each item in the basket, we will also be calculating a running total for the entire order. The total amount for each line is added to this running total. We are also calculating the total shipping cost as we iterate through each item. The shipping cost for each line is computed to be $1.50 per book. Again, this is stored here for simplicity, and would probably come from an external source in a production application. For example, if the line item included three copies of the book, then the shipping charge for that line item would be $4.50.

      vOrderTotalPrice = vOrderTotalPrice + vTotalPrice
      vShippingPrice = vShippingPrice + (1.5 * CDec(vNumItems))
      
    Next

After we have iterated through each item in the basket, we are ready to add the summary information to the collection that will be returned to the client. This collection that is returned, vOrder, will be a collection of collections. Each collection that it contains will contain information about a particular part of the order. The reason that a collection of collections is used is so that each part of the order can contain different informational details. There will be entries in the vOrder collection for:

    Set vOrder = New Collection
      
    Set vOrderItem = New Collection
    vOrderItem.Add vBasket, Key:="basketitems"
    vOrder.Add vOrderItem, "basket"
    Set vOrderItem = Nothing

The collection containing information about the line items in the order will be stored in the vOrder collection using the key "basket." This collection will simply contain the collection of line items with the information that was calculated earlier in the method.

    Set vOrderItem = New Collection
    vTotalPrice = Format(vShippingPrice, "$###,##0.00")
    vOrderItem.Add vTotalPrice, Key:="totalprice"
    vOrderItem.Add "$4.00 and $1.50 per item", "details"
    vOrder.Add vOrderItem, "shipping"
    vOrderTotalPrice = vOrderTotalPrice + vShippingPrice
    Set vOrderItem = Nothing

The shipping information and the tax information will both contain their amount as well as detail information about how the amount is calculated. The price for each is stored using the same key, "totalprice," so that the same piece of code in the ASP script can be used to display both of these items in the order. This also makes it very easy to extend the application to support other kinds of charges, without having to change the display code, or the method for returning information to the client.

 

    Set vOrderItem = New Collection
    vTaxPrice = vOrderTotalPrice * 0.125
    vTotalPrice = Format(vTaxPrice, "$###,##0.00")
    vOrderItem.Add vTotalPrice, Key:="totalprice"
    vOrderItem.Add "12.5%", "details" ' Tax % hardcoded in this example – would be stored externally in a production application
    vOrder.Add vOrderItem, "tax"
    vOrderTotalPrice = vOrderTotalPrice + vTaxPrice
    Set vOrderItem = Nothing

The total price for the order has been calculated as we have moved through this method. This total amount is added to the vOrder collection so that it can be returned to the client. Once we have added all of the information to the response collection, we indicate a successful completion of this method by setting the error element to false.

    Set vOrderItem = New Collection
    vTotalPrice = Format(vOrderTotalPrice, "$###,##0.00")
    vOrderItem.Add vTotalPrice, Key:="totalprice"
    vOrder.Add vOrderItem, "ordertotal"
    Set vOrderItem = Nothing
  
    vOrder.Add False, Key:="error"
    
    Set vResponse = vOrder
  End If
  

Finally, we add the standard Transaction Server plumbing. When the method has completed successfully, the SetComplete method of the current object context is called. This indicates that this method is casting its vote in favor of completing the transaction it may be in. If there is an error, then the method-level error handler routes it to the error-handling routine. This aborts the current transaction, using the SetAbort method. It then uses the global RaiseError method to generate an error collection and causes an error message to be written to the error log.

  oObjectContext.SetComplete
  Set BuildTransactionDetails = vResponse
  
  Exit Function
  
BuildTransactionDetailsErr:
  oObjectContext.SetAbort
  Set BuildTransactionDetails = RaiseError("BuildTransactionDetails")

End Function

Now that we have our application logic complete, we can take a look at how the user interface that displays this information to the user is made up.

Building the Active Server Pages

The BuildTransactionDetails method computes the detailed information about the order that is created from the contents of the user's shopping basket.This information is then displayed for the user to review. They can choose to move forward with the purchase process, revise the contents of the order, or abort the transaction completely. If the user chooses to revise the contents of the shopping basket, they are returned to the screen showing the contents of the basket that was examined in the previous chapter.

<%@ LANGUAGE="VBSCRIPT" %>
<% Option Explicit %>
<% Response.Expires = 0 %>
<% if not Session("Auth") = true then %>
  <% Response.Redirect "checkreg.asp"%>
<% End If %>

The ASP script will check to make sure that the user has already registered with the system. If they have not properly registered, they will be redirected to the login page. This will prevent users from typing in this URL directly and accessing this page without passing the proper information to the server. This part of the case study was described in Chapter 15. As with the other ASP scripts in this case study, we will set the page to expire immediately. This will cause it to be regenerated by the server every time it is accessed.

<HTML>
<HEAD>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

<TITLE>Review Order</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="main.css">
</HEAD>
<BODY>
 

Since we have selected to use Option Explicit, which you should use in all but the simplest of ASP scripts, we will need to declare all of the variables that this page will use.

<% dim state %>
<% dim objDB %>
<% dim objOrder %>
<% dim objResponse %>
<% dim objOrderItems %>
<% dim objBasketItems %>
<% dim objItem %>

<TABLE WIDTH=100% CELLSPACING=0 CELLPADDING=0 BORDER=0>
<TR CLASS="pageheader">
<TH ALIGN=left>&nbsp;Shop Checkout
</TH>
</TR>
</TABLE>

We need to check to make sure that the current logged in user has a valid orderID. This orderID can be retrieved from the database using the GetOrder method of the Baskets data object. In all of the previous pages, we have only accessed the Application objects. This example shows that we can by pass the application objects if we want, and use the Data object to get the information that we want. Just because the system is set up as a three tier system, it does not mean that ALL access has to go through all tiers. In this instance, it is more efficient to retrieve information directly from the Data object.

<%  if Session("OrderID") = "" then %>
<%  set objDB = Server.CreateObject("DpkgBaskets.DBaskets") %>
<%  set objResponse = objDB.GetOrder(Session("email")) %>
<%  Session("OrderID") = objResponse("OrderID") %>
<% End If %>

<%  set objOrder = Server.CreateObject("BpkgOrderProcessing.Order") %>
<%  set objResponse = objOrder.BuildTransactionDetails(Session("OrderID")) %>

We will then call the BuildTransactionDetails method that we have just created. This will return a collection of the line items in the order. We first need to check to make sure that no errors occurred. If there were errors in the method, then we will notify the user via the standard error display method included in a server-side include file. If the method was successful, we will store the information returned in a session-level variable for later use.

<%  if objResponse("error") then %>
<!--#include file=error.inc-->
<%  else %>
<%   set Session("OrderItems") = objResponse %>
<%  set objOrder = nothing %> 

  <p>Order details for 
<%  Response.Write Session("firstname") & " " & Session("surname") %>
  </P>

<%  set objOrderItems = Session("OrderItems") %>

We will first retrieve the items that were in the shopping basket. These items are stored as a collection of a collection. The basket item in the OrderItems collection is a collection of information about the contents of the basket. The basketitems item of this collection is a collection of the information about each item in the basket itself.

<%  set objBasketItems = objOrderItems("basket")("basketitems") %>

<%  if objBasketItems.count = 0 then %>
   <P>Your basket is currently empty</P>

If there are no items in the basket, then there is nothing to display. We output a message to the user indicating that their basket is empty. If there are items, then we set up a table to display the information in a structured format. The next step is to iterate through all of the items in the BasketItems collection. This will give us each item in the order, which we can display for the user.

<%  else %>
     <TABLE cellspacing=0 cellpadding=0 border=0>   
     <tr class="tableheader">
     <th>Item</th>
     <th width=15>&nbsp;</th>
     <th>Unit price</th>
     <th width=15>&nbsp;</th>
     <th>No.</th>
     <th width=15>&nbsp;</th>
     <th>Total</th>
     </tr>
<%    for each objItem in objBasketItems %>

There may be items in the basket that have a quantity of 0. This may have happened if the user reduced the number desired to 0. Since these items really aren't part of the order, we will check to see if the quantity is greater than 0 before displaying a particular item. The information displayed for each item includes the name, the unit price, the quantity, and the total price for the line item.

<%     if objItem("numitems") > 0 then %>
      <tr class="tabledata">
        <td>
<%       Response.Write objItem("name") %>
        </td>
        <td>&nbsp;</td>
        <td align=right>
<%       Response.Write objItem("todaysprice") %>
        </td>   
        <td>&nbsp;</td>
        <td align=right>
<%       Response.Write objItem("numitems") %>
        </td>   
        <td>&nbsp;</td>
        <td align=right>
<%       Response.Write objItem("totalprice") %>
        </td>   
        </tr>
<%     end if %>
<%    next %>

After iterating through all of the items in the order, we will then display the additional charges. These charges include the shipping cost and the taxes. The totals for the entire order are stored in the same manner in the collection returned from the application object. This allows us to use very similar code to retrieve and display this data for the user. Each piece of information is stored as an element of a collection of a collection. For example, to retrieve the shipping price, we want the totalprice element of the shipping collection, which is an element of the OrderItems collection.

      <tr class="tabledata">
      <td colspan= 6>
     <i>plus</i> Shipping:
<%      Response.Write objOrderItems("shipping")("details") %>
      </td>
      <td align=right>
<%      Response.Write objOrderItems("shipping")("totalprice") %>
      </td>   
      </tr>
      <tr class="tabledata">
      <td colspan=6>
     <i>plus</i> Tax: 
<%      Response.Write objOrderItems("tax")("details") %>
      </td>
      <td align=right>
<%      Response.Write objOrderItems("tax")("totalprice") %>
      </td>   
      </tr>
      <tr class="tabledata">
      <td colspan=6>
     <b>Order total</b>
      </td>
      <td align=right>
     <hr><b>
<%      Response.Write objOrderItems("ordertotal")("totalprice") %>
<%     Session("totalprice") = objOrderItems("ordertotal")("totalprice") %>
      <b>
      </td>   
      </tr>
     </table>
<%  end if %>
   <hr>

Once all of the order details have been displayed for the user, we will present them with a list of options to take from this page. The user can choose to continue with the purchase, which will take them to the payments area. Or, they can choose to amend the order, which will return them to the page where they can view the contents of the basket.

<%  if objBasketItems.count > 0 then %>
    <input type=button value="Purchase" 
       onclick=window.document.location.href='GetCreditInfo.asp'>
    <input type=button value="Amend order" 
      onclick=window.document.location.href='viewBasket.asp'>

  <% end if %>
<%  end if %>
</body>
</html>

When the user displays this page in the browser, they will see:

Now that we have seen how the user can review the details about their order, including the additional charges, we can move on to the final phase of the order process. The final phase requires the user to provide payment information before the order can be completed.

© 1998 by Wrox Press. All rights reserved.