Magelia WebStore

  • Summary

How to manage the Basket

Overview: Basket Management

Information about inventory in Magelia WebStore:

Download the code sample of this tutorial:
How to manage basket - ZIP - 30.8 mo

Magelia WebStore lets you manage several product stocks:
• Virtual stock: Virtual stock corresponds to the available stock on sale on a website.
• Minimum stock: When an inventory of a product falls below the minimum stock, it can no longer be sold.
• Real stock: Real stock corresponds to the available merchandise that is actually in the warehouse. Sales do not affect the real stock level. The real stock is not taken into account in the calculations; you must manually manage the stock.
• Reorder Level: This is a simple piece of information that enables the seller to make a list of products that are below the inventory level and the reorder level.

To sell products that are not in stock, you must define a virtual stock of 0 (zero) and a negative minimum stock. If you define a virtual stock of 0 and a negative stock of -50, you can sell 50 products.

What is the role of real stock if it’s not taken into account for stock calculations? This allows you to manage the real stock and decrease it only when products have been removed from the warehouse or when they’ve been shipped for delivery.

For more information, refer to the tutorial video Manage inventory with Magelia WebStore on YouTube.

Basket management:

The concept of stock is important and the basket contents of users acts strongly on the stocks. As soon as a product is added, the virtual stock is decreased.

We can better understand by following the changing of stock via the administration interface. Here before adding the product to the basket:

The concept of stock is important and the basket contents of users acts strongly on the stocks. As soon as a product is added

And here, after adding the product unit to the basket:

The concept of stock is important and the basket contents of users acts strongly on the stocks. As soon as a product is added

As we see on the screen captures above, the product virtual stock, here at the Berlin Warehouse, is indeed decreased upon the addition to the basket, while there has not yet been an order made.

Magelia WebStore tries to group as many products together in order to be able to ship from one warehouse. Therefore, if all the products from one order could be shipped from the same warehouse, Magelia WebStore would choose this option. The rules for determining a warehouse can be extremely complex and specific for a seller. We have established simple rules for determining a warehouse, but it is possible to expand the solutions by using specific developments

Any operation on the basket entails the complete recalculation of the basket. Therefore we must keep in mind that the stock is checked each time that the basket is calculated, and likewise for the price.

Scheduler Flush basket:

By default, in Magelia WebStore the baskets are never deleted. A user may compose a basket without ever purchasing it. As a result, the basket remains impacted by this basket.

It is however possible to configure a task that empty the baskets that have not been purchased at the end of a given time.

For this, Magelia WebStore proposes a Task Scheduler with different types of possible tasks. You merely need to set it up using the Administration Console by going to the menu Sellers - Stores > Scheduler. To manage non-purchased baskets, you must add a Flush basket type task:

For this

How to Add Products to a Basket

How to Add Products to a Basket

using System;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout
{
    public class BasketCountViewModel
    {
        public Int32 ProductCount { get; set; }
    }
}
@model Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.BasketCountViewModel
@Html.ActionLink(String.Format(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.BasketCount.BasketLinkText, this.Model.ProductCount), "Basket", "Checkout")
@using (Ajax.BeginForm("AddProductToBasket", "Checkout", new { productId = this.Model.ProductId }, new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "sBasketCount", LoadingElementId = "bLoading", OnSuccess = "AddToBasketConfirm", OnFailure = "AddToBasketFailure" }))
    {
        <h1>@this.Model.Name</h1>
        <p>@this.Model.BrandName</p>
 
        <!-- … -->
 
        if (this.Model.Quantities.Count() > 0)
        {
            <p><button type="submit" class="continue">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Catalog.Product.AddToBasketButtonText</button></p>
        }
    }
using Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.MVC;
using Magelia.WebStore.Services.Contract.Data.Store;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Linq;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
    public class CheckoutController : BaseController
    {
        public CheckoutController(SampleSiteContext sampleSiteContext)
            : base(sampleSiteContext)
        { }
 
        public ActionResult Basket()
        {
            return View();
        }
 
        [HttpGet]
        public PartialViewResult BasketCount()
        {
            return this.PartialView(new BasketCountViewModel { 
                ProductCount = this.SampleSiteContext.StoreClient.GetBasketProductsCount(this.SampleSiteContext.UserName) });
        }
 
        [HttpPost]
        public ActionResult AddProductToBasket(Guid productId, Int32 quantity)
        {
            IEnumerable<BasketEntryResult> results = this.SampleSiteContext.StoreClient.AddProductsToBasket(
                this.SampleSiteContext.UserName, this.SampleSiteContext.IsAnonymous, productId, quantity,
                this.SampleSiteContext.Currency.CurrencyId, this.SampleSiteContext.Culture.LCID,
                this.SampleSiteContext.Location);
            if (!results.Any(r => r.ProductId == productId && r.State == BasketEntryState.Dispatched))
            {
                throw new Exception("Product undispatched");
            }
            return this.PartialView("BasketCount", new BasketCountViewModel {
                ProductCount = this.SampleSiteContext.StoreClient.GetBasketProductsCount(this.SampleSiteContext.UserName) 
            });
        }
    }
}
$(document).ready(
    function () {
        $('div.popin').popin();
        UpdateBasketCount();
    }
);
 
function AddToBasketConfirm() {
    $('#bAddToBasketSuccess').popin('show');
};
 
function AddToBasketFailure() {
    $('#bAddToBasketFailure').popin('show');
};
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title</title>
        <link href="@Url.Content("~/Content/Themes/styles.css")" rel="stylesheet" type="text/css" />
        <link href='http://fonts.googleapis.com/css?family=Terminal+Dosis' rel='stylesheet' type='text/css'>
        <script src="@Url.Content("~/Scripts/Libraries/jquery-1.6.4.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/Libraries/jquery.validate.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/Libraries/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/Libraries/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/Plugins/popin.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/Views/Shared/_Layout.js")" type="text/javascript"></script>
 
        <script type="text/javascript">
            //<![CDATA[
            function UpdateBasketCount() {
                $.get('@Url.Action("BasketCount", "Checkout")', function (response) {
                    $('#sBasketCount').html(response);
                });
            };
            //]]>
        </script>
 
        @if (IsSectionDefined("Scripts"))
        { 
            @RenderSection("Scripts")
        }
    </head>
    <body>
        <div id="bLoading">
        </div>
        <div id="bAddToBasketSuccess" class="popin">
            @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.AddToBasketConfirmationMessage
        </div>
        <div id="bAddToBasketFailure" class="popin">
            @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.AddToBasketFailureMessage
        </div>
        <header>
            <div id="top">
                <div class="wrapper">
                   <!-- … -->
                    <form>
                            <input type="text" placeholder="Search WebStore..." /><button type="submit">
                            @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.SearchButtonText</button>
                    </form>
                    <div id="action-bar">
                        @if (this.Request.IsAuthenticated)
                        {
                            @Html.ActionLink(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.MyAccountLinkText, "Index", "Account")
                            
                            @Html.ActionLink(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.LogOffLinkText, "LogOff", "Account")
                        }
                        else{
                            @Html.ActionLink(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Shared._Layout.LoginLinkText, "Logon", "Account") 
                        }
                        | <span id="sBasketCount"></span>
                    </div>
                </div>
            </div>
        </header>
        <!-- … -->
    </body>
</html>

How to List Basket Content

