NOP 4.0 Tier price - Grouped product - Lower price when CLient buy any quantity from associated product?

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
6 years ago
Hi we have scenerio like this:

Grouped product + 3 Variants
1 Variant
2 Variant
3 Variant

We want make tier prices, where Client have been lower price when he buy any 3 pcs from this variants. So can take 1 pcs from all 3 variants and he should have lower price... is this possible? Can someone help use to configure that?

So if he buy 2 x Variant 2 + 1 pcs Variant 1 He should have lower price for all 3 items. Is it possible?
6 years ago
How you added Tier price to Grouped Product?
There is no available tier price for grouped product.
6 years ago
Yes. I add to "single product" associated in grouped product.

I just wondering is possbile to "connect" tier prices from few variants? like example on top.
5 years ago
If you are able to code,it's simple to implement this.
My thought is to overwrite GetFinalPrice in PriceCalculationService in a plugin project.To check if current product has parent product id or not, if it has,then change the quantity to the total of all variants.

As this method has been called after all items have been saved to cart,so it's no problem.

Hope you are able to code,this is something I worked out for you.

Service file like this:

using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Services.Catalog;
using Nop.Services.Catalog.Cache;
using Nop.Services.Customers;
using Nop.Services.Discounts;
using Nop.Services.Orders;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nop.Plugin.Product.GroupedRules.Services
{
    public partial class GroupedProductPriceCalculationService : PriceCalculationService
    {
        private readonly IStoreContext _storeContext;
        private readonly CatalogSettings _catalogSettings;
        private readonly IStaticCacheManager _cacheManager;
        private readonly IWorkContext _workContext;
        public GroupedProductPriceCalculationService(IWorkContext workContext, IStoreContext storeContext, IDiscountService discountService, ICategoryService categoryService, IManufacturerService manufacturerService, IProductAttributeParser productAttributeParser, IProductService productService, IStaticCacheManager cacheManager, ShoppingCartSettings shoppingCartSettings, CatalogSettings catalogSettings) : base(workContext, storeContext, discountService, categoryService, manufacturerService, productAttributeParser, productService, cacheManager, shoppingCartSettings, catalogSettings)
        {
            this._storeContext = storeContext;
            this._catalogSettings = catalogSettings;
            this._cacheManager = cacheManager;
            this._workContext = workContext;
        }

        

        /// <summary>
        /// Gets the final price
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="customer">The customer</param>
        /// <param name="overriddenProductPrice">Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations</param>
        /// <param name="additionalCharge">Additional charge</param>
        /// <param name="includeDiscounts">A value indicating whether include discounts or not for final price computation</param>
        /// <param name="quantity">Shopping cart item quantity</param>
        /// <param name="rentalStartDate">Rental period start date (for rental products)</param>
        /// <param name="rentalEndDate">Rental period end date (for rental products)</param>
        /// <param name="discountAmount">Applied discount amount</param>
        /// <param name="appliedDiscounts">Applied discounts</param>
        /// <returns>Final price</returns>
        public override decimal GetFinalPrice(Nop.Core.Domain.Catalog.Product product,
            Customer customer,
            decimal? overriddenProductPrice,
            decimal additionalCharge,
            bool includeDiscounts,
            int quantity,
            DateTime? rentalStartDate,
            DateTime? rentalEndDate,
            out decimal discountAmount,
            out List<DiscountForCaching> appliedDiscounts)
        {
            if (product == null)
                throw new ArgumentNullException(nameof(product));

            discountAmount = decimal.Zero;
            appliedDiscounts = new List<DiscountForCaching>();

            var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
                product.Id,
                overriddenProductPrice.HasValue ? overriddenProductPrice.Value.ToString(CultureInfo.InvariantCulture) : null,
                additionalCharge.ToString(CultureInfo.InvariantCulture),
                includeDiscounts,
                quantity,
                string.Join(",", customer.GetCustomerRoleIds()),
                _storeContext.CurrentStore.Id);
             var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;
            //we do not cache price for rental products
            //otherwise, it can cause memory leaks (to store all possible date period combinations)
            if (product.IsRental)
                cacheTime = 0;

            //this only create unitprice,as subtotal also call this method to generate amount.
            if (product.ParentGroupedProductId > 0)
            {
                quantity = 0;
                var cart = _workContext.CurrentCustomer.ShoppingCartItems
                .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart)
                .LimitPerStore(_storeContext.CurrentStore.Id)
                .ToList();

                var sampParentProductCart = cart.Where(c => c.Product.ParentGroupedProductId == product.ParentGroupedProductId).ToList();
                foreach(var cartSameParent in sampParentProductCart)
                {
                    quantity += cartSameParent.Quantity;
                }
            }

            

            var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
            {
                var result = new ProductPriceForCaching();

                //initial price
                var price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;

                //tier prices
                var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);
                if (tierPrice != null)
                    price = tierPrice.Price;

                //additional charge
                price = price + additionalCharge;

                //rental products
                if (product.IsRental)
                    if (rentalStartDate.HasValue && rentalEndDate.HasValue)
                        price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);

                if (includeDiscounts)
                {
                    //discount
                    var tmpDiscountAmount = GetDiscountAmount(product, customer, price, out List<DiscountForCaching> tmpAppliedDiscounts);
                    price = price - tmpDiscountAmount;

                    if (tmpAppliedDiscounts != null)
                    {
                        result.AppliedDiscounts = tmpAppliedDiscounts;
                        result.AppliedDiscountAmount = tmpDiscountAmount;
                    }
                }

                if (price < decimal.Zero)
                    price = decimal.Zero;

                result.Price = price;
                return result;
            });

            if (includeDiscounts)
            {
                if (cachedPrice.AppliedDiscounts.Any())
                {
                    appliedDiscounts.AddRange(cachedPrice.AppliedDiscounts);
                    discountAmount = cachedPrice.AppliedDiscountAmount;
                }
            }

            return cachedPrice.Price;
        }


    }
}

