<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Retail life</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/retaillife/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/retaillife/feed.xml</link>
    <description>Reflections on retail industry and technology</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:25:24 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Moved</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/moved</link>
      <pubDate>Tue, 18 Dec 2018 14:39:18 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=1045</guid>
      <description><![CDATA[I have moved my blog to Dynamics 365 Community.]]></description>
      <content:encoded><![CDATA[I have moved <a href="https://community.dynamics.com/365/retail/b/retaillife">my blog to Dynamics 365 Community</a>.]]></content:encoded>
    </item>
    <item>
      <title>Pricing and Discount – Product Price Precision</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/pricing-and-discount-product-price-precision</link>
      <pubDate>Tue, 18 Jul 2017 14:06:54 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=1035</guid>
      <description><![CDATA[In the United States, the common price precision is two digits - for example, a mug costs $9.99 -...]]></description>
      <content:encoded><![CDATA[In the United States, the common price precision is two digits - for example, a mug costs $9.99 - except when it is not.
<h2>Example: gasoline</h2>
In the gas pump, it is three digits, for example, $2.499 per gallon
<h2>Example: nails</h2>
In some hardware stores, you would get $4.99 for 100 nails. So per "each" nail, the price is $0.0499. One option is model 100 nails as a unit of measure - say "hundreds" - but then what about 150 nails? Partial quantity, like 1.5 "hundreds" of nails, could be tricky. For example, if the price is $4.99 for 70 nails, so "seventy" is a unit of measure. Then how do you model 80 nails?
<h2>Per product price precision</h2>
Introduce per product price precision. By default, we still have a common price precision. On top of that, we can have override by product. In the gasoline case, it is three digits for gasoline, while two for everything else.

It is a simple solution, yet not perfect. If we take unit of measure into account, then it is possible that price precision can vary by unit of measure, the price for one nail can be $0.0499, but for a box (say 200 nails per box), you may want a different precision.

Before we go on with the complexity, ask yourself whether you really need it. In case you absolutely need it per unit of measure, we have two options.
<ol>
 	<li>Model price precision per product per unit of measure</li>
 	<li>Model price precision per product for the default unit of measure, and convert the precision when converting the unit of measure. (There may be some rounding of precision.)</li>
</ol>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Extensibility – Engagement Notes</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-extensibility-engagement-notes</link>
      <pubDate>Mon, 17 Jul 2017 17:15:10 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=1025</guid>
      <description><![CDATA[I have talked to many partners about pricing and discount extensibility, not just for Dynamics 365...]]></description>
      <content:encoded><![CDATA[I have talked to many partners about pricing and discount extensibility, not just for Dynamics 365 for Retail, but also for AX 2012. I thought to share the experience.
<h2>Full picture of customer discount scenarios</h2>
I often get asked about a specific discount customization, yet I always ask for full discount scenarios. For example, buy a phone and get all its accessories 20% off. In this case, the discount by itself is easy to understand.

The question I would always ask is what happens when there is another discount for the phone or its accessories: do they complete or do they compound?

In the end, we need to understand all of the customer's discount offerings, and how they would expect to handle concurrency. To me, it is paramount for the customer and the partner to understand the full picture. The last thing you want is unexpected behaviors, and there are plenty if we do not think or probe.

The partner would sometimes have to go back to the customer for the answers. It could be tricky, as it could increase the scope of what to deliver for the customer. In the worse cases, it could be like opening a can of worms. The good news is that customers in general would like to have a transparent and easy-to-understand discount setup.

A main part of the full picture is the discount concurrency control model, which we will cover next,
<h2>Best price within priority and compound across</h2>
We have <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/07/retail-discount-concurrency-control-pricing-zone/">another model</a>, so in reality, you could choose that one, but the new model, <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/08/retail-discount-concurrency-control-compete-within-priority-and-compound-across/">best price within priority and compound across</a>, is simpler, more flexible and easier to understand. From all of my engagements so far, the new model suit the customers better. In fact, I do not see a reason to use the old model. (Prove me wrong if you will.)
<h2>Individual discount design</h2>
I have covered <a href="https://blogs.msdn.microsoft.com/retaillife/retail-discount-engine-design/individual-retail-discount-design/">individual discount design</a> in <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/12/retail-discount-design-mix-and-match-discount-i/">many</a> <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/13/retail-discount-design-mix-and-match-discount-ii/">posts</a> <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/17/retail-discount-design-simplex-discount-with-quantity-control/">before</a>. In the end, technically as well as functionally, the goal is to strike a balance between a maintainable complexity and features.
<h2>Sample and tests in SDK</h2>
The only thing you need to play with pricing sample and tests in SDK is visual studio. Please play with it. You have no excuse not to.

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/08/retail-discount-concurrency-control-compete-within-priority-and-compound-across/">Retail Discount Concurrency Control - Compete Within Priority and Compound Across</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-sample-test/">Dynamics Retail Discount Extensibility â€“ Sample Test</a>]]></content:encoded>
    </item>
    <item>
      <title>Pricing and Discount – Dual Unit of Measures</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/pricing-and-discount-dual-unit-of-measures</link>
      <pubDate>Mon, 17 Jul 2017 13:12:11 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=1015</guid>
      <description><![CDATA[For some products, we have to deal with dual unit of measures for pricing and discount.
Example:...]]></description>
      <content:encoded><![CDATA[For some products, we have to deal with dual unit of measures for pricing and discount.
<h2>Example: cheese</h2>
The price on a piece of cheese depends on its weight, while a retailer may also want to offer a buy one get one free discount. In other words, price depends on unit of measure "pounds", while B1G1 free discount rests on unit of measure "ea".
<h2>Example: wine</h2>
A bottle of wine cost $10, in other words, "ea" is $10. If we want to do volume-based discount, then we have to rely on the volume of "ea" bottle, say 750 ml. For a big bottle, it may be 1500ml.
<h2>Dual unit of measure analysis</h2>
There is an important difference between wine and cheese: for a bottle of wine, the volume is fixed, while for cheese, each piece has its own weight.
<h3>Variant-specific</h3>
In addition, if we consider wine as variant by volume, then the volume is variant specific.
<h3>Not the same as common case of unit of measure conversion.</h3>
You scan a bottle of wine during checkout, then you update unit of measure to "case", which is 6 "ea". For the dual unit of measures we talked about, weight (or volume) is inherent in the item, and you cannot update unit of measure to "pounds" or "ml".
<h2>Two models of dual unit of measures</h2>
<h3>Per product (or variant) unit of measure conversion</h3>
For a specific wine, "ea" is converted to a fixed volume, say 750ml or 1500ml
<h3>Per barcode unit of measure conversion</h3>
When we generate the barcode for a piece of cheese, either the weight is embedded in the barcode, or the barcode-weight is stored somewhere in the system, and we are able to recover the weight when we scan the barcode.]]></content:encoded>
    </item>
    <item>
      <title>Retail Discount Design – Mix and Match with Amount Qualification</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/retail-discount-design-mix-and-match-with-amount-qualification</link>
      <pubDate>Wed, 24 May 2017 16:13:36 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=1005</guid>
      <description><![CDATA[We covered mix and match with fixed quantity in the past. What about minimal amount...]]></description>
      <content:encoded><![CDATA[We covered <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/12/retail-discount-design-mix-and-match-discount-i/">mix and match with fixed quantity</a> in the past. What about minimal amount threshold?
<h2>Setup</h2>
<ul>
 	<li>One <strong>qualifying group</strong> with amount threshold</li>
 	<li>One <strong>application group</strong> with fixed quantity</li>
</ul>
Example: buy shirts worth $100+, get a free tie.
<h2>Preliminary analysis</h2>
In general, it is not suited for the <a href="https://blogs.msdn.microsoft.com/retaillife/2016/12/06/retail-discount-concurrency-compounded-discounts/">dynamic programming algorithm</a> for <a href="https://blogs.msdn.microsoft.com/retaillife/2016/12/05/retail-discount-concurrency-how-to-get-best-deal/">discount best deal problem</a>, in that the number of possible basic discount application can mushroom exponentially as number of qualifying items increases. For example, amount threshold is $100, and you have items from $9, $10, all the way to $25, how many possible basic discount application can you have?

The <strong>recommendation</strong> is not to get it into <a href="https://blogs.msdn.microsoft.com/retaillife/2016/12/06/retail-discount-concurrency-compounded-discounts/">discount best deal algorithm</a>. Instead, finish it post-best-deal-algorithm, as we did for <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/13/retail-discount-design-mix-and-match-discount-ii/">mix and match least expensive favor retail discount</a>.

For qualifying, a common goal is to maximize number of qualifying groups for a given transaction. For example, 4 shirts: $40 $50 $60 $70. If you make $60 and $70 a group, then the other two will not qualify. One maximizing grouping would be {$40, $70} and {$50, $60}
<h2>How to apply the mix and match when it is (or as if it were) the only discount for the checkout cart?</h2>
For now, we assume qualifying group and application group do not overlap.

As we discussed earlier, the problem we have to solve is to maximize the number of qualifying groups, given the items in the checkout cart.
<ul>
 	<li>If the price of an item &gt;= the minimal amount threshold, then it itself can make a qualifying group. We can single them out and work on the rest. From now on, we will assume all item prices &lt; minimal amount threshold.</li>
 	<li>First, we can get maximum number of discount applications possible, by dividing total price by the minimal amount threshold. During the evaluation, if we reach it, then we can quit.</li>
 	<li>In addition, we may adjust maximum <strong><em>number of discount applications</em></strong> possible by checking <strong><em>number of application groups</em></strong></li>
</ul>
Now, it becomes a generic problem: given a list of decimal numbers, a threshold number and a possible maximum number of groups, find an arrangement of groups,
<ol>
 	<li>Satisfying: for each group, sum(number) &gt; = threshold number</li>
 	<li>Maximizing the number of groups, up to the possible maximum number of groups.</li>
</ol>
This is not a trivial problem.

If you want a fast, saying linear complexity solution: Form groups one by one, by getting one item at a time, if total price reaches minimal threshold, then finish the group and start a new group.

Obviously, this does not always give you the maximum number of discount applications, but we can apply some optimization if it does not. As an example â€“ you can come up with your own as well - we can check if we can swap items between high priced items in groups and low priced in leftover items, and we may be able to form a new group. It is relatively fast. While it does not guarantee the maximum, it often works well.

To solve the problem fully, we can apply dynamic programming. In short, if we can solve the problems for all first i items, where i between 1 and n, we can solve the problem for n+1. It is similar to <a href="https://blogs.technet.microsoft.com/galstertechblog/2016/03/10/text-justification-problem/">this text justification problem</a>. Now, this may not finish in reasonable amount of time, so we have to interrupt it if necessary, and resort to a backup algorithm, for example, the one we discussed earlier.
<h2>Qualifying group and application group overlap</h2>
We have to decide whether to put the item in the qualifying group or the application group. In general, the goal is to maximize total number of discount applications.
<h2>Multiple mix and match discounts with amount threshold</h2>
First, we should avoid setting up conflicting mix and match discounts with amount threshold. If we cannot guarantee non-conflict, we need to figure out a way to rank them, so we can apply them in a sequential order. One way to rank overlapped discounts is to <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/24/retail-discount-knapsack-marginal-value-ranking-algorithm-i/">compare marginal values for discounts over the overlapped items</a>.

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/12/retail-discount-design-mix-and-match-discount-i/">Retail Discount Design - Mix and Match Discount I</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/13/retail-discount-design-mix-and-match-discount-ii/">Retail Discount Design - Mix and Match Discount II</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2016/12/05/retail-discount-concurrency-how-to-get-best-deal/">Retail Discount Concurrency - Best Deal Knapsack Problem</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2016/12/06/dynamic-programming-for-retail-discount-knapsack-problem/">Dynamic Programming for Retail Discount Knapsack Problem</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/19/dynamics-retail-discount-concepts-discount-deal-estimate/">Dynamics Retail Discount Concepts - Discount Deal Estimate</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/24/retail-discount-knapsack-marginal-value-ranking-algorithm-i/">Retail Discount Knapsack - Marginal Value Ranking Algorithm I</a>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Extensibility – Multiple ISVs</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-extensibility-multiple-isvs</link>
      <pubDate>Fri, 05 May 2017 07:38:07 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=985</guid>
      <description><![CDATA[Update - this blog post has been moved to Dynamics 365 Community.

The core of Dynamics retail...]]></description>
      <content:encoded><![CDATA[<u><span style="color: #000120"><strong>Update</strong> - this blog post has been <a href="https://community.dynamics.com/365/retail/b/retaillife/archive/2017/05/05/dynamics-retail-discount-extensibility-multiple-isvs">moved to Dynamics 365 Community</a>.</span></u>

The core of Dynamics retail discount extensibility is to allow new discount types, and it is natural for a customer to employ new discount types from multiple ISVs. The post is also a replacement of <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-register-a-new-discount-type/">an early post of how to register a new discount type</a>.

The issue with the <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-register-a-new-discount-type/">original retail discount store approach</a> is that someone has to integrate two discount types in the retail discount store. Now we would like to achieve ISV isolation, i.e. to be able to register multiple discount types from multiple ISVs independently.

The core ingredients for a new discount type are the new discount type itself, creation of the new discount type and loading discount specific data. Introduce the concept of the discount package.
<pre>    public interface IDiscountPackage
    {
        PeriodicDiscountOfferType DiscountOfferType { get; }

        DiscountBase CreateDiscount(PeriodicDiscount discountAndLine);

        void LoadDiscountDetails(
            Dictionary&lt;string, DiscountBase&gt; offerIdToDiscountMap,
            IPricingDataAccessor pricingDataManager,
            SalesTransaction transaction);
    }
</pre>
For ISV isolation, we can't have ISV data accessor piggyback on core <em>IPricingDataAccessor</em>. Now you may notice that it does not cover the <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-main-picture/">new discount data accessor interface which would bridge channel and AX difference</a>. The interface is still there, just not in <em>IDiscountPackage</em> directly, as we can see in the following <em>IDiscountPackage</em> implementation for discount offer with amount cap.
<pre>    public class DiscountPackageAmountCap : IDiscountPackage
    {
        private IDataAccessorAmountCap amountCapDataAccessor;

        public DiscountPackageAmountCap(IDataAccessorAmountCap amountCapDataAccessor)
        {
            this.amountCapDataAccessor = amountCapDataAccessor;
        }

        public PeriodicDiscountOfferType DiscountOfferType
        {
            get { return (PeriodicDiscountOfferType)AmountCapDiscount.PeriodicDiscountOfferTypeAmountCap; }
        }

        public DiscountBase CreateDiscount(PeriodicDiscount discountAndLine)
        {
            ThrowIf.Null(discountAndLine, "discountAndLine");
            return new AmountCapDiscount(discountAndLine.ValidationPeriod);
        }

        public void LoadDiscountDetails(
            Dictionary&lt;string, DiscountBase&gt; offerIdToDiscountMap,
            IPricingDataAccessor pricingDataManager,
            SalesTransaction transaction)
        {
            ThrowIf.Null(offerIdToDiscountMap, "offerIdToDiscountMap");

            IEnumerable amountCapOfferIds = offerIdToDiscountMap.Where(p =&gt; (int)p.Value.PeriodicDiscountType == AmountCapDiscount.PeriodicDiscountOfferTypeAmountCap).Select(p =&gt; p.Key);
            if (amountCapOfferIds.Any())
            {
                IEnumerable caps = this.amountCapDataAccessor.GetDiscountAmountCapsByOfferIds(amountCapOfferIds) as IEnumerable;

                foreach (DiscountAmountCap cap in caps)
                {
                    AmountCapDiscount discount = offerIdToDiscountMap[cap.OfferId] as AmountCapDiscount;
                    if (discount != null)
                    {
                        discount.DiscountAmountCap = cap.AmountCap;
                        discount.ApplyBaseReduction = cap.ApplyBaseReduction;
                    }
                }
            }
        }
    }</pre>
The next step is to register the discount package in the retail discount engine. At this moment, you can register it at the top of pricing services customization, before calling pricing engine. AX follows similar approach.
<pre>    IDiscountPackage package = new DiscountPackageAmountCap(new ChannelDataAccessorAmountCap(requestContext));
    PricingEngineExtensionRepository.RegisterDiscountPackage(package);
</pre>
We are thinking of total dynamic invocation to avoid any code change at all, such as table-configuration driven.

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-main-picture/">Dynamics Retail Discount Extensibility - Main picture</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-sample-test/">Dynamics Retail Discount Extensibility - Sample Test</a>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Extensibility - Sample Test</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-extensibility-sample-test</link>
      <pubDate>Wed, 12 Apr 2017 19:07:53 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=955</guid>
      <description><![CDATA[Update - this blog post has been moved to Dynamics 365 Community.

It is under private review. If...]]></description>
      <content:encoded><![CDATA[<u><span style="color: #000120"><strong>Update</strong> - this blog post has been <a href="//community.dynamics.com/365/retail/b/retaillife/archive/2017/04/12/dynamics-retail-discount-extensibility-sample-test">moved to Dynamics 365 Community</a>.</span></u>

<del>It is under private review. If you need it now, ping me or your Dynamics retail extensibility team contact.</del>

We build sample test (<strong><em>Runtime.Extensions.PricingEngineSample.Tests</em></strong> in sample SDK) on top on the <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-test-foundation/">Dynamics retail pricing engine test foundation</a>.
<h2>Mock your new discount type</h2>
<ol>
 	<li>(Folder <strong><em>TestDataManager</em></strong>) Extends <strong><em>PeriodicDisocuntData</em></strong> and <strong><em>PeriodicDiscountLineData</em></strong> from test foundation. See <strong><em>ExtensionDiscountData</em></strong> and <strong><em>ExtensionDiscountLineData</em></strong>.</li>
 	<li>(Folder <strong><em>TestDataManager\SampleData</em></strong>) Add xml file for your discount data. See <strong><em>DiscountOffersAmountCap.xml</em></strong> or <strong><em>DiscountOffersLineFilter.xml</em></strong>. If a price group is associated with a non-zero priority, add it to <strong><em>ExtensionPriceGroups.xml</em></strong></li>
 	<li>(Folder <strong><em>TestDataManager</em></strong>) <strong><em>PricingSampleRepository</em></strong>: read extension discount data from xml</li>
 	<li>(Folder <strong><em>TestDataManager</em></strong>) <strong><em>GlobalTestSampleDataCache</em></strong>: your sample data global cache.</li>
 	<li>(Folder <strong><em>TestDataManager</em></strong>) <strong><em>ExtensionTestPricingDataManager</em></strong>. You can override <strong><em>ReadRetailDiscounts</em></strong> and <strong><em>GetRetailDiscountPriceGroups</em></strong>. In addition, it needs to support your own extension data access interface.</li>
</ol>
<h2>Now you can have fun with your new discount type and your tests.</h2>
All tests need to inherit from <strong><em>PricingBaseFunctionalTests</em></strong>.
<h2>Test Foundation Core Data</h2>
Under <em>RetailSdk\Documents\SampleExtensionsInstructions\PricingAndDiscounts\TestFoundationData</em>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-test-foundation/">Dynamics Retail Discount Extensibility - Test Foundation</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-main-picture/">Dynamics Retail Discount Extensibility - Main picture</a>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Extensibility - Test Foundation</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-extensibility-test-foundation</link>
      <pubDate>Wed, 12 Apr 2017 19:03:09 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=945</guid>
      <description><![CDATA[Creating a new discount type is a complex task as we really need to understand how discount works...]]></description>
      <content:encoded><![CDATA[Creating a new discount type is a complex task as we really need to understand <a href="https://blogs.msdn.microsoft.com/retaillife/retail-discount-engine-design/individual-retail-discount-design/">how discount works functionally</a>. Assuming we have just created a new discount type, how do we test it out? We can always deploy it to <a href="https://lcs.tie.dynamics.com/">Dynamics Lifecycle Services</a> sandbox and test it out there. I have tried it myself a few times. It is a great experience for me, but it would be beyond time-consuming for daily development.
<h2>Introduce the test foundation for Dynamics retail pricing engine.</h2>
It is under private review. If you need it now, ping me or your Dynamics retail extensibility team contact.

In short, you can run component level unit test in your SDK.
<ol>
 	<li>Mock pricing data accessor with XML. If you remember, we have <em>IPricingDataAccessor</em> to <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-main-picture/">bridge the difference between AX and channel data access</a>.</li>
 	<li>Automatic validation of data integrity.</li>
 	<li>Miscellaneous utilities.</li>
</ol>
<h2>Now, technical details.</h2>
It is more of a reference below. You may skip to <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-sample-test/">sample test post</a>.
<h3>Test data interface <em>ITransactionSetupAndDataManager</em></h3>
It inherits from pricing engine data accessor: <em>IPricingDataAccessor</em>. In addition, it helps with test data setup
<ol>
 	<li>Test configurations: concurrency control model, compound behavior, and hold-or-split-for-rounding, etc.</li>
 	<li><em>AddPriceGroupToTransaction</em> and <em>AddPriceGroupsToTransaction</em>: this sets up the (mock) discounts for the discount calculation.</li>
</ol>
<h3>Default mock implementation of <em>ITransactionSetupAndDataManager</em></h3>
<em>TestFoundationPricingDataManager</em>
<h3>Data integration verification</h3>
<em>SalesTransactionVerification</em>
<h3>Mock (main) discount data types.</h3>
<em>PeriodicDisocuntData</em> and <em>PeriodicDiscountLineData</em>. You need to know them if you want to extend them to support your new discount types.
<h3>Helpers</h3>
<em>TestFoundationCommonData</em>

<em>TestFoundationPriceContextHelper</em>

<em>TestFoundationPricingHelper</em>

<em>TestFoundationSalesTransactionHelper</em>
<h3>Test Foundation Core Data</h3>
Under <em>RetailSdk\Documents\SampleExtensionsInstructions\PricingAndDiscounts\TestFoundationData</em>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-main-picture/">Dynamics Retail Discount Extensibility - Main picture</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/12/dynamics-retail-discount-extensibility-sample-test/">Dynamics Retail Discount Extensibility - Sample Test</a>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Extensibility - Main Picture</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-extensibility-main-picture</link>
      <pubDate>Wed, 12 Apr 2017 14:37:52 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=935</guid>
      <description><![CDATA[Update - this blog post has been moved to Dynamics 365 Community.

Prerequisite: we assume you are...]]></description>
      <content:encoded><![CDATA[<u><span style="color: #000120"><strong>Update</strong> - this blog post has been <a href="https://community.dynamics.com/365/retail/b/retaillife/archive/2017/04/12/dynamics-retail-discount-extensibility-main-picture">moved to Dynamics 365 Community</a>.</span></u>

Prerequisite: we assume you are familiar with <a href="https://ax.help.dynamics.com/en/wiki/commerce-runtime-overview/">Dynamics Retail Commerce Runtime framework</a> and <a href="https://ax.help.dynamics.com/en/wiki/commerce-runtime-overview/">Dynamics AX</a>.
<h2>One pricing engine serves all retail channels</h2>
All retail channels: store POS, call center orders and store front orders share the same pricing engine. Call center is part of AX, while POS and store front utilize commerce runtime framework via retail server, so we have two pricing engine invocation patterns.
<ol>
 	<li>Retail server calls pricing services via commence runtime framework, which in turn calls pricing engine.</li>
 	<li>AX calls pricing engine directly, as AX does not understand commerce runtime.</li>
</ol>
In addition, to accommodate the difference in data access, we have <em>IPricingDataAccessor</em>. When retail server calls pricing engine via pricing services, pricing services would construct channel version of the data accessor, while AX would construct its own.

The following diagram illustrates the main flow and difference between AX and channel side implementations.

<img width="3144" height="3473" class="alignnone size-full wp-image-915" alt="pricing-engine" src="https://msdnshared.blob.core.windows.net/media/2017/04/pricing-engine.jpg" />

&nbsp;
<h2>Now onto extensibility.</h2>
Say, we are creating a new discount type.

The number in parenthesis corresponds the number in the diagram.

<img width="2836" height="2720" class="alignnone size-full wp-image-857" alt="pricing-extensibility" src="https://msdnshared.blob.core.windows.net/media/2017/03/pricing-extensibility.jpg" />
<h3>First, the schema, the form and CDX</h3>
<ol>
 	<li>[AX schema extension (5)] new table(s)</li>
 	<li>[AX form extension (6)] RetailPeriodicDiscount form extension</li>
 	<li>[Channel database schema (8)] new table(s), view(s) and/or stored procedure(s).</li>
 	<li>[CDX extension (7)] <a href="https://ax.help.dynamics.com/en/wiki/add-customer-preference-data-to-a-channel-database/">manual setup</a>.</li>
</ol>
<h3>Secondly, core pricing engine</h3>
<ol>
 	<li>[pricing engine extension (1)] Create a new discount type.</li>
 	<li>[pricing engine extension (1)] <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-register-a-new-discount-type/">Register the discount type in retail discount store</a> that loads discount data from database and instantiates discount types</li>
 	<li>[pricing data accessor interface extension (3)]</li>
</ol>
<h3>Thirdly, channel stack</h3>
<ol>
 	<li>[pricing services extension (2)] Register the retail discount store extension at the top of commerce runtime pricing services extension. Instantiate the extension data accessor and pass it to the pricing engine.</li>
 	<li>[pricing data accessor channel extension (4)] Derive from <em>PricingDataServiceManager</em> and implement the extension interface.</li>
</ol>
<h3>Lastly, the AX stack</h3>
<ol>
 	<li>[pricing data accessor AX extension (9)] derive from <em>RetailPricingDataManager</em> and <em>RetailPricingDataManagerSimulator</em></li>
 	<li>[AX pricing proxy (10)] Equivalent to channel side commerce runtime pricing services extension: register the retail discount store extension. Instantiate the extension AX data accessor and pass it to the pricing engine.</li>
</ol>
We will go over details in coming posts.

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-overall-approach/">Dynamics Retail Discount Extensibility Overall Approach</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-three-discount-categories/">Dynamics Retail Discount Extensibility – Three Discount Categories</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/02/17/dynamics-retail-discount-extensibility-register-a-new-discount-type/">Dynamics Retail Discount Extensibility – Register a New Discount Type</a>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics Retail Discount Sample – Rounding</title>
      <link>https://docs.microsoft.com/archive/blogs/retaillife/dynamics-retail-discount-sample-rounding</link>
      <pubDate>Tue, 04 Apr 2017 12:09:52 GMT</pubDate>
      <dc:creator><![CDATA[Zhiyi Zhang]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/retaillife/?p=896</guid>
      <description><![CDATA[Rounding is hard.

Sometimes, we do not have a choice. Let’s get down to...]]></description>
      <content:encoded><![CDATA[<a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/04/retail-discount-rounding-i/">Rounding</a> is <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/04/dynamics-retail-discount-tribe-rounding/">hard</a>.

Sometimes, we do not have a choice. Let’s get down to code.

<strong><em>PriceContext.HoldTogetherForDisocuntRounding = false</em></strong>

Example first: $10 distributed to quantity 7, down to penny,
<ol>
 	<li>Smallest amount: $0.01</li>
 	<li>Unit amount not-rounded is $1.42857143 (10 / 7)</li>
 	<li>Take the floor: $1.42</li>
 	<li>Remainder: $0.06 ($10 – 7 * $1.42)</li>
 	<li>Remainder / smallest amount: 6 ($0.06/0.01)</li>
 	<li>Split the sales line with quantity 7 into two:</li>
 	<li>Quantity 1 (7 – 6) with discount amount $1.42</li>
 	<li>Quantity 6 with discount amount $1.43 ($1.42 + $0.01)</li>
</ol>
Sample code
<pre>
     decimal smallestAmount = PriceContextHelper.GetSmallestNonNegativeAmount(
         priceContext,
         Math.Min(discountAmount, price));
     if (smallestAmount &gt; decimal.Zero &amp;&amp; quantityToApply &gt; decimal.Zero)
     {
         int totalDiscountAmountInSmallestAmount = (int)(discountAmount / smallestAmount);
         int averageDiscountAmountRoundingDownInSmallestAmount =
             totalDiscountAmountInSmallestAmount / (int)quantityToApply;
         decimal quantityForHigherDiscountAmount = totalDiscountAmountInSmallestAmount -
                                   (averageDiscountAmountRoundingDownInSmallestAmount * (int)quantityToApply);
         decimal quantityForLowerDiscountAmount = quantityToApply - quantityForHigherDiscountAmount;
         decimal unitDiscountAmountForLowerDiscountAmount =
                 averageDiscountAmountRoundingDownInSmallestAmount * smallestAmount;

         if (quantityForHigherDiscountAmount &gt; decimal.Zero)
         {             
             DiscountLine discountLine = this.NewDiscountLine(
                 appliedDiscountApplication.DiscountApplication.DiscountCode,
                 discountableItemGroups[itemIndex].ItemId);
             discountLine.DiscountAmount = unitDiscountAmountForLowerDiscountAmount + smallestAmount;
             appliedDiscountApplication.AddDiscountLine(
                 itemGroupIndex,
                 new DiscountLineQuantity(discountLine, quantityForHigherDiscountAmount));
         }

         if (quantityForLowerDiscountAmount &gt; decimal.Zero
             &amp;&amp; unitDiscountAmountForLowerDiscountAmount &gt; decimal.Zero)
         {
             DiscountLine discountLine = this.NewDiscountLine(
                 appliedDiscountApplication.DiscountApplication.DiscountCode,
                 discountableItemGroups[itemIndex].ItemId);
             discountLine.DiscountAmount = unitDiscountAmountForLowerDiscountAmount;
             appliedDiscountApplication.AddDiscountLine(
                 itemGroupIndex,
                 new DiscountLineQuantity(discountLine, quantityForLowerDiscountAmount));
         }
     }
</pre>
<strong><em>PriceContext.HoldTogetherForDisocuntRounding = true</em></strong>

First, Dynamics retail discount engine may group multiple sales lines together into one <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/29/dynamics-retail-discount-concepts-discountable-item-group/">discountable item group</a>, and in discount engine, we deal with <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/29/dynamics-retail-discount-concepts-discountable-item-group/">discountable item group</a>, not sales line directly. This complicates rounding a bit.

Again example: $0.99 distributed to quantity 2, down to penny. We have one discountable item group with quantity 2.

Case 1: one sales line with quantity 2: all $0.99 goes to the sales line.

Case 2: two sales lines, each with quantity 1: $0.50 goes to one, while $0.49 to the other.

As we said earlier, in the discount engine, we do not deal with sales line directly. On the face, it would violate the design. No worry, we have covered it in discount totaling - side note, one more reason why we cannot get rid of discount totaling – all you have to do (when coding a new discount type) is to make sure the flag requiresFurtherRounding is true when creating a new DiscountLineQuantity.

Sample code
<pre>
    AppliedDiscountApplication appliedDiscountApplication;
    discountLine.DiscountAmount = totalAmount / quantity; 
    appliedDiscountApplication.AddDiscountLine(
        itemGroupIndex,
        new DiscountLineQuantity(discountLine, quantity, requiresFurtherRounding: true));
</pre>
Lastly, for scale products with non-integer quantity, we hold as well, i.e. no split.

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/04/retail-discount-rounding-i/">Retail Discount: Rounding I</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/04/04/dynamics-retail-discount-tribe-rounding/">Dynamics Retail Discount Tribe – Rounding</a>

<strong><em>Related</em></strong>: <a href="https://blogs.msdn.microsoft.com/retaillife/2017/01/29/dynamics-retail-discount-concepts-discountable-item-group/">Dynamics Retail Discount Concept: Discountable Item Group</a>]]></content:encoded>
    </item>
  </channel>
</rss>