<?php
/**
 * AgileBill - Open Billing Software
 *
 * This body of work is free software; you can redistribute it and/or
 * modify it under the terms of the Open AgileBill License
 * License as published at http://www.agileco.com/agilebill/license1-4.txt
 *
 * Originally authored by Tony Landis, AgileBill LLC
 *
 * Recent modifications by Deon George
 *
 * @author Deon George <deonATleenooksDOTnet>
 * @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 <tony@agileco.com>
 * @package AgileBill
 * @subpackage Module:Cart
 */

/**
 * The main AgileBill Module Cart Class
 *
 * @package AgileBill
 * @subpackage Module:Cart
 */
class cart extends OSB_module {
	/**
	 * Admin View Cart Contents
	 */
	function admin_view($VAR) {
		if(!empty($VAR['account_id'])) {
			$this->account_id = $VAR['account_id'];
		} else {
			return false;
		}
		$this->admin_view = true;
		$this->user_view($VAR,$this);
	}

	/**
	 * Admin Add Cart Contents
	 */
	function admin_add($VAR) {
		if(!empty($VAR['account_id'])) {
			$this->account_id = $VAR['account_id'];
			$db = &DB();
			$rs = $db->Execute(sqlSelect($db,"session","id","account_id=$this->account_id"));
			if($rs && $rs->RecordCount()) {
				$this->session_id = $rs->fields['id'];
			} else {
				$this->session_id = SESS;
			}
		} else {
			return false;
		}
		$this->add($VAR,$this);
	}

	/**
	 * Admin change cart qty
	 */
	function admin_changeqty($VAR) {
		if(!empty($VAR['account_id'])) {
			$this->account_id = $VAR['account_id'];
		} else {
			return false;
		}
		$this->admin = true;
		$this->changeqty($VAR,$this);
	}

