Magelia WebStore

  • Summary

How to save a basket as an order

How to get shipping information

Download the code sample of this tutorial:
How to save a basket as an order - ZIP - 16.9 mo

In Magelia WebStore, you can define the default rules of tax zones and shipping zones for the different countries where you intend to sell your products. You need to define these default rules because when a potential customer visits a website, prices and taxes must be available for him/her to see, even if the customer’s location is unknown and taxes/fees cannot be applied. Default tax and shipping rules are useful when the shipping address, or location of the customer, is unknown.

For example, if you defined a shipping zone for the United States (Mainland) and a shipping zone for Alaska, the default location, when the location of the customer is unknown, would be the U.S. (Mainland) and not Alaska. Of course, when an order is finalized and the shipping address is known, the tax and shipping fees will be recalculated accordingly.

Shipping

Shipping Zones: A shipping zone corresponds to a geographical area for which one or several modes of transportation will be proposed and for which shipping fees must be calculated. A shipping zone can correspond to a region smaller than a state, to a state or several states, to a country, or to a region larger than a country. WebStore is designed so that the delivery zones of your carriers can be easily set up. For example, it is easy to determine an 11 UPS zone that corresponds to South Korea, Singapore, Thailand, and Turkey.

Carriers: Carriers are the businesses that are in charge of delivering the merchandise from the departing warehouse to the customer’s delivery address. Carriers can, for example, be created to designate businesses like DHL, FedEx, UPS, etc. Magelia WebStore allows you to easily define carriers and assign them a name and a logo. When delivery fees are created, these fees will vary according to the carrier chosen.

Shipping Rates: Shipping/Delivery charges correspond to price ranges determined for the carrier, the departing warehouse of the merchandise and the shipping zone. The shipping/delivery charges themselves can be subject to taxes and are therefore assigned to a tax category. It is possible to define the shipping rates based on the order amount, the product quantity of the order placed, or the product weight.


Taxes

Tax Zones: Tax zones determine where to apply taxes. A tax zone can be defined as a country, a geographic zone smaller than a country, a state or province, or a zone smaller than a state. This simplifies applying taxes to regions that have exceptions.

For example, in the Magelia WebStore, it is easy to define a tax zone that corresponds to Spain and which surrounding territories (like the Canary Islands) would be excluded because they do not fall under the same tax regulations as the rest of the country.

Tax Categories: A tax category is associated with a tax type and corresponds to a product category where all the same tax calculation rules apply. Tax categories permit in Europe, for example, to determine products that are subject to the VAT standard rate or the VAT reduced rate, etc. You can also determine tax categories according to merchandise classifications, commodity codes, etc. Tax categories are easy to assign to products and enable Magelia WebStore to automatically calculate the taxes that apply to a certain product. Various tax categories can be assigned to a product in order to apply different taxes to the same product.

Tax Rates: A tax rate is determined by three factors: the tax category, the departure tax zone and the destination tax zone.

The following code sample illsutratess how to get the shipping costs for an order in Magelia WebStore:

using Magelia.WebStore.Client.Web.UI.Sample.Runtime.Validation;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout
{
    public class ShippingRatesViewModel
    {
        public class ShippingRateValue
        {
            public Guid ShippingRateValueId { get; set; }
            public String Description { get; set; }
            public String CarrierName { get; set; }
            public String CarrierCode { get; set; }
            public String LogoUrl { get; set; }
            public String LogoAlternateText { get; set; }
            public Decimal Price { get; set; }
            public Decimal Taxes { get; set; }
            public Decimal PriceIncludingTaxes { get; set; }
        }
 
        public class ShippingRateSelection
        {
            private List<ShippingRateValue> _availableShippingRateValues;
 
            [Required(ErrorMessageResourceType = typeof(Magelia.WebStore.Client.Web.UI.Sample.Resources.Models.Checkout.ShippingRatesViewModel), ErrorMessageResourceName = "SelectedShippingRateValueIdRequiredErrorMessage")]
            public Nullable<Guid> SelectedShippingRateValueId { get; set; }
 
            public Guid OrderSubsetId { get; set; }
            public Int32 OrderSubsetIndex { get; set; }
 
            public List<ShippingRateValue> AvailableShippingRateValues
            {
                get
                {
                    if (this._availableShippingRateValues == null)
                    {
                        this._availableShippingRateValues = new List<ShippingRateValue>();
                    }
                    return this._availableShippingRateValues;
                }
                set
                {
                    this._availableShippingRateValues = value;
                }
            }
        }
 
        private List<ShippingRateSelection> _shippingRateSelections;
 
        public BasketViewModel Basket { get; set; }
        public AddressViewModel BillingAddress { get; set; }
        public AddressViewModel ShippingAddress { get; set; }
 
        [ShippingRate(ErrorMessageResourceType = typeof(Magelia.WebStore.Client.Web.UI.Sample.Resources.Models.Checkout.ShippingRatesViewModel), ErrorMessageResourceName = "MissingShippingRateValueErrorMessage")]
        public List<ShippingRateSelection> ShippingRateSelections
        {
            get
            {
                if (this._shippingRateSelections == null)
                {
                    this._shippingRateSelections = new List<ShippingRateSelection>();
                }
                return this._shippingRateSelections;
            }
            set
            {
                this._shippingRateSelections = value;
            }
        }
    }
}
@model Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.ShippingRatesViewModel
@{
    this.ViewBag.Title = Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.PageTitle;
}
@{
    Boolean hasShippingAddress = this.Model.ShippingAddress != 
        default(Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.RawAddressViewModel);    
}
<div class="checkout">
    <div class="block basket">
        <h1>
          @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.BasketTitle
        </h1>
    </div>
    <div class="addresses">
        <div class="block">
            <h4>
         @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.BillingAddressTitle
            </h4>
            <div class="content">
                @Html.DisplayFor(m => m.BillingAddress)
            </div>
        </div>
        <div class="block">
            <h4>
                @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.ShippingAddressTitle
            </h4>
            <div class="content">
                @if (hasShippingAddress)
                {
                    @Html.DisplayFor(m => m.ShippingAddress)
                }
                else
                { 
                    @Html.DisplayFor(m => m.BillingAddress)
                }
            </div>
        </div>
    </div>
    <div class="block">
        <h4>
            @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.ShippingRatesTitle
        </h4>
        <div class="content">
            @using (this.Html.BeginForm())
            { 
                @Html.ValidationSummary()
                @Html.EditorForModel()
                <div class="buttons">
                    <ul>
                        <li>
                            @Html.ActionLink(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.BackLinkText, this.User.Identity.IsAuthenticated ? "CustomerAddress" : "RawAddress", new { shipping = hasShippingAddress })
                        </li>
                        <li>
                            <button type="submit" class="highlight">@Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.ShippingRates.NextButtonText</button>
                        </li>
                    </ul>
                </div>
            }
        </div>
    </div>