How to List Basket Content

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout
{
    public class BasketViewModel
    {
        public class LineItem
        {
            private IEnumerable<SelectListItem> _availableQuantities;
            private IEnumerable<String> _productDetails;
 
            public Guid LineItemId { get; set; }
            public String SKU { get; set; }
            public String ProductName { get; set; }
            public String ProductDescription { get; set; }
            public Decimal UnitPrice { get; set; }
            public Int32 Quantity { get; set; }
            public Decimal Weight { get; set; }
            public Decimal Total { get; set; }
            public Decimal Discount { get; set; }
            public Decimal Taxes { get; set; }
            public Decimal TotalIncludingDiscountAndTaxes { get; set; }
            public Boolean IsGift { get; set; }
            public String ImageUrl { get; set; }
            public String ImageAlternateText { get; set; }
 
            public IEnumerable<String> ProductDetails
            {
                get
                {
                    if (this._productDetails == null)
                    {
                        this._productDetails = Enumerable.Empty<String>();
                    }
                    return this._productDetails;
                }
                set
                {
                    this._productDetails = value;
                }
            }
 
            public IEnumerable<SelectListItem> AvailableQuantities
            {
                get
                {
                    if (this._availableQuantities == null)
                    {
                        this._availableQuantities = Enumerable.Empty<SelectListItem>();
                    }
                    return this._availableQuantities;
                }
                set
                {
                    this._availableQuantities = value;
                }
            }
        }
 
        public class OrderSubset
        {
            private IEnumerable<LineItem> _lineItems;
 
            public Guid OrderSubsetId { get; set; }
            public Int32 Index { get; set; }
            public Boolean Virtual { get; set; }
            public String WarehouseName { get; set; }
            public String WarehouseCode { get; set; }
            public Decimal ShippingRateValue { get; set; }
            public Decimal ShippingDiscount { get; set; }
            public Decimal ShippingTaxes { get; set; }
            public Decimal ShippingTotalIncludingDiscountAndTaxes { get; set; }
            public String CarrierName { get; set; }
            public String CarrierCode { get; set; }
            public String ShippingRateDescription { get; set; }
            public String ShippingRateCode { get; set; }
            public Decimal Weight { get; set; }
            public Decimal LineItemsTotal { get; set; }
            public Decimal LineItemsDiscount { get; set; }
            public Decimal LineItemsTaxes { get; set; }
            public Decimal LineItemsTotalIncludingDiscountAndTaxes { get; set; }
 
            public IEnumerable<LineItem> LineItems
            {
                get
                {
                    if (this._lineItems == null)
                    {
                        this._lineItems = Enumerable.Empty<LineItem>();
                    }
                    return this._lineItems;
                }
                set
                {
                    this._lineItems = value;
                }
            }
        }
 
        private IEnumerable<OrderSubset> _orderSubsets;
 
        public Decimal LineItemsTotal { get; set; }
        public Decimal LineItemsDiscount { get; set; }
        public Decimal LineItemsTaxes { get; set; }
        public Decimal LineItemsTotalInlcludingDiscountAndTaxes { get; set; }
        public Decimal Weight { get; set; }
        public Decimal ShippingTotal { get; set; }
        public Decimal ShippingDiscount { get; set; }
        public Decimal ShippingTaxes { get; set; }
        public Decimal ShippingTotalIncludingDiscountAndTaxes { get; set; }
        public Decimal OrderTotal { get; set; }
        public Decimal OrderDiscount { get; set; }
        public Decimal OrderTaxes { get; set; }
        public Decimal OrderTotalIncludingDiscountAndTaxes { get; set; }
        public String WeightUnit { get; set; }
 
        public IEnumerable<OrderSubset> OrderSubsets
        {
            get
            {
                if (this._orderSubsets == null)
                {
                    this._orderSubsets = Enumerable.Empty<OrderSubset>();
                }
                return this._orderSubsets;
            }
            set
            {
                this._orderSubsets = value;
            }
        }
    }
}
@model Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.BasketViewModel
@{
    this.ViewBag.Title = Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.PageTitle;
}
 
<article id="basket">
    <h1>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.BasketTitle</h1>
 
    @if (this.Model == default(Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.BasketViewModel))
    {
        <p>
            @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.EmptyBasketText
        </p>
    }
    else
    {
        <table width="100" border="1">
            @foreach (var orderSubset in this.Model.OrderSubsets)
            {
                <tr>
                    <th colspan="3">
                        @String.Format(orderSubset.Virtual ? 
                        Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.VirtualOrderSubsetHeaderText : 
                        Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.ShippableOrderSubsetHeaderText, 
                        orderSubset.Index, String.IsNullOrEmpty(orderSubset.WarehouseName) ? 
                        orderSubset.WarehouseCode : orderSubset.WarehouseName, String.IsNullOrEmpty(orderSubset.CarrierName) ? 
                        orderSubset.CarrierCode : orderSubset.CarrierName)
                    </th>
                </tr>
                <tr>
                    <th scope="col" class="description">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.Col1Title</th>
                    <th scope="col" class="options">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.Col2Title</th>
                    <th align="right" scope="col" class="price">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.Col3Title</th>
                </tr>
                foreach (var lineItem in orderSubset.LineItems)
                {
                    <tr>
                        <td align="left" valign="top" class="description">
                            @Html.Image(lineItem.ImageUrl, lineItem.ImageAlternateText, new { @class = "left"})
                            <p>
                                @lineItem.ProductName<br />
                                @lineItem.ProductDescription
                            </p>
                            @if (!lineItem.IsGift)
                            {
                                @Html.ActionLink(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.RemoveText, 
                                "UpdateLineItemQuantity", "Checkout", new { lineItemId = lineItem.LineItemId, quantity = 0 }, null)
                            }
                        </td>
                        <td align="left" valign="top" class="options">
                                <dl>
                                <dt>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.ProductIDLabel</dt>
                                <dd>@lineItem.SKU</dd>
                                <dt>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.QuantityLabel</dt>
                                <dd>
                                    @lineItem.Quantity
                                </dd>
                            </dl>
                        </td>
                        <td align="right" valign="top" class="price">@lineItem.TotalIncludingDiscountAndTaxes.ToString("C")</td>
                    </tr>
                }
            }
        </table>
        <img src="~/Content/Medias/Images/creditcards.gif" class="safe" />
        <div class="right">
            <strong>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.SubTotalLabel</strong> <em>@Model.LineItemsTotalInlcludingDiscountAndTaxes.ToString("C")</em><br />
            <p>
                <strong>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.ShippingLabel</strong> <em>@Model.ShippingTotalIncludingDiscountAndTaxes.ToString("C")</em>
            </p>
            <strong>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.TotalLabel</strong> <em>@Model.OrderTotalIncludingDiscountAndTaxes.ToString("C")</em>
        </div>
        <button class="continue">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.PaymentButtonText</button>
    }