Then set DependencyRegistrar:

using Autofac;
using Nop.Core.Configuration;
using Nop.Core.Infrastructure;
using Nop.Core.Infrastructure.DependencyManagement;
using Nop.Plugin.Product.GroupedRules.Services;
using Nop.Services.Catalog;

namespace Nop.Plugin.Product.GroupedRules.Infrastructure
{


    public class DependencyRegistrar : IDependencyRegistrar
    {


        public int Order
        {
            get { return int.MaxValue; }
        }

        public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
        {
            

            builder.RegisterType<GroupedProductPriceCalculationService>().As<IPriceCalculationService>().InstancePerLifetimeScope();

            //throw new NotImplementedException();
        }
    }
}
5 years ago
Willian wrote:
If you are able to code,it's simple to implement this.
My thought is to overwrite GetFinalPrice in PriceCalculationService in a plugin project.To check if current product has parent product id or not, if it has,then change the quantity to the total of all variants.

As this method has been called after all items have been saved to cart,so it's no problem.

Hope you are able to code,this is something I worked out for you.
....
    }
}



Hi! i'm not programmer:) but i show this to my "support" :) Thx a lot!
5 years ago
Hope you work for you perfectly.
5 years ago
Zyje_sobie wrote:
If you are able to code,it's simple to implement this.
My thought is to overwrite GetFinalPrice in PriceCalculationService in a plugin project.To check if current product has parent product id or not, if it has,then change the quantity to the total of all variants.

As this method has been called after all items have been saved to cart,so it's no problem.

Hope you are able to code,this is something I worked out for you.
....
    }
}


Hi! i'm not programmer:) but i show this to my "support" :) Thx a lot!


I tested this code today, and I found this would cause a bug in product detail page.
If current customer still have this product in cart,then the price in product details page would be the same price as in cart. This definitely wrong. Hope you could see this.
5 years ago
I just change another part of code in the same file. in  this method,a shoppingCartItem is in it,so we know this method is called by shoppingcart, and next level should be product level that both cart and product details page call it. So I made this changes.

I tested in both cart(unit price and subtotal etc) and product detail page,it works well. But I am not sure whether other places have some problems.

public override decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
            bool includeDiscounts,
            out decimal discountAmount,
            out List<DiscountForCaching> appliedDiscounts)
        {
            if (shoppingCartItem == null)
                throw new ArgumentNullException(nameof(shoppingCartItem));
            int quantity = shoppingCartItem.Quantity;
            if (shoppingCartItem.Product.ParentGroupedProductId > 0)
            {
                quantity = 0;
                var cart = _workContext.CurrentCustomer.ShoppingCartItems
                .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart)
                .LimitPerStore(_storeContext.CurrentStore.Id)
                .ToList();

                var sampParentProductCart = cart.Where(c => c.Product.ParentGroupedProductId == shoppingCartItem.Product.ParentGroupedProductId).ToList();
                foreach (var cartSameParent in sampParentProductCart)
                {
                    quantity += cartSameParent.Quantity;
                }
            }


            return GetUnitPrice(shoppingCartItem.Product,
                shoppingCartItem.Customer,
                shoppingCartItem.ShoppingCartType,
                quantity,
                shoppingCartItem.AttributesXml,
                shoppingCartItem.CustomerEnteredPrice,
                shoppingCartItem.RentalStartDateUtc,
                shoppingCartItem.RentalEndDateUtc,
                includeDiscounts,
                out discountAmount,
                out appliedDiscounts);
        }
5 years ago
btw,
this property by default should be false. if this one is true, the quantity of variant should back to it's original quantity.

update [dbo].[Setting] set Value='False' where Name='shoppingcartsettings.grouptierpricesfordistinctshoppingcartitems'
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.