diff --git a/Makefile b/Makefile index 62a4826c728fd7059d5890c974193c7f57a8af99..3289f16e67a595aa8e4db50ee4a3f2cc61094b06 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ BASE=rules_shipping BASE_ADV=rules_shipping_advanced PLUGINTYPE=vmshipment ZIPBASE=opentools_vm -VERSION=5.99 +VERSION=5.99.1 PLUGINFILES=$(BASE).php $(BASE)_base.php $(BASE)_framework_joomla.php $(BASE).script.php $(BASE).xml index.html PLUGINFILES_ADV=$(BASE_ADV).php $(BASE)_base.php $(BASE)_framework_joomla.php $(BASE_ADV).script.php $(BASE_ADV).xml index.html diff --git a/library/rules_shipping_framework.php b/library/rules_shipping_framework.php index d0b6e1c307bbb1d27a7b1eb7c0ce389aea732440..3c89c16218870dd25b21bbedb06c81e17fb60666 100644 --- a/library/rules_shipping_framework.php +++ b/library/rules_shipping_framework.php @@ -1,6 +1,8 @@ <?php -defined ('_JEXEC') or die('Restricted access'); +if ( !defined( 'ABSPATH' ) && !defined('_JEXEC') ) { + die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ); +} /** * Shipping By Rules Framework for general, rules-based shipments, like regular postal services with complex shipping cost structures @@ -217,6 +219,27 @@ class RulesShippingFramework { 'maxquantity' => 0, ); } + + protected function getDateTimeVariables($cart, $products, $method) { + $utime = microtime(true); + $milliseconds = (int)(1000*($utime - (int)$utime)); + $millisecondsstring = sprintf('%03d', $milliseconds); + return array( + 'year' => date("Y", $utime), + 'year2' => date("y", $utime), + 'month' => date("m", $utime), + 'day' => date("d", $utime), + 'weekday' => date("N", $utime), + 'hour' => date("H", $utime), + 'hour12' => date("h", $utime), + 'ampm' => date("a", $utime), + 'minute' => date("i", $utime), + 'second' => date("s", $utime), + 'decisecond' => $millisecondsstring[0], + 'centisecond' => substr($millisecondsstring, 0, 2), + 'millisecond' => $millisecondsstring, + ); + } protected function getOrderDimensions ($cart, $products, $method) { return array(); @@ -237,6 +260,46 @@ class RulesShippingFramework { protected function getOrderPrices ($cart, $products, $method) { return array(); } + + /** + * Extract information about non-numerical zip codes (UK and Canada) from the postal code + */ + protected function getAddressZIP ($zip) { + $values = array(); + + // Postal code Check for UK postal codes: Use regexp to determine if ZIP structure matches and also to extract the parts. + // Also handle UK overseas areas/islands that use four-letter outward codes rather than "A{1,2}0{1,2}A{0,1} 0AA" + $zip=strtoupper($zip); + if (isset($zip) and preg_match('/^\s*(([A-Z]{1,2})(\d{1,2})([A-Z]?)|[A-Z]{4}|GIR)\s*(\d[A-Z]{2})\s*$/', $zip, $match)) { + $values['uk_outward'] = $match[1]; + $values['uk_area'] = $match[2]; + $values['uk_district'] = $match[3]; + $values['uk_subdistrict'] = $match[4]; + $values['uk_inward'] = $match[5]; + } else { + $values['uk_outward'] = NULL; + $values['uk_area'] = NULL; + $values['uk_district'] = NULL; + $values['uk_subdistrict'] = NULL; + $values['uk_inward'] = NULL; + } + // Postal code Check for Canadian postal codes: Use regexp to determine if ZIP structure matches and also to extract the parts. + if (isset($zip) and preg_match('/^\s*(([A-Za-z])(\d)([A-Za-z]))\s*(\d[A-Za-z]\d)\s*$/', $zip, $match)) { + $values['canada_fsa'] = $match[1]; + $values['canada_area'] = $match[2]; + $values['canada_urban'] = $match[3]; + $values['canada_subarea'] = $match[4]; + $values['canada_ldu'] = $match[5]; + } else { + $values['canada_fsa'] = NULL; + $values['canada_area'] = NULL; + $values['canada_urban'] = NULL; + $values['canada_subarea'] = NULL; + $values['canada_ldu'] = NULL; + } + // print("<pre>values: ".print_r($values,1)."</pre>"); + return $values; + } /** Allow child classes to add additional variables for the rules or modify existing one */ @@ -250,6 +313,7 @@ class RulesShippingFramework { public function getCartValues ($cart, $products, $method) { $cartvals = array_merge ( + $this->getDateTimeVariables($cart, $products, $method), $this->getOrderCounts($cart, $products, $method), // Add the prices, optionally calculated from the products subset of the cart $this->getOrderPrices ($cart, $products, $method), @@ -296,31 +360,33 @@ class RulesShippingFramework { $cartvals_callback = function ($products) use ($this_class, $cart, $method) { return $this_class->getCartValues ($cart, $products, $method, NULL); }; - foreach ($this->rules[$id] as $r) { - if ($r->matches($cartvals, $cart->products, $cartvals_callback)) { - $rtype = $r->getType(); - switch ($rtype) { - case 'shipping': - case 'shippingwithtax': - case 'noshipping': - $result["rule"] = $r; - $result["rule_name"] = $r->getRuleName(); - break; - case 'modifiers_add': - case 'modifiers_multiply': - $result[$rtype][] = $r; - break; - case 'definition': // A definition updates the $cartvals, but has no other effects - $cartvals[strtolower($r->getRuleName())] = $r->getValue(); - break; - default: - $this->warning('OTSHIPMENT_RULES_UNKNOWN_TYPE', $r->getType(), $r->rulestring); - break; + if (isset($this->rules[$id])) { + foreach ($this->rules[$id] as $r) { + if ($r->matches($cartvals, $this->getCartProducts($cart, $method), $cartvals_callback)) { + $rtype = $r->getType(); + switch ($rtype) { + case 'shipping': + case 'shippingwithtax': + case 'noshipping': + $result["rule"] = $r; + $result["rule_name"] = $r->getRuleName(); + break; + case 'modifiers_add': + case 'modifiers_multiply': + $result[$rtype][] = $r; + break; + case 'definition': // A definition updates the $cartvals, but has no other effects + $cartvals[strtolower($r->getRuleName())] = $r->getValue(); + break; + default: + $this->warning('OTSHIPMENT_RULES_UNKNOWN_TYPE', $r->getType(), $r->rulestring); + break; + } + } + if (!is_null($result["rule"])) { + $this->match[$id] = $result; + return $result; // <- This also breaks out of the foreach loop! } - } - if (!is_null($result["rule"])) { - $this->match[$id] = $result; - return $result; // <- This also breaks out of the foreach loop! } } } @@ -329,6 +395,18 @@ class RulesShippingFramework { return NULL; } + protected function handleNoShipping($match, $method) { + if ($match['rule']->isNoShipping()) { + if (!empty($match["rule_name"])) + $this->warning('OTSHIPMENT_RULES_NOSHIPPING_MESSAGE', $match["rule_name"]); + $name = $this->getMethodName($method); + $this->debug('checkConditions '.$name.' indicates NoShipping for this method, specified by rule "'.$match["rule_name"].'" ('.$match['rule']->rulestring.').'); + return true; + } else { + return false; + } + } + /** * @param $cart * @param int $method @@ -339,19 +417,16 @@ class RulesShippingFramework { $name = $this->getMethodName($method); if (!isset($this->rules[$id])) $this->parseMethodRules($method); + // TODO: This needs to be redone sooner or later! $match = $this->evaluateMethodRules ($cart, $method); if ($match && !is_null ($match['rule'])) { $this->setMethodCosts($method, $match, null); // If NoShipping is set, this method should NOT offer any shipping at all, so return FALSE, otherwise TRUE // If the rule has a name, print it as warning (otherwise don't print anything) - if ($match['rule']->isNoShipping()) { - if (!empty($match["rule_name"])) - $this->warning('OTSHIPMENT_RULES_NOSHIPPING_MESSAGE', $match["rule_name"]); - $this->debug('checkConditions '.$name.' indicates NoShipping for this method, specified by rule "'.$match["rule_name"].'" ('.$match['rule']->rulestring.').'); + if ($this->handleNoShipping($match, $method)) { return FALSE; - } else { - return TRUE; } + return TRUE; } $this->debug('checkConditions '.$name.' does not fulfill all conditions, no rule matches'); return FALSE; @@ -370,11 +445,16 @@ class RulesShippingFramework { * @return int */ function getCosts ($cart, $method) { + $results = array(); $id = $this->getMethodId($method); if (!isset($this->rules[$id])) $this->parseMethodRules($method); $match = $this->evaluateMethodRules ($cart, $method); if ($match) { + if ($this->handleNoShipping($match, $method)) { + return $results; + } + $r = $match["rule"]; $this->debug('Rule ' . $match["rule_name"] . ' ('.$r->rulestring.') matched.'); @@ -390,11 +470,20 @@ class RulesShippingFramework { } $this->setMethodCosts($method, $match, $cost); - return $cost; + $res = array( + 'method' => $id, + 'name' => $this->getMethodName($method), +// 'rulesetname'=>$match['ruleset_name'], + 'rulename' => $match["rule_name"], + 'cost' => $cost, + ); + $results[] = $res; } - $this->debug('getCosts '.$this->getMethodName($method).' does not return shipping costs'); - return 0; + if (empty($results)) { + $this->debug('getCosts '.$this->getMethodName($method).' does not return shipping costs'); + } + return $results; } public function getRuleName($methodid) { @@ -676,6 +765,7 @@ class ShippingRule { if (count($conditionvals)<1) return $this->evaluateTerm($expr, $vals, $products, $cartvals_callback); + // TODO: Make this more general! $filterkeys = array( "evaluate_for_categories" => 'categories', "evaluate_for_products" => 'skus', diff --git a/releases/plg_opentools_vm_rules_shipping_advanced_v5.99.1.zip b/releases/plg_opentools_vm_rules_shipping_advanced_v5.99.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..ea0fe3954b9dd2285ca7663571e513ab94b608f4 Binary files /dev/null and b/releases/plg_opentools_vm_rules_shipping_advanced_v5.99.1.zip differ diff --git a/releases/plg_opentools_vm_rules_shipping_v5.99.1.zip b/releases/plg_opentools_vm_rules_shipping_v5.99.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..443982bf7dbae2aba81d44bb2029e246ab4ffb32 Binary files /dev/null and b/releases/plg_opentools_vm_rules_shipping_v5.99.1.zip differ diff --git a/rules_shipping.xml b/rules_shipping.xml index afc6f0ba033de854e56992d59a4f0b7b7b75f2f2..7711e204a53699cf060c3dc463707281816dc055 100644 --- a/rules_shipping.xml +++ b/rules_shipping.xml @@ -6,7 +6,7 @@ <authorUrl>http://www.open-tools.net</authorUrl> <copyright>Copyright (C) 2013-2014, Reinhold Kainhofer</copyright> <license>GPL v3+</license> - <version>5.99</version> + <version>5.99.1</version> <description>OTSHIPMENT_RULES_DESC</description> <files> <filename plugin="rules_shipping">rules_shipping.php</filename> diff --git a/rules_shipping_advanced.xml b/rules_shipping_advanced.xml index da37c2e33f0f0d76415427f91e8d03d52ad7272b..188815a03863d9af945f5f7c2a9755d0260ef3d6 100644 --- a/rules_shipping_advanced.xml +++ b/rules_shipping_advanced.xml @@ -6,7 +6,7 @@ <authorUrl>http://www.open-tools.net</authorUrl> <copyright>Copyright (C) 2013-2014, Reinhold Kainhofer</copyright> <license>GPL v3+</license> - <version>5.99</version> + <version>5.99.1</version> <description>OTSHIPMENT_RULES_ADV_DESC</description> <files> <filename plugin="rules_shipping_advanced">rules_shipping_advanced.php</filename> diff --git a/rules_shipping_base.php b/rules_shipping_base.php index 1c92cf0ec24ee2d28e7cc522e02d30c2816d42c2..9a3d279bdaae9a88f46812a827c32d7aaa866908 100644 --- a/rules_shipping_base.php +++ b/rules_shipping_base.php @@ -120,7 +120,10 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { // We need to call getCosts, because in J3 $method->rule_name and $method->cost as set in getCosts is no longer preserved. // Instead, we simply call getCosts again, which as a side-effect sets all those members of $method. $costs = $this->helper->getCosts($cart,$method); - $rulename = $this->helper->getRuleName($method->virtuemart_shipmentmethod_id); + if (empty($costs)) + return FALSE; + $rulename = $costs[0]['rulename']; +// $rulename = $this->helper->getRuleName($method->virtuemart_shipmentmethod_id); $variables = $this->helper->getRuleVariables($method->virtuemart_shipmentmethod_id); $values['virtuemart_order_id'] = $order['details']['BT']->virtuemart_order_id; $values['order_number'] = $order['details']['BT']->order_number; @@ -138,7 +141,12 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { return TRUE; } function getCosts (VirtueMartCart $cart, $method, $cart_prices) { - return $this->helper->getCosts($cart, $method, $cart_prices); + $costs = $this->helper->getCosts($cart, $method, $cart_prices); + if (empty($costs)) { + return false; + } else { + return $costs[0]['cost']; + } } protected function checkConditions ($cart, $method, $cart_prices) { return $this->helper->checkConditions($cart, $method, $cart_prices); diff --git a/rules_shipping_framework_joomla.php b/rules_shipping_framework_joomla.php index 0c54b3eb0beac362cff6c966461f8b1a54445ca1..3792c516e0124bf1bcdd7f48141d21fecad955f8 100644 --- a/rules_shipping_framework_joomla.php +++ b/rules_shipping_framework_joomla.php @@ -102,10 +102,11 @@ class RulesShippingFrameworkJoomla extends RulesShippingFramework { $counts = array( 'articles' => 0, 'products' => count($products), + 'quantity' => 0, 'minquantity' => 9999999999, 'maxquantity' => 0, ); - + foreach ($products as $product) { $counts['articles'] += $product->quantity; $counts['maxquantity'] = max ($counts['maxquantity'], $product->quantity);