</div>
@model Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout.ShippingRatesViewModel
@if (this.Model.ShippingRateSelections.Any())
{
    <table>
        <tbody>
            @{
    Int32 shippingRateSelectionCount = 0;    
            }
            @foreach (var shippingRateSelection in this.Model.ShippingRateSelections)
            {
                <tr class="header">
                    <td colspan="7">
                        @String.Format(Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.OrderSubsetHeaderText, shippingRateSelection.OrderSubsetIndex)
                        @Html.HiddenFor(m => m.ShippingRateSelections[shippingRateSelectionCount].OrderSubsetId)
                    </td>
                </tr>
                <tr class="header">
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.LogoHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.DescriptionHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.CarrierNameHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.PriceHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.TaxesHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.PriceIncludingTaxesHeaderText
                    </td>
                    <td>
                        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.SelectionHeaderText
                    </td>
                </tr>
                foreach (var shippingRateValue in shippingRateSelection.AvailableShippingRateValues)
                { 
                <tr>
                    <td>
                        @Html.Image(shippingRateValue.LogoUrl, shippingRateValue.LogoAlternateText)
                    </td>
                    <td>
                        @shippingRateValue.Description
                    </td>
                    <td>
                        @(String.IsNullOrEmpty(shippingRateValue.CarrierName) ? shippingRateValue.CarrierCode : shippingRateValue.CarrierName)
                    </td>
                    <td>
                        @shippingRateValue.Price.ToString("C")
                    </td>
                    <td>
                        @shippingRateValue.Taxes.ToString("C")
                    </td>
                    <td>
                        @shippingRateValue.PriceIncludingTaxes.ToString("C")
                    </td>
                    <td>
                        @Html.RadioButtonFor(m => m.ShippingRateSelections[shippingRateSelectionCount].SelectedShippingRateValueId, shippingRateValue.ShippingRateValueId)
                    </td>
                </tr>
                }
                shippingRateSelectionCount++;
            }
        </tbody>
    </table>
}
else
{ 
    <p>
        @Magelia.WebStore.Client.Web.UI.Sample.Resources.Views.Checkout.EditorTemplates.ShippingRatesViewModel.NoShippingRateAvailableText
    </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 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;
using CustomerAddress = Magelia.WebStore.Services.Contract.Data.Customer.Address;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.Security;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
    public class CheckoutController : BaseController
    {
         //…
        private const String BasketHashSessionKey = "BasketHash";
        private String _basketHash
        {
            get
            {
                return this.Session[CheckoutController.BasketHashSessionKey] as String;
            }
            set
            {
                this.Session[CheckoutController.BasketHashSessionKey] = value;
            }
        }
 
        [HttpGet]
        public ActionResult ShippingRates()
        {
            BasketViewModel basketViewModel = this.GetBasketViewModel(false);
            if (basketViewModel == null || this._billingAddress == null)
            {
                return this.RedirectToAction("Basket");
            }
            else if (basketViewModel.OrderSubsets.All(p => p.Virtual))
            {
                this._basketHash = this.SampleSiteContext.StoreClient.GetBasketHash(this.SampleSiteContext.UserName);
                return this.RedirectToAction("Order");
            }
            else
            {
                ShippingRatesViewModel viewModel = new ShippingRatesViewModel();
                this.SetShippingRatesParameters(viewModel, basketViewModel);
                return this.View(viewModel);
            }
        }
 
        [HttpPost]
        public ActionResult ShippingRates(ShippingRatesViewModel viewModel)
        {
            if (this.ModelState.IsValid)
            {
                this.SampleSiteContext.StoreClient.SetShippingRateValues(this.SampleSiteContext.UserName, viewModel.ShippingRateSelections.ToDictionary(srs => srs.OrderSubsetId, srs => srs.SelectedShippingRateValueId.Value));
                this._basketHash = this.SampleSiteContext.StoreClient.GetBasketHash(this.SampleSiteContext.UserName);
                return this.RedirectToAction("Order");
            }
            this.SetShippingRatesParameters(viewModel, this.GetBasketViewModel(false));
            return this.View(viewModel);
        }
 
        private void SetShippingRatesParameters(ShippingRatesViewModel viewModel, BasketViewModel basketViewModel)
        {
            viewModel.Basket = basketViewModel;
            viewModel.BillingAddress = this._billingAddress;
            viewModel.ShippingAddress = this._shippingAddress;
            viewModel.ShippingRateSelections = this.SampleSiteContext.StoreClient.GetShippingRateValues(this.SampleSiteContext.UserName, this.SampleSiteContext.Culture.LCID)
                                                    .Select(
                                                        psrv => new ShippingRatesViewModel.ShippingRateSelection
                                                        {
                                                            OrderSubsetId = psrv.Key,
                                                            OrderSubsetIndex = basketViewModel.OrderSubsets.First(p => p.OrderSubsetId == psrv.Key).Index,
                                                            AvailableShippingRateValues = psrv.Value.Select(
                                                                srv => new ShippingRatesViewModel.ShippingRateValue
                                                                {
                                                                    ShippingRateValueId = srv.ShippingRateValueId,
                                                                    Description = srv.ShippingRateDescription,
                                                                    CarrierName = srv.CarrierName,
                                                                    CarrierCode = srv.CarrierCode,
                                                                    LogoUrl = srv.LogoUrl,
                                                                    LogoAlternateText = srv.LogoAlternateText,
                                                                    Price = srv.Price,
                                                                    Taxes = srv.TotalTax,
                                                                    PriceIncludingTaxes = srv.PriceTaxIncluded
                                                                }
                                                            )
                                                            .OrderBy(srv => srv.PriceIncludingTaxes)
                                                            .ToList()
                                                        }
                                                    )
                                                    .OrderBy(srs => srs.OrderSubsetIndex)
                                                    .ToList();
        }
    }
}
using Ninject;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Runtime.Validation
{
    public class ShippingRateAttribute : ValidationAttribute
    {
        [Inject]
        public SampleSiteContext StarterSiteContext { get; set; }
 
#if !NET40
        public override bool RequiresValidationContext
        {
            get
            {
                return true;
            }
        }
#endif
 
        protected override ValidationResult IsValid(Object value, ValidationContext validationContext)
        {
            IEnumerable<dynamic> shippingRateSelections = ((IEnumerable)value).OfType<dynamic>();
 
            if (!this.StarterSiteContext.StoreClient.GetBasket(this.StarterSiteContext.UserName)
                       .OrderSubsets.Where(p => !p.Virtual)
                       .Select(p => p.OrderSubsetId)
                       .All(pid => shippingRateSelections.Any(srs => srs.OrderSubsetId == pid && srs.SelectedShippingRateValueId != null)))
            {
                String errorMessage = this.FormatErrorMessage(validationContext.DisplayName);
                return new ValidationResult(errorMessage, new String[] { validationContext.MemberName });
            }
            else
            {
                return ValidationResult.Success;
            }
        }
    }
}

