diff --git a/rules_shipping_advanced.php b/rules_shipping_advanced.php index 13d69a82be507bde0c530a0e75d2cca98983048a..e6c845c9a16ecaffa16d5f389da457218a9e5e6b 100644 --- a/rules_shipping_advanced.php +++ b/rules_shipping_advanced.php @@ -152,7 +152,7 @@ class ShippingRule_Advanced extends ShippingRule { // Special-case the name assignment, where we don't want to interpret the value as an arithmetic expression! - if (preg_match('/^\s*(name)\s*=\s*(["\']?)(.*)\2\s*$/i', $rulepart, $matches)) { + if (preg_match('/^\s*(name|variable|definition)\s*=\s*(["\']?)(.*)\2\s*$/i', $rulepart, $matches)) { $this->handleAssignment ($matches[1], $matches[3], $rulepart); return; } @@ -389,20 +389,21 @@ class ShippingRule_Advanced extends ShippingRule { // 5a) if (count($stack) != 1) { JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_EVALUATE_UNKNOWN_ERROR', $rulepart), 'error'); + JFactory::getApplication()->enqueueMessage(JText::sprintf('Outstack: <pre>%s</pre>', print_r($out_stack,1)), 'error'); + $stack = array (0); } $res = array_pop($stack); // 5) - if ($is_condition) { // Comparisons are conditions - $this->conditions[] = $res; - } elseif ($is_assignment) { - + if ($is_assignment) { // Assignments are handled first, so conditions can be assigned to variables if ($res[0]=='=') { $this->handleAssignment ($res[1], $res[2], $rulepart); } else { // Assignment has to be top-level! JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_EVALUATE_ASSIGNMENT_TOPLEVEL', $rulepart), 'error'); } + } elseif ($is_condition) { // Comparisons are conditions + $this->conditions[] = $res; } else { // Terms without comparisons or assignments are shipping cost expressions $this->shipping = $res; diff --git a/rules_shipping_base.php b/rules_shipping_base.php index 78457dc868cc6793e78b73c6286f1f4765207de8..8ba11e33358628b13259a86752376085a8f60767 100644 --- a/rules_shipping_base.php +++ b/rules_shipping_base.php @@ -224,6 +224,48 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { + protected function findMatchingRule (&$cartvals, $method) { + $result = array("rule"=>Null, "rule_name"=>"", "modifiers"=>array()); + // TODO: Handle modifiers + foreach ($method->rules as $r) { + // If the rule is a variable definition, it will NOT match, but modify the $cartvals array for the next rules + if ($r->matches($cartvals)) { + $result["rule"] = $r; + $result["rule_name"] = $r->getRuleName($cartvals); + return $result; + } + } + // None of the rules matched, so return NULL; + return NULL; + } + + /** + * @param \VirtueMartCart $cart + * @param int $method + * @param array $cart_prices + * @return bool + */ + protected function checkConditions ($cart, $method, $cart_prices) { + if (!isset($method->rules)) $this->parseMethodRules($method); + + $cartvals = $this->getCartValues ($cart, $method, $cart_prices); + $match = $this->findMatchingRule ($cartvals, $method); + if ($match) { + $method->matched_rule = $match["rule"]; + $method->rule_name = $match["rule_name"]; + // If NoShipping is set, this method should NOT offer any shipping at all, so return FALSE, otherwise TRUE + if ($method->matched_rule->isNoShipping()) { + $this->printWarning(JText::sprintf('VMSHIPMENT_RULES_NOSHIPPING_MESSAGE', $method->rule_name)); + vmdebug('checkConditions '.$method->shipment_name.' indicates NoShipping for rule "'.$method->rule_name.'" ('.$method->matched_rule->rulestring.').'); + return FALSE; + } else { + return TRUE; + } + } + vmdebug('checkConditions '.$method->shipment_name.' does not fit'); + return FALSE; + } + /** * @param VirtueMartCart $cart * @param $method @@ -233,18 +275,17 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { function getCosts (VirtueMartCart $cart, $method, $cart_prices) { if (!isset($method->rules)) $this->parseMethodRules($method); $cartvals = $this->getCartValues ($cart, $method, $cart_prices); - - foreach ($method->rules as $r) { - if ($r->matches($cartvals)) { - $rulename=$r->getRuleName($cartvals); - vmdebug('Rule '.$rulename.' ('.$r->rulestring.') matched.'); - $method->tax_id = $r->tax_id; - $method->matched_rule = $r; - $method->rule_name = $r->getRuleName($cartvals); - $method->cost = $r->getShippingCosts($cartvals); - $method->includes_tax = $r->includes_tax; - return $method->cost; - } + $match = $this->findMatchingRule ($cartvals, $method); + if ($match) { + $r = $match["rule"]; + $rulename = $match["rule_name"]; + vmdebug('Rule '.$rulename.' ('.$r->rulestring.') matched.'); + $method->tax_id = $r->tax_id; + $method->matched_rule = $r; + $method->rule_name = $rulename; + $method->cost = $r->getShippingCosts($cartvals); + $method->includes_tax = $r->includes_tax; + return $method->cost; } vmdebug('getCosts '.$method->name.' does not return shipping costs'); @@ -530,35 +571,6 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { return $cartvals; } - - /** - * @param \VirtueMartCart $cart - * @param int $method - * @param array $cart_prices - * @return bool - */ - protected function checkConditions ($cart, $method, $cart_prices) { - if (!isset($method->rules)) $this->parseMethodRules($method); - - $cartvals = $this->getCartValues ($cart, $method, $cart_prices); - foreach ($method->rules as $r) { - if ($r->matches($cartvals)) { - $method->matched_rule = $r; - $method->rule_name = $r->getRuleName($cartvals); - // If NoShipping is set, this method should NOT offer any shipping at all, so return FALSE, otherwise TRUE - if ($r->isNoShipping()) { - $this->printWarning(JText::sprintf('VMSHIPMENT_RULES_NOSHIPPING_MESSAGE', $method->rule_name)); - vmdebug('checkConditions '.$method->shipment_name.' indicates NoShipping for rule "'.$method->rule_name.'" ('.$r->rulestring.').'); - return FALSE; - } else { - return TRUE; - } - } - } - vmdebug('checkConditions '.$method->shipment_name.' does not fit'); - return FALSE; - } - /** * Create the table for this plugin if it does not yet exist. * This functions checks if the called plugin is active one. @@ -683,6 +695,7 @@ class ShippingRule { var $shipping = 0; var $includes_tax = 0; var $name = ''; + var $is_definition = 0; function __construct ($rule, $countries, $tax_id) { if (is_array($countries)) { @@ -704,10 +717,14 @@ class ShippingRule { function handleAssignment ($var, $value, $rulepart) { switch (strtolower($var)) { + case 'name': $this->name = $value; break; case 'shipping': $this->shipping = $value; $this->includes_tax = False; break; case 'shippingwithtax': $this->shipping = $value; $this->includes_tax = True; break; - case 'name': $this->name = $value; break; + case 'variable': // Variable=... is the same as Definition=... + case 'definition': $this->name = $value; $this->is_definition = True; break; + case 'value': $this->shipping = $value; break; // definition values are also stored in the shipping member! case 'comment': break; // Completely ignore all comments! + case 'condition': $this->conditions[] = $value; break; default: JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_UNKNOWN_VARIABLE', $var, $rulepart), 'error'); } } @@ -951,24 +968,29 @@ class ShippingRule { return $this->evaluateTerm($this->shipping, $vals); } - function matches($vals) { + function matches(&$vals) { // First, check the country, if any conditions are given: if (count ($this->countries) > 0 && !in_array ($vals['countryid'], $this->countries)) { // vmdebug('Rule::matches: Country check failed: countryid='.print_r($vals['countryid'],1).', countries are: '.print_r($this->countries,1).'...'); return False; } - + foreach ($this->conditions as $c) { // All conditions have to match! $ret = $this->evaluateTerm($c, $vals); - // JFactory::getApplication()->enqueueMessage("Evaluating term <pre>".print_r($c,1)."</pre> returns $ret", 'error'); if (is_null($ret) || (!$ret)) { return false; } } - // All conditions match, so return true - return true; + // All conditions match, so return true for rules; For definitions add the variable to the vals + if ($this->is_definition) { + $vals[$this->name] = $this->evaluateTerm($this->shipping, $vals); + // This rule does not specify shipping costs (just modify the cart values!), so return false + return false; + } else { + return true; + } } function getRuleName($vals) { @@ -994,6 +1016,9 @@ class ShippingRule { // NoShipping is set, so if the rule matches, this method should not offer any shipping at all return (is_string($this->shipping) && (strtolower($this->shipping)=="noshipping")); } + function isDefinition() { + return $this->is_definition; + } }