diff --git a/rules_shipping_base.php b/rules_shipping_base.php index 819bc9ce31d5664db217291e37b661eeb213ffc2..af26407326bf6758446aa2ef0115c480563096ac 100644 --- a/rules_shipping_base.php +++ b/rules_shipping_base.php @@ -173,9 +173,9 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { $values['virtuemart_shipmentmethod_id'] = $order['details']['BT']->virtuemart_shipmentmethod_id; $values['shipment_name'] = $this->renderPluginName ($method); $values['rule_name'] = $method->rule_name; - $values['order_weight'] = $this->getOrderWeight ($cart, $method->weight_unit); - $values['order_articles'] = $this->getOrderArticles ($cart); - $values['order_products'] = $this->getOrderProducts ($cart); +// $values['order_weight'] = $this->getOrderWeight ($cart, $method->weight_unit); +// $values['order_articles'] = $this->getOrderArticles ($cart); +// $values['order_products'] = $this->getOrderProducts ($cart); $values['shipment_weight_unit'] = $method->weight_unit; $values['shipment_cost'] = $method->cost; $values['tax_id'] = $method->tax_id; @@ -265,7 +265,9 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { } - + /** This function evaluates all rules, one after the other until it finds a matching rule that + * defines shipping costs (or uses NoShipping). If a modifier or definition is encountered, + * its effect is stored, but the loop continues */ protected function evaluateMethodRules ($cart, $method, $cart_prices) { // $method->match will cache the matched rule and the modifiers if (isset($this->match[$method->virtuemart_shipmentmethod_id])) { @@ -274,8 +276,13 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { // Evaluate all rules and find the matching ones (including modifiers and definitions!) $result = array("rule"=>Null, "rule_name"=>"", "modifiers_add"=>array(), "modifiers_multiply"=>array()); $cartvals = $this->getCartValues ($cart, $cart->products, $method, $cart_prices); + // Pass a callback function to matches to obtain the cartvals for a subset of the products + $this_class = $this; + $cartvals_callback = function ($products) use ($this_class, $cart, $method, $cart_prices) { + return $this_class->getCartValues ($cart, $products, $method, NULL); + }; foreach ($this->rules[$method->virtuemart_shipmentmethod_id] as $r) { - if ($r->matches($cartvals)) { + if ($r->matches($cartvals, $cart->products, $cartvals_callback)) { $rtype = $r->getType(); switch ($rtype) { case 'shipping': @@ -288,7 +295,9 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { case 'modifiers_multiply': $result[$rtype][] = $r; break; - case 'definition': // A definition has modified the $cartvals, but has no other effects + case 'definition': // A definition updates the $cartvals, but has no other effects + $cartvals[strtolower($r->getRuleName())] = $r->getShippingCosts(); + // TODO break; default: $this->printWarning(JText::sprintf('VMSHIPMENT_RULES_UNKNOWN_TYPE', $r->getType(), $r->rulestring)); @@ -657,18 +666,18 @@ class plgVmShipmentRules_Shipping_Base extends vmPSPlugin { 'zip6'=>substr($zip,0,6), 'city'=>isset($address['city'])?trim($address['city']):'', ); - data['company'] = $address['company']; - data['title'] = $address['title']; - data['first_name'] = $address['first_name']; - data['middle_name'] = $address['middle_name']; - data['last_name'] = $address['last_name']; - data['address1'] = $address['address_1']; - data['address2'] = $address['address_2']; - data['city'] = $address['city']; - data['phone1'] = $address['phone_1']; - data['phone2'] = $address['phone_2']; - data['fax'] = $address['fax']; - data['email'] = $address['email']; + $data['company'] = $address['company']; + $data['title'] = $address['title']; + $data['first_name'] = $address['first_name']; + $data['middle_name'] = $address['middle_name']; + $data['last_name'] = $address['last_name']; + $data['address1'] = $address['address_1']; + $data['address2'] = $address['address_2']; + $data['city'] = $address['city']; + $data['phone1'] = $address['phone_1']; + $data['phone2'] = $address['phone_2']; + $data['fax'] = $address['fax']; + $data['email'] = $address['email']; return $data; } @@ -885,7 +894,7 @@ function filterProducts($products, $filter_conditions) { foreach ($products as $p) { if (!empty($filter_conditions['skus']) && !in_array($p->product_sku, $filter_conditions['skus'])) continue; - if (!empty($filter_conditions['categories']) && count(array_intersect($filter_conditions['categories'], $p->product_categories))==0) + if (!empty($filter_conditions['categories']) && count(array_intersect($filter_conditions['categories'], $p->categories))==0) continue; if (!empty($filter_conditions['manufacturers']) && count(array_intersect($filter_conditions['manufacturers'], $p->product_manufacturers))==0) continue; @@ -1115,10 +1124,11 @@ class ShippingRule { /** Evaluate the given expression $expr only for the products that match the filter given by the scoping * function and the corresponding conditions */ - protected function evaluateScoping($expr, $scoping, $conditionvals, $vals) { - if (count($conditions)<1) - return $this->evaluateTerm($expr, $vals); - + protected function evaluateScoping($expr, $scoping, $conditionvals, $vals, $products, $cartvals_callback) { +// JFactory::getApplication()->enqueueMessage("<pre>Scoping, begin, scoping=$scoping, expression=".print_r($expr,1).", conditionvals=".print_r($conditionvals, 1)."</pre>", 'error'); + if (count($conditionvals)<1) + return $this->evaluateTerm($expr, $vals, $products, $cartvals_callback); + $filterkeys = array( "evaluate_for_categories" => 'categories', "evaluate_for_products" => 'products', @@ -1126,15 +1136,17 @@ class ShippingRule { "evaluate_for_manufacturers" => 'manufacturers' ); +// JFactory::getApplication()->enqueueMessage("<pre>Scoping: condition is ".$filterkeys[$scoping].'='.print_r($conditionvals,1).", Old cartvals are: ".print_r($vals,1)."</pre>", 'error'); $conditions = array(); if (isset($filterkeys[$scoping])) - $conditions[$filterkeys[$scoping]] = $conditionsvals; + $conditions[$filterkeys[$scoping]] = $conditionvals; // Pass the conditions to the parent plugin class to filter the current list of products: - $products = filterProducts($currentproducts, $conditions); - $vals = $this->plugin->getCartValues ($cart, $products, /* FIXME: method*/ $this->method, $cart_prices); - // FIXME: IMPLEMENT - // TODO + $filteredproducts = filterProducts($products, $conditions); + // We have been handed a callback function to calculate the cartvals for the filtered list of products, so use it: + $filteredvals = $cartvals_callback($filteredproducts); +// JFactory::getApplication()->enqueueMessage("<pre>Scoping: condition is ".print_r($conditions,1).", products filtered: ".count($filteredproducts).", New cartvals are: ".print_r($filteredvals,1)."</pre>", 'error'); + return $this->evaluateTerm ($expr, $filteredvals, $filteredproducts, $cartvals_callback); } protected function evaluateFunction ($function, $args) { @@ -1231,7 +1243,7 @@ class ShippingRule { } } - protected function evaluateTerm ($expr, $vals) { + protected function evaluateTerm ($expr, $vals, $products, $cartvals_callback) { // The scoping functions need to be handled differently, because they first need to adjust the cart variables to the filtered product list // before evaluating its first argument. So even though parsing the rules handles scoping functions like any other function, their // evaluation is fundamentally different and is special-cased here: @@ -1254,7 +1266,7 @@ class ShippingRule { $func = array_shift($expr); // The scoping function name $expression = array_shift($expr); // The expression to be evaluated $conditions = $expr; // the remaining $expr list now contains the conditions - return $this->evaluateScoping ($expression, $func, $conditions, $vals); + return $this->evaluateScoping ($expression, $func, $conditions, $vals, $products, $cartvals_callback); } elseif (is_array($expr)) { // Operator @@ -1266,7 +1278,7 @@ class ShippingRule { $evaluate = false; } foreach ($expr as $e) { - $term = $evaluate ? ($this->evaluateTerm($e, $vals)) : $e; + $term = $evaluate ? ($this->evaluateTerm($e, $vals, $products, $cartvals_callback)) : $e; if ($op == 'COMPARISON') { // For comparisons, we only evaluate every other term (the operators are NOT evaluated!) // The data format for comparisons is: array('COMPARISON', $operand1, '<', $operand2, '<=', ....) @@ -1329,11 +1341,11 @@ class ShippingRule { } } - protected function calculateShipping ($vals) { - return $this->evaluateTerm($this->shipping, $vals); + protected function calculateShipping ($vals, $products, $cartvals_callback) { + return $this->evaluateTerm($this->shipping, $vals, $products, $cartvals_callback); } - protected function evaluateRule (&$vals) { + protected function evaluateRule (&$vals, $products, $cartvals_callback) { if ($this->evaluated) return; // Already evaluated @@ -1347,7 +1359,7 @@ class ShippingRule { foreach ($this->conditions as $c) { // All conditions have to match! - $ret = $this->evaluateTerm($c, $vals); + $ret = $this->evaluateTerm($c, $vals, $products, $cartvals_callback); if (is_null($ret) || (!$ret)) { return; @@ -1356,7 +1368,7 @@ class ShippingRule { // All conditions match $this->match = True; // Calculate the value (i.e. shipping cost or modifier) - $this->value = $this->calculateShipping($vals); + $this->value = $this->calculateShipping($vals, $products, $cartvals_callback); // For definitions add the variable to the vals if ($this->ruletype=='definition') { $vals[strtolower($this->name)] = $this->value; @@ -1376,8 +1388,8 @@ class ShippingRule { $this->rulename = $name; } - function matches(&$vals) { - $this->evaluateRule($vals); + function matches(&$vals, $products, $cartvals_callback) { + $this->evaluateRule($vals, $products, $cartvals_callback); return $this->match; }