Index: branches/5.2.x/units/affiliates/affiliates_event_handler.php
===================================================================
--- branches/5.2.x/units/affiliates/affiliates_event_handler.php	(revision 14766)
+++ branches/5.2.x/units/affiliates/affiliates_event_handler.php	(revision 14767)
@@ -1,634 +1,634 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Commerce
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license	Commercial License
 * This software is protected by copyright law and international treaties.
 * Unauthorized reproduction or unlicensed usage of the code of this program,
 * or any portion of it may result in severe civil and criminal penalties,
 * and will be prosecuted to the maximum extent possible under the law
 * See http://www.in-portal.org/commercial-license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class AffiliatesEventHandler extends kDBEventHandler {
 
 		/**
 		 * Allows to override standard permission mapping
 		 *
 		 */
 		function mapPermissions()
 		{
 			parent::mapPermissions();
 
 			$permissions = Array(
 				'OnItemBuild' => Array ('self' => true),
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Checks user permission to execute given $event
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(&$event)
 		{
 			if ( $event->Name == 'OnBecomeAffiliate' || $event->Name == 'OnChangePaymentType' ) {
 				return $this->Application->LoggedIn() && $this->Application->ConfigValue('Comm_RegisterAsAffiliate');
 			}
 
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Allows to get ID of affiliate record, associated with currently logged-in user
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function getPassedID(&$event)
 		{
 			if ( $event->Special == 'user' ) {
 				$event->setEventParam('raise_warnings', 0);
 				
 				$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
 						FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 						WHERE PortalUserId = ' . (int)$this->Application->RecallVar('user_id');
 				$id = $this->Conn->GetOne($sql);
 				
 				if ( $id ) {
 					return $id;
 				}
 			}
 
 			return parent::getPassedID($event);
 		}
 
 		/**
 		 * Generate new affiliate code
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function generateAffiliateCode(&$event)
 		{
 			// accepts 1 - 36
 			$number_length = 11;
 			$num_chars = Array(	'1'=>'a','2'=>'b','3'=>'c','4'=>'d','5'=>'e','6'=>'f',
 								'7'=>'g','8'=>'h','9'=>'i','10'=>'j','11'=>'k','12'=>'l',
 								'13'=>'m','14'=>'n','15'=>'o','16'=>'p','17'=>'q','18'=>'r',
 								'19'=>'s','20'=>'t','21'=>'u','22'=>'v','23'=>'w','24'=>'x',
 								'25'=>'y','26'=>'z','27'=>'0','28'=>'1','29'=>'2','30'=>'3',
 								'31'=>'4','32'=>'5','33'=>'6','34'=>'7','35'=>'8','36'=>'9');
 
 			$ret = '';
 			for($i=1; $i<=$number_length; $i++)
 			{
 				mt_srand((double)microtime() * 1000000);
 				$num = mt_rand(1,36);
 				$ret .= $num_chars[$num];
 			}
 			$ret = strtoupper($ret);
 
 			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 			$table = $this->Application->getUnitOption($event->Prefix, 'TableName');
 
 			$sql = 'SELECT %s FROM %s WHERE AffiliateCode = %s';
 			$code_found = $this->Conn->GetOne( sprintf($sql, $id_field, $table, $this->Conn->qstr($ret) ) );
 			if($code_found) return $this->generateAffiliateCode($event);
 
 			return $ret;
 		}
 
 		/**
 		 * Creates new affiliate code when new affiliate is created
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemCreate(&$event)
 		{
 			parent::OnBeforeItemCreate($event);
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$object->SetDBField('AffiliateCode', $this->generateAffiliateCode($event));
 
 			if ( $object->getFormName() == 'registration' ) {
 				if ( $this->Application->LoggedIn() ) {
 					$object->SetDBField('PortalUserId', $this->Application->RecallVar('user_id'));
 				}
 
 				$object->SetDBField('AffiliatePlanId', $this->_getPrimaryAffiliatePlan());
 			}
 		}
 
 		/**
 		 * Ensures, that user can only update his affiliate record
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(&$event)
 		{
 			parent::OnBeforeItemUpdate($event);
 
 			if ( !$this->Application->isAdmin ) {
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				$object->SetDBField('PortalUserId', $object->GetOriginalField('PortalUserId'));
 
 				if ( $object->GetDBField('PortalUserId') != $this->Application->RecallVar('user_id') ) {
 					$object->SetError('PortalUserId', 'not_owner');
 				}
 			}
 		}
 
 		/**
 		 * Stores affiliate id using method from Config (session or cookie) if correct code is present in url
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function OnStoreAffiliate(&$event)
 		{
 			$event->Init($event->Prefix, '-item');
 			$object =& $event->getObject( Array('skip_autoload'=>true) );
 
 			$affiliate_storage_method = $this->Application->ConfigValue('Comm_AffiliateStorageMethod');
 
 			$affiliate = $this->Application->GetVar('affiliate');
 			if($affiliate)
 			{
 				$object->Load($affiliate, 'AffiliateCode');
 			}
 			elseif($affiliate_storage_method == 2)
 			{
 				$affiliate_id = $this->Application->GetVar('affiliate_id');
 				$object->Load($affiliate_id);
 			}
 
 			if( $object->isLoaded() && ($object->GetDBField('Status') == 1) )
 			{
 				// user is found with such email
 				$affiliate_user =& $this->Application->recallObject('u.affiliate', null, Array('skip_autoload'=>true) );
 				$affiliate_user->Load( $object->GetDBField('PortalUserId') );
 
 				if($affiliate_user->GetDBField('Status') == 1)
 				{
 					$affiliate_id = $object->GetDBField('AffiliateId');
 					$this->Application->setVisitField('AffiliateId', $affiliate_id);
 
 					if($affiliate_storage_method == 1)
 					{
 						$this->Application->StoreVar('affiliate_id', $affiliate_id); // per session
 					}
 					else
 					{
 						// in cookie
 						$this->Application->Session->SetCookie('affiliate_id', $affiliate_id, $this->getCookieExpiration());
 					}
 				}
 			}
 		}
 
 		/**
 		 * Returns affiliate cookie expiration date
 		 *
 		 * @return int
 		 */
 		function getCookieExpiration()
 		{
 			$expire = $this->Application->ConfigValue('Comm_AffiliateCookieDuration'); // days
 			return adodb_mktime() + $expire * 24 * 60 * 60;
 		}
 
 		/**
 		 * Calculate what amount is earned by affiliate based on it's affiliate plan & store it
 		 *
 		 * @param kEvent $event
 		 * @author Alex
 		 */
 		function OnOrderApprove(&$event)
 		{
 			$order =& $this->Application->recallObject( $event->getEventParam('Order_PrefixSpecial') );
 			$affiliate_id = $order->GetDBField('AffiliateId');
 			if(!$affiliate_id) return false;
 
 			$object =& $event->getObject( Array('ship_autoload' => true) );
 			if( $object->Load($affiliate_id) )
 			{
 				$affiliate_plan =& $this->Application->recallObject('ap', null, Array('skip_autoload'=>true) );
 				$affiliate_plan->Load( $object->GetDBField('AffiliatePlanId') );
 				if( $affiliate_plan->isLoaded() )
 				{
 					$sql = 'SELECT SUM(Quantity) FROM %s WHERE OrderId = %s';
 					$orderitems_table = $this->Application->getUnitOption('orditems', 'TableName');
 					$items_sold = $this->Conn->GetOne( sprintf($sql, $orderitems_table, $order->GetID() ) );
 
 					$object->SetDBField('AccumulatedAmount', $object->GetDBField('AccumulatedAmount') + $order->GetDBField('TotalAmount') );
 					$object->SetDBField('ItemsSold', $object->GetDBField('ItemsSold') + $items_sold);
 
 					switch( $affiliate_plan->GetDBField('PlanType') )
 					{
 						case 1: // by amount
 							$value = $object->GetDBField('AccumulatedAmount');
 							break;
 
 						case 2: // by items sold (count)
 							$value = $object->GetDBField('ItemsSold');
 							break;
 					}
 
 					$apb_table = $this->Application->getUnitOption('apbrackets', 'TableName');
 					$sql = 'SELECT Percent FROM %1$s WHERE (%2$s >= FromAmount) AND ( (%2$s <= ToAmount) OR (ToAmount = -1) ) AND (AffiliatePlanId = %3$s)';
 					$commission_percent = $this->Conn->GetOne( sprintf($sql, $apb_table, $this->Conn->qstr($value), $affiliate_plan->GetID() ) );
 
 					// process only orders of current affiliate from period start to this order date
 					$period_ends = $order->GetDBField('OrderDate');
 					$period_starts = $this->getPeriodStartTS( $period_ends, $affiliate_plan->GetDBField('ResetInterval') );
-					$sql = 'SELECT AffiliateCommission, (SubTotal+ShippingCost+VAT) AS TotalAmount, OrderId FROM '.$order->TableName.' WHERE OrderDate >= %s AND OrderDate <= %s';
+					$sql = 'SELECT AffiliateCommission, (SubTotal+ShippingCost+VAT) AS TotalAmount, OrderId FROM '.$order->TableName.' WHERE OrderDate >= %s AND OrderDate <= %s AND AffiliateId = ' . $affiliate_id;
 
 					$amount_to_pay_before = 0;
 					$amount_to_pay_after = 0;
 					$order_update_sql = 'UPDATE '.$order->TableName.' SET AffiliateCommission = %s WHERE '.$order->IDField.' = %s';
 					$orders = $this->Conn->Query( sprintf($sql, $period_starts, $period_ends), 'OrderId' );
 					if($orders)
 					{
 						foreach($orders as $order_id => $order_data)
 						{
 							$amount_to_pay_before += $order_data['AffiliateCommission'];
 							$commission = $order_data['TotalAmount'] * ($commission_percent / 100);
 							$this->Conn->Query( sprintf($order_update_sql, $this->Conn->qstr($commission), $order_id) );
 							$amount_to_pay_after += $commission;
 						}
 
 
 					}
 					$object->SetDBField('AmountToPay', $object->GetDBField('AmountToPay') - $amount_to_pay_before + $amount_to_pay_after );
 					$object->SetDBField('LastOrderDate_date', $order->GetDBField('OrderDate_date') );
 					$object->SetDBField('LastOrderDate_time', $order->GetDBField('OrderDate_time') );
 					$object->Update();
 
 					$order->SetDBField('AffiliateCommission', $commission); // set last commission to this order, because ApproveEvent was called for him
 				}
 			}
 		}
 
 		/**
 		 * [HOOK] Validates affiliate fields on user registration form
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnValidateAffiliate(&$event)
 		{
 			if ( $this->Application->GetVar('RegisterAsAffiliate') != 'on' || $event->MasterEvent->status != kEvent::erSUCCESS ) {
 				return;
 			}
 			
 			$object =& $event->getObject( Array('form_name' => 'registration', 'skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			$field_values = $this->getSubmittedFields($event);
 			$object->SetFieldsFromHash($field_values);
 			$object->setID(0);
 
 			if ( !$object->Validate() ) {
 				$user =& $event->MasterEvent->getObject();
 				/* @var $user kDBItem */
 
 				$user->Validate();
 
 				$event->MasterEvent->status = kEvent::erFAIL;
 			}
 		}
 
 		/**
 		 * [AFTER HOOK] to u:OnCreate
 		 *
 		 * @param kEvent $event
 		 */
 		function OnRegisterAffiliate(&$event)
 		{
 			if ( $this->Application->GetVar('RegisterAsAffiliate') != 'on' || $event->MasterEvent->status != kEvent::erSUCCESS ) {
 				return;
 			}
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$user =& $event->MasterEvent->getObject();
 			/* @var $user UsersItem */
 
 			$object->SetDBField('PortalUserId', $user->GetID());
 
 			if ( $object->Create() ) {
 				$this->Application->EmailEventUser('AFFILIATE.REGISTER', $user->GetID());
 				$this->Application->EmailEventAdmin('AFFILIATE.REGISTER');
 			}
 		}
 
 		/**
 		 * Returns primary affiliate plan
 		 *
 		 * @return int
 		 * @access protected
 		 */
 		protected function _getPrimaryAffiliatePlan()
 		{
 			$sql = 'SELECT AffiliatePlanId
 					FROM ' . $this->Application->getUnitOption('ap', 'TableName') . '
 					WHERE IsPrimary = 1';
 
 			return (int)$this->Conn->GetOne($sql);
 		}
 
 		/**
 		 * Creates affiliate record for logged-in user
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBecomeAffiliate(&$event)
 		{
 			$object =& $event->getObject( Array('form_name' => 'registration', 'skip_autoload' => true) );
 			/* @var $object UsersItem */
 
 			$event->CallSubEvent('OnCreate');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$event->SetRedirectParam('opener', 's');
 
 				$next_template = $this->Application->GetVar('next_template');
 
 				if ( $next_template ) {
 					$event->redirect = $next_template;
 				}
 			}
 		}
 
 		/**
 		 * Change affiliate payment type of affiliate record associated with logged-in user
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnChangePaymentType(&$event)
 		{
 			$event->CallSubEvent('OnUpdate');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				$this->Application->EmailEventUser('AFFILIATE.PAYMENT.TYPE.CHANGED', $object->GetDBField('PortalUserId'));
 				$this->Application->EmailEventAdmin('AFFILIATE.PAYMENT.TYPE.CHANGED');
 
 				$next_template = $this->Application->GetVar('next_template');
 
 				if ( $next_template ) {
 					$event->redirect = $this->Application->GetVar('next_template');
 				}
 
 				$event->SetRedirectParam('opener', 's');
 			}
 		}
 
 		/**
 		 * If new payments made, then send email about that
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeDeleteFromLive(&$event)
 		{
 			parent::OnBeforeDeleteFromLive($event);
 
 			$payment_object =& $this->Application->recallObject('apayments', 'apayments', Array ('skip_autoload' => true));
 			/* @var $payment_object kDBItem */
 
 			$id = $event->getEventParam('id');
 			$ap_table = $this->Application->getUnitOption('apayments', 'TableName');
 
 			$sql = 'SELECT AffiliatePaymentId
 					FROM ' . $ap_table . '
 					WHERE AffiliateId = ' . $id;
 			$live_ids = $this->Conn->GetCol($sql);
 
 			$sql = 'SELECT AffiliatePaymentId
 					FROM ' . $payment_object->TableName . '
 					WHERE AffiliateId = ' . $id;
 			$temp_ids = $this->Conn->GetCol($sql);
 
 			$new_ids = array_diff($temp_ids, $live_ids);
 
 			foreach ($new_ids as $payment_id) {
 				$payment_object->Load($payment_id);
 				$this->Application->EmailEventUser('AFFILIATE.PAYMENT', $payment_object->GetDBField('PortalUserId'));
 				$this->Application->EmailEventAdmin('AFFILIATE.PAYMENT');
 			}
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$passed_id = $event->getEventParam('id');
 
 			if ( $object->GetID() != $passed_id ) {
 				$object->Load($passed_id);
 			}
 
 			$sql = 'SELECT Status
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE ' . $object->IDField . ' = ' . $object->GetID();
 			$old_status = $this->Conn->GetOne($sql);
 
 			if ( $old_status == 2 && $object->GetDBField('Status') == 1 ) {
 				$this->Application->EmailEventUser('AFFILIATE.REGISTRATION.APPROVED', $object->GetDBField('PortalUserId'));
 				$this->Application->EmailEventAdmin('AFFILIATE.REGISTRATION.APPROVED');
 			}
 		}
 
 		/**
 		 * Resets statistics (accumulated amount & items sold) for affiliates based on ResetInterval in their plan
 		 *
 		 * @param kEvent $event
 		 * @author Alex
 		 */
 		function OnResetStatistics(&$event)
 		{
 			$intervals = Array(86400 => 'la_day', 604800 => 'la_week', 2628000 => 'la_month', 7884000 => 'la_quartely', 31536000 => 'la_year');
 
 			$affil_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
 			$ap_table = $this->Application->getUnitOption('ap', 'TableName');
 
 			$sql = 'SELECT AffiliateId
 					FROM '.$affil_table.' a
 					LEFT JOIN '.$ap_table.' ap ON a.AffiliatePlanId = ap.AffiliatePlanId';
 
 			$base_time = adodb_mktime();
 			$where_clause = Array();
 			foreach($intervals as $interval_length => $interval_description)
 			{
 				$start_timestamp = $this->getPeriodStartTS($base_time, $interval_length);
 				$where_clause[] = 'ap.ResetInterval = '.$interval_length.' AND LastOrderDate < '.$start_timestamp;
 			}
 			$sql .= ' WHERE ('.implode(') OR (', $where_clause).')';
 
 			$affiliate_ids = $this->Conn->GetCol($sql);
 
 			if ($affiliate_ids && defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) {
 				$this->Application->Debugger->appendHTML('Affiliates Pending Totals Reset: ');
 				$this->Application->Debugger->dumpVars($affiliate_ids);
 			}
 
 			$sql = 'UPDATE '.$affil_table.' SET AccumulatedAmount = 0, ItemsSold = 0, LastOrderDate = %s WHERE AffiliateId IN (%s)';
 			if($affiliate_ids) $this->Conn->Query( sprintf($sql, $base_time, implode(',', $affiliate_ids) ) );
 		}
 
 		/**
 		 * Returns calendar period start timestamp based on current timestamp ($base_time) and $period_length
 		 *
 		 * @param int $base_time
 		 * @param int $period_length
 		 * @return int
 		 * @author Alex
 		 */
 		function getPeriodStartTS($base_time, $period_length)
 		{
 			switch($period_length)
 			{
 				case 86400:	// day
 					$start_timestamp = adodb_mktime(0,0,0, adodb_date('m', $base_time), adodb_date('d', $base_time), adodb_date('Y', $base_time) );
 					break;
 
 				case 604800: // week
 					$day_seconds = 86400;
 					$first_week_day = $this->Application->ConfigValue('FirstDayOfWeek');
 					$morning = adodb_mktime(0,0,0, adodb_date('m', $base_time), adodb_date('d', $base_time), adodb_date('Y', $base_time) );
 					$week_day = adodb_date('w', $morning);
 					if($week_day == $first_week_day)
 					{
 						// if it is already first week day, then don't search for previous week day
 						$day_diff = 0;
 					}
 					else
 					{
 						// this way, because sunday is 0, but not 7 as it should be
 						$day_diff = $week_day != 0 ? $week_day - $first_week_day: 7 - $first_week_day;
 					}
 					$start_timestamp = $morning - $day_diff * $day_seconds;
 					break;
 
 				case 2628000: // month
 					$start_timestamp = adodb_mktime(0,0,0, adodb_date('m', $base_time), 1, adodb_date('Y', $base_time) );
 					break;
 
 				case 7884000: // quartal
 					$first_quartal_month = (ceil( adodb_date('m', $base_time) / 3)-1)*3 + 1;
 					$start_timestamp = adodb_mktime(0,0,0, $first_quartal_month, 1, adodb_date('Y', $base_time) );
 					break;
 
 				case 31536000:
 					$start_timestamp = adodb_mktime(0,0,0,1,1, adodb_date('Y', $base_time) );
 					break;
 			}
 			return $start_timestamp;
 		}
 
 		function iterateItems(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$ids = $this->StoreSelectedIDs($event);
 
 			if($ids)
 			{
 				$status_field = array_shift( $this->Application->getUnitOption($event->Prefix,'StatusField') );
 
 				foreach($ids as $id)
 				{
 					$object->Load($id);
 
 					switch ($event->Name)
 					{
 						case 'OnMassApprove':
 							$object->SetDBField($status_field, 1);
 							break;
 
 						case 'OnMassDecline':
 							$object->SetDBField($status_field, 0);
 							break;
 
 						case 'OnMassMoveUp':
 							$object->SetDBField('Priority', $object->GetDBField('Priority') + 1);
 							break;
 
 						case 'OnMassMoveDown':
 							$object->SetDBField('Priority', $object->GetDBField('Priority') - 1);
 							break;
 					}
 
 					if( $object->Update() )
 					{
 						switch ($event->Name)
 						{
 							case 'OnMassApprove':
 								$email_event_user =& $this->Application->EmailEventUser('AFFILIATE.REGISTRATION.APPROVED', $object->GetDBField('PortalUserId'));
 								$email_event_admin =& $this->Application->EmailEventAdmin('AFFILIATE.REGISTRATION.APPROVED');
 								break;
 							case 'OnMassDecline':
 								$email_event_user =& $this->Application->EmailEventUser('AFFILIATE.REGISTRATION.DENIED', $object->GetDBField('PortalUserId'));
 								$email_event_admin =& $this->Application->EmailEventAdmin('AFFILIATE.REGISTRATION.DENIED');
 								break;
 						}
 						$event->status=kEvent::erSUCCESS;
 						$event->setRedirectParams(Array('opener' => 's'), true); //stay!
 					}
 					else
 					{
 						$event->status=kEvent::erFAIL;
 						$event->redirect=false;
 						break;
 					}
 				}
 			}
 		}
 
 		/**
 		 * Checks that user in affiliate record matches current user
 		 * (non permission-based)
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function checkItemStatus(&$event)
 		{
 			if ($this->Application->isAdminUser) {
 				return true;
 			}
 
 			$object =& $event->getObject();
 			if (!$object->isLoaded()) {
 				return true;
 			}
 
 			return $object->GetDBField('PortalUserId') == $this->Application->RecallVar('user_id');
 		}
 	}
\ No newline at end of file