Walkthrough: Adding customer preferences to the Retail online sample store
Important
This content is archived and is not being updated. For the latest documentation, see Microsoft Dynamics 365 product documentation. For the latest release plans, see Dynamics 365 and Microsoft Power Platform release plans.
Applies To: Microsoft Dynamics AX 2012 R3
To make the new customer preference data available in SharePoint pages, you must modify code in Retail SDK\Online Channel\Storefront\Storefront.sln. First, you add code to make the new fields available in the data model and the view model. Then you modify the web controls, and then you modify the store pages to enable display, entry, and modification of the new fields. Finally, you rebuild and redeploy the solution.
You make the new field available on the Customer control, which is included on the Account page and the Edit Profile. On the Account page, the field is read-only. Customers can edit the field on the EditProfile page.
Prerequisites Make the new fields available in the data model and the view model Make changes to the controller Make the new fields visible on the site pages Add code to ControlResources.Designer.cs Add text to ControlResources.resx |
Prerequisites
These walkthroughs illustrate adding a field to a retail channel that enables customers to opt in to receive special offers by email. In this scenario, the retailer wants to indicate whether customers wish to receive email about special offers, either in an online store or in a modern POS client. The walkthroughs should be completed in the following order:
Walkthrough: Adding a table for customer preferences to the AX 2012 database
Walkthrough: Extending the CRT to add customer preference data for Retail clients
Walkthrough: Extending retail data distribution infrastructure for customer preferences
Walkthrough: Adding customer preferences to the Retail online sample store (online store only)
Make the new fields available in the data model and the view model
In the SharePoint.Web.Services project of the Storefront.sln solution, you modify ViewModel\Customer.cs and ObjectModel\Customermapper.cs. You modify the Javascript files Customer.js and CustomerDisplay.js inStorefront\Scripts\Scripts\ViewModel.
To modify the view model
Open the Retail SDK\Online channel folder and open Storefront.sln. and navigate to Modify Customer.cs in SharePoint.Web.Services\ViewModel\Customer.cs by adding the following code at the end of the file:
Open Customer.cs in SharePoint.Web.Services\ViewModel.
Add the following code at the end of the file:
[DataMember] public Enum EmailOptIn { get; set; }
To map the new field to the object model
You modify SharePoint.Web.Services/ObjectModel/ CustomerMapper.cs to create a mapping between the new fields in the data model and the view model.
You add the new fields to ConvertToViewModel and ConvertToDataModel methods.
Add the following lines of code to the ConvertToViewModel method after the first If statement.
bool emailOptIn = false; /// NEW Code var emailOptInExtensionProperty = customer.ExtensionProperties.Where(c => c.Key == "EMAILOPTIN").FirstOrDefault(); if (emailOptInExtensionProperty != null) { int intEmailOptIn = (int)emailOptInExtensionProperty.Value.GetPropertyValue(); // NEW Code emailOptIn = intEmailOptIn == 0 ? false : true; }
Add the following code after the statement LoyaltyCardNumber – customer.LoyaltyCardNumber,.
EmailOptIn = emailOptIn // NEW Code
The following example shows the ConvertToViewModel method after adding the code.
internal static ViewModel.Customer ConvertToViewModel(DataModel.Customer customer) { if (customer == null) { throw new ArgumentNullException("customer"); } bool emailOptIn = false; /// NEW Code var emailOptInExtensionProperty = customer.ExtensionProperties.Where(c => c.Key == "EMAILOPTIN").FirstOrDefault(); if (emailOptInExtensionProperty != null) { int intEmailOptIn = (int)emailOptInExtensionProperty.Value.GetPropertyValue(); // NEW Code emailOptIn = intEmailOptIn == 0 ? false : true; } ViewModel.Customer vmCustomer = new ViewModel.Customer() { AccountNumber = customer.AccountNumber, Addresses = ConvertToViewModel(customer, customer.Addresses), Email = customer.Email, FirstName = customer.FirstName, LastName = customer.LastName, MiddleName = customer.MiddleName, Phone = customer.Phone, PhoneExt = customer.PhoneExt, RecordId = customer.RecordId, Url = customer.Url, PrimaryAddress = AddressMapper.ConvertToViewModel(customer.GetPrimaryAddress(), customer), LoyaltyCardNumber = customer.LoyaltyCardNumber, EmailOptIn = emailOptIn // NEW Code }; return vmCustomer; }
Add the following line of code to the UpdateDataModel method.
dataModelCustomer["EMAILOPTIN"] = Convert.ToInt64(customer.EmailOptIn); ///NEW Code
The following example shows where the line is added.
internal static DataModel.Customer UpdateDataModel(DataModel.Customer dataModelCustomer, ViewModel.Customer customer) { if (customer == null) { throw new ArgumentNullException("customer"); } if (dataModelCustomer == null) { throw new ArgumentNullException("dataModelCustomer"); } dataModelCustomer.Email = customer.Email; dataModelCustomer.FirstName = customer.FirstName; dataModelCustomer.LastName = customer.LastName; dataModelCustomer.MiddleName = customer.MiddleName; dataModelCustomer.Phone = customer.Phone; dataModelCustomer.PhoneExt = customer.PhoneExt; dataModelCustomer.Url = customer.Url; dataModelCustomer.Language = "en-us"; // only support en-Us in the UI at this time dataModelCustomer. ["EMAILOPTIN"] = Convert.ToInt64(customer.EmailOptIn); ///NEW Code
Add the following example code to ConvertToDataModel immediately before the return statement:
dmCustomer["Age"] = customer.Age; dmCustomer["SpecialOffers"] = customer.SpecialOffers;
To modify Customer.js
Open Customer.js in the Scripts\Scripts folder of Storefront.sln.
Add the following code to the function updateCustomer.
/// NEW Code if ($view.find('.msax-EmailOptInCheckBox').is(':checked')) { customer.EmailOptIn = true; } else { customer.EmailOptIn = false; }//NEW code
The following example shows the function after the code is added.
// Update a customer this.UpdateCustomer = function () { if (!Microsoft.Trigger($view, "UpdateCustomer")) { return; } validator.Validate($view); // NEW Code if ($view.find('.msax-EmailOptInCheckBox').is(':checked')) { customer.EmailOptIn = true; } else { customer.EmailOptIn = false; } //NEW code if (validator.IsValid) { services.UpdateCustomer(customer); } }
To modify CustomerDisplay.js
Open CustomerDisplay.js in the Scripts\Scripts folder of Storefront.sln.
Add the following code to the Initialize function.
// NEW Code if (customer.EmailOptIn) { $view.find('.msax-EmailOptInCheckBox').prop('checked', true); } else { $view.find('.msax-EmailOptInCheckBox').prop('checked', false); } $view.find('.msax-EmailOptInCheckBox').attr("disabled", true);//NEW code
The following example shows the Initialize function after adding the code.
// CustomerViewModel constructor. function Initialize() { // Attach the Render event to the AfterRefresh // event so when the data is loaded it will be // displayed immediately. services.OnGetCustomerSuccess(onGetCustomerSuccess); services.OnGetCustomerFailure(onGetCustomerFailure); binder = new Microsoft.Dynamics.Retail.SharePoint.Web.UI.TemplateBinder( $view.find('.msax-CustomerDisplayTemplate'), $view.find('.msax-CustomerDisplayContainer')); // Set the customer control to refresh upon page load, me.GetCustomer(); } // bind the customer to the display only fields this.BindCustomerDisplayData = function (customer) { $view.find('.msax-CustomerDisplayAddress1').text(customer.FirstName + ' ' + customer.MiddleName + ' ' + customer.LastName); $view.find('.msax-CustomerDisplayAddress2').text(customer.PrimaryAddress.StreetNumber + ' ' + customer.PrimaryAddress.Street); $view.find('.msax-CustomerDisplayAddress3').text(customer.PrimaryAddress.City + ' ' + customer.PrimaryAddress.State + ' ' + customer.PrimaryAddress.ZipCode); $view.find('.msax-CustomerDisplayAddress4').text(customer.PrimaryAddress.Country); $view.find('.msax-CustomerDisplayPrimaryEmail').text(customer.Email); $view.find('.msax-CustomerDisplayLoyaltyNumber').text(customer.LoyaltyCardNumber); // NEW Code if (customer.EmailOptIn) { $view.find('.msax-EmailOptInCheckBox').prop('checked', true); } else { $view.find('.msax-EmailOptInCheckBox').prop('checked', false); } $view.find('.msax-EmailOptInCheckBox').attr("disabled", true); //NEW code }
Make changes to the controller
To modify SharePointWebControls/Customer/Customer.cs
Open Customer.cs in the SharePoint.Web.Controls/Customer folder.
Add the following code to the RetailWebControl class.
private HtmlInputCheckBox emailOptIn; // NEW code
The following example shows the after adding the code to the RetailWebControl class.
public class Customer : RetailWebControl { private HtmlInputText firstName; private HtmlInputText lastName; private HtmlInputText email; private HtmlInputText phone; private HtmlInputText street; private HtmlInputText city; private HtmlInputText state; private HtmlInputText zipCode; private HtmlInputText country; private HtmlInputCheckBox emailOptIn; // NEW code
Add the following two code blocks to the GetCustomerTemplate method.
This code creates the checkbox.
///NEW code this.emailOptIn = ControlFactory.CreateCheckBox("msax-EmailOptInCheckBox"); this.emailOptIn.AddAttribute(AXDataBindAttributes.Value, "EmailOptIn"); HtmlLabel emailOptInLabel = ControlFactory.CreateLabel(ControlResources.CustomerEmailOptIn, null, this.emailOptIn); HtmlFieldPanel emailOptInContainer = ControlFactory.CreateFieldPanel(emailOptInLabel, this.emailOptIn, "msax-CustomerEmailOptIn"); //NEW code
This code adds the control to the container.
contactInfo.Controls.Add(emailOptInContainer);
The following example shows the GetCustomerTemplate method with the new code added.
private HtmlTemplate GetCustomerTemplate() { // Create the master template to put the shopping cart table in. HtmlTemplate template = new HtmlTemplate(); template.AddCssClass("msax-CustomerTemplate"); this.CssClass = "msax-CustomerControl"; HtmlFieldSet contactInfo = ControlFactory.CreateFieldSet("msax-ContactInfoControls"); HtmlParagraph contactInfoParagraph = ControlFactory.CreateParagraph("msax-ContactInfoHeader"); contactInfoParagraph.InnerText = ControlResources.CustomerContactInformation; // Create the controls this.email = ControlFactory.CreateTextBox("msax-EmailTextBox"); this.email.AddAttribute(AXDataBindAttributes.Value, "Email"); this.email.AddRegularExpressionValidator(ControlResources.CustomerEmailNotSpecified, @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"); this.email.MaxLength = 255; HtmlLabel emailLabel = ControlFactory.CreateLabel(ControlResources.CustomerEmail, null, this.email); HtmlFieldPanel emailContainer = ControlFactory.CreateFieldPanel(emailLabel, this.email, "msax-CustomerEmail"); this.firstName = ControlFactory.CreateTextBox("msax-FirstNameTextBox"); this.firstName.AddAttribute(AXDataBindAttributes.Value, "FirstName"); this.firstName.AddRequiredFieldValidator(ControlResources.CustomerFirstnameNotSpecified, ""); this.firstName.MaxLength = 100; HtmlLabel firstNameLabel = ControlFactory.CreateLabel(ControlResources.CustomerFirstName, null, this.firstName); HtmlFieldPanel firstNameContainer = ControlFactory.CreateFieldPanel(firstNameLabel, this.firstName, "msax-CustomerFirstName"); this.lastName = ControlFactory.CreateTextBox("msax-LastNameTextBox"); this.lastName.AddAttribute(AXDataBindAttributes.Value, "LastName"); this.lastName.AddRequiredFieldValidator(ControlResources.CustomerLastnameNotSpecified, ""); this.lastName.MaxLength = 100; HtmlLabel lastNameLabel = ControlFactory.CreateLabel(ControlResources.CustomerLastName, null, this.lastName); HtmlFieldPanel lastNameContainer = ControlFactory.CreateFieldPanel(lastNameLabel, this.lastName, "msax-CustomerLastName"); this.phone = ControlFactory.CreateTextBox("msax-PhoneTextBox"); this.phone.AddAttribute(AXDataBindAttributes.Value, "Phone"); this.phone.MaxLength = 255; HtmlLabel phoneLabel = ControlFactory.CreateLabel(ControlResources.CustomerPhone, null, this.phone); HtmlFieldPanel phoneContainer = ControlFactory.CreateFieldPanel(phoneLabel, this.phone, "msax-CustomerPhone"); /// NEW code this.emailOptIn = ControlFactory.CreateCheckBox("msax-EmailOptInCheckBox"); this.emailOptIn.AddAttribute(AXDataBindAttributes.Value, "EmailOptIn"); HtmlLabel emailOptInLabel = ControlFactory.CreateLabel(ControlResources.CustomerEmailOptIn, null, this.emailOptIn); HtmlFieldPanel emailOptInContainer = ControlFactory.CreateFieldPanel(emailOptInLabel, this.emailOptIn, "msax-CustomerEmailOptIn"); /// NEW code // Add the individual controls to the fieldset contactInfo.Controls.Add(contactInfoParagraph); contactInfo.Controls.Add(emailContainer); contactInfo.Controls.Add(firstNameContainer); contactInfo.Controls.Add(lastNameContainer); contactInfo.Controls.Add(phoneContainer); contactInfo.Controls.Add(emailOptInContainer); //NEW code HtmlFieldSet primaryAddressInfo = ControlFactory.CreateFieldSet("msax-AddressInfoControls"); HtmlParagraph primaryAddressInfoParagraph = ControlFactory.CreateParagraph("msax-AddressInfoHeader"); primaryAddressInfoParagraph.InnerText = ControlResources.CustomerAddressInformation;
To modify SharePointWebControls/Customer/CustomerDisplay.cs
Open CustomerDisplay.cs
Add the following code line to the header.
using System.Web.UI.HtmlControls; /// NEW Code
Add the following code to the GetCustomerTemplate method.
/// NEW code HtmlLabel displayEmailOptInLabel = ControlFactory.CreateLabel(ControlResources.CustomerEmailOptIn, "msax-CustomerDisplayEmailOptInLabel"); HtmlInputCheckBox displayEmailOptIn = ControlFactory.CreateCheckBox("msax-CustomerDisplayEmailOptIn"); /// NEW code
Add the following code to the GetCustomerTemplate method.
/// NEW Code displayFieldSet.Controls.Add(displayEmailOptInLabel); displayFieldSet.Controls.Add(displayEmailOptIn); /// NEW Code
The following example shows the new code added to the GetCustomerTemplate method.
private HtmlTemplate GetCustomerTemplate() { // Create the master template to put the shopping cart table in. HtmlTemplate template = new HtmlTemplate(); template.AddCssClass("msax-CustomerDisplayTemplate"); this.CssClass = "msax-CustomerDisplayControl"; HtmlFieldSet displayFieldSet = ControlFactory.CreateFieldSet("msax-CustomerDisplayControls"); HtmlLabel displayAddressLabel = ControlFactory.CreateLabel(ControlResources.CustomerDisplayAddressLabel, "msax-CustomerDisplayAddressLabel"); HtmlSpan displayAddressLine1 = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayAddress1"); HtmlSpan displayAddressLine2 = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayAddress2"); HtmlSpan displayAddressLine3 = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayAddress3"); HtmlSpan displayAddressLine4 = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayAddress4"); HtmlSpan displayAddressLine5 = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayAddress5"); HtmlLabel displayEmailLabel = ControlFactory.CreateLabel(ControlResources.CustomerDisplayPrimaryEmail, "msax-CustomerDisplayPrimaryEmailLabel"); HtmlSpan displayEmail = ControlFactory.CreateSpan(string.Empty, "msax-CustomerDisplayPrimaryEmail"); /// NEW code HtmlLabel displayEmailOptInLabel = ControlFactory.CreateLabel(ControlResources.CustomerEmailOptIn, "msax-CustomerDisplayEmailOptInLabel"); HtmlInputCheckBox displayEmailOptIn = ControlFactory.CreateCheckBox("msax-CustomerDisplayEmailOptIn"); /// NEW code displayFieldSet.Controls.Add(displayAddressLabel); displayFieldSet.Controls.Add(displayAddressLine1); displayFieldSet.Controls.Add(displayAddressLine2); displayFieldSet.Controls.Add(displayAddressLine3); displayFieldSet.Controls.Add(displayAddressLine4); displayFieldSet.Controls.Add(displayAddressLine5); /// NEW Code displayFieldSet.Controls.Add(displayEmailOptInLabel); displayFieldSet.Controls.Add(displayEmailOptIn); /// NEW Code displayFieldSet.Controls.Add(displayEmailLabel); displayFieldSet.Controls.Add(displayEmail); template.Controls.Add(displayFieldSet); return template; } }
Make the new fields visible on the site pages
Modify the following files in SharePoint.Web.Storefront/Controls project:
ControlResources.Designer.cs
ControlResources.resx
Add code to ControlResources.Designer.cs
/// <summary> NEW code
/// Looks up a localized string similar to Email for special offers:.
/// </summary>
internal static string CustomerEmailOptIn
{
get
{
return ResourceManager.GetString("CustomerEmailOptIn", resourceCulture);
}
}
/// NEW code
The following example shows where the new code is added in ControlResourcesDisigner.cs.
/// <summary>
/// Looks up a localized string similar to Share my information with Contoso partners..
/// </summary>
internal static string CustomerShareInformation {
get {
return ResourceManager.GetString("CustomerShareInformation", resourceCulture);
}
}
/// <summary> NEW code
/// Looks up a localized string similar to Email for special offers:.
/// </summary>
internal static string CustomerEmailOptIn
{
get
{
return ResourceManager.GetString("CustomerEmailOptIn", resourceCulture);
}
}
/// NEW code
/// <summary>
/// Looks up a localized string similar to State:.
/// </summary>
internal static string CustomerState {
get {
return ResourceManager.GetString("CustomerState", resourceCulture);
}
}
Add text to ControlResources.resx
Add text for the labels and messages to Storefront/Controls/ControlResources.resx as shown in the following table:
Name |
Value |
Comment |
---|---|---|
CustomerEmailOptIn |
Email for special offers |
Checkbox for EmailOptIn |