Pages - Menu

nopCommerce Discount Rules Plugin - Exclude Sale Products


Create a discount rule plugin that when a user create a discount, they have the option to add requirement whether to include sale products to the discount or not.

We are currently on the latest nopCommerce 3.3 platform, as of today.

How it works

I went through the source code of how other DiscountRules plugin works and concluded the followings:
  • The relationship between Discount and DiscountRequirement is just a one-to-many FK relationship. One Discount have many DiscountRequirements.
  • Any properties such as which customer role is required, or how much dollar value to pass the requirement rule is stored in setting.
  • The setting key is defined within the plugin and transparent from the calling controller / services.
  • In XxxDiscountRequirementRule.cs, we define CheckRequirement() that will return true / false if the requirement is fulfilled. It reads the relevant settings to check the properties such as dollar value or customer roles for passing the discount rule. 
  • The rule file must implement the IDiscountRequirementRule interface.
  • The pattern for how the settings are stored are generally in this format. string.Format("{0}", discountRequirement.Id)
  • There is an ajax call in the view that does a post back to the XxxController.Configure when saving the DiscountRequirement. 


Create the project

Following my previous post How to write a nopCommerce plugin I have created a new project called DiscountRules.SaleProduct from other DiscountRules plugin.


I added a boolean property in the RequirementModel to indicate if we want to include sale products.

public bool IncludeSales { get; set; }


The project I copied from were using DropDownList, I changed to CheckBox and modified the Ajax call that post back the property to the controller. Make sure to follow the pattern on suffixing the requirement id to the setting, as the screen can have multiple instance of the same rule.

Change the controller name in the ajax post back to match the new plugin name.


Modified the Configure method to work with the Ajax post back call. Make sure the parameter names match.


This CheckRequirement() method is the magic method that we will put our logics in checking sale items in the cart or not, and we will return true / false accordingly to exclude sale products from discount. Hopefully the customers still happy...

Depending on your implementations, the code might look like something below and could be very simple.
public bool CheckRequirement(CheckDiscountRequirementRequest request)

    foreach (var product in cart)
        if (product.SpecialPrice.GetValueOrDefault() > 0
            && product.SpecialPriceStartDateTimeUtc < DateTime.Now
            && product.SpecialPriceEndDateTimeUtc > DateTime.Now)
            return false;

    return true;


While in debug mode developing the plugin, if you found the plugin not reset properly even after you rebuild the solutions or clear cache. You could try to clear the Temporary ASP.NET Files folder. If you get access denied because it is being used, try delete the w3wp.exe from task manager. That will stop the IIS from accessing it.

nopCommerce Plugin - Reflection error about LoaderExceptions

Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.

Today trying to setup a few build configurations for my multi-tenant projects, so that I can use different web config transformation when working on different projects.

I came to an error complaining about pluginDescriptor.ReferencedAssembly reflection System.Reflection.ReflectionTypeLoadException. The error looks like this.


It is basically the reflector try to look up the dlls for reflection and throw exception as dlls are not there.

I quickly went through the build configurations in one of my plugin project Nop.Plugin.Tax.FixedRate and found the following.

The new build configuration "DebugSpeedo" that I just added to the system had a default output path to the bin folder. It looks like the only place had the proper path (that puts the dlls inside the presentation layer for nop) was the "Debug" and "Release" configuration. 

It seems to me that we have to fix up the path for any new custom build configurations that we create. I have changed the output path value for "All Configurations" which hopefully, I will not see this issue again when I create more custom build configurations down the track.


It is a similar error as to the ones when we first create custom plugins, that we need to fix the output path. Just didn't expect I had to revisit it again.

nopCommerce - Painless Upgrade from 3.1 to 3.3

