VM2 Shipping by Rules Plugin - Shipping costs calculated from general rules
Introduction
This is a Virtuemart 2 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:
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 | ||
| Arbitrary number of conditions | ||
| Number of different country zones | 8 | 8 |
| Comparisons as conditions | ||
| Shipping costs given before or after taxes | ||
| Shipping costs as fixed amount | ||
| NoShipping identifier to prevent shipping for certain conditions | ||
| Shipping costs described by mathematical formulas, involving all cart properties |
||
| Mathematical expressions as conditions | ||
| OR operator in conditions | ||
| ~ operator to compare beginning of variables | ||
| Available Variables: | ||
| Amount, prices before/after tax/discounts | ||
| Weight | ||
| Postal code (ZIP) First characters of ZIP: ZIP1, ZIP2, ZIP3, ZIP4, ZIP5, ZIP6 |
||
| Support for alphanumeric postal codes (UK, Canada, Netherlands) | ||
| max/min/total volume/height/length/width/volume of the products |
||
| Coupon code | ||
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"
- Click "Save" to populate the "Configuration" tab
- Go to the "Configuration" tab:
- 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 VirtueMart. 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) - Articles<5
Condition: number of articles needs to be less than five - 10<=Amount<100
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 - Shipping=3.50
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.
Available Variables
In the conditions, the following properties of the order (case-insensitive) can be used:
| Amount, AmountWithTax, salesPrice |
Invoice amount, including taxes |
amountWithoutTaxbasePricesalesPriceWithDiscount |
Various Cart prices before and after taxes and discounts |
|
Weight |
Total weight of the order Minimal / maximal weights of the all articles in the order |
| ZIP ZIP1, ZIP2, ZIP3, ZIP4, ZIP5, ZIP6 |
Postal code of the order First 1, 2, 3, 4, 5 and 6 characters of the postal code |
| Products | Number of different products in the order |
| Articles | Total number of articles (each prduct counted with the according quantity) in the order |
| Volume 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 parcell will NOT have extensions TotalLength x TotalWidth x TotalHeight! It will rather be considerably smaller!) |
| Coupon | Coupon code entered by the user (Advanced version only!) |
| UK_Outward UK_Area UK_District UK_Subdistrict UK_Inward |
UK postal code support (see below) (ADVANCED version only) |
| Canada_FSA Canada_Area Canada_Urban Canada_Subarea Canada_LDU |
Canadian postal code support (see below) (ADVANCED version only) |
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) |
| 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 |
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.
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
Available Operators (in their order of precendence)
| Operator | Description | In basic plugin |
|---|---|---|
| ^ | 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 |
Like with normal mathematical expressions, parentheses (...) can be used to achieve a different grouping of the terms than the normal precedence rules would imply.
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:
- Power ^
- Multiplication *, Division /, Modulo %
- Addition +, Subtraction -
- Comparisons: <, <=, >, >=, =>, =<, ==, !=, <>
- String startsWith: ~
- AND, &, &&
- OR
- Assignment =
To achieve a different order of evaluation, one can always add parentheses, e.g. (1+3)*4 to evaluate the addition first.
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=0However, 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 (starting with version 2.4) 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
The 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=0The Britisch 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 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
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
The 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=0Please 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.
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
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.
To make use of this feature, you need to enable VirtueMart's coupon system (AwoCoupon will not work) and create a coupon, e.g. with code "COUPON_CODE". You can then check for the coupon code in the shipping rule and make the shipping costs depend on the existence of this coupon code:
Name=Free shipping with coupon; Coupon=="COUPON_CODE"; Shipping=0This allows standard VirtueMart coupons 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.
Demo Server
Examples
Frequently Asked Questions (FAQ)
License
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 version of the plugin with no time restriction.
Version History
2013-05-07: Version 2.4.3 (Allow UK and Canadian postal codes to be entered without the space; ADVANCED version only)
2013-05-03: Version 2.4.2 (Added MinWeight and MaxWeight variables)
2013-03-31: Version 2.4 (Added support for UK and Canadian alphanumeric postal codes - ADVANCED version ONLY)
2013-03-30: Version 2.3 (Added NoShipping identifier to exclude shipping; Fixed shipping cost when Shipping= was left out)
2013-03-21: Version 2.2 (Fixed chained conditions; added length unit setting and conversion; added ZIP1, ZIP2, ... variables; added ~ operator to compare beginning of variables; fixed totalLength/Width/Height calculation; )
2013-03-18: Version 2.1.1 (Fixed shipping cost display in shipping selection page)
2013-03-18: Version 2.1 (Fixed bug that parameters were not saved sometimes; totalLength, totalWidth and totalHeight variables; support for Joomla! 1.5)
2013-02-14: Version 2.0 (OR operator, Coupon variable, fixed ZIP when no address given, all Price variables of the cart)
2013-01-22: Version 1.1 (implement Volume and minimal/maximal volume and product extensions )
2013-01-15: Version 1.0 (initial public release)
