Copied to clipboard

Flag this post as spam?

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


  • Tito 235 posts 416 karma points
    Dec 16, 2015 @ 15:38
    Tito
    0

    How to implement this type of Discounts?

    I can see the marketing section in Merchello where i can create coupons. But i would need to create a descount without coupon code. I would like to apply this constraints:

    • By category/collection or some content related on Umbraco
    • By provider
    • By product
    • By variant

    I think i could use coupons system with empty codes. But i would need to create new constraints, how could i do it?

  • Tito 235 posts 416 karma points
    Dec 17, 2015 @ 13:17
    Tito
    0

    I need to store a price on Merchello but show another price calculated on the fly (it depends of if is customer, discounts...) Whats the best method?

    I have made an extension method on ProductDisplay that i have to call before showing the product and when adding it to basket:

    public static class ProductDisplayExtensions
        {
            public static void CheckPrices(this ProductDisplay model)
            {
                decimal precio = model.Price;
                //Normal user
                precio = precio * (decimal)1.25;
                //Registered
                var customerContext = new CustomerContext(UmbracoContext.Current);
                if (!customerContext.CurrentCustomer.IsAnonymous)
                {
                    model.OnSale = true;
                    model.SalePrice = model.Price * (decimal)1.20;
                }
                model.Price = precio;
                return;
            }
        }
    

    This works ok but when preparing the invoice it shows the original price. What is the best method?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 17, 2015 @ 18:00
    Rusty Swayne
    0

    There are a number of ways to do this, but I would suggest making adding a price modifier similar to the way we do tax included in product price. You should able to use the same base class - ProductVariantDataModifierTaskBase

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/DataModifiers/Product/ProductVariantDataModifierTaskBase.cs

    You would add your task to the merchello.config in the MerchelloHelperProductDataModifiers chain

          <taskChain alias="MerchelloHelperProductDataModifiers">
        <tasks>
            <task type="Merchello.Web.DataModifiers.Product.IncludeTaxInProductPriceDataModifierTask, Merchello.Web" />
        </tasks>
      </taskChain>
    
  • Tito 235 posts 416 karma points
    Dec 18, 2015 @ 09:35
    Tito
    0

    @Rusty i have added this class:

    namespace Merchello.iCreativos.DataModifiers.Product
    {
        using Merchello.Web.DataModifiers.Product;
        using System;
        using System.Runtime.InteropServices;
    
        using Merchello.Core;
        using Merchello.Core.Models;
        using Umbraco.Web;
        using Merchello.Web;
    
    
        using Umbraco.Core;
    
        /// <summary>
        /// The include tax in product price data modifier task.
        /// </summary>
        public class ProductPriceDataModifierTask : ProductVariantDataModifierTaskBase
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="IncludeTaxInProductPriceDataModifierTask"/> class.
            /// </summary>
            /// <param name="merchelloContext">
            /// The merchello context.
            /// </param>
            public ProductPriceDataModifierTask(IMerchelloContext merchelloContext)
                : base(merchelloContext)
            {
            }
    
            /// <summary>
            /// Attempts to modify the product pricing with tax.
            /// </summary>
            /// <param name="value">
            /// The value.
            /// </param>
            /// <returns>
            /// The <see cref="Attempt"/>.
            /// </returns>
            public override Attempt<IProductVariantDataModifierData> PerformTask(IProductVariantDataModifierData value)
            {
                decimal precio = value.Price;
                //Margen
                precio = precio * (decimal)1.25;
                //Compruebo si hay que hacerle descuento
                var customerContext = new CustomerContext(UmbracoContext.Current);
                if (!customerContext.CurrentCustomer.IsAnonymous)
                {
                    //Si es taller
                    value.OnSale = true;
                    value.SalePrice = value.Price * (decimal)1.20;
                }
                value.Price = precio;
                //var taxationContent = MerchelloContext.Gateways.Taxation;
                //if (!taxationContent.ProductPricingEnabled || !value.Taxable) return Attempt<IProductVariantDataModifierData>.Succeed(value);
    
                //var result = taxationContent.CalculateTaxesForProduct(value);
                //value.AlterProduct(result);
                return Attempt<IProductVariantDataModifierData>.Succeed(value);
            }
        }
    }
    

    Note that i can not use value.AlterProduct because it is protected.

    And in merchello.config:

    <taskChain alias="MerchelloHelperProductDataModifiers">
            <tasks>
                <task type="Merchello.Web.DataModifiers.Product.IncludeTaxInProductPriceDataModifierTask, Merchello.Web" />
                <task type="Merchello.iCreativos.DataModifiers.Product.ProductPriceDataModifierTask, Merchello.Web" />
            </tasks>
          </taskChain>
    

    The thing is that my method is not called (i have a breakpoint on that) and the price is not showed on the product page. If i delete the task in merchello.config everything is fine. Why is not showing the price if my code is not called?

  • Tito 235 posts 416 karma points
    Dec 18, 2015 @ 10:14
    Tito
    0

    Even when only one line of code:

    public override Attempt<IProductVariantDataModifierData> PerformTask(IProductVariantDataModifierData value)
            {
                return Attempt<IProductVariantDataModifierData>.Succeed(value);
            }
    

    It is not executing and price is not showing. Im guessing is launching some error im not getting...

  • Tito 235 posts 416 karma points
    Dec 18, 2015 @ 11:56
    Tito
    0

    Definitely it is something related to this line:

    <task type="Merchello.iCreativos.DataModifiers.Product.ProductPriceDataModifierTask, Merchello.Web" />
    

    Merchello is failing to add this task, and that is the reason the price is not showing.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 18, 2015 @ 16:21
    Rusty Swayne
    0

    @Tito

    Unless you recompiled Merchello, your task would not be in the Merchello.Web.dll.

    You need to reference your project dll ...

     <task type="Merchello.iCreativos.DataModifiers.Product.ProductPriceDataModifierTask, [YOUR PROJECT]" />
    

    Also, the data modifiers are executed in two places.

    1) The MerchellHelper

     // This will execute the data modifier chain
     var merchello = new MerchelloHelper();
    
     // This will NOT execute the data modifier chain
     var merchello = new MerchelloHelper(false);
    

    2) The CustomerItemCacheBase which the basket and sales preparation uses.

    Checkout the comments in the Bazaar controller here:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Bazaar/Controllers/BazaarBasketController.cs#L93

    It is important to be aware when you are executing the chain or your values could be modified more than once.

  • Tito 235 posts 416 karma points
    Dec 20, 2015 @ 08:32
    Tito
    0

    My code was in Merchello.iCreativos namespace, in my App_Code. Does it need to be inside Merchello.Web? Do i need to change Merchello base code? I would not like to do so, because of reusing.

    Im going to detail what i need to achieve in order for a better implementation. When importing all products into merchello, they all have a base price. (The price stored in Merchello). When showing / buying a product/variant i have to modify the product price due to this restrictions:

    • All users have a price= base price*1.25
    • Registered users marked as profesionals have a price = base price * 1.20.
    • This factors (1.25 or 120) will depend of the main category of the product.
    • In a future this could be used to manage discounts (as a discount by category or other umbraco related content)

    So i guess it need to be recalculated each time the user logs in/out because it depends on current user. I think the taxesmodifier task will need to be ran after this pricemodifier task.

    @Rusty you said there are different ways of do this, i would like to know them to choose the better. For future versions, it would be good to do this as Umbraco events, like having a PreProductBuilder for once the price and sale attributes are added to productdisplay you could modifie them and afterwards apply the taxes modifier. So doing this, would apply to the basket and invoice creation.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 21, 2015 @ 19:05
    Rusty Swayne
    0

    I think you can use _Code instead of the dll name if your class is in AppCode. That's from my memory so if it does not work exactly look it up - I know it's close. You should not need to touch Merchello code to do this =)

    The data modifiers are executed in the order listed in the merchello.config - so put your modifier before the tax modifier.

    Also, you should not need to do anything when an invoice is created if you are using the data modifiers. What happens is:

    1. Product is displayed to user via MerchelloHelper with modified data.
    2. Product is added to the Basket, data modifiers modify the data internally and serialize the modified data in Extended Data collection for the respective product line item.
    3. At checkout, the validation chain verifies the data (including modification) from item in Extended Data
    4. Invoice generation simply creates Invoice Line Items from Item Cache Line Items (basket items) without going back to the actual product data.
  • Tito 235 posts 416 karma points
    Dec 22, 2015 @ 11:29
    Tito
    0

    Thanks Rusty, now it works:

    <task type="Merchello.iCreativos.DataModifiers.Product.ProductPriceDataModifierTask, App_Code" />
    
  • Tito 235 posts 416 karma points
    Dec 22, 2015 @ 15:42
    Tito
    0

    @Rusty i would like to access the Umbraco content Id asociated to the product inside the modifier. This is because my price modifier factor depends on data related to the product (ie. categories). I could do this getting the sku of the product, but i guess i cant not get it from IProductVariantDataModifierData object passed to the PerformTask method. Is there some way?

  • Tito 235 posts 416 karma points
    Dec 28, 2015 @ 09:20
    Tito
    0

    If there is no way of getting product sku or access the productdisplay from the modifier, i have seen in another post there is an "AddItem" method in the Basket that accepts the "price".

    So i can use it like this:

    //Here i use this extension method to modify de Price and SalePrice depending on brand, category, etc
    product.CheckPrices();
    var updatedPrice = product.OnSale ? product.SalePrice : product.Price;
    this.Basket.AddItem(product.Name, product.Sku,  model.Quantity, updatedPrice, extendedData);
    

    This seem to work as i can see on the basket page. But on the checkout i get a null shipment from the PackageBasket method, why?

    var shipment = Basket.PackageBasket(shippingAddress).FirstOrDefault();
    

    By the way, this is the post where i have seen this method in use:

    https://our.umbraco.org/projects/collaboration/merchello/merchello//69645-tiered-pricing

    In that post, he adds this to the extended data (may be this is the reason of the error):

    var masterVariant = product.GetProductVariantForPurchase();
    extendedData.AddProductVariantValues(masterVariant);
    

    But i cant find "GetProductVariantForPurchase" method anymore. Whats the purpose of adding masterVariant to the extendedData collection?

  • Tito 235 posts 416 karma points
    Dec 28, 2015 @ 09:52
    Tito
    0

    I have managed to get the masterVariant, the code is like this:

    var masterVariant = merchello.Query.Product.GetProductVariantBySku(product.Sku);
    masterVariant.CheckPrices();
    extendedData.AddProductVariantValues(masterVariant);
    //var masterVariant = product.GetProductVariantForPurchase();
    //extendedData.AddProductVariantValues(masterVariant);
    
    var updatedPrice = masterVariant.OnSale ? masterVariant.SalePrice : masterVariant.Price;
    this.Basket.AddItem(product.Name, product.Sku,  model.Quantity, updatedPrice, extendedData);
    

    Now i can get the shipment, but as soon as i call "PackageBasket" method all my prices get overrided :(

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 28, 2015 @ 19:02
    Rusty Swayne
    0

    before adding to the basket, try

    this.Basket.EnableDataModifiers = false;

  • Tito 235 posts 416 karma points
    Dec 29, 2015 @ 11:59
    Tito
    0

    Thanks @Rusty, but that didnt work...

    I think the problem is not in the "PackageBasket". It is in the line:

    viewModel.SaleSummary = _salePreparationSummaryFactory.Value.Build(basket.SalePreparation());
    

    of the CreateCheckout method of the ViewModelFactory class of Bazaar.

    As i see this basket.SalePreparation() is an extension method. It actually calls:

    BasketSalePreparation.GetBasketCheckoutPreparation(merchelloContext, basket);
    

    I think it is there where it overrides the price set in the Basket with the AddItem method.

    Please, i need help with this as i believe it is the only thing its needed to complete my customer requirements and launch the site

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 29, 2015 @ 19:43
    Rusty Swayne
    0

    Hmm - Another thought is it may be the validation that is catching the price.

    Try setting the merchLineItemAllowsValidation to false in the extended data for the line item or removing the validation task altogether from the merchello.config ....

              <tasks>
            <task type="Merchello.Web.Validation.Tasks.ValidateProductsExistTask, Merchello.Web" />
            <!--
                The following task is intended to assert that pricing and/or on sale value has not changed in the back office since the
                customer has placed an item into their basket or wish list. If you have made custom pricing modifications in your
                implementation, you may either remove this task or adjust your code to add a new extended data value
                merchLineItemAllowsValidation = false
                to the line item so that it is skipped in the validation process.
            -->
            <task type="Merchello.Web.Validation.Tasks.ValidateProductPriceTask, Merchello.Web" />
            <!--
                Validates that products are still in inventory
            -->
            <task type="Merchello.Web.Validation.Tasks.ValidateProductInventoryTask, Merchello.Web" />
        </tasks>
    

    You test before doing any additional code by removing the

      <task type="Merchello.Web.Validation.Tasks.ValidateProductPriceTask, Merchello.Web" />
    

    line.

  • Tito 235 posts 416 karma points
    Dec 30, 2015 @ 10:48
    Tito
    0

    Thanks @Rusty, commenting that line in the merchello.config now it works!

    I dont understand why i have to add this data to the extendeddata on basket controller. Could you tell me the purpose of this?

    And i dont know if it is well done, as i have copied the code from another (old version) post. This is my final code:

        if (model.OptionChoices != null && model.OptionChoices.Any())
                    {
                        // NEW in 1.9.1
                        // ProductDisplay and ProductVariantDisplay classes can be added directly to the Basket
                        // so you don't have to query the service.
                        var variant = product.GetProductVariantDisplayWithAttributes(model.OptionChoices);
    
    //Here i change the prices
                        variant.CheckPrices();
    
                        //https://our.umbraco.org/projects/collaboration/merchello/merchello//69645-tiered-pricing
                        extendedData.AddProductVariantValues(variant);
                        List<string> attributes = variant.Attributes.Select(x => x.Name).ToList();
                        extendedData.SetValue("productAttributes", JsonConvert.SerializeObject(attributes));
    
                        var updatedPrice = variant.OnSale ? variant.SalePrice : variant.Price;
                        this.Basket.AddItem(variant.Name, variant.Sku, model.Cantidad, updatedPrice, extendedData);
                        //this.Basket.AddItem(variant, variant.Name,  model.Cantidad, extendedData);
                    }
                    else
                    {
                        //https://our.umbraco.org/projects/collaboration/merchello/merchello//69645-tiered-pricing
                        var masterVariant = merchello.Query.Product.GetProductVariantBySku(product.Sku);
    
    //Here i change the prices
                        masterVariant.CheckPrices();
    
                        extendedData.AddProductVariantValues(masterVariant);
                        //var masterVariant = product.GetProductVariantForPurchase();
                        //extendedData.AddProductVariantValues(masterVariant);
    
                        var updatedPrice = masterVariant.OnSale ? masterVariant.SalePrice : masterVariant.Price;
                        this.Basket.AddItem(product.Name, product.Sku,  model.Cantidad, updatedPrice, extendedData);
                        //this.Basket.AddItem(product, product.Name, model.Cantidad, extendedData);
                    }
    
  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 30, 2015 @ 16:44
    Rusty Swayne
    0

    Hey Tito,

    If you remove the validation line there is no need to add the additional key to your extended data collection. The key itself is used by the validation process to determine if the line item should be considered for validation - so it's sort of an either or.

    The validation is there to protect the site from say a customer adding a product that is on sale to a saved basket or wishlist and then returning a week later when the item is no longer on sale. At that point, the prices are adjusted to the price in the back office so that the store does not sell something on sale when the sale is over.

  • Tito 235 posts 416 karma points
    Dec 31, 2015 @ 09:37
    Tito
    0

    OK i understand. Disabling the validation, i can add the item the old way. Here is my code now:

    if (model.OptionChoices != null && model.OptionChoices.Any())
                {
                    var variant = product.GetProductVariantDisplayWithAttributes(model.OptionChoices);
                    variant.CheckPrecios();
                    this.Basket.AddItem(variant, variant.Name,  model.Cantidad, extendedData);
                }
                else
                {
                    product.CheckPrecios();
                    this.Basket.AddItem(product, product.Name, model.Cantidad, extendedData);
                }
    

    What i dont understand is that i updated the prices in the mastervariant that i added to the extendeddata. Why validation got the old prices? dont validation look for the values on the extendeddata for checking? I dont like disabling validation, may be i could write my own validation?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 31, 2015 @ 15:15
    Rusty Swayne
    0

    Hey Tito, I would have to step through it to see exactly where things are getting caught up, but writing your own validation is going to be pretty easy.

    Validation is another task based chain, so you can write as many tasks as you need (or remove as you've seen) via the Merchello.config file.

    Here is the product pricing task for reference.

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Validation/Tasks/ValidateProductPriceTask.cs

    Happy New Year!

  • Tito 235 posts 416 karma points
    Jan 07, 2016 @ 18:29
    Tito
    0

    Hi @Rusty,

    I have added my custom validation class and added to the task chain. After a while, i have seen that the validator is the same, what i need to change is the ProductPricingValidationVisitor class. The code changed is as follows:

    public void Visit(ILineItem lineItem)
            {
                if (lineItem.LineItemType != LineItemType.Product || !lineItem.AllowsValidation()) return;
    
                if (!lineItem.ExtendedData.DefinesProductVariant()) return;
    
                var item = _merchello.Query.Product.GetProductVariantByKey(lineItem.ExtendedData.GetProductVariantKey());
    
                //Here i change the prices to the variant
                item.CheckPrices();
    
                if (item.OnSale != lineItem.ExtendedData.GetOnSaleValue())
                {
                    _invalidPrices.Add(lineItem, item);
                    return;
                }
    
                if ((item.OnSale && (item.SalePrice != lineItem.Price)) || (!item.OnSale && (item.Price != lineItem.Price)))
                {
                    _invalidPrices.Add(lineItem, item);
                    return;
                }
    
                // Check if there have been any changes to the product through the service
                if (lineItem.ExtendedData.GetVersionKey() == item.VersionKey) return;
    
                // on sale
                if (item.OnSale != lineItem.ExtendedData.GetOnSaleValue())
                {
                    _invalidPrices.Add(lineItem, item);
                }
            }
    
  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Jan 07, 2016 @ 21:10
    Rusty Swayne
    0

    @Tito,

    I would just write my own visitor and use it in your validation task. If your not familiar with the Visitor pattern, there is a good walk through on PluralSite

  • Tito 235 posts 416 karma points
    Jan 08, 2016 @ 09:14
    Tito
    0

    Thanks @Rusty, thats what i did and works OK. I created my own ValidateProductPriceTask and ProductPricingValidationVisitor classes. My ValidateProductPriceTask class is equal to the Merchello one but it calls my ProductPricingValidationVisitor custom class. This ProductPricingValidationVisitor only has one modification (as showed on upper code), it calls my "changing prices method" before checking changes.

  • Shaun 248 posts 475 karma points
    Dec 01, 2016 @ 10:43
    Shaun
    0

    Did anyone answer Tito's original query?

    He mentioned he wanted to generate discounts

    By category/collection or some content related on Umbraco By provider By product By variant

    The idea being, if you have 100 t shirts, you don't want to have to tick them all individually to include them in the offer.

    Is there a way of selecting an entire category/subcategory of products and have the offer apply to them all?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Dec 01, 2016 @ 16:28
    Rusty Swayne
    1

    Hi Shaun,

    You can manually add a discount (not a coupon) like https://our.umbraco.org/projects/collaboration/merchello/merchello/76447-auto-apply-marketing-coupons-discounts#comment-258767

    However, what you're really looking for is a new coupon (offer) constraint based on product collection. Offer constraints are resolved so they can be created in external projects without having to alter the Merchello code base.

    You create a class, inheriting from one of the OfferConstraintBase classes ... like CouponConstraintBase and include the OfferComponentAttribute.

    I think this would be an excellent add to the Merchello base install - collections were added to Merchello after the discounts so we've just not thought to go back and create this one ... but it makes a lot of sense to have it.

    If you make a feature request in the issue tracker (http://issues.merchello.com) I'll get it tasked.

  • Shaun 248 posts 475 karma points
    Dec 06, 2016 @ 14:15
    Shaun
    0

    Thanks Rusty! I have added this via the issue tracker as you suggested.

    I agree, I think it would be very useful functionality.

    All the best

    Shaun

  • Salomons 9 posts 79 karma points
    Jul 13, 2018 @ 07:27
    Salomons
    0

    Good day,

    I successfully implemented my own ProductVariantDataModifierTaskBase class to handle customer based prices.

    I notice that the PerformTask is reached once for each product, I assume that the values are being cached, because the PerformTask function is not called any more, only when I restart application or save some products. In that way other users getting the same prices displayed. This is not the desired situation.

    Is there some way to disable the cache for the datamodifiers?

    Many tanks in advance.

  • Puck Holshuijsen 90 posts 352 karma points
    Jun 12, 2019 @ 11:28
    Puck Holshuijsen
    0

    Hi Salomons,

    I'm having this exact issue right now. Did you find the solution to this problem?

    Thanks!

    //Puck

  • Salomons 9 posts 79 karma points
    Aug 05, 2019 @ 14:32
    Salomons
    0

    Hello Puck,

    Sorry for the late reply, been on holliday. Yes, i've managed to solve it, but had to adjust te merchello Core, just disabled the cache at some point.

    If youre still strugling for a sollution, i can look up where it's done.

    Also see this post: https://our.umbraco.com/packages/collaboration/merchello/merchello/92884-customer-price-by-productvariantdatamodifiertaskbase-is-being-cached

  • Puck Holshuijsen 90 posts 352 karma points
    Aug 07, 2019 @ 13:21
    Puck Holshuijsen
    0

    Hi Salomons,

    Thanks for the reply, but I already solved it!

Please Sign in or register to post replies

Write your reply to:

Draft