</article>
using Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.MVC;
using Magelia.WebStore.Services.Contract.Data.Store;
using Magelia.WebStore.Services.Contract.Parameters.Store;
using Magelia.WebStore.Client.Web.Security;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
    public class CheckoutController : BaseController
    {
        public CheckoutController(SampleSiteContext sampleSiteContext)
            : base(sampleSiteContext)
        { }
 
        private BasketViewModel GetBasketViewModel(Boolean composeQuantities)
        {
            Basket basket = this.SampleSiteContext.StoreClient.GetBasket(this.SampleSiteContext.UserName);
 
            if (basket == null)
            {
                return null;
            }
 
            IEnumerable<Stock> stocks = null;
            IEnumerable<Magelia.WebStore.Services.Contract.Data.Store.Price> prices = null;
            IEnumerable<LineItem> lineItems = basket.OrderSubsets.SelectMany(p => p.LineItems);
            IEnumerable<Guid> distinctProductIds = lineItems.Select(li => li.ProductId.Value).Distinct();
            String[] excludedTypes = new String[] { "file", "files", "video", "videos", "audio", "audios", "application", "applications", "document", "documents", "other", "others" };
 
            if (composeQuantities)
            {
                stocks = this.SampleSiteContext.StoreClient.GetInventory(distinctProductIds, this.SampleSiteContext.Location);
                prices = this.SampleSiteContext.StoreClient.GetPrices(distinctProductIds, this.SampleSiteContext.Currency.CurrencyId, this.SampleSiteContext.Culture.LCID, this.SampleSiteContext.Location);
            }
 
            return new BasketViewModel
            {
                LineItemsTotal = basket.LineItemsTotal,
                LineItemsDiscount = basket.LineItemsDiscount,
                LineItemsTaxes = basket.LineItemsTaxesConsideringUnitDiscount,
                LineItemsTotalInlcludingDiscountAndTaxes = basket.LineItemsTotalIncludingDiscountAndTaxes,
                ShippingTotal = basket.ShippingTotal,
                ShippingDiscount = basket.ShippingDiscount,
                ShippingTaxes = basket.ShippingTaxesConsideringShippingDiscount,
                ShippingTotalIncludingDiscountAndTaxes = basket.ShippingTotalIncludingDiscountAndTaxes,
                Weight = basket.Weight,
                WeightUnit = basket.WeightUnit,
                OrderTotal = basket.OrderTotal,
                OrderDiscount = basket.OrderDiscount,
                OrderTaxes = basket.OrderTotalIncludingTotalDiscountAndTaxes - basket.OrderTotalIncludingDiscount,
                OrderTotalIncludingDiscountAndTaxes = basket.OrderTotalIncludingTotalDiscountAndTaxes,
                OrderSubsets = basket.OrderSubsets.Select(
                                (p, i) => new BasketViewModel.OrderSubset
                                {
                                    OrderSubsetId = p.OrderSubsetId,
                                    Index = i,
                                    Virtual = p.Virtual,
                                    ShippingRateValue = p.ShippingRateValue,
                                    ShippingDiscount = p.ShippingDiscount,
                                    ShippingTaxes = p.ShippingTaxesConsideringShippingDiscount,
                                    ShippingTotalIncludingDiscountAndTaxes = p.ShippingRateValueIncludingDiscountAndTaxes,
                                    WarehouseCode = p.WarehouseCode,
                                    WarehouseName = p.WarehouseName,
                                    CarrierCode = p.CarrierCode,
                                    CarrierName = p.CarrierName,
                                    ShippingRateCode = p.ShippingRateCode,
                                    ShippingRateDescription = p.ShippingRateDescription,
                                    Weight = p.Weight,
                                    LineItemsTotal = p.LineItemsTotal,
                                    LineItemsTaxes = p.LineItemsTaxesConsideringUnitDiscount,
                                    LineItemsDiscount = p.LineItemsDiscount,
                                    LineItemsTotalIncludingDiscountAndTaxes = p.LineItemsTotalIncludingDiscountAndTaxes,
                                    LineItems = p.LineItems.Select(
                                                    li =>
                                                    {
                                                        BasketViewModel.LineItem lineItem = new BasketViewModel.LineItem
                                                        {
                                                            LineItemId = li.LineItemId,
                                                            SKU = li.SKU,
                                                            IsGift = li.Gift,
                                                            ProductName = li.ProductName,
      ProductDescription = li.ProductDescription,
                                                            UnitPrice = li.UnitPrice,
                                                            Quantity = li.Quantity,
                                                            Weight = li.TotalWeight,
                                                            Total = li.LineItemPrice,
                                                            Discount = li.LineItemDiscount,
                                                            Taxes = li.LineItemTaxesConsideringUnitDiscount,
                                                            TotalIncludingDiscountAndTaxes = li.TotalIncludingDiscountAndTaxesConsideringUnitDiscount,
                                                            AvailableQuantities = composeQuantities ? this.GetAvailableQuantities(li, lineItems, stocks, prices) : Enumerable.Empty<SelectListItem>(),
                                                            ProductDetails = li.ProductDetail
                                                                                .Where(pd => !excludedTypes.Contains(pd.Type, StringComparer.InvariantCultureIgnoreCase))
                                                                                .Select(
                                                                                    pd =>
                                                                                    {
                                                                                        String value = String.Empty;
                                                                                        switch (pd.Type.ToLower())
                                                                                        {
                                                                                            case "boolean":
                                                                                                value = pd.BooleanValue.HasValue && pd.BooleanValue.Value ? 
                                                                                                    Magelia.WebStore.Client.Web.UI.Sample.Resources.Global.Yes :
                                                                                                    Magelia.WebStore.Client.Web.UI.Sample.Resources.Global.No;
                                                                                                break;
                                                                                            case "integer":
                                                                                                value = pd.IntValue.ToString();
                                                                                                break;
                                                                                            case "decimal":
                                                                                                value = pd.DecimalValue.ToString();
                                                                                                break;
                                                                                            case "datetime":
                                                                                                value = pd.DateTimeValue.ToString();
                                                                                                break;
                                                                                            case "list":
                                                                                                value = pd.ListValue;
                                                                                                break;
                                                                                            case "string":
                                                                                                value = pd.StringValue;
                                                                                                break;
                                                                                        }
                                                                                        return String.IsNullOrEmpty(value) ? String.Empty : String.Format("{0} : {1}", String.IsNullOrEmpty(pd.Name) ? pd.Code : pd.Name, value);
                                                                                    }
                                                                                )
                                                                                .Where(s => !String.IsNullOrEmpty(s))
                                                        };
                                                        FileDetail mainImage = li.ProductDetail.Where(pd => pd.Code.Equals("MainImage", StringComparison.OrdinalIgnoreCase)).SelectMany(pd => pd.Files).FirstOrDefault();
                                                        if (mainImage != null)
                                                        {
                                                            lineItem.ImageUrl = mainImage.Path;
                                                            lineItem.ImageAlternateText = mainImage.AlternateText;
                                                        }
                                                        return lineItem;
                                                    }
                                                )
                                }
                            )
            };
 
        }
 
        private Boolean IsComposable(Int32 quantity, IEnumerable<Magelia.WebStore.Services.Contract.Data.Store.Price> prices)
        {
            Int32 quantityToCompose = quantity;
            foreach (Int32 priceQuantity in prices.OrderByDescending(ps => ps.Quantity).Select(s => s.Quantity))
            {
                Int32 factor = quantityToCompose / priceQuantity;
                if (factor >= 1)
                    quantityToCompose -= factor * priceQuantity;
            }
            return quantityToCompose == 0;
        }
 
        private IEnumerable<SelectListItem> GetAvailableQuantities(LineItem lineItem, IEnumerable<LineItem> lineItems, IEnumerable<Stock> stocks, IEnumerable<Magelia.WebStore.Services.Contract.Data.Store.Price> prices)
        {
            List<Int32> availableQuantities = new List<Int32> { 0, lineItem.Quantity };
            IEnumerable<Stock> productStocks = stocks.Where(s => s.ProductId == lineItem.ProductId);
            IEnumerable<LineItem> productLineItems = lineItems.Where(li => li.ProductId == lineItem.ProductId);
            Int32 existingQuantity = productLineItems.Where(pli => pli.LineItemId != lineItem.LineItemId).Sum(pli => pli.Quantity);
            Int32 availableQuantity = productStocks.Any(ps => ps.Unlimited) ? Int32.MaxValue : productStocks.Sum(ps => ps.Quantity ?? 0) + existingQuantity + lineItem.Quantity;
            IEnumerable<Int32> range = Enumerable.Range(1, (lineItem.Quantity >= 50 ? lineItem.Quantity * 2 : 50) + 1);
            range = range.Where(q => this.IsComposable(q + existingQuantity, prices.Where(p => p.ProductId == lineItem.ProductId)) && availableQuantity >= q + existingQuantity);
            availableQuantities.AddRange(range);
            return availableQuantities
                    .Distinct()
                    .OrderBy(aq => aq)
                    .Select(
                        aq =>
                        {
                            String quantity = aq.ToString();
                            return new SelectListItem { Text = quantity, Value = quantity, Selected = aq == lineItem.Quantity };
                        }
                    );
        }
 
        [HttpGet]
        public ActionResult Basket()
        {
            return View(this.GetBasketViewModel(true));
        }
 
        // ...
    }
}

