Introduction

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 costs (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, but in general theis shipment plugin is not intended for per-product shipping costs.

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:

Enter a title for the method ("Austrian Post - Paket" in this example). This method name will be shown in the Cart and the invoice.

For each method, you can add any number of rule sets. Click on "Add a new ruleset" to dynamically insert a new ruleset. Each ruleset can be applied to different 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 ruleset, checks whether the country matches and then checks each of the rules until a rule matches. If no rules matches, the next ruleset is investigated.

If a rule is encountered where all conditions are fulfilled, that rule will be used and the remaining rules and rulesets will be ignored.

As the order of the rule sets is relevant, you can reorder them by simply dragging them with the mouse.

After you have written all your rules, don't forget to click "Update" to save the changes.

Configuration of the Shipping methods with Shipping Zones (Plugin version 2.0+ (unreleased))

Starting with version 2.0 of the plugin, shipping zones (introduced in WooCommerce 2.6) are fully supported. To create a shipping method using the Shipping by Rules plugins, follow these steps:

Go to the shipping zones configuration (by default, "Rest of the World" exists, covering all countries):

Select the shipping zone to which you want to add your new Shipping by Rules-based method:

Click on "Add shipping method" and select "Shipping by Rules" as the underlying shipping method:

After clicking the blue "Add shipping method" button, the configuration screen for your new shipping method will appear:

Enter a title for the method ("Shipping by Rules Method" in this example). This method name will be shown in the Cart and the invoice.

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 ruleset, checks whether the country matches and then checks each of the rules until a rule matches. If no rules matches, the next ruleset is investigated.

If a rule is encountered where all conditions are fulfilled, that rule will be used and the remaining rules and rulesets will be ignored.

After you have written all your rules, don't forget to click "Save Changes"

To get a short explanation of the rules syntax, click on the "Help on rules syntax" header to open/close the section providing help and links to the full plugin documentation.

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.

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.:

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.

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:

Cost, Amount, AmountWithTax

The order total amount, including taxes (all three variables are idential)

Total, SubtotalTaxTotal, TaxSubtotal

Product and Tax totals/subtotals

WeightMinWeight, MaxWeight

Total weight of the orderMinimal / maximal weights of the all articles in the order

ZIP, PostcodeZIP1, ZIP2, ZIP3, ZIP4, ZIP5, ZIP6

Postal code of the order (ZIP and Postcode are identical)First 1, 2, 3, 4, 5 and 6 characters of the postal code

Country, State, CountryIDCity

Country and State codesCity 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).

username, first_name, last_name, email

Username, first and last name and email address of the customer

Products

Number of different products in the order

Articles

Total number of articles (each prduct counted with the according quantity) in the order

MinQuantity, MaxQuantity

minimum/maximum number of units purchased for all the articles

VolumeMinVolume, 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, MaxLengthMinWidth, MaxWidthMinHeight, 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_OutwardUK_AreaUK_DistrictUK_SubdistrictUK_Inward

UK postal code support (see below) (ADVANCED version only)

Canada_FSACanada_AreaCanada_UrbanCanada_SubareaCanada_LDU

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).

CategoriesTagsShippingClasses

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 category are purchased. (ADVANCED version only).

VendorsVendorNamesVendorIDs

List of the vendor SLUGs (for "WooThemes Product Vendors") / logins (for "WC Vendors"), of human-readable vendor names, and of vendor IDs 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. (ADVANCED version only).

UserRoles

List of all user roles of the customer (for logged-in customers only). The user roles are identified by their SLUG, not by their display name! (ADVANCED version only)

Date/time when the shipping cost was calculated (which is usually before the order is submitted). Also notice that the reults might be cached and not be re-calculated during each step of the order! (New in version 6.0)Weekday is a value from 1(Monday) to 7 (Sunday).

Values_Debug

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 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.

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 orDefinition

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=...)

Message, Notice, Warning, Error

Display the given message as an error/warning, notice or normal message, displayed typically at the top of the page with red (error and warning), blue (notice) and green (message) background

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.

Showing Notices, Warnings and Error Messages

The plugin provides a way to show warnings, notices or error messages if a particular rule is included in the shipping cost calculation. There are the special variables Error, Warning, Notice and Message available that will show their corresponding values as a message if all conditions of the rule match. Error and Warning will be shown in red, Message in blue and notice in blue:

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:

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:

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:

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.

Latest Plugin versions

With the advent of the warnings/messages feature in version 1.2.6 one can include debug output in the form of notification and warning messages anywhere in the ruleset. Together with the {Values_Debug} variable, which prints out a long list of all available variables, one can easily check all variables and see why a condition does not apply. For example:

Normally, the Message will only be printed out for matching rules (i.e. rules which define the shipping cost that will be applied), but the Value=0 causes the plugin t treat the rule like a variable definition, which means that the message will be printed out, but the evaluation of the rules will not stop and further rules will be applied.

Old Plugin versions (<=1.2.5)

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.

If you only want to know the value of one variable, simply include that one variable in the name as {Variablename}.

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:

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:

Function calls

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.

Available functions

The following functions are available (case-insensitive):

Function

Description

Nr of arguments

not(val)

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(val)floor(val)ceil(val)

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 unitvalRound down to multiples of unitvalRound up to multiples of unitval

Scoping functions (see below for more information): Evaluate EXPRESSION restricted to only products of the given categories/skus or vendors. evaluate_for_subcategories evaluates the expression to all products that are in any subcategory of the given categories.evaluate_for_vendors takes either vendor SLUGs/login names as strings or numeric IDs.

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.

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. As a special case, evaluate_for_subcategories evaluates EXPRESSION for all products that are in either the given categories or in any subcategory of the given categories.

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:

The evaluate_for_vendors function can take either vendor SLUGs / login IDs as strings or numeric user IDs.

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

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

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)

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.

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)

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:

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.

Compatibility with other WooCommerce Plugins

Currently, we are not aware of any incompatibilities with other plugins. However, we tailored / designed our plugin specifically to support certain other WooCommerce plugins. If a third-party plugin is not listed here, it does not mean that there are problems.

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:

"opentools_shipping_by_rules_replacements"

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:

$cart ... Full information about the order. DO NOT evaluate values from this object, use $products instead!

$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)

$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.

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 versions of the plugin with no time restriction.