	/**
	 * Change the quantity of an item
	 */
	function changeqty($VAR) {
		if(empty($this->account_id)) $this->account_id = SESS_ACCOUNT;
		if(empty($this->session_id)) $this->session_id = SESS;

		@$id = $VAR['id'];
		@$qty = $VAR['qty'];
		if ($id <= 0 ) return;

		$db = &DB();
		if ($qty == '0') {
			# Product Plugin Level deletion
			$cartid = & $db->Execute($sql=sqlSelect($db,"cart","*","id=::{$id}:: AND session_id = ::{$this->session_id}::"));
			if($cartid) $product = & $db->Execute($sql=sqlSelect($db,"product","*","id=::{$cartid->fields['product_id']}::"));
			if(!empty($product->fields['prod_plugin']) && !empty($product->fields['prod_plugin_data'])) {
				$prodplgfile = PATH_PLUGINS.'product/'. $product->fields['prod_plugin_file'] . '.php';
				if(is_file($prodplgfile)) {
					include_once(PATH_PLUGINS.'product/'. $product->fields['prod_plugin_file'] . '.php');
					eval('$prodplg = new plgn_prov_'. $product->fields['prod_plugin_file'] .';');
					if(is_object($prodplg)) {
						if(is_callable(array($prodplg,'delete_cart'))) {
							$prodplg->delete_cart($VAR,$cartid->fields);
						}
					}
				}
			}

			# delete main cart items & subitems:
			$sql='';
			if(empty($this->admin)) $sql = "AND session_id = ::{$this->session_id}::";
			$rs = & $db->Execute($sql=sqlDelete($db,"cart","(id=::{$id}:: OR cart_parent_id=::{$id}:: ) $sql"));

			global $smarty;
			$smarty->assign('js',false);
			return false;
		}

		# update the quantity:
		if(!eregi("^[0-9]{1,5}$",$qty)) $qty = 1;
		if($qty < 1) $qty = 1;

		if(!$this->admin) $sql_extra = " AND session_id=::$this->session_id::"; else $sql_extra='';
		if($VAR["type"] == 1) {
			$fields=Array('quantity'=>$qty);
			$db->Execute($sql=sqlUpdate($db,"cart",$fields,"id=::$id:: $sql_extra"));
		} else if ($VAR["type"] == 2) {
			$fields=Array('recurr_schedule'=>$VAR["schedule"]);
			$db->Execute($sql=sqlUpdate($db,"cart",$fields,"id=::$id:: $sql_extra"));
		} else if ($VAR["type"] == 3) {
			# change domain term
			$fields=Array('domain_term'=>$VAR["term"]);
			$db->Execute($sql=sqlUpdate($db,"cart",$fields,"id=::$id:: $sql_extra"));
		}

		# get the product id:
		$result = $db->Execute(sqlSelect($db,"cart","*","id=$id $sql_extra","cart_type,date_orig"));

		# get the product details:
		$product = $db->Execute(sqlSelect($db,"product","*","id={$result->fields["product_id"]}"));

		if($result->fields["cart_type"] == "2") {
			# domain name, get pricing
			include_once(PATH_MODULES.'host_tld/host_tld.inc.php');
			$tldObj=new host_tld;
			$tldprice = $tldObj->price_tld_arr($result->fields['domain_tld'],$result->fields['host_type'],false,false,false,$this->account_id);
			$qty = $result->fields['domain_term'];
			$base_price = $tldprice[$qty];
			$setup_price = 0;
		} else if($result->fields["cart_type"] == "3") {
			# ad-hoc, get price
			$base_price = $result->fields["ad_hoc_amount"] * $result->fields["quantity"];
			$setup_price = $result->fields["ad_hoc_setup"] * $result->fields["quantity"];
		} else {
			include_once(PATH_MODULES.'product/product.inc.php');
			$productObj=new product;

			# get pricing for this product:
			$prod_price = $productObj->price_prod($product->fields,$result->fields["recurr_schedule"],$this->account);
			$setup_price = $prod_price["setup"] * $result->fields["quantity"];
			$base_price = $prod_price["base"] * $result->fields["quantity"];

			# get pricing for any attributes:
			$attr_price = $productObj->price_attr($product->fields,$result->fields["product_attr"],$result->fields["recurr_schedule"],$this->account);
			$setup_price += ($attr_price["setup"] * $result->fields["quantity"]);
			$base_price += ($attr_price["base"] * $result->fields["quantity"]);

			# get the qty
			$qty = $result->fields["quantity"];
		}

		# set the smarty fields:
		global $smarty;
		$smarty->assign('qty',$qty);
		$smarty->assign('base',$base_price);
		$smarty->assign('setup',$setup_price);
		$smarty->assign('js',true);
		return;
	}

	/**
	 * Get cart contents and return adodb rs
	 */
	public function sGetContents() {
		return $this->sql_GetRecords(array('where'=>array('session_id'=>SESS)));
	}

	/**
	 * View Cart Contents
	 *
	 * @uses invoice
	 */
	public function user_view($VAR) {
		global $smarty;

		# Get cart contents
		if (! count($results = $this->sGetContents())) {
			$smarty->assign('result','0');

			return false;
		}

		include_once(PATH_MODULES.'invoice/invoice.inc.php');
		$invoice = new invoice;
		$invoice->setRecordAttr('account_id',SESS_ACCOUNT);

		foreach ($results as $result) {
			$invoice->aaddItem(array(
				'cart_id'=>$result['id'],
				'domain_name'=>$result['domain_name'],
				'domain_term'=>$result['domain_term'],
				'domain_tld'=>$result['domain_tld'],
				'domain_type'=>$result['domain_type'],
				'item_type'=>0,
				'product_id'=>$result['product_id'],
				'quantity'=>$result['quantity'],
				'recurr_schedule'=>$result['recurr_schedule'],
				'product_attr'=>$result['product_attr'],
				'product_attr_cart'=>$result['product_attr'],
				'host_type'=>$result['host_type'],
				'type'=>in_array($result['host_type'],array('register')) ? 'domain' : null
			));
		}

		$smarty->assign('results',$invoice->sCountItems());
		$smarty->assign('cart',$invoice->getItems());
	}

