Displaying and updating orders

A common aspect of many on-line stores is a section where customers can review their past orders and inspect each one in detail.

Usually, such sections allow users to:

  • Display information about an order’s current processing status to a customer
  • Provide a summary of the order’s contents and related information, such as its status or payment results
  • Display a list of all orders a customer has made

Preparing the controller class

Initialize an instance of the IShoppingService, ICurrencyInfoProvider and IOrderInfoProvider interfaces in the controller you use to handle order-related functionality. ShoppingService, the default implementation of the IShoppingService interface, facilitates various E-commerce scenarios.

The ICurrencyInfoProvider instance is later used in the order view model to get the order’s currency and formatting information. IOrderInfoProvider is used for basic getting and setting (saving) of order objects.




        /// <summary>
        /// Initializes instances of service used to facilitate shopping cart, currency and order interactions.
        /// </summary>
        public OrderController(IShoppingService shoppingService,
                               ICurrencyInfoProvider currencyInfoProvider,
                               IOrderInfoProvider orderInfoProvider,
                               ISiteService siteService)
        {
            this.shoppingService = shoppingService;
            this.currencyInfoProvider = currencyInfoProvider;
            this.orderInfoProvider = orderInfoProvider;
            this.siteService = siteService;
        }



 See our API reference for a complete list of API exposed by the IShoppingService interface. 

We recommend using a dependency injection container to initialize instances of used API services (e.g. IShoppingService).

After you have prepared your controller class, you can implement new actions that:

Displaying and updating a specific order