How to Change Product Quantities

How to Change Product Quantities

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout
{
    public class BasketViewModel
    {
        public class LineItem
        {
            private IEnumerable<SelectListItem> _availableQuantities;
            private IEnumerable<String> _productDetails;
 
            public Guid LineItemId { get; set; }
            public String SKU { get; set; }
            public String ProductName { get; set; }
            public String ProductDescription { get; set; }
            public Decimal UnitPrice { get; set; }
            public Int32 Quantity { get; set; }
            public Decimal Weight { get; set; }
            public Decimal Total { get; set; }
            public Decimal Discount { get; set; }
            public Decimal Taxes { get; set; }
            public Decimal TotalIncludingDiscountAndTaxes { get; set; }
            public Boolean IsGift { get; set; }
            public String ImageUrl { get; set; }
            public String ImageAlternateText { get; set; }
 
            public IEnumerable<String> ProductDetails
            {
                get
                {
                    if (this._productDetails == null)
                    {
                        this._productDetails = Enumerable.Empty<String>();
                    }
                    return this._productDetails;
                }
                set
                {
                    this._productDetails = value;
                }
            }
 
            public IEnumerable<SelectListItem> AvailableQuantities
            {
                get
                {
                    if (this._availableQuantities == null)
                    {
                        this._availableQuantities = Enumerable.Empty<SelectListItem>();
                    }
                    return this._availableQuantities;
                }
                set
                {
                    this._availableQuantities = value;
                }
            }
        }
 
        public class OrderSubset
        {
            private IEnumerable<LineItem> _lineItems;
 
            public Guid OrderSubsetId { get; set; }
            public Int32 Index { get; set; }
            public Boolean Virtual { get; set; }
            public String WarehouseName { get; set; }
            public String WarehouseCode { get; set; }
            public Decimal ShippingRateValue { get; set; }
            public Decimal ShippingDiscount { get; set; }
            public Decimal ShippingTaxes { get; set; }
            public Decimal ShippingTotalIncludingDiscountAndTaxes { get; set; }
            public String CarrierName { get; set; }
            public String CarrierCode { get; set; }
            public String ShippingRateDescription { get; set; }
            public String ShippingRateCode { get; set; }
            public Decimal Weight { get; set; }
            public Decimal LineItemsTotal { get; set; }
            public Decimal LineItemsDiscount { get; set; }
            public Decimal LineItemsTaxes { get; set; }
            public Decimal LineItemsTotalIncludingDiscountAndTaxes { get; set; }
 
            public IEnumerable<LineItem> LineItems
            {
                get
                {
                    if (this._lineItems == null)
                    {
                        this._lineItems = Enumerable.Empty<LineItem>();
                    }
                    return this._lineItems;
                }
                set
                {
                    this._lineItems = value;
                }
            }
        }
 
        private IEnumerable<OrderSubset> _orderSubsets;
 
        public Decimal LineItemsTotal { get; set; }
        public Decimal LineItemsDiscount { get; set; }
        public Decimal LineItemsTaxes { get; set; }
        public Decimal LineItemsTotalInlcludingDiscountAndTaxes { get; set; }
        public Decimal Weight { get; set; }
        public Decimal ShippingTotal { get; set; }
        public Decimal ShippingDiscount { get; set; }
        public Decimal ShippingTaxes { get; set; }
        public Decimal ShippingTotalIncludingDiscountAndTaxes { get; set; }
        public Decimal OrderTotal { get; set; }
        public Decimal OrderDiscount { get; set; }
        public Decimal OrderTaxes { get; set; }
        public Decimal OrderTotalIncludingDiscountAndTaxes { get; set; }
        public String WeightUnit { get; set; }
 
        public IEnumerable<OrderSubset> OrderSubsets
        {
            get
            {
                if (this._orderSubsets == null)
                {
                    this._orderSubsets = Enumerable.Empty<OrderSubset>();
                }
                return this._orderSubsets;
            }
            set
            {
                this._orderSubsets = value;
            }
        }
    }
}
<!-- … -->
<td align="left" valign="top" class="options">
                                <dl>
                                <dt>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.ProductIDLabel</dt>
                                <dd>@lineItem.SKU</dd>
                                <dt>@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.Basket.QuantityLabel</dt>
                                <dd>
                                    @if (lineItem.AvailableQuantities.Any() && !lineItem.IsGift)
                                    {
                                        String ddlQuantityId = String.Format("_{0}", Guid.NewGuid());
                                        @Html.DropDownList("quantity", lineItem.AvailableQuantities, new { id = ddlQuantityId })
                                        <script type="text/javascript">
                                            //<![CDATA[
                                            $('#@ddlQuantityId').change(function () { document.location.href = '@Url.Action("UpdateLineItemQuantity", "Checkout")/LineItemId/@lineItem.LineItemId/quantity/' + $(this).val(); });
                                            //]]>
                                        </script>
                                    }
                                    else
                                    {
                                        @lineItem.Quantity
                                    }
                                </dd>
                            </dl>
                        </td>
