Connect pricelist to channel

Hi,

Is it possible to connect a pricelist to a channel somehow? The scenario is that same account can purchase order on both b2c and b2b (which are 2 different channels). The user is connected to a specific group which is in its turn connected to b2b-pricelist. so when user is logged in on b2c-channel he/she gets b2b-pricces which is incorrect. Any ideas how to solve this?

Litium version: [7.2.0]

Could you add a decorator for the price calculator method GetPriceLists that excludes this price list if it’s the b2c channel?

Untested:

using Litium.Products;
using Litium.Products.PriceCalculator;
using Litium.Runtime.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Litium.Accelerator.Services
{
	[ServiceDecorator(typeof(IPriceCalculator))]
	public class CustomPriceCalculator : IPriceCalculator
	{
		private readonly IPriceCalculator _parent;
		private readonly PriceListService _priceListService;

		public CustomPriceCalculator(IPriceCalculator parent, PriceListService priceListService)
		{
			_parent = parent;
			_priceListService = priceListService;
		}

		public IDictionary<Guid, PriceCalculatorResult> GetListPrices(PriceCalculatorArgs calculatorArgs, params PriceCalculatorItemArgs[] itemArgs)
		{
			calculatorArgs.PriceListSystemIds = GetPriceLists(calculatorArgs).Select(x => x.SystemId).ToList();

			return _parent.GetListPrices(calculatorArgs, itemArgs);
		}

		public ICollection<PriceList> GetPriceLists(PriceCalculatorArgs calculatorArgs)
		{
			// if channel is B2C, exclude B2B price list
			if (IsB2C()) {
				return _parent.GetPriceLists(calculatorArgs).Where(x => x.Id != "B2BPriceListId").ToList();
			}

			return _parent.GetPriceLists(calculatorArgs);
		}
	}
}
1 Like

That could definitly be a solution, I will test and come back! Thanks Nils!

I just tested this in 2 different ways. But GetPriceLists() never gets called? :thinking:
I first tested it by modify MVC/Routing/PriceCalculatorDecorator which comes as standard with Accelerator 7.2.0. And then I actually created a custom price calculator located in same path/namespace as in ur example, but still GetPriceLists() doesn’t get called. Any idea why?

You should see it called when opening a category for example, since it’s called directly from the price filter service.

That’s also why we call it in GetListPrices, to ensure that when calculating a price (for when looking at a product page for example) we use our subset of price lists. Otherwise, when the original calculator calls it, it will use its own implementation. Is GetListPrices not called either?

Thank you Nils!! I totaly missed this line :slight_smile:

calculatorArgs.PriceListSystemIds = GetPriceLists(calculatorArgs).Select(x => x.SystemId).ToList();

I changed it and it works as expected now.

One issue that I noticed is when adding some product to the cart from e.g. b2b channel and then visit the checkout page on b2c, the orderrows in the cart will have the prices for the b2b, I hoped they were updated “automatically”, so I need to look into that. :thinking:

Do you have the code mentioned here in your ControllerBase?

Yes, I have it.

Not sure what the issue could be. I tried in a 7.2.4 with two channels on the same domain (but different prefix), and the call to SetChannel will trigger a re-fetch of list prices which would hit your decorator.

I think I found the issue… in GetPriceLists() I use _requestModelAccessor.RequestModel?.ChannelModel?.Channel to check the current channel and when the call comes from Cart.SetChannel (see the stack trace below to see the call stack) the _requestModelAccessor.RequestModel is null which cause the issue. I think I will change to fetch the current channel from either the orderCarrier or store it in the http session from ControllerBase.

The call stack:

vid Litium.Accelerator.Mvc.Routing.PriceCalculatorDecorator.GetPriceLists(PriceCalculatorArgs calculatorArgs) i Src\\Litium.Accelerator.Mvc\\Routing\\PriceCalculatorDecorator.cs:rad 42
vid Litium.Accelerator.Mvc.Routing.PriceCalculatorDecorator.GetListPrices(PriceCalculatorArgs calculatorArgs, PriceCalculatorItemArgs[] itemArgs) i Src\\Litium.Accelerator.Mvc\\Routing\\PriceCalculatorDecorator.cs:rad 33
vid Litium.Foundation.Modules.ECommerce.Plugins.Orders.OrderRowFactory.GetProductCatalogEntities(ShoppingCartItemCarrier shoppingCartCarrier, Guid websiteId, Guid currencyId, Guid customerId, SecurityToken token, PriceCalculatorResult& listPriceResult, Guid countryId)
vid Litium.Foundation.Modules.ECommerce.Plugins.Orders.OrderRowFactory.Create(ShoppingCartItemCarrier shoppingCartRow, Guid websiteId, Guid currencyId, Guid customerId, Guid countryId, SecurityToken token)
vid Litium.Foundation.Modules.ECommerce.ShoppingCarts.Cart.SetProductUnitListPrices(OrderCarrier orderCarrier, SecurityToken token)
vid Litium.Foundation.Modules.ECommerce.ShoppingCarts.Cart.SetChannel(Channel channel, Country country, SecurityToken securityToken)
vid Litium.Accelerator.Mvc.Controllers.ControllerBase.Initialize(RequestContext requestContext) i Src\\Litium.Accelerator.Mvc\\Controllers\\ControllerBase.cs:rad 41
vid System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)\r\n   vid System.Web.Mvc.MvcHandler.<>c.<BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)
vid System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)
vid System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
vid System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
vid Litium.Web.Mvc.Routing.MvcPageHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
vid System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
vid System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
vid System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
vid System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error)
vid System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb)
vid System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)
vid System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
vid System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
vid System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus)
vid System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus)
vid System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)
vid System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)

Try using the RouteRequestLookupInfoAccessor instead, it has a property for the channel. Initialize is run prior to any filters I believe, so RequestModel won’t be set.

1 Like

Tested it with both RouteRequestLookupInfoAccessor and CartAccessor and both work fine, but I will go for RouteRequestLookupInfoAccessor feels kinda safer :smile:

Thanks a lot for ur help, Nils!

1 Like

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.