--- uc_discounts.module.beta33	Wed Jul 22 05:34:14 2009
+++ uc_discounts.module	Mon Jul 27 21:15:40 2009
@@ -19,10 +19,12 @@
 define("FILTER_TYPE_PRODUCTS", 1);
 define("FILTER_TYPE_TERMS", 2);
 define("FILTER_TYPE_SKUS", 3);
+define("FILTER_TYPE_CLASS", 4);
 
 define("ALL_PRODUCTS", "-1");
 define("ALL_TERMS", "-1");
 define("ALL_SKUS", "");
+define("ALL_CLASSES", "");
 define("ALL_ROLES", "-1");
 
 define("REQUIRES_SINGLE_PRODUCT_TO_QUALIFY_DEFAULT", FALSE);
@@ -278,6 +280,10 @@
 
 /**
  * Implementation of Ubercart's hook_line_item().
+ *
+ * Displays all discounts as a single line item
+ *
+ * @see hook_line_item()
  */
 function uc_discounts_line_item()
 {
@@ -294,6 +300,8 @@
 
 /**
  * Implementation of hook_cart_pane().
+ * 
+ * @see hook_cart_pane()
  */
 function uc_discounts_cart_pane($items)
 {
@@ -355,6 +363,8 @@
 
 /**
  * Implementation of hook_checkout_pane().
+ * 
+ * @see hook_checkout_pane()
  */
 function uc_discounts_checkout_pane()
 {
@@ -583,7 +593,7 @@
 	return array("name", "short_description", "description", "qualifying_type", 
 		"qualifying_amount", "has_qualifying_amount_max", "qualifying_amount_max", "discount_type", 
 		"discount_amount", "requires_code", "filter_type", "has_role_filter", 
-		"requires_single_product_to_qualify", "max_times_applied", "can_be_combined_with_other_discounts", "max_uses", 
+		"requires_single_product_to_qualify", "required_product", "max_times_applied", "can_be_combined_with_other_discounts", "max_uses", 
 		"max_uses_per_user", "max_uses_per_code", "has_expiration", "expiration", 
 		"is_published", "weight");
 }
@@ -610,7 +620,7 @@
 	return array("'%s'", "'%s'", "'%s'", "%d", 
 		"%f", "%d", "%f", "%d", 
 		"%f", "%d", "%d", "%d", 
-		"%d", "%d", "%d", "%d", 
+		"%d", "'%s'", "%d", "%d", "%d", 
 		"%d", "%d", "%d", "%d", 
 		"%d", "%d");
 }
@@ -635,7 +645,7 @@
 function uc_discounts_insert($name, $short_description, $description, $qualifying_type, 
 	$qualifying_amount, $has_qualifying_amount_max, $qualifying_amount_max, $discount_type, 
 	$discount_amount, $requires_code, $filter_type, $has_role_filter, 
-	$requires_single_product_to_qualify, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
+	$requires_single_product_to_qualify, $required_product, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
 	$max_uses_per_user, $max_uses_per_code, $has_expiration, $expiration, 
 	$is_published, $weight, $insert_timestamp = NULL)
 {
@@ -649,7 +659,7 @@
 		$name, $short_description, $description, $qualifying_type, 
 		$qualifying_amount, $has_qualifying_amount_max, $qualifying_amount_max, $discount_type, 
 		$discount_amount, $requires_code, $filter_type, $has_role_filter, 
-		$requires_single_product_to_qualify, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
+		$requires_single_product_to_qualify, $required_product, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
 		$max_uses_per_user, $max_uses_per_code, $has_expiration, $expiration, 
 		$is_published, $weight, $insert_timestamp);
 	$discount_id = db_last_insert_id("uc_discounts", "discount_id");
@@ -664,7 +674,7 @@
 	$name, $short_description, $description, $qualifying_type, 
 	$qualifying_amount, $has_qualifying_amount_max, $qualifying_amount_max, $discount_type, 
 	$discount_amount, $requires_code, $filter_type, $has_role_filter, 
-	$requires_single_product_to_qualify, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
+	$requires_single_product_to_qualify, $required_product, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
 	$max_uses_per_user, $max_uses_per_code, $has_expiration, $expiration, 
 	$is_published, $weight)
 {
@@ -687,7 +697,7 @@
 		$name, $short_description, $description, $qualifying_type, 
 		$qualifying_amount, $has_qualifying_amount_max, $qualifying_amount_max, $discount_type, 
 		$discount_amount, $requires_code, $filter_type, $has_role_filter, 
-		$requires_single_product_to_qualify, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
+		$requires_single_product_to_qualify, $required_product, $max_times_applied, $can_be_combined_with_other_discounts, $max_uses, 
 		$max_uses_per_user, $max_uses_per_code, $has_expiration, $expiration, 
 		$is_published, $weight, $discount_id);
 }
@@ -744,6 +754,11 @@
 /**
  * Returns product_ids for discount object.
  * Note: this function correctly returns all products for term-based discounts.
+ *
+ * @param $discount object
+ * @param $exclude_all_products bool
+ *
+ * @return array of product ids (nids)
  */
 function get_product_ids_for_discount_object($discount, $exclude_all_products = FALSE)
 {
@@ -775,6 +790,16 @@
 			while ( $row = db_fetch_array($result) )
 				$product_ids[] = $row["nid"];
 			return $product_ids;
+
+		case FILTER_TYPE_CLASS:
+			$query = "SELECT DISTINCT n.nid FROM {node} n
+				INNER JOIN {uc_discounts_classes} dcl ON n.type=dcl.class
+				WHERE dcl.discount_id=%d";
+			uc_discounts_log($query);
+			$result = db_query($query, $discount->discount_id);
+			while ( $row = db_fetch_array($result) )
+				$product_ids[] = $row["nid"];
+			return $product_ids;
 	}
 	return array();
 }
@@ -783,6 +808,11 @@
  * Returns product_ids for discount.
  * Note: this function does not check filter_type so a discount with filter_type other than 
  *	  FILTER_TYPE_PRODUCTS will return no values.
+ *
+ * @param $discount_id
+ * @param $exclude_all_products
+ * 
+ * @return array of product ids (nids)
  */
 function get_product_ids_for_discount($discount_id, $exclude_all_products = FALSE)
 {
@@ -866,6 +896,27 @@
 }
 
 /**
+ * Returns Product Class names for discount.
+ */
+function get_classes_for_discount($discount_id, $exclude_all_classes = FALSE)
+{
+	$classes = array();
+
+	//Get Classes from database
+	if ($exclude_all_classes)
+	{
+		$result = db_query("SELECT class FROM {uc_discounts_classes} WHERE discount_id=%d AND class<>%d",
+			$discount_id, ALL_CLASSES);
+	}
+	else
+		$result = db_query("SELECT class FROM {uc_discounts_classes} WHERE discount_id=%d", $discount_id);
+
+	while ( $row = db_fetch_array($result) )
+		$classes[] = $row["class"];
+	return $classes;
+}
+
+/**
  * Returns role_ids for discount.
  */
 function get_role_ids_for_discount($discount_id, $exclude_all_roles = FALSE)
@@ -929,6 +980,27 @@
 }
 
 /**
+ * Inserts a uc_discounts_classes row and returns its discount_class_id.
+ */
+function uc_discounts_classes_insert($discount_id, $class)
+{
+	$query = "INSERT INTO {uc_discounts_classes}(discount_id, class) VALUES(%d, '%s')";
+	uc_discounts_log("query=".$query);
+	db_query($query, $discount_id, $class);
+	return db_last_insert_id("uc_discounts_classes", "discount_class_id");
+}
+
+/**
+ * Deletes all uc_discounts_classes rows for a discount.
+ */
+function uc_discounts_classes_delete($discount_id)
+{
+	$query = "DELETE FROM {uc_discounts_classes} WHERE discount_id=%d";
+	uc_discounts_log("query=".$query);
+	db_query($query, $discount_id);
+}
+
+/**
  * Inserts a uc_discounts_roles row and returns its discount_role_id.
  */
 function uc_discounts_roles_insert($discount_id, $role_id)
@@ -956,14 +1028,16 @@
  * @param $order Order to get discounts for
  * @param $errors Reference to array to add error messages to
  * @param $messages Reference to array to add success messages to
+ *
+ * @return array of discount objects
  */
 function get_discounts_for_order($order, &$errors = NULL, &$warnings = NULL, &$messages = NULL)
 {
-	$order_product_id_subtotal_map = array();
-	$order_product_id_quantity_map = array();
-	$order_product_ids = array();
-	$order_product_ids_set = array();
-	$order_product_id_product_array_map = array();
+	$order_product_id_subtotal_map = array();        // Product NIDS in cart => subtotal of individual item
+	$order_product_id_quantity_map = array();        // Product NIDS in cart => quantity of individual item
+	$order_product_ids = array();                    // Product NIDS in cart
+	$order_product_ids_set = array();                // Product NIDS in cart=> bool
+	$order_product_id_product_array_map = array();   // Product objects in cart
 	$order_subtotal = 0;
 
 	//Create IN string of product node IDs in order
@@ -989,6 +1063,7 @@
 		}
 	}
 
+	// Populate product NID array with NIDs from the order
 	$order_product_ids = array_keys($order_product_ids_set);
 
 	$temp_product_ids = $order_product_ids;
@@ -1020,11 +1095,24 @@
 		//Get SKUs for order's products
 		$result = db_query( "SELECT DISTINCT model FROM {uc_products} WHERE nid IN(%s)", join(",", $order_product_ids) );
 		while ( $row = db_fetch_array($result) )
-			$temp_skus[] = "'" . db_escape_string($row["model"]) . "'";;
+			$temp_skus[] = "'" . db_escape_string($row["model"]) . "'";
 	}
 	$skus_clause = sprintf( "d.filter_type<>%d OR ds.sku IN(%s)", 
 		FILTER_TYPE_SKUS, join(",", $temp_skus) );
 
+	//Create IN string of classes in order
+	$temp_classes = array();
+	$temp_classes[] = "'" . db_escape_string(ALL_CLASSES) . "'";
+	if ( is_array($order->products) && !empty($order->products) )
+	{
+		//Get classes for order's products
+		$result = db_query( "SELECT DISTINCT type FROM {node} WHERE nid IN(%s)", join(",", $order_product_ids) );
+		while ( $row = db_fetch_array($result) )
+			$temp_classes[] = "'" . db_escape_string($row["type"]) . "'";
+	}
+	$classes_clause = sprintf( "d.filter_type<>%d OR dcl.class IN(%s)",
+			FILTER_TYPE_CLASS, join(",", $temp_classes) );
+
 	//Create codes clause
 	$escaped_codes_string = NULL;
 	if ( !empty($order->uc_discounts_codes) )
@@ -1056,6 +1144,7 @@
 			LEFT JOIN {uc_discounts_skus} ds ON d.discount_id=ds.discount_id
 			LEFT JOIN {uc_discounts_roles} dr ON d.discount_id=dr.discount_id
 			LEFT JOIN {uc_discounts_codes} dc ON d.discount_id=dc.discount_id
+			LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id=dcl.discount_id
 			WHERE dc.code IN(%s)
 			AND (d.has_role_filter=0%s)
 			AND (%s)
@@ -1063,7 +1152,7 @@
 			AND (%s)
 			AND (d.has_expiration<>0 AND d.expiration<=%d)
 			ORDER BY weight", $escaped_codes_string, $roles_clause, $product_ids_clause, 
-				$term_ids_clause, $skus_clause, time() );
+				$term_ids_clause, $skus_clause, $classes_clause, time() );
 		uc_discounts_log("query=" . $query);
 		$result = db_query($query);
 		while ( $discount = db_fetch_object($result) )
@@ -1077,18 +1166,20 @@
 		LEFT JOIN {uc_discounts_products} dp ON d.discount_id=dp.discount_id
 		LEFT JOIN {uc_discounts_terms} dt ON d.discount_id=dt.discount_id
 		LEFT JOIN {uc_discounts_skus} ds ON d.discount_id=ds.discount_id
+		LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id=dcl.discount_id
 		WHERE (d.requires_code=0%s)
 		AND (d.has_role_filter=0%s)
 		AND (%s)
 		AND (%s)
 		AND (d.has_expiration=0 OR d.expiration>%d)
 		ORDER BY weight", $codes_clause, $roles_clause, $product_ids_clause, 
-			$term_ids_clause, $skus_clause, time() );
+			$term_ids_clause, $skus_clause, $classes_clause, time() );
 	uc_discounts_log("query=" . $query);
 	$result = db_query($query);
 
 	$total_discount_amount = 0;
 	$discounts = array();
+	// Appears to check if order qualifies for each discount then applies discount.  Functionality should be separated, no?
 	while ( $discount = db_fetch_object($result) )
 	{
 		//Get code for discount (if one exists)
@@ -1105,7 +1196,7 @@
 		//The query handled valid codes and expiration, this block must:
 		//	check max uses (if applicable)
 		//	check if discount is being combined and can be combined
-		//	check if order qualifies (type, requires_single_product_to_qualify, can_be_combined_with_other_discounts)
+		//	check if order qualifies (type, requires_single_product_to_qualify, required_product, can_be_combined_with_other_discounts)
 		//	determine number of times to apply discount
 
 		//If this discount has a max uses amount, check max uses
@@ -1146,7 +1237,7 @@
 			$discount->user_uses_count = $row["user_uses_count"];
 		}
 
-		//If code exists and this discount has a max uses per code amount, check max uses per user
+		//If code exists and this discount has a max uses per code amount, check max uses per code
 		if ( !is_null($discount->code) && ($discount->max_uses_per_code > 0) )
 		{
 			$row = db_fetch_array( db_query("SELECT COUNT(*) as code_uses_count FROM {uc_discounts_uses} WHERE discount_id=%d AND code='%s'", 
@@ -1179,6 +1270,7 @@
 				continue;
 			}
 
+			// Check if the first discount can't be combined
 			if (!$discounts[0]->can_be_combined_with_other_discounts) 
 			{
 				//If first discount was a coded discount, add error message (only print warning if both 
@@ -1192,15 +1284,13 @@
 			}
 		}
 
-
-		//Check if order qualifies for this discount (check type and requires_single_product_to_qualify)
+		//Check if order qualifies for this discount (check type, requires_single_product_to_qualify, required_product)
 
 		//Get product IDs for discount
 		$discount_product_ids = get_product_ids_for_discount_object($discount);
 		if ( in_array(ALL_PRODUCTS, $discount_product_ids) )
 			$discount_product_ids = $order_product_ids;
 
-
 		//Determine total qualifying amount of order (store in order_qualifying_amount)
 		//and qualifying subtotal of order (store in order_qualifying_subtotal)
 
@@ -1210,36 +1300,37 @@
 		//If discount requires a single product
 		if ($discount->requires_single_product_to_qualify)
 		{
-			switch ($discount->qualifying_type)
+			// Check if required_product is set or trust that form validation did the right thing?  Check anyway...
+			if ($discount->required_product)
 			{
-				case QUALIFYING_TYPE_MINIMUM_PRICE:
-					//Determine the maximum product subtotal
-					foreach ($discount_product_ids as $product_id)
+
+				// Get nid of required_product
+				$required_product_result = db_query("SELECT DISTINCT nid from {uc_products} WHERE model=('%s')",$discount->required_product);
+				$required_product_id = db_result($required_product_result);
+
+				// Check if required_product nid is in cart
+				if (in_array($required_product_id,$order_product_ids))
+				{
+					switch ($discount->qualifying_type)
 					{
-						if ( isset($order_product_id_subtotal_map[$product_id]) )
-						{
-							$order_qualifying_amount = 
-								max($order_qualifying_amount, $order_product_id_subtotal_map[$product_id]);
-						}
-					}
+						// Buy $50 of X product
+						case QUALIFYING_TYPE_MINIMUM_PRICE:
+							//Determine the maximum product subtotal
+							$order_qualifying_amount = max($order_qualifying_amount, $order_product_id_subtotal_map[$required_product_id]);
 
-					//Subtract already discounted amount
-					$order_qualifying_amount -= $total_discount_amount;
+							//Subtract already discounted amount
+							$order_qualifying_amount -= $total_discount_amount;
 //TO DO: keep track of each item's subtotal and decrement upon applying discount
-					break;
+							break;
 
-				case QUALIFYING_TYPE_MINIMUM_QUANTITY:
-					//Determine the maximum product quantity
-					foreach ($discount_product_ids as $product_id)
-					{
-						if ( isset($order_product_id_quantity_map[$product_id]) )
-						{
-							$order_qualifying_amount = 
-								max($order_qualifying_amount, $order_product_id_quantity_map[$product_id]);
-						}
-					}
+						// Buy 5 of X product...
+						case QUALIFYING_TYPE_MINIMUM_QUANTITY:
+							//Determine the maximum product quantity
+							$order_qualifying_amount = max($order_qualifying_amount, $order_product_id_quantity_map[$required_product_id]);
 //TO DO: keep track of each item's quantity and decrement upon applying discount
-					break;
+							break;
+					}
+				}
 			}
 		}
 		//Otherwise sum product subtotals or quantities
