diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 074783bec135c30e3ac9d1dbfe1e4b295483d3de..6b087b8f035dabf46b98dcd5859bc521225ca78d 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -135,7 +135,8 @@ class Piwik_API_DocumentationGenerator
 			'url' => 'http://forum.piwik.org/',
 			'apiModule' => 'UserCountry',
 			'apiAction' => 'getCountry',
-			'lastMinutes' => '30'
+			'lastMinutes' => '30',
+			'abandonedCarts' => '0',
 		);
 		
 		foreach($parametersToSet as $name => $value)
diff --git a/core/API/Proxy.php b/core/API/Proxy.php
index 61e3a2da163b9c426d1c7b307dd7ff3c4c37bf5d..1a283e221e8e95bb24f0d6d2447c118d884efbd9 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -35,6 +35,7 @@ class Piwik_API_Proxy
 	protected $alreadyRegistered = array();
 	
 	private $metadataArray = array();
+	public $hideIgnoredFunctions = true;
 	
 	// when a parameter doesn't have a default value we use this
 	private $noDefaultValue;
@@ -295,7 +296,7 @@ class Piwik_API_Proxy
 			&& !$method->isConstructor()
 			&& $method->getName() != 'getInstance'
 			&& false === strstr($method->getDocComment(), '@deprecated')
-			&& false === strstr($method->getDocComment(), '@ignore')
+			&& (!$this->hideIgnoredFunctions || false === strstr($method->getDocComment(), '@ignore'))
 			 )
 		{
 			$name = $method->getName();
diff --git a/core/Archive.php b/core/Archive.php
index 65c26ae03ae6b4e4ed7f5e55cddcf81862c80fdb..4a70e43eee6c74f0af798be0bb030eda564d0639 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -68,11 +68,23 @@ abstract class Piwik_Archive
 	const INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH = 21;
 	const INDEX_PAGE_ENTRY_BOUNCE_COUNT = 22;
 	
+	// Ecommerce Items reports
+	const INDEX_ECOMMERCE_ITEM_REVENUE = 23;
+	const INDEX_ECOMMERCE_ITEM_QUANTITY = 24;
+	const INDEX_ECOMMERCE_ITEM_PRICE = 25;
+	const INDEX_ECOMMERCE_ORDERS = 26;
+
 	// Goal reports
 	const INDEX_GOAL_NB_CONVERSIONS = 1;
 	const INDEX_GOAL_REVENUE = 2;
 	const INDEX_GOAL_NB_VISITS_CONVERTED = 3;
-
+	
+	const INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL = 4;
+	const INDEX_GOAL_ECOMMERCE_REVENUE_TAX = 5;
+	const INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING = 6;
+	const INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT = 7;
+	const INDEX_GOAL_ECOMMERCE_ITEMS = 8;
+	
 	public static $mappingFromIdToName = array(
 				Piwik_Archive::INDEX_NB_UNIQ_VISITORS 		=> 'nb_uniq_visitors',
 				Piwik_Archive::INDEX_NB_VISITS				=> 'nb_visits',
@@ -100,12 +112,23 @@ abstract class Piwik_Archive
 				Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS => 'entry_nb_actions',
 				Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH => 'entry_sum_visit_length',
 				Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT => 'entry_bounce_count',
+				
+				// Items reports metrics
+				Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE => 'revenue',
+				Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY => 'quantity',
+				Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE => 'price',
+				Piwik_Archive::INDEX_ECOMMERCE_ORDERS => 'orders',
 			);
 
 	public static $mappingFromIdToNameGoal = array(
 				Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS 	=> 'nb_conversions',
 				Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED 	=> 'nb_visits_converted',
 				Piwik_Archive::INDEX_GOAL_REVENUE 			=> 'revenue',
+				Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 'revenue_subtotal',
+				Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX  => 'revenue_tax',
+				Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING  => 'revenue_shipping',
+				Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT  => 'revenue_discount',
+				Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS  => 'items',
 	);
 
 	/*
@@ -125,6 +148,9 @@ abstract class Piwik_Archive
 				'sum_daily_nb_uniq_visitors' => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
 	);
 	
+	const LABEL_ECOMMERCE_CART = 'ecommerceAbandonedCart';
+	const LABEL_ECOMMERCE_ORDER = 'ecommerceOrder';
+	
 	/**
 	 * Website Piwik_Site
 	 *
diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php
index cb00e5cc4f4b8f11402bf8d7312584a6c9eb225b..37b4b187ee34be9ac4760e6aa2a6118702b004c4 100644
--- a/core/ArchiveProcessing/Day.php
+++ b/core/ArchiveProcessing/Day.php
@@ -240,7 +240,9 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
                 'referer_keyword',
                 'visitor_returning',
 	    		'visitor_days_since_first',
+	    		'visitor_days_since_order',
 	    		'visitor_count_visits',
+	    		'visit_goal_buyer',
                 'location_country',
                 'location_continent',
                 'revenue',
@@ -267,18 +269,25 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
 	}
 	
 	/**
-	 * @see queryVisitsByDimension() Similar to this function, but queries metrics for the requested dimensions, for each Goal conversion 
+	 * @see queryVisitsByDimension() Similar to this function, 
+	 * but queries metrics for the requested dimensions, 
+	 * for each Goal conversion 
 	 */
 	public function queryConversionsByDimension($label, $where = '')
 	{
-	    if(is_array($label))
+		if(empty($label))
+		{
+			$select = "";
+			$groupBy = "";
+		}
+	    elseif(is_array($label))
 	    {
-	        $select = implode(", ", $label);
-	        $groupBy = $select;
+	        $groupBy = implode(", ", $label);
+	        $select =  $groupBy . ", ";
 	    }
 	    else
 	    {
-	        $select = $label . " AS label ";
+	        $select = $label . " AS label, ";
 	        $groupBy = 'label';
 	    }
 	    if(!empty($where)) 
@@ -295,20 +304,32 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
 	    {
 	        $segment = ' AND '.$segmentSql['sql'];
 	    }
-		$query = "SELECT idgoal,
-						count(*) as `". Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS ."`,
-						truncate(sum(revenue),2) as `". Piwik_Archive::INDEX_GOAL_REVENUE ."`,
-						count(distinct idvisit) as `". Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED."`,
+	    
+	    $isEcommerceEnabled = Piwik::isEcommerceEnabled($this->idsite);
+	    
+	    if($isEcommerceEnabled)
+	    {
+	    	$select .= 	$this->getSqlRevenue('SUM(revenue_subtotal)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL ."`,".
+			    		$this->getSqlRevenue('SUM(revenue_tax)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX ."`,".
+			    		$this->getSqlRevenue('SUM(revenue_shipping)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING ."`,".
+			    		$this->getSqlRevenue('SUM(revenue_discount)')." as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT ."`,".
+			    		"SUM(items) as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS ."`, ";
+	    }
+	    $groupBy = !empty($groupBy) ? ", $groupBy" : '';
+		$query = "SELECT 
 						$select
+						idgoal,
+						count(*) as `". Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS ."`,
+						".$this->getSqlRevenue('SUM(revenue)')." as `". Piwik_Archive::INDEX_GOAL_REVENUE ."`,
+						count(distinct idvisit) as `". Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED."`
 			 	FROM ".Piwik_Common::prefixTable('log_conversion')."
 			 	WHERE server_time >= ?
 						AND server_time <= ?
 			 			AND idsite = ?
 			 			$where
 						$segment
-			 	GROUP BY idgoal, $groupBy
+			 	GROUP BY idgoal $groupBy
 				ORDER BY NULL";
-						
 		$bind = array_merge( array( $this->getStartDatetimeUTC(), 
                                     $this->getEndDatetimeUTC(), 
                                     $this->idsite ),
@@ -317,6 +338,38 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
 		return $query;
 	}
 	
+	public function queryEcommerceItems($field)
+	{
+		$query = "SELECT 
+						name as label,
+						".$this->getSqlRevenue('SUM(quantity * price)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE ."`,
+						".$this->getSqlRevenue('SUM(quantity)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY ."`,
+						".$this->getSqlRevenue('SUM(price)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE ."`,
+						count(distinct idorder) as `". Piwik_Archive::INDEX_ECOMMERCE_ORDERS."`,
+						count(idvisit) as `". Piwik_Archive::INDEX_NB_VISITS."`,
+						case idorder when 0 then ".Piwik_Tracker_GoalManager::IDGOAL_CART." else ".Piwik_Tracker_GoalManager::IDGOAL_ORDER." end as type
+			 	FROM ".Piwik_Common::prefixTable('log_conversion_item')."
+			 		LEFT JOIN ".Piwik_Common::prefixTable('log_action')." 
+			 		ON $field = idaction
+			 	WHERE server_time >= ?
+						AND server_time <= ?
+			 			AND idsite = ?
+			 			AND deleted = 0
+			 	GROUP BY $field, type
+				ORDER BY NULL";
+						
+		$bind = array( $this->getStartDatetimeUTC(), 
+                       $this->getEndDatetimeUTC(), 
+                       $this->idsite
+        );
+		$query = $this->db->query($query, $bind);
+		return $query;
+	}
+	
+	protected function getSqlRevenue($field)
+	{
+		return "ROUND(".$field.",".Piwik_Tracker_GoalManager::REVENUE_PRECISION.")";
+	}
 	
 	public function getDataTableFromArray( $array )
 	{
@@ -580,8 +633,12 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
 				$revenue = $conversions = 0;
 				foreach($values[Piwik_Archive::INDEX_GOALS] as $idgoal => $goalValues)
 				{
-					$revenue += $goalValues[Piwik_Archive::INDEX_GOAL_REVENUE];
-					$conversions += $goalValues[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
+					// Do not sum Cart revenue since it is a lost revenue
+					if($idgoal >= Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+					{
+						$revenue += $goalValues[Piwik_Archive::INDEX_GOAL_REVENUE];
+						$conversions += $goalValues[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
+					}
 				}
 				$values[Piwik_Archive::INDEX_NB_CONVERSIONS] = $conversions;
 				$values[Piwik_Archive::INDEX_REVENUE] = $revenue;
@@ -605,14 +662,50 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing
 		$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS]	+= $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
 		$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED]	+= $newRowToAdd[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED];
 		$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_REVENUE] 			+= $newRowToAdd[Piwik_Archive::INDEX_GOAL_REVENUE];
+		
+		// Cart & Order
+		if(isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS]))
+		{
+			$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS];
+			
+			// Order only
+			if(isset($oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL]))
+			{
+				$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL];
+				$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX];
+				$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING];
+				$oldRowToUpdate[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT] += $newRowToAdd[Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT];
+			}
+		}
 	}
 	
-	function getNewGoalRow()
+	function getNewGoalRow($idGoal)
 	{
+		if($idGoal > Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+		{
+			return array(	Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS 	=> 0, 
+							Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, 
+							Piwik_Archive::INDEX_GOAL_REVENUE 			=> 0, 
+						);
+		}
+		if($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+		{
+			return array(	Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS 	=> 0, 
+							Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, 
+							Piwik_Archive::INDEX_GOAL_REVENUE 			=> 0, 
+							Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 0,
+							Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 0,
+							Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 0,
+							Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 0,
+							Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
+			);
+		}
+		// $row['idgoal'] == Piwik_Tracker_GoalManager::IDGOAL_CART
 		return array(	Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS 	=> 0, 
 						Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, 
 						Piwik_Archive::INDEX_GOAL_REVENUE 			=> 0, 
-					);
+						Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0,
+		);
 	}
 	
 	function getGoalRowFromQueryRow($queryRow)
diff --git a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
index eb4359437e55d0183036bcba54cd8471af0ff4fd..50a1e665b2c00c1418f581245169084b1ef53b6a 100644
--- a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
+++ b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
@@ -59,7 +59,7 @@ class Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal extends Piwik_DataTa
 	{
 		// Add standard processed metrics
 		parent::filter($table);
-		$roundingPrecision = 2;
+		$roundingPrecision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
 		$expectedColumns = array();
 
 		foreach($table->getRows() as $key => $row)
@@ -76,7 +76,10 @@ class Piwik_DataTable_Filter_AddColumnsProcessedMetricsGoal extends Piwik_DataTa
 				$revenue = 0;
 				foreach($goals as $goalId => $columnValue)
 				{
-					$revenue += (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
+					if($goalId >= Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+					{
+						$revenue += (int)$this->getColumn($columnValue, Piwik_Archive::INDEX_GOAL_REVENUE, Piwik_Archive::$mappingFromIdToNameGoal);
+					}
 				}
 
 				if($revenue == 0)
diff --git a/core/DataTable/Filter/ReplaceColumnNames.php b/core/DataTable/Filter/ReplaceColumnNames.php
index 2d22de7046b69853296feae7631df556116dc347..810ba5c3222893201b1a7f9b7bd9d85eb6b117fe 100644
--- a/core/DataTable/Filter/ReplaceColumnNames.php
+++ b/core/DataTable/Filter/ReplaceColumnNames.php
@@ -71,9 +71,18 @@ class Piwik_DataTable_Filter_ReplaceColumnNames extends Piwik_DataTable_Filter
 					$newSubColumns = array();
 					foreach($columnValue as $idGoal => $goalValues)
 					{
+						$mapping = Piwik_Archive::$mappingFromIdToNameGoal;
+						if($idGoal == Piwik_Tracker_GoalManager::IDGOAL_CART)
+						{
+							$idGoal = Piwik_Archive::LABEL_ECOMMERCE_CART;
+						}
+						elseif($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+						{
+							$idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+						}
 						foreach($goalValues as $id => $goalValue)
 						{
-							$subColumnName = Piwik_Archive::$mappingFromIdToNameGoal[$id];
+							$subColumnName = $mapping[$id];
 							$newSubColumns['idgoal='.$idGoal][$subColumnName] = $goalValue;
 						}
 					}
diff --git a/core/Db/Schema/Myisam.php b/core/Db/Schema/Myisam.php
index 2f19b54d52075dd878245a3526765b6c1d4518a6..007d174e40270987adb12b2117cadc8e3d2a173a 100644
--- a/core/Db/Schema/Myisam.php
+++ b/core/Db/Schema/Myisam.php
@@ -176,6 +176,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
 							  visitor_returning TINYINT(1) NOT NULL,
 							  visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL,
 							  visitor_days_since_last SMALLINT(5) UNSIGNED NOT NULL,
+							  visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL,
 							  visitor_days_since_first SMALLINT(5) UNSIGNED NOT NULL,
 							  visit_first_action_time DATETIME NOT NULL,
 							  visit_last_action_time DATETIME NOT NULL,
@@ -186,6 +187,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
 							  visit_total_actions SMALLINT(5) UNSIGNED NOT NULL,
 							  visit_total_time SMALLINT(5) UNSIGNED NOT NULL,
 							  visit_goal_converted TINYINT(1) NOT NULL,
+							  visit_goal_buyer TINYINT(1) NOT NULL, 
 							  referer_type TINYINT(1) UNSIGNED NULL,
 							  referer_name VARCHAR(70) NULL,
 							  referer_url TEXT NOT NULL,
@@ -225,6 +227,25 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
 							  INDEX index_idsite_idvisitor (idsite, idvisitor)
 							)  DEFAULT CHARSET=utf8
 			",
+		
+			'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` (
+												  idsite int(10) UNSIGNED NOT NULL,
+										  		  idvisitor BINARY(8) NOT NULL,
+										          server_time DATETIME NOT NULL,
+												  idvisit INTEGER(10) UNSIGNED NOT NULL,
+												  idorder varchar(100) NOT NULL,
+												  
+												  idaction_sku INTEGER(10) UNSIGNED NOT NULL,
+												  idaction_name INTEGER(10) UNSIGNED NOT NULL,
+												  idaction_category INTEGER(10) UNSIGNED NOT NULL,
+												  price FLOAT NOT NULL,
+												  quantity INTEGER(10) UNSIGNED NOT NULL,
+												  deleted TINYINT(1) UNSIGNED NOT NULL,
+												  
+												  PRIMARY KEY(idvisit, idorder, idaction_sku),
+										          INDEX index_idsite_servertime ( idsite, server_time )
+												)  DEFAULT CHARSET=utf8
+			",
 
 			'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
 									  idvisit int(10) unsigned NOT NULL,
@@ -240,13 +261,22 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
 									  visitor_returning tinyint(1) NOT NULL,
         							  visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL,
         							  visitor_days_since_first SMALLINT(5) UNSIGNED NOT NULL,
+							  		  visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL,
 									  location_country char(3) NOT NULL,
 									  location_continent char(3) NOT NULL,
 									  url text NOT NULL,
 									  idgoal int(10) unsigned NOT NULL,
-									  revenue float default NULL,
 									  buster int unsigned NOT NULL,
-        							  custom_var_k1 VARCHAR(50) DEFAULT NULL,
+									  
+									  idorder varchar(100) default NULL,
+									  items SMALLINT UNSIGNED DEFAULT NULL,
+									  revenue float default NULL,
+									  revenue_subtotal float default NULL,
+									  revenue_tax float default NULL,
+									  revenue_shipping float default NULL,
+									  revenue_discount float default NULL,
+        							  
+									  custom_var_k1 VARCHAR(50) DEFAULT NULL,
         							  custom_var_v1 VARCHAR(50) DEFAULT NULL,
         							  custom_var_k2 VARCHAR(50) DEFAULT NULL,
         							  custom_var_v2 VARCHAR(50) DEFAULT NULL,
@@ -257,6 +287,7 @@ class Piwik_Db_Schema_Myisam implements Piwik_Db_Schema_Interface
         							  custom_var_k5 VARCHAR(50) DEFAULT NULL,
         							  custom_var_v5 VARCHAR(50) DEFAULT NULL,
 									  PRIMARY KEY (idvisit, idgoal, buster),
+									  UNIQUE KEY unique_idorder (idorder),
 									  INDEX index_idsite_datetime ( idsite, server_time )
 									) DEFAULT CHARSET=utf8
 			",
diff --git a/core/Piwik.php b/core/Piwik.php
index 7af1de6abd228d833e42f4a242eb67038fcc217b..5bb6ea89e4fda150d108e478c4839a932495ba40 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -52,6 +52,11 @@ class Piwik
 			|| Zend_Registry::get('config')->General->enable_processing_unique_visitors_year_and_range ;
 	}
 
+	static public function isEcommerceEnabled($idSite)
+	{
+		//TODO ECOMMERCE
+		return true;
+	}
 /*
  * Prefix/unprefix class name
  */
@@ -1366,7 +1371,7 @@ class Piwik
 			}
 			else
 			{
-				$precision = 2;
+				$precision = Piwik_Tracker_GoalManager::REVENUE_PRECISION;
 				$value = sprintf( "%01.".$precision."f", $value);
 			}
 		}
diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php
index 3efe824275065e77df7d06c8f003703d0259380d..49f635bf772673a58f18bc079cd464f518af3d36 100644
--- a/core/Tracker/Action.php
+++ b/core/Tracker/Action.php
@@ -22,6 +22,9 @@ interface Piwik_Tracker_Action_Interface {
 	const TYPE_OUTLINK  = 2;
 	const TYPE_DOWNLOAD = 3;
 	const TYPE_ACTION_NAME = 4;
+	const TYPE_ECOMMERCE_ITEM_SKU = 5;
+	const TYPE_ECOMMERCE_ITEM_NAME = 6;
+	const TYPE_ECOMMERCE_ITEM_CATEGORY = 7;
 	
 	public function setRequest($requestArray);
 	public function setIdSite( $idSite );
@@ -48,8 +51,8 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 	private $idSite;
 	private $timestamp;
 	private $idLinkVisitAction;
-	private $idActionName = null;
-	private $idActionUrl = null;
+	private $idActionName = false;
+	private $idActionUrl = false;
 
 	private $actionName;
 	private $actionType;
@@ -184,9 +187,12 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 		}
 		$parsedUrl['query'] = substr($validQuery,0,-strlen($separator));
 		$url = Piwik_Common::getParseUrlReverse($parsedUrl);
-		printDebug('Excluded parameters "'.implode(',',$excludedParameters).'" from URL');
-		printDebug(' Before was "'.$originalUrl.'"');
-		printDebug(' After is "'.$url.'"');
+		printDebug('Excluding parameters "'.implode(',',$excludedParameters).'" from URL');
+		if($originalUrl != $url)
+		{
+			printDebug(' Before was "'.$originalUrl.'"');
+			printDebug(' After is "'.$url.'"');
+		}
 		return $url;
 	}
 	
@@ -200,12 +206,108 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 	
 	static public function getSqlSelectActionId()
 	{
-		$sql = "SELECT idaction, type 
+		$sql = "SELECT idaction, type, name
 							FROM ".Piwik_Common::prefixTable('log_action')
 						."  WHERE "
 						."		( hash = CRC32(?) AND name = ? AND type = ? ) ";
 		return $sql;
 	}
+	
+	/**
+	 * This function will find the idaction from the lookup table piwik_log_action,
+	 * given an Action name and type.
+	 * 
+	 * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories
+	 * 
+	 * If the action name does not exist in the lookup table, it will INSERT it
+	 * @param array $actionNamesAndTypes Array of one or many (name,type)
+	 * @return array Returns the input array, with the idaction appended ie. Array of one or many (name,type,idaction)
+	 */
+	static public function loadActionId( $actionNamesAndTypes )
+	{
+		// First, we try and select the actions that are already recorded
+		$sql = self::getSqlSelectActionId();
+		$bind = array();
+		$i = 0;
+		foreach($actionNamesAndTypes as &$actionNameType)
+		{
+			list($name,$type) = $actionNameType;
+			if(empty($name))
+			{
+				$actionNameType[] = false;
+				continue;
+			}
+			if($i > 0)
+			{
+				$sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) ";
+			}
+			$bind[] = $name;
+			$bind[] = $name;
+			$bind[] = $type;
+			$i++;
+		}
+		// Case URL & Title are empty
+		if(empty($bind))
+		{
+			return $actionNamesAndTypes;
+		}
+		$actionIds = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
+		
+		// For the Actions found in the lookup table, add the idaction in the array, 
+		// If not found in lookup table, queue for INSERT
+		$actionsToInsert = array();
+		foreach($actionNamesAndTypes as $index => &$actionNameType)
+		{
+			list($name,$type) = $actionNameType;
+			if(empty($name)) { continue; }
+			$found = false;
+			foreach($actionIds as $row)
+			{
+				if($name == $row['name']
+					&& $type == $row['type'])
+				{
+					$found = true;
+					$actionNameType[] = $row['idaction'];
+					continue;
+				}
+			}
+			if(!$found)
+			{
+				$actionsToInsert[] = $index;
+			}
+		}
+		
+		$sql = "INSERT INTO ". Piwik_Common::prefixTable('log_action'). 
+				"( name, hash, type ) VALUES (?,CRC32(?),?)";
+		// Then, we insert all new actions in the lookup table
+		foreach($actionsToInsert as $actionToInsert)
+		{
+			list($name,$type) = $actionNamesAndTypes[$actionToInsert];
+	
+			Piwik_Tracker::getDatabase()->query($sql, array($name, $name, $type));
+			$actionId = Piwik_Tracker::getDatabase()->lastInsertId();
+			printDebug("Recorded a new action (".self::getActionTypeName($type).") in the lookup table: ". $name . " (idaction = ".$actionId.")");
+			
+			$actionNamesAndTypes[$actionToInsert][] = $actionId;
+		}
+		return $actionNamesAndTypes;
+	}
+	
+	static public function getActionTypeName($type)
+	{
+		switch($type)
+		{
+			case self::TYPE_ACTION_URL: return 'Page URL'; break;
+			case self::TYPE_OUTLINK: return 'Outlink URL'; break;
+			case self::TYPE_DOWNLOAD: return 'Download URL'; break;
+			case self::TYPE_ACTION_NAME: return 'Page Title'; break;
+			case self::TYPE_ECOMMERCE_ITEM_SKU: return 'Ecommerce Item SKU'; break;
+			case self::TYPE_ECOMMERCE_ITEM_NAME: return 'Ecommerce Item Name'; break;
+			case self::TYPE_ECOMMERCE_ITEM_CATEGORY: return 'Ecommerce Item Category'; break;
+			default: throw new Exception("Unexpected action type ".$type); break;
+		}
+	}
+	
 	/**
 	 * Loads the idaction of the current action name and the current action url.
 	 * These idactions are used in the visitor logging table to link the visit information
@@ -214,55 +316,38 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 	 * 
 	 * The methods takes care of creating a new record(s) in the action table if the existing
 	 * action name and action url doesn't exist yet.
-	 * 
 	 */
 	function loadIdActionNameAndUrl()
 	{
-		if( !is_null($this->idActionUrl) && !is_null($this->idActionName) )
+		if( $this->idActionUrl !== false 
+			&& $this->idActionName !== false )
 		{
 			return;
 		}
-		$idAction = Piwik_Tracker::getDatabase()->fetchAll(
-						$this->getSqlSelectActionId()
-						."	OR "
-						."		( hash = CRC32(?) AND name = ? AND type = ? ) ",
-						array($this->getActionName(), $this->getActionName(), $this->getActionNameType(),
-							$this->getActionUrl(), $this->getActionUrl(), $this->getActionType())
-					);
-
-		if( $idAction !== false )
+		$actions = array();
+		$action = array($this->getActionName(), $this->getActionNameType());
+		if(!is_null($action[1]))
 		{
-			foreach($idAction as $row)
-			{
-				if( $row['type'] == Piwik_Tracker_Action_Interface::TYPE_ACTION_NAME )
-				{
-					$this->idActionName = $row['idaction'];
-				}
-				else
-				{
-					$this->idActionUrl = $row['idaction'];
-				}
-			}
+			$actions[] = $action;
 		}
-
-		$sql = "INSERT INTO ". Piwik_Common::prefixTable('log_action'). 
-				"( name, hash, type ) VALUES (?,CRC32(?),?)";
-
-		if( is_null($this->idActionName) 
-		    && !is_null($this->getActionNameType()) )
+		$action = array($this->getActionUrl(), $this->getActionType());
+		if(!is_null($action[1]))
 		{
-			Piwik_Tracker::getDatabase()->query($sql,
-				array($this->getActionName(), $this->getActionName(), $this->getActionNameType()));
-			$this->idActionName = Piwik_Tracker::getDatabase()->lastInsertId();
-			printDebug("Recording a new page name in the lookup table: ". $this->idActionName);
+			$actions[] = $action;
 		}
-
-		if( is_null($this->idActionUrl) )
+		$loadedActionIds = self::loadActionId($actions);
+		
+		foreach($loadedActionIds as $loadedActionId)
 		{
-			Piwik_Tracker::getDatabase()->query($sql,
-				array($this->getActionUrl(), $this->getActionUrl(), $this->getActionType()));
-			$this->idActionUrl = Piwik_Tracker::getDatabase()->lastInsertId();
-			printDebug("Recording a new page URL in the lookup table: ". $this->idActionUrl);
+			list($name, $type, $actionId) = $loadedActionId;
+			if($type == $this->getActionType())
+			{
+				$this->idActionUrl = $actionId;
+			}
+			elseif($type == $this->getActionNameType())
+			{
+				$this->idActionName = $actionId;
+			}
 		}
 	}
 	