	/**
	 * Start add to cart process
	 */
	public function add($VAR) {
		# Set the current account
		if (empty($this->account_id))
			$this->account_id = SESS_ACCOUNT;
		if (empty($this->session_id))
			$this->session_id = SESS;

		# Determine the type to be added to the domain: (domain or product)
		if (! empty($VAR['product_id'])) {
			if ($this->validate_product($VAR,$VAR['product_id'],$this->account_id)) {
				# Add hosting / domain
				if (! empty($VAR['domain_type']))
					$this->product_add_host($VAR);
				# Standard product (no hosting or domain)
				else
					$this->product_add($VAR);

				return true;
			}

		} elseif (! empty($VAR['domain_name']) && ! empty($VAR['domain_tld'])) {
			# Add Domain only:
			# @todo Remove the is_array test, and have 1 set of code
			if (is_array($VAR['domain_name'])) {
				for ($i=0;$i<count($VAR['domain_name']); $i++) {
					if($this->validate_domain($VAR['domain_name'][$i],$VAR['domain_tld'][$i])) {
						$this->domain_add($VAR['domain_name'][$i],$VAR['domain_tld'][$i],@$VAR['host_type'][$i],@$VAR['domain_term'][$i]);
					}
				}

			} else {
				if($this->validate_domain($VAR['domain_name'],$VAR['domain_tld'])) {
					$this->domain_add($VAR['domain_name'],$VAR['domain_tld'],@$VAR['host_type'],@$VAR['domain_term']);
				}
			}
		}
	}

	/**
	 * Add a domain name to cart
	 *
	 * @param string $domain
	 * @param string $tld
	 * @param int $type
	 * @param int $term
	 */
	private function domain_add($domain,$tld,$type,$term=false) {
		if (empty($type))
			return false;

		$db = &DB();

		if (! $term || empty($term) || ! is_numeric($term)) {
			# Get the default term for this domain:
			$rs = $db->Execute(sqlSelect($db,'host_tld','default_term_new',array('name'=>$tld)));
			if ($rs === false || empty($rs->fields['default_term_new']))
				$term = 1;
			else
				$term = $rs->fields['default_term_new'];
		}

		# Insert into cart
		$fields =array(
			'date_orig' => time(),
			'session_id' => $this->session_id,
			'account_id' => $this->account_id,
			'cart_type' => 2,
			'host_type' => $type,
			'domain_term' => $term,
			'domain_name' => $domain,
			'domain_tld' => $tld);

		$db->Execute(sqlInsert($db,'cart',$fields));
	}

	/**
	 * Add an ad-hoc line item to the cart
	 */
	public function ad_hoc($VAR) {
		$db = &DB();
		if (! empty($VAR['account_id'])) {
			$rs = $db->Execute(sqlSelect($db,'session','id',array('account_id'=>$VAR['account_id'])));
			if ($rs && $rs->RecordCount() ==1)
				$this->session_id = $rs->fields['id'];
			else
				$this->session_id = SESS;

		} else {
			return false;
		}

		if (empty($VAR['ad_hoc_sku']) || empty($VAR['ad_hoc_name']) || $VAR['ad_hoc_amount'] == '') {
			global $C_debug,$C_translate;

			$C_debug->alert($C_translate->translate('ad_hoc_err','cart',''));

			return false;
		}

		if (empty($VAR['quantity']))
			$qty = 1;
		else
			$qty = $VAR['quantity'];

		if (empty($VAR['ad_hoc_taxable']))
			$VAR['ad_hoc_taxable'] = 0;

		# Attribs: (ad_hoc_attr_var & ad_hoc_attr_val)
		foreach ($VAR['ad_hoc_attr_var'] as $key => $value)
			if ($value && $VAR['ad_hoc_attr_val'][$key])
				$attr[$value] = $VAR['ad_hoc_attr_val'][$key];

		if (count($attr))
			$attrib = serialize($attr);
		else
			$attrib = '';

		# Create the record
		$fields = array(
			'date_orig' => time(),
			'session_id' => $this->session_id,
			'account_id' => $VAR['account_id'],
			'product_attr' => $attrib,
			'cart_type' => 3,
			'quantity' => $qty,
			'ad_hoc_sku' => $VAR['ad_hoc_sku'],
			'ad_hoc_name' => $VAR['ad_hoc_name'],
			'ad_hoc_amount' => $VAR['ad_hoc_amount'],
			'ad_hoc_taxable' => $VAR['ad_hoc_taxable']);

		$db->Execute(sqlInsert($db,'cart',$fields));
	}