To display and update a specific order:

  1. Implement new action methods that retrieve an order based on a specified ID.

    Getting an order
    
    
    
             /// <summary>
             /// Displays a page where order details can be listed.
             /// </summary>
             public ActionResult OrderDetail()
             {
                 OrderViewModel order = null;
    
                 return View(order);
             }
    
             /// <summary>
             /// Displays details about an order specified with its ID.
             /// </summary>
             /// <param name="textBoxValue">Order ID as a string</param>
             [HttpPost]
             public ActionResult OrderDetail(string textBoxValue)
             {
                 // Gets the order based on the entered order ID
                 OrderViewModel order = GetOrderViewModel(textBoxValue);
    
                 return View(order);
             }
    
             /// <summary>
             /// Returns the order view model wrapper based on the entered ID.
             /// </summary>
             /// <param name="textBoxValue">String containing the user-entered order ID</param>
             /// <returns>View model object of the order</returns>
             private OrderViewModel GetOrderViewModel(string textBoxValue)
             {
                 OrderInfo info = GetOrder(textBoxValue);
    
                 OrderViewModel order = (info == null) ? null : new OrderViewModel(info, currencyInfoProvider);
    
                 return order;
             }
    
             /// <summary>
             /// Returns the order based on the entered order ID.
             /// </summary>
             /// <param name="textOrderID">String containing the user-entered order ID</param>
             /// <returns>Order object of the order</returns>
             private OrderInfo GetOrder(string textOrderID)
             {
                 Int32.TryParse(textOrderID, out int orderID);
    
                 // If the text value is not a number, returns null
                 if (orderID <= 0)
                 {
                     return null;
                 }
    
                 // Gets the order based on the order ID
                 OrderInfo order = orderInfoProvider.Get(orderID);
    
                 // Gets the current customer
                 CustomerInfo customer = shoppingService.GetCurrentCustomer();
    
                 var currentSiteID = siteService.CurrentSite.SiteID;
                 // Validates that the order was created on the current site and that it belongs to the current customer
                 if ((order?.OrderSiteID != currentSiteID) || (order?.OrderCustomerID != customer?.CustomerID))
                 {
                     order = null;
                 }
    
                 return order;
             }
    
    
    
     

    IOrderInfoProvider.Get(int) returns any order with the given ID, provided it exists. Any necessary validation, for instance if the order belongs to the correct customer, or was made on the current site, is the responsibility of the developer. See Ensuring validation for more information.

    The example above validates whether the specified order was created on the current site, and whether it belongs to the current customer. Should any of these conditions be violated, it returns a null value.

  2. Prepare a view model to store select properties of the OrderInfo object for display in the related view.

    
    
    
         public class OrderViewModel
         {
             public int OrderID { get; set; }
    
             public int OrderStatusID { get; set; }
    
             public string CurrencyFormatString { get; set; }
    
             public DateTime OrderDate { get; set; }
    
             public decimal OrderTotalPrice { get; set; }
    
             public bool OrderIsPaid { get; set; }
    
             public OrderPaymentResultViewModel OrderPaymentResult { get; set; }
    
             public string OrderStatusDisplayName { get; set; }
    
             public OrderViewModel(OrderInfo order, ICurrencyInfoProvider currencyInfoProvider)
             {
                 OrderID = order.OrderID;
                 OrderStatusID = order.OrderStatusID;
                 CurrencyFormatString = currencyInfoProvider.Get(order.OrderCurrencyID).CurrencyFormatString;
                 OrderDate = order.OrderDate;
                 OrderTotalPrice = order.OrderTotalPrice;
                 OrderIsPaid = order.OrderIsPaid;
                 OrderStatusDisplayName = OrderStatusInfo.Provider.Get(order.OrderStatusID)?.StatusDisplayName;
                 if (order.OrderPaymentResult != null)
                 {
                     OrderPaymentResult = new OrderPaymentResultViewModel()
                     {
                         PaymentMethodName = order.OrderPaymentResult.PaymentMethodName,
                         PaymentIsCompleted = order.OrderPaymentResult.PaymentIsCompleted
                     };
                 }
             }
         }
    
    
    
     
  3. Implement a new action method that updates the specified order as required. For example, you can allow store managers to set orders as paid for:

    Setting an order as paid
    
    
    
             /// <summary>
             /// Marks an order specified by an order ID as paid.
             /// </summary>
             /// <param name="textBoxValue">Order ID as a string</param>
             [HttpPost]
             public ActionResult MarkOrderAsPaid(string textBoxValue)
             {            
                 // Gets the order based on the entered order ID
                 OrderInfo order = GetOrder(textBoxValue);
    
                 // Sets the order as paid
                 order.OrderIsPaid = true;
                 orderInfoProvider.Set(order);
    
                 return RedirectToAction("OrderDetail");
             }
    
    
    
     
  4. Prepare a view with the content required for your order detail page. For example:

    
    
    
     @* Renders a form field for entering order IDs. *@
     @using (Html.BeginForm("OrderDetail", "Order"))
     {    
         <input type="text" name="TextBoxValue" />
         <input type="submit" name="DisplayOrder" value="Display order" />
     }
    
     @if (Model != null)
     {
         <h2>Order details</h2>
         @* Displays order details. *@
         <ul>
             <li>Order number: @Model.OrderID</li>
             <li>Status ID: @Model.OrderStatusID</li>
             <li>Is paid: @Model.OrderIsPaid</li>
             @if (Model.OrderPaymentResult != null)
             {
                 <li>Payment method name: @Model.OrderPaymentResult.PaymentMethodName</li>
                 <li>Was payment successful: @Model.OrderPaymentResult.PaymentIsCompleted</li>
             }
             <li>Total price: @String.Format(Model.CurrencyFormatString, Model.OrderTotalPrice)</li>
         </ul>
         @* If the order is not paid for, renders a button invoking the 'MarkOrderAsPaid' action that marks the order as paid. *@
         if (!Model.OrderIsPaid)
         {
             using (Html.BeginForm("MarkOrderAsPaid", "Order"))
             {
                 <input type="text" hidden="hidden" name="TextBoxValue" value="@Model.OrderID" />
                 <input type="submit" name="MarkOrderAsPaid" value="Mark order as paid" />
             }
         }
     }
    
    
    
     

Users are now able to display details of a specific order by entering its ID. If the order is not paid for, they are also able to set its status as paid.

Displaying a list of orders