@@ -291,11 +376,10 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 	 public function record( $idVisit, $visitorIdCookie, $idRefererActionUrl, $idRefererActionName, $timeSpentRefererAction)
 	 {
 		$this->loadIdActionNameAndUrl();
-		$idActionName = $this->getIdActionName();
-		if(is_null($idActionName))
-		{
-			$idActionName = 0;
-		}
+		
+		$idActionName = in_array($this->getActionType(), array(Piwik_Tracker_Action::TYPE_ACTION_NAME, Piwik_Tracker_Action::TYPE_ACTION_URL))
+							? $this->getIdActionName()
+							: null;
 		Piwik_Tracker::getDatabase()->query( 
 						"INSERT INTO ".Piwik_Common::prefixTable('log_link_visit_action')
 						." (idvisit, idsite, idvisitor, server_time, idaction_url, idaction_name, idaction_url_ref, idaction_name_ref, time_spent_ref_action) 
@@ -304,8 +388,8 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
 							$this->idSite, 
 							$visitorIdCookie,
 							Piwik_Tracker::getDatetimeFromTimestamp($this->timestamp),
-							$this->getIdActionUrl(), 
-							$idActionName , 
+							(int)$this->getIdActionUrl(), 
+							$idActionName, 
 							$idRefererActionUrl, 
 							$idRefererActionName, 
 							$timeSpentRefererAction
diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index d32ae8a5b6e54adc34e6d0718ba474e9d3d06339..ec91de3d1594b7220b27803db9fc78e717b2b085 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -16,12 +16,67 @@
  */
 class Piwik_Tracker_GoalManager 
 {
+	// log_visit.visit_goal_buyer
+	const TYPE_BUYER_NONE = 0;
+	const TYPE_BUYER_ORDERED = 1;
+	const TYPE_BUYER_OPEN_CART = 2;
+	const TYPE_BUYER_ORDERED_AND_OPEN_CART = 3;
+
+	// log_conversion.idorder is NULLable, but not log_conversion_item which defaults to zero for carts
+	const ITEM_IDORDER_ABANDONED_CART = 0;
+	
+	// log_conversion.idgoal special values
+	const IDGOAL_CART = -1;
+	const IDGOAL_ORDER = 0;
+	
+	const REVENUE_PRECISION = 2;
+	
+	public $idGoal;
+	public $requestIsEcommerce;
+	public $isGoalAnOrder;
+	
 	/**
 	 * @var Piwik_Tracker_Action
 	 */
 	protected $action = null;
 	protected $convertedGoals = array();
+	protected $isThereExistingCartInVisit = false;
+	protected $request;
+	protected $orderId;
+	
+	function init($request)
+	{
+		$this->request = $request;
+		$this->orderId = Piwik_Common::getRequestVar('ec_id', false, 'string', $this->request);
+		$this->isGoalAnOrder = !empty($this->orderId);
+		$this->idGoal = Piwik_Common::getRequestVar('idgoal', -1, 'int', $this->request);
+		$this->requestIsEcommerce = ($this->idGoal == 0);
+	}
 
+	function getBuyerType($existingType = self::TYPE_BUYER_NONE)
+	{
+		// Was there a Cart for this visit prior to the order?
+		$this->isThereExistingCartInVisit = in_array($existingType, 
+											array(	Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART,
+													Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART));
+		
+		if(!$this->requestIsEcommerce)
+		{
+			return $existingType;
+		}
+		if($this->isGoalAnOrder)
+		{
+			return self::TYPE_BUYER_ORDERED;
+		}
+		// request is Add to Cart
+		if($existingType == self::TYPE_BUYER_ORDERED
+			|| $existingType == self::TYPE_BUYER_ORDERED_AND_OPEN_CART)
+		{
+			return self::TYPE_BUYER_ORDERED_AND_OPEN_CART;
+		}
+		return self::TYPE_BUYER_OPEN_CART;
+	}
+	
 	static public function getGoalDefinitions( $idSite )
 	{
 		$websiteAttributes = Piwik_Common::getCacheWebsiteAttributes( $idSite );
@@ -62,8 +117,11 @@ class Piwik_Tracker_GoalManager
 	}
 
 	/**
+	 * Look at the URL or Page Title and sees if it matches any existing Goal definition
+	 * 
 	 * @param int $idSite
 	 * @param Piwik_Tracker_Action $action
+	 * @return int Number of goals matched
 	 */
 	function detectGoalsMatchingUrl($idSite, $action)
 	{
@@ -149,27 +207,30 @@ class Piwik_Tracker_GoalManager
 		return count($this->convertedGoals) > 0;
 	}
 
-	function detectGoalId($idSite, $idGoal, $request)
+	function detectGoalId($idSite)
 	{
 		if(!$this->isGoalPluginEnabled())
 		{
 			return false;
 		}
 		$goals = $this->getGoalDefinitions($idSite);
-		if(!isset($goals[$idGoal]))
+		if(!isset($goals[$this->idGoal]))
 		{
 			return false;
 		}
-		$goal = $goals[$idGoal];
+		$goal = $goals[$this->idGoal];
 		
-		$url = Piwik_Common::getRequestVar( 'url', '', 'string', $request);
+		$url = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request);
 		$goal['url'] = Piwik_Tracker_Action::excludeQueryParametersFromUrl($url, $idSite);
-		$goal['revenue'] = Piwik_Common::getRequestVar('revenue', $goal['revenue'], 'float', $request);
+		$goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', $goal['revenue'], 'float', $this->request));
 		$this->convertedGoals[] = $goal;
 		return true;
 	}
 
-	function recordGoals($idSite, $visitorInformation, $visitCustomVariables, $action, $referrerTimestamp, $referrerUrl, $referrerCampaignName, $referrerCampaignKeyword)
+	/**
+	 * Records one or several goals matched in this request.
+	 */
+	public function recordGoals($idSite, $visitorInformation, $visitCustomVariables, $action, $referrerTimestamp, $referrerUrl, $referrerCampaignName, $referrerCampaignKeyword)
 	{
 		$location_country = isset($visitorInformation['location_country']) 
 							? $visitorInformation['location_country'] 
@@ -191,6 +252,7 @@ class Piwik_Tracker_GoalManager
 			'location_continent'=> $location_continent,
 			'visitor_returning' => $visitorInformation['visitor_returning'],
 			'visitor_days_since_first' => $visitorInformation['visitor_days_since_first'],
+			'visitor_days_since_order' => $visitorInformation['visitor_days_since_order'],
 			'visitor_count_visits' => $visitorInformation['visitor_count_visits'],
 		
 		);
@@ -240,17 +302,375 @@ class Piwik_Tracker_GoalManager
 
 		$goal += $visitCustomVariables;
 		
+		// some goals are converted, so must be ecommerce Order or Cart Update 
+		if($this->requestIsEcommerce)
+		{
+			$this->recordEcommerceGoal($goal, $visitorInformation);
+		}
+		else
+		{
+			$this->recordStandardGoals($goal, $action, $visitorInformation);
+		}
+	}
+	
+	/**
+	 * Returns rounded decimal revenue, or if revenue is integer, then returns as is.
+	 * 
+	 * @param int|float $revenue
+	 * @return int|float
+	 */
+	protected function getRevenue($revenue)
+	{
+		if(round($revenue) == $revenue)
+		{
+			return $revenue;
+		}
+		return round($revenue, self::REVENUE_PRECISION);
+	}
+	
+	/**
+	 * Records an Ecommerce conversion in the DB. Deals with Items found in the request.
+	 * Will deal with 2 types of conversions: Ecommerce Order and Ecommerce Cart update (Add to cart, Update Cart etc).
+	 * 
+	 * @param array $goal
+	 * @param array $visitorInformation
+	 */
+	protected function recordEcommerceGoal($goal, $visitorInformation)
+	{
+		// Is the transaction a Cart Update or an Ecommerce order?
+		$updateWhere = array(
+			'idvisit' => $visitorInformation['idvisit'],
+			'idgoal' => self::IDGOAL_CART,
+			'buster' => 0,
+		);
+		
+		if($this->isThereExistingCartInVisit)
+		{
+			printDebug("There is an existing cart for this visit");
+		}
+		if($this->isGoalAnOrder)
+		{
+			// If Order, make sure that we don't record the same order twice by using the order ID as a buster
+			$orderIdHash = substr(md5($this->orderId), 0, 8);
+			$orderIdNumeric = base_convert($orderIdHash, 16, 10);
+			$goal['idgoal'] = self::IDGOAL_ORDER;
+			$goal['idorder'] = $this->orderId; 
+			$goal['buster'] = $orderIdNumeric;
+			$goal['revenue_subtotal'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_st', false, 'float', $this->request));
+			$goal['revenue_tax'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_tx', false, 'float', $this->request));
+			$goal['revenue_shipping'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_sh', false, 'float', $this->request));
+			$goal['revenue_discount'] = $this->getRevenue(Piwik_Common::getRequestVar('ec_dt', false, 'float', $this->request));
+			
+			$debugMessage = 'The conversion is an Ecommerce order';
+		}
+		// If Cart update, select current items in the previous Cart
+		else
+		{
+			$goal['buster'] = 0;
+			$goal['idgoal'] = self::IDGOAL_CART;
+			$debugMessage = 'The conversion is an Ecommerce Cart Update';
+		}
+		$goal['revenue'] = $this->getRevenue(Piwik_Common::getRequestVar('revenue', 0, 'float', $this->request)); 
+		
+		printDebug($debugMessage . ':' . var_export($goal, true));
+
+		// INSERT or Sync items in the Cart / Order for this visit & order
+		$items = $this->getEcommerceItemsFromRequest();
+		if($items === false)
+		{
+			return;
+		}
+		
+		$itemsCount = 0;
+		foreach($items as $item)
+		{
+			$itemsCount += $item[self::INDEX_ITEM_QUANTITY];
+		}
+		$goal['items'] = $itemsCount;
+		
+		// If there is already a cart for this visit
+		// 1) If conversion is Order, we update the cart into an Order
+		// 2) If conversion is Cart Update, we update the cart
+		$recorded = $this->recordGoal($goal, $this->isThereExistingCartInVisit, $updateWhere);
+		if($recorded)
+		{
+			$this->recordEcommerceItems($goal, $items);
+		}
+	}
+	
+	/**
+	 * Returns Items read from the request string
+	 * @return array|false
+	 */
+	protected function getEcommerceItemsFromRequest()
+	{
+		$items = Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('ec_items', '', 'string', $this->request));
+		if(empty($items))
+		{
+			printDebug("There are no Ecommerce items in the request");
+			// we still record an Ecommerce order without any item in it
+			return array();
+		}
+		$items = json_decode($items, $assoc = true);
+		if(!is_array($items))
+		{
+			printDebug("Error while json_decode the Ecommerce items = ".var_export($items, true));
+			return false;
+		}
+		
+		$cleanedItems = $this->getCleanedEcommerceItems($items);
+		return $cleanedItems;
+	}
+	
+	/**
+	 * Loads the Ecommerce items from the request and records them in the DB
+	 * 
+	 * @param array $goal
+	 * @return int $items Number of items in the cart
+	 */
+	protected function recordEcommerceItems($goal, $items)
+	{
+		$itemBySku = array();
+		foreach($items as $item)
+		{
+			$itemBySku[$item[0]] = $item;
+		}
+//		var_dump($items); echo "Items by SKU:";var_dump($itemBySku);
+
+		// Select all items currently in the Cart if any
+		$sql = "SELECT idaction_sku, idaction_name, idaction_category, price, quantity
+				FROM ". Piwik_Common::prefixTable('log_conversion_item') . "
+				WHERE idvisit = ?
+					AND idorder = ?
+					AND deleted = 0";
+		$bind = array($goal['idvisit'], isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART);
+		$itemsInDb = Piwik_Tracker::getDatabase()->fetchAll($sql, $bind);
+		
+		// Look at which items need to be deleted, which need to be added or updated, based on the SKU
+		$skuFoundInDb = $itemsToUpdate = array();
+		foreach($itemsInDb as $itemInDb)
+		{
+			$skuFoundInDb[] = $itemInDb['idaction_sku'];
+			
+			// Ensure price comparisons will have the same assumption
+			$itemInDb['price'] = $this->getRevenue($itemInDb['price']);
+			$itemInDb = array_values($itemInDb);
+			
+			// Cast all as string, because what comes out of the fetchAll() are strings
+			$itemInDb = $this->getItemRowCast($itemInDb);
+		
+			//Item in the cart in the DB, but not anymore in the cart
+			if(!isset($itemBySku[$itemInDb[0]]))
+			{
+				$itemsToUpdate[] = $itemInDb + array('deleted' => 1);
+				continue;
+			}
+			
+			$newItem = $itemBySku[$itemInDb[0]];
+			$newItem = $this->getItemRowCast($newItem);
+			
+			if(count($itemInDb) != count($newItem))
+			{
+				throw new Exception(" Item in DB and Item in cart have a different format, this is not expected... ".var_export($itemInDb, true) . var_export($newItem, true));
+			}
+//			echo "NEW ITEM:";var_dump($newItem); echo "ITEM IN DB:";var_dump($itemInDb);
+			if($newItem != $itemInDb)
+			{
+//				echo "The following item is out of date in the DB: DB VERSION:"; var_dump($itemInDb); echo "NEW ITEM:"; var_dump($newItem);
+				$itemsToUpdate[] = $newItem;
+			}
+		}
+		
+		// Items to UPDATE
+		$this->updateEcommerceItems($goal, $itemsToUpdate);
+		
+		// Items to INSERT
+		$itemsToInsert = array();
+		foreach($items as $item)
+		{
+			if(!in_array($item[0], $skuFoundInDb))
+			{
+				$itemsToInsert[] = $item;
+			}
+		}
+		$this->insertEcommerceItems($goal, $itemsToInsert);
+	}
+	
+	const INDEX_ITEM_SKU = 0;
+	const INDEX_ITEM_NAME = 1;
+	const INDEX_ITEM_CATEGORY = 2;
+	const INDEX_ITEM_PRICE = 3;
+	const INDEX_ITEM_QUANTITY = 4;
+	
+	/**
+	 * Reads items from the request, then looks up the names from the lookup table 
+	 * and returns a clean array of items ready for the database.
+	 * 
+	 * @param array $items
+	 * @return array $cleanedItems
+	 */
+	protected function getCleanedEcommerceItems($items)
+	{
+		// Clean up the items array
+		$cleanedItems = array();
+		foreach($items as $item)
+		{
+			$name = $category = false;
+			$price = 0;
+			$quantity = 1;
+			// items are passed in the request as an array: ( $sku, $name, $category, $price, $quantity )
+			if(empty($item[self::INDEX_ITEM_SKU])) { 
+				continue; 
+			}
+			
+			$sku = $item[self::INDEX_ITEM_SKU];
+			if(!empty($item[self::INDEX_ITEM_NAME])) { 
+				$name = $item[self::INDEX_ITEM_NAME];
+			}
+			if(!empty($item[self::INDEX_ITEM_CATEGORY])) { 
+				$category = $item[self::INDEX_ITEM_CATEGORY];
+			}
+			if(!empty($item[self::INDEX_ITEM_PRICE]) 
+				&& is_numeric($item[self::INDEX_ITEM_PRICE])) { 
+					$price = $this->getRevenue($item[self::INDEX_ITEM_PRICE]); 
+			}
+			if(!empty($item[self::INDEX_ITEM_QUANTITY]) 
+				&& is_numeric($item[self::INDEX_ITEM_QUANTITY])) { 
+					$quantity = (int)$item[self::INDEX_ITEM_QUANTITY];
+			}
+			
+			// self::INDEX_ITEM_* are in order
+			$cleanedItems[] = array( $sku, $name, $category, $price, $quantity );
+		}
+		
+		// Lookup Item SKUs, Names & Categories Ids
+		$actionsToLookup = array();
+		foreach($cleanedItems as $item)
+		{
+			list($sku, $name, $category, $price, $quantity) = $item;
+			$actionsToLookup[] = array($sku, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_SKU);
+			$actionsToLookup[] = array($name, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_NAME); 
+			$actionsToLookup[] = array($category, Piwik_Tracker_Action::TYPE_ECOMMERCE_ITEM_CATEGORY); 
+		}
+		
+		$actionsLookedUp = Piwik_Tracker_Action::loadActionId($actionsToLookup);
+//		var_dump($actionsLookedUp);
+
+		// Replace SKU, name & category by their ID action
+		foreach($cleanedItems as $index => &$item)
+		{
+			list($sku, $name, $category, $price, $quantity) = $item;
+			
+			// SKU
+			$item[0] = $actionsLookedUp[ $index * 3 + self::INDEX_ITEM_SKU][2];
+			// Name
+			$item[1] = $actionsLookedUp[ $index * 3 + self::INDEX_ITEM_NAME][2];
+			// Category
+			$item[2] = $actionsLookedUp[ $index * 3 + self::INDEX_ITEM_CATEGORY][2];
+		}
+		return $cleanedItems;
+	}
+	
+	/**
+	 * Updates the cart items in the DB 
+	 * that have been modified since the last cart update
+	 */
+	protected function updateEcommerceItems($goal, $itemsToUpdate)
+	{
+		if(empty($itemsToUpdate))
+		{
+			return;
+		}
+		printDebug("Ecommerce items that are updated in the cart:");
+		printDebug($itemsToUpdate);
+		
+		foreach($itemsToUpdate as $item)
+		{
+			$newRow = $this->getItemRowEnriched($goal, $item);
+			$updateParts = $sqlBind = array();
+			foreach($newRow AS $name => $value)
+			{
+				$updateParts[] = $name." = ?";
+				$sqlBind[] = $value;
+			}
+			$sql = 'UPDATE ' . Piwik_Common::prefixTable('log_conversion_item') . "	
+					SET ".implode($updateParts, ', ')."
+						WHERE idvisit = ?
+							AND idorder = ? 
+							AND idaction_sku = ?";
+			$sqlBind[] = $newRow['idvisit'];
+			$sqlBind[] = $newRow['idorder'];
+			$sqlBind[] = $newRow['idaction_sku'];
+			Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
+		}
+	}
+	
+	/**
+	 * Inserts in the cart in the DB the new items 
+	 * that were not previously in the cart
+	 */
+	protected function insertEcommerceItems($goal, $itemsToInsert)
+	{
+		if(empty($itemsToInsert))
+		{
+			return;
+		}
+		printDebug("Ecommerce items that are added to the cart/order");
+		printDebug($itemsToInsert);
+		
+		$sql = "INSERT INTO " . Piwik_Common::prefixTable('log_conversion_item') . "
+					(idaction_sku, idaction_name, idaction_category, price, quantity, deleted, 
+					idorder, idsite, idvisitor, server_time, idvisit) 
+					VALUES ";
+		$i = 0;
+		$bind = array();
+		foreach($itemsToInsert as $item)
+		{
+			if($i > 0) { $sql .= ','; }
+			
+			$newRow = array_values($this->getItemRowEnriched($goal, $item));
+			$sql .= " ( ". Piwik_Common::getSqlStringFieldsArray($newRow) . " ) ";
+			$i++;
+			$bind = array_merge($bind, $newRow);
+		}
+		Piwik_Tracker::getDatabase()->query($sql, $bind);
+		printDebug($sql);printDebug($bind);
+	}
+	
+	protected function getItemRowEnriched($goal, $item)
+	{
+		$newRow = array(
+			'idaction_sku' => $item[0],
+			'idaction_name' => $item[1],
+			'idaction_category' => $item[2],
+			'price' => $item[3],
+			'quantity' => $item[4],
+			'deleted' => isset($item['deleted']) ? $item['deleted'] : 0, //deleted
+			'idorder' => isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART, //idorder = 0 in log_conversion_item for carts
+			'idsite' => $goal['idsite'],
+			'idvisitor' => $goal['idvisitor'],
+			'server_time' => $goal['server_time'],
+			'idvisit' => $goal['idvisit']
+		);
+		return $newRow;
+	}
+	/**
+	 * Records a standard non-Ecommerce goal in the DB (URL/Title matching), 
+	 * linking the conversion to the action that triggered it
+	 */
+	protected function recordStandardGoals($goal, $action, $visitorInformation)
+	{
 		foreach($this->convertedGoals as $convertedGoal)
 		{
 			printDebug("- Goal ".$convertedGoal['idgoal'] ." matched. Recording...");
 			$newGoal = $goal;
 			$newGoal['idgoal'] = $convertedGoal['idgoal'];
 			$newGoal['url'] = $convertedGoal['url'];
-			$newGoal['revenue'] = $convertedGoal['revenue'];
+			$newGoal['revenue'] = $this->getRevenue($convertedGoal['revenue']);
 			
 			if(!is_null($action))
 			{
-				$newGoal['idaction_url'] = $action->getIdActionUrl();
+				$newGoal['idaction_url'] = (int)$action->getIdActionUrl();
 				$newGoal['idlink_va'] = $action->getIdLinkVisitAction();
 			}
 
@@ -258,17 +678,68 @@ class Piwik_Tracker_GoalManager
 			$newGoal['buster'] = $convertedGoal['allow_multiple'] == 0 
 										? '0' 
 										: $visitorInformation['visit_last_action_time'];
-			$newGoalDebug = $newGoal;
-			$newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']);
-			printDebug($newGoalDebug);
+										
+			$this->recordGoal($newGoal);
+		}
+	}
+	/**
+	 * Helper function used by other record* methods which will INSERT or UPDATE the conversion in the DB
+	 * 
+	 * @param array $newGoal
+	 * @param bool $mustUpdateNotInsert If set to true, the previous conversion will be UPDATEd. This is used for the Cart Update conversion (only one cart per visit)
+	 * @param array $updateWhere
+	 */
+	protected function recordGoal($newGoal, $mustUpdateNotInsert = false, $updateWhere = array())
+	{
+		$newGoalDebug = $newGoal;
+		$newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']);
+		printDebug($newGoalDebug);
 
-			$fields = implode(", ", array_keys($newGoal));
-			$bindFields = substr(str_repeat( "?,",count($newGoal)),0,-1);
-			
-			$sql = "INSERT IGNORE INTO " . Piwik_Common::prefixTable('log_conversion') . "	
+		$fields = implode(", ", array_keys($newGoal));
+		$bindFields = Piwik_Common::getSqlStringFieldsArray($newGoal);
+		
+		if($mustUpdateNotInsert)
+		{
+			$updateParts = $sqlBind = $updateWhereParts = array();
+			foreach($newGoal AS $name => $value)
+			{
+				$updateParts[] = $name." = ?";
+				$sqlBind[] = $value;
+			}
+			foreach($updateWhere as $name => $value)
+			{
+				$updateWhereParts[] = $name." = ?";
+				$sqlBind[] = $value;
+			}
+			$sql = 'UPDATE  ' . Piwik_Common::prefixTable('log_conversion') . "	
+					SET ".implode($updateParts, ', ')."
+						WHERE ".implode($updateWhereParts, ' AND ');
+			Piwik_Tracker::getDatabase()->query($sql, $sqlBind);
+			return true;
+		}
+		else
+		{
+			$sql = 'INSERT IGNORE INTO ' . Piwik_Common::prefixTable('log_conversion') . "	
 					($fields) VALUES ($bindFields) ";
 			$bind = array_values($newGoal);
-			Piwik_Tracker::getDatabase()->query($sql, $bind);
+			$result = Piwik_Tracker::getDatabase()->query($sql, $bind);
+			
+			// If a record was inserted, we return true
+			return Piwik_Tracker::getDatabase()->rowCount($result) > 0;
 		}
 	}
+	
+	/**
+	 * Casts the item array so that array comparisons work nicely
+	 */
+	protected function getItemRowCast($row)
+	{			
+		return array(
+				(string)(int)$row[0],
+				(string)(int)$row[1],
+				(string)(int)$row[2],
+				(string)$row[3],
+				(string)$row[4],
+		);
+	}
 }
diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php
index 089477439d7811a3d408711e7ea596f557f16286..1162ea15853c452c787022abe6b687959829764e 100644
--- a/core/Tracker/Visit.php
+++ b/core/Tracker/Visit.php
@@ -50,6 +50,11 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 	protected $ip;
 	// via setForcedVisitorId()
 	protected $forcedVisitorId;
+	
+	/**
+	 * @var Piwik_Tracker_GoalManager
+	 */
+	protected $goalManager;
 
 	const TIME_IN_PAST_TO_SEARCH_FOR_VISITOR = 86400;
 
@@ -132,22 +137,36 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			return;
 		}
 		$this->visitorCustomVariables = $this->getCustomVariables();
-		$goalManager = new Piwik_Tracker_GoalManager();
-		$someGoalsConverted = false;
-		$idActionUrl = $idActionName = 0;
+		$this->goalManager = new Piwik_Tracker_GoalManager();
+		
+		$someGoalsConverted = $visitIsConverted = false;
+		$idActionUrl = $idActionName = false;
 		$action = null;
 
-		$idGoal = Piwik_Common::getRequestVar('idgoal', 0, 'int', $this->request);
-		$requestIsManualGoalConversion = ($idGoal > 0);
+		$this->goalManager->init($this->request);
+		
+		$requestIsManualGoalConversion = ($this->goalManager->idGoal > 0);
+		
+		if($this->goalManager->requestIsEcommerce)
+		{
+			$someGoalsConverted = true;
+			
+			// Mark the visit as Converted only if it is an order (not for a Cart update)
+			if($this->goalManager->isGoalAnOrder)
+			{
+				$visitIsConverted = true;
+			}
+		}
 		// this request is from the JS call to piwikTracker.trackGoal()
-		if($requestIsManualGoalConversion)
+		elseif($requestIsManualGoalConversion)
 		{
-			$someGoalsConverted = $goalManager->detectGoalId($this->idsite, $idGoal, $this->request);
+			$someGoalsConverted = $this->goalManager->detectGoalId($this->idsite);
+			$visitIsConverted = $someGoalsConverted;
 			// if we find a idgoal in the URL, but then the goal is not valid, this is most likely a fake request
 			if(!$someGoalsConverted)
 			{
-				printDebug('Invalid goal tracking request for goal id = '.$idGoal);
-				unset($goalManager);
+				printDebug('Invalid goal tracking request for goal id = '.$this->goalManager->idGoal);
+				unset($this->goalManager);
 				return;
 			}
 		}
@@ -156,11 +175,12 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 		{
 			$action = $this->newAction();
 			$this->handleAction($action);
-			$someGoalsConverted = $goalManager->detectGoalsMatchingUrl($this->idsite, $action);
-
+			$someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->idsite, $action);
+			$visitIsConverted = $someGoalsConverted;
+			
 			$action->loadIdActionNameAndUrl();
-			$idActionUrl = $action->getIdActionUrl();
-			$idActionName = $action->getIdActionName();
+			$idActionUrl = (int)$action->getIdActionUrl();
+			$idActionName = (int)$action->getIdActionName();
 		}
 
 		// the visitor and session
