Cart Management from frontend
Allowing customers to work with multiple open carts from frontend
Some solutions – like CPQ (Configure, Price, Quote) solutions – need to be implemented in a way which allows users to work with carts from frontend, often multiple named carts.
In this guide we will implement a simple setup where the customer can work with carts from frontend – in concrete terms:
- Show a list of carts to a user
- Create new empty carts
- Switch between open carts
- Change cart states from frontend
We will also touch upon how users with impersonation rights can apply custom discounts to open carts.
Note
Most of the functionality implemented in this guide uses cart commands.
While cart commands can be used anywhere, we will implement the features inside a Customer experience center as it supports ViewModels, but feel free to choose your own adventure if you want to.
Part 1: Showing a list of carts to a user
The first thing you want to do is show a list of carts to a user. This is easily done using standard Customer Experience Center functionality:
- Add a Customer Experience Center app to a paragraph
- Under Order type select Carts
- Under Retrieve list based on select Own orders and orders made by users that current user can impersonate
- Under Display select a field to sort by – e.g. ID – and an appropriate sort order
- Under Templates select or create a simple template for rendering the list of carts – it could look like this:
@using Dynamicweb.Rendering
@using Dynamicweb.Ecommerce.Frontend
@inherits ViewModelTemplate<OrderListViewModel>
<table class="table">
<tr>
<th>Cart ID</th>
<th>Name</th>
<th>Current total</th>
</tr>
@foreach (var cart in Model.Orders)
{
<tr>
<td>@cart.Id</td>
<td>@cart.DisplayName</td>
<td>@cart.Price.PriceFormatted</td>
</tr>
}
</table>
Part 2: Showing the current cart details
Now that we have the list of carts associated with the user (and users that the current user can impersonate) we want to see more details about the currently active cart.
This can be done very easily by using the API to fetch the cart object and render details about it – here in a sort of cart-like view:
@using Dynamicweb.Rendering
@using Dynamicweb.Ecommerce.Frontend
@inherits ViewModelTemplate<OrderListViewModel>
<h2>Current Cart</h2>
@{
var currentcart = Dynamicweb.Ecommerce.Common.Context.Cart;
}
@if (currentcart != null)
{
<table class="table">
<thead>
<tr>
<th>Product name</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var orderline in currentcart.OrderLines)
{
<tr>
<td>@orderline.ProductName</td>
<td>@orderline.Quantity</td>
<td>@orderline.Price</td>
</tr>
}
<tr>
<td><b>Total with VAT</b></td>
<td></td>
<td><b>@currentcart.Price</b></td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
<div><em><small><b>AutoID:</b> @currentcart.AutoId</small></em></div>
<div><em><small><b>CartID:</b> @currentcart.Id</small></em></div>
<div><em><small><b>Creation Date:</b> @currentcart.Date</small></em></div>
<div><em><small><b>Display Name:</b> @currentcart.DisplayName</small></em></div>
<div><em><small><b>Language ID:</b> @currentcart.LanguageId</small></em></div>
<div><em><small><b>Shop ID:</b> @currentcart.ShopId</small></em></div>
</td>
<td></td>
<td>
</td>
</tr>
</tfoot>
</table>
}
else
{
<div>There is no active cart</div>
}
<h2>Other carts</h2>
<table class="table">
<tr>
<th>Cart ID</th>
<th>Name</th>
<th>Current total</th>
</tr>
@foreach (var cart in Model.Orders)
{
<tr>
<td>@cart.Id</td>
<td>@cart.DisplayName</td>
<td>@cart.Price.PriceFormatted</td>
</tr>
}
</table>
Part 3: Renaming, copying, and archiving carts
Now that you can see basic information about both the current cart and other carts available to this user we can implement some of the cart management operations:
- Naming
- Copying
- Archiving
As mentioned previously, this is done using cart commands - specifically setname, copy, and archive. In this implementation we create buttons for copying and archiving a cart, and a form for (re)naming the cart:
@using Dynamicweb.Rendering
@using Dynamicweb.Ecommerce.Frontend
@inherits ViewModelTemplate<OrderListViewModel>
<h2>Current Cart</h2>
@{
var currentcart = Dynamicweb.Ecommerce.Common.Context.Cart;
}
@if (currentcart != null)
{
<table class="table">
<thead>
<tr>
<th>Product name</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var orderline in currentcart.OrderLines)
{
<tr>
<td>@orderline.ProductName</td>
<td>@orderline.Quantity</td>
<td>@orderline.Price</td>
</tr>
}
<tr>
<td><b>Total with VAT</b></td>
<td></td>
<td><b>@currentcart.Price</b></td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
<div><em><small><b>AutoID:</b> @currentcart.AutoId</small></em></div>
<div><em><small><b>CartID:</b> @currentcart.Id</small></em></div>
<div><em><small><b>Creation Date:</b> @currentcart.Date</small></em></div>
<div><em><small><b>Display Name:</b> @currentcart.DisplayName</small></em></div>
<div><em><small><b>Language ID:</b> @currentcart.LanguageId</small></em></div>
<div><em><small><b>Shop ID:</b> @currentcart.ShopId</small></em></div>
</td>
<td></td>
<td>
<a class="btn btn-primary" href='@Pageview.SearchFriendlyUrl?cartcmd=copy&CartId=@currentcart.Id&CartName=Copy_of_@currentcart.Id'>Copy</a>
<a class="btn btn-danger" href='@Pageview.SearchFriendlyUrl?cartcmd=archive'>Archive</a>
<br />
<br />
<form class="form-inline" method="post">
<input class="form-control" type="text" name="CartName" id="CartName" value="" />
<input type="hidden" name="CartID" id="CartID" value="@currentcart.Id" />
<button class="btn btn-primary" type="submit" name="CartCmd" value="setname">Set name</button>
</form>
</td>
</tr>
</tfoot>
</table>
}
else
{
<div>There is no active cart</div>
}
<h2>Other carts</h2>
<table class="table">
<tr>
<th>Cart ID</th>
<th>Name</th>
<th>Current total</th>
</tr>
@foreach (var cart in Model.Orders)
{
<tr>
<td>@cart.Id</td>
<td>@cart.DisplayName</td>
<td>@cart.Price.PriceFormatted</td>
</tr>
}
</table>
Part 4: Setting the active cart
Now that you can do things to the active cart, it's time to make it possible to set the active cart using the setcart command with a cartID parameter:
(…)
<h2>Other carts</h2>
<table class="table">
<tr>
<th>Cart ID</th>
<th>Name</th>
<th>Current total</th>
<th></th>
</tr>
@foreach (var cart in Model.Orders)
{
<tr>
<td>@cart.Id</td>
<td>@cart.DisplayName</td>
<td>@cart.Price.PriceFormatted</td>
<th><a class="btn btn-primary" href='@Pageview.SearchFriendlyUrl?cartcmd=setcart&CartID=@cart.Id'>Setcart</a></th>
</tr>
}
</table>
Part 5: Creating new carts
Normally, carts are created when there is no active cart and a customer adds a product to cart - but you can also create new carts from frontend using the createcart cart command. This cart command takes two optional parameters; CartName and CartUserId. The CartUserId parameter must be set to either the id of the current user or a user he or she can impersonate.
Here we’ve added a simple form for creating a named cart associated with the current user – in more advanced setups you could make it possible to select a user to impersonate and create carts on their behalf.
(…)
@if (Pageview.User != null)
{
<h2>Create new cart</h2>
<form class="form-inline" method="post">
<input type="hidden" id="CartUserId" name="CartUserId" value="@Pageview.User.ID" />
<input class="form-control" type="text" id="CartName" name="CartName" value="" />
<button class="btn btn-primary" type="submit" name="CartCmd" value="createnew">Create new cart</button>
</form>
}
Part 6: Changing cart states from frontend
On some solutions you may want to use cart flows to create frontend-workflows for carts to move through before being checked out and converted to an order.
To implement this feature you will use a customer center command called cartchangestate, and this means that it only works when submitted from or to a customer experience center template.
In this example, we first fetch the cart flow, then the cart states from the flow. We then create a form for changing the cart state using the cartchangestate command and the CartID and StateID parameters:
@{
var orderflowservice = new Dynamicweb.Ecommerce.Orders.OrderFlowService();
var currentflow = orderflowservice.GetFlowById(currentcart.OrderState.OrderFlowId);
var orderstateservice = new Dynamicweb.Ecommerce.Orders.OrderStateService();
var cartstates = orderstateservice.GetStatesByFlow(currentflow);
}
<div>
<h4>Cart States</h4>
<div><b>Current state:</b> @currentcart.OrderState.GetName(currentcart.LanguageId) (@currentcart.OrderState.Id)</div>
<form method="post" id="ChangeDraftStatusForm">
<input type="hidden" name="CartID" id="CartID" value="@currentcart.Id" />
<select name="StateId" id="StateID">
@foreach (Dynamicweb.Ecommerce.Orders.OrderState state in cartstates)
{
<option value="@state.Id">@state.GetName(currentcart.LanguageId)</option>
}
</select>
<button class="btn btn-primary pull-right" name="CustomerCenterCmd" value="cartchangestate" type="submit">Change state</button>
</form>
</div>
Part 7: Custom discounts
Some solutions which implement cart management from frontend also want to empower certain users to apply custom discounts to carts from frontend.
To implement this you should:
- Create discounts of the type custom amount discount and custom percentage discount
- Create users with impersonation rights
You can then use the setdiscount cart command to assign either a percentage-based or fixed amount discount to a cart, depending on the parameter used:
@if (Dynamicweb.Security.UserManagement.UserContext.Current.GetImpersonatableUsers().Count() > 0)
{
<!--Fixed discount-->
<tr>
<td><b>Apply fixed discount: </b> (@currentcart.CurrencyCode)</td>
<td></td>
<td>
<form class="form-inline" method="post">
<input type="hidden" name="CartID" id="CartID" value="@currentcart.Id" />
<input class="form-control" type="number" name="OrderDiscount" id="OrderDiscount" value="0" />
<button class="btn btn-primary" type="submit" name="CartCmd" value="setdiscount">Apply</button>
</form>
</td>
</tr>
<!--Percentage discount-->
<tr>
<td><b>Apply % discount: </b></td>
<td></td>
<td>
<form class="form-inline" method="post">
<input type="hidden" name="CartID" id="CartID" value="@currentcart.Id" />
<input class="form-control" type="number" name="OrderDiscountPercentage" id="OrderDiscountPercentage" value="0" />
<button class="btn btn-primary" type="submit" name="CartCmd" value="setdiscount">Apply</button>
</form>
</td>
</tr>
}