Shipping by Rules Plugin for WooCommerce - Shipping costs calculated from general rules


This is a WoCommerce plugin that allows the shop owner to determine shipping costs according to arbitrary sets of general rules. Very complex shipping cost structures (depending on the amount, weight, number of products and/or articles and postal code of the order) can be easily implemented as one shipping method by describing each by a simple rule with conditions.

There are two flavors of the plugin: The free "Shipping by Rules" plugin and the paid "Advanced Shipping by Rules" plugin. The main difference is that in the advanced plugin, all terms can be given as mathematical expressions, while in the basic version, only comparisons of the order properties with fixed values are possible.

Shipping costs can depend on:

  • Total amount of the order (before or after tax, before or after discounts)
  • Total weight of the order
  • Number of articles or different products in the order
  • Volume or minimal and maximal extensions of the products
  • Postal code of the delivery address
  • Coupon code (advanced version)
  • SKUs of the products in the cart (advanced version)
  • Categories of the products in the cart (advanced version)

What the plugin does NOT easily provide is per-product shipping (e.g. Product A costs 5€ to ship, Product B costs 1€, and if the customer buys them both, shipping is 6€). There are workarounds in some simple cases and for smaller shops, though.

The name of the rule can also contain variables, which are replaced from the cart values. So e.g. you can have a shipping name "Domestic (Weight 3.25kg)".

The rules are described as a simple line of text with an easy structure (semicolons separate the parts of the rule). For example:

Name=Free Shipping; 100<=Amount; 0
Name=Domestic Small; Articles<5; Amount<100; Shipping=1.50 Name=Domestic Standard; Amount<100; Shipping=3.50

This set of rules describes three shipping costs: Orders of 100€ and more are free, otherwise orders with less than five articles have shipping costs of 1.5€, all others 3.50€.


As another example, consider the following shipping costs of a webshop:

  <50€ <100€ above 100€
Domestic 2,50€ for up to 3 articles
2,50€ for weight less than 1kg
5€ otherwise
6,50€ FREE
International 8,50€ 8,50€ FREE

The standard shipping plugin of Virtuemart requires you to create at least four different shipping methods (domestic <50€, domestic otherwise, international <50€, international otherwise), while the dependence on the article count cannot be implemented at all. Another drawback of using different shipping methods to model this shipping cost structure is that if the customer selects a shipping method and then modifies the quantities in the cart, the new order amount might fall into the new category, and the selected shipping method will be deselected, forcing the user to select a shipping method again.

In contrast, this plugin allows this cost structure to be described by the following rules:

For the domestic country/countries:

Name=Domestic small; Articles<=3 OR Weight<=1; Amount<50; Shipping=2.50
Name=Domestic medium; Amount<50; Shipping=5
Name=Domestic Standard; 50<=Amount<100; Shipping=6.5
Name=Free Shipping above 100€; 100<=Amount; 0

For all other countries (international):

Name=International Shipping; Amount<100; Shipping=8.50
Name=International Free Shipping; Amount>=100; 0

Differences between the standard and the advanced version

  "Shipping by Rules" "Advanced Shipping by Rules"
Arbitrary number of rules yes yes
Arbitrary number of conditions yes yes
Number of different country zones unlimited unlimited
Comparisons as conditions yes yes
Shipping costs given before or after taxes yes yes
Shipping costs as fixed amount yes yes
NoShipping identifier to prevent shipping for certain conditions; 
Print warning message for the user if desired
yes yes
Shipment modifier (ExtraShippingCharge and ExtraShippingModifier):
define overall charges that are added to the matching rule
yes yes
Variable substitutions in the rule name strings yes yes
Allow comments in the rules (to document a rule with no effect to the user) yes yes
Shipping costs described by mathematical 
formulas, involving all cart properties
no yes
Mathematical expressions as conditions no yes
OR operator and "in" operator (for lists) in conditions no yes
~ operator to compare beginning of variables no yes
Functions like not, round, floor, ceil, max, min, day, weekday, etc. no yes
Custom functions can be provided by vmshipmentrules extensions no yes
Available Variables:
Amount, prices before/after tax/discounts yes yes
Weight yes yes
Postal code (ZIP)
First characters of ZIP: ZIP1, ZIP2, ZIP3, ZIP4, ZIP5, ZIP6
Country and State name variables
yes yes
Support for alphanumeric postal codes (UK, Canada, Netherlands) no yes
max/min/total volume/height/length/width/volume
of the products
yes yes
Coupon code no yes
Custom variable definitions for arbitrary expressions no yes
Custom variables can be provided by custom-developed extensions yes yes