<!--…  -->
using Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.MVC;
using Magelia.WebStore.Services.Contract.Data.Store;
using Magelia.WebStore.Services.Contract.Parameters.Store;
using Magelia.WebStore.Client.Web.Security;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
 
        // ...
 
        [HttpGet]
        public ActionResult UpdateLineItemQuantity(Guid lineItemId, Int32 quantity)
        {
            Basket basket = this.SampleSiteContext.StoreClient.GetBasket(this.SampleSiteContext.UserName);
            if (basket != null)
            {
                IEnumerable<LineItem> lineItems = basket.OrderSubsets.SelectMany(p => p.LineItems);
                LineItem lineItem = lineItems.FirstOrDefault(li => li.LineItemId == lineItemId);
                if (lineItem != null)
                {
                    Int32 totalQuantity = lineItems.Where(li => li.ProductId == lineItem.ProductId && li.LineItemId != lineItem.LineItemId && !li.Gift).Sum(li => li.Quantity) + quantity;
                    this.SampleSiteContext.StoreClient.UpdateProductQuantity(this.SampleSiteContext.UserName, lineItem.ProductId.Value, totalQuantity);
                }
            }
            return this.RedirectToAction("Basket");
        }
    }
}
routes.MapRoute("UpdateProductQuantity", "Checkout/UpdateLineItemQuantity/LineItemId/{lineItemId}/Quantity/{quantity}", new { controller = "Checkout", action = "UpdateLineItemQuantity" });