	/**
	 * Add a product to the cart
	 */
	private function product_add($VAR) {
		if (empty($VAR['quantity']))
			$qty = 1;
		else
			$qty = $VAR['quantity'];

		if (!empty($VAR['attr']))
			@$attr = serialize($VAR['attr']);
		else
			$attr = '';

		# Create the Main DB Record:
		$db = &DB();
		$fields = array(
			'date_orig' => time(),
			'session_id' => $this->session_id,
			'account_id' => @$VAR['account_id'],
			'product_attr' => $attr,
			'cart_type' => 0,
			'quantity' => $qty,
			'product_id' => $VAR['product_id'],
			'recurr_schedule' => @$VAR['recurr_schedule'],
			'service_id' => @$VAR['service_id']);

		$db->Execute(sqlInsert($db,'cart',$fields));
	}

	/**
	 * Add an assoc required product
	 *
	 * @param int $product_id
	 * @todo Unused, calling code is commented out.
	 */
	private function product_req_add($product_id) {
		$db = &DB();
		$fields = array(
			'date_orig' => time(),
			'session_id' => $this->session_id,
			'account_id' => $this->account_id,
			'cart_type' => 0,
			'quantity' => 1,
			'product_id' => $product_id);

		$db->Execute(sqlInsert($db,'cart',$fields));
	}

	/**
	 * Add a product to the cart
	 */
	private function product_add_host($VAR) {
		if (! empty($VAR['attr']))
			@$attr = serialize($VAR['attr']);
		else
			$attr = serialize(array(''));

		$db = &DB();
		$fields = array(
			'date_orig' => time(),
			'session_id' => $this->session_id,
			'account_id' => @$VAR['account_id'],
			'product_attr' => $attr,
			'cart_type' => 1,
			'quantity' => 1,
			'product_id' => $VAR['product_id'],
			'recurr_schedule' => @$VAR['recurr_schedule'],
			'service_id' => @$VAR['service_id'],
			'host_type' => $VAR['domain_type'],
			'domain_name' => $VAR['domain_name'],
			'domain_tld' => $VAR['domain_tld']);

		$id = sqlGenID($db,'cart');
		$db->Execute(sqlInsert($db,'cart',$fields,$id));

		# Get the default domain registration length:
		if ($VAR['domain_type'] == 'transfer' || $VAR['domain_type'] == 'register') {
			$domain_term = 1;

			$result = $db->Execute(sqlSelect($db,'host_tld','default_term_new',array('name'=>$VAR['domain_tld'])));
			if ($result && ! empty($result->fields['default_term_new']))
				$domain_term = $result->fields['default_term_new'];

			# Add child domain if register or transfer
			$fields = array(
				'date_orig' => time(),
				'session_id' => $this->session_id,
				'account_id' => @$VAR['account_id'],
				'product_attr' => $attr,
				'cart_type' => 2,
				'quantity' => 1,
				'host_type' => $VAR['domain_type'],
				'domain_name' => $VAR['domain_name'],
				'domain_tld' => $VAR['domain_tld'],
				'domain_term' => $domain_term,
				'cart_parent_id' => $id);

			$db->Execute(sqlInsert($db,'cart',$fields));
		}
	}

	/**
	 * Validate A Domain
	 *
	 * @param string $domain
	 * @param string $tld
	 * @return bool
	 */
	private function validate_domain($domain,$tld) {
		# Check that it is not already in the user's cart:
		$db = &DB();

		$result = $db->Execute(sqlSelect($db,'cart','id',sprintf("(session_id=::%s:: OR account_id=) AND domain_name=:: AND domain_tld=::%s::",
			$this->session_id,SESS_ACCOUNT,$tld)));

		if ($result && $result->RecordCount()) {
			global $C_debug,$C_translate;
			$C_debug->alert($C_translate->translate('err_prod_already','cart',''));

			return false;

		} else {
			return true;
		}
	}

