Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


These support forums are now closed for new topics and comments.
Please head on over to http://eureka.ucommerce.net/ for support.

  • marcelh 171 posts 471 karma points
    May 03, 2014 @ 11:00
    marcelh
    0

    Two-dimensional variant pricing and base pricing

    Based on an earlier UCommerce project I want to propose this for another customer. This customer however has an interesting princing model that I'm trying to get covered in UCommerce, but I need some out-of-box thinking.

    A) The customer sells material that is priced per m2. The material comes in various fabrics each having a price per m2. Where it gets interesting is that this m2 price is degreessive and depends on the m2 ordered. This leads to, from my perspective, a two dimensional priced variant - or a variant with a pricing table.

    An example: Material A comes in 3 fabrics:

    • Fabric 6: priced at 7 up to 10m2, 5 up to 20m2 and 4 for over 20m2
    • Fabric 7: priced at 8 up to 10m2, 7 up to 20m2 and 6 for over 20m2
    • Fabric 8: priced at 9 up to 10m2, 8 up to 20m2 and 5 for over 20m2

    My initial thought was customizing the IPricingService or a PipelineTask alternatively, and based on the m2's select the correct price, but how would I store the degressive prices on a variant?

    1. I could (mis)use the PriceGroups for this, but then these PriceGroups would also pop-up on other products that are sold and have nothing to do with these fabrics.
    2. I've also been thinking about using Marketing Foundation, but in the real world, the degressive prices aren't linear or a percentage of, but fixed prices instead.
    3. Last, I could use a dedicated table for this in the database (prices don't change that often), but I'd rather not go this way.

    Any thoughts on this or options I'm missing?

    B) Now suppose the customer selected a material and fabric, and entered the m2. The product has some configurable options. I've done the apple-style configurable product before, thats not the problem. But the price calculation for these options are based on the m2 ordered and start with an offset.

    An example:

    Material A with fabric 7 is selected, and the options X, Y and Z apply. These options have the following base  prices:

    • Option X: base price for the first 8 m2 is 9, every m2 more 3 per m2
    • Option Y: base price for the first 5 m2 is 6, every m2 more 1 per m2
    • Option Z: base price for the first 9 m2 is 4, every m2 more 2 per m2

    My thoughts were on creating a OffsetPricedProduct type, set the offset price on the product and create a field for the additional per m2 price, or have that in a variant alternatively. Then a customized IPricingService or PipelineTask could do the pricing magic.

    What would be the rationale for choosing the IPricingService and choosing a PipelineTask for calculating the orderline totals?

  • Martin 181 posts 740 karma points
    May 03, 2014 @ 13:19
    Martin
    0

    Hi marcelh,

    That is a lot of complex logic and a big chance it's more complex than it seems, never the less :-) 

    If we take a look at "problem A". Depending on the number of products you're going to have in the shop I think option 2 is a fair solution. The biggest issue I see is that if you have a lot of products it might affect how fast the basket pipeline will run due to a lot of "rules" need to be tested to see if they satisfy. In option one you're mention that you could create a pricegroup for each treshold but I think that can be problematic. If your users are going to maintain prices directly in uCommerce it could be a big problem to find out which pricegroup are for this threshold (could be) and as I understand you there is a big chance there might be plenty of thresholds (depending on different products?).

    I have used custom tables for prices on one project and it works fine.

    You're not menting which version of uCommerce you're using but I assume it's a newer version. If it was me I would try to look into creating a custom data type list which supports a currency, a kind of threshold and a amount. I think I would use a pricegroup for base price. And if that's possible I would create a custom pricingservice or a pipelinetask (or perhaps both, and some unit tests).

    So that are some of my thoughts. I know I haven't answered to all but one step at time :)

    Best regards
    Martin 

     

     

  • marcelh 171 posts 471 karma points
    May 04, 2014 @ 11:56
    marcelh
    0

    Hi Martin et al,

    Thanks for you swift response and sharing your thoughts! It's a new to be developed quotation and ordering system, running on Umbraco 7.1.1 with the latest UCommerce edition.

    Fortunately, the logic does not get more complex than this. What I'm struggling most with, is where to store the relevant prices. I am convinced that UCommerce has enough possibilities to solve the price calculation problem. Marketing Foundation would not be my first thought for this particular problem. The main reason for not looking at MF too much for this, is that I suggested UCommerce to be able solve this problem and the professional edition is currently above budget for my customer. Although it might be for a next release, I want to prove that UCommerce is able to solve the problem first and when additional requirements are added for a next release that require MF or other Pro features that's the moment to upgrade the license for this project. Multicurrency is not a requirement at the moment, neither are multilanguange, or facets.

    As for the tresholds in problem A, all tresholds are the same. Based on 10m2, 20m2 or over 20m2 for about 4 to 6 fabric variants. So that's basically 3 additional pricegroups (if pricegroups were the way to go). When pricegroups would be configurable per product this would suit me fine but unfortunately they are not, thus for all other products than "material a, b or c" these pricegroups will be shown.
    Another direction that crossed my mind this morning is to add a non-editable variant property that indicates the m2 threshold, but configuring these variants will become quite cumbersome.

    Interesting thought about the custom datatype list, do have some references for this or sample code to be shared?

    Somewhere (can't find the thread right now) I read a reply from Soren that refered to this two-dimensional pricing, it stated that multiple prices per variant are configurable. Curious at the direction he was pointing in ;-)

    As for the rationale for choosing between IPricingService or a PipelineTask, I guess I figured that out. When pricing logic needs to be evaluated when browsing the catalog, customizing IPricingService would be the way to go. When orderline totals need to be calculated based on additional information (such as m2) the CalculateOrderLineTotals should be customized. As these products are highly configurable and each configuration results in one order, the latter will do. Am I right?

  • Martin 181 posts 740 karma points
    May 12, 2014 @ 17:31
    Martin
    0

    Hi marcel,

    Sorry for a "bit" late answer. I don't have any article at my hand to show you but I will try to find one or two. I know that uCommerce is capable of using Umbraco data types so you can try to install uComponents and use one of their types (if any suits your needs).

    About your thoughts when to use either IPricingService or Pipeline seems fair. I think I have the same usage (So far I have actually only used Pipeline for setting prices).

    I will try to look for some documentation about creating custom data types.

    Best regards
    Martin 

  • Søren Spelling Lund 1797 posts 2786 karma points
    May 21, 2014 @ 10:53
    Søren Spelling Lund
    0

    Hi Marcel,

    I believe the simplest solution would be to add three new fields, which would hold the price for each level. If the level is always 10, 20, and 30 this is a pragmatic approach which wouldn't require a lot of extra work.

    You could use the fields in a pipeline task to override the default provided by IPricingService.

  • marcelh 171 posts 471 karma points
    May 29, 2014 @ 18:14
    marcelh
    1

    Hi Soren and Martin,

    Completely missed your replies, but thanks for your input. Just wanted to let you (and others) know which approach I took in this, so here we go ;-)

    The casus is a sailmaker selling sails, and sails come with configurable options. For some options (like a reef, or radial patches) the price is dependent on the total sail area, other options (lika a vision window) are just fixed priced.

    A) I went for the pricegroup option. For the material pricing I created 3 pricegroups, DEALER-USD, DEALER-USD-10, DEALER-USD-20. Now the -10 and -20 are not applicable for all products but for some (sails and options) they are. This gives me the additional feature that for customer prices, I would just create 3 more CUSTOMER pricegroups, and separate the products in dealer and customer catalogs, each having the appropriate pricegroups set. The default pricegroup for a dealer or customer is set when one logs in.

    B) Initially the user story for options required to have adjustable option pricing, starting with a base, or offset, price followed by a rate per m2 sail area. This user story was dropped and the configurable options that are dependend on the sail area have just a price per m2 which makes live much easier ;-)

    For sails and configurable options I added a product field for "is area dependent" and a non editable field "area" on which the total sail area is set before a customized pricingservices calculates the price.

    The magic for A and B is done with a customized SailPricingService that basically sorts out the correct pricegroup and price per m2 and an additional CalculateSailAreaTotalsTask in the basket pipeline that calculates the total price for the sail area.I could do without the pricingservice, but this gives me the ability to show the user the correct price per m2 for a given sail area.

    For anyone interested, here's the code. Any feedback both on solution and code is most welcome! Especially when there are any drawbacks on using pricegroups for this.

    public class SailPricingService : PricingService
        {
            public override Money GetProductPrice(Product product, PriceGroup priceGroup)
            {
                //if the product is not area dependent use the default pricing service
                if (product.IsAreaDependent() == false)
                    return base.GetProductPrice(product, priceGroup);

                //if there is no area specified use the default pricing service
                if (product["Area"] == null)
                    return base.GetProductPrice(product, priceGroup);

                //get the sail area set before the pricing service was called
                decimal area = 0;
                Decimal.TryParse(product["Area"].Value, out area);

                var areaPriceGroup = priceGroup;

                //go through each configured pricegroup that starts with same name as the current price group
                //and get the pricegroup for the set sail area
                if (product.GetPriceGroups().Count() > 1)
                {
                    string pattern = priceGroup.Name + @"-(\d{2})m2$";
                    foreach (var group in product.GetPriceGroupsByArea(pattern).OrderByDescending(d => d.Key))
                    {
                        if (area > group.Key)
                        {
                            areaPriceGroup = group.Value;
                            break;
                        }
                    }
                }
               
                //use the default pricing service with the found pricegroup
                return base.GetProductPrice(product, areaPriceGroup);
            }
        }

     

    public class CalculateSailAreaTotalsTask : IPipelineTask<PurchaseOrder>
        {
            private ICatalogContext CatalogContext { get; set; }
            private IPricingService PricingService { get; set; }
            private IRepository<Product> ProductRepository { get; set; }
           
            public CalculateSailAreaTotalsTask(ICatalogContext catalogContext, IPricingService pricingService, IRepository<Product> productRepository)
            {
                this.CatalogContext = catalogContext;
                this.PricingService = pricingService;
                this.ProductRepository = productRepository;
            }

            public PipelineExecutionResult Execute(PurchaseOrder order)
            {
                foreach (var line in order.OrderLines)
                {
                    var product = ProductRepository.SingleOrDefault(p => p.Sku == line.Sku && p.VariantSku == line.VariantSku);
                   
                    //if the product is not area dependent, there's nothing to do
                    if (product.IsAreaDependent() == false)
                        continue;

                    decimal area = order.GetSailArea();

                    //if the area property is available on this product,
                    //set the sail area which is used by the pricingservice to figure out the correct pricegroup per m2
                    if (product["Area"] != null)
                        product["Area"].SetValue(area);

                    //let the pricing service figure out the correct price based on the current pricegroup
                    var price = PricingService.GetProductPrice(product, CatalogContext.CurrentPriceGroup);

                    //if a price was found with a pricegroup, set the price
                    if (price != null)
                        line.Price = price.Value * area;                   
                    else
                        line.Price = 0;
                }

                return PipelineExecutionResult.Success;
            }
        }
Please Sign in or register to post replies

Write your reply to:

Draft