* @copyright 2009 Deon George * @link http://osb.leenooks.net * * @link http://www.agileco.com/ * @copyright 2004-2008 Agileco, LLC. * @license http://www.agileco.com/agilebill/license1-4.txt * @author Tony Landis * @package AgileBill * @subpackage Module:Service */ /** * The main AgileBill Service Class * * @package AgileBill * @subpackage Module:Service */ class service extends OSB_module { /** * SQL Join details */ public function sql_join($table,$value) { switch ($table) { case 'invoice_item': return sprintf('%sinvoice.id=%sinvoice_item.invoice_id AND %sinvoice_item.service_id=%s',AGILE_DB_PREFIX,AGILE_DB_PREFIX,AGILE_DB_PREFIX,$value); break; default: printf("ERROR: Unknown join :%s",$table); return; } } ############################## ## Resend hosting details ## ############################## function resend_hosting_email($VAR) { if(!empty($VAR['id'])) { include_once(PATH_MODULES.'email_template/email_template.inc.php'); $email = new email_template; $email->send('host_new_user', $VAR['account_id'], $VAR['id'], '', ''); global $C_debug, $C_translate; $C_debug->alert($C_translate->translate('hosting_email_sent','service','')); } } ############################## ## Cleanup group access ## ############################## function cleanup($VAR) { # update services to suspended that meet the following criteria: # one-time charge (cannot be subscription) # group access of any kind # expired by the access days permited $db = &DB(); $sql = 'SELECT id,group_days,date_orig FROM ' . AGILE_DB_PREFIX . 'service WHERE type LIKE ' . $db->qstr("%group%") . ' AND queue != ' . $db->qstr("inactive") . ' AND active = ' . $db->qstr(1) . ' AND price_type = ' . $db->qstr(0) . ' AND group_type = ' . $db->qstr(0) . ' AND group_days > ' . $db->qstr(0) . ' AND site_id = ' . $db->qstr(DEFAULT_SITE); $service = $db->Execute($sql); $total = $service->RecordCount(); $i=0; while(!$service->EOF) { # check if expired: $exp = $service->fields['date_orig'] + ( $service->fields['group_days'] * 86400 ); if( time() > $exp ) { # Update the service status: # should we delete instead? todo # should we send email notification? todo $sql = 'UPDATE ' . AGILE_DB_PREFIX . 'service SET queue = ' . $db->qstr("inactive") . ', date_last = ' . $db->qstr(time()) . ', active = ' . $db->qstr(0) . ' WHERE id = ' . $db->qstr($service->fields['id']) . ' AND site_id = ' . $db->qstr(DEFAULT_SITE); $db->Execute($sql); $i++; } $service->MoveNext(); } # Display results: $remain = $total - $i; $msg = "While cleaning up one-time Services granting group access, located $i Services(s) that have expired and were suspended and $remain Service(s) are active and will remain active."; # void all services that have been canceled by the user # or billing has been suspended by the admin that # would normally be due for billing now. $sql = 'SELECT id FROM ' . AGILE_DB_PREFIX . 'service WHERE queue != ' . $db->qstr("inactive") . ' AND active = ' . $db->qstr(1) . ' AND suspend_billing = ' . $db->qstr(1) . ' AND date_next_invoice <= ' . $db->qstr(time()) . ' AND site_id = ' . $db->qstr(DEFAULT_SITE); $service = $db->Execute($sql); $total = $service->RecordCount(); if($total > 0) { while(!$service->EOF) { # deactivate: $this->voidService($service->fields['id']); $service->MoveNext(); } # Display results: $remain = $total - $i; $msg .= "