@@ -1327,7 +1418,7 @@
 		//Get product IDs in order that are in discount
 		$order_and_discount_product_ids = array_intersect($discount_product_ids, $order_product_ids);
 
-		//Create array of product objects related to this discount
+		//Create array of product objects in cart to which this discount gets applied.
 		$order_and_discount_products = array();
 		foreach ($order_and_discount_product_ids as $product_id)
 		{
@@ -1459,16 +1550,19 @@
 				$discount_product_ids = get_product_ids_for_discount_object($discount, TRUE);
 
 				// Do we have any products
-				if (count($discount_product_ids) > 0) {
-				  $discounted_products_amount = 0;
-				  foreach ($order_and_discount_products as $product)
-				  {
-					$discounted_products_amount += $product->price * $product->qty;
-				  }
-				  $discount->amount = $discounted_products_amount * $discount->discount_amount;
+				if (count($discount_product_ids) > 0)
+				{
+					$discounted_products_amount = 0;
+					foreach ($order_and_discount_products as $product)
+					{
+						$discounted_products_amount += $product->price * $product->qty;
+					}
+					$discount->amount = $discounted_products_amount * $discount->discount_amount;
 				// Discount the subtotal so far
-				} else {
-				  $discount->amount = max($order_subtotal - $total_discount_amount, 0) * $discount->discount_amount;
+				}
+				else
+				{
+					$discount->amount = max($order_subtotal - $total_discount_amount, 0) * $discount->discount_amount;
 				}
 //End patch from lutegrass
 				break;
@@ -1519,7 +1613,7 @@
 
 		//Add this discount to list of discounts applied to order
 		$discounts[] = $discount;
-	}
+	} // end of db fetch while loop
 	return $discounts;
 }
 
@@ -1573,6 +1667,13 @@
 	while ( $row = db_fetch_array($result) )
 		$skus[] = "'" . db_escape_string($row["model"]) . "'";
 
+	//Get classes for product
+	$classes = array();
+	$classes[] = "'" . db_escape_string(ALL_CLASSES) . "'";
+	$result = db_query("SELECT DISTINCT type FROM {node} WHERE nid=%d", $product->nid);
+	while ( $row = db_fetch_array($result) )
+		$classes[] = "'" . db_escape_string($row["type"]) . "'";
+
 	//Create roles clause
 	global $user;
 	$auth_rid = ($user->uid != 0) ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
@@ -1590,19 +1691,23 @@
 	$skus_clause = sprintf( "d.filter_type<>%d OR ds.sku IN(%s)", 
 		FILTER_TYPE_SKUS, join(",", $skus) );
 
+	$classes_clause = sprintf( "d.filter_type<>%d OR dcl.class IN(%s)",
+		FILTER_TYPE_CLASS, join(",", $classes) );
+
 	$sort_order_string = (is_ascending_sort) ? "ASC" : "DESC";
 
 	$query = sprintf("SELECT d.* FROM {uc_discounts} d
 		LEFT JOIN {uc_discounts_products} dp ON d.discount_id=dp.discount_id
 		LEFT JOIN {uc_discounts_terms} dt ON d.discount_id=dt.discount_id
 		LEFT JOIN {uc_discounts_skus} ds ON d.discount_id=ds.discount_id
+		LEFT JOIN {uc_discounts_classes} dcl ON d.discount_id=dcl.discount_id
 		WHERE d.requires_code=0
 		AND (d.has_role_filter=0%s)
 		AND (%s)
 		AND (%s)
 		AND (%s)
 		AND (d.has_expiration=0 OR d.expiration>%d)
-		ORDER BY d.%s %s", $roles_clause, $product_ids_clause, $term_ids_clause, $skus_clause, time(), 
+		ORDER BY d.%s %s", $roles_clause, $product_ids_clause, $term_ids_clause, $skus_clause, $classes_clause, time(), 
 			$sort_column, $sort_order_string);
 	uc_discounts_log("query=" . $query);
 	$result = db_query($query);
@@ -1774,3 +1879,5 @@
 {
 //	error_log($s);
 }
+
+
