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.
NOTE: The latest stable version 2.0 FINALLY SUPPORTS WooCommerce's shipping zones.
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:
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:
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€.
=> For more examples, please see our Shipping Rules Examples page!
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 |
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 methods and rules | ||
Arbitrary number of conditions | ||
Number of different country zones | unlimited | unlimited |
Comparisons as conditions | ||
Shipping costs as fixed amount | ||
NoShipping identifier to prevent shipping for certain conditions; Print warning message for the user if desired |
||
Shipment modifier (ExtraShippingCharge and ExtraShippingModifier): define overall charges that are added to the matching rule |
||
Variable substitutions in the rule name strings | ||
Allow comments in the rules (to document a rule with no effect to the user) | ||
Show error/warning/notice messages | ||
Shipping costs described by mathematical formulas, involving all cart properties |
||
Mathematical expressions as conditions | ||
OR operator and "in" operator (for lists) in conditions | ||
~ operator to compare beginning of variables | ||
Functions like not, round, floor, ceil, max, min, day, weekday, etc. | ||
Custom functions can be provided by third-party extensions | ||
Available Variables: | ||
Amount, prices before/after tax/discounts | ||
Weight | ||
Postal code (ZIP) First characters of ZIP (ZIP1, ZIP2, ZIP3, ZIP4, ZIP5, ZIP6) Country and State name variables |
||
Support for alphanumeric postal codes (UK, Canada, Netherlands) | ||
max/min/total volume/height/length/width/volume of the products |
||
Coupon code | ||
Custom variable definitions for arbitrary expressions | ||
Custom variables can be provided by third-party extensions | ||
Support for Third-party Plugins: | ||
Support for "WC Vendors" and "Dokan Marketplace" plugin:
|
||
Support for "WooThemes Product Vendors" plugin:
|
Installation and Configuration
After installation (like any WordPress extension, using WordPress' plugin manager), you need to enable the plugin.
This will also enable the link to the Settings and also to the documentation.
Configuration of the Shipping methods (Plugin versions up to 1.2.x and legacy-mode in plugin version 2.0+ (unreleased))
Next, you need to create a shipping method in the "Shipping by Rules" shipping:
- Go to "WooCommerce" -> "Settings" -> "Shipping" -> "Shipping by Rules":
- Click on "Add Shipping by Rules method" to create a new rule.
- 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.:
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.
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, Subtotal |
Product and Tax totals/subtotals |
Weight |
Total weight of the order Minimal / maximal weights of the all articles in the order |
ZIP, Postcode ZIP1, 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, 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). |
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 |
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 |
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_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) |
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). |
Categories |
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). |
Vendors |
List of the vendor SLUGs (for "WooThemes Product Vendors") / logins (for "WC Vendors" and "Dokan Marketplace"), 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) |
Year, Year2, Month, Day, Hour, Hour12, AmPm, Minute, Second, Decisecond, Centisecond, Millisecond, Weekday | 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 or Definition |
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.
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)".
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:
Message="Thursday orders have tripple shipping costs"; Weekday==4; ExtraShippingMultiplier=3
Warning="Shipping to Vienna might take longer!"; Error="Please contact us for delivery details!";1000<=ZIP<=1999; Shipping=1
Debug="Fall-Through ZIP condition"; Shipping=15
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)
Operator | Description | In 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.
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:
Name=Free shipping above 100€; Amount>100; Shipping=0
Message="<pre>All variables: {Values_Debug}</pre>"; Value=0
Shipping=3*Weight
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:
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:
- 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 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(list1, list2, ...), join(list1, list2, ...) | Join two lists to one (union and join behave identical) | 2 or more |
complement(list1, list2, ...) | Return list of all elements of list1 that are NOT in any of the other lists | 2 or more |
intersection(list1, list2, ...) | Return list of all elements that are in all lists | 2 or more |
issubset(childlist, parentlist) |
Returns whether childlist is a subset of parentlist (notice the different order of arguments for issubset and contains) | exactly 2 LISTS |
contains_any(list, elem1, 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(value, n) | Returns the n-th digit (or character) of value | exactly 2 |
substring(string, begin, len) | Returns the substring of string of length len that starts at position begin | exactly 3 |
evaluate_for_categories(EXPRESSION, categorySLUG, ...) |
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.
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, categorySLUG, ...)
evaluate_for_subcategories(EXPRESSION, categorySLUG, ...)
evaluate_for_products(EXPRESSION, sku1, sku2, ...)
evaluate_for_skus(EXPRESSION, sku1, sku2, ...)
evaluate_for_vendors(EXPRESSION, vendor1, vendor2, ...)
evaluate_for_shippingclasses(EXPRESSION, shippingclass1, shippingclass2, ...)
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:
Name=Only articles in categories books and pencils cost 5€ shipping, all others are free; Shipping=5*evaluate_for_categories(Articles, "books", "pencils")
Name=Weight of all articles except product SKU1; Weight-evaluate_for_products(Weight, "SKU1")<50; Shipping=50
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
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
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=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
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=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.
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.
- WC Vendors, Product Vendors, Dokan Marketplace: Variables Vendors, VendorNames and VendorIDs; Function evaluate_for_vendors function (see above)
- Wholesale Market: Wholesale customers are identified by their userrole "ced_cwsm_wholesale_user". For example:
Name="Wholesale customer"; "ced_cwsm_wholesale_user" in userroles; shipping=987
Name="Non-wholesale customer"; Shipping=123
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:
array (
'functionname1' => 'function-to-be-called', // to call op-level functions
'functionname2' => array($classobject, 'memberfunc')), // to call lass members
...
);
"opentools_shipping_by_rules_get_cart_values"
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:
- &$cartvalues .... Associative array containing all variables. To add, modify or remove a variable, simply modify this array.
- $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.
Demo Server
Examples
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.
Version History
2018-02-24: Version 2.0.6 (Added function evaluate_for_shippingclasses)
2018-02-24: Version 2.0.5 (Support for Dokan Marketplace)
2018-01-29: Version 2.0.4 (Compat with WC vendors and product variations; PHP warnings)
2017-12-02: Version 2.0.3 (PHP7 compat; use product dimensions only when actually set)
2017-09-02: Version 2.0.2 (Support for WC Product Vendors 2.0; Make UK postcodes more forgiving; WP compat update)
2017-05-29: Version 2.0.1 (Add missing files in SVN)
2017-05-27: Version 2.0 (Support for Shipping zones (legacy mode provided, too); compatibility with WC 3.0; Fix non-latin SLUGs)
2017-03-04: Version 1.2.8 (Fix issues with UK variables before an address was entered)
2016-12-17: Version 1.2.7 (Add debug messages to the update system)
2016-12-08: Version 1.2.6 (Add message functionality)
2016-08-28: Version 1.2.5 (Add variables username, first_name, last_name, email, userroles and userID; fix issue with debug_values)
2016-08-08: Version 1.2.4 (Fix incompatibility with cash-on-delivery)
2016-07-12: Version 1.2.3 (Fix several PHP errors; fix entering update credentials)
2016-06-29: Version 1.2.2 (Fix Coupons variable; Fix warning about function signature mismatch)
2016-03-13: Version 1.2.1 (Fix warning when rule line contains only spaces)
2016-02-22: Version 1.2 (Support for third-party vendor plugins)
2016-01-16: Version 1.1 (Added time variables, MaxQuantity/MinQuantity, new function evaluate_for_subcategories)
2016-01-12: Version 1.0 (Initial release, all planned features implemented)