While searching for services cancelled by the user or admin, $total service(s) were located and have been voided."; } global $C_debug; $C_debug->alert($msg); return true; } /** User reactivate */ function user_reactivate($VAR) { if(!SESS_LOGGED || empty($VAR['id'])) return false; global $C_debug, $C_translate, $smarty; /* get service details */ $db =& DB(); $rs = $db->Execute(sqlSelect($db,"service","*","id=::{$VAR['id']}::")); if($rs && $rs->RecordCount()) { extract($rs->fields); /* can reinstate? */ if(!$suspend_billing || SESS_ACCOUNT != $account_id || !$recur_cancel) { $C_debug->alert('This service cannot be reactivated at this time.'); return false; } /* invoice date needs moved? */ if($active == 1 && $date_next_invoice >= time()) { /* no, change the suspend_billing status */ $fields=Array('suspend_billing'=>0); $db->Execute(sqlUpdate($db,"service",$fields,"id=::{$VAR['id']}:: ")); $C_debug->alert('This service has been reactivated and will continue to be billed normally without service interruption.'); } else { /* no, change the suspend_billing status */ $fields=Array('suspend_billing'=>0, 'date_next_invoice'=>time()+86400); $db->Execute(sqlUpdate($db,"service",$fields,"id=::{$VAR['id']}:: ")); $C_debug->alert('An invoice for this service will be generated within the next 24 hours and service will be reactivated immediately after payment of that invoice is made.'); } } } ############################## ## USER MODIFY RECURR SCHED ## ############################## function user_changeschedule($VAR) { global $C_translate, $C_debug; if(!isset($VAR['id']) || !isset($VAR['service_recur_schedule'])) return false; # get the account id & confirm changing the schedule is allowed $db = &DB(); $dbm = new CORE_database; $sql = $dbm->sql_select("service","*","id = {$VAR['id']}", "", $db); $service = $db->Execute($sql); if($service->fields['account_id'] == SESS_ACCOUNT && $service->fields['recur_schedule_change'] == 1) { # prev schedule $prev = $service->fields['recur_schedule']; # current schedule $cur = $VAR['service_recur_schedule']; if(!is_numeric($cur) || $cur > 6) return false; # validate a change has occurred if($cur != $prev) { $this->changeschedule($cur, $prev, $service, $VAR); } } else { $msg = $C_translate->translate('changeservice_auth','service', ''); $C_debug->alert($msg); } } /** * Called when an admin changes the billing schedule */ public function admin_changeschedule($VAR) { # Quick validation that we were called correctly if (! isset($VAR['id']) || ! isset($VAR['service_recur_schedule']) || ! is_numeric($VAR['service_recur_schedule']) || ($VAR['service_recur_schedule'] > 6)) return false; # Get the account id & confirm changing the schedule is allowed $db = &DB(); $service = $db->Execute(sqlSelect($db,'service','*',array('id'=>$VAR['id']))); # Validate a change has occurred if ($service && ($service->RecordCount() == 1) && $VAR['service_recur_schedule'] != $service->fields['recur_schedule']) $this->changeschedule($VAR['service_recur_schedule'],$service->fields['recur_schedule'],$service,$VAR); } /** * Change the billing schedule for a service */ private function changeschedule($cur,$prev,&$service,$VAR) { global $C_translate,$C_debug,$C_auth; $db = &DB(); # Get the associated product: $product_id = $service->fields['product_id']; # Validate a product is associated with this service: if ($product_id > 0 ) { # Get the product details: $product = $db->Execute(sqlSelect($db,'product','*',array('id'=>$product_id))); # Get the price for the associated product and billing schedule if ($product->fields['price_recurr_default'] == $cur) { # Use default base price: $price = $product->fields['price_base']; } else { $arr = unserialize($product->fields['price_group']); if(is_array($arr)) { # Get the base price for the selected period: $price = false; $parr = $arr[$cur]; # Loop through each group price and assign this user the lowest available price: while(list($group, $parr2) = each($parr)) { if (isset($parr2['price_base'])) { $arr_price = $parr2['price_base']; if ($arr_price != '' && $C_auth->auth_group_by_id($group)) if ($price == false || $price > $arr_price) $price = $arr_price; } } } } # Update service status $q = "UPDATE ".AGILE_DB_PREFIX."service SET recur_schedule = $cur, price = '$price' WHERE id = {$VAR['id']} AND site_id = ".$db->qstr(DEFAULT_SITE); $db->Execute($q); echo '
';print_r(array('c'=>$cur,'p'=>$prev,'q'=>$q,'s'=>$service,'v'=>$VAR,'pr'=>$product_id,'pro'=>$product));
		} else {
			# Update service status
			$q = "UPDATE ".AGILE_DB_PREFIX."service SET recur_schedule = $cur WHERE id = {$VAR['id']} AND site_id = ".$db->qstr(DEFAULT_SITE);
			$db->Execute($q);
		}

		# Create a memo
		$fields=Array('date_orig'=>time(), 'staff_id'=> SESS_ACCOUNT, 'service_id'=>$VAR['id'], 'type'=> 'changeschedule', 'memo'=> "Changed recurring schedule from $prev to $cur");
		$db->Execute($sql=sqlInsert($db,"service_memo",$fields));

		return true;
	}




	##############################
	##  USER CANCEL SERVICES    ##
	##############################
	function user_cancelservice($VAR)
	{
		if(!isset($VAR['id']) || SESS_LOGGED == false) return false;

		# get the account id & confirm cancelation allowed
		$db = &DB();
		$sql    = 'SELECT id,account_id,recur_cancel FROM ' . AGILE_DB_PREFIX . 'service WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$service =  $db->Execute($sql);
		if($service->fields['account_id'] == SESS_ACCOUNT &&  $service->fields['recur_cancel'] == 1)
		{
			$VAR['user'] = 1;
			$this->cancelservice($VAR, $this);

			# Create a memo
			$fields=Array('date_orig'=>time(), 'staff_id'=> SESS_ACCOUNT, 'service_id'=>$VAR['id'], 'type'=> 'cancel', 'memo'=> "User Canceled Service");
			$db->Execute($sql=sqlInsert($db,"service_memo",$fields));
		}
		else
		{
			global $C_translate, $C_debug;
			$msg = $C_translate->translate('cancelservice_auth','service', '');
			$C_debug->alert($msg);
		}
	}


	##############################
	##    CANCEL SERVICES       ##
	##############################
	function cancelservice($VAR)
	{
		if(!isset($VAR['id'])) return false;

		# Update service status
		$db = &DB();
		$q = "UPDATE ".AGILE_DB_PREFIX."service SET
                    suspend_billing = ".$db->qstr( '1' )." WHERE
                    id          = ".$db->qstr( $VAR['id']  )." AND
                    site_id     = ".$db->qstr(DEFAULT_SITE);
		$db->Execute($q);

		# get the account id
		$sql    = 'SELECT id,account_id FROM ' . AGILE_DB_PREFIX . 'service WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$service =  $db->Execute($sql);

		# send user email
		include_once(PATH_MODULES.'email_template/email_template.inc.php');
		$email = new email_template;
		$email->send('service_cancel_user', $service->fields['account_id'], $service->fields['id'], '', '');

		# send admin email only if user canceled
		if(isset($VAR['user']))
		{
			$email = new email_template;
			$email->send('admin->service_cancel_admin', $service->fields['account_id'], $service->fields['id'], '', '');
		}

		# Create a memo
		$fields=Array('date_orig'=>time(), 'staff_id'=> SESS_ACCOUNT, 'service_id'=>$VAR['id'], 'type'=> 'cancel', 'memo'=> "Staff Canceled Service");
		$db->Execute($sql=sqlInsert($db,"service_memo",$fields));
	}


	##############################
	##	ADD/APPROVE SERVICES    ##
	##############################
	function approveService($id)
	{
		# Update service status
		$db = &DB();
		$q = "UPDATE ".AGILE_DB_PREFIX."service SET
			        active		= ".$db->qstr( 1 ).",
			        queue		= ".$db->qstr( 'active' )." WHERE
			        id          = ".$db->qstr(  $id  )." AND
			        site_id     = ".$db->qstr(DEFAULT_SITE);
		$db->Execute($q);

		# Create a memo
		$fields=Array('date_orig'=>time(), 'staff_id'=> SESS_ACCOUNT, 'service_id'=>$id, 'type'=> 'approve', 'memo'=> "Approved Service");
		$db->Execute($sql=sqlInsert($db,"service_memo",$fields));

		# Run queue now
		$this->queue_one($id, false);
		return true;
	}


	##############################
	##	VOID SERVICE		    ##
	##############################
	function voidService($id)
	{
		# Update service status
		$db = &DB();
		$q = "UPDATE ".AGILE_DB_PREFIX."service SET
			        active		= ".$db->qstr( 0 ).",
			        queue		= ".$db->qstr( 'inactive' )." WHERE
			        id          = ".$db->qstr(  $id  )." AND
			        site_id     = ".$db->qstr(DEFAULT_SITE);
		$db->Execute($q);

		# Create a memo
		$fields=Array('date_orig'=>time(), 'staff_id'=> SESS_ACCOUNT, 'service_id'=>$id, 'type'=> 'void', 'memo'=> "Voided Service");
		$db->Execute($sql=sqlInsert($db,"service_memo",$fields));

		/** call queue now */
		$this->queue_one($id);
		return true;
	}

	/** queue all services */
	function queue($VAR) {
		if(!empty($VAR['id']) && !empty($VAR["do"])) {
			/** queue one */
			$this->queue_one($VAR['id'], false);
		} else {
			/** queue all services */
			$db = &DB();
			$rs = $db->Execute(sqlSelect($db, "service", "*", "queue!='none'"));
			if ($rs && $rs->RecordCount()) {
				while ( !$rs->EOF ) {
					$this->queue_one($rs->fields['id'], $rs->fields);
					$rs->MoveNext();
				}
			}
		}
	}

	/** queue one service
	 * @param int $id
	 * @param array $service Fields of service row
	 */
	function queue_one($id, $service=false) {
		if(!$service) {
			$db=&DB();
			$rs = $db->Execute(sqlSelect($db, "service", "*", "id=::$id::"));
			if(!$rs || !$rs->RecordCount()) return false;
			$service=$rs->fields;
			$this->service = $rs->fields;
		} else {
			$this->service = $service;
		}
		switch($service['type'])
		{
			case 'group':
				$this->queue_group($id);
				break;
			case 'host':
				$this->queue_host($id);
				break;
			case 'domain':
				$this->queue_domain($id);
				break;
			case 'product':
				$this->queue_product($id);
				break;
			case 'host_group':
				$this->queue_host($id);
				$this->queue_group($id, false);
				break;
			case 'product_group':
				$this->queue_product($id);
				$this->queue_group($id, false);
				break;
		}
	}

	/** set queue action to 'none'
	 * @param int $id Service ID
	 */
	function queue_complete($id=false) {
		if(!$id) return false;
		$db =& DB();
		$db->Execute("UPDATE ".AGILE_DB_PREFIX."service SET queue = ".$db->qstr( 'none' )." WHERE id=".$db->qstr( $id )." AND site_id=".$db->qstr(DEFAULT_SITE));
	}

	/** group type service queue
	 * @param int $id Service ID
	 * @param bool $update Update service queue to 'none' after running
	 */
	function queue_group($id, $update=true)
	{
		# Select Service Details
		$db = &DB();
		$q = "SELECT * FROM  ".AGILE_DB_PREFIX."service WHERE id = ".$db->qstr( $id )." AND site_id = ".$db->qstr(DEFAULT_SITE);;
		$rs = $db->Execute($q);
		if ($rs && $rs->RecordCount()) {

			# Get the groups to grant access to:
			$groups = unserialize($rs->fields['group_grant']);
			if(!is_array($groups)) return false;

			# Get the action to perform:
			include_once(PATH_CORE.'service_group.inc.php');
			$srv = new service_group( $rs->fields, $groups );
			switch ($rs->fields['queue'])
			{
				case 'new':
					$srv->s_new();
					break;
				case 'active':
					$srv->s_active();
					break;
				case 'inactive':
					$srv->s_inactive();
					break;
				case 'edit':
					$srv->s_edit();
					break;
				case 'delete':
					$srv->s_delete();
					break;
				case 'none':
					if($rs->fields['active'])
					$srv->s_active();
				else
					$srv->s_inactive();
					break;
			}

			# Update service queue status
			if($update) $this->queue_complete($id);
		}
	}

	##############################
	##	DOMAIN QUEUE HANDLER    ##
	##############################
	function queue_host($id)
	{
		global $VAR;

		# Get the service type (task based / real time)
		$host_id = $this->service['host_server_id'];
		$db     = &DB();
		$sql    = 'SELECT debug,provision_plugin FROM ' . AGILE_DB_PREFIX . 'host_server WHERE
                       id           =  ' . $db->qstr( $host_id ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		if($rs->RecordCount() > 0)
		{
			$file = $rs->fields['provision_plugin'];
			require_once ( PATH_PLUGINS . 'provision/'.$file.'.php' );
			eval ( '$_plg = new plgn_prov_'.$file.';' );

			#If realtime, load module and run command now
			if(@$_plg->remote_based == true) $_plg->p_one($id);
		}

		return true;
	}

	##############################
	##	DOMAIN QUEUE HANDLER    ##
	##############################
	function queue_domain($id)
	{
		# Select Service Details
		$db = &DB();
		$q = "SELECT * FROM  ".AGILE_DB_PREFIX."service WHERE
			        id			= ".$db->qstr( $id )." AND
			        site_id     = ".$db->qstr(DEFAULT_SITE);;
		$rs = $db->Execute($q);
		if ($rs->RecordCount() == 0) {
			return false;
		} else {

			# Get the action to perform:
			include_once(PATH_CORE.'service_domain.inc.php');
			$srv = new service_domain( $rs->fields );

			if ($rs->fields['queue'] == 'new')
			{
				if ( $srv->s_new() )
				$this->queue_complete( $id );
				return;
			}

			# Update service queue status
			$this->queue_complete( $id );
			return;
		}
	}

	######################################
	##	PRODUCT PLUGIN QUEUE HANDLER    ##
	######################################
	function queue_product($id)
	{
		global $VAR;

		# Get the plugin name type (task based / real time)
		$file = $this->service['prod_plugin_name'];
		if(!empty($file)) {
			$path = PATH_PLUGINS . 'product/'.$file.'.php';
			if(is_file($path))
			{
				require_once ($path);
				eval ( '$_plg = new plgn_prov_'.$file.';' );

				# If realtime, load module and run command now
				if(!empty($_plg) && is_object($_plg))
					if($_plg->remote_based == true)
						$_plg->p_one($id);


			} else {
				return false;
			}
		}
		return true;
	}

	##############################
	##	ADD/APPROVE SERVICES    ##
	##############################
	function invoiceItemToService($invoice_item_id, $invoice, $service_id=false)
	{
		include_once(PATH_MODULES.'product/product.inc.php');
		$product  = new product;

		$trial = false;

		$db= &DB();

		# Get the invoice_item record
		$item = & $db->Execute( sqlSelect($db, "invoice_item", "*", "id = $invoice_item_id"));

		# Get the product details
		$prod = & $db->Execute ( sqlSelect($db, "product", "*", "id = {$item->fields['product_id']}"));

		# Determine Price, Price Type, and Next Invoice Date:
		if ($item->fields['price_type'] == '2')
		{
			### Item is trial for another item:
			$trial = true;

			# Determine trial length.
			$tl = $prod->fields['price_trial_length_type'];
			if($tl == 0)
			$this->next_invoice = time() + ( $prod->fields['price_trial_length'] * 86400 );
			elseif ($tl == 1)
			$this->next_invoice = time() + ( $prod->fields['price_trial_length'] * 86400 * 7 );
			elseif ($tl == 2)
			$this->next_invoice = mktime(0,0,0,date('m')+$prod->fields['price_trial_length'],date('d'), date('Y'));
			else
			$this->next_invoice = time() + ( 365 * 86400 );

			# get the details of the permanent item
			$q = "SELECT * FROM ".AGILE_DB_PREFIX."product WHERE
		        	  id 		=  ".$db->qstr($prod->fields['price_trial_prod'])." AND
		        	  site_id 	=  ".$db->qstr(DEFAULT_SITE);
			$prod = $db->Execute($q);

			/* set the product id to the perm item */
			$item->fields['product_id']= $prod->fields['id'];
			$this->recurring_schedule = $item->fields['recurring_schedule'];

			### Get the price
			$price = $product->price_prod($prod->fields, $prod->fields['price_recurr_default'], $invoice->fields['account_id'], false);
			$this->price  = @$price['base'] / $item->fields['quantity'];

			$this->bind   = '1';
			$item->fields['sku'] = $prod->fields['sku'];
		}
		elseif ($item->fields['price_type'] == '1')
		{
			# Recurring Item
			$this->recurring_schedule = $item->fields['recurring_schedule'];
			$this->price  = $item->fields['price_base'] / $item->fields['quantity'];
			$this->bind   = '1';

			# Determine the next invoice date:
			$this->next_invoice = $this->calcNextInvoiceDate(	$invoice->fields['due_date'],
			$this->recurring_schedule,
			$prod->fields['price_recurr_type'],
			$prod->fields['price_recurr_weekday'],
			$prod->fields['price_recurr_week'] );
		}
		elseif ($item->fields['price_type'] == '0')
		{
			# One-time charge
			$this->recurring_schedule = '';
			$this->next_invoice		  = '';
			$this->price  			  = $item->fields['price_base'] / $item->fields['quantity'];
			$this->bind   			  = '0';
		}
		else
		{
			return false;
		}

		# If set-date type recurring transaction, determine full price:
		if (!$trial && $prod->fields['price_type'] == '1' && $prod->fields['price_recurr_type'] == '1')
		{
			# Get the base product price:
			$price = $product->price_prod($prod->fields, $this->recurring_schedule, $invoice->fields['account_id'], false);
			$this->price  = $price['base'] / $item->fields['quantity'];

			# Get the price of any attributes:
			$price = $product->price_attr($prod->fields, $item->fields['product_attr_cart'], $this->recurring_schedule, $invoice->fields['account_id'], false);
			$this->price += $price['base'] / $item->fields['quantity'];
		}

		# Service settings:
		$this->active 					= '1';
		$this->queue  					= 'new';
		$this->host_ip 		 			= '';
		$this->host_username 			= '';
		$this->host_password 			= '';
		$this->domain_host_tld_id 		= '';
		$this->domain_host_registrar_id = '';
		$this->domain_date_expire 		= '';

		# Parent ID
		$this->parent_id = $service_id;

		# determine if groups defined:
		$groups_defined=false;
		if(!empty($prod->fields['assoc_grant_group'])) {
			// type > 0 or num of days defined?
			if($prod->fields['assoc_grant_group_type'] > 0 || $prod->fields['assoc_grant_group_days'] > 0) {
				// actual groups defined?
				$grant_groups=unserialize($prod->fields['assoc_grant_group']);
				if(is_array($grant_groups) && count($grant_groups)>0) {
					foreach($grant_groups as $key=>$group_id) {
						if($group_id>0) {
							$groups_defined=true;
							break;
						}
					}
				}
			}
			if(!$groups_defined) {
				$prod->fields['assoc_grant_group']=false;
				$prod->fields['assoc_grant_group_type']=false;
				$prod->fields['assoc_grant_group_days']=false;
			}
		}

		# Determine the Service Type:
		$this->type = 'none';
		if($item->fields['item_type'] == '0')
		{
			# NONE, GROUP, PRODUCT, OR PRODUCT_GROUP:
			if (!$groups_defined && empty($prod->fields['prod_plugin']))
			{
				$this->type   		 = 'none';
			}
			else
			{
				if( $groups_defined && !empty($prod->fields['prod_plugin']))
				{
					$this->type = 'product_group';
				}
				elseif(!empty($prod->fields['prod_plugin']))
				{
					$this->type = 'product';
				}
				elseif($groups_defined)
				{
					$this->type = 'group';
				}
			}
		}
		elseif($item->fields['item_type'] == '1')
		{
			# HOSTING:
			$this->type   		 = 'host';
			$this->host_ip 		 = '';
			$this->host_username = '';
			$this->host_password = '';

			# Is group access also defined?
			if(!empty($prod->fields['assoc_grant_group']))
			$this->type = 'host_group';
		}
		elseif($item->fields['item_type'] == '2')
		{
			# DOMAIN:
			$this->type   		 = 'domain';
			$this->domain_date_expire 		= time() + ($item->fields['domain_term'] * (86400*365));

			# Get the host_tld_id
			$q = "SELECT id, registrar_plugin_id FROM ".AGILE_DB_PREFIX."host_tld WHERE
		        	  name 		=  ".$db->qstr($item->fields['domain_tld'])." AND
		        	  site_id 	=  ".$db->qstr(DEFAULT_SITE);
			$tld = $db->Execute($q);
			$this->domain_host_tld_id 		= $tld->fields['id'];
			$this->domain_host_registrar_id = $tld->fields['registrar_plugin_id'];
		}

		if($this->type == "none" && $this->recurring_schedule == "") {
			# do not create service for one-time charge with no hosting,domain, or group settings
		} else {
			# Create the service record(s):

			for($iii=0; $iii<$item->fields['quantity']; $iii++)
			{
				$this->id = sqlGenID($db,"service");
				$fields = Array('date_orig' 				=> time(),
				'date_orig'					=> time(),
				'parent_id' 				=> $this->parent_id,
				'invoice_id'				=> $item->fields['invoice_id'],
				'invoice_item_id' 			=> $invoice_item_id,
				'account_id'				=> $invoice->fields['account_id'],
				'account_billing_id'		=> $invoice->fields['account_billing_id'],
				'product_id'				=> $item->fields['product_id'],
				'sku' 						=> $item->fields['sku'],
				'active'					=> $this->active,
				'bind' 						=> $this->bind,
				'type'						=> $this->type,
				'queue' 					=> $this->queue,
				'price'						=> $this->price,
				'price_type' 				=> $item->fields['price_type'],
				'taxable'					=> $prod->fields['taxable'],
				'date_last_invoice' 		=> $invoice->fields['date_orig'],
				'date_next_invoice'			=> $this->next_invoice,
				'recur_schedule' 			=> $this->recurring_schedule,
				'recur_type'				=> $prod->fields['price_recurr_type'],
				'recur_weekday' 			=> $prod->fields['price_recurr_weekday'],
				'recur_week'				=> $prod->fields['price_recurr_week'],
				'recur_schedule_change' 	=> $prod->fields['price_recurr_schedule'],
				'recur_cancel'				=> $prod->fields['price_recurr_cancel'],
				'recur_modify' 				=> $prod->fields['price_recurr_modify'],
				'group_grant'				=> $prod->fields['assoc_grant_group'],
				'group_type' 				=> $prod->fields['assoc_grant_group_type'],
				'group_days'				=> $prod->fields['assoc_grant_group_days'],
				'host_server_id' 			=> $prod->fields['host_server_id'],
				'host_provision_plugin_data'=> $prod->fields['host_provision_plugin_data'],
				'host_ip' 					=> $this->host_ip,
				'host_username'				=> $this->host_username,
				'host_password' 			=> $this->host_password,
				'domain_name'				=> $item->fields['domain_name'],
				'domain_tld' 				=> $item->fields['domain_tld'],
				'domain_term'				=> $item->fields['domain_term'],
				'domain_type' 				=> $item->fields['domain_type'],
				'domain_date_expire'		=> $this->domain_date_expire,
				'domain_host_tld_id'		=> $this->domain_host_tld_id,
				'domain_host_registrar_id'	=> $this->domain_host_registrar_id,
				'prod_attr'					=> $item->fields['product_attr'],
				'prod_attr_cart'			=> $item->fields['product_attr_cart'],
				'prod_plugin_name' 			=> @$prod->fields["prod_plugin_file"],
				'prod_plugin_data'			=> @$prod->fields["prod_plugin_data"]);
				$rs = & $db->Execute( sqlInsert($db, "service", $fields, $this->id));
				if ($rs === false) {
					global $C_debug;
					$C_debug->error('service.inc.php','invoiceItemToService', $q . " | " . @$db->ErrorMsg());
				} else {
					# Run the queue on this item:
					$arr['id'] = $this->id;
					$this->queue($arr, $this);
				}
			}
		}

		# Create any discount codes:
		if($prod->fields['discount'] == '1' && !empty($prod->fields['discount_amount']))
		{
			$id = $db->GenID(AGILE_DB_PREFIX . 'discount_id');
			$q = "INSERT INTO ".AGILE_DB_PREFIX."discount SET
	        	id					= ". $db->qstr( $id ) .",
	        	site_id				= ". $db->qstr( DEFAULT_SITE ) .",
	        	date_orig			= ". $db->qstr( time() ) .",
	        	date_start			= ". $db->qstr( time() ) .",
	        	status				= ". $db->qstr( '1' ) .",
	        	name				= ". $db->qstr( 'DISCOUNT-'.$id ) .",
	        	notes				= ". $db->qstr( 'Autogenerated for Invoice Number '.$item->fields['invoice_id'].', SKU '.$item->fields['sku'] ) .",
	        	max_usage_account 	= ". $db->qstr( '1' ) .",
	        	max_usage_global	= ". $db->qstr( '1' ) .",
	        	avail_account_id	= ". $db->qstr( $invoice->fields['account_id'] ) .",
	        	new_status			= ". $db->qstr( '1' ) .",
	        	new_type			= ". $db->qstr( '1' ) .",
	        	new_rate			= ". $db->qstr( $prod->fields['discount_amount'] ) .",
	        	recurr_status		= ". $db->qstr( '1' ) .",
	        	recurr_type			= ". $db->qstr( '1' ) .",
	        	recurr_rate			= ". $db->qstr( $prod->fields['discount_amount'] );
			$db->Execute($q);
		}

		return true;
	}


	##########################
	###   Renew Domain		##
	##########################
	function renewDomain( $item, $billing_id )
	{
		$db = &DB();
		$dbm = new CORE_database();

		# Get the current service details:
		$service = $db->Execute( $dbm->sql_select('service', '*', "id = {$item->fields['service_id']}",'', $db ) );

		# Get new dates
		$term = $service->fields['domain_term'] + $item->fields['domain_term'];
		$expire = $service->fields['domain_date_expire'] + (86400*365*$item->fields['domain_term']);

		$rs = $db->Execute( $sql = sqlUpdate($db, 'service',
		Array(	'date_last_invoice'  => $service->fields['domain_date_expire'],
		'domain_date_expire' => $expire,
		'domain_term' 		 => $term,
		'domain_type' 		 => 'renew',
		'queue' 			=> 'new',
		'account_billing_id' => $billing_id),
		" id = {$item->fields['service_id']} " ) );
		if($rs) return true;
		return false;
	}


	/** get daily cost for a given recurring schedule
        *  @param $schedule Recurring schedule
        */
	function getDailyCost($schedule,$price) {
		$d=Array(7,30.43685,91.31055,182.6211,365.2422, 730.4844, 1095.7266);
		if($price <= 0) return 0;
		return $price/$d["$schedule"];
	}


	##############################
	##	ADD/APPROVE SERVICES    ##
	##############################
	function modifyService($item, $billing_id )
	{
		global $C_debug;

		# Get the product details
		$db= &DB();
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."product WHERE
	        	  id 		=  ".$db->qstr($item->fields['product_id'])." AND
	        	  site_id 	=  ".$db->qstr(DEFAULT_SITE);
		$prod = $db->Execute($q);

		# Get the current service details
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."service WHERE
	        	  id 		=  ".$db->qstr($item->fields['service_id'])." AND
	        	  site_id 	=  ".$db->qstr(DEFAULT_SITE);
		$servrs = $db->Execute($q);
		$service = $servrs->fields;
		$service_id = $service['id'];

		# Determine Price, Price Type, and Next Invoice Date:
		if ($item->fields['price_type'] == '1')
		{
			# Recurring Item
			$this->recurring_schedule = $item->fields['recurring_schedule'];
			$this->price  = $item->fields['price_base'];
			$this->bind   = '1';

			# Determine the next invoice date:
			$this->next_invoice = $this->calcNextInvoiceDate(	$service['date_next_invoice'],
			$this->recurring_schedule,
			$prod->fields['price_recurr_type'],
			$prod->fields['price_recurr_weekday'],
			$prod->fields['price_recurr_week'] );

			# Determine the last invoice date:
			if(empty($service['date_last_invoice']))
			$this->last_invoice = time();
			else
			$this->last_invoice = $service['date_last_invoice'];

			$old_unit = $this->getDailyCost($service['recur_schedule'], $service['price']);
			$new_unit = $this->getDailyCost($item->fields['recurring_schedule'], $this->price);
			//echo "old_unit=$old_unit 
new_unit=$new_unit
"; $daysLeft = ceil(($service['date_next_invoice'] - time())/86400); $prorated = $old_unit * $daysLeft; $daysDiff = ceil($prorated / $new_unit); //echo "daysLeft=$daysLeft prorated=$prorated daysDiff=$daysDiff
"; //echo "dt=". date("d-m-Y", $this->next_invoice)."
"; $this->next_invoice += ($daysDiff * 86400); //echo "final dt=". date("d-m-Y", $this->next_invoice) ."
"; } # If set-date type recurring transaction, determine full price: if ($prod->fields['price_type'] == '1' && $prod->fields['price_recurr_type'] == '1') { include_once(PATH_MODULES.'cart/cart.inc.php'); $cart = new cart; $price = $cart->price_prod($prod->fields, $this->recurring_schedule, $invoice->fields['account_id'], false); $this->price = $price['base']; $price = $cart->price_attr($prod->fields, $item->fields['product_attr_cart'], $this->recurring_schedule, $invoice->fields['account_id'], false); $this->price += $price['base']; } # Determine the Service Type: if(!empty($prod->fields['assoc_grant_group'])) { if(!empty($prod->fields['prod_plugin'])) $this->type = 'product_group'; elseif(!empty($prod->fields['host'])) $this->type = 'host_group'; else $this->type = 'group'; } elseif(!empty($prod->fields['prod_plugin'])) { $this->type = 'product'; } elseif(!empty($prod->fields['host'])) { $this->type = 'host'; } else { $this->type = 'none'; } # Reconfigure host data: $host_arr = ""; if($this->type == "host" || $this->type == "host_group") { $old = serialize($service['host_provision_plugin_data']); $host_arr = $prod->fields['host_provision_plugin_data']; if(is_array($old) && count($old) > 0) foreach($old as $key => $val) if(!isset($host_arr["$key"]) && @$old["$key"] != "") $host_arr["$key"] = $val; } # Create the item record: $q = "UPDATE ".AGILE_DB_PREFIX."service SET date_last = ". $db->qstr( time() ) .", invoice_id = ". $db->qstr( $item->fields['invoice_id'] ) .", invoice_item_id = ". $db->qstr( $item->fields['id'] ) .", account_billing_id = ". $db->qstr( $billing_id ) .", product_id = ". $db->qstr( $prod->fields['id'] ) .", sku = ". $db->qstr( $item->fields['sku'] ) .", active = ". $db->qstr( 1 ) .", type = ". $db->qstr( $this->type ) .", queue = ". $db->qstr( 'edit' ) .", price = ". $db->qstr( @$this->price ) .", price_type = ". $db->qstr( $prod->fields['price_type'] ) .", taxable = ". $db->qstr( $prod->fields['taxable'] ) .", date_last_invoice = ". $db->qstr( @$this->last_invoice ) .", date_next_invoice = ". $db->qstr( @$this->next_invoice ) .", recur_schedule = ". $db->qstr( @$this->recurring_schedule ) .", recur_type = ". $db->qstr( $prod->fields['price_recurr_type'] ) .", recur_weekday = ". $db->qstr( $prod->fields['price_recurr_weekday'] ) .", recur_week = ". $db->qstr( $prod->fields['price_recurr_week'] ) .", recur_schedule_change = ". $db->qstr( $prod->fields['price_recurr_schedule'] ) .", recur_cancel = ". $db->qstr( $prod->fields['price_recurr_cancel'] ) .", recur_modify = ". $db->qstr( $prod->fields['price_recurr_modify'] ) .", group_grant = ". $db->qstr( $prod->fields['assoc_grant_group'] ) .", group_type = ". $db->qstr( $prod->fields['assoc_grant_group_type'] ) .", group_days = ". $db->qstr( $prod->fields['assoc_grant_group_days'] ) .", host_provision_plugin_data=".$db->qstr( @$host_arr ) .", prod_plugin_name = ". $db->qstr( @$prod->fields["prod_plugin_file"] ) .", prod_plugin_data = ". $db->qstr( @$prod->fields["prod_plugin_data"] ) . " WHERE site_id = ".DEFAULT_SITE . " AND id = $service_id"; $rs = $db->Execute($q); if ($rs === false) { global $C_debug; $C_debug->error('service.inc.php','invoiceItemToService', $q . " | " . @$db->ErrorMsg()); } # Run the queue on this item: $this->queue_one($service_id, false); return true; } /** * Calculate next invoice date * * @param int $s last billed date * @param int $schedule schedule: 0=weekly, 1=monthly, 2=quarterly, 3=semi-annually, 4=yearly, 5=2year * @param bool $type type: 0=anniversary, 1=fixed date * @param int $weekday (for fixed date) 1-28 day of month * @param int $week * @return int date */ function calcNextInvoiceDate($s, $schedule, $type, $weekday, $week=false) { //echo '
';print_r(array('s'=>$s,'schedule'=>$schedule,'type'=>$type,'weekday'=>$weekday));
  		# Anniversary billing routine:
		if($type == 0)
		{
			if($schedule == 0)
			return mktime (0, 0, 0, date("m", $s), 	date("d", $s)+7, 	date("Y", $s));
			if($schedule == 1)
			return mktime (0, 0, 0, date("m", $s)+1, date("d", $s), 	date("Y", $s));
			if($schedule == 2)
			return mktime (0, 0, 0, date("m", $s)+3, date("d", $s), 	date("Y", $s));
			if($schedule == 3)
			return mktime (0, 0, 0, date("m", $s)+6, date("d", $s), 	date("Y", $s));
			if($schedule == 4)
			return mktime (0, 0, 0, date("m", $s), 	date("d", $s), 		date("Y", $s)+1);
			if($schedule == 5)
			return mktime (0, 0, 0, date("m", $s), 	date("d", $s), 		date("Y", $s)+2);
			if($schedule == 6)
			return mktime (0, 0, 0, date("m", $s), 	date("d", $s), 		date("Y", $s)+3);
			return false;
		}

		# Set-day/week billing routine:
		if ($type == 1)
		{
			if($schedule == 0) {
				return mktime (0, 0, 0, date("m", $s), 	date("d", $s)+7, 	date("Y", $s));
			} elseif ($schedule == 1) {
				$inc_months = 1;
			}  elseif ($schedule == 2) {
				$inc_months = 3;
			} elseif ($schedule == 3) {
				$inc_months = 6;
			} elseif ($schedule == 4) {
				$inc_months = 12;
			} elseif ($schedule == 5) {
				$inc_months = 24;
			} elseif ($schedule == 6) {
				$inc_months = 36;
			} else {
				return false;
			}

			# calculate the set day of month to bill:
			return mktime(0,0,0,date('m', $s)+$inc_months, $weekday, date('y', $s));
		}
		return 0;
	}


	##############################
	##		ADD   		        ##
	##############################
	function add($VAR)
	{
		$this->construct();
		global $C_debug, $C_translate;
		$validate = true;

		## Set type:
		if(!empty($VAR['service_none'])) {
			$VAR['service_type'] = 'none';
		} elseif(!empty($VAR['service_domain'])) {
			$VAR['service_type'] = 'domain';
		} elseif (!empty($VAR['service_group'])) {
			if(!empty($VAR['service_hosting']))
			$VAR['service_type'] = 'host_group';
			elseif(!empty($VAR['service_product']))
			$VAR['service_type'] = 'product_group';
			else
			$VAR['service_type'] = 'group';
		} elseif (!empty($VAR['service_hosting'])) {
			$VAR['service_type'] = 'host';
		} elseif (!empty($VAR['service_product'])) {
			$VAR['service_type'] = 'product';
		}

		## Set Price Type
		if(!empty($VAR['billing_type']))
		$VAR['service_price_type'] = "1";
		else
		$VAR['service_price_type'] = "0";

		### loop through the field list to validate the required fields
		$type = 'add';
		$this->method["$type"] = explode(",", $this->method["$type"]);
		$arr = $this->method["$type"];
		include_once(PATH_CORE . 'validate.inc.php');
		$validate = new CORE_validate;
		$this->validated = true;

		while (list ($key, $value) = each ($arr)) {
			# get the field value
			$field_var  	= $this->module . '_' . $value;
			$field_name 	= $value;

			# check if this value is unique
			if(isset($this->field["$value"]["unique"]) && isset($VAR["$field_var"])) {
				if(!$validate->validate_unique($this->table, $field_name, "record_id", $VAR["$field_var"])) {
					$this->validated = false;
					$this->val_error[] =  array('field' 		=> $this->table . '_' . $field_name,
					'field_trans' 	=> $C_translate->translate('field_' . $field_name, $this->module, ""),							# translate
					'error' 		=> $C_translate->translate('validate_unique',"", ""));
				}
			}
			if(isset($this->field["$value"]["validate"])) {
				if(isset($VAR["$field_var"])) {
					if($VAR["$field_var"] != '') {
						if(!$validate->validate($field_name, $this->field["$value"], $VAR["$field_var"], $this->field["$value"]["validate"])) {
							$this->validated = false;
							$this->val_error[] =  array('field' 		=> $this->module . '_' . $field_name,
							'field_trans' 	=> $C_translate->translate('field_' . $field_name, $this->module, ""),
							'error' 		=> $validate->error["$field_name"] );
						}
					} else {
						$this->validated = false;
						$this->val_error[] =  array('field' 		=> $this->module . '_' . $field_name,
						'field_trans' 	=> $C_translate->translate('field_' . $field_name, $this->module, ""),
						'error' 		=> $C_translate->translate('validate_any',"", ""));
					}
				} else {
					$this->validated = false;
					$this->val_error[] =  array('field' 		=> $this->module . '_' . $field_name,
					'field_trans' 	=> $C_translate->translate('field_' . $field_name, $this->module, ""),
					'error' 		=> $C_translate->translate('validate_any',"", ""));
				}
			}
		}

		# If recurring, validate & set defaults
		if($VAR['service_price_type'] == 1)
		{
			if(!empty($VAR['date_last_invoice']))
			$last_invoice = $validate->convert_date($VAR['date_last_invoice'],DEFAULT_DATE_FORMAT);
			else
			$last_invoice = time();

			# Determine the next invoice date:
			$next_invoice = $this->calcNextInvoiceDate( $last_invoice,
			@$VAR['product_price_recurr_default'],
			@$VAR['product_price_recurr_type'],
			@$VAR['product_price_recurr_weekday'],
			@$VAR['product_price_recurr_week'] );

		}

		$active = 1;
		$queue  = 'new';

		# Product details
		if(!empty($VAR['service_sku'])) {
			$product_id 	= @$VAR['product_id'];
			$product_sku 	= @$VAR['service_sku'];
		}


		# Hosting Details:
		if(@$VAR['service_type'] == 'host' || @$VAR['service_type'] == 'host_group')
		{
			# validate domain/tld set
			if(empty($VAR['host_domain_name']) || empty($VAR['host_domain_tld'])) {
				$this->validated = false;
				$this->val_error[] =  array('field' 		=> 'service_domain_name',
				'field_trans' 	=> $C_translate->translate('field_domain_name', 'service', ""),
				'error' 		=> $C_translate->translate('validate_any',"", ""));
			} else {
				$domain_name = $VAR['host_domain_name'];
				$domain_tld = $VAR['host_domain_tld'];
			}
		} else if ( @$VAR['service_type'] == 'domain' ) {
			# validate domain/tld set
			if(empty($VAR['domain_name']) || empty($VAR['domain_tld']) || empty($VAR['domain_type'])) {
				$this->validated = false;
				$this->val_error[] =  array('field' 		=> 'service_domain_name',
				'field_trans' 	=> $C_translate->translate('field_domain_name', 'service', ""),
				'error' 		=> $C_translate->translate('validate_any',"", ""));
			}
			else
			{
				$domain_name = $VAR['domain_name'];
				$domain_tld  = $VAR['domain_tld'];
				$domain_type = $VAR['domain_type'];

				# Get the host_tld_id
				$db = &DB();
				$q = "SELECT id,default_term_new,registrar_plugin_id FROM ".AGILE_DB_PREFIX."host_tld WHERE
			        	  name 		=  ".$db->qstr($domain_tld)." AND site_id 	=  ".$db->qstr(DEFAULT_SITE);
				$tld = $db->Execute($q);
				$domain_host_tld_id 		= $tld->fields['id'];
				$domain_host_registrar_id 	= $tld->fields['registrar_plugin_id'];
				$domain_term				= $tld->fields['default_term_new'];
				$domain_date_expire 		= time() + ($domain_term * (86400*365));
			}
		}


		if(!$this->validated)
		{
			# errors...
			global $smarty;
			$smarty->assign('form_validation', $this->val_error);
			global $C_vars;
			$C_vars->strip_slashes_all();
			return;
		} else {

			# Generate the SQL:
			$db = &DB();
			$id = $db->GenID(AGILE_DB_PREFIX.'service_id');
			$q = "INSERT INTO ".AGILE_DB_PREFIX."service SET
		        id						= ". $db->qstr( $id ) .",
		        site_id					= ". $db->qstr( DEFAULT_SITE ) .",
		        date_orig				= ". $db->qstr( time() ) .",
		        date_last				= ". $db->qstr( time() ) .",
		        account_id				= ". $db->qstr( $VAR['service_account_id'] ) .",
		        account_billing_id 		= ". $db->qstr( @$VAR['ccnum'] ) .",
		        product_id				= ". $db->qstr( @$product_id ) .",
		        sku						= ". $db->qstr( @$product_sku ) .",
		        active					= ". $db->qstr( '1' ) .",
		        type					= ". $db->qstr( $VAR['service_type'] ) .",
		        queue					= ". $db->qstr( 'new' ) .",
		        price					= ". $db->qstr( @$VAR['product_price_base'] ) .",
		        price_type				= ". $db->qstr( @$VAR['service_price_type'] ) .",
		        taxable					= ". $db->qstr( @$VAR['product_taxable'] ) .",
		        date_last_invoice		= ". $db->qstr( @$last_invoice ) .",
		        date_next_invoice		= ". $db->qstr( @$next_invoice ) .",
		        recur_schedule			= ". $db->qstr( @$VAR['product_price_recurr_default'] ) .",
		        recur_type				= ". $db->qstr( @$VAR['product_price_recurr_type'] ) .",
		        recur_weekday			= ". $db->qstr( @$VAR['product_price_recurr_weekday'] ) .",
		        recur_schedule_change 	= ". $db->qstr( @$VAR['product_price_recurr_schedule'] ) .",
		        recur_cancel			= ". $db->qstr( @$VAR['product_price_recurr_cancel'] ) .",
		        recur_modify			= ". $db->qstr( @$VAR['product_price_recurr_modify'] ) .",
		        group_grant				= ". $db->qstr( serialize(@$VAR['product_assoc_grant_group']) ) .",
		        group_type				= ". $db->qstr( @$VAR['product_assoc_grant_group_type'] ) .",
		        group_days				= ". $db->qstr( @$VAR['product_assoc_grant_group_days'] ) .",
		        host_server_id			= ". $db->qstr( @$VAR['product_host_server_id'] ) .",
		        host_provision_plugin_data=".$db->qstr( serialize(@$VAR['product_host_provision_plugin_data']) ) .",
		        host_ip					= ". $db->qstr( @$VAR['host_ip'] ) .",
		        host_username			= ". $db->qstr( @$VAR['host_username'] ) .",
		        host_password			= ". $db->qstr( @$VAR['host_password'] ) .",
		        domain_name				= ". $db->qstr( @$domain_name ) .",
		        domain_tld				= ". $db->qstr( @$domain_tld ) .",
		        domain_term				= ". $db->qstr( @$domain_term ) .",
		        domain_type				= ". $db->qstr( @$domain_type ) .",
		        domain_date_expire		= ". $db->qstr( @$domain_date_expire ) .",
		        domain_host_tld_id		= ". $db->qstr( @$domain_host_tld_id ) .",
		        domain_host_registrar_id= ". $db->qstr( @$domain_host_registrar_id ) . ",
		        prod_plugin_name		= ". $db->qstr( @$VAR["product_prod_plugin_file"] ) .",
		        prod_plugin_data		= ". $db->qstr( serialize(@$VAR["product_prod_plugin_data"]) );
			$rs = $db->Execute($q);

			if($VAR['service_type'] == 'group' || $VAR['service_type'] = 'product' || $VAR['service_type'] = 'product_group') $this->queue_one($id, false);

			global $VAR;
			$VAR["id"] = $id;
			define('FORCE_PAGE', 'service:view');
			return;

		}
	}

	/**
	 * Add a service to an account - data for the add template
	 */
	public function tpl_add($VAR) {
		global $smarty,$C_validate;

		$db = &DB();

		if (! empty($VAR['product_id']) && $VAR['clearall'] == 0) {
			if (! empty($VAR['changeproduct'])) {
				# Get selected product ID and use it as a template
				$rs = $db->Execute(sqlSelect($db,'product','*',sprintf('id=%s',$VAR['product_id'])));

				# Get assoc groups
				if (! empty($rs->fields['assoc_grant_group'])) {
					$groups = unserialize($rs->fields['assoc_grant_group']);

					if (! empty($groups[0]))
						$rs->fields['group'] = $groups;
				}

				$fields = $rs->fields;
			}
		}

		# Get changes submitted, if product not changed:
		if (empty($VAR['clearall']) && empty($VAR['changeproduct'])) {
			foreach ($VAR as $key => $val) {
				if (! empty($val)) {
					$key = ereg_replace('^product_','',$key);
					if (is_array($val))
					$fields[$key] = serialize($val);
					else
					$fields[$key] = $val;
				}
			}
		}

		$smarty->assign('product',@$fields);

		# Get all available products
		$prod = array();
		$rs = $db->Execute($q = sqlSelect($db,'product','id,sku','(prod_plugin=1 OR price_type=1 OR (assoc_grant_group_type=0 OR assoc_grant_group_type>=1) OR host=1)','sku'));
		while (! $rs->EOF) {
			array_push($prod,$rs->fields);
			$rs->MoveNext();
		}

		if (! empty($prod))
			$smarty->assign('prod_menu',$prod);
	}

	/**
	 * Allow a user to modify a service
	 */
	public function user_modify($VAR) {
		global $smarty;

		# Validate user is logged in
		if (empty($VAR['service_id']) || SESS_LOGGED == false)
			return;

		# Validate user is auth for current service id:
		$db = &DB();
		$rs = $db->Execute(sqlSelect($db,'service','*',array('account_id'=>SESS_ACCOUNT,'id'=>$VAR['service_id'],'recur_modify'=>1)));
		if (! $rs || ! $rs->RecordCount())
			return false;

		$this->modify($VAR);
	}


	/**
	 * Modify Service
	 */
	public function modify($VAR) {
		global $smarty,$C_debug,$C_translate;
		$dbm = new CORE_database;

		# Get service details:
		$db = &DB();
		$rs = $db->Execute(sqlSelect($db,'service','*',array('id'=>$VAR['service_id'])));
		if (! $rs || ! $rs->RecordCount())
			return false;

		# If product id not set, generate array
		if (empty($VAR['id'])) {
			$product_id = $rs->fields['product_id'];
			if (empty($product_id))
				return false;

			$prod = $db->Execute(sqlSelect($db,'product','modify_waive_setup,modify_product_arr',array('id'=>$product_id)));
			if (! $prod || ! $prod->RecordCount())
				return false;

			$arr = unserialize($prod->fields['modify_product_arr']);
			if (! is_array($arr) || ! count($arr) || empty($arr[0]))
				return false;

			$smart = array();
			foreach ($arr as $pid) {
				$prod = $db->Execute(sqlselect($db,'product','id,sku,price_base',array('id'=>$pid)));

				if ($prod && $prod->RecordCount())
					array_push($smart,$prod->fields);
			}

			$smarty->assign('product_arr',$smart);

		} elseif(empty($VAR['confirm_modify'])) {
			# Validate selected product is authorized
			$do = true;
			$product_id = $rs->fields['product_id'];
			$prod = $db->Execute(sqlSelect($db,'product','modify_waive_setup,modify_product_arr',array('id'=>$product_id)));
			if (! $prod || ! $prod->RecordCount())
				$do = false;

			$arr = unserialize($prod->fields['modify_product_arr']);
			if (! is_array($arr) || ! count($arr) || empty($arr[0]))
				$do = false;

			if ($do) {
				$do = false;
				foreach($arr as $pid)
					if ($pid == $VAR['id']) {
						$do = true;
						break;
					}
			}

			$smarty->assign('product_show',$do);

			# Determine if setup fees are ignored
			$smarty->assign('waive_setup',$prod->fields['modify_waive_setup']);
		}
	}

	/**
	 * View a record
	 */
	public function view($VAR) {
		global $smarty,$C_auth;

		$smart = parent::view($VAR);

		$dbm = new CORE_database;
		$db  = &DB();

		# Get the invoice days, to see if the next invoice can be generated
		require_once PATH_MODULES.'invoice/invoice.inc.php';
		$invoice = new invoice($VAR);

		# Get recent invoice details for this service
		$sql = sprintf('SELECT A.id,A.date_orig,A.total_amt,A.billed_amt,A.process_status FROM %sinvoice_item B INNER JOIN %sinvoice A ON (B.invoice_id=A.id AND service_id=%s) WHERE A.site_id=%s AND B.site_id=%s ORDER BY A.id DESC',AGILE_DB_PREFIX,AGILE_DB_PREFIX,$smart['id'],DEFAULT_SITE,DEFAULT_SITE);

		$inv = $db->SelectLimit($sql,5);
		if ($inv && $inv->RecordCount() > 0) {
			while (! $inv->EOF) {
				if ($inv->fields['total_amt'] > $inv->fields['billed_amt'] && $inv->fields['suspend_billing'] != 1)
					$inv->fields['due'] = $inv->fields['total_amt']-$inv->fields['billed_amt'];

					$smart['invoice'][] = $inv->fields;
					$inv->MoveNext();
			}
		}

		# Allow modification of service plan?
		if (! empty($VAR['user']) && ! empty($smart['product_id'])) {
		} elseif (empty($VAR['user'])) {
		} else {
			$smart['recur_modify'] = '0';
		}

		# Get recurring details?
		if (! empty($VAR['user']) && $smart['recur_schedule_change'] == 1 && ! empty($smart['product_id']))
			$do = true;
		elseif (empty($VAR['user']) && ! empty($smart['product_id']))
			$do = true;
		else
			$do = false;

		if ($do && $smart['date_next_invoice'] > 0 && !empty($smart['product_id'])) {
			# Get the product details:
			$prod = $db->Execute(sqlSelect($db,'product','*',array('id'=>$smart['product_id'])));

			global $C_auth;
			$g_ar = unserialize($prod->fields['price_group']);

			if (is_array($g_ar)) {
				foreach ($g_ar as $period => $price_arr) {
					foreach ($price_arr as $group => $vals) {
						if (@$price_arr['show'] == '1') {
							if (is_numeric($group) && $C_auth->auth_group_by_account_id($smart['account_id'],$group)) {
								if ($vals['price_base'] != '' && $vals['price_base'] > 0)
									if (empty($ret[$period]['base']) || $vals['price_base'] < $ret[$period]['base'])
										$ret[$period]['base'] = $vals['price_base'];
							}
						}
					}
				}
			}

			# Make sure the current billing price is set correctly.
			$ret[$smart['recur_schedule']]['base'] = $smart['price'];
			ksort($ret);

			if (! is_array($ret)) {
				if (! empty($VAR['user'])) {
					$ret[$smart['recur_schedule']]['base'] = $smart['price'];
					$smarty->assign('recur_price',$ret);

				} else {
					$smarty->assign('recur_price',false);
				}

			} else {
				$smarty->assign('recur_price',$ret);
			}

		} else {
			$smarty->assign('recur_price',false);
		}

		if ($smart['date_next_invoice'] && $smart['date_next_invoice']-86400*$invoice->invoice_days() < time())
			$smart['generate_invoice'] = true;

		$smarty->clear_assign('record');
		$smarty->assign('record',$smart);
	}


	##############################
	##		UPDATE		        ##
	##############################
	function update($VAR)
	{
		$this->construct();
		# provisioning data;
		if(!empty($VAR['product_host_provision_plugin_data']))
		{
			$VAR['service_host_provision_plugin_data'] = $VAR['product_host_provision_plugin_data'];
			$s = serialize($VAR['service_host_provision_plugin_data']);
		}

		# product plugin data;
		if(!empty($VAR['product_prod_plugin_data']))
		{
			$VAR['service_prod_plugin_data'] = $VAR['product_prod_plugin_data'];
		}


		# check if any changes were made that calls for edit queue status
		$queue = true;

		# get the previous data
		$db     = &DB();
		$sql    = 'SELECT * FROM ' . AGILE_DB_PREFIX . 'service WHERE
                       id           =  ' . $db->qstr( $VAR['service_id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);

		if(!empty($VAR['queue_force'])) {
			$queue = false;
		} elseif(!empty($VAR['service_host_provision_plugin_data']) ) {
			# compare username
			if($rs->fields['host_username'] != $VAR['service_host_username']) {
				$VAR['service_queue'] = 'edit';
				# compare password
			} elseif ($rs->fields['host_password'] != $VAR['service_host_password']) {
				$VAR['service_queue'] = 'edit';
				# compare ip
			} elseif (!empty($VAR['service_host_ip']) && $rs->fields['host_ip'] != $VAR['service_host_ip']) {
				$VAR['service_queue'] = 'edit';
				# compare plugin data
			} elseif ( $rs->fields['host_provision_plugin_data'] != $s ) {
				$VAR['service_queue'] = 'edit';
			} else {
				# suspend/unsuspend
				if($VAR['service_active'] == 0 && $VAR['service_active'] != $rs->fields['active'] ) {
					$VAR['service_queue'] = 'inactive';
				} elseif ($VAR['service_active'] == 1 && $VAR['service_active'] != $rs->fields['active'] ) {
					$VAR['service_queue'] = 'active';
				} else {
					$VAR['service_queue'] =   $rs->fields['queue'];
					$queue = false;
				}
			}
		} else {
			# suspend/unsuspend
			if($VAR['service_active'] == 0 && $VAR['service_active'] != $rs->fields['active'] ) {
				$VAR['service_queue'] = 'inactive';
			} elseif ($VAR['service_active'] == 1 && $VAR['service_active'] != $rs->fields['active'] ) {
				$VAR['service_queue'] = 'active';
			} else {
				$VAR['service_queue'] =   $rs->fields['queue'];
				$queue = false;
			}
		}

		# update record
		$type = "update";
		$this->method["$type"] = explode(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->update($VAR, $this, $type);

		# Run queue now
		if($queue)  {
			$this->queue_one($VAR['service_id'],false);
			return true;
		}
	}

	function delete($VAR)  {
		$this->construct();
		$dbx = new CORE_database;
		$db  = &DB();

		### Get the array
		if(isset($VAR["delete_id"]))
		$id = explode(',', $VAR["delete_id"]);
		elseif (isset($VAR["id"]))
		$id = explode(',', $VAR["id"]);

		### Loop:
		for($i=0; $iconstruct();
		$type = "search";
		$this->method["$type"] = explode(",", $this->method["$type"]);
		$dba = new CORE_database;
		$smart = $dba->search_show($VAR, $this, $type);

		global $smarty, $C_list;
		if($C_list->is_installed('host_server')) $host=true;
		$total_amount=0;
		$db = &DB();

		for($i=0; $iserver[$id])) {
					$smart[$i]['server_name'] = $this->server_id;
				} else {
					$sql = $dba->sql_select("host_server", "name", "id = $id", false, $db);
					$rs = $db->Execute($sql);
					$this->server_id = $rs->fields['name'];
					$smart[$i]['server_name'] = $this->server_id;
				}
			}
		}

		$smarty->assign('service', $smart);
		$smarty->assign('total_amount', $C_list->format_currency($total_amount, ""));
	}

	/**
    	 * User initiate domain renewal
    	 */
	function user_renew_domain($VAR)
	{
		# Validate user is owner of this domain
		$db = &DB();
		$rs = $db->Execute ( sqlSelect($db, 'service', '*', "id = ::{$VAR['id']}:: AND account_id = ". SESS_ACCOUNT) );
		if(!SESS_LOGGED OR !$rs OR $rs->RecordCount() == 0) {
			global $C_debug;
			$C_debug->alert('Unable to renew domain at this time');
			return;
		}
		include_once (PATH_MODULES.'invoice/invoice.inc.php');
		$invoice = new invoice;
		$id = $invoice->generatedomaininvoice($rs->fields, $invoice);
		if($id) {
			global $VAR;
			$VAR['id'] = $id;
			define('FORCE_PAGE', "invoice:user_view");
		}
	}

	function user_search($VAR) {
		# Lock the user only for his billing_records:
		if(!SESS_LOGGED)  {
			return false;
		}

		# Lock the account_id
		$VAR['service_account_id'] = SESS_ACCOUNT;
		$this->construct();
		$type = "search";
		$this->method["$type"] = explode(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->search($VAR, $this, $type);
	}

	function user_search_show($VAR)  {
		# Lock the user only for his billing_records:
		if(!SESS_LOGGED)  {
			return false;
		}
		$this->construct();
		$type = "search";
		$this->method["$type"] = explode(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->search_show($VAR, $this, $type);
	}


	/**
	 * User view a service
	 */
	public function user_view($VAR) {
		# Check that user is logged in
		if (SESS_LOGGED != '1') {
			echo 'Sorry, you must be logged in!';

			return false;
		}

		# Check that the correct account owns this billing record
		$db = &DB();
		$rs = $db->Execute(sqlSelect($db,'service','*',array('id'=>$VAR['id'],'account_id'=>SESS_ACCOUNT)));
		if (! $rs || $rs->RecordCount() == 0)
			return false;

		$VAR['user'] = true;
		$this->view($VAR);
	}

	function search_export($VAR) {
		# require the export class
		$this->construct();
		require_once (PATH_CORE   . "export.inc.php");

		# Call the correct export function for inline browser display, download, email, or web save.
		if($VAR["format"] == "excel")
		{
			$type = "export_excel";
			$this->method["$type"] = explode(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_excel($VAR, $this, $type);
		}
		else if ($VAR["format"] == "pdf")
		{
			echo 'Not Supported';
		}
		else if ($VAR["format"] == "xml")
		{
			$type = "export_xml";
			$this->method["$type"] = explode(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_xml($VAR, $this, $type);
		}
		else if ($VAR["format"] == "csv")
		{
			$type = "export_csv";
			$this->method["$type"] = explode(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_csv($VAR, $this, $type);
		}
		else if ($VAR["format"] == "tab")
		{
			$type = "export_tab";
			$this->method["$type"] = explode(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_tab($VAR, $this, $type);
		}
	}

	function construct()  {
		$this->module = "service";
		$this->xml_construct = PATH_MODULES . "" . $this->module . "/" . $this->module . "_construct.xml";
		$C_xml = new CORE_xml;
		$construct = $C_xml->xml_to_array($this->xml_construct);
		$this->method   = $construct["construct"]["method"];
		$this->trigger  = $construct["construct"]["trigger"];
		$this->field    = $construct["construct"]["field"];
		$this->table 	= $construct["construct"]["table"];
		$this->module 	= $construct["construct"]["module"];
		$this->cache	= $construct["construct"]["cache"];
		$this->order_by = $construct["construct"]["order_by"];
		$this->limit	= $construct["construct"]["limit"];
	}
}
?>