@@ -181,7 +201,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$idRefererActionUrl = $this->visitorInfo['visit_exit_idaction_url'];
 			$idRefererActionName = $this->visitorInfo['visit_exit_idaction_name'];
 			try {
-				$this->handleKnownVisit($idActionUrl, $idActionName, $someGoalsConverted);
+				$this->handleKnownVisit($idActionUrl, $idActionName, $visitIsConverted);
 				if(!is_null($action))
 				{
 					$action->record( 	$this->visitorInfo['idvisit'],
@@ -201,7 +221,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 				// In this case, we cancel the current conversion to be recorded:
 				if($requestIsManualGoalConversion)
 				{
-					$someGoalsConverted = false;
+					$someGoalsConverted = $visitIsConverted = false;
 				}
 				// When the row wasn't found in the logs, and this is a pageview or
 				// goal matching URL, we force a new visitor
@@ -219,7 +239,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 		if(!$this->isVisitorKnown()
 			|| !$isLastActionInTheSameVisit)
 		{
-			$this->handleNewVisit($idActionUrl, $idActionName, $someGoalsConverted);
+			$this->handleNewVisit($idActionUrl, $idActionName, $visitIsConverted);
 			if(!is_null($action))
 			{
 				$action->record( $this->visitorInfo['idvisit'], $this->visitorInfo['idvisitor'], 0, 0, 0 );
@@ -237,7 +257,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$refererCampaignName = Piwik_Common::getRequestVar('_rcn', '', 'string', $this->request);
 			$refererCampaignKeyword = Piwik_Common::getRequestVar('_rck', '', 'string', $this->request);
 
-			$goalManager->recordGoals( 	$this->idsite,
+			$this->goalManager->recordGoals( 
+										$this->idsite,
 										$this->visitorInfo,
 										$this->visitorCustomVariables,
 										$action,
@@ -247,7 +268,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 										$refererCampaignKeyword
 			);
 		}
-		unset($goalManager);
+		unset($this->goalManager);
 		unset($action);
 		$this->printCookie();
 	}
@@ -270,20 +291,10 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 		}
 		if(isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'])
 		{
-			switch($action->getActionType()) {
-				case Piwik_Tracker_Action::TYPE_ACTION_URL:
-					$type = "normal page view";
-					break;
-				case Piwik_Tracker_Action::TYPE_DOWNLOAD:
-					$type = "download";
-					break;
-				case Piwik_Tracker_Action::TYPE_OUTLINK:
-					$type = "outlink";
-					break;
-			}
-			printDebug("Action is a <u>$type</u>,".
-						"\n  Action name: ". $action->getActionName() . ",".
-						"\n  Action URL = ". $action->getActionUrl() );
+			$type = Piwik_Tracker_Action::getActionTypeName($action->getActionType());
+			printDebug("Action is a $type,
+						Action name =  ". $action->getActionName() . ",
+						Action URL = ". $action->getActionUrl() );
 		}
 	}
 
@@ -301,25 +312,21 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 	 * Tracker.knownVisitorInformation is triggered after saving the new visit data
 	 * Even data is an array with updated information about the visit
 	 */
-	protected function handleKnownVisit($idActionUrl, $idActionName, $someGoalsConverted)
+	protected function handleKnownVisit($idActionUrl, $idActionName, $visitIsConverted)
 	{
 		// gather information that needs to be updated
 		$valuesToUpdate = array();
-		if($someGoalsConverted)
+		if($visitIsConverted)
 		{
 			$valuesToUpdate['visit_goal_converted'] = 1;
 		}
 
 		$sqlActionUpdate = '';
-		if(!empty($idActionUrl))
+		if($idActionUrl !== false)
 		{
 			$valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl;
 			$sqlActionUpdate = "visit_total_actions = visit_total_actions + 1, ";
-			if(empty($idActionName))
-			{
-				$idActionName = 0;
-			}
-			$valuesToUpdate['visit_exit_idaction_name'] = $idActionName;
+			$valuesToUpdate['visit_exit_idaction_name'] = (int)$idActionName;
 		}
 
 		$datetimeServer = Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp());
@@ -344,6 +351,9 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$valuesToUpdate['idvisitor'] = Piwik_Common::hex2bin($idVisitor);
 		}
 
+		// Ecommerce buyer status
+		$valuesToUpdate['visit_goal_buyer'] = $this->goalManager->getBuyerType($this->visitorInfo['visit_goal_buyer']);
+		
 		// Custom Variables overwrite previous values on each page view
 		$valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables);
 
@@ -372,7 +382,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$updateParts[] = $name." = ?";
 			$sqlBind[] = $value;
 		}
-
+//var_dump($valuesToUpdate);exit;
 		$sqlQuery = "UPDATE ". Piwik_Common::prefixTable('log_visit')."
 						SET $sqlActionUpdate ".implode($updateParts, ', ')."
 						WHERE idsite = ?
@@ -413,7 +423,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 	 *
 	 * 2) Insert the visit information
 	 */
-	protected function handleNewVisit($idActionUrl, $idActionName, $someGoalsConverted)
+	protected function handleNewVisit($idActionUrl, $idActionName, $visitIsConverted)
 	{
 		printDebug("New Visit (IP = ".Piwik_IP::N2P($this->getVisitorIp()).")");
 
@@ -456,6 +466,14 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$daysSinceLastVisit = round(($this->getCurrentTimestamp() - $lastVisitTimestamp)/86400, $precision = 0);
 			if($daysSinceLastVisit < 0) $daysSinceLastVisit = 0;
 		}
+		
+		$daysSinceLastOrder = 0;
+		$lastOrderTimestamp = Piwik_Common::getRequestVar('_ects', 0, 'int', $this->request);
+		if($this->isTimestampValid($lastOrderTimestamp))
+		{
+			$daysSinceLastOrder = round(($this->getCurrentTimestamp() - $lastOrderTimestamp)/86400, $precision = 0);
+			if($daysSinceLastOrder < 0) $daysSinceLastOrder = 0;
+		}
 
 		// User settings
 		$userInfo = $this->getUserSettingsInformation();
@@ -479,6 +497,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			'visitor_returning' 		=> $visitCount > 1 || $this->isVisitorKnown() ? 1 : 0,
 			'visitor_count_visits'		=> $visitCount,
 			'visitor_days_since_last'	=> $daysSinceLastVisit,
+			'visitor_days_since_order'	=> $daysSinceLastOrder,
 			'visitor_days_since_first' 	=> $daysSinceFirstVisit,
 			'visit_first_action_time' 	=> Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
 			'visit_last_action_time' 	=> Piwik_Tracker::getDatetimeFromTimestamp($this->getCurrentTimestamp()),
@@ -488,7 +507,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			'visit_exit_idaction_name' 	=> (int)$idActionName,
 			'visit_total_actions' 		=> 1,
 			'visit_total_time' 			=> $defaultTimeOnePageVisit,
-			'visit_goal_converted'  	=> $someGoalsConverted ? 1: 0,
+			'visit_goal_converted'  	=> $visitIsConverted ? 1: 0,
+			'visit_goal_buyer'			=> $this->goalManager->getBuyerType(),
 			'referer_type' 				=> $refererInfo['referer_type'],
 			'referer_name' 				=> $refererInfo['referer_name'],
 			'referer_url' 				=> Piwik_Common::unsanitizeInputValue($refererInfo['referer_url']),
@@ -545,7 +565,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 		$this->visitorInfo['config_resolution'] = substr($this->visitorInfo['config_resolution'], 0, 9);
 
 		$fields = implode(", ", array_keys($this->visitorInfo));
-		$values = substr(str_repeat( "?,",count($this->visitorInfo)),0,-1);
+		$values = Piwik_Common::getSqlStringFieldsArray($this->visitorInfo);
 
 		$sql = "INSERT INTO ".Piwik_Common::prefixTable('log_visit'). " ($fields) VALUES ($values)";
 		$bind = array_values($this->visitorInfo);
@@ -895,10 +915,12 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 							visit_exit_idaction_name,
 							visitor_returning,
 							visitor_days_since_first,
+							visitor_days_since_order,
 							referer_name,
 							referer_keyword,
 							referer_type,
-							visitor_count_visits
+							visitor_count_visits,
+							visit_goal_buyer
 				FROM ".Piwik_Common::prefixTable('log_visit').
 				" WHERE ".$where."
 				ORDER BY visit_last_action_time DESC
@@ -918,8 +940,10 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 			$this->visitorInfo['visit_exit_idaction_name'] = $visitRow['visit_exit_idaction_name'];
 			$this->visitorInfo['visitor_returning'] = $visitRow['visitor_returning'];
 			$this->visitorInfo['visitor_days_since_first'] = $visitRow['visitor_days_since_first'];
+			$this->visitorInfo['visitor_days_since_order'] = $visitRow['visitor_days_since_order'];
 			$this->visitorInfo['visitor_count_visits'] = $visitRow['visitor_count_visits'];
-
+			$this->visitorInfo['visit_goal_buyer'] = $visitRow['visit_goal_buyer'];
+			
 			// Referer information will be potentially used for Goal Conversion attribution
 			$this->visitorInfo['referer_name'] = $visitRow['referer_name'];
 			$this->visitorInfo['referer_keyword'] = $visitRow['referer_keyword'];
@@ -930,7 +954,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
 						config_id = ".bin2hex($configId).",
 						idvisit = {$this->visitorInfo['idvisit']},
 						last action = ".date("r", $this->visitorInfo['visit_last_action_time']).",
-						first action = ".date("r", $this->visitorInfo['visit_first_action_time']) .")");
+						first action = ".date("r", $this->visitorInfo['visit_first_action_time']) .",
+						visit_goal_buyer' = ".$this->visitorInfo['visit_goal_buyer'].")");
 		}
 		else
 		{
diff --git a/core/Updates/1.5-b1.php b/core/Updates/1.5-b1.php
new file mode 100644
index 0000000000000000000000000000000000000000..3fcd4a6937a7e74c286a1feb7b8153bf17295d70
--- /dev/null
+++ b/core/Updates/1.5-b1.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ * @version $Id$
+ *
+ * @category Piwik
+ * @package Updates
+ */
+
+/**
+ * @package Updates
+ */
+class Piwik_Updates_1_5_b1 extends Piwik_Updates
+{
+	static function getSql($schema = 'Myisam')
+	{
+		$tables = Piwik::getTablesCreateSql();
+
+		return array(
+			'CREATE TABLE `'. Piwik_Common::prefixTable('log_conversion_item') .'` (
+												  idsite int(10) UNSIGNED NOT NULL,
+										  		  idvisitor BINARY(8) NOT NULL,
+										          server_time DATETIME NOT NULL,
+												  idvisit INTEGER(10) UNSIGNED NOT NULL,
+												  idorder varchar(100) NOT NULL,
+												  
+												  idaction_sku INTEGER(10) UNSIGNED NOT NULL,
+												  idaction_name INTEGER(10) UNSIGNED NOT NULL,
+												  idaction_category INTEGER(10) UNSIGNED NOT NULL,
+												  price FLOAT NOT NULL,
+												  quantity INTEGER(10) UNSIGNED NOT NULL,
+												  deleted TINYINT(1) UNSIGNED NOT NULL,
+												  
+												  PRIMARY KEY(idvisit, idorder, idaction_sku),
+										          INDEX index_idsite_servertime ( idsite, server_time )
+												)  DEFAULT CHARSET=utf8' => false,
+			'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_visit') .'`
+				 ADD  visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL AFTER visitor_days_since_last,
+				 ADD  visit_goal_buyer TINYINT(1) NOT NULL AFTER visit_goal_converted' => false,
+			'ALTER IGNORE TABLE `'. Piwik_Common::prefixTable('log_conversion') .'`
+				 ADD visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL AFTER visitor_days_since_first,
+				 ADD idorder varchar(100) default NULL AFTER buster,
+				 ADD items SMALLINT UNSIGNED DEFAULT NULL,
+				 ADD revenue_subtotal float default NULL,
+				 ADD revenue_tax float default NULL,
+				 ADD  revenue_shipping float default NULL,
+				 ADD revenue_discount float default NULL,
+				 ADD UNIQUE KEY unique_idorder (idorder)' => false,
+
+		);
+	}
+
+	static function update()
+	{
+		Piwik_Updater::updateDatabase(__FILE__, self::getSql());
+
+		$obsoleteFile = '/plugins/ExamplePlugin/API.php';
+		if(file_exists(PIWIK_INCLUDE_PATH . $obsoleteFile))
+		{
+			@unlink(PIWIK_INCLUDE_PATH . $obsoleteFile);
+		}
+
+		$obsoleteDirectories = array(
+			'/plugins/AdminHome',
+			'/plugins/Home',
+			'/plugins/PluginsAdmin',
+		);
+		foreach($obsoleteDirectories as $dir)
+		{
+			if(file_exists(PIWIK_INCLUDE_PATH . $dir))
+			{
+				Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . $dir, true);
+			}
+		}
+	}
+}
diff --git a/core/Version.php b/core/Version.php
index d0307259976a94d2b5ec7d0c7486eb5fe130140f..f9cabbd42d00cdeae71ab8507dffb576bbc881b9 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -17,5 +17,5 @@
  */
 final class Piwik_Version
 {
-	const VERSION = '1.4';
+	const VERSION = '1.5-b1';
 }
diff --git a/js/piwik.js b/js/piwik.js
index 510e8f312e9a7f2b4fac241cf39f0b3f97702351..0e1c0e1beaa61e05b1d802d48c54c05c78329b02 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -402,7 +402,7 @@ if (!this.JSON2) {
 	doNotTrack, setDoNotTrack,
 	addListener, enableLinkTracking, setLinkTrackingTimer,
 	setHeartBeatTimer, killFrame, redirectFile,
-	trackGoal, trackLink, trackPageView,
+	trackGoal, trackLink, trackPageView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate,
 	addPlugin, getTracker, getAsyncTracker
 */
 var
@@ -1010,6 +1010,9 @@ var
 				// Maximum number of custom variables
 				maxCustomVariables = 5,
 
+				// Ecommerce items
+				ecommerceItems = {},
+				
 				// Browser features via client-side data collection
 				browserFeatures = {},
 
@@ -1239,8 +1242,8 @@ var
 			 * Sets the Visitor ID cookie: either the first time loadVisitorIdCookie is called
 			 * or when there is a new visit or a new page view 
 			 */
-			function setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs) {
-				  setCookie(getCookieName('id'), uuid + '.' + createTs + '.' + visitCount + '.' + nowTs + '.' + lastVisitTs, configVisitorCookieTimeout, configCookiePath, configCookieDomain, cookieSecure);
+			function setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, lastEcommerceOrderTs) {
+				  setCookie(getCookieName('id'), uuid + '.' + createTs + '.' + visitCount + '.' + nowTs + '.' + lastVisitTs + '.' + lastEcommerceOrderTs, configVisitorCookieTimeout, configCookiePath, configCookieDomain, cookieSecure);
 			}
 			
 			/*
@@ -1285,6 +1288,9 @@ var
 						nowTs,
 
 						// last visit timestamp - blank = no previous visit
+						'',
+						
+						// last ecommerce order timestamp
 						''
 					];
 				}
@@ -1329,7 +1335,7 @@ var
 			 * with the standard parameters (plugins, resolution, url, referrer, etc.).
 			 * Sends the pageview and browser settings with every request in case of race conditions.
 			 */
-			function getRequest(request, customData, pluginMethod) {
+			function getRequest(request, customData, pluginMethod, currentEcommerceOrderTs) {
 				var i,
 					now = new Date(),
 					nowTs = Math.round(now.getTime() / 1000),
@@ -1339,6 +1345,7 @@ var
 					createTs,
 					currentVisitTs,
 					lastVisitTs,
+					lastEcommerceOrderTs,
 					referralTs,
 					referralUrl,
 					referralUrlMaxLength = 1024,
@@ -1370,6 +1377,15 @@ var
 				visitCount = id[3];
 				currentVisitTs = id[4];
 				lastVisitTs = id[5];
+				// case migrating from pre-1.5 cookies
+				if(!isDefined(id[6])) {
+					id[6] = "";
+				}
+				lastEcommerceOrderTs = id[6];
+				
+				if(!isDefined(currentEcommerceOrderTs)) {
+					currentEcommerceOrderTs = "";
+				}
 
 				campaignNameDetected = attributionCookie[0];
 				campaignKeywordDetected = attributionCookie[1];
@@ -1432,21 +1448,21 @@ var
 						setCookie(refname, JSON2.stringify(attributionCookie), configReferralCookieTimeout, configCookiePath, configCookieDomain, cookieSecure);
 					}
 				}
-
 				// build out the rest of the request
 				request += '&idsite=' + configTrackerSiteId +
 					'&rec=1' +
-					'&rand=' + Math.random() +
+					'&r=' + String(Math.random()).slice(2,8) + // keep the string to a minimum
 					'&h=' + now.getHours() + '&m=' + now.getMinutes() + '&s=' + now.getSeconds() +
 					'&url=' + encodeWrapper(purify(currentUrl)) +
-					'&urlref=' + encodeWrapper(purify(configReferrerUrl)) +
+					(configReferrerUrl.length ? '&urlref=' + encodeWrapper(purify(configReferrerUrl)) : '') +
 					'&_id=' + uuid + '&_idts=' + createTs + '&_idvc=' + visitCount + 
 					'&_idn=' + newVisitor + // currently unused
-					'&_rcn=' + encodeWrapper(campaignNameDetected) +
-					'&_rck=' + encodeWrapper(campaignKeywordDetected) +
+					(campaignNameDetected.length ? '&_rcn=' + encodeWrapper(campaignNameDetected) : '') +
+					(campaignKeywordDetected.length ? '&_rck=' + encodeWrapper(campaignKeywordDetected) : '') +
 					'&_refts=' + referralTs +
 					'&_viewts=' + lastVisitTs +
-					'&_ref=' + encodeWrapper(purify(referralUrl.slice(0, referralUrlMaxLength)))
+					(String(lastEcommerceOrderTs).length ? '&_ects=' + lastEcommerceOrderTs : '') +
+					(String(referralUrl).length ? '&_ref=' + encodeWrapper(purify(referralUrl.slice(0, referralUrlMaxLength))) : '')
 				;
 
 				// browser features
@@ -1463,9 +1479,12 @@ var
 					request += '&data=' + encodeWrapper(JSON2.stringify(configCustomData));
 				}
 
-				// Don't send custom variables if empty
 				if (customVariables) {
-					request += '&_cvar=' + encodeWrapper(JSON2.stringify(customVariables));
+					var customVariablesStringified = JSON2.stringify(customVariables);
+					// Don't sent empty custom variables {}
+					if(customVariablesStringified.length > 2) {
+						request += '&_cvar=' + encodeWrapper(customVariablesStringified);
+					}
 
 					// Don't save deleted custom variables in the cookie
 					for (i in customVariablesCopy) {
@@ -1480,7 +1499,7 @@ var
 				}
 
 				// update cookies
-				setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs);
+				setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, isDefined(currentEcommerceOrderTs) && String(currentEcommerceOrderTs).length ? currentEcommerceOrderTs : lastEcommerceOrderTs);
 				setCookie(sesname, '*', configSessionCookieTimeout, configCookiePath, configCookieDomain, cookieSecure);
 
 				// tracker plugin hook
@@ -1488,7 +1507,76 @@ var
 
 				return request;
 			}
+			
+			function logEcommerce(orderId, grandTotal, subTotal, tax, shipping, discount) {
+				var request = 'idgoal=0', 
+					lastEcommerceOrderTs,
+					now = new Date(),
+					items = [],
+					sku;
+				
+				if(String(orderId).length) {
+					request += '&ec_id=' + encodeWrapper(orderId);
+					// Record date of order in the visitor cookie
+					lastEcommerceOrderTs = Math.round(now.getTime() / 1000);
+				}
+				
+				request += '&revenue=' + grandTotal;
+				if(String(subTotal).length) {
+					request += '&ec_st=' + subTotal;
+				}
+				if(String(tax).length) {
+					request += '&ec_tx=' + tax;
+				}
+				if(String(shipping).length) {
+					request += '&ec_sh=' + shipping;
+				}
+				if(String(discount).length) {
+					request += '&ec_dt=' + discount;
+				}
+				if(ecommerceItems) {
+					// Removing the SKU index in the array before JSON encoding
+					for(sku in ecommerceItems) {
+						if (Object.prototype.hasOwnProperty.call(ecommerceItems, sku)) {
+							// Ensure name and category default to healthy value
+							if(!isDefined(ecommerceItems[sku][1])) {
+								ecommerceItems[sku][1] = "";
+							}
+							if(!isDefined(ecommerceItems[sku][2])) {
+								ecommerceItems[sku][2] = "";
+							}
+							// Set price to zero
+							if(!isDefined(ecommerceItems[sku][3])
+								|| String(ecommerceItems[sku][3]).length === 0) {
+								ecommerceItems[sku][3] = 0;
+							}
+							// Set quantity to 1
+							if(!isDefined(ecommerceItems[sku][4])
+								|| String(ecommerceItems[sku][4]).length === 0) {
+								ecommerceItems[sku][4] = 1;
+							}
+							items.push(ecommerceItems[sku]);
+						}
+					}
+					request += '&ec_items=' + encodeWrapper(JSON2.stringify(items));
+				}
+				request = getRequest(request, configCustomData, 'ecommerce', lastEcommerceOrderTs);
+				sendRequest(request, configTrackerPause);
+			}
+
+			function logEcommerceOrder(orderId, grandTotal, subTotal, tax, shipping, discount) {
+				if(String(orderId).length
+					&& isDefined(grandTotal)) {
+					logEcommerce(orderId, grandTotal, subTotal, tax, shipping, discount);
+				}	
+			}
 
+			function logEcommerceCartUpdate(grandTotal) {
+				if(isDefined(grandTotal)) {
+					logEcommerce("", grandTotal, "", "", "", "");
+				}
+			}
+			
 			/*
 			 * Log the page view / visit
 			 */
@@ -1848,8 +1936,8 @@ var
 				 * To access specific data point, you should use the other functions getAttributionReferrer* and getAttributionCampaign*
 				 * 
 				 * @return array Attribution array, Example use:
-				 *               1) Call JSON2.stringify(piwikTracker.getAttributionInfo()) 
-				 *               2) Pass this json encoded string to the Tracking API (php or java client): setAttributionInfo()
+				 *   1) Call JSON2.stringify(piwikTracker.getAttributionInfo()) 
+				 *   2) Pass this json encoded string to the Tracking API (php or java client): setAttributionInfo()
 				 */
 				getAttributionInfo: function() {
 					return loadReferrerAttributionCookie();
@@ -2292,7 +2380,55 @@ var
 				 */
 				trackPageView: function (customTitle, customData) {
 					logPageView(customTitle, customData);
+				},
+				
+				/**
+				 * Adds an item (product) that is in the current Cart or in the Ecommerce order.
+				 * This function is called for every item (product) in the Cart or the Order. 
+				 * The only required parameter is sku.
+				 * 
+				 * @param string sku (required) Item's SKU Code. This is the unique identifier for the product.
+				 * @param string name (optional) Item's name
+				 * @param string name (optional) Item's category
+				 * @param float price (optional) Item's price. If not specified, will default to 0
+				 * @param float quantity (optional) Item's quantity. If not specified, will default to 1
+				 */
+				addEcommerceItem: function(sku, name, category, price, quantity) {
+					if(sku.length) {
+						ecommerceItems[sku] = [ sku, name, category, price, quantity ]; 
+					}
+				},
+				
+				/**
+				 * Tracks an Ecommerce order.
+				 * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order.
+				 * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports.
+				 * Parameters orderId and grandTotal are required. For others, you can set empty string "" if you don't need specify them.
+				 * 
+				 * @param string|int orderId (required) Unique Order ID. 
+				 *                   This will be used to count this order only once in the event the order page is reloaded several times.
+				 *                   orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik.
+				 * @param float grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.)
+				 * @param float subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied) 
+				 * @param float tax (optional) Tax amount for this order
+				 * @param float shipping (optional) Shipping amount for this order
+				 * @param float discount (optional) Discounted amount in this order
+				 */
+				trackEcommerceOrder: function(orderId, grandTotal, subTotal, tax, shipping, discount) {
+					logEcommerceOrder(orderId, grandTotal, subTotal, tax, shipping, discount);
+				},
+				
+				/**
+				 * Tracks a Cart Update (add item, remove item, update item).
+				 * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart, including the items that haven't been updated since the last cart update.
+				 * Then you can call this function with the Cart grandTotal (typically the sum of all items' prices)
+				 * 
+				 * @param float grandTotal (required) Items (products) amount in the Cart
+				 */
+				trackEcommerceCartUpdate: function(grandTotal) {
+					logEcommerceCartUpdate(grandTotal);
 				}
+				
 			};
 		}
 