Installation and Configuration

After installation (like any Joomla plugin, using Joomla's extension manager), you need to create a shipping method of the type "Shipping by Rules" in VirtueMart:

  • Go to "Shop" -> "Shipment Methods" and click on "New"
  • Choose a name and select the "(Advanced) Shipping by Rules" plugin in the "Shipment Method"
    Shipping by Rules plugin configuration
  • Click "Save" to populate the "Configuration" tab
  • Go to the "Configuration" tab:
    Shipping by Rules Plugin configuration
  • There are eight sections for different country zones that can have different rules.
  • Select the countries fo the first zone (if no countries are selected, the rules apply to all countries!)
  • Enter your rules, one per line, in the "Rules"input field (see below for the exact format). Each rule consists of conditions, a shipping cost specification and optionally a name. These parts can be given in any order, separated by semicolons.
  • When calculating the shipping costs, the plugin looks at each country zone, checks whether the country matches and then checks each of the rules until a rule matches. If no rules matches, the next country zone it investigated.
  • The first matching rule will be returned as the shipping cost.

How the shipping cost is determined

When determining a possible shipping rate for a shipping method derived from this plugin, it will walk through all rules in the order they are given and check their conditions. The first rule that matches the country selection as well as all given conditions (with the current cart properties) will be returned and offered to the user by WooCommerce. If the shipping cost of the first matching rule is set to the identifier NoShipping, then no further rules are investigated the method will not offer any shipping.
If the cart properties change (e.g. a new product is added, the quantities are updated or the shipping address is changed), this is repeated.

Knowing this procedure can help you save a lot of time an effort, since once you have checked a condition in the first rule, for all further rules you can safely assume that the conditions of the first rule are not fulfilled.

VirtueMart allows only one shipping cost to be returned per shipping method. If you want to offer e.g. a standard and an express rate, you need to set up two shipping methods with this plugin.

Format of the rules

Each line of the rules field specifies one rule and consists of several rule parts, separated by a semicolon. E.g.:

Name=Domestic Small; Articles<5; 10<=Amount<100; Shipping=3.50
Name=Free shipping with Coupon; Coupon=="FREE_SHIPPING"; Shipping=0
Name=Free Shipping; 100<=Amount; 0

All rule parts with comparison operators (supported operators are: <, <=, =<, ==, !=, <>, >=, =>, >) are understood as conditions, where multiple comparisons can be chained to one longer condition.

Let us now look at the first rule of the example above. It consists of four parts, separated by semicolons:

Name=Domestic Small
Name of the rule (will be displayed in the cart and the invoice)
Condition: number of articles needs to be less than five
Condition: the total prices of the order needs to be at least 10 and smaller than 100 currency units; This is equivalent to two separate conditions: 10<=Amount; Amount<100
Specifies the shipping cost without taxes (in the default currency units). The Shipping= can be left out. This means that any part of the rule that does not contain a comparison operator or an = is understood as the shipping costs without taxes

The second rule shows how to use a string, which is indicated by simply wrapping it with quotes. The name of the rule is an exception and will always be understood as a string, without the need to be wrapped in quotes (although it can be). All identifiers that contain letters and are not wrapped in quotes, are understood as variables, all identifiers containing only of digits (and optionally a decimal point) are understood as numbers. ATTENTION: only a point is understood as a decimal point, not a comma like many European countries use.

The general format of a rule is:

Name=Name of the rule (displayed); [Comment=Comment that won't be shown]; [Condition=]Articles<5; ...; [Shipping=]3.50

None of the parts are required.

The "Condition=" can be left out if the condition contains any comparison operators(like <, >=, ==, etc.). If the condition consists only of a variable or of a function call (advanced version only), the "Condition=" is required to mark this part as a condition and not as shipping costs.

The "Shipping=" can always be left out. Rule parts without any assignment are always interpreted as shipping costs unless the part contains a comparison operator (like <, >=, ==, etc.)

Available Variables

In the conditions, the following properties of the order (case-insensitive) can be used:

Amount, AmountWithTax, salesPrice
Invoice amount, including taxes
Various Cart prices before and after taxes and discounts

MinWeight, MaxWeight

Total weight of the order
Minimal / maximal weights of the all articles in the order
Postal code of the order
First 1, 2, 3, 4, 5 and 6 characters of the postal code

Country, State, CountryID

Country and State codes
City name (as a string); CAUTION: Your customer might use different spellings!

address1, address2

Address fields of the shipping address (can be different from the billing address), as entered by the user. In particular, these might contain dummy data (like "asdfasdfasdf" or typos).
Products Number of different products in the order
Articles Total number of articles (each prduct counted with the according quantity) in the order
MinVolume, MaxVolume
total/minimal/maximal volume of the products in the order. The volume of each product is calculated as length*width*height in the given length unit

MinLength, MaxLength
MinWidth, MaxWidth
MinHeight, MaxHeight

minimal/maximal extensions of the products in the order
TotalLength, TotalWidth, TotalHeight
The length/width/height of all articles summed up (ATTENTION: The parcel will NOT have extensions TotalLength x TotalWidth x TotalHeight! It will rather be considerably smaller!).
Coupons Coupon codes entered by the user (Advanced version only!)
UK postal code support (see below) (ADVANCED version only)
Canadian postal code support (see below) (ADVANCED version only)
SKUs List of all SKUs of the products in the cart. (ADVANCED version only). This can be used to check whether a particular product is in the cart (condition: "your-sku" in SKUs).


List of the category IDs, Tags and Shippingclasses of all products in the cart. In the advanced version, which can be used to check if e.g. only products from one particular vendor are purchased.


DEBUGGING ONLY: A string representation of all available variables. Use e.g. as Name="All values: <pre>{Values_Debug}</pre>" to print all available (built-in) variables

All rule parts of the form [VARIABLE]=VALUE are assignments, with [VARIABLE] being one of

Name (optional) Name of the shipping cost rule (will be displayed in the cart and in the invoice). This can also be a translatable identifier, which will be translated if a translation is available.
Shipping Shipping cost (before taxes) if the rule matches, the tax gross shipping cost will be calculated from the selected tax rule; If set to NoShipping, the shipping method will not offer any shipping.
ShippingWithTax Shipping cost including taxes if the rule matches, the tax and the net shipping cost will be calculated from the selected tax rule
ExtraShippingCharge This rule defines an extra shipping charge that will be added to the matching rule that gives the actuall shipping costs.
ExtraShippingMultiplier This rule defines an extra multiplier for shipping costs that will be multiplied to the shipping costs of the matching rule that gives the actual shipping cost. E.g. to increase shipping by 7% under certain conditions, use ExtraShippingMultiplier=1.07 .
Comment A comment that will be completely ignored (i.e. you can use such fields to add comments in the backend that won't be shown to the customers)
Variable or
For variable definitions: The name of the variable to be defined
Value For variable definitions: The value of the variable to be defined (name of the variable given in Variable=.. or Definition=...)

For the shipping cost, Shipping= can be left out. I.e. if a rule part consists entirely of a numerical value, with no assignment or comparison operator, it is understood as shipping cost before taxes.

Shipping rule name

The (visible) name of the shipping rule is a free form string (no quotes needed, but possible) that further describes the shipping costs. The displayed shipping name will be "Shipping method name (rule name)". The string given as a rule name is translatable through the normal Joomla translation system.

To insert cart values like the total weight into the rule name, you can simply use {variablename} for any of the available variables above. E.g.

Name=Small package: {articles} articles, weight {weight} kg; Articles<3; Weight<5; Shipping=3

will display a name like "Shipping method (Small package: 2 articles, weight 3.2 kg)".

Shipping Modifiers

Sometimes under certain conditions there should be an extra shipping charge added to all shipping rates. This can be the case for oversized products or products that need special care (e.g. glass products might need extra, expensive packaging). The plugin provides two modifiers: ExtraShippingCharge and ExtraShippingMultiplicator, which are applied to the shipping costs determined by the matching rule.

In the following example if any product of category 1234 is in the order, the shipping costs of 3€ or 5€ will be increased by an extra shipping charge of 5€ to 8€ and 10€ respectively: 

Name=Orders with glass products get an extra charge; contains_any(Categories, 1234); ExtraShippingCharge=5
Name=Light package; Weight<50; Shipping=3
Name=Heavy package; Weight>=50; Shipping=5

In the following example, all orders to Alaska will get a 50% (i.e. a multiplier of 1.5) addon to the shipping costs:

Name=Alaska has 50% higher shipping costs; State2=="AK"; ExtraShippingMultiplier=1.5
Name=Light package; Weight<50; Shipping=3
Name=Heavy package; Weight>=50; Shipping=5

The multiplier does not apply to an extra charge, if both an extra charge and an extra multiplier are given. In particular, the calculation of the final shipping cost is: (Shipping * ExtraShippingMultiplier) + ExtraShippingCharge.

If a modifier is given, but none of the rules matches, the method will also not provide shipping costs.

Excluding certain conditions from shipping

Setting the shipping costs to the special identifier NoShipping can be used to disallow shipping when certain conditions are met. For example, you might want to exclude some postal codes from shipping at all (e.g. some islands). An example is:

Name=No shipping of heavy packages to a certain area; Weight>100; 8000<=ZIP<9000; NoShipping
Name=No shipping of more than 100 articles; Articles>100; Shipping=NoShipping
Name=Flat rate otherwise; Shipping=15

Starting with version 4.0 of the plugin, the name of the NoShipping rule will be displayed to the user as a warning message! If the rule does not have any name set, no warning will be printed. You can use Comment=... in the rule to add a description for yourself, which will NOT be displayed to the user:

Comment="No Warning displayed, but disallow shipping to a certain ZIP range"; ZIP>9000; NoShipping

Available Operators (in their order of precendence)

OperatorDescriptionIn basic plugin
in Check whether a certain value is contained in a list  
 ^ Power  
 *, /, % Multiplication, Division, Modulo  
+, - Addition, Subtraction  
<, <=, =< less then / less or equal X
>, >=, => larger / larger or equal X
== equal X
!=, <> NOT equal X
~ starts with (the longer term of the left or right side starts with the term on the other side)  
AND, &, && logical AND operator to join two comparisons to one condition  
OR logical OR operator to join two comparisions to one condition  
= Assignment (possible LHS variables are Shippping, ShippingWithTax and Name) X

The operator names are case-insensitive.

Like with normal mathematical expressions, parentheses (...) can be used to achieve a different grouping of the terms than the normal precedence rules would imply.

Debugging problems in the Plugin

To debug problems with the plugin, it is often useful to find out the value of a certain variable, or even the full list of all available variables. I recommend to create a new shipping method called "Debugging" without any restrictions that contains just one NoShipping rule:

Name=Here you create some debug output that will be displayed to the user while debugging; NoShipping

The advantage of this approach is that this method will never offer a shipping rate (and does not influence your existing shipping methods),  but it will always be considered and(starting with version 4.0 of the plugin) print out its name as a warning.

  1. If you only want to know the value of one variable, simply include that one variable in the name as {Variablename}.
  2. If you want to check which variables are available and see the values of all variables, use the varible "Values_Debug", which is a string representation of all pre-defined variables. You can use a rule like the following (note, though, that this does NOT work with user-defined variables, which are available only inside the shipping method they are defined).
Name=All variables: <pre>{Values_Debug}</pre>; NoShipping

This rather long list will show you all variables and their values. Finding the problem is then usually just a matter of thinking your rules through with the displayed values.

Mathematical expression in the "Advanced Shipping by Rules" Plugin

The normal "Shipping by Rules" plugin suffices for most webshops, as it allows arbitrary many shipping cost rules with fixed shipping cost for each rule and supports numeric postal codes.

However, in some cases the shipping costs need to be even more flexible and can not be expressed by fixed shipping amount, but only by arithmetic mathematical expressions. Simple cases are shipping costs of 5% of the order amount, or 10€ per kg, or 2€ shipping per additional article. More advanced rules are employed by cargo companies, where the shipping per kg gets cheaper the more you ship.

For this reason, an advanced version of the plugin is available for sale, which adds support for alphanumeric postal codes and also incorporates arbitrary basic arithmetic expressions (allowed operators are +, -, *, /, %, ^, OR, AND and parentheses) of the above variables in all terms (conditions and shipping costs). An example is the following rule, which applies to all orders of at least 2 articles below 100€ and specifies shipping costs as 5€ fixed plus 3% of the order amount plus 1€ per kg plus 0.5€ per additional article:

Name=Complex shipping function; articles>=2; amount<100; shipping=5+amount*0.03+1*weight+0.5*(articles-2)

For more examples of such advanced shipping cost calculations, see the examples below.

NOTE: When using the OR or AND operator, it is strongly recommended to use a space before and after the operator to prevent parsing errors in certain cirumstances. E.g. "1<3OR3<5" would NOT be parsed correctly, because "3OR3" is understood as one expression!

The operator precedence (which operators are evaluated first, e.g. in 1+3*4, the multiplication has a higher precedence and is first evaluated to get the correct result of 1+12=13) is:

  1. Function calls
  2. Power ^
  3. Multiplication *, Division /, Modulo %
  4. Addition +, Subtraction -
  5. Comparisons: <, <=, >, >=, =>, =<, ==, !=, <>
  6. String startsWith: ~
  7. AND, &, &&
  8. OR
  9. Assignment =

To achieve a different order of evaluation, one can always add parentheses, e.g. (1+3)*4 to evaluate the addition first.

Available functions

The following functions are available (case-insensitive):

FunctionDescriptionNr of arguments
Logical NOT of the argument (e.g. not("testsku" in SKUs) to check whether a particular product is not in the cart). 1 (logical value)
Round the argument (less than .5 rounded down, .5 or more rounded up)
Round down (decimal part cut off)
Round up (always round up to next integer)
 exactly 1
round(val, unitval)
floor(val, unitval)
ceil(val, unitval)
Round the argument to multiples of unitval
Round down to multiples of unitval
Round up to multiples of unitval
exactly 2
max(val1, val2, val3, ...)
min(val1, val2, val3, ...)
Maximum/minimum of the arguments (any number of arguments possible) 1 or more
year(), month(), 
yearday(), day(), weekday(), 
hour(), minute(), second()
Current date components as integer NONE
list(val1, val2, val3, ....) Create a list from the given arguments. The result can be used with the "in" operator, e.g. to differentiate shipping costs for different states (condition state2 in list("TX", "WS", "MS") ) 1 or more
length(list) Number of elements in the list 1 (list)
union(list1list2, ...), join(list1list2, ...) Join two lists to one (union and join behave identical) 2 or more
complement(list1list2, ...) Return list of all elements of list1 that are NOT in any of the other lists 2 or more
intersection(list1list2, ...) Return list of all elements that are in all lists 2 or more


Returns whether childlist is a subset of parentlist (notice the different order of arguments for issubset and contains) exactly 2 LISTS

contains_any(listelem1, elem2, ...)
contains_all(listelem1, elem2, ...)
contains_only(listelem1, elem2, ...)
contains_none(listelem1, elem2, ...)

Returns whether the list contains any/all/only/none of the other arguments given. First argument needs to be a list. 2 or more
digit(valuen) Returns the n-th digit (or character) of value exactly 2
substring(stringbeginlen) Returns the substring of string of length len that starts at position begin exactly 3
evaluate_for_categories(EXPRESSION, categoryID, ...)
evaluate_for_products(EXPRESSION, sku1, sku2, ...)
evaluate_for_skus(EXPRESSION, sku1, sku2, ...)
Scoping functions (see below for more information): Evaluate EXPRESSION restricted to only products of the given categories/manufacturers/vendors/skus 2 or more
print_r(value) DEBUGGING function: returns the value (e.g. a list or a numeric value) as a string exactly 1

Custom Variable Definitions

It is also possibleto define custom variables to hold e.g. complex conditions that are used in multiple rules. The format is similar to rules (i.e. one line per variable definition):

Definition=VariableName; [Value=]ValueOfTheVariable

Instead of "Definition=" one can also use "Variable=", and the "Value=" can be left out (as long as the value does not contain any comparison operators). The VariableName needs to be one string without spaces or any special characters, the value can be any expression or condition that can be used in a rule. 

Variable definitions and shipping rules can be given in any order, but they are evaluated sequentially. In particular, a variable definition will only be available in all rules that are given after the definition.

If you want to store conditions in a variable and then use that variable in a rule, you need to mark it as a condition with "Condition=YOURVARIABLE". Otherwise, the plugin would interpret a single variable as the shipping rate.

Here is an example of a variable definition:

Name="Here VAR is not available yet: {VAR}"; Weight>100; 10
Definition=VAR; Value=1000<=ZIP<2000
Name="Here VAR is available: {VAR}"; Condition=VAR; Shipping=50

Variable definitions can overwrite/modify a previous variable definition, and they can also contain conditions:

Definition=myship; Value=0
Definition=myship; 1 in Categories; Value=myship+4
Definition=myship; 2 in Categories; Value=myship+12345
Name="Shipping costs summed up"; Shipping=myship

Obtaining values for only a subset of the cart (e.g. articles or weight of one category only)

It is also possible to access values for a subset of the order, like only for a given category or a given manufacturer. This is implemented using the following functions:

evaluate_for_categories(EXPRESSION, categoryID, ...)
evaluate_for_products(EXPRESSION, sku1, sku2, ...)
evaluate_for_skus(EXPRESSION, sku1, sku2, ...)

These functions evaluate the given EXPRESSION (can be a complex mathematical expression!) for only those products that match the given category ID or sku. If multiple IDs are given, all products that match any of the ids are used. To evaluate an expression e.g. for all products in the order that are in both of two given categories, you need to stack two calls to evaluate_for_categories. For example:

Name=Only articles in categories 42 and 45 cost 5€ shipping, all others are free; Shipping=5*evaluate_for_categories(Articles, 42, 45)
Name=Weight of all articles except from manufacturer 3; Weight-evaluate_for_manufacturers(Weight, 3)<50; Shipping=50
Name=Check if product from cat 42 AND manufacturer 5 is included; evaluate_for_manufacturer(evaluate_for_categories(Articles, 42), 5)>0; Shipping=0

Please note this while this gives a lot of potential for checks and calculations, this stil does NOT provide a way to calculate per-product shipping costs, where you can easily assign each product a shipping cost based on their sku or category. As a workaround you can check for each and every SKU or category manually, but this is (1) quite lengthy and cumbersome and (2)whenever you add a new one, you'd have to update the shipping rule, too.

Supporting alphanumeric postal codes

Most countries employ numeric postal codes, which allow direct comparisons using the ZIP variable, e.g.

Name=Free shipping to Vienna (Austria); 1000<=ZIP<2000; Shipping=0

However, some countries, most notably the UK, Canada and the Netherlands use alphanumeric postal codes, which also contain letters. Their postal codes all have a certain structure, which allows the postal code to be split up into smaller parts that describe the area further. The advanced version of the plugin also supports those alphanumeric postal codes by providing several variables that contain the different parts of the postal codes and can be used in the conditions.

UK postal codes

UK PostalCode PartsThe UK postal codes have the form "A[A]0[0][A] 0AA", where parts in square brackets are options. The first part before the space is called "Outward" part of the postal code and is used to distribute letters to a post office for final distribution. The part after the space is called the "Inward" part and is used by the post office to sort the mail for final delivery. The "Outward" part begins with one or two letters, indicating the postcode area, followed by one or two digits, identifying the district within the area. Some districts in Central London have been further subdivided by an additional letter after the digit.

If the plugin detects that the postal code of the delivery address (or the invoice address if no shipping address is given) matches the form of UK postal codes, it will provide the following variables for use in the conditions:

UK_Outward The Outward part of the postal code (the two to five characters before the space)
UK_Area The postal area (one or two letter)
UK_District The postal district within the postal area (one or two digits)
UK_Subdistrict The subdivision of some central London districts (a letter, or empty)
UK_Inward The Inward part of the postal code (the three characters after the space)

Here are a few examples of rules for UK postal codes:

Name="Free shipping to Birmingham"; UK_Area=="B"; Shipping=0 
Name="Free shipping to parts of Walsall"; UK_Area=="WS" AND 15<=UK_District; Shipping=0
Name=No Shipping to PO boxes in North London; UK_Outward=="N1P"; NoShipping

The British overseas territories also use postal codes the follow the the structure of UK postal codes, except that the outward part consists of four letters and the inward part is always "1ZZ", e.g. "ASCN 1ZZ" for Ascension or "PCRN 1ZZ" for the pitcairn Islands. The only exception is Gibraltar whith a postal code of "GX11 1AA". For those postal codes, only the outward and the inward parts are assigned, while the area, district and subdistrict variables are empty.

Name="Free shipping to Gibraltar"; UK_Outward=="GX11" AND UK_Inward=="1AA"; Shipping=0 
Name="No shipping to Falklands"; UK_Outward=="FIQQ"; NoShipping


Canadian postal codes

Canadian PostalCode PartsThe Canadian postal codes have the form "A0A 0A0", where the first part is called the "Forward Sortation Area" (or FSA) and the final three characters are the "Local Delivery Unit" (LDU). The initial letter indicates the postal district (province/region), the number at the second position indicates either a particular urban area (if >0) or a rural area (if 0), and the letter at the third position subdivides the urban or rual area further into districts or smaller cities. The LDU then usually simply gives the delivery post office with no particular geographic rules or order.

If the plugin detects that the postal code of the delivery address (or the invoice address if no shipping address is given) matches the form of Canadian postal codes, it will provide the following variables for use in the conditions:

Canada_FSA The Forward Sortation Area (the three characters before the space)
Canada_Area The first letter (indication the Postal District, i.e. typically the province or region)
Canada_Urban The digit at the second position (indicating either a rural area if 0, or a particular urban area if larger than 0)
Canada_Subarea The letter at the third position (subdividing the urban or rural area even further)
Canada_LDU The Local Delivery Unit (the three characters after the space)

Here are a few examples of rules for Canadian postal codes:

Name=Free Shipping to British Columbia; Canada_Area=="V"; Shipping=0
Name=Chicoutimi (Quebec); Canada_Area=="G" AND Canada_Urban==7 AND "G"<=Canada_Subarea<="K"; Shipping=5
Name=Saint-Joseph-de-Coleraine; Canada_FSA=="G0N" AND Canada_LDU=="1B0"; Shipping=7

Please notice that in the third rule, the separate checks for FSA and LDU are better than a check for ZIP=="G0N 1B0", since it will also work if the user enters multiple spaces between the FSA and the LSU.
In the second rule the area and urban check could also be combined into a check whether the FSA starts with "G7": Canada_FSA~"G7"

Dutch postal codes

The postal code in the Netherlands has the structure "0000 AA" (i.e. four digits and then two letters), where the final two letters only divide the area described by the digits into even smaller parts (at the street/house level). For the calculation of shipping costs, the two final letters are practically never relevant, so by using the variable ZIP4 (the first four characters of the postal code), the Dutch postal codes can be used just like any numeric postal code:

Name=No shipping to Amsterdam; 1011<=ZIP4<=1109; NoShipping

Coupons for Lower Shipping Costs

The advanced version also provides the coupon code as a variable, which allows to make shipping depend on the coupon code given:

Name=Free shipping with coupon; contains_any(Coupons, "COUPON_CODE"); Shipping=0

This allows standard coupon system to influence the price as well as the shipping cost. Of course, you can use other conditions to make the coupon only apply if e.g. the order amount reaches a certain threshold, or to set a lower rater per kg, etc.

Extending the Plugin with Custom Functions and Variables using other plugins

Sometimes, shipping costs have to depend on properties (e.g. custom field values, third-party plugin settings, etc.) that are not by default provided by this plugin. The Shipping by Rules plugins can be extended by other plugins if those plugins react two certain filters and hooks.  Such extensions can add new variables, modify existing variables and add new functions (advanced version only). 

The corresponding filters are:


Let other plugins add custom functions. No arguments are provided by this filter, and it will be called when the plugin is initialized. The opentools_shipping_by_rules_replacements filter is expected to return an array of the form:

array (
  'functionname1' => 'function-to-be-called',  // to call op-level functions
  'functionname2' => array($classobject, 'memberfunc')),  // to call lass members

Let other plugins add custom variables. This filter is called whenever shipping costs are evaluated (possibly also for a subset of the order). Its parameters are:

  1. &$cartvalues .... Associative array containing all variables. To add, modify or remove a variable, simply modify this array.
  2. $cart ... Full information about the order. DO NOT evaluate values from this object, use $products instead!
  3. $products ... List of products for which the variables are evaluated. This does not have to the full list of products in the order (e.g. when evaluate_for_categories is called, the variables are only evaluated for all products in the given categories)
  4. $method ... A pointer to the shipping by rules shipping method. Normally you should not need this parameer.

The return value is ignored. You can apply all changes to the variables directly to the $cartvalues array. This allows you to remove existing variables, too.

Demo Server



This plugin is licenced unter the GNU GPLv3. The "Shipping by Rules Plugin" can be downloaded free of charge, while the "Advanced Shipping by Rules Plugin" (providing additional arithmetic expressions as described above) can only be downloaded after payment. In both cases, you will get all the rights (and duties) that the GPL gives you. You are allowed to use the plugin on as many webshops as you like. We try to give support as our time allows, but we cannot guarantee a certain response time. A payment for the Advanced version gives you access to all future versions of the plugin with no time restriction.

Version History

2015-10-20: Version 1.0 (Initial release, all features implemented)


VM - Shopping cart


Cart empty

Login Formular