How to call the SaveAsOrder method

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Models.Checkout
{
    public class OrderViewModel : IValidatableObject
    {
        public BasketViewModel Basket { get; set; }
        public AddressViewModel ShippingAddress { get; set; }
        public AddressViewModel BillingAddress { get; set; }
 
        [Display(ResourceType = typeof(Magelia.WebStore.Client.Web.UI.Sample.Resources.Models.Checkout.OrderViewModel), Name = "AcceptConditionsLabel")]
        public Boolean AcceptConditions { get; set; }
 
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (!this.AcceptConditions)
            {
                yield return new ValidationResult(Magelia.WebStore.Client.Web.UI.Sample.Resources.Models.Checkout.OrderViewModel.AcceptConditionsErrorMessage);
            }
        }
    }
}
Your order is confirmed.
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;
using CustomerAddress = Magelia.WebStore.Services.Contract.Data.Customer.Address;
using Magelia.WebStore.Client.Web.UI.Sample.Runtime.Security;
 
namespace Magelia.WebStore.Client.Web.UI.Sample.Controllers
{
    public class CheckoutController : BaseController
    {
          //…
 
        [HttpGet]
        public ActionResult Order()
        {
            OrderViewModel viewModel = new OrderViewModel();
            this.SetOrderParameters(viewModel);
            if (String.IsNullOrEmpty(this._basketHash) || viewModel.Basket == null)
            {
                return this.RedirectToAction("Basket");
            }
            else
            {
                return this.View(viewModel);
            }
        }
 
        [HttpPost]
        public ActionResult Order(OrderViewModel viewModel)
        {
            if (this.ModelState.IsValid)
            {
                SaveAsOrderResult result;
                Order order = this.SampleSiteContext.StoreClient.SaveAsOrder(this.SampleSiteContext.UserName, this._basketHash, this.Request.UserHostAddress, out result);
                switch (result)
                {
                    case SaveAsOrderResult.AddressMissing:
                        this.ModelState.AddModelError("AddressMissing", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.AddressMissingErrorMessage);
                        break;
                    case SaveAsOrderResult.BasketNotFound:
                        this.ModelState.AddModelError("BasketNotFound", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.BasketNotFoundErrorMessage);
                        break;
                    case SaveAsOrderResult.Error:
                        this.ModelState.AddModelError("Error", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.ErrorErrorMessage);
                        break;
                    case SaveAsOrderResult.InconsistentBasket:
                        this.ModelState.AddModelError("InconsistentBasket", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.InconsistentBasketErrorMessage);
                        break;
                    case SaveAsOrderResult.InvalidHash:
                        this.ModelState.AddModelError("InvalidHash", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.InvalidHashErrorMessage);
                        break;
                    case SaveAsOrderResult.ProductNotFound:
                        this.ModelState.AddModelError("ProductNotFound", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.ProductNotFoundErrorMessage);
                        break;
                    case SaveAsOrderResult.ShippingRateValueMissing:
                        this.ModelState.AddModelError("ShippingRateValueMissing", Magelia.WebStore.Client.Web.UI.Sample.Resources.Controllers.CheckoutController.ShippingRateValueMissingErrorMessage);
                        break;
                    case SaveAsOrderResult.Success:
                        return this.View("OrderConfirm");
                }
            }
            this.SetOrderParameters(viewModel);
            return this.View(viewModel);
        }
    }
}

Once the order has been saved, it is available in Magelia WebStore administration console (in the Dashboard menu).

The following list is the different order statuses in the Magelia WebStore software:

New order: An order placed for your store by a customer that has not yet been approved or prepared.
Approved order: Once an order has been verified as valid, the order status becomes “Approved”.
Order in progress: With this status, you can indicate when an order is being prepared, or that the order has been transferred to a logistician.
Payment due: This status indicates that the order payment has not been received.
Shipped order: When a delivery service becomes responsible for the shipment of an order, the status becomes “Shipped Order”.
Delivered order: If your logistic provider is able to inform you of the delivery of a customer’s order, you can use this status to indicate that the order has been delivered.
Canceled order: This status indicates that an order has been canceled by the customer.
Rejected order: This status indicates that the person in charge of approving orders has rejected the customer’s order.
Further inquiry required: This status signifies that an order has been shipped but has not yet been received by the customer and requires further inquiry with the delivery service.
On hold: This status holds an order before its delivery. This status can refer to, for example, the orders for which more information is required about the customer before approving the order, or to orders for which you are not certain if the stock is available.

Download the code sample of this tutorial:
How to save a basket as an order - ZIP - 16.9 mo