diff --git a/lang/en.php b/lang/en.php
index 2d56104b313cb3f82679688a738434bc096b06a3..53e36429771fad1352f49b36d04bacaadf5d1d0d 100644
--- a/lang/en.php
+++ b/lang/en.php
@@ -20,6 +20,7 @@ $translations = array(
 	'General_Never' => 'Never',
 	'General_Required' => '%s required',
 	'General_NotValid' => '%s is not valid',
+	'General_NotDefined' => '%s not defined',
 	'General_Id' => 'Id',
 	'General_Error' => 'Error',
 	'General_Warning' => 'Warning',
@@ -48,8 +49,10 @@ $translations = array(
 	'General_VisitType' => 'Visitor type',
 	'General_DaysSinceLastVisit' => 'Days since last visit',
 	'General_DaysSinceFirstVisit' => 'Days since first visit',
+	'General_DaysSinceLastEcommerceOrder' => 'Days since last Ecommerce order',
 	'General_NumberOfVisits' => 'Number of visits',
 	'General_VisitConvertedGoal' => 'Visit converted at least one Goal',
+	'General_EcommerceVisitStatus' => 'Visit Ecommerce status at the end of the visit. For example, to select all visits that have made an Ecommerce order, the API request would contain %s',
 	'General_VisitConvertedNGoals' => 'Visit converted %s Goals',
 	'General_NewVisitor' => 'New Visitor',
 	'General_ReturningVisitor' => 'Returning Visitor',
@@ -565,6 +568,9 @@ $translations = array(
 	'Goals_Pattern' => 'Pattern',
 	'Goals_ExceptionInvalidMatchingString' => 'If you choose \'exact match\', the matching string must be a URL starting with %s. For example, \'%s\'.',
 	'Goals_LearnMoreAboutGoalTrackingDocumentation' => 'Learn more about %s Tracking Goals in Piwik%s in the user documentation.',
+	'Goals_ProductSKU' => 'Product SKU',
+	'Goals_ProductName' => 'Product Name',
+	'Goals_ProductCategory' => 'Product Category',
 	'Installation_PluginDescription' => 'Installation process of Piwik. The Installation is usually done once only. If the configuration file config/config.inc.php is deleted, the installation will start again.',
 	'Installation_Installation' => 'Installation',
 	'Installation_InstallationStatus' => 'Installation status',
diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php
index b4ad4ba5f805f6dab259ded3304d8597d91384ef..a7f04e12546218f4e3c1a884e5d7b7b44c3e2aff 100644
--- a/libs/PiwikTracker/PiwikTracker.php
+++ b/libs/PiwikTracker/PiwikTracker.php
@@ -75,6 +75,7 @@ class PiwikTracker
     	$this->forcedDatetime = false;
     	$this->token_auth = false;
     	$this->attributionInfo = false;
+    	$this->ecommerceItems = array();
 
     	$this->requestCookie = '';
     	$this->idSite = $idSite;
@@ -192,6 +193,9 @@ class PiwikTracker
     	return $cookieDecoded[$id];
     }
     
+    
+    
+    
     /**
      * Sets the Browser language. Used to guess visitor countries when GeoIP is not enabled
      * 
@@ -252,6 +256,139 @@ class PiwikTracker
     	$url = $this->getUrlTrackAction($actionUrl, $actionType);
     	return $this->sendRequest($url); 
     }
+
+    /**
+     * Adds an item in the Ecommerce order.
+     * This can be called before doTrackEcommerceOrder to define individual items in the order.
+     * SKU parameter is mandatory. Other parameters are optional, you can set all or only some to false or empty string.
+     * Ecommerce items added via this function are automatically cleared when doTrackEcommerceOrder or getUrlTrackEcommerceOrder is called.
+     * 
+     * @param string $sku (required) SKU, Product identifier 
+     * @param string $name (optional) Product name
+     * @param string $category (optional) Product category
+     * @param float|int $price (optional) Individual product price (supports integer and decimal prices)
+     * @param int $quantity (optional) Product quantity. If not specified, will default to 1 in the Reports 
+     */
+    public function addEcommerceItem($sku, $name = false, $category = false, $price = false, $quantity = false)
+    {
+    	if(empty($sku))
+    	{
+    		throw new Exception("You must specify a SKU for the Ecommerce item");
+    	}
+    	$this->ecommerceItems[$sku] = array( $sku, $name, $category, $price, $quantity );
+    }
+    
+    /**
+	 * Tracks a Cart Update (add item, remove item, update item).
+	 * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart, including the items that haven't been updated since the last cart update.
+	 * 
+	 * @param float $grandTotal Cart grandTotal (typically the sum of all items' prices)
+	 */ 
+    public function doTrackEcommerceCartUpdate($grandTotal)
+    {
+    	$url = $this->getUrlTrackEcommerceCartUpdate($grandTotal);
+    	return $this->sendRequest($url); 
+    }
+    
+    /**
+	 * Tracks an Ecommerce order.
+	 * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order.
+	 * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports.
+	 * Only parameters $orderId and $grandTotal are required. 
+	 * 
+	 * @param string|int $orderId (required) Unique Order ID. 
+	 * 				This will be used to count this order only once in the event the order page is reloaded several times.
+	 * 				orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik.
+	 * @param float $grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.)
+	 * @param float $subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied) 
+	 * @param float $tax (optional) Tax amount for this order
+	 * @param float $shipping (optional) Shipping amount for this order
+	 * @param float $discount (optional) Discounted amount in this order
+     */
+    public function doTrackEcommerceOrder($orderId, $grandTotal, $subTotal = false, $tax = false, $shipping = false, $discount = false)
+    {
+    	$url = $this->getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount);
+    	return $this->sendRequest($url); 
+    }
+    
+    /**
+     * Returns URL used to track Ecommerce Cart updates
+     * Calling this function will reinitializes the property ecommerceItems to empty array 
+     * so items will have to be added again via addEcommerceItem()  
+     * @ignore
+     */
+    public function getUrlTrackEcommerceCartUpdate($grandTotal)
+    {
+    	$url = $this->getUrlTrackEcommerce($grandTotal);
+    	return $url;
+    }
+    
+    /**
+     * Returns URL used to track Ecommerce Orders
+     * Calling this function will reinitializes the property ecommerceItems to empty array 
+     * so items will have to be added again via addEcommerceItem()  
+     * @ignore
+     */
+    public function getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal = false, $tax = false, $shipping = false, $discount = false)
+    {
+    	if(empty($orderId))
+    	{
+    		throw new Exception("You must specifiy an orderId for the Ecommerce order");
+    	}
+    	$url = $this->getUrlTrackEcommerce($grandTotal, $subTotal, $tax, $shipping, $discount);
+    	$url .= '&ec_id=' . urlencode($orderId);
+    	
+    	return $url;
+    }
+    
+    /**
+     * Returns URL used to track Ecommerce orders
+     * Calling this function will reinitializes the property ecommerceItems to empty array 
+     * so items will have to be added again via addEcommerceItem()  
+     * @ignore
+     */
+    protected function getUrlTrackEcommerce($grandTotal, $subTotal = false, $tax = false, $shipping = false, $discount = false)
+    {
+    	if(!is_numeric($grandTotal))
+    	{
+    		throw new Exception("You must specifiy a grandTotal for the Ecommerce order (or Cart update)");
+    	}
+    	
+    	$url = $this->getRequest( $this->idSite );
+    	$url .= '&idgoal=0';
+    	if(!empty($grandTotal))
+    	{
+    		$url .= '&revenue='.$grandTotal;
+    	}
+    	if(!empty($subTotal))
+    	{
+    		$url .= '&ec_st='.$subTotal;
+    	}
+    	if(!empty($tax))
+    	{
+    		$url .= '&ec_tx='.$tax;
+    	}
+    	if(!empty($shipping))
+    	{
+    		$url .= '&ec_sh='.$shipping;
+    	}
+    	if(!empty($discount))
+    	{
+    		$url .= '&ec_dt='.$discount;
+    	}
+    	if(!empty($this->ecommerceItems))
+    	{
+    		// Removing the SKU index in the array before JSON encoding
+    		$items = array();
+    		foreach($this->ecommerceItems as $item)
+    		{
+    			$items[] = $item;
+    		}
+    		$this->ecommerceItems = array();
+    		$url .= '&ec_items='. urlencode(json_encode($items));
+    	}
+    	return $url;
+    }
     
     /**
      * @see doTrackPageView()
@@ -443,6 +580,15 @@ class PiwikTracker
     	$this->hasCookies = $bool ;
     }
     
+    /**
+     * Will append a custom string at the end of the Tracking request. 
+     * @param string $string
+     */
+    public function setDebugStringAppend( $string )
+    {
+    	$this->DEBUG_APPEND_URL = $string;
+    }
+    
     /**
      * Sets visitor browser supported plugins 
      *
diff --git a/piwik.js b/piwik.js
index 5872246f71b99c4db512eab05dcd0393309019e8..7f0cbabdc6c6da64fee4f44cc6051f34bd496ba4 100644
--- a/piwik.js
+++ b/piwik.js
@@ -14,13 +14,14 @@ return typeof f==="function"?m({"":n},""):n}throw new SyntaxError("JSON.parse")}
 k()})}else{if(d.attachEvent){d.attachEvent("onreadystatechange",function i(){if(d.readyState==="complete"){d.detachEvent("onreadystatechange",i);k()}});if(d.documentElement.doScroll&&H===H.top){(function i(){if(!h){try{d.documentElement.doScroll("left")}catch(K){setTimeout(i,0);return}k()}}())}}}if((new RegExp("WebKit")).test(j.userAgent)){J=setInterval(function(){if(h||/loaded|complete/.test(d.readyState)){clearInterval(J);k()}},10)}t(H,"load",k,false)}function f(){var i="";try{i=H.top.document.referrer}catch(K){if(H.parent){try{i=H.parent.document.referrer}catch(J){i=""}}}if(i===""){i=d.referrer}return i}function A(i){var K=new RegExp("^([a-z]+):"),J=K.exec(i);return J?J[1]:null}function y(i){var K=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),J=K.exec(i);return J?J[1]:i}function p(K,J){var N=new RegExp("^(?:https?|ftp)(?::/*(?:[^?]+)[?])([^#]+)"),M=N.exec(K),L=new RegExp("(?:^|&)"+J+"=([^&]*)"),i=M?L.exec(M[1]):0;return i?I(i[1]):""}function s(O,L,K,N,J,M){var i;if(K){i=new Date();
 i.setTime(i.getTime()+K)}d.cookie=O+"="+e(L)+(K?";expires="+i.toGMTString():"")+";path="+(N?N:"/")+(J?";domain="+J:"")+(M?";secure":"")}function F(K){var i=new RegExp("(^|;)[ ]*"+K+"=([^;]*)"),J=i.exec(d.cookie);return J?I(J[2]):0}function r(i){return unescape(e(i))}function u(Z){var L=function(W,i){return(W<<i)|(W>>>(32-i))},aa=function(ag){var af="",ae,W;for(ae=7;ae>=0;ae--){W=(ag>>>(ae*4))&15;af+=W.toString(16)}return af},O,ac,ab,K=[],S=1732584193,Q=4023233417,P=2562383102,N=271733878,M=3285377520,Y,X,V,U,T,ad,J,R=[];Z=r(Z);J=Z.length;for(ac=0;ac<J-3;ac+=4){ab=Z.charCodeAt(ac)<<24|Z.charCodeAt(ac+1)<<16|Z.charCodeAt(ac+2)<<8|Z.charCodeAt(ac+3);R.push(ab)}switch(J&3){case 0:ac=2147483648;break;case 1:ac=Z.charCodeAt(J-1)<<24|8388608;break;case 2:ac=Z.charCodeAt(J-2)<<24|Z.charCodeAt(J-1)<<16|32768;break;case 3:ac=Z.charCodeAt(J-3)<<24|Z.charCodeAt(J-2)<<16|Z.charCodeAt(J-1)<<8|128;break}R.push(ac);while((R.length&15)!==14){R.push(0)}R.push(J>>>29);R.push((J<<3)&4294967295);for(O=0;O<R.length;
 O+=16){for(ac=0;ac<16;ac++){K[ac]=R[O+ac]}for(ac=16;ac<=79;ac++){K[ac]=L(K[ac-3]^K[ac-8]^K[ac-14]^K[ac-16],1)}Y=S;X=Q;V=P;U=N;T=M;for(ac=0;ac<=19;ac++){ad=(L(Y,5)+((X&V)|(~X&U))+T+K[ac]+1518500249)&4294967295;T=U;U=V;V=L(X,30);X=Y;Y=ad}for(ac=20;ac<=39;ac++){ad=(L(Y,5)+(X^V^U)+T+K[ac]+1859775393)&4294967295;T=U;U=V;V=L(X,30);X=Y;Y=ad}for(ac=40;ac<=59;ac++){ad=(L(Y,5)+((X&V)|(X&U)|(V&U))+T+K[ac]+2400959708)&4294967295;T=U;U=V;V=L(X,30);X=Y;Y=ad}for(ac=60;ac<=79;ac++){ad=(L(Y,5)+(X^V^U)+T+K[ac]+3395469782)&4294967295;T=U;U=V;V=L(X,30);X=Y;Y=ad}S=(S+Y)&4294967295;Q=(Q+X)&4294967295;P=(P+V)&4294967295;N=(N+U)&4294967295;M=(M+T)&4294967295}ad=aa(S)+aa(Q)+aa(P)+aa(N)+aa(M);return ad.toLowerCase()}function o(K,i,J){if(K==="translate.googleusercontent.com"){if(J===""){J=i}i=p(i,"u");K=y(i)}else{if(K==="cc.bingj.com"||K==="webcache.googleusercontent.com"||K.slice(0,5)==="74.6."){i=d.links[0].href;K=y(i)}}return[K,i,J]}function l(J){var i=J.length;if(J.charAt(--i)==="."){J=J.slice(0,i)}if(J.slice(0,2)==="*."){J=J.slice(1)
-}return J}function E(aF,aD){var ao=o(d.domain,H.location.href,f()),aa=l(ao[0]),W=ao[1],aG=ao[2],L="GET",ad=aF||"",aZ=aD||"",aR,aY=d.title,aj="7z|aac|ar[cj]|as[fx]|avi|bin|csv|deb|dmg|doc|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|ms[ip]|od[bfgpst]|og[gv]|pdf|phps|png|ppt|qtm?|ra[mr]?|rpm|sea|sit|tar|t?bz2?|tgz|torrent|txt|wav|wm[av]|wpd||xls|xml|z|zip",aH=[aa],P=[],aI=[],aN=[],ac=500,K,am,an,aA,at=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aC=["pk_kwd","piwik_kwd","utm_term"],aJ="_pk_",S,aE,M,ax,a0=63072000000,ag=1800000,ab=15768000000,aW=d.location.protocol==="https",aO=false,U=100,aL=5,aq={},aw=false,T=false,Z,aV,au,aQ=u,aB,al;function aS(a1){var a2;if(an){a2=new RegExp("#.*");return a1.replace(a2,"")}return a1}function ai(a3,a1){var a4=A(a1),a2;if(a4){return a1}if(a1.slice(0,1)==="/"){return A(a3)+"://"+y(a3)+a1}a3=aS(a3);if((a2=a3.indexOf("?"))>=0){a3=a3.slice(0,a2)}if((a2=a3.lastIndexOf("/"))!==a3.length-1){a3=a3.slice(0,a2+1)}return a3+a1
-}function av(a4){var a2,a1,a3;for(a2=0;a2<aH.length;a2++){a1=l(aH[a2].toLowerCase());if(a4===a1){return true}if(a1.slice(0,1)==="."){if(a4===a1.slice(1)){return true}a3=a4.length-a1.length;if((a3>0)&&(a4.slice(a3)===a1)){return true}}}return false}function i(a1){var a2=new Image(1,1);a2.onLoad=function(){};a2.src=ad+(ad.indexOf("?")<0?"?":"&")+a1}function Y(a1){try{var a3=H.XDomainRequest?new H.XDomainRequest():H.XMLHttpRequest?new H.XMLHttpRequest():H.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;a3.open("POST",ad,true);a3.onreadystatechange=function(){if(this.readyState===4&&this.status!==200){i(a1)}};a3.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");a3.send(a1)}catch(a2){i(a1)}}function aU(a3,a2){var a1=new Date();if(!M){if(L==="POST"){Y(a3)}else{i(a3)}m=a1.getTime()+a2}}function Q(a1){return aJ+a1+"."+aZ+"."+aB}function az(){var a1=Q("testcookie");if(!b(j.cookieEnabled)){s(a1,"1");return F(a1)==="1"?"1":"0"}return j.cookieEnabled?"1":"0"
-}function ak(){aB=aQ((S||aa)+(aE||"/")).slice(0,4)}function X(){var a2=Q("cvar"),a1=F(a2);if(a1.length){a1=JSON2.parse(a1);if(n(a1)){return a1}}return{}}function aK(){if(aO===false){aO=X()}}function R(){var a1=new Date();Z=a1.getTime()}function N(a5,a2,a1,a4,a3){s(Q("id"),a5+"."+a2+"."+a1+"."+a4+"."+a3,a0,aE,S,aW)}function O(){var a2=new Date(),a1=Math.round(a2.getTime()/1000),a4=F(Q("id")),a3;if(a4){a3=a4.split(".");a3.unshift("0")}else{if(!al){al=aQ((j.userAgent||"")+(j.platform||"")+JSON2.stringify(aq)+a1).slice(0,16)}a3=["1",al,a1,0,a1,""]}return a3}function aM(){var a1=F(Q("ref"));if(a1.length){try{a1=JSON2.parse(a1);if(n(a1)){return a1}}catch(a2){}}return["","",0,""]}function ap(a3,bn,bo){var bl,a2=new Date(),a9=Math.round(a2.getTime()/1000),bq,bm,a5,bf,bi,a8,a6,bk,a4=1024,br,bc,bh=aO,be=Q("id"),ba=Q("ses"),bb=Q("ref"),bs=Q("cvar"),bg=O(),bd=F(ba),bj=aM(),bp=aR||W,a7,a1;if(M){s(be,"",-1,aE,S);s(ba,"",-1,aE,S);s(bs,"",-1,aE,S);s(bb,"",-1,aE,S);return""}bq=bg[0];bm=bg[1];bf=bg[2];a5=bg[3];
-bi=bg[4];a8=bg[5];a7=bj[0];a1=bj[1];a6=bj[2];bk=bj[3];if(!bd){a5++;a8=bi;if(!ax||!a7.length){for(bl in at){if(Object.prototype.hasOwnProperty.call(at,bl)){a7=p(bp,at[bl]);if(a7.length){break}}}for(bl in aC){if(Object.prototype.hasOwnProperty.call(aC,bl)){a1=p(bp,aC[bl]);if(a1.length){break}}}}br=y(aG);bc=bk.length?y(bk):"";if(br.length&&!av(br)&&(!ax||!bc.length||av(bc))){bk=aG}if(bk.length||a7.length){a6=a9;bj=[a7,a1,a6,aS(bk.slice(0,a4))];s(bb,JSON2.stringify(bj),ab,aE,S,aW)}}a3+="&idsite="+aZ+"&rec=1&rand="+Math.random()+"&h="+a2.getHours()+"&m="+a2.getMinutes()+"&s="+a2.getSeconds()+"&url="+e(aS(bp))+"&urlref="+e(aS(aG))+"&_id="+bm+"&_idts="+bf+"&_idvc="+a5+"&_idn="+bq+"&_rcn="+e(a7)+"&_rck="+e(a1)+"&_refts="+a6+"&_viewts="+a8+"&_ref="+e(aS(bk.slice(0,a4)));for(bl in aq){if(Object.prototype.hasOwnProperty.call(aq,bl)){a3+="&"+bl+"="+aq[bl]}}if(bn){a3+="&data="+e(JSON2.stringify(bn))}else{if(aA){a3+="&data="+e(JSON2.stringify(aA))}}if(aO){a3+="&_cvar="+e(JSON2.stringify(aO));for(bl in bh){if(Object.prototype.hasOwnProperty.call(bh,bl)){if(aO[bl][0]===""||aO[bl][1]===""){delete aO[bl]
-}}}s(bs,JSON2.stringify(aO),ag,aE,S,aW)}N(bm,bf,a5,a9,a8);s(ba,"*",ag,aE,S,aW);a3+=g(bo);return a3}function J(a4,a5){var a1=new Date(),a3=ap("action_name="+e(a4||aY),a5,"log");aU(a3,ac);if(K&&am&&!T){T=true;t(d,"click",R);t(d,"mouseup",R);t(d,"mousedown",R);t(d,"mousemove",R);t(d,"mousewheel",R);t(H,"DOMMouseScroll",R);t(H,"scroll",R);t(d,"keypress",R);t(d,"keydown",R);t(d,"keyup",R);t(H,"resize",R);t(H,"focus",R);t(H,"blur",R);Z=a1.getTime();setTimeout(function a2(){var a6=new Date(),a7;if((Z+am)>a6.getTime()){if(K<a6.getTime()){a7=ap("ping=1",a5,"ping");aU(a7,ac)}setTimeout(a2,am)}},am)}}function aT(a1,a4,a3){var a2=ap("idgoal="+a1+(a4?"&revenue="+a4:""),a3,"goal");aU(a2,ac)}function ah(a2,a1,a4){var a3=ap(a1+"="+e(aS(a2)),a4,"link");aU(a3,ac)}function ay(a3,a2){var a4,a1="(^| )(piwik[_-]"+a2;if(a3){for(a4=0;a4<a3.length;a4++){a1+="|"+a3[a4]}}a1+=")( |$)";return new RegExp(a1)}function aX(a4,a1,a5){if(!a5){return"link"}var a3=ay(aI,"download"),a2=ay(aN,"link"),a6=new RegExp("\\.("+aj+")([?&#]|$)","i");
-return a2.test(a4)?"link":(a3.test(a4)||a6.test(a1)?"download":0)}function V(a6){var a4,a2,a1;while(!!(a4=a6.parentNode)&&((a2=a6.tagName)!=="A"&&a2!=="AREA")){a6=a4}if(b(a6.href)){var a7=a6.hostname||y(a6.href),a8=a7.toLowerCase(),a3=a6.href.replace(a7,a8),a5=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript):","i");if(!a5.test(a3)){a1=aX(a6.className,a3,av(a8));if(a1){ah(a3,a1)}}}}function ae(a1){var a2,a3;a1=a1||H.event;a2=a1.which||a1.button;a3=a1.target||a1.srcElement;if(a1.type==="click"){if(a3){V(a3)}}else{if(a1.type==="mousedown"){if((a2===1||a2===2)&&a3){aV=a2;au=a3}else{aV=au=null}}else{if(a1.type==="mouseup"){if(a2===aV&&a3===au){V(a3)}aV=au=null}}}}function aP(a2,a1){if(a1){t(a2,"mouseup",ae,false);t(a2,"mousedown",ae,false)}else{t(a2,"click",ae,false)}}function ar(a2){if(!aw){aw=true;var a3,a1=ay(P,"ignore"),a4=d.links;if(a4){for(a3=0;a3<a4.length;a3++){if(!a1.test(a4[a3].className)){aP(a4[a3],a2)}}}}}function af(){var a1,a2,a3={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"};
-if(j.mimeTypes&&j.mimeTypes.length){for(a1 in a3){if(Object.prototype.hasOwnProperty.call(a3,a1)){a2=j.mimeTypes[a3[a1]];aq[a1]=(a2&&a2.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&b(j.javaEnabled)&&j.javaEnabled()){aq.java="1"}if(a(H.GearsFactory)){aq.gears="1"}aq.res=v.width+"x"+v.height;aq.cookie=az()}af();ak();return{getVisitorId:function(){return(O())[1]},getVisitorInfo:function(){return O()},getAttributionInfo:function(){return aM()},getAttributionCampaignName:function(){return aM()[0]},getAttributionCampaignKeyword:function(){return aM()[1]},getAttributionReferrerTimestamp:function(){return aM()[2]},getAttributionReferrerUrl:function(){return aM()[3]},setTrackerUrl:function(a1){ad=a1},setSiteId:function(a1){aZ=a1},setCustomData:function(a1,a2){if(n(a1)){aA=a1}else{if(!aA){aA=[]}aA[a1]=a2}},getCustomData:function(){return aA},setCustomVariable:function(a2,a1,a3){aK();if(a2>0&&a2<=aL){aO[a2]=[a1.slice(0,U),a3.slice(0,U)]}},getCustomVariable:function(a2){var a1;
-aK();a1=aO[a2];if(a1&&a1[0]===""){return}return aO[a2]},deleteCustomVariable:function(a1){if(this.getCustomVariable(a1)){this.setCustomVariable(a1,"","")}},setLinkTrackingTimer:function(a1){ac=a1},setDownloadExtensions:function(a1){aj=a1},addDownloadExtensions:function(a1){aj+="|"+a1},setDomains:function(a1){aH=q(a1)?[a1]:a1;aH.push(aa)},setIgnoreClasses:function(a1){P=q(a1)?[a1]:a1},setRequestMethod:function(a1){L=a1||"GET"},setReferrerUrl:function(a1){aG=a1},setCustomUrl:function(a1){aR=ai(W,a1)},setDocumentTitle:function(a1){aY=a1},setDownloadClasses:function(a1){aI=q(a1)?[a1]:a1},setLinkClasses:function(a1){aN=q(a1)?[a1]:a1},setCampaignNameKey:function(a1){at=q(a1)?[a1]:a1},setCampaignKeywordKey:function(a1){aC=q(a1)?[a1]:a1},discardHashTag:function(a1){an=a1},setCookieNamePrefix:function(a1){aJ=a1;aO=X()},setCookieDomain:function(a1){S=l(a1);ak()},setCookiePath:function(a1){aE=a1;ak()},setVisitorCookieTimeout:function(a1){a0=a1*1000},setSessionCookieTimeout:function(a1){ag=a1*1000},setReferralCookieTimeout:function(a1){ab=a1*1000
-},setConversionAttributionFirstReferrer:function(a1){ax=a1},setDoNotTrack:function(a1){M=a1&&j.doNotTrack},addListener:function(a2,a1){aP(a2,a1)},enableLinkTracking:function(a1){if(h){ar(a1)}else{C.push(function(){ar(a1)})}},setHeartBeatTimer:function(a3,a2){var a1=new Date();K=a1.getTime()+a3*1000;am=a2*1000},killFrame:function(){if(H.location!==H.top.location){H.top.location=H.location}},redirectFile:function(a1){if(H.location.protocol==="file:"){H.location=a1}},trackGoal:function(a1,a3,a2){aT(a1,a3,a2)},trackLink:function(a2,a1,a3){ah(a2,a1,a3)},trackPageView:function(a1,a2){J(a1,a2)}}}function c(){return{push:z}}t(H,"beforeunload",B,false);x();G=new E();for(D=0;D<_paq.length;D++){z(_paq[D])}_paq=new c();return{addPlugin:function(i,J){w[i]=J},getTracker:function(i,J){return new E(i,J)},getAsyncTracker:function(){return G}}}()),piwik_track,piwik_log=function(b,f,d,g){function a(h){try{return eval("piwik_"+h)}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);
-e.setCustomData(g);if(!!(c=a("tracker_pause"))){e.setLinkTrackingTimer(c)}if(!!(c=a("download_extensions"))){e.setDownloadExtensions(c)}if(!!(c=a("hosts_alias"))){e.setDomains(c)}if(!!(c=a("ignore_classes"))){e.setIgnoreClasses(c)}e.trackPageView();if((a("install_tracker"))){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}};
\ No newline at end of file
+}return J}function E(ab,ax){var M=o(d.domain,H.location.href,f()),aP=l(M[0]),a2=M[1],aD=M[2],aB="GET",L=ab||"",aT=ax||"",ao,ag=d.title,ai="7z|aac|ar[cj]|as[fx]|avi|bin|csv|deb|dmg|doc|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|ms[ip]|od[bfgpst]|og[gv]|pdf|phps|png|ppt|qtm?|ra[mr]?|rpm|sea|sit|tar|t?bz2?|tgz|torrent|txt|wav|wm[av]|wpd||xls|xml|z|zip",az=[aP],P=[],at=[],aa=[],ay=500,Q,ac,R,S,ak=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],af=["pk_kwd","piwik_kwd","utm_term"],a0="_pk_",U,a1,aV,an,Y=63072000000,Z=1800000,ap=15768000000,X=d.location.protocol==="https",O=false,aW=100,ae=5,aJ={},aU={},aG=false,aE=false,aC,au,V,aj=u,aF,am;function aX(a5){var a6;if(R){a6=new RegExp("#.*");return a5.replace(a6,"")}return a5}function aO(a7,a5){var a8=A(a5),a6;if(a8){return a5}if(a5.slice(0,1)==="/"){return A(a7)+"://"+y(a7)+a5}a7=aX(a7);if((a6=a7.indexOf("?"))>=0){a7=a7.slice(0,a6)}if((a6=a7.lastIndexOf("/"))!==a7.length-1){a7=a7.slice(0,a6+1)}return a7+a5
+}function aA(a8){var a6,a5,a7;for(a6=0;a6<az.length;a6++){a5=l(az[a6].toLowerCase());if(a8===a5){return true}if(a5.slice(0,1)==="."){if(a8===a5.slice(1)){return true}a7=a8.length-a5.length;if((a7>0)&&(a8.slice(a7)===a5)){return true}}}return false}function a4(a5){var a6=new Image(1,1);a6.onLoad=function(){};a6.src=L+(L.indexOf("?")<0?"?":"&")+a5}function aL(a5){try{var a7=H.XDomainRequest?new H.XDomainRequest():H.XMLHttpRequest?new H.XMLHttpRequest():H.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;a7.open("POST",L,true);a7.onreadystatechange=function(){if(this.readyState===4&&this.status!==200){a4(a5)}};a7.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");a7.send(a5)}catch(a6){a4(a5)}}function al(a7,a6){var a5=new Date();if(!aV){if(aB==="POST"){aL(a7)}else{a4(a7)}m=a5.getTime()+a6}}function aK(a5){return a0+a5+"."+aT+"."+aF}function N(){var a5=aK("testcookie");if(!b(j.cookieEnabled)){s(a5,"1");return F(a5)==="1"?"1":"0"}return j.cookieEnabled?"1":"0"
+}function av(){aF=aj((U||aP)+(a1||"/")).slice(0,4)}function W(){var a6=aK("cvar"),a5=F(a6);if(a5.length){a5=JSON2.parse(a5);if(n(a5)){return a5}}return{}}function K(){if(O===false){O=W()}}function aS(){var a5=new Date();aC=a5.getTime()}function T(a9,a6,a5,a8,a7,ba){s(aK("id"),a9+"."+a6+"."+a5+"."+a8+"."+a7+"."+ba,Y,a1,U,X)}function J(){var a6=new Date(),a5=Math.round(a6.getTime()/1000),a8=F(aK("id")),a7;if(a8){a7=a8.split(".");a7.unshift("0")}else{if(!am){am=aj((j.userAgent||"")+(j.platform||"")+JSON2.stringify(aU)+a5).slice(0,16)}a7=["1",am,a5,0,a5,"",""]}return a7}function i(){var a5=F(aK("ref"));if(a5.length){try{a5=JSON2.parse(a5);if(n(a5)){return a5}}catch(a6){}}return["","",0,""]}function ah(a7,bu,bv,a8){var bs,a6=new Date(),be=Math.round(a6.getTime()/1000),bx,bt,ba,bl,bp,bd,bn,bb,br,a9=1024,by,bh,bo=O,bj=aK("id"),bf=aK("ses"),bg=aK("ref"),bz=aK("cvar"),bm=J(),bi=F(bf),bq=i(),bw=ao||a2,bc,a5;if(aV){s(bj,"",-1,a1,U);s(bf,"",-1,a1,U);s(bz,"",-1,a1,U);s(bg,"",-1,a1,U);return""}bx=bm[0];
+bt=bm[1];bl=bm[2];ba=bm[3];bp=bm[4];bd=bm[5];if(!b(bm[6])){bm[6]=""}bn=bm[6];if(!b(a8)){a8=""}bc=bq[0];a5=bq[1];bb=bq[2];br=bq[3];if(!bi){ba++;bd=bp;if(!an||!bc.length){for(bs in ak){if(Object.prototype.hasOwnProperty.call(ak,bs)){bc=p(bw,ak[bs]);if(bc.length){break}}}for(bs in af){if(Object.prototype.hasOwnProperty.call(af,bs)){a5=p(bw,af[bs]);if(a5.length){break}}}}by=y(aD);bh=br.length?y(br):"";if(by.length&&!aA(by)&&(!an||!bh.length||aA(bh))){br=aD}if(br.length||bc.length){bb=be;bq=[bc,a5,bb,aX(br.slice(0,a9))];s(bg,JSON2.stringify(bq),ap,a1,U,X)}}a7+="&idsite="+aT+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+a6.getHours()+"&m="+a6.getMinutes()+"&s="+a6.getSeconds()+"&url="+e(aX(bw))+(aD.length?"&urlref="+e(aX(aD)):"")+"&_id="+bt+"&_idts="+bl+"&_idvc="+ba+"&_idn="+bx+(bc.length?"&_rcn="+e(bc):"")+(a5.length?"&_rck="+e(a5):"")+"&_refts="+bb+"&_viewts="+bd+(String(bn).length?"&_ects="+bn:"")+(String(br).length?"&_ref="+e(aX(br.slice(0,a9))):"");for(bs in aU){if(Object.prototype.hasOwnProperty.call(aU,bs)){a7+="&"+bs+"="+aU[bs]
+}}if(bu){a7+="&data="+e(JSON2.stringify(bu))}else{if(S){a7+="&data="+e(JSON2.stringify(S))}}if(O){var bk=JSON2.stringify(O);if(bk.length>2){a7+="&_cvar="+e(bk)}for(bs in bo){if(Object.prototype.hasOwnProperty.call(bo,bs)){if(O[bs][0]===""||O[bs][1]===""){delete O[bs]}}}s(bz,JSON2.stringify(O),Z,a1,U,X)}T(bt,bl,ba,be,bd,b(a8)&&String(a8).length?a8:bn);s(bf,"*",Z,a1,U,X);a7+=g(bv);return a7}function aN(a8,a7,bc,a9,a5,bf){var ba="idgoal=0",bb,a6=new Date(),bd=[],be;if(String(a8).length){ba+="&ec_id="+e(a8);bb=Math.round(a6.getTime()/1000)}ba+="&revenue="+a7;if(String(bc).length){ba+="&ec_st="+bc}if(String(a9).length){ba+="&ec_tx="+a9}if(String(a5).length){ba+="&ec_sh="+a5}if(String(bf).length){ba+="&ec_dt="+bf}if(aJ){for(be in aJ){if(Object.prototype.hasOwnProperty.call(aJ,be)){if(!b(aJ[be][1])){aJ[be][1]=""}if(!b(aJ[be][2])){aJ[be][2]=""}if(!b(aJ[be][3])||String(aJ[be][3]).length===0){aJ[be][3]=0}if(!b(aJ[be][4])||String(aJ[be][4]).length===0){aJ[be][4]=1}bd.push(aJ[be])}}ba+="&ec_items="+e(JSON2.stringify(bd))
+}ba=ah(ba,S,"ecommerce",bb);al(ba,ay)}function aM(a5,a9,a8,a7,a6,ba){if(String(a5).length&&b(a9)){aN(a5,a9,a8,a7,a6,ba)}}function aZ(a5){if(b(a5)){aN("",a5,"","","","")}}function ar(a8,a9){var a5=new Date(),a7=ah("action_name="+e(a8||ag),a9,"log");al(a7,ay);if(Q&&ac&&!aE){aE=true;t(d,"click",aS);t(d,"mouseup",aS);t(d,"mousedown",aS);t(d,"mousemove",aS);t(d,"mousewheel",aS);t(H,"DOMMouseScroll",aS);t(H,"scroll",aS);t(d,"keypress",aS);t(d,"keydown",aS);t(d,"keyup",aS);t(H,"resize",aS);t(H,"focus",aS);t(H,"blur",aS);aC=a5.getTime();setTimeout(function a6(){var ba=new Date(),bb;if((aC+ac)>ba.getTime()){if(Q<ba.getTime()){bb=ah("ping=1",a9,"ping");al(bb,ay)}setTimeout(a6,ac)}},ac)}}function aw(a5,a8,a7){var a6=ah("idgoal="+a5+(a8?"&revenue="+a8:""),a7,"goal");al(a6,ay)}function aR(a6,a5,a8){var a7=ah(a5+"="+e(aX(a6)),a8,"link");al(a7,ay)}function ad(a7,a6){var a8,a5="(^| )(piwik[_-]"+a6;if(a7){for(a8=0;a8<a7.length;a8++){a5+="|"+a7[a8]}}a5+=")( |$)";return new RegExp(a5)}function aQ(a8,a5,a9){if(!a9){return"link"
+}var a7=ad(at,"download"),a6=ad(aa,"link"),ba=new RegExp("\\.("+ai+")([?&#]|$)","i");return a6.test(a8)?"link":(a7.test(a8)||ba.test(a5)?"download":0)}function aI(ba){var a8,a6,a5;while(!!(a8=ba.parentNode)&&((a6=ba.tagName)!=="A"&&a6!=="AREA")){ba=a8}if(b(ba.href)){var bb=ba.hostname||y(ba.href),bc=bb.toLowerCase(),a7=ba.href.replace(bb,bc),a9=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript):","i");if(!a9.test(a7)){a5=aQ(ba.className,a7,aA(bc));if(a5){aR(a7,a5)}}}}function a3(a5){var a6,a7;a5=a5||H.event;a6=a5.which||a5.button;a7=a5.target||a5.srcElement;if(a5.type==="click"){if(a7){aI(a7)}}else{if(a5.type==="mousedown"){if((a6===1||a6===2)&&a7){au=a6;V=a7}else{au=V=null}}else{if(a5.type==="mouseup"){if(a6===au&&a7===V){aI(a7)}au=V=null}}}}function aH(a6,a5){if(a5){t(a6,"mouseup",a3,false);t(a6,"mousedown",a3,false)}else{t(a6,"click",a3,false)}}function aq(a6){if(!aG){aG=true;var a7,a5=ad(P,"ignore"),a8=d.links;if(a8){for(a7=0;a7<a8.length;a7++){if(!a5.test(a8[a7].className)){aH(a8[a7],a6)
+}}}}}function aY(){var a5,a6,a7={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"};if(j.mimeTypes&&j.mimeTypes.length){for(a5 in a7){if(Object.prototype.hasOwnProperty.call(a7,a5)){a6=j.mimeTypes[a7[a5]];aU[a5]=(a6&&a6.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&b(j.javaEnabled)&&j.javaEnabled()){aU.java="1"}if(a(H.GearsFactory)){aU.gears="1"}aU.res=v.width+"x"+v.height;aU.cookie=N()}aY();av();return{getVisitorId:function(){return(J())[1]},getVisitorInfo:function(){return J()},getAttributionInfo:function(){return i()},getAttributionCampaignName:function(){return i()[0]},getAttributionCampaignKeyword:function(){return i()[1]},getAttributionReferrerTimestamp:function(){return i()[2]},getAttributionReferrerUrl:function(){return i()[3]},setTrackerUrl:function(a5){L=a5
+},setSiteId:function(a5){aT=a5},setCustomData:function(a5,a6){if(n(a5)){S=a5}else{if(!S){S=[]}S[a5]=a6}},getCustomData:function(){return S},setCustomVariable:function(a6,a5,a7){K();if(a6>0&&a6<=ae){O[a6]=[a5.slice(0,aW),a7.slice(0,aW)]}},getCustomVariable:function(a6){var a5;K();a5=O[a6];if(a5&&a5[0]===""){return}return O[a6]},deleteCustomVariable:function(a5){if(this.getCustomVariable(a5)){this.setCustomVariable(a5,"","")}},setLinkTrackingTimer:function(a5){ay=a5},setDownloadExtensions:function(a5){ai=a5},addDownloadExtensions:function(a5){ai+="|"+a5},setDomains:function(a5){az=q(a5)?[a5]:a5;az.push(aP)},setIgnoreClasses:function(a5){P=q(a5)?[a5]:a5},setRequestMethod:function(a5){aB=a5||"GET"},setReferrerUrl:function(a5){aD=a5},setCustomUrl:function(a5){ao=aO(a2,a5)},setDocumentTitle:function(a5){ag=a5},setDownloadClasses:function(a5){at=q(a5)?[a5]:a5},setLinkClasses:function(a5){aa=q(a5)?[a5]:a5},setCampaignNameKey:function(a5){ak=q(a5)?[a5]:a5},setCampaignKeywordKey:function(a5){af=q(a5)?[a5]:a5
+},discardHashTag:function(a5){R=a5},setCookieNamePrefix:function(a5){a0=a5;O=W()},setCookieDomain:function(a5){U=l(a5);av()},setCookiePath:function(a5){a1=a5;av()},setVisitorCookieTimeout:function(a5){Y=a5*1000},setSessionCookieTimeout:function(a5){Z=a5*1000},setReferralCookieTimeout:function(a5){ap=a5*1000},setConversionAttributionFirstReferrer:function(a5){an=a5},setDoNotTrack:function(a5){aV=a5&&j.doNotTrack},addListener:function(a6,a5){aH(a6,a5)},enableLinkTracking:function(a5){if(h){aq(a5)}else{C.push(function(){aq(a5)})}},setHeartBeatTimer:function(a7,a6){var a5=new Date();Q=a5.getTime()+a7*1000;ac=a6*1000},killFrame:function(){if(H.location!==H.top.location){H.top.location=H.location}},redirectFile:function(a5){if(H.location.protocol==="file:"){H.location=a5}},trackGoal:function(a5,a7,a6){aw(a5,a7,a6)},trackLink:function(a6,a5,a7){aR(a6,a5,a7)},trackPageView:function(a5,a6){ar(a5,a6)},addEcommerceItem:function(a9,a5,a7,a6,a8){if(a9.length){aJ[a9]=[a9,a5,a7,a6,a8]}},trackEcommerceOrder:function(a5,a9,a8,a7,a6,ba){aM(a5,a9,a8,a7,a6,ba)
+},trackEcommerceCartUpdate:function(a5){aZ(a5)}}}function c(){return{push:z}}t(H,"beforeunload",B,false);x();G=new E();for(D=0;D<_paq.length;D++){z(_paq[D])}_paq=new c();return{addPlugin:function(i,J){w[i]=J},getTracker:function(i,J){return new E(i,J)},getAsyncTracker:function(){return G}}}()),piwik_track,piwik_log=function(b,f,d,g){function a(h){try{return eval("piwik_"+h)}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);if(!!(c=a("tracker_pause"))){e.setLinkTrackingTimer(c)}if(!!(c=a("download_extensions"))){e.setDownloadExtensions(c)}if(!!(c=a("hosts_alias"))){e.setDomains(c)}if(!!(c=a("ignore_classes"))){e.setIgnoreClasses(c)}e.trackPageView();if((a("install_tracker"))){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}};
\ No newline at end of file
diff --git a/plugins/API/API.php b/plugins/API/API.php
index b46f1328a00941b60f081f47f232b6fa4bbbe9d4..93b46a2e25853ca898ef265d7d34fa2682acfd6a 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -201,6 +201,25 @@ class Piwik_API_API
 				'acceptedValues' => '0, 1',
 		        'sqlSegment' => 'visit_goal_converted',
 	    );
+	    
+		$segments[] = array(
+		        'type' => 'metric',
+		        'category' => 'Visit',
+		        'name' => Piwik_Translate('General_EcommerceVisitStatus', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"'),
+		        'segment' => 'visitEcommerceStatus',
+				'acceptedValues' => implode(", ", self::$visitEcommerceStatus),
+		        'sqlSegment' => 'visit_goal_buyer',
+		        'sqlFilter' => array('Piwik_API_API', 'getVisitEcommerceStatus'),
+	    );
+	    
+	    $segments[] = array(
+		        'type' => 'metric',
+		        'category' => 'Visit',
+		        'name' => 'General_DaysSinceLastEcommerceOrder',
+		        'segment' => 'daysSinceLastEcommerceOrder',
+		        'sqlSegment' => 'visitor_days_since_order',
+	    );
+	    
 		foreach ($segments as &$segment)
 		{
 		    $segment['name'] = Piwik_Translate($segment['name']);
@@ -217,6 +236,32 @@ class Piwik_API_API
 		return $segments;
 	}
 	
+	static protected $visitEcommerceStatus = array(
+		Piwik_Tracker_GoalManager::TYPE_BUYER_NONE => 'none',
+		Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED => 'ordered',
+		Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART => 'abandonedCart',
+		Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART => 'orderedThenAbandonedCart',
+	);
+	
+	static public function getVisitEcommerceStatusFromId($id)
+	{
+		if(!isset(self::$visitEcommerceStatus[$id]))
+		{
+			throw new Exception("Unexpected ECommerce status value ");
+		}
+		return self::$visitEcommerceStatus[$id];
+	}
+	
+	static public function getVisitEcommerceStatus($status)
+	{
+		$id = array_search($status, self::$visitEcommerceStatus);
+		if($id === false)
+		{
+			throw new Exception("Invalid 'visitEcommerceStatus' segment value");
+		}
+		return $id;
+	}
+	
 	private function sortSegments($row1, $row2)
 	{
 		$columns = array('type', 'category', 'name', 'segment');
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index c93aff5ec6e72d59f89a96b50b690fe10beeaae9..789a96731dbfe16a96fc1830632743768b9dca6d 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -320,12 +320,13 @@ class Piwik_Actions extends Piwik_Plugin
 							count(distinct log_link_visit_action.idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`,
 							count(*) as `". Piwik_Archive::INDEX_PAGE_NB_HITS ."`
 					FROM ".Piwik_Common::prefixTable('log_link_visit_action')." as log_link_visit_action
-							LEFT JOIN ".Piwik_Common::prefixTable('log_action')." as log_action ON (log_link_visit_action.%s = idaction)
+							LEFT JOIN ".Piwik_Common::prefixTable('log_action')." as log_action 
+							ON (log_link_visit_action.%s = idaction)
 							$sqlJoinVisitTable
 					WHERE server_time >= ?
 						AND server_time <= ?
 						AND log_link_visit_action.idsite = ?
-				 		AND %s > 0
+						AND %s IS NOT NULL
 				 		$sqlSegmentWhere
 					GROUP BY idaction
 					ORDER BY `". Piwik_Archive::INDEX_PAGE_NB_HITS ."` DESC";
@@ -397,7 +398,7 @@ class Piwik_Actions extends Piwik_Plugin
 		$queryString = str_replace("%s", $sprintfParameter, $queryString);
 		$bind = array_merge(array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite ), $bind);
 		$resultSet = $archiveProcessing->db->query($queryString, $bind);
-		$modified = $this->updateActionsTableWithRowQuery($resultSet);
+		$modified = $this->updateActionsTableWithRowQuery($resultSet, $sprintfParameter);
 		return $modified;
 	}
 	protected function archiveDayRecordInDatabase($archiveProcessing)
@@ -522,11 +523,7 @@ class Piwik_Actions extends Piwik_Plugin
 		
 		if( empty($split) )
 		{
-		    if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
-		        $defaultName = self::$defaultActionNameWhenNotDefined;
-		    } else {
-		        $defaultName = self::$defaultActionUrlWhenNotDefined;
-		    }
+			$defaultName = self::getUnknownActionName($type);
 			return array( trim($defaultName) );
 		}
 
@@ -546,15 +543,31 @@ class Piwik_Actions extends Piwik_Plugin
 		return array_values( $split );
 	}
 	
+	static protected function getUnknownActionName($type)
+	{
+	    if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
+	        return self::$defaultActionNameWhenNotDefined;
+	    } 
+	    return self::$defaultActionUrlWhenNotDefined;
+	}
+	
 	const CACHE_PARSED_INDEX_NAME = 0;
 	const CACHE_PARSED_INDEX_TYPE = 1;
 	static $cacheParsedAction = array();
 	
-	protected function updateActionsTableWithRowQuery($query)
+	protected function updateActionsTableWithRowQuery($query, $fieldQueried = false)
 	{
 		$rowsProcessed = 0;
 		while( $row = $query->fetch() )
 		{
+			if(empty($row['idaction']))
+			{
+				$row['type'] = ($fieldQueried == 'idaction_url' ? Piwik_Tracker_Action::TYPE_ACTION_URL : Piwik_Tracker_Action::TYPE_ACTION_NAME);
+				// This will be replaced with 'X not defined' later 
+				$row['name'] = '';
+				// Yes, this is kind of a hack, so we don't mix 'page url not defined' with 'page title not defined' etc.
+				$row['idaction'] = -$row['type'];
+			}
 			// Only the first query will contain the name and type of actions, for performance reasons
 			if(isset($row['name'])
 				&& isset($row['type']))
@@ -580,9 +593,6 @@ class Piwik_Actions extends Piwik_Plugin
 					// - We select an entry page ID that was only seen yesterday, so wasn't selected in the first query
 					// - We count time spent on a page, when this page was only seen yesterday
 					continue;
-					var_dump($row);
-					debug_print_backtrace();
-					throw new Exception("id action ". $row['idaction'] . " was not cached, but we expected it. Please report this issue in Piwik forums.");
 				}
 				$currentTable = self::$cacheParsedAction[$row['idaction']];
 				// Action processed as "to skip" for some reasons
diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php
index 80c0e07501e22dc49729179a43776f3ae1c5e73e..e9da3d53906898187552afa6f1e7a14bfa34c1b6 100644
--- a/plugins/CustomVariables/CustomVariables.php
+++ b/plugins/CustomVariables/CustomVariables.php
@@ -171,8 +171,8 @@ class Piwik_CustomVariables extends Piwik_Plugin
         	{
         		while($row = $query->fetch() )
         		{
-    				if(!isset($this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
-    				if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+    				if(!isset($this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+    				if(!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
     				
     				$archiveProcessing->updateGoalStats( $row, $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
     				$archiveProcessing->updateGoalStats( $row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index 65d902813e856befecb0626ccb752e99753c9a14..2b3fadfbf3831f60357e653895cb3ebdf122f6e5 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -14,8 +14,19 @@
  * Goals API lets you Manage existing goals, via "updateGoal" and "deleteGoal", create new Goals via "addGoal", 
  * or list existing Goals for one or several websites via "getGoals" 
  * 
- * It also lets you request overall Goal metrics via the method "get" and the additional helpers "getConversions", "getRevenue", etc.
+ * If you are tracking Ecommerce orders and products on your site, the functions "getItemsSku", "getItemsName" and "getItemsCategory"
+ * will return the list of products purchased on your site, either grouped by Product SKU, Product Name or Product Category. For each name, SKU or category, the following
+ * metrics are returned: Total revenue, quantity, average price, average quantity, number of orders with this product.
+ * 
+ * By default, these functions returns the 'Products purchased'. These functions also accept an optional parameter &abandonedCarts=1.
+ * If the parameter is set, it will instead return the metrics for products that were left in an abandoned cart therefore not purchased. 
+ * 
+ * The API also lets you request overall Goal metrics via the method "get": Conversions, Visits with at least one conversion, Conversion rate and Revenue.
+ * If you wish to request specific metrics about Ecommerce goals, you can set the parameter &idGoal=ecommerceAbandonedCart to get metrics about abandoned carts (including Lost revenue, and number of items left in the cart) 
+ * or &idGoal=ecommerceOrder to get metrics about Ecommerce orders (number of orders, visits with an order, subtotal, tax, shipping, discount, revenue, items ordered)
+ * 
  * See also the documentation about <a href='http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>Tracking Goals</a> in Piwik.
+ * 
  * @package Piwik_Goals
  */
 class Piwik_Goals_API 