	/**
	 * Validate A Product
	 *
	 * @param array $VAR
	 * @param int $product_id
	 * @param int $account_id
	 * @return bool
	 */
	private function validate_product($VAR,$product_id,$account_id) {
		global $C_translate,$C_debug,$C_auth;
		$db = &DB();

		# Can user add inactive items
		if ($C_auth->auth_method_by_name('invoice','add'))
			$active = array();
		else
			$active = array('active'=>1);

		# Validate that product exists
		$result = $db->Execute(sqlSelect($db,'product','*',array_merge(array('id'=>$product_id),$active)));
		if (! $result || $result->RecordCount() == 0) {
			$C_debug->alert($C_translate->translate('err_no_prod','cart',''));

			return false;
		}

		# Check that product is not already in cart
		if ($result->fields['cart_multiple'] != '1' && empty($VAR['service_id'])) {
			$rs = $db->Execute(sqlSelect($db,'cart','id',array('product_id'=>$product_id,'session_id'=>$this->session_id)));
			if(! $rs || $rs->RecordCount() > 0) {
				$C_debug->alert($C_translate->translate('err_prod_already','cart',''));

				return false;
			}
		}

		# Validate groups:
		$groups = unserialize($result->fields['group_avail']);
		$auth = false;
		for ($ii=0; $ii<count($groups); $ii++) {
			if ($C_auth->auth_group_by_id($groups[$ii])) {
				$auth = true;
				break;
			}
		}

		if (! $auth)
			return false;

		# Validate any required products:
		if (! empty($result->fields['assoc_req_prod']))
			$reqarr = unserialize($result->fields["assoc_req_prod"]);
		else
			$reqarr = false;

		$reqtype = $result->fields['assoc_req_prod_type'];
		$assoc = true;

		if (is_array($reqarr) && ! empty($reqarr[0])) {
			/*
			if($reqtype == 0 && is_array($reqarr)) {
				# ALL are required
				for($i=0; $i<count($reqarr); $i++)
				if(!$this->assoc_prod($reqarr[$i])) {
					$assoc = false;

					# Add the required product:
					$this->product_req_add($reqarr[$i]);
				}
			} elseif ($reqtype == 1 && is_array($reqarr)) {
				# ONE is required
				$assoc = false;
				for($i=0; $i<count($reqarr); $i++) {
					if($this->assoc_prod($reqarr[$i])) {
						$assoc = true;
						$i = count( $reqarr );
					} else {
						# add the required product:
						$this->product_req_add($reqarr[$i]);
					}
				}
			}*/

			if(! SESS_LOGGED) {
				$C_debug->alert($C_translate->translate('err_assoc_login','cart',''));

				return false;
			}

			$items='<br/>';
			foreach ($reqarr as $prod_id) {
				$prodrs = $db->Execute(sqlSelect($db,'product_translate','*',array('product_id'=>$prod_id,'language_id'=>SESS_LANGUAGE)));
				if ($prodrs && $prodrs->RecordCount())
					$items .= sprintf('- <b><a href="?_page=product:details&id=%s">%s</a></b><br/>',$prod_id,$prodrs->fields['name']);
			}

			$C_translate->value('cart','items',$items);
			$msg = $C_translate->translate('err_assoc_req','cart','');

			if ($reqtype == 0)
				$C_debug->alert(sprintf('%s %s',$msg,$C_translate->translate('assoc_all','cart','')));
			else
				$C_debug->alert(sprintf('%s %s',$msg,$C_translate->translate('assoc_one','cart','')));

			return false;
		}

		# Product Plugin Level Validation
		$product = $result;
		if (! empty($product->fields['prod_plugin']) && !empty($product->fields['prod_plugin_data'])) {
			$prodplgfile = sprintf('%sproduct/%s.php',PATH_PLUGINS,$product->fields['prod_plugin_file']);

			if (is_file($prodplgfile)) {
				include_once($prodplgfile);
				eval('$prodplg = new plgn_prov_'.$product->fields['prod_plugin_file'].';');
				if (is_object($prodplg)) {
					if (is_callable(array($prodplg,'validate_cart'))) {
						$result = $prodplg->validate_cart($VAR,$product);
						if (! $result) {
							$C_debug->alert($result);
							return false;
						}
					}
				}
			}
		}

		# Service upgrade
		if (! empty($VAR['service_id'])) {
			# Validate account logged in
			if (SESS_LOGGED == false)
				return false;

			$dbm = new CORE_database;

			# Validate account owns service, service is modifyable, active, not canceled, and exists
			$rs = $db->Execute(sqlSelect($db,'service','*',sprintf('recur_modify=1 AND active=1 AND (suspend_billing IS NULL OR suspend_billing=0) AND account_id=%s AND id=%s',
				SESS_ACCOUNT,$VAR['service_id'])));
			if (! $rs || $rs->RecordCount() == 0)
				return false;

			# Validate selected product_id is in allowed modify array for selected service
			if (empty($rs->fields['product_id']))
				return false;

			$product_id = $rs->fields['product_id'];
			$prod = $db->Execute(sqlSelect($db,'product','*',array('id'=>$product_id)));
			if (! $prod || $prod->RecordCount() == 0)
				return false;

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

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

			if (!$do)
				return false;

			# Make sure this service is not in the cart
			$sql = 'DELETE FROM ' . AGILE_DB_PREFIX . 'cart WHERE site_id='.$db->qstr(DEFAULT_SITE).' AND service_id='.$db->qstr($VAR['service_id']) ;
			$rs = $db->Execute($sql);

			# Make sure this service has no outstanding invoices:
			$p = AGILE_DB_PREFIX;
			$sql = "SELECT DISTINCT {$p}invoice.id, {$p}invoice_item.id
	            		FROM {$p}invoice,{$p}invoice_item
	            		WHERE {$p}invoice.site_id = ".DEFAULT_SITE." AND {$p}invoice_item.site_id = ".DEFAULT_SITE."
	            		AND {$p}invoice_item.service_id = ".$db->qstr($VAR['service_id'])."
	            		AND {$p}invoice_item.invoice_id = {$p}invoice.id  AND {$p}invoice.billing_status != 1";
			$rs = $db->Execute($sql);
			if ($rs && $rs->RecordCount() > 0) {
				echo "Invoice(s) in unpaid status for selected service ID {$VAR['service_id']}, cannot upgrade";
				return false;
			}
		}

		return true;
	}

