How to intercept event in checkout process to check product attributes?

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
9 years ago
Hi,

I have a lease product which needs a contract and a credit check. Once the contract is in place I add a product for checkout to the shopping cart. This has been implemented. However, the customer can also circumvent the contract process and just add the product to the cart. Then I want him/ her to go to the contract page. I can subscribe to the addtocart event(), is there a way to subscribe to a checkout event so that I can redirect the customer to sign the contract before placing the order?

I am also looking how I can subcribe to a 'paid event' so that I know that I can start collecting monthly installments.
==> FOUND:

/// <summary>
        /// Process order paid status
        /// </summary>
        /// <param name="order">Order</param>
        protected virtual void ProcessOrderPaid(Order order)
        {
            if (order == null)
                throw new ArgumentNullException("order");

            //raise event
            _eventPublisher.PublishOrderPaid(order);

            //order paid email notification
            if (order.OrderTotal != decimal.Zero)
            {
                //we should not send it for free ($0 total) orders?
                //remove this "if" statement if you want to send it in this case
                _workflowMessageService.SendOrderPaidStoreOwnerNotification(order, _localizationSettings.DefaultAdminLanguageId);
            }

            //customer roles with "purchased with product" specified
            ProcessCustomerRolesWithPurchasedProductSpecified(order, true);
        }


J.
9 years ago
No such event.  The events really indicate "change to entity", and not "I'm here in the flow/GUI".  And, I don't know if it would be safe to RedirectToRoute inside an event handler.

If you're modifying the core, then (I think)  the easy way would be to put code in

\Presentation\Nop.Web\Controllers\CheckoutController.cs

            public ActionResult Index()

If you wanted to do in plugin, then that will be trickier.
9 years ago
Hi,

Thanks, I was maybe thinking of adding a partial view to the view of Checkout/ Shopping cart to do the change. That impacts the source code less since you can add it to a theme. In Microsoft Dynamics AX there is a concept with consumers by which you literally can hook in to everything.

Maybe I will try to write a custom event handler, have that consumed by my plugin. I saw in the code 'getwarnings' which returns warnings which prevent checkout (not sure). In that case consuming there and adding events will prevent checkout.

J.
9 years ago
Again I caution about Redirect in an Event Handler.  Raising an event is basically making a call to execute the handler method(s).  If you Redirect in the handler the call may not return to finish executing the remainder of the caller method.

Consider a plugin with an Action Filter.  It's OK to redirect there.
9 years ago
Quantis001 wrote:
Hi,

Thanks, I was maybe thinking of adding a partial view to the view of Checkout/ Shopping cart to do the change. That impacts the source code less since you can add it to a theme. In Microsoft Dynamics AX there is a concept with consumers by which you literally can hook in to everything.

Maybe I will try to write a custom event handler, have that consumed by my plugin. I saw in the code 'getwarnings' which returns warnings which prevent checkout (not sure). In that case consuming there and adding events will prevent checkout.

J.


If I understand you correctly, you might want to use Action Filter: Overriding (Intercepting) NopCommerce Controllers And Actions
9 years ago
Hi,

Thank you. Your help is very useful and I managed to create this to detect a 'Checkout'.


using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Infrastructure;
using Nop.Plugin.Misc.QELease.Domain;
using Nop.Plugin.Misc.QELease.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;


namespace Nop.Plugin.Misc.QELease.ActionFilters
{
    public class CheckoutActionFilterAttribute : ActionFilterAttribute, IFilterProvider
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext == null || filterContext.HttpContext == null)
                return;

            HttpRequestBase request = filterContext.HttpContext.Request;
            if (request == null)
                return;

            string actionName = filterContext.ActionDescriptor.ActionName;
            if (String.IsNullOrEmpty(actionName))
                return;

            string controllerName = filterContext.Controller.ToString();
            if (String.IsNullOrEmpty(controllerName))
                return;

            //don't apply filter to child methods
            if (filterContext.IsChildAction)
                return;

            if (!DataSettingsHelper.DatabaseIsInstalled())
                return;

            var leaseSettings = EngineContext.Current.Resolve<QELeaseSettings>();
            if (!leaseSettings.Enabled)
                return;

            if (controllerName.Equals("Nop.Web.Controllers.CheckoutController", StringComparison.InvariantCultureIgnoreCase) &&
                actionName.Equals("Index", StringComparison.InvariantCultureIgnoreCase))
            {
                var customer = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer;

                if (customer == null)
                    throw new ArgumentNullException("Customer is null.");

                LeaseContract leaseContract = EngineContext.Current.Resolve<ILeaseContractService>().GetLatestRatingByCustomerId(customer.Id);

                if (leaseContract == null || leaseContract.ContractStatus == ContractStatus.New)
                {
                    var subscribeUrl = new UrlHelper(filterContext.RequestContext).RouteUrl("Plugin.Misc.QELease.Subscribe");
                    filterContext.Result = new RedirectResult(subscribeUrl);
                }
            }
        }

        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            //WRONG: string controllerName = controllerContext.ToString();
            string controllerName = controllerContext.Controller.ToString();
            if (!string.IsNullOrEmpty(controllerName) &&
                controllerName.Equals("Nop.Web.Controllers.CheckoutController", StringComparison.InvariantCultureIgnoreCase) &&
                actionDescriptor.ActionName.Equals("Index", StringComparison.InvariantCultureIgnoreCase))
            {
                return new List<Filter>() { new Filter(this, FilterScope.Action, 0) };
            }

            return new List<Filter>();
        }
    }
}



In my plugin I register:
   builder.RegisterType<CheckoutActionFilterAttribute>()
                .As<IFilterProvider>();


Is it not a lot comparison on controller name by the way?

J.
9 years ago
Hi,
Works great! Thanks. Now the logic to detect specific products in the shopping cart. They are in a table in the database and a comparison with the shopping cart items will do. Some (additional) caching would be great since a lot of extra data is requested for a checkout.
J.
9 years ago
Hi,
For some reason I do not get an updated version of my leasecontract. Can it be that the ActionFilter is memorizing data? I have put the service in the constructor to have the working context, etc.
J.
9 years ago
Quantis001 wrote:
Hi,
For some reason I do not get an updated version of my leasecontract. Can it be that the ActionFilter is memorizing data? I have put the service in the constructor to have the working context, etc.
J.


You cannot use constructor injection. Reason is Action Filter is only instantiated once (not per HTTP request). So you need to get a new instance of the services you need in every request by using the following inside the method itself (and not using constructor injection):

EngineContext.Current.Resolve<>
9 years ago
Hi,
Thank you. I just removed that because I also believe that would be the issue.
J.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.