@@ -177,6 +188,53 @@ class Piwik_Goals_API
 		Piwik_Common::regenerateCacheWebsiteAttributes($idSite);
 	}
 	
+	/**
+	 * Returns a datatable of Items SKU/name or categories and their metrics
+	 * If $abandonedCarts set to 1, will return items abandoned in carts. If set to 0, will return items ordered
+	 */
+	protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts )
+	{
+		Piwik::checkUserHasViewAccess( $idSite );
+		if($abandonedCarts)
+		{
+			$recordName = Piwik_Goals::getItemRecordNameAbandonedCart($recordName);
+		}
+		$archive = Piwik_Archive::build($idSite, $period, $date );
+		$dataTable = $archive->getDataTable($recordName);
+		$dataTable->filter('Sort', array(Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE));
+		$dataTable->queueFilter('ReplaceColumnNames');
+		
+		$ordersColumn = 'orders';
+		if($abandonedCarts)
+		{
+			$ordersColumn = 'abandoned_carts';
+			$dataTable->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
+		}
+		// Average price = sum product revenue / quantity
+		$dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_price', 'price', $ordersColumn, Piwik_Tracker_GoalManager::REVENUE_PRECISION));
+
+		// Average quantity = sum product quantity / abandoned carts 
+		$dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_quantity', 'quantity', $ordersColumn, $precision = 1));
+		$dataTable->queueFilter('ColumnDelete', array('price'));
+		
+		return $dataTable;
+	}
+	
+	public function getItemsSku($idSite, $period, $date, $abandonedCarts = false )
+	{
+		return $this->getItems('Goals_ItemsSku', $idSite, $period, $date, $abandonedCarts);
+	}
+	
+	public function getItemsName($idSite, $period, $date, $abandonedCarts = false )
+	{
+		return $this->getItems('Goals_ItemsName', $idSite, $period, $date, $abandonedCarts);
+	}
+	
+	public function getItemsCategory($idSite, $period, $date, $abandonedCarts = false )
+	{
+		return $this->getItems('Goals_ItemsCategory', $idSite, $period, $date, $abandonedCarts);
+	}
+	
 	/**
 	 * Returns Goals data
 	 * 
@@ -193,14 +251,16 @@ class Piwik_Goals_API
 		$archive = Piwik_Archive::build($idSite, $period, $date, $segment );
 		$columns = Piwik::getArrayFromApiParameter($columns);
 		
+		// Mapping string idGoal to internal ID
+		$idGoal = ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) 
+						? Piwik_Tracker_GoalManager::IDGOAL_ORDER
+						: ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART
+							? Piwik_Tracker_GoalManager::IDGOAL_CART
+							: $idGoal);
+							
 		if(empty($columns))
 		{
-			$columns = array(
-						'nb_conversions',
-						'nb_visits_converted',
-						'conversion_rate', 
-						'revenue',
-			);
+			$columns = Piwik_Goals::getGoalColumns($idGoal);
 		}
 		$columnsToSelect = array();
 		foreach($columns as &$columnName)
@@ -225,21 +285,33 @@ class Piwik_Goals_API
 		return $dataTable;		
 	}
 
+	/**
+	 * @ignore 
+	 */
 	public function getConversions( $idSite, $period, $date, $segment = false, $idGoal = false )
 	{
 		return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_conversions', $idGoal));
 	}
 	
+	/**
+	 * @ignore 
+	 */
 	public function getNbVisitsConverted( $idSite, $period, $date, $segment = false, $idGoal = false )
 	{
 		return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_visits_converted', $idGoal));
 	}
 	
+	/**
+	 * @ignore 
+	 */
 	public function getConversionRate( $idSite, $period, $date, $segment = false, $idGoal = false )
 	{
 		return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('conversion_rate', $idGoal));
 	}
 	
+	/**
+	 * @ignore 
+	 */
 	public function getRevenue( $idSite, $period, $date, $segment = false, $idGoal = false )
 	{
 		return $this->getNumeric( $idSite, $period, $date, $segment, Piwik_Goals::getRecordName('revenue', $idGoal));
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index 96ed86b34dcddfaed7bc12142aaf74a39705bb58..728caec14f499c75d3f656a2c4b28d130b2e169a 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -83,9 +83,11 @@ class Piwik_Goals_Controller extends Piwik_Controller
 		$view->topDimensions = $this->getTopDimensions($idGoal);
 		
 		// conversion rate for new and returning visitors
-		$conversionRateReturning = $this->getConversionRateReturningVisitors($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $idGoal);
+		$segment = 'visitorType==returning';
+		$conversionRateReturning = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
 		$view->conversion_rate_returning = $this->formatConversionRate($conversionRateReturning);
-		$conversionRateNew = $this->getConversionRateNewVisitors($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $idGoal);
+		$segment = 'visitorType==new';
+		$conversionRateNew = Piwik_Goals_API::getInstance()->getConversionRate($this->idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), $segment, $idGoal);
 		$view->conversion_rate_new = $this->formatConversionRate($conversionRateNew);
 		return $view;
 	}
@@ -278,52 +280,4 @@ class Piwik_Goals_Controller extends Piwik_Controller
 				'urlSparklineRevenue' 			=> $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
 		);
 	}
-	
-	private function getConversionRateReturningVisitors( $idSite, $period, $date, $idGoal = false )
-	{
-		// visits converted for returning for all goals = call Frequency API
-		if($idGoal === false)
-		{
-			$request = new Piwik_API_Request("method=VisitFrequency.getConvertedVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original");
-			$nbVisitsConvertedReturningVisitors = $request->process();
-		}
-		// visits converted for returning = nb conversion for this goal
-		else
-		{
-			$nbVisitsConvertedReturningVisitors = Piwik_Goals_API::getInstance()->getConversions($idSite, $period, $date, $segment=false,$idGoal);
-		}
-		// all returning visits
-		$request = new Piwik_API_Request("method=VisitFrequency.getVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original");
-		$nbVisitsReturning = $request->process();
-//		echo $nbVisitsConvertedReturningVisitors;
-//		echo "<br />". $nbVisitsReturning;exit;
-
-		return Piwik::getPercentageSafe($nbVisitsConvertedReturningVisitors, $nbVisitsReturning, Piwik_Goals::ROUNDING_PRECISION);
-	}
-
-	private function getConversionRateNewVisitors( $idSite, $period, $date, $idGoal = false )
-	{
-		// new visits converted for all goals = nb visits converted - nb visits converted for returning
-		if($idGoal == false)
-		{
-			$request = new Piwik_API_Request("method=VisitsSummary.getVisitsConverted&idSite=$idSite&period=$period&date=$date&format=original");
-			$convertedVisits = $request->process();
-			$request = new Piwik_API_Request("method=VisitFrequency.getConvertedVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original");
-			$convertedReturningVisits = $request->process();
-			$convertedNewVisits = $convertedVisits - $convertedReturningVisits;
-		}
-		// new visits converted for a given goal = nb conversion for this goal for new visits
-		else
-		{
-			$convertedNewVisits = Piwik_Goals_API::getInstance()->getConversions($idSite, $period, $date, $segment=false, $idGoal);
-		}
-		// all new visits = all visits - all returning visits
-		$request = new Piwik_API_Request("method=VisitFrequency.getVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original");
-		$nbVisitsReturning = $request->process();
-		$request = new Piwik_API_Request("method=VisitsSummary.getVisits&idSite=$idSite&period=$period&date=$date&format=original");
-		$nbVisits = $request->process();
-		$newVisits = $nbVisits - $nbVisitsReturning;
-		return Piwik::getPercentageSafe($convertedNewVisits, $newVisits, Piwik_Goals::ROUNDING_PRECISION);
-	}
-	
 }
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index 4b1a15d3d3cb84487597146c1202656cfb289217..049117dc86bffe6313464e33a17bab773a1b720d 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -16,8 +16,6 @@
  */
 class Piwik_Goals extends Piwik_Plugin
 {
-	const ROUNDING_PRECISION = 2;
-	
 	public function getInformation()
 	{
 		$info = array(
@@ -206,22 +204,16 @@ class Piwik_Goals extends Piwik_Plugin
 	/**
 	 * @param string $recordName 'nb_conversions'
 	 * @param int $idGoal idGoal to return the metrics for, or false to return overall
-	 * @param int $visitorReturning 0 for new visitors, 1 for returning visitors, false for all
-	 * @return unknown
+	 * @return string Archive record name
 	 */
-	static public function getRecordName($recordName, $idGoal = false, $visitorReturning = false)
+	static public function getRecordName($recordName, $idGoal = false)
 	{
-		$idGoalStr = $returningStr = '';
-		if(!empty($idGoal))
+		$idGoalStr = '';
+		if($idGoal !== false)
 		{
 			$idGoalStr = $idGoal . "_";
 		}
-		if($visitorReturning !== false)
-		{
-			$returningStr = 'visitor_returning_' . $visitorReturning . '_';
-		}
-		
-		return 'Goal_' . $returningStr . $idGoalStr . $recordName;
+		return 'Goal_' . $idGoalStr . $recordName;
 	}
 	
 	/**
@@ -233,37 +225,86 @@ class Piwik_Goals extends Piwik_Plugin
 	 */
 	function archivePeriod($notification )
 	{
+		/**
+		 * @var Piwik_ArchiveProcessing 
+		 */
 		$archiveProcessing = $notification->getNotificationObject();
 		
 		if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-		$metricsToSum = array( 'nb_conversions', 'revenue');
-		$goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
 		
+		/*
+		 * Archive Ecommerce Items
+		 */
+		if($this->shouldArchiveEcommerceItems($archiveProcessing))
+		{
+			$dataTableToSum = $this->dimensions;
+			foreach($this->dimensions as $recordName)
+			{
+				$dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
+			}
+			$nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum);
+		}
+				
+		/*
+		 *  Archive General Goal metrics
+		 */
+		$goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
+		if(Piwik::isEcommerceEnabled($archiveProcessing->idsite))
+		{
+			$goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+			$goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART;
+			// Overall goal metrics
+			$goalIdsToSum[] = false;
+		}
 		$fieldsToSum = array();
-		foreach($metricsToSum as $metricName)
+		foreach($goalIdsToSum as $goalId)
 		{
-			foreach($goalIdsToSum as $goalId)
+			$metricsToSum = Piwik_Goals::getGoalColumns($goalId);
+			unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
+			foreach($metricsToSum as $metricName)
 			{
 				$fieldsToSum[] = self::getRecordName($metricName, $goalId);
-				$fieldsToSum[] = self::getRecordName($metricName, $goalId, 0);
-				$fieldsToSum[] = self::getRecordName($metricName, $goalId, 1);
 			}
-			$fieldsToSum[] = self::getRecordName($metricName);
 		}
 		$records = $archiveProcessing->archiveNumericValuesSum($fieldsToSum);
 		
 		// also recording conversion_rate for each goal
 		foreach($goalIdsToSum as $goalId)
 		{
-			$nb_conversions = $records[self::getRecordName('nb_conversions', $goalId)];
+			$nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
 			$conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing);
 			$archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
+		} 
+	}
+	
+	static public function getGoalColumns($idGoal)
+	{
+		$columns = array(
+					'nb_conversions',
+					'nb_visits_converted',
+					'conversion_rate', 
+					'revenue',
+		);
+		if($idGoal === false)
+		{
+			return $columns;
 		}
-		
-		// global conversion rate
-		$nb_conversions = $records[self::getRecordName('nb_conversions')];
-		$conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing);
-		$archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate'), $conversion_rate);
+		// Orders
+		if($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER)
+		{
+			$columns = array_merge($columns, array(
+				'revenue_subtotal',
+				'revenue_tax',
+				'revenue_shipping',
+				'revenue_discount',
+			));
+		}
+		// Abandoned carts & orders
+		if($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER) 
+		{
+			$columns[] = 'items';
+		}
+		return $columns;
 	}
 	
 	/**
@@ -283,25 +324,34 @@ class Piwik_Goals extends Piwik_Plugin
 		
 		if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
 		
-		// by processing visitor_returning segment, we can also simply sum and get stats for all goals.
-		$query = $archiveProcessing->queryConversionsByDimension('visitor_returning');
+		$this->archiveGeneralGoalMetrics($archiveProcessing);
+		$this->archiveEcommerceItems($archiveProcessing);
+	}
+	
+	/**
+	 * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+	 */
+	function archiveGeneralGoalMetrics($archiveProcessing)
+	{
+		$query = $archiveProcessing->queryConversionsByDimension('');
 		
-		if($query === false) return;
+		if($query === false) { return; }
 		
-		$nb_conversions = $revenue = $nb_visits_converted = 0;
-		$goals = $goalsByVisitorReturning = array();
+		$goals = array();
+		// Get a standard empty goal row
+		$overall = $archiveProcessing->getNewGoalRow( $idGoal = 1);
 		while($row = $query->fetch() )
 		{
-			$goalsByVisitorReturning[$row['idgoal']][$row['label']] = $archiveProcessing->getGoalRowFromQueryRow($row);
-			
-			if(!isset($goals[$row['idgoal']])) $goals[$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+			if(!isset($goals[$row['idgoal']])) $goals[$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 			$archiveProcessing->updateGoalStats($row, $goals[$row['idgoal']]);
-
-			$revenue += $row[Piwik_Archive::INDEX_GOAL_REVENUE];
-			$nb_conversions += $row[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS];
-			$nb_visits_converted += $row[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED];
+			
+			// We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits 
+			// since it is a "negative conversion"
+			if($row['idgoal'] != Piwik_Tracker_GoalManager::IDGOAL_CART)
+			{
+				$archiveProcessing->updateGoalStats($row, $overall);
+			}
 		}
-		
 		// Stats by goal, for all visitors
 		foreach($goals as $idgoal => $values)
 		{
@@ -311,32 +361,19 @@ class Piwik_Goals extends Piwik_Plugin
 				$recordName = self::getRecordName($metricName, $idgoal);
 				$archiveProcessing->insertNumericRecord($recordName, $value);
 			}
-			$conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS], $archiveProcessing);
+			
+			$conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
 			$recordName = self::getRecordName('conversion_rate', $idgoal);
 			$archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
 		}
 		