How to Transfer a Basket from an Anonymous to a Logged-in User ?

if (this.SampleSiteContext.StoreClient.TransferBasket(this.SampleSiteContext.UserName, viewModel.UserName) != null)
                {
                    this.SampleSiteContext.StoreClient.UpdateBasket(viewModel.UserName, this.SampleSiteContext.Currency.CurrencyId, 
                        this.SampleSiteContext.Culture.LCID, this.SampleSiteContext.Location);
                }

AccountController

using Magelia.WebStore.Client.Web.UI.Sample.Runtime.Profile;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.Security;
using Magelia.WebStore.Services.Contract.Data;
using Magelia.WebStore.Services.Contract.Parameters.Store;
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
    public class AccountController : BaseController
    {
         //…
 
         /// <summary>
        /// Post Logon action
        /// </summary>
        /// <param name="viewModel"></param>
        /// <returns>Action result</returns>
        [HttpPost]
        public ActionResult Logon(LogonViewModel viewModel)
        {
            if (Membership.ValidateUser(viewModel.UserName, viewModel.Password))
            {
                if (this.SampleSiteContext.StoreClient.TransferBasket(this.SampleSiteContext.UserName, viewModel.UserName) != null)
                {
                    this.SampleSiteContext.StoreClient.UpdateBasket(viewModel.UserName, this.SampleSiteContext.Currency.CurrencyId, 
                        this.SampleSiteContext.Culture.LCID, this.SampleSiteContext.Location);
                }
                FormsAuthentication.SetAuthCookie(viewModel.UserName, viewModel.RememberMe);
                viewModel.HasBeenAuthenticated = true;
                if (String.IsNullOrEmpty(viewModel.ReturnUrl))
                {
                    return this.RedirectToAction("Index");
                }
                else
                {
                    return this.Redirect(viewModel.ReturnUrl);
                }
            }
            else
            {
                this.ModelState.AddModelError(String.Empty, Resources.Controllers.AccountController.InvalidCredentialsErrorMessage);
                return this.View(viewModel);
            }
        }
 
        /// <summary>
        /// Get LogOff action
        /// </summary>
        /// <param name="returnUrl">Return Url</param>
        /// <returns>Action result</returns>
        [HttpGet]
        [Authorize]
        public ActionResult LogOff(String returnUrl)
        {
            FormsAuthentication.SignOut();
            if (this.SampleSiteContext.StoreClient.TransferBasket(this.SampleSiteContext.UserName, this.HttpContext.Request.AnonymousID) != null)
            {
                this.SampleSiteContext.StoreClient.UpdateBasket(this.HttpContext.Request.AnonymousID, this.SampleSiteContext.Currency.CurrencyId,
                    this.SampleSiteContext.Culture.LCID, this.SampleSiteContext.Location);
            }
            if (String.IsNullOrEmpty(returnUrl))
            {
                return this.RedirectToAction("Index", "Home");
            }
            else
            {
                return this.Redirect(returnUrl);
            }
        }
 
        /// <summary>
        /// Post New action 
        /// </summary>
        /// <param name="viewModel"></param>
        /// <param name="returnUrl">Return url</param>
        /// <returns>Action result</returns>
        [HttpPost]
        public ActionResult New(NewViewModel viewModel, String returnUrl)
        {
            if (this.ModelState.IsValid)
            {
                MembershipCreateStatus createStatus;
                WebStoreSampleMembershipUser user = Membership.CreateUser(viewModel.Username, viewModel.Password, viewModel.Email, viewModel.Question, viewModel.Answer, true, out createStatus).AsSampleSiteUser();
                if (createStatus == MembershipCreateStatus.Success)
                {
                    user.Profile.FirstName = viewModel.FirstName;
                    user.Profile.MiddleName = viewModel.MiddleName;
                    user.Profile.LastName = viewModel.LastName;
                    user.Profile.Title = (Title)viewModel.TitleId;
                    user.Profile.Save();
                    FormsAuthentication.SetAuthCookie(viewModel.Username, false);
                    if (this.SampleSiteContext.StoreClient.TransferBasket(this.SampleSiteContext.UserName, viewModel.Username) != null)
                    {
                        this.SampleSiteContext.StoreClient.UpdateBasket(viewModel.Username, this.SampleSiteContext.Currency.CurrencyId,
                            this.SampleSiteContext.Culture.LCID, this.SampleSiteContext.Location);
                    }
                    if (String.IsNullOrWhiteSpace(returnUrl))
                    {
                        IndexViewModel editViewModel = this.GetIndexViewModel(user);
                        editViewModel.AccountCreated = true;
                        return this.View("Index", editViewModel);
                    }
                    return this.Redirect(returnUrl);
                }
                else
                {
                    this.ModelState.AddModelError("User", this.GetMembershipCreateErrorMessage(createStatus));
                }
            }
            viewModel.AvailableTitles = SampleSiteContext.Titles;
            return this.View(viewModel);
        }
    }
}

Download the code sample of this tutorial:
How to manage basket - ZIP - 30.8 mo