Displaying a list of orders is suitable especially for “My orders” sections in customers’ accounts. 

  1. Add an action to the controller that manges the retrieval of current customer’s orders, creates a collection, and displays it in a related view. The examples uses OrderViewModel from the Displaying and updating a specific order section to store select OrderInfo object properties.

    
    
    
             /// <summary>
             /// Displays a listing of the current user's orders.
             /// </summary>
             public ActionResult MyOrders()
             {            
                 // Gets the current customer
                 CustomerInfo currentCustomer = shoppingService.GetCurrentCustomer();
    
                 // If the customer does not exist, returns error 404
                 if (currentCustomer == null)
                 {
                     return HttpNotFound();
                 }
    
                 // Retrieves from the database a collection of all orders made by the current customer 
                 var orders = orderInfoProvider.GetBySite(siteService.CurrentSite.SiteID)
                                                                .WhereEquals("OrderCustomerID", currentCustomer.CustomerID)
                                                                .OrderByDescending(orderInfo => orderInfo.OrderDate);
    
                 // Creates a list of view models representing the collection of a customer's orders
                 IList<OrderViewModel> model = orders
                             .Select(order => new OrderViewModel(order, currencyInfoProvider))
                             .ToList();
    
                 return View(model);
             }
    
    
    
     

    The IOrderInfoProvider.GetBySite call retrieves all orders created by a customer with the provided ID. Any validation, is the responsibility of the developer. See Ensuring validation for more information.

  2. Prepare a view with the content required for your order detail page. For example:

    
    
    
     @model IEnumerable<OrderViewModel>
    
     @if (Model.Any())
     {
         <h3>Your orders:</h3>
         @* Ensures basic formatting of the displayed information. *@
         <table>
             <thead>
                 <tr>
                     <th>Id</th>
                     <th>Date</th>
                     <th>Status</th>
                     <th>Total</th>
                     <th></th>
                 </tr>
             </thead>
             @* Iterates over all orders in the collection and displays their properties. *@
             @foreach (var order in Model)
             {
                 <tr>
                     <td>
                         @order.OrderID
                     </td>
                     <td>
                         @order.OrderDate
                     </td>
                     <td>
                         @(order.OrderStatusDisplayName)
                     </td>
                     <td>
                         @String.Format(order.CurrencyFormatString, order.OrderTotalPrice)
                     </td>
                     <td>
                         @using (Html.BeginForm("Reorder", "Order", FormMethod.Post))
                         {
                             @Html.Hidden("OrderId", order.OrderID)
                             <input type="submit" value="Reorder" />
                         }
                     </td>
                 </tr>
             }
         </table>
     }
    
    
    
     

Your live site application is now able to list all orders a customer has created.

Reordering existing orders

To add the contents of an existing order directly into the customer’s shopping cart, call ShoppingCartInfoProvider.UpdateShoppingCartFromOrder. The method iterates over all products in the specified order and adds them to the shopping cart accounting for any Buy X, get Y discounts, product bundles, sales, and other promotions the store might be running at the time.

The following example demonstrates the usage of this method in a controller action:




        /// <summary>
        /// Recreates shopping cart content based on a specified order.
        /// </summary>
        /// <param name="orderId">ID of an order to repurchase</param>
        [HttpPost]
        public ActionResult Reorder(int orderId)
        {           
            // Gets the current shopping cart
            ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

            // Adds products from the specified order to the current shopping cart
            // If the operation was successful, redirects to the shopping cart
            if (ShoppingCartInfoProvider.UpdateShoppingCartFromOrder(cart, orderId))
            {
                // Displays the shopping cart
                return RedirectToAction(nameof(CheckoutController.ShoppingCart), "Checkout");
            }

            // If the reorder was unsuccessful, returns back to the list of customer's orders
            return RedirectToAction(nameof(MyOrders));
        }



Ensuring validation

The Xperience InfoProvider and ObjectQuery API works with data retrieved directly from the database. Ensuring that data retrieved this way satisfies required constraints and other input criteria is the responsibility of the MVC site’s developer.

Common validation checks that we recommend you employ are:

  • Null checks – these validate the existence of objects retrieved via API.
  • Checks for matching SiteContext.CurrentSiteID and OrderInfo.OrderSiteID properties – in enviroments where there are multiple e-commerce sites running on the same instance, the same customer could have created orders on more than one site. These checks validate that the retrieved orders were created on the current site.
  • Checks for matching CustomerInfo.CustomerID and OrderInfo.OrderCustomerID properties – these validate whether the order belongs to the customer with the given ID.