We are upgrading from our current nopCommerce 3.1 platform to the latest 3.3 version. It was a fair amount of works and efforts but not bad at all. Glad that I took away the pain by utilizing MVC and n-tier techniques in earlier days.


  1. Pay attention to 
    1. Use NuGet to fix any packages.config package version issue.
    2. .net framework target
    3. Nop.Admin\sitemap.config
    4. Any RouteProvider, GenericUrlRouteProvider, DependencyRegister
    5. Themes Folder
    6. Custom keys in Web.Config
    7. Custom Controllers
  2. What I noticed
    1. BaseNopController now split into BasePublicController and BaseAdminController
    2. BaseNopPaymentController is renamed to BasePaymentController
    3. My payment plugin that implements IPaymentMethod now need to implement the SkipPaymentInfo method. This method is used by CheckoutController if payment info should be skipped. (Why don't we call it RequirePaymentInfo? ^^")
    4. CategoryNavigationModel.CategoryModel became CategorySimpleModel
    5. Follow compiler error, the followings are to be fixed
      1. Change from private to protected for methods or properties that was customized in the core.
      2. Recreate any hook
    6. For Kendo UI, replace,
      1. GridCommand -> DataSourceRequest
      2. GridModel -> DataSourceResult (and remove strongly type)
      3. Remove Grid Attribute
    7. Reinstall and fix any 7spike plugin issues. Be warned, the AjaxFilter one is problematic!!
    8. Our own custom plugins worked seamless :) Just need to update the supported version from 3.1 to 3.3, or else the plugin list won't load the plugins.
    9. Copy and replace Web.Config from other nop plugins to our custom plugins.


As you can see, there were lots of work involved but I wouldn't say it was upgrade-resistance. The Kendo UI upgrade was huge, but it is for a good sake. Telerik is just overdue to retire.

What do I need for upgrading from 3.3 to 3.4?
  • This cheat sheet
  • And, a cup of coffee.

BitCoin Hoax Email - Wrong Target Audience?

Received an hoax email about successful bit coin authorization today. Occasionally you get emails about winning lotto or inherit some money from your lost ancestors, but why bit coin?

BitCoin was certainly a hot topic in the recent years, but it is still among the more IT savvy who knows the digital world in and out. It seems a bit contradicting when an hoax email suppose to target someone less geeky?

To think out of the box, I am going to look at the email from a non-technical perspective.

  • BTC is an acronym short for Bit Coin, but not for general public.
  • Most people wouldn't click since there is no incentive like big lotto winning or money waiting for you.
  • For people who do own or use bit coin, there simply no such thing as invoice confirm. Bit coin is all about block chains.
It did however added some fun to my day while I am doing branching and merging. :)

eCommerce store review - Kmart

Kmart is a large department store that sells clothes, stationary, electronics etc in Australia.

I was trying to search for some "Wii U" stuff in Kmart and as we know, that implies the Nintendo Wii U game console stuff.

A little disappointed to see what I get back in the search result. I ended up with a bunch of U-shaped pillows with no Wii.

Technical Insight

How it works?

The site used "or" clause in their search. It means they are trying to match any products that have "Wii" or "U" in the product keywords / names / descriptions. Thus the result set is not relevant to what was intended.

What happen to the Wii? My logic is simple. They didn't have any Wii products online but only their brick and mortar store.

How would I do better?

  1. One way I can think of is to add ban word to the pillow products to disallow them showing up in a search of "Wii anything".
  2. Another way is to give the user an advance options so they can choose to use "or" or "and" for their keywords. (This one is quite commonly found as it is easier to implement.)
  3. Take out ambiguous or noise words from keyword such as "U"

SQL Server - Transaction log chewing up harddrive space


In a production server that have Recovery Model set to Full mode, it is easily noticeable the transaction log will grow to some unmanageable size in a short period of time depending on the number of activities of the database.


There are numerous ways to do it, and depends on different version of SQL Server. I found this is the easiest and worked across the board.


Do a full backup before we begin. This will create a last well known tlog backup as we are about to destroy the chain.

Ideally you want to switch your website off during this operations. Otherwise there will be no logging during this operation time. I personally think that's not a big deal, but I thought I will point it out anyway.


Change Recovery Model to Simple Mode. This is to truncate the transaction log. 


Run command to shrink the log.

exec sp_helpdb database_name

DBCC SHRINKFILE (transaction_log_name, 1)

exec sp_helpdb database_name

Change Recovery Model back to what it was - Full Mode

Backup Again

At this point, I prefer to do another full backup. It is not compulsory, but will clearly define where the new tlog chain begin.

Further Reading