	/**
	 * Validate Associated Prod Req.
	 *
	 * @param int $product_id
	 * @return bool
	 * @todo Unused, calling code is commented out.
	 */
	function assoc_prod($product_id) {
		# check if required assoc product is in cart
		$db = &DB();
		$p 	= AGILE_DB_PREFIX;
		$rs = $db->Execute(sqlSelect($db,"cart","*","product_id=::$product_id:: AND session_id=::".SESS."::"));
		if($rs->RecordCount() > 0) {
			return true;
		} else if (SESS_LOGGED) {
			# check if required assoc product has been purchased
			$sql ="SELECT {$p}invoice.account_id, {$p}invoice_item.product_id
                FROM {$p}invoice_item LEFT JOIN {$p}invoice ON {$p}invoice_item.invoice_id = {$p}invoice.id
                WHERE   {$p}invoice.account_id       = " . $db->qstr(SESS_ACCOUNT) . "
                AND     {$p}invoice_item.product_id  = " . $db->qstr( $product_id ) . "
                AND     {$p}invoice.process_status   = " . $db->qstr( "1" ) . "
                AND     {$p}invoice.site_id          = " . $db->qstr(DEFAULT_SITE) . "
                AND     {$p}invoice_item.site_id     = " . $db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($sql);
			$rs->RecordCount();
			if($rs->RecordCount() > 0)  return true;
		}
		return false;
	}
}
?>