-		// Stats by goal, for visitor returning / non returning
-		foreach($goalsByVisitorReturning as $idgoal => $values)
-		{
-			foreach($values as $visitor_returning => $goalValues)
-			{
-				foreach($goalValues as $metricId => $value)
-				{
-					$metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
-					$recordName = self::getRecordName($metricName, $idgoal, $visitor_returning);
-					$archiveProcessing->insertNumericRecord($recordName, $value);
-//					echo $record . "<br />";
-				}
-			}
-		}
-	
+		
 		// Stats for all goals
 		$totalAllGoals = array(
 			self::getRecordName('conversion_rate')	=> $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
-			self::getRecordName('nb_conversions')	=> $nb_conversions,
-			self::getRecordName('nb_visits_converted')	=> $nb_visits_converted,
-			self::getRecordName('revenue') 			=> $revenue,
+			self::getRecordName('nb_conversions')	=> $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
+			self::getRecordName('nb_visits_converted')	=> $archiveProcessing->getNumberOfVisitsConverted(),
+			self::getRecordName('revenue') 			=> $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
 		);
 		foreach($totalAllGoals as $recordName => $value)
 		{
@@ -344,10 +381,99 @@ class Piwik_Goals extends Piwik_Plugin
 		}
 	}
 	
+	protected $dimensions = array(
+		'idaction_sku' => 'Goals_ItemsSku', 
+		'idaction_name' => 'Goals_ItemsName', 
+		'idaction_category'  => 'Goals_ItemsCategory'
+	);
+	
+	protected function shouldArchiveEcommerceItems($archiveProcessing)
+	{
+	    if(!Piwik::isEcommerceEnabled($archiveProcessing->idsite)
+	    	// Per item doesn't support segment
+	    	// Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request 
+	    	// event if it did support segment 
+	    	// (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested) 
+	    	|| !$archiveProcessing->getSegment()->isEmpty())
+	    {
+	    	return false;
+	    }
+	    return true;
+	}
+	
+	/**
+	 * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+	 */
+	function archiveEcommerceItems($archiveProcessing)
+	{
+		if(!$this->shouldArchiveEcommerceItems($archiveProcessing))
+		{
+			return false;
+		}
+		$dimensionsNotSet = array(
+			'idaction_sku' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductSKU')), // Note: this should never happen 
+			'idaction_name' => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductName')), 
+			'idaction_category'  => Piwik_Translate('General_NotDefined', Piwik_Translate('Goals_ProductCategory'))
+		);
+		$items = array();
+		foreach($this->dimensions as $dimension => $recordName)
+		{
+			$query = $archiveProcessing->queryEcommerceItems($dimension);
+			if($query == false) { return; }
+			
+			while($row = $query->fetch())
+			{
+				$label = $row['label'];
+				$ecommerceType = $row['type'];
+				
+				if(empty($label))
+				{
+					$label = $dimensionsNotSet[$dimension];
+				}
+				// For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
+				if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
+				{
+					$row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
+				}
+				unset($row[Piwik_Archive::INDEX_NB_VISITS]);
+				unset($row['label']);
+				unset($row['type']);
+				if($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE] == round($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE]))
+				{
+					$row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE] = round($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE]);
+				}
+				$items[$dimension][$ecommerceType][$label] = $row;
+			}
+		}
+		
+		foreach($this->dimensions as $dimension => $recordName)
+		{
+			foreach(array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType)
+			{
+				if(!isset($items[$dimension][$ecommerceType]))
+				{
+					continue;
+				}
+				$recordNameInsert = $recordName;
+				if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
+				{
+					$recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
+				}
+				$table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
+				$archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
+			}
+		}
+	}
+	
+	static public function getItemRecordNameAbandonedCart($recordName)
+	{
+		return $recordName . '_Cart';
+	}
+	
 	function getConversionRate($count, $archiveProcessing)
 	{
 		$visits = $archiveProcessing->getNumberOfVisits();
-		return round(100 * $count / $visits, self::ROUNDING_PRECISION);
+		return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
 	}
 
 }
diff --git a/plugins/Live/API.php b/plugins/Live/API.php
index 3c74dd3da1fda3cbc0f4c458dba0734c496caf69..3460c8387183ef1b44fabc7d9fad2b2f09fc1953 100644
--- a/plugins/Live/API.php
+++ b/plugins/Live/API.php
@@ -202,10 +202,39 @@ class Piwik_Live_API
 						goal.idgoal = log_conversion.idgoal)
 					AND goal.deleted = 0
 				WHERE log_conversion.idvisit = ?
+					AND log_conversion.idgoal > 0
 			";
 			$goalDetails = Piwik_FetchAll($sql, array($idvisit));
 
-			$actions = array_merge($actionDetails, $goalDetails);
+			$sql = "SELECT 
+						case idgoal when ".Piwik_Tracker_GoalManager::IDGOAL_CART." then '".Piwik_Archive::LABEL_ECOMMERCE_CART."' else '".Piwik_Archive::LABEL_ECOMMERCE_ORDER."' end as type,
+						idorder as orderId,
+						revenue as revenue,
+						revenue_subtotal as revenueSubTotal,
+						revenue_tax as revenueTax,
+						revenue_shipping as revenueShipping,
+						revenue_discount as revenueDiscount,
+						items as items,
+						
+						log_conversion.server_time as serverTimePretty
+					FROM ".Piwik_Common::prefixTable('log_conversion')." AS log_conversion
+					WHERE idvisit = ?
+						AND idgoal <= ".Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+			$ecommerceDetails = Piwik_FetchAll($sql, array($idvisit));
+
+			foreach($ecommerceDetails as &$ecommerceDetail)
+			{
+				if($ecommerceDetail['type'] == Piwik_Archive::LABEL_ECOMMERCE_CART)
+				{
+					unset($ecommerceDetail['orderId']);
+					unset($ecommerceDetail['revenueSubTotal']);
+					unset($ecommerceDetail['revenueTax']);
+					unset($ecommerceDetail['revenueShipping']);
+					unset($ecommerceDetail['revenueDiscount']);
+				}
+			}
+			
+			$actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
 			
 			usort($actions, array($this, 'sortByServerTime'));
 			
@@ -216,6 +245,8 @@ class Piwik_Live_API
 				switch($details['type'])
 				{
 					case 'goal':
+					case Piwik_Archive::LABEL_ECOMMERCE_ORDER:
+					case Piwik_Archive::LABEL_ECOMMERCE_CART:
 					break;
 					case Piwik_Tracker_Action_Interface::TYPE_DOWNLOAD:
 						$details['type'] = 'download';
@@ -232,6 +263,39 @@ class Piwik_Live_API
 			}
 			$visitorDetailsArray['goalConversions'] = count($goalDetails);
 			
+			// Enrich ecommerce carts/orders with the list of products 
+			usort($ecommerceDetails, array($this, 'sortByServerTime'));
+			foreach($ecommerceDetails as $key => $ecommerceConversion)
+			{
+				$sql = "SELECT 
+							log_action_sku.name as itemSKU,
+							log_action_name.name as itemName,
+							log_action_category.name as itemCategory,
+							price as price,
+							quantity as quantity
+						FROM ".Piwik_Common::prefixTable('log_conversion_item')."
+							INNER JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_sku
+							ON  idaction_sku = log_action_sku.idaction
+							LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_name
+							ON  idaction_name = log_action_name.idaction
+							LEFT JOIN " .Piwik_Common::prefixTable('log_action')." AS log_action_category
+							ON idaction_category = log_action_category.idaction
+						WHERE idvisit = ? 
+							AND idorder = ?
+							AND deleted = 0
+				";
+				$bind = array($idvisit, isset($ecommerceConversion['orderId']) ? $ecommerceConversion['orderId'] : Piwik_Tracker_GoalManager::ITEM_IDORDER_ABANDONED_CART);
+				
+				$itemsDetails = Piwik_FetchAll($sql, $bind);
+				
+				// unreference the array or items added to the reference will show up in the 'actionDetails'
+				$value = $ecommerceDetails[$key];
+				unset($ecommerceDetails[$key]);
+				$value['itemDetails'] = $itemsDetails;
+				$ecommerceDetails[$key] = $value;
+			}
+			
+			$visitorDetailsArray['ecommerce'] = $ecommerceDetails;
 			$table->addRowFromArray( array(Piwik_DataTable_Row::COLUMNS => $visitorDetailsArray));
 		}
 		return $table;
@@ -305,7 +369,14 @@ class Piwik_Live_API
 			}
 			else
 			{
-				$processedDate = Piwik_Date::factory($date)->subDay(1);
+				$processedDate = Piwik_Date::factory($date);
+				
+				if($date == 'today'
+					|| $date == 'now'
+					|| $processedDate == Piwik_Date::factory('today'))
+				{
+					$processedDate = $processedDate->subDay(1);
+				}
 				$processedPeriod = Piwik_Period::factory($period, $processedDate); 
 			}
 			$dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone);
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index 1d0315920196c3bb2541378e5a36aa2efd5d84bc..77b97c0c811a0351e69ee93ac3f04f37a2e48dc7 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -44,6 +44,7 @@ class Piwik_Live_Visitor
 			'visitorId' => $this->getVisitorId(),
 			'visitorType' => $this->isVisitorReturning() ? 'returning' : 'new',
 			'visitConverted' => $this->isVisitorGoalConverted(),
+			'visitEcommerceStatus' => $this->getVisitEcommerceStatus(),
 		
 			'actions' => $this->getNumberOfActions(),
 			// => false are placeholders to be filled in API later
@@ -65,6 +66,7 @@ class Piwik_Live_Visitor
 			'visitCount' => $this->getVisitCount(),
 			'daysSinceLastVisit' => $this->getDaysSinceLastVisit(),
 			'daysSinceFirstVisit' => $this->getDaysSinceFirstVisit(),
+			'daysSinceLastEcommerceOrder' => $this->getDaysSinceLastEcommerceOrder(),
 			'country' => $this->getCountryName(),
 			'countryFlag' => $this->getCountryFlag(),
 			'continent' => $this->getContinent(),
@@ -117,6 +119,10 @@ class Piwik_Live_Visitor
 		return $this->details['visitor_days_since_last'];
 	}
 	
+	function getDaysSinceLastEcommerceOrder()
+	{
+		return $this->details['visitor_days_since_order'];
+	}
 	function getDaysSinceFirstVisit()
 	{
 		return $this->details['visitor_days_since_first'];
@@ -379,6 +385,11 @@ class Piwik_Live_Visitor
 		return date('Y-m-d H:i:s', strtotime($this->details['visit_last_action_time']));
 	}
 
+	function getVisitEcommerceStatus()
+	{
+		return Piwik_API_API::getVisitEcommerceStatusFromId($this->details['visit_goal_buyer']);
+	}
+	
 	function isVisitorGoalConverted()
 	{
 		return $this->details['visit_goal_converted'];
diff --git a/plugins/PDFReports/API.php b/plugins/PDFReports/API.php
index 38d8a8a20b7b90fb073d339cf77d7c2d609203c0..22b1bde7a777086e34d2efdcd015791ef468c38c 100644
--- a/plugins/PDFReports/API.php
+++ b/plugins/PDFReports/API.php
@@ -355,7 +355,6 @@ class Piwik_PDFReports_API
 	{
 		$reports = $this->getReports($idSite, $period = false, $idReport);
 		$report = reset($reports);
-		
 		if($report['period'] == 'never')
 		{
 			$report['period'] = 'day';
diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php
index 092e774a2b7d91b7a98776eab0091a8b94ab1e12..4d5fa40697977999ed058e52bf8d514d8b1ac288 100644
--- a/plugins/Referers/Referers.php
+++ b/plugins/Referers/Referers.php
@@ -429,25 +429,25 @@ class Piwik_Referers extends Piwik_Plugin
 				switch($row['referer_type'])
 				{
 					case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
-						if(!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
-						if(!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+						if(!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+						if(!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 						
 						$archiveProcessing->updateGoalStats( $row, $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 						$archiveProcessing->updateGoalStats( $row, $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 					break;
 
 					case Piwik_Common::REFERER_TYPE_WEBSITE:
-						if(!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+						if(!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 						$archiveProcessing->updateGoalStats( $row, $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 					break;
 
 					case Piwik_Common::REFERER_TYPE_CAMPAIGN:
 						if(!empty($row['referer_keyword']))
 						{
-							if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+							if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 							$archiveProcessing->updateGoalStats( $row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 						}
-						if(!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+						if(!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 						$archiveProcessing->updateGoalStats( $row, $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 					break;
 
@@ -457,7 +457,7 @@ class Piwik_Referers extends Piwik_Plugin
 					break;
 				}
 			}
-			if(!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] )) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+			if(!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] )) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 			$archiveProcessing->updateGoalStats($row, $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 		}
 	
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index fc72df6e6da02d3d8292c1cab313c8e0d19d5727..8fcb565da6ecd8aa07be66ad3bb23565767e59b8 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -115,6 +115,9 @@ class Piwik_UserCountry extends Piwik_Plugin
 	
 	function archivePeriod( $notification )
 	{
+		/**
+		 * @param Piwik_ArchiveProcessing_Period  $archiveProcessing
+		 */
 		$archiveProcessing = $notification->getNotificationObject();
 		
 		if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
@@ -131,6 +134,9 @@ class Piwik_UserCountry extends Piwik_Plugin
 	
 	function archiveDay($notification)
 	{
+		/**
+		 * @var Piwik_ArchiveProcessing
+		 */
 		$archiveProcessing = $notification->getNotificationObject();
 		
 		if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
@@ -140,6 +146,9 @@ class Piwik_UserCountry extends Piwik_Plugin
 		$this->archiveDayRecordInDatabase($archiveProcessing);
 	}
 	
+	/**
+	 * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+	 */
 	protected function archiveDayAggregateVisits($archiveProcessing)
 	{
 		$labelSQL = "location_country";
@@ -149,6 +158,9 @@ class Piwik_UserCountry extends Piwik_Plugin
 		$this->interestByContinent = $archiveProcessing->getArrayInterestForLabel($labelSQL);
 	}
 	
+	/**
+	 * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+	 */
 	protected function archiveDayAggregateGoals($archiveProcessing)
 	{
 		$query = $archiveProcessing->queryConversionsByDimension(array("location_continent","location_country"));
@@ -157,8 +169,8 @@ class Piwik_UserCountry extends Piwik_Plugin
 		
 		while($row = $query->fetch() )
 		{
-			if(!isset($this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
-			if(!isset($this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow();
+			if(!isset($this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
+			if(!isset($this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
 			$archiveProcessing->updateGoalStats($row, $this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 			$archiveProcessing->updateGoalStats($row, $this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
 		}
@@ -166,6 +178,9 @@ class Piwik_UserCountry extends Piwik_Plugin
 		$archiveProcessing->enrichConversionsByLabelArray($this->interestByContinent);
 	}
 	
+	/**
+	 * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+	 */
 	protected function archiveDayRecordInDatabase($archiveProcessing)
 	{
 		$tableCountry = $archiveProcessing->getDataTableFromArray($this->interestByCountry);
diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php
index 8cb246bd3229818a6758fe81de157055a6c4e9c2..074377974d60f6a766bf5581d77b37974fe376ac 100644
--- a/plugins/VisitFrequency/API.php
+++ b/plugins/VisitFrequency/API.php
@@ -97,26 +97,41 @@ class Piwik_VisitFrequency_API
 		return $dataTable;		
 	}
 
+	/**
+	 * @ignore
+	 */
 	public function getVisitsReturning( $idSite, $period, $date )
 	{
 		return $this->getNumeric( $idSite, $period, $date, 'nb_visits_returning');
 	}
 	
+	/**
+	 * @ignore
+	 */
 	public function getActionsReturning( $idSite, $period, $date )
 	{
 		return $this->getNumeric( $idSite, $period, $date, 'nb_actions_returning');
 	}
 	
+	/**
+	 * @ignore
+	 */
 	public function getSumVisitsLengthReturning( $idSite, $period, $date )
 	{
 		return $this->getNumeric( $idSite, $period, $date, 'sum_visit_length_returning');
 	}
 	
+	/**
+	 * @ignore
+	 */
 	public function getBounceCountReturning( $idSite, $period, $date )
 	{
 		return $this->getNumeric( $idSite, $period, $date, 'bounce_count_returning');
 	}
 	
+	/**
+	 * @ignore
+	 */
 	public function getConvertedVisitsReturning( $idSite, $period, $date )
 	{
 		return $this->getNumeric( $idSite, $period, $date, 'nb_visits_converted_returning');
diff --git a/tests/integration/Integration.php b/tests/integration/Integration.php
index 2edabc6d8cc39a1c26c4f58d06dccdc9bd418701..954a5d7097e435223e9942c7106f46c308bc5b26 100644
--- a/tests/integration/Integration.php
+++ b/tests/integration/Integration.php
@@ -277,6 +277,7 @@ abstract class Test_Integration extends Test_Database
         				$parametersToSet['format'] = $format;
         				$parametersToSet['hideIdSubDatable'] = 1;
         				$parametersToSet['serialize'] = 1;
+        				
             			$exampleUrl = $apiMetadata->getExampleUrl($class, $methodName, $parametersToSet);
             			if($exampleUrl === false) 
             			{
@@ -320,10 +321,11 @@ abstract class Test_Integration extends Test_Database
 	 * @param $language 2 letter language code to request data in
 	 * @param $segment Custom Segment to query the data  for
 	 * @param $visitorId Only used for Live! API testing
+	 * @param $abandonedCarts Only used in Goals API testing
 	 * 
 	 * @return bool Passed or failed
 	 */
-	function callGetApiCompareOutput($testName, $formats = 'xml', $idSite = false, $dateTime = false, $periods = false, $setDateLastN = false, $language = false, $segment = false, $visitorId = false)
+	function callGetApiCompareOutput($testName, $formats = 'xml', $idSite = false, $dateTime = false, $periods = false, $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal = false)
 	{
 		$pass = true;
 		
@@ -367,6 +369,7 @@ abstract class Test_Integration extends Test_Database
 			'showTimer'     => 0,
 		
 			'language' => $language ? $language : 'en',
+			'abandonedCarts' => $abandonedCarts ? 1 : 0,
 		);
 		if(!empty($visitorId ))
 		{
@@ -376,6 +379,10 @@ abstract class Test_Integration extends Test_Database
 		{
 			$parametersToSet['segment'] = $segment;
 		}
+		if($idGoal !== false)
+		{
+			$parametersToSet['idGoal'] = $idGoal;
+		}
 		// Give it enough time for the current API test to finish (call all get* APIs)
 		Zend_Registry::get('config')->General->time_before_today_archive_considered_outdated = 10;
 		
@@ -383,8 +390,8 @@ abstract class Test_Integration extends Test_Database
     	
     	foreach($requestUrls as $apiId => $requestUrl)
     	{
-			$isLiveMustDeleteDates = strpos($requestUrl, 'Live.getLastVisits') !== false;
 //    		echo "$requestUrl <br>";
+			$isLiveMustDeleteDates = strpos($requestUrl, 'Live.getLastVisits') !== false;
     		$request = new Piwik_API_Request($requestUrl);
 
     		// $TEST_NAME - $API_METHOD
diff --git a/tests/integration/Main.test.php b/tests/integration/Main.test.php
index 322b732d5e40865714559b0ccf2fecdc744fbb7b..25fda0552277252b1c29be64dec3f6bec4aecfbc 100644
--- a/tests/integration/Main.test.php
+++ b/tests/integration/Main.test.php
@@ -31,6 +31,116 @@ class Test_Piwik_Integration_Main extends Test_Integration
 		return PIWIK_INCLUDE_PATH . '/tests/integration';
 	}
 	
+	function test_ecommerceOrderWithItems()
+	{
+		$this->setApiNotToCall(array());
+		$dateTime = '2011-04-05 00:11:42';
+		$idSite = $this->createWebsite($dateTime);
+		
+		$idGoal = Piwik_Goals_API::getInstance()->addGoal($idSite, 'triggered js ONCE', 'title', 'incredible', 'contains', $caseSensitive=false, $revenue=10, $allowMultipleConversions = true);
+        
+        $t = $this->getTracker($idSite, $dateTime, $defaultInit = true);
+    	// Record 1st page view
+        $t->setUrl( 'http://example.org/index.htm' );
+        $this->checkResponse($t->doTrackPageView( 'incredible title!'));
+        
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.3)->getDatetime());
+        
+        //Add to cart
+        $t->addEcommerceItem($sku = 'SKU VERY nice indeed', $name = 'PRODUCT name' , $category = 'Electronics & Cameras', $price = 500, $quantity = 1);
+        $t->addEcommerceItem($sku = 'SKU VERY nice indeed', $name = 'PRODUCT name' , $category = 'Electronics & Cameras', $price = 500, $quantity = 2);
+        $this->checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 1000));
+
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.4)->getDatetime());
+        //Order
+        $t->addEcommerceItem($sku = 'SKU VERY nice indeed', $name = 'PRODUCT name' , $category = 'Electronics & Cameras', $price = 500, $quantity = 2);
+        $this->checkResponse($t->doTrackEcommerceOrder($orderId = '937nsjusu 3894', $grandTotal = 1111.11, $subTotal = 1000, $tax = 111, $shipping = 0.11, $discount = 666));
+        
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.5)->getDatetime());
+        //Another Order
+        $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR' , $category = 'Electronics & Cameras', $price = 1500, $quantity = 1);
+        $this->checkResponse($t->doTrackEcommerceOrder($orderId = '1037nsjusu4s3894', $grandTotal = 2000, $subTotal = 1500, $tax = 400, $shipping = 100, $discount = 0));
+        
+        // Refresh the page with the receipt for the second order, should be ignored
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.55)->getDatetime());
+        
+        // Recording the same ecommerce order, this time with some crazy amount and quantity
+        // we test that both the order, and the products, are not updated on subsequent "Receipt" views
+        $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR' , $category = 'Electronics & Cameras', $price = 15000000000, $quantity = 10000); 
+        $this->checkResponse($t->doTrackEcommerceOrder($orderId = '1037nsjusu4s3894', $grandTotal = 20000000, $subTotal = 1500, $tax = 400, $shipping = 100, $discount = 0));
+        
+        // Leave with an opened cart
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour(0.6)->getDatetime());
+        // No category
+        $t->addEcommerceItem($sku = 'SKU IN ABANDONED CART ONE', $name = 'PRODUCT ONE LEFT in cart' , $category = '', $price = 500.11111112, $quantity = 2);
+        $this->checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 1000));
+
+        // Record the same visit leaving twice an abandoned cart
+        foreach(array(0, 5, 24) as $offsetHour)
+        {   
+	        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour($offsetHour + 0.65)->getDatetime());
+        	// Also recording an order the day after
+        	if($offsetHour >= 24)
+        	{
+	        	$t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR' , $category = 'Electronics & Cameras', $price = 1500, $quantity = 1);
+	        	$this->checkResponse($t->doTrackEcommerceOrder($orderId = '1037nsjusu4s3894', $grandTotal = 20000000, $subTotal = 1500, $tax = 400, $shipping = 100, $discount = 0));
+        	}        
+	        $t->addEcommerceItem($sku = 'SKU IN ABANDONED CART ONE', $name = 'PRODUCT ONE LEFT in cart' , $category = '', $price = 500.11111112, $quantity = 1);
+	        $t->addEcommerceItem($sku = 'SKU IN ABANDONED CART TWO', $name = 'PRODUCT TWO LEFT in cart' , $category = 'Category TWO LEFT in cart', $price = 1000, $quantity = 2);
+	        $this->checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 2500.11111112));
+        }
+        
+        // One more Ecommerce order to check weekly archiving works fine on orders
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour( 30.65 )->getDatetime());
+        $t->addEcommerceItem($sku = 'TRIPOD SKU', $name = 'TRIPOD - bought day after' , $category = 'Tools', $price = 100, $quantity = 2);
+        $this->checkResponse($t->doTrackEcommerceOrder($orderId = '666', $grandTotal = 240, $subTotal = 200, $tax = 20, $shipping = 20, $discount = 20));
+        
+        // One more Ecommerce order, without any product in it, because we still track orders without products
+        $t->setForceVisitDateTime(Piwik_Date::factory($dateTime)->addHour( 30.75 )->getDatetime());
+        $this->checkResponse($t->doTrackEcommerceOrder($orderId = '777', $grandTotal = 10000));
+        
+        //------------------------------------- End tracking
+        
+		// From Piwik 1.5, we hide Goals.getConversions and other get* methods via @ignore, but we ensure that they still work
+		// This hack allows the API proxy to let us generate example URLs for the ignored functions
+		Piwik_API_Proxy::getInstance()->hideIgnoredFunctions = false;
+        
+		$this->setApiToCall( array('Live.getLastVisitsDetails', 'UserCountry', 'API.getProcessedReport', 'Goals.get', 'Goals.getConversions', 'Goals.getItemsSku', 'Goals.getItemsName', 'Goals.getItemsCategory'	) );
+        $this->callGetApiCompareOutput(__FUNCTION__, 'xml', $idSite, $dateTime, $periods = array('day'));
+        
+		$this->setApiToCall( array('Goals.get', 'Goals.getItemsSku', 'Goals.getItemsName', 'Goals.getItemsCategory'	) );
+        $this->callGetApiCompareOutput(__FUNCTION__, 'xml', $idSite, $dateTime, $periods = array('week'));
+        
+        $abandonedCarts = 1;
+		$this->setApiToCall( array('Goals.getItemsSku', 'Goals.getItemsName', 'Goals.getItemsCategory') );
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_AbandonedCarts', 'xml', $idSite, $dateTime, $periods = array('day', 'week'), $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts);
+		
+        // Test Goals.get with idGoal=ecommerceOrder and ecommerceAbandonedCart
+        $this->setApiToCall( array('Goals.get') );
+        $idGoal = Piwik_Archive::LABEL_ECOMMERCE_CART;
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_GoalAbandonedCart', 'xml', $idSite, $dateTime, $periods = array('day', 'week'), $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal);
+        
+        $idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_GoalOrder', 'xml', $idSite, $dateTime, $periods = array('day', 'week'), $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal);
+        $idGoal = 1;
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_GoalMatchTitle', 'xml', $idSite, $dateTime, $periods = array('day', 'week'), $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal);
+        $idGoal = '';
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_GoalOverall', 'xml', $idSite, $dateTime, $periods = array('day', 'week'), $setDateLastN = false, $language = false, $segment = false, $visitorId = false, $abandonedCarts = false, $idGoal);
+        
+        $this->setApiToCall( array('VisitsSummary.get') );
+        $segment = 'visitEcommerceStatus==none';
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_SegmentNoEcommerce', 'xml', $idSite, $dateTime, $periods = array('day'), $setDateLastN = false, $language = false, $segment);
+        $segment = 'visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart';
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_SegmentOrderedSomething', 'xml', $idSite, $dateTime, $periods = array('day'), $setDateLastN = false, $language = false, $segment);
+        $segment = 'visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart';
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_SegmentAbandonedCart', 'xml', $idSite, $dateTime, $periods = array('day'), $setDateLastN = false, $language = false, $segment);
+        
+        // test Live! output is OK also for the visit that just bought something (other visits leave an abandoned cart)
+        $this->setApiToCall(array('Live.getLastVisitsDetails'));
+        $this->callGetApiCompareOutput(__FUNCTION__ . '_LiveEcommerceStatusOrdered', 'xml', $idSite, Piwik_Date::factory($dateTime)->addHour( 30.65 )->getDatetime(), $periods = array('day'));
+//        exit;
+	}
+	
 	function test_trackGoals_allowMultipleConversionsPerVisit()
 	{
 		$this->setApiToCall(array(
@@ -312,8 +422,6 @@ class Test_Piwik_Integration_Main extends Test_Integration
     								'Actions.getPageUrls', 
     								'Actions.getPageTitles',
     	                            'Actions.getOutlinks'));
-    	ob_start();
-    	
     	// -
     	// First visitor on Idsite 1: two page views
     	$datetimeSpanOverTwoDays = '2010-01-03 23:55:00'; 
@@ -322,6 +430,7 @@ class Test_Piwik_Integration_Main extends Test_Integration
         $visitorA->setUrl('http://example.org/homepage');
         $this->checkResponse($visitorA->doTrackPageView('first page view'));
     	$visitorA->setForceVisitDateTime(Piwik_Date::factory($datetimeSpanOverTwoDays)->addHour(0.1)->getDatetime());
+    	// Testing with empty URL and empty page title
     	$visitorA->setUrl('  ');
         $this->checkResponse($visitorA->doTrackPageView('  '));
         
@@ -380,7 +489,6 @@ class Test_Piwik_Integration_Main extends Test_Integration
         // We also test a single period to check that this use case (Reports per idSite in the response) works
     	$this->setApiToCall(array('VisitsSummary.get', 'Goals.get'));
     	$this->callGetApiCompareOutput(__FUNCTION__ . '_NotLastNPeriods', 'xml', $idSite = 'all', $dateTime, array('day', 'month'), $setDateLastN = false);
-         
 	}
 	
 	private function doTest_twoVisitsWithCustomVariables($dateTime, $width=1111, $height=222)
@@ -569,6 +677,10 @@ class Test_Piwik_Integration_Main extends Test_Integration
 				$seenVisitorId = true;
 				$value = '34c31e04394bdc63';
 			}
+			if($segment['segment'] == 'visitEcommerceStatus')
+			{
+				$value = 'none';
+			}
 			$segmentExpression[] = $segment['segment'] .'!='.$value;
 		}
 		// just checking that this segment was tested (as it has the only visible to admin flag)
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsCategory_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsCategory_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsName_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsName_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsSku_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits__Goals.getItemsSku_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsCategory_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsCategory_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsName_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsName_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsSku_day.xml b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Goals.getItemsSku_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_PiwikTracker_trackForceUsingVisitId_insteadOfHeuristics_alsoTestsCampaignTracking__Referers.getCampaigns_day.xml b/tests/integration/expected/test_PiwikTracker_trackForceUsingVisitId_insteadOfHeuristics_alsoTestsCampaignTracking__Referers.getCampaigns_day.xml
index 9b6c31b0559b7e019456bb29f2d10a8588f030a8..69520dea3d353860fcc1664093e64d57d73b74a7 100644
--- a/tests/integration/expected/test_PiwikTracker_trackForceUsingVisitId_insteadOfHeuristics_alsoTestsCampaignTracking__Referers.getCampaigns_day.xml
+++ b/tests/integration/expected/test_PiwikTracker_trackForceUsingVisitId_insteadOfHeuristics_alsoTestsCampaignTracking__Referers.getCampaigns_day.xml
@@ -12,11 +12,11 @@
 			<row idgoal='1'>
 				<nb_conversions>1</nb_conversions>
 				<nb_visits_converted>1</nb_visits_converted>
-				<revenue>42.25</revenue>
+				<revenue>42.26</revenue>
 			</row>
 		</goals>
 		<nb_conversions>1</nb_conversions>
-		<revenue>42.25</revenue>
+		<revenue>42.26</revenue>
 		<subtable>
 			<row>
 				<label>Piwik kwd</label>
@@ -30,11 +30,11 @@
 					<row idgoal='1'>
 						<nb_conversions>1</nb_conversions>
 						<nb_visits_converted>1</nb_visits_converted>
-						<revenue>42.25</revenue>
+						<revenue>42.26</revenue>
 					</row>
 				</goals>
 				<nb_conversions>1</nb_conversions>
-				<revenue>42.25</revenue>
+				<revenue>42.26</revenue>
 			</row>
 		</subtable>
 	</row>
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_day.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_day.xml
index 12de83797fd7a192fc8cd7ce1d3e0a597e0b1b04..e91916f991db56b8f95170e38872e5fd530f59bc 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_day.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_day.xml
@@ -27,11 +27,9 @@
 				<nb_uniq_visitors>1</nb_uniq_visitors>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
-				<exit_nb_visits>1</exit_nb_visits>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 			</row>
 		</result>
 		<result date="2010-01-05">
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_month.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_month.xml
index e0dd626f42f6e123d2788637baf6f9f0a838ee00..dcfde2bd995c6df8f2e74aefb801a41e8578123e 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_month.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_month.xml
@@ -79,12 +79,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 			</row>
 		</result>
 		<result date="2010-02" />
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_week.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_week.xml
index 5ad111fece393d4f416d33e82e25d81a9e35f4f4..11c2526451ef9570570e1a7c582adffa472ce592 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_week.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_week.xml
@@ -81,12 +81,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 			</row>
 		</result>
 		<result date="2010-01-11 to 2010-01-17" />
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_year.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_year.xml
index 5776b676b7347a8a7f285ad246efdb1ea7007f43..5ae46a54f32dc29e2adec790591c2eb563157180 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_year.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageTitles_year.xml
@@ -79,12 +79,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 			</row>
 		</result>
 		<result date="2011" />
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_day.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_day.xml
index fea2843acc7e73507451b4d719d04efac60f19fa..92cc681d9d852f2d63c131db50c06c3f25ec366f 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_day.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_day.xml
@@ -39,11 +39,9 @@
 				<nb_uniq_visitors>1</nb_uniq_visitors>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
-				<exit_nb_visits>1</exit_nb_visits>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 				<url></url>
 			</row>
 		</result>
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_month.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_month.xml
index 6b29877f2b7b63c877b34466c173e7cd84c87bae..b9d1acb081fec9fa82b6e06c93ee5747f2d9b822 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_month.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_month.xml
@@ -52,12 +52,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 				<url></url>
 			</row>
 		</result>
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_week.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_week.xml
index 9225c807cc690bfbcb6189770a3d858cb134ad5e..a6916d10584f37cb650d75098fc785b04922c3d5 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_week.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_week.xml
@@ -65,12 +65,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 				<url></url>
 			</row>
 		</result>
diff --git a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_year.xml b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_year.xml
index 0ce97c43b0b80e567f8d67afb76c563c8d7cdfe9..19b419e61f507cabb3a9b7a1451ff5e18649c79d 100644
--- a/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_year.xml
+++ b/tests/integration/expected/test_TwoVisitors_twoWebsites_differentDays__Actions.getPageUrls_year.xml
@@ -52,12 +52,10 @@
 				<nb_visits>1</nb_visits>
 				<nb_hits>1</nb_hits>
 				<sum_time_spent>0</sum_time_spent>
-				<exit_nb_visits>1</exit_nb_visits>
 				<sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
-				<sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
 				<avg_time_on_page>0</avg_time_on_page>
 				<bounce_rate>0%</bounce_rate>
-				<exit_rate>100%</exit_rate>
+				<exit_rate>0%</exit_rate>
 				<url></url>
 			</row>
 		</result>
diff --git a/tests/integration/expected/test_apiGetReportMetadata__API.getMetadata.xml b/tests/integration/expected/test_apiGetReportMetadata__API.getMetadata.xml
deleted file mode 100644
index 1c8ffd506bd7829467ccfafdcbc8a5f399db4e6b..0000000000000000000000000000000000000000
--- a/tests/integration/expected/test_apiGetReportMetadata__API.getMetadata.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<row>
-		<category>Visitors</category>
-		<name>Country</name>
-		<module>UserCountry</module>
-		<action>getCountry</action>
-		<dimension>Country</dimension>
-		<metrics>
-			<nb_visits>Visits</nb_visits>
-			<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
-			<nb_actions>Actions</nb_actions>
-
-		</metrics>
-		<processedMetrics>
-			<nb_actions_per_visit>Actions per Visit</nb_actions_per_visit>
-			<avg_time_on_site>Avg. Time on Website</avg_time_on_site>
-			<bounce_rate>Bounce Rate</bounce_rate>
-			<conversion_rate>Conversion Rate</conversion_rate>
-
-		</processedMetrics>
-		<metricsGoal>
-			<nb_conversions>Conversions</nb_conversions>
-			<conversion_rate>Conversion Rate</conversion_rate>
-			<revenue>Revenue</revenue>
-
-		</metricsGoal>
-		<processedMetricsGoal>
-			<revenue_per_visit>Value per Visit</revenue_per_visit>
-
-		</processedMetricsGoal>
-		<uniqueId>UserCountry_getCountry</uniqueId>
-
-	</row>
-
-</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml b/tests/integration/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml
index 0ac04890708580d40dd7e0d7a0b45e935e9dbdb9..a9b9650936680e5191902bc253c5bc01d2f208d0 100644
--- a/tests/integration/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml
+++ b/tests/integration/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml
@@ -62,7 +62,7 @@
 			<nb_uniq_visitors>1</nb_uniq_visitors>
 			<nb_visits>1</nb_visits>
 			<nb_actions>1</nb_actions>
-			<revenue>$ 42.25</revenue>
+			<revenue>$ 42.26</revenue>
 			<nb_actions_per_visit>1</nb_actions_per_visit>
 			<avg_time_on_site>00:18:00</avg_time_on_site>
 			<bounce_rate>100%</bounce_rate>
diff --git a/tests/integration/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml b/tests/integration/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
index 94659d9ff4b22b1ba2df997bd101964d9f95c8dc..9bcc275e38f3023404b0246c7ff2c9ca23ed7aec 100644
--- a/tests/integration/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
+++ b/tests/integration/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
@@ -7,6 +7,13 @@
 		<segment>visitConverted</segment>
 		<acceptedValues>0, 1</acceptedValues>
 	</row>
+	<row>
+		<type>metric</type>
+		<category>Visit</category>
+		<name>Visit Ecommerce status at the end of the visit. For example, to select all visits that have made an Ecommerce order, the API request would contain &quot;&amp;segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart&quot;</name>
+		<segment>visitEcommerceStatus</segment>
+		<acceptedValues>none, ordered, abandonedCart, orderedThenAbandonedCart</acceptedValues>
+	</row>
 	<row>
 		<type>metric</type>
 		<category>Visit</category>
@@ -31,6 +38,12 @@
 		<name>Days since last visit</name>
 		<segment>daysSinceLastVisit</segment>
 	</row>
+	<row>
+		<type>metric</type>
+		<category>Visit</category>
+		<name>Days since last Ecommerce order</name>
+		<segment>daysSinceLastEcommerceOrder</segment>
+	</row>
 	<row>
 		<type>metric</type>
 		<category>Visit</category>
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..53a82090efb1cad986c807f7b2bdfd08d9a8edcd
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_day.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Category TWO LEFT in cart</label>
+		<revenue>4000</revenue>
+		<quantity>4.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>Product Category not defined</label>
+		<revenue>1000.22</revenue>
+		<quantity>2.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..263c329f3e86e02268ec2bb32a34c250253631c0
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsCategory_week.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Category TWO LEFT in cart</label>
+		<revenue>6000</revenue>
+		<quantity>6</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>Product Category not defined</label>
+		<revenue>1500.33</revenue>
+		<quantity>3</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c3b0b3a35840b470d637bba439905ac6ba575af5
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_day.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>PRODUCT TWO LEFT in cart</label>
+		<revenue>4000</revenue>
+		<quantity>4.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>PRODUCT ONE LEFT in cart</label>
+		<revenue>1000.22</revenue>
+		<quantity>2.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1d603853ae14dc92ae72e94b0ba65e81f2101249
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsName_week.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>PRODUCT TWO LEFT in cart</label>
+		<revenue>6000</revenue>
+		<quantity>6</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>PRODUCT ONE LEFT in cart</label>
+		<revenue>1500.33</revenue>
+		<quantity>3</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2d73a799ff8bf28f10dc2c11ca132df823b608c8
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_day.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>SKU IN ABANDONED CART TWO</label>
+		<revenue>4000</revenue>
+		<quantity>4.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>SKU IN ABANDONED CART ONE</label>
+		<revenue>1000.22</revenue>
+		<quantity>2.00</quantity>
+		<abandoned_carts>2</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..59fd6ae2eff7866a922e2f1db4b5e0d1010acf8a
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_AbandonedCarts__Goals.getItemsSku_week.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>SKU IN ABANDONED CART TWO</label>
+		<revenue>6000</revenue>
+		<quantity>6</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>1000</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>SKU IN ABANDONED CART ONE</label>
+		<revenue>1500.33</revenue>
+		<quantity>3</quantity>
+		<abandoned_carts>3</abandoned_carts>
+		<avg_price>500.11</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..693e7dbc3235c99ae4104c1f7238402917281e82
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_day.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>2</nb_conversions>
+	<nb_visits_converted>2</nb_visits_converted>
+	<conversion_rate>100</conversion_rate>
+	<revenue>5000.22</revenue>
+	<items>6</items>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5c8effaf28e60e69969c70378ca6379eff4e4688
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalAbandonedCart__Goals.get_week.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>3</nb_conversions>
+	<nb_visits_converted>3</nb_visits_converted>
+	<conversion_rate>75</conversion_rate>
+	<revenue>7500.33</revenue>
+	<items>9</items>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc62e5e6a5df74ba0f66b23a081d9a783f543ceb
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_day.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>1</nb_conversions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<conversion_rate>50</conversion_rate>
+	<revenue>10</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c29e706e50d3f269af6de0edf48709672021bc39
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalMatchTitle__Goals.get_week.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>1</nb_conversions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<conversion_rate>25</conversion_rate>
+	<revenue>10</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..857aa10333bfa7ef8c2649543ffbab8f50b5655f
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_day.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>2</nb_conversions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<conversion_rate>50</conversion_rate>
+	<revenue>3111.11</revenue>
+	<revenue_subtotal>2500</revenue_subtotal>
+	<revenue_tax>511</revenue_tax>
+	<revenue_shipping>100.11</revenue_shipping>
+	<revenue_discount>666</revenue_discount>
+	<items>3</items>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..af971d80480f5f73afbcf55b45206b77ae9070e9
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOrder__Goals.get_week.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>4</nb_conversions>
+	<nb_visits_converted>2</nb_visits_converted>
+	<conversion_rate>50</conversion_rate>
+	<revenue>13351.1</revenue>
+	<revenue_subtotal>2700</revenue_subtotal>
+	<revenue_tax>531</revenue_tax>
+	<revenue_shipping>120.11</revenue_shipping>
+	<revenue_discount>686</revenue_discount>
+	<items>5</items>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5867f1dd24b8bf86d7b9a1dbec8aa9dbf34c1143
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_day.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>3</nb_conversions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<conversion_rate>50</conversion_rate>
+	<revenue>3121.11</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3b8f44347024ce2acfebe903b6f08a70336f3b30
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_GoalOverall__Goals.get_week.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>5</nb_conversions>
+	<nb_visits_converted>3</nb_visits_converted>
+	<conversion_rate>75</conversion_rate>
+	<revenue>13361.1</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsForVisitor.xml b/tests/integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
similarity index 55%
rename from tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsForVisitor.xml
rename to tests/integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
index 9a016901b2e642701b0702d735c8bc77d42dc28c..d2520843fa87c919a09ca3562904ee665d90cb63 100644
--- a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsForVisitor.xml
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
@@ -2,31 +2,38 @@
 <result>
 	<row>
 		<idSite>1</idSite>
-		<idVisit>3</idVisit>
+		<idVisit>4</idVisit>
 		<visitIp>156.5.3.2</visitIp>
 		
 		<visitorType>returning</visitorType>
-		<visitConverted>0</visitConverted>
+		<visitConverted>1</visitConverted>
+		<visitEcommerceStatus>ordered</visitEcommerceStatus>
 		<actions>1</actions>
 		<actionDetails>
 			<row>
-				<type>outlink</type>
-				<url>http://test.com</url>
-				<pageTitle></pageTitle>
-				<pageIdAction>5</pageIdAction>
-				<pageId>4</pageId>
+				<type>ecommerceOrder</type>
+				<orderId>666</orderId>
+				<revenue>240</revenue>
+				<revenueSubTotal>200</revenueSubTotal>
+				<revenueTax>20</revenueTax>
+				<revenueShipping>20</revenueShipping>
+				<revenueDiscount>20</revenueDiscount>
+				<items>2</items>
 				
 			</row>
-		</actionDetails>
-		<customVariables>
-			<row>
-				<customVariableName1>VisitorType</customVariableName1>
-				<customVariableValue1>LoggedOut</customVariableValue1>
-			</row>
 			<row>
-				<customVariableName2>Othercustom value which should be truncated abcdef</customVariableName2>
-				<customVariableValue2>abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx</customVariableValue2>
+				<type>ecommerceOrder</type>
+				<orderId>777</orderId>
+				<revenue>10000</revenue>
+				<revenueSubTotal>0</revenueSubTotal>
+				<revenueTax>0</revenueTax>
+				<revenueShipping>0</revenueShipping>
+				<revenueDiscount>0</revenueDiscount>
+				<items>0</items>
+				
 			</row>
+		</actionDetails>
+		<customVariables>
 		</customVariables>
 		<goalConversions>0</goalConversions>
 		<siteCurrency>USD</siteCurrency>
@@ -35,11 +42,12 @@
 		
 		
 		
-		<visitDuration>0</visitDuration>
-		<visitDurationPretty>0s</visitDurationPretty>
+		<visitDuration>360</visitDuration>
+		<visitDurationPretty>6 min 0s</visitDurationPretty>
 		<visitCount>1</visitCount>
 		<daysSinceLastVisit>0</daysSinceLastVisit>
 		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
 		<country>France</country>
 		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
 		<continent>Europe</continent>
@@ -47,10 +55,10 @@
 		<providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
 		<referrerType>direct</referrerType>
 		<referrerTypeName>Direct Entry</referrerTypeName>
+		<referrerName></referrerName>
 		<referrerKeyword></referrerKeyword>
 		<referrerKeywordPosition></referrerKeywordPosition>
 		<referrerUrl></referrerUrl>
-		<referrerName></referrerName>
 		<referrerSearchEngineUrl></referrerSearchEngineUrl>
 		<referrerSearchEngineIcon></referrerSearchEngineIcon>
 		<operatingSystem>Windows XP</operatingSystem>
@@ -58,11 +66,11 @@
 		<operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
 		<browserFamily>gecko</browserFamily>
 		<browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
-		<browserName>Firefox 3.0</browserName>
+		<browserName>Firefox 3.6</browserName>
 		<browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
-		<screenType>dual</screenType>
-		<resolution>1111x222</resolution>
-		<screenTypeIcon>plugins/UserSettings/images/screens/dual.gif</screenTypeIcon>
+		<screenType>normal</screenType>
+		<resolution>1024x768</resolution>
+		<screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
 		<plugins>flash, java</plugins>
 		<pluginsIcons>
 			<row>
@@ -79,36 +87,62 @@
 		
 		
 		
+		<ecommerce>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>666</orderId>
+				<revenue>240</revenue>
+				<revenueSubTotal>200</revenueSubTotal>
+				<revenueTax>20</revenueTax>
+				<revenueShipping>20</revenueShipping>
+				<revenueDiscount>20</revenueDiscount>
+				<items>2</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>TRIPOD SKU</itemSKU>
+						<itemName>TRIPOD - bought day after</itemName>
+						<itemCategory>Tools</itemCategory>
+						<price>100</price>
+						<quantity>2</quantity>
+					</row>
+				</itemDetails>
+			</row>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>777</orderId>
+				<revenue>10000</revenue>
+				<revenueSubTotal>0</revenueSubTotal>
+				<revenueTax>0</revenueTax>
+				<revenueShipping>0</revenueShipping>
+				<revenueDiscount>0</revenueDiscount>
+				<items>0</items>
+				
+				<itemDetails>
+				</itemDetails>
+			</row>
+		</ecommerce>
 	</row>
 	<row>
 		<idSite>1</idSite>
-		<idVisit>2</idVisit>
+		<idVisit>3</idVisit>
 		<visitIp>156.5.3.2</visitIp>
 		
-		<visitorType>new</visitorType>
+		<visitorType>returning</visitorType>
 		<visitConverted>1</visitConverted>
+		<visitEcommerceStatus>orderedThenAbandonedCart</visitEcommerceStatus>
 		<actions>1</actions>
 		<actionDetails>
 			<row>
-				<type>goal</type>
-				<goalName>triggered js</goalName>
-				<revenue>0</revenue>
-				<goalPageId></goalPageId>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
 				
-				<url>http://example.org/homepage</url>
 			</row>
 		</actionDetails>
 		<customVariables>
-			<row>
-				<customVariableName1>VisitorType</customVariableName1>
-				<customVariableValue1>LoggedOut</customVariableValue1>
-			</row>
-			<row>
-				<customVariableName2>Othercustom value which should be truncated abcdef</customVariableName2>
-				<customVariableValue2>abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx</customVariableValue2>
-			</row>
 		</customVariables>
-		<goalConversions>1</goalConversions>
+		<goalConversions>0</goalConversions>
 		<siteCurrency>USD</siteCurrency>
 		
 		<visitLocalTime>12:34:06</visitLocalTime>
@@ -120,6 +154,7 @@
 		<visitCount>1</visitCount>
 		<daysSinceLastVisit>0</daysSinceLastVisit>
 		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
 		<country>France</country>
 		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
 		<continent>Europe</continent>
@@ -127,10 +162,10 @@
 		<providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
 		<referrerType>direct</referrerType>
 		<referrerTypeName>Direct Entry</referrerTypeName>
+		<referrerName></referrerName>
 		<referrerKeyword></referrerKeyword>
 		<referrerKeywordPosition></referrerKeywordPosition>
 		<referrerUrl></referrerUrl>
-		<referrerName></referrerName>
 		<referrerSearchEngineUrl></referrerSearchEngineUrl>
 		<referrerSearchEngineIcon></referrerSearchEngineIcon>
 		<operatingSystem>Windows XP</operatingSystem>
@@ -138,11 +173,11 @@
 		<operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
 		<browserFamily>gecko</browserFamily>
 		<browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
-		<browserName>Firefox 3.0</browserName>
+		<browserName>Firefox 3.6</browserName>
 		<browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
-		<screenType>dual</screenType>
-		<resolution>1111x222</resolution>
-		<screenTypeIcon>plugins/UserSettings/images/screens/dual.gif</screenTypeIcon>
+		<screenType>normal</screenType>
+		<resolution>1024x768</resolution>
+		<screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
 		<plugins>flash, java</plugins>
 		<pluginsIcons>
 			<row>
@@ -159,5 +194,29 @@
 		
 		
 		
+		<ecommerce>
+			<row>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART ONE</itemSKU>
+						<itemName>PRODUCT ONE LEFT in cart</itemName>
+						<itemCategory></itemCategory>
+						<price>500.11</price>
+						<quantity>1</quantity>
+					</row>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART TWO</itemSKU>
+						<itemName>PRODUCT TWO LEFT in cart</itemName>
+						<itemCategory>Category TWO LEFT in cart</itemCategory>
+						<price>1000</price>
+						<quantity>2</quantity>
+					</row>
+				</itemDetails>
+			</row>
+		</ecommerce>
 	</row>
 </result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_SegmentAbandonedCart__VisitsSummary.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentAbandonedCart__VisitsSummary.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e8e1fa23a484b209e7a70c21cd592f7d1555095e
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentAbandonedCart__VisitsSummary.get_day.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_visits>2</nb_visits>
+	<nb_uniq_visitors>1</nb_uniq_visitors>
+	<nb_actions>2</nb_actions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<bounce_count>2</bounce_count>
+	<sum_visit_length>2340</sum_visit_length>
+	<max_actions>1</max_actions>
+	<bounce_rate>100%</bounce_rate>
+	<nb_actions_per_visit>1</nb_actions_per_visit>
+	<avg_time_on_site>1170</avg_time_on_site>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_SegmentNoEcommerce__VisitsSummary.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentNoEcommerce__VisitsSummary.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a4933d174cc5e36fd57dc0b4cd903f7a83646556
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentNoEcommerce__VisitsSummary.get_day.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_visits>0</nb_visits>
+	<nb_uniq_visitors>0</nb_uniq_visitors>
+	<nb_actions>0</nb_actions>
+	<nb_visits_converted>0</nb_visits_converted>
+	<bounce_count>0</bounce_count>
+	<sum_visit_length>0</sum_visit_length>
+	<max_actions>0</max_actions>
+	<bounce_rate>0%</bounce_rate>
+	<nb_actions_per_visit>0</nb_actions_per_visit>
+	<avg_time_on_site>0</avg_time_on_site>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems_SegmentOrderedSomething__VisitsSummary.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentOrderedSomething__VisitsSummary.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..95b8e6c66735855faf3e982c5ec3aed29d13a990
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems_SegmentOrderedSomething__VisitsSummary.get_day.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_visits>1</nb_visits>
+	<nb_uniq_visitors>1</nb_uniq_visitors>
+	<nb_actions>1</nb_actions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<bounce_count>1</bounce_count>
+	<sum_visit_length>2340</sum_visit_length>
+	<max_actions>1</max_actions>
+	<bounce_rate>100%</bounce_rate>
+	<nb_actions_per_visit>1</nb_actions_per_visit>
+	<avg_time_on_site>2340</avg_time_on_site>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..959c03d202aceb3f4db042df3f385d5d155d20cf
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<website>Piwik test</website>
+	<prettyDate>Tuesday 5 April 2011</prettyDate>
+	<metadata>
+		<category>Visitors</category>
+		<name>Country</name>
+		<module>UserCountry</module>
+		<action>getCountry</action>
+		<dimension>Country</dimension>
+		<metrics>
+			<nb_visits>Visits</nb_visits>
+			<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
+			<nb_actions>Actions</nb_actions>
+
+		</metrics>
+		<processedMetrics>
+			<nb_actions_per_visit>Actions per Visit</nb_actions_per_visit>
+			<avg_time_on_site>Avg. Time on Website</avg_time_on_site>
+			<bounce_rate>Bounce Rate</bounce_rate>
+
+		</processedMetrics>
+		<metricsDocumentation>
+			<nb_visits>If a visitor comes to your website for the first time or if he visits a page more than 30 minutes after his last page view, this will be recorded as a new visit.</nb_visits>
+			<nb_uniq_visitors>The number of unduplicated visitors coming to your website. Every user is only counted once, even if he visits the website multiple times a day.</nb_uniq_visitors>
+			<nb_actions>The number of actions performed by your visitors. Actions can be page views, downloads or outlinks.</nb_actions>
+			<nb_actions_per_visit>The average number of actions (page views, downloads or outlinks) that were performed during the visits.</nb_actions_per_visit>
+			<avg_time_on_site>The average duration of a visit.</avg_time_on_site>
+			<bounce_rate>The percentage of visits that only had a single pageview. This means, that the visitor left the website directly from the entrance page.</bounce_rate>
+			<conversion_rate>The percentage of visits that triggered a goal conversion.</conversion_rate>
+			<avg_time_on_page>The average amount of time visitors spent on this page (only the page, not the entire website).</avg_time_on_page>
+			<nb_hits>The number of times this page was visited.</nb_hits>
+			<exit_rate>The percentage of visits that left the website after viewing this page (unique pageviews devided by exists)</exit_rate>
+
+		</metricsDocumentation>
+		<metricsGoal>
+			<nb_conversions>Conversions</nb_conversions>
+			<revenue>Revenue</revenue>
+
+		</metricsGoal>
+		<processedMetricsGoal>
+			<revenue_per_visit>Revenue per Visit</revenue_per_visit>
+
+		</processedMetricsGoal>
+		<uniqueId>UserCountry_getCountry</uniqueId>
+
+	</metadata>
+	<columns>
+		<label>Country</label>
+		<nb_visits>Visits</nb_visits>
+		<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
+		<nb_actions>Actions</nb_actions>
+		<nb_actions_per_visit>Actions per Visit</nb_actions_per_visit>
+		<avg_time_on_site>Avg. Time on Website</avg_time_on_site>
+		<bounce_rate>Bounce Rate</bounce_rate>
+		<revenue>Revenue</revenue>
+
+	</columns>
+	<reportData>
+		<row>
+			<label>France</label>
+			<nb_uniq_visitors>1</nb_uniq_visitors>
+			<nb_visits>2</nb_visits>
+			<nb_actions>2</nb_actions>
+			<revenue>$ 3121.11</revenue>
+			<nb_actions_per_visit>1</nb_actions_per_visit>
+			<avg_time_on_site>00:19:30</avg_time_on_site>
+			<bounce_rate>100%</bounce_rate>
+
+		</row>
+
+	</reportData>
+	<reportMetadata>
+		<row>
+			<code>fr</code>
+			<logo>plugins/UserCountry/flags/fr.png</logo>
+			<logoWidth>18</logoWidth>
+			<logoHeight>12</logoHeight>
+
+		</row>
+
+	</reportMetadata>
+
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getConversions_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getConversions_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..15ef03fb49cfea4767aa035a031e96c3b348bc93
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getConversions_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>3</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0112cf98d43d36d59a260eab4591fccd3dbc9e48
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_day.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Electronics &amp; Cameras</label>
+		<revenue>2500</revenue>
+		<quantity>3.00</quantity>
+		<orders>2</orders>
+		<avg_price>1000</avg_price>
+		<avg_quantity>1.5</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..84a8f9e8998b04355e4a5784f10e703685def197
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsCategory_week.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Electronics &amp; Cameras</label>
+		<revenue>2500</revenue>
+		<quantity>3.00</quantity>
+		<orders>2</orders>
+		<avg_price>1000</avg_price>
+		<avg_quantity>1.5</avg_quantity>
+	</row>
+	<row>
+		<label>Tools</label>
+		<revenue>200</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>100</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d72196172abd0d64702ee375e21cc241e570ffa7
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_day.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Canon SLR</label>
+		<revenue>1500</revenue>
+		<quantity>1.00</quantity>
+		<orders>1</orders>
+		<avg_price>1500</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+	<row>
+		<label>PRODUCT name</label>
+		<revenue>1000</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>500</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4199ef33379a7564ba14c98d145dddf3f70f00dc
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsName_week.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Canon SLR</label>
+		<revenue>1500</revenue>
+		<quantity>1.00</quantity>
+		<orders>1</orders>
+		<avg_price>1500</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+	<row>
+		<label>PRODUCT name</label>
+		<revenue>1000</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>500</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>TRIPOD - bought day after</label>
+		<revenue>200</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>100</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..43bc239c7121bdeb75554c94b05317364bd75396
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_day.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>SKU2</label>
+		<revenue>1500</revenue>
+		<quantity>1.00</quantity>
+		<orders>1</orders>
+		<avg_price>1500</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+	<row>
+		<label>SKU VERY nice indeed</label>
+		<revenue>1000</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>500</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6263163fd88a4c003bd5a6e76bcb00e7d6564d99
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.getItemsSku_week.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>SKU2</label>
+		<revenue>1500</revenue>
+		<quantity>1.00</quantity>
+		<orders>1</orders>
+		<avg_price>1500</avg_price>
+		<avg_quantity>1</avg_quantity>
+	</row>
+	<row>
+		<label>SKU VERY nice indeed</label>
+		<revenue>1000</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>500</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+	<row>
+		<label>TRIPOD SKU</label>
+		<revenue>200</revenue>
+		<quantity>2.00</quantity>
+		<orders>1</orders>
+		<avg_price>100</avg_price>
+		<avg_quantity>2</avg_quantity>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5867f1dd24b8bf86d7b9a1dbec8aa9dbf34c1143
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_day.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>3</nb_conversions>
+	<nb_visits_converted>1</nb_visits_converted>
+	<conversion_rate>50</conversion_rate>
+	<revenue>3121.11</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_week.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_week.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3b8f44347024ce2acfebe903b6f08a70336f3b30
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Goals.get_week.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<nb_conversions>5</nb_conversions>
+	<nb_visits_converted>3</nb_visits_converted>
+	<conversion_rate>75</conversion_rate>
+	<revenue>13361.1</revenue>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7fddc2974fc30828f50474d22ed7d49f0433233e
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<idSite>1</idSite>
+		<idVisit>2</idVisit>
+		<visitIp>156.5.3.2</visitIp>
+		
+		<visitorType>returning</visitorType>
+		<visitConverted>0</visitConverted>
+		<visitEcommerceStatus>abandonedCart</visitEcommerceStatus>
+		<actions>1</actions>
+		<actionDetails>
+			<row>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
+				
+			</row>
+		</actionDetails>
+		<customVariables>
+		</customVariables>
+		<goalConversions>0</goalConversions>
+		<siteCurrency>USD</siteCurrency>
+		
+		<visitLocalTime>12:34:06</visitLocalTime>
+		
+		
+		
+		<visitDuration>0</visitDuration>
+		<visitDurationPretty>0s</visitDurationPretty>
+		<visitCount>1</visitCount>
+		<daysSinceLastVisit>0</daysSinceLastVisit>
+		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+		<country>France</country>
+		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
+		<continent>Europe</continent>
+		<provider>Unknown</provider>
+		<providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
+		<referrerType>direct</referrerType>
+		<referrerTypeName>Direct Entry</referrerTypeName>
+		<referrerName></referrerName>
+		<referrerKeyword></referrerKeyword>
+		<referrerKeywordPosition></referrerKeywordPosition>
+		<referrerUrl></referrerUrl>
+		<referrerSearchEngineUrl></referrerSearchEngineUrl>
+		<referrerSearchEngineIcon></referrerSearchEngineIcon>
+		<operatingSystem>Windows XP</operatingSystem>
+		<operatingSystemShortName>Win XP</operatingSystemShortName>
+		<operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
+		<browserFamily>gecko</browserFamily>
+		<browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+		<browserName>Firefox 3.6</browserName>
+		<browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
+		<screenType>normal</screenType>
+		<resolution>1024x768</resolution>
+		<screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
+		<plugins>flash, java</plugins>
+		<pluginsIcons>
+			<row>
+				<pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
+				<pluginName>flash</pluginName>
+			</row>
+			<row>
+				<pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
+				<pluginName>java</pluginName>
+			</row>
+		</pluginsIcons>
+		
+		
+		
+		
+		
+		<ecommerce>
+			<row>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART ONE</itemSKU>
+						<itemName>PRODUCT ONE LEFT in cart</itemName>
+						<itemCategory></itemCategory>
+						<price>500.11</price>
+						<quantity>1</quantity>
+					</row>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART TWO</itemSKU>
+						<itemName>PRODUCT TWO LEFT in cart</itemName>
+						<itemCategory>Category TWO LEFT in cart</itemCategory>
+						<price>1000</price>
+						<quantity>2</quantity>
+					</row>
+				</itemDetails>
+			</row>
+		</ecommerce>
+	</row>
+	<row>
+		<idSite>1</idSite>
+		<idVisit>1</idVisit>
+		<visitIp>156.5.3.2</visitIp>
+		
+		<visitorType>new</visitorType>
+		<visitConverted>1</visitConverted>
+		<visitEcommerceStatus>orderedThenAbandonedCart</visitEcommerceStatus>
+		<actions>1</actions>
+		<actionDetails>
+			<row>
+				<type>action</type>
+				<url>http://example.org/index.htm</url>
+				<pageTitle>incredible title!</pageTitle>
+				<pageIdAction>2</pageIdAction>
+				<pageId>1</pageId>
+				
+			</row>
+			<row>
+				<type>goal</type>
+				<goalName>triggered js ONCE</goalName>
+				<revenue>10</revenue>
+				<goalPageId>1</goalPageId>
+				
+				<url>http://example.org/index.htm</url>
+			</row>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>937nsjusu 3894</orderId>
+				<revenue>1111.11</revenue>
+				<revenueSubTotal>1000</revenueSubTotal>
+				<revenueTax>111</revenueTax>
+				<revenueShipping>0.11</revenueShipping>
+				<revenueDiscount>666</revenueDiscount>
+				<items>2</items>
+				
+			</row>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>1037nsjusu4s3894</orderId>
+				<revenue>2000</revenue>
+				<revenueSubTotal>1500</revenueSubTotal>
+				<revenueTax>400</revenueTax>
+				<revenueShipping>100</revenueShipping>
+				<revenueDiscount>0</revenueDiscount>
+				<items>1</items>
+				
+			</row>
+			<row>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
+				
+			</row>
+		</actionDetails>
+		<customVariables>
+		</customVariables>
+		<goalConversions>1</goalConversions>
+		<siteCurrency>USD</siteCurrency>
+		
+		<visitLocalTime>12:34:06</visitLocalTime>
+		
+		
+		
+		<visitDuration>2340</visitDuration>
+		<visitDurationPretty>39 min 0s</visitDurationPretty>
+		<visitCount>1</visitCount>
+		<daysSinceLastVisit>0</daysSinceLastVisit>
+		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+		<country>France</country>
+		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
+		<continent>Europe</continent>
+		<provider>Unknown</provider>
+		<providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
+		<referrerType>direct</referrerType>
+		<referrerTypeName>Direct Entry</referrerTypeName>
+		<referrerName></referrerName>
+		<referrerKeyword></referrerKeyword>
+		<referrerKeywordPosition></referrerKeywordPosition>
+		<referrerUrl></referrerUrl>
+		<referrerSearchEngineUrl></referrerSearchEngineUrl>
+		<referrerSearchEngineIcon></referrerSearchEngineIcon>
+		<operatingSystem>Windows XP</operatingSystem>
+		<operatingSystemShortName>Win XP</operatingSystemShortName>
+		<operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
+		<browserFamily>gecko</browserFamily>
+		<browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+		<browserName>Firefox 3.6</browserName>
+		<browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
+		<screenType>normal</screenType>
+		<resolution>1024x768</resolution>
+		<screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
+		<plugins>flash, java</plugins>
+		<pluginsIcons>
+			<row>
+				<pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
+				<pluginName>flash</pluginName>
+			</row>
+			<row>
+				<pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
+				<pluginName>java</pluginName>
+			</row>
+		</pluginsIcons>
+		
+		
+		
+		
+		
+		<ecommerce>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>937nsjusu 3894</orderId>
+				<revenue>1111.11</revenue>
+				<revenueSubTotal>1000</revenueSubTotal>
+				<revenueTax>111</revenueTax>
+				<revenueShipping>0.11</revenueShipping>
+				<revenueDiscount>666</revenueDiscount>
+				<items>2</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>SKU VERY nice indeed</itemSKU>
+						<itemName>PRODUCT name</itemName>
+						<itemCategory>Electronics &amp; Cameras</itemCategory>
+						<price>500</price>
+						<quantity>2</quantity>
+					</row>
+				</itemDetails>
+			</row>
+			<row>
+				<type>ecommerceOrder</type>
+				<orderId>1037nsjusu4s3894</orderId>
+				<revenue>2000</revenue>
+				<revenueSubTotal>1500</revenueSubTotal>
+				<revenueTax>400</revenueTax>
+				<revenueShipping>100</revenueShipping>
+				<revenueDiscount>0</revenueDiscount>
+				<items>1</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>SKU2</itemSKU>
+						<itemName>Canon SLR</itemName>
+						<itemCategory>Electronics &amp; Cameras</itemCategory>
+						<price>1500</price>
+						<quantity>1</quantity>
+					</row>
+				</itemDetails>
+			</row>
+			<row>
+				<type>ecommerceAbandonedCart</type>
+				<revenue>2500.11</revenue>
+				<items>3</items>
+				
+				<itemDetails>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART ONE</itemSKU>
+						<itemName>PRODUCT ONE LEFT in cart</itemName>
+						<itemCategory></itemCategory>
+						<price>500.11</price>
+						<quantity>1</quantity>
+					</row>
+					<row>
+						<itemSKU>SKU IN ABANDONED CART TWO</itemSKU>
+						<itemName>PRODUCT TWO LEFT in cart</itemName>
+						<itemCategory>Category TWO LEFT in cart</itemCategory>
+						<price>1000</price>
+						<quantity>2</quantity>
+					</row>
+				</itemDetails>
+			</row>
+		</ecommerce>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f96445a3b91cbc9632befb5cf998f46b8ddd8160
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>Europe</label>
+		<nb_uniq_visitors>1</nb_uniq_visitors>
+		<nb_visits>2</nb_visits>
+		<nb_actions>2</nb_actions>
+		<max_actions>1</max_actions>
+		<sum_visit_length>2340</sum_visit_length>
+		<bounce_count>2</bounce_count>
+		<goals>
+			<row idgoal='ecommerceAbandonedCart'>
+				<nb_conversions>2</nb_conversions>
+				<nb_visits_converted>2</nb_visits_converted>
+				<revenue>5000.22</revenue>
+				<items>6</items>
+			</row>
+			<row idgoal='ecommerceOrder'>
+				<nb_conversions>2</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>3111.11</revenue>
+				<revenue_subtotal>2500</revenue_subtotal>
+				<revenue_tax>511</revenue_tax>
+				<revenue_shipping>100.11</revenue_shipping>
+				<revenue_discount>666</revenue_discount>
+				<items>3</items>
+			</row>
+			<row idgoal='1'>
+				<nb_conversions>1</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>10</revenue>
+			</row>
+		</goals>
+		<nb_conversions>3</nb_conversions>
+		<revenue>3121.11</revenue>
+		<code>Europe</code>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getCountry_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getCountry_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2509d485b5b581604797f540f3a841b258dc9d49
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getCountry_day.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+	<row>
+		<label>France</label>
+		<nb_uniq_visitors>1</nb_uniq_visitors>
+		<nb_visits>2</nb_visits>
+		<nb_actions>2</nb_actions>
+		<max_actions>1</max_actions>
+		<sum_visit_length>2340</sum_visit_length>
+		<bounce_count>2</bounce_count>
+		<goals>
+			<row idgoal='ecommerceAbandonedCart'>
+				<nb_conversions>2</nb_conversions>
+				<nb_visits_converted>2</nb_visits_converted>
+				<revenue>5000.22</revenue>
+				<items>6</items>
+			</row>
+			<row idgoal='ecommerceOrder'>
+				<nb_conversions>2</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>3111.11</revenue>
+				<revenue_subtotal>2500</revenue_subtotal>
+				<revenue_tax>511</revenue_tax>
+				<revenue_shipping>100.11</revenue_shipping>
+				<revenue_discount>666</revenue_discount>
+				<items>3</items>
+			</row>
+			<row idgoal='1'>
+				<nb_conversions>1</nb_conversions>
+				<nb_visits_converted>1</nb_visits_converted>
+				<revenue>10</revenue>
+			</row>
+		</goals>
+		<nb_conversions>3</nb_conversions>
+		<revenue>3121.11</revenue>
+		<code>fr</code>
+		<logo>plugins/UserCountry/flags/fr.png</logo>
+		<logoWidth>18</logoWidth>
+		<logoHeight>12</logoHeight>
+	</row>
+</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getNumberOfDistinctCountries_day.xml b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getNumberOfDistinctCountries_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..606fbb524182170284d7f1baad7fce4697d9b8b3
--- /dev/null
+++ b/tests/integration/expected/test_ecommerceOrderWithItems__UserCountry.getNumberOfDistinctCountries_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>1</result>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit__Goals.getItemsCategory_day.xml b/tests/integration/expected/test_noVisit__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_noVisit__Goals.getItemsCategory_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit__Goals.getItemsName_day.xml b/tests/integration/expected/test_noVisit__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_noVisit__Goals.getItemsName_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit__Goals.getItemsSku_day.xml b/tests/integration/expected/test_noVisit__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c234bed59e963e268d7a9bc05348d941758c4aa9
--- /dev/null
+++ b/tests/integration/expected/test_noVisit__Goals.getItemsSku_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result />
\ No newline at end of file
diff --git a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__API.getProcessedReport_range.xml b/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__API.getProcessedReport_range.xml
index ca5197dbde291dd4cc1bccdcabc77c3f4c6217a4..3c5635cb3ffe8600efef4d003acf3035502a8aad 100644
--- a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__API.getProcessedReport_range.xml
+++ b/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__API.getProcessedReport_range.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <result>
 	<website>Piwik test</website>
-	<prettyDate>29 Apr 11 - 5 May 11</prettyDate>
+	<prettyDate>7 May 11 - 13 May 11</prettyDate>
 	<metadata>
 		<category>Visitors</category>
 		<name>Country</name>
diff --git a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml b/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
index 5042086bf2fc6e5921f7fbe2bf487a22482a60d2..96e14ebee67a05253fa5f6911b21d33e4341760d 100644
--- a/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
+++ b/tests/integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
@@ -7,6 +7,7 @@
 		
 		<visitorType>returning</visitorType>
 		<visitConverted>0</visitConverted>
+		<visitEcommerceStatus>none</visitEcommerceStatus>
 		<actions>1</actions>
 		<actionDetails>
 			<row>
@@ -40,6 +41,7 @@
 		<visitCount>1</visitCount>
 		<daysSinceLastVisit>0</daysSinceLastVisit>
 		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
 		<country>France</country>
 		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
 		<continent>Europe</continent>
@@ -79,6 +81,8 @@
 		
 		
 		
+		<ecommerce>
+		</ecommerce>
 	</row>
 	<row>
 		<idSite>1</idSite>
@@ -87,6 +91,7 @@
 		
 		<visitorType>new</visitorType>
 		<visitConverted>1</visitConverted>
+		<visitEcommerceStatus>none</visitEcommerceStatus>
 		<actions>1</actions>
 		<actionDetails>
 			<row>
@@ -120,6 +125,7 @@
 		<visitCount>1</visitCount>
 		<daysSinceLastVisit>0</daysSinceLastVisit>
 		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
 		<country>France</country>
 		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
 		<continent>Europe</continent>
@@ -159,6 +165,8 @@
 		
 		
 		
+		<ecommerce>
+		</ecommerce>
 	</row>
 	<row>
 		<idSite>1</idSite>
@@ -167,6 +175,7 @@
 		
 		<visitorType>new</visitorType>
 		<visitConverted>1</visitConverted>
+		<visitEcommerceStatus>none</visitEcommerceStatus>
 		<actions>3</actions>
 		<actionDetails>
 			<row>
@@ -220,6 +229,7 @@
 		<visitCount>1</visitCount>
 		<daysSinceLastVisit>0</daysSinceLastVisit>
 		<daysSinceFirstVisit>0</daysSinceFirstVisit>
+		<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
 		<country>France</country>
 		<countryFlag>plugins/UserCountry/flags/fr.png</countryFlag>
 		<continent>Europe</continent>
@@ -259,5 +269,7 @@
 		
 		
 		
+		<ecommerce>
+		</ecommerce>
 	</row>
 </result>
\ No newline at end of file
diff --git a/tests/javascript/index.php b/tests/javascript/index.php
index 26346747832f924c385b15b74e8d20a4d0de9080..8a60b10ae7932f094b7782cdc67084e782c3a263 100644
--- a/tests/javascript/index.php
+++ b/tests/javascript/index.php
@@ -721,7 +721,7 @@ if ($sqlite) {
 	});
 
 	test("tracking", function() {
-		expect(52);
+		expect(59);
 
 		/*
 		 * Prevent Opera and HtmlUnit from performing the default action (i.e., load the href URL)
@@ -887,6 +887,16 @@ if ($sqlite) {
 		tracker3.setCookieNamePrefix("PREFIX");
 		ok( typeof tracker3.getCustomVariable(1) == "undefined", "getCustomVariable(cvarDeleted) from cookie  === undefined" );
 
+		//Ecommerce tests
+		tracker3.addEcommerceItem("SKU PRODUCT", "PRODUCT NAME", "PRODUCT CATEGORY", 11.1111, 2); 
+		tracker3.addEcommerceItem("SKU PRODUCT", "random", "random PRODUCT CATEGORY", 11.1111, 2); 
+		tracker3.addEcommerceItem("SKU ONLY SKU", "", "", "", ""); 
+		tracker3.addEcommerceItem("SKU ONLY NAME", "PRODUCT NAME 2", "", ""); 
+		tracker3.addEcommerceItem("SKU NO PRICE NO QUANTITY", "PRODUCT NAME 3", "CATEGORY", "", "" ); 
+		tracker3.addEcommerceItem("SKU ONLY" ); 
+		tracker3.trackEcommerceCartUpdate( 555.55 );
+		tracker3.trackEcommerceOrder( "ORDER ID YES", 666.66, 333, 222, 111, 1 );
+		
 		// do not track
 		tracker3.setDoNotTrack(false);
 		tracker3.trackPageView("DoTrack");
@@ -900,8 +910,7 @@ if ($sqlite) {
 			xhr.open("GET", "piwik.php?requests=" + getToken(), false);
 			xhr.send(null);
 			results = xhr.responseText;
-
-			equal( (/<span\>([0-9]+)\<\/span\>/.exec(results))[1], "17", "count tracking events" );
+			equal( (/<span\>([0-9]+)\<\/span\>/.exec(results))[1], "19", "count tracking events" );
 
 			// tracking requests
 			ok( /PiwikTest/.test( results ), "trackPageView(), setDocumentTitle()" );
@@ -930,6 +939,25 @@ if ($sqlite) {
 			ok( /DoTrack/.test( results ), "setDoNotTrack(false)" );
 			ok( ! /DoNotTrack/.test( results ), "setDoNotTrack(true)" );
 
+			// Test Custom vars
+			ok( /_cvar=/, "test custom vars are set");
+			
+			// Test campaign parameters set
+			ok( /&_rcn=YEAH&_rck=RIGHT!/.test( results), "Test campaign parameters found"); 
+			ok( /&_ref=http%3A%2F%2Freferrer.example.com%2Fpage%2Fsub%3Fquery%3Dtest%26test2%3Dtest3/.test( results), "Test cookie Ref URL found "); 
+			
+			// Ecommerce order
+			ok( /idgoal=0&ec_id=ORDER%20ID%20YES&revenue=666.66&ec_st=333&ec_tx=222&ec_sh=111&ec_dt=1&ec_items=%5B%5B%22SKU%20PRODUCT%22%2C%22random%22%2C%22random%20PRODUCT%20CATEGORY%22%2C11.1111%2C2%5D%2C%5B%22SKU%20ONLY%20SKU%22%2C%22%22%2C%22%22%2C0%2C1%5D%2C%5B%22SKU%20ONLY%20NAME%22%2C%22PRODUCT%20NAME%202%22%2C%22%22%2C0%2C1%5D%2C%5B%22SKU%20NO%20PRICE%20NO%20QUANTITY%22%2C%22PRODUCT%20NAME%203%22%2C%22CATEGORY%22%2C0%2C1%5D%2C%5B%22SKU%20ONLY%22%2C%22%22%2C%22%22%2C0%2C1%5D%5D/.test( results ), "logEcommerceOrder() with items" );
+
+			// Not set for the first ecommerce order
+			ok( ! /idgoal=0&ec_id=ORDER%20ID.*_ects=1/.test(results), "Ecommerce last timestamp set");
+			
+			// Ecommerce last timestamp set properly for subsequent page view
+			ok( /DoTrack.*_ects=1/.test(results), "Ecommerce last timestamp set");
+
+			// Cart update
+			ok( /idgoal=0&revenue=555.55&ec_items=%5B%5B%22SKU%20PRODUCT%22%2C%22random%22%2C%22random%20PRODUCT%20CATEGORY%22%2C11.1111%2C2%5D%2C%5B%22SKU%20ONLY%20SKU%22%2C%22%22%2C%22%22%2C0%2C1%5D%2C%5B%22SKU%20ONLY%20NAME%22%2C%22PRODUCT%20NAME%202%22%2C%22%22%2C0%2C1%5D%2C%5B%22SKU%20NO%20PRICE%20NO%20QUANTITY%22%2C%22PRODUCT%20NAME%203%22%2C%22CATEGORY%22%2C0%2C1%5D%2C%5B%22SKU%20ONLY%22%2C%22%22%2C%22%22%2C0%2C1%5D%5D/.test( results ), "logEcommerceCartUpdate() with items" );
+			
 			// parameters inserted by plugin hooks
 			ok( /testlog/.test( results ), "plugin hook log" );
 			ok( /testlink/.test( results ), "plugin hook link" );
diff --git a/tests/javascript/piwik.php b/tests/javascript/piwik.php
index 3675e3ce45fae801abb4e8ec90edcb5eab95f091..22aea83d57353ec5e907d570eefa97ec0f6331de 100644
--- a/tests/javascript/piwik.php
+++ b/tests/javascript/piwik.php
@@ -63,7 +63,7 @@ if (isset($_GET['requests'])) {
 		if($_SERVER['REQUEST_METHOD'] == 'POST') {
 			$uri .= '?' . file_get_contents('php://input');
 		}
-		$uri = htmlspecialchars($uri);
+//		$uri = htmlspecialchars($uri);
 
 		$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
 		$ua = $_SERVER['HTTP_USER_AGENT'];