<?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
 * 
 * For questions, help, comments, discussion, etc., please join the
 * Agileco community forums at http://forum.agileco.com/ 
 *
 * @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
 * @version 1.4.93
 */
	
	
/**
 * The main AgileBill Invoice Class 
 */
class invoice
{
	/**  Enable summary invoice view that rolls multiple instances of the same sku w/identical base&setup price & attributes into one line item */
	var $summarizeInvoice=true;	
	
	/**
	 * Holds sku's of line items to exclude from invoice summarys (pdf view only)
	 *
	 * @var array
	 */
	var $summarizeInvoiceExclude;	 
 
	/** Invoice type, 0=new, 1=recurr */
	var $type=0;
	
	/** Invoice Creation Timestamp */
	var $date_orig;
	
	/** Last modification Timestamp */
	var $date_last;
	
	/** Invoice Id */
	var $record_id;
	
	/** Invoice Item Id */
	var $item_id=1;
	
	/** Parent Invoice Id for recurring */
	var $parent_id;	
	
	/** Account Id for invoice */
	var $account_id;
	
	/** Affiliate Id of Invoice */
	var $affiliate_id;
	
	/** Account Billing Id */
	var $account_billing_id;
	
	/** Campaign Id */
	var $campaign_id;
	
	/** Reseller Id */
	var $reseller_id;

	/** Billed Currency Id */
	var $billed_currency_id;
	
	/** Actual Billed Currency selected by Client */
	var $actual_billed_currency_id;
	 
	/** Checkout Plugin Id */
	var $checkout_plugin_id;
	
	/** Array of checkout plugin data returned by checkout plugin */
	var $checkout_plugin_data;
	
	/** Current Notice Count */
	var $notice_count=0;
			
	/** Last Notice Timestamp */
	var $notice_date;	
	
	/** Due Date Timestamp */
	var $due_date;
	
	/** Net Term Id */
	var $net_term_id=false;
	
	/** Net Term Last Notice/Late fee Timestamp */
	var $net_term_date_last;
	
	/** Net Term Interval Count */
	var $net_term_intervals=0;
	
	/** Process Status */
	var $process_status=0;
	
	/** Billing Status */
	var $billing_status=0;
	
	/** Suspend Billing Status */
	var $suspend_billing=0;
	
	/** Printed Invoice Status */
	var $print_status=0;
	
	/** Refunded Invoice Status */
	var $refund_status=0;
	
	/** Calcuate Taxes */
	var $tax=true;		

	/** Calculate Discounts */
	var $discount=true;			 
	
	/** Total Invoice Amount */
	var $total_amt=0;
	
	/** Total Amount Billed */
	var $billed_amt=0;
	
	/** Actual total amount billed (converted to local) */
	var $actual_billed_amt=0;
	
	/** Total Tax Amount */
	var $tax_amt=0;
	
	/** Total Discount Amount */
	var $discount_amt=0;
 
	/** Recurring Amount */
	var $recur_amt;
	
	/** Recurring Array for Later Processing */
	var $recur_arr;
	
	/** IP Address of User */
	var $ip=USER_IP;
	
	/** Array of the Invoice items  */
	var $invoice_item;
	
	/** Array of the discounts for the Invoice items */
	var $item_discount;
	
	/** Array of the taxes for the Invoice Items */
	var $item_tax;
	
	/** Tracking to determine payment options */
 	var $any_new=false;
 	var $any_trial=false;
 	var $any_recurring=false;
 	
 	/** Invoice Config Global Options */
 	var $invoice_delivery=1;
 	var $invoice_format=0; 
	var $notice_max=MAX_BILLING_NOTICE; 
	var $grace_period=GRACE_PERIOD; 	
 	 
	
	
	/**
	 * Get the global level invoice settings 
	 */
	function setupGlobal() {
		$db=&DB();
		$invopt=$db->Execute(sqlSelect($db,"setup_invoice","*","")); 
		if($invopt && $invopt->RecordCount()) { 
			$this->invoice_delivery=$invopt->fields['invoice_delivery'];
			$this->invoice_format=$invopt->fields['invoice_show_itemized'];
		}
	}
	
	/**
	 * Get the account level invoice options
	 *
	 * @param int $id Account Id
	 */
	function setupAccount() {
		if(!$this->account_id) return; 
		$db=&DB();
		$acctrs=$db->Execute(sqlSelect($db,"account","invoice_grace,invoice_advance_gen","id=$this->account_id"));
		if($acctrs && $acctrs->RecordCount()) {
			$this->advance_gen=$acctrs->fields['invoice_advance_gen'];
			if($this->grace_period == GRACE_PERIOD && !empty($acctrs->fields['invoice_grace'])) $this->grace_period=$acctrs->fields['invoice_grace'];
		}  
	}	
	
	/**
	 * Initialize new invoice creation
	 *
	 * @param bool $type 0=new 1=recur
	 */
	function initNew($type=0) {	
		$this->type=$type;
		$this->date_orig=time();
		$this->date_last=time(); 
		
		global $C_list;
		$this->net_term = $C_list->is_installed('net_term'); 
		 
		// get account invoice defaults
		$this->setupAccount();
	}
		
	/**
	 * Commit the current invoice/items/discounts/taxes
	 *
	 * @param object $taxObj		Object for Tax Calculation
	 * @param object $discountObj	Object for Discount Calculation	  
	 * @param bool $email			Send customer/admin e-mails 
	 */
	function commitNew(&$taxObj, &$discountObj, $email=true) {
		
		// init DB transaction
		$db=&DB(); 
		$db->BeginTrans();
		
		// get invoice id
		if(empty($this->record_id)) $this->record_id = sqlGenID($db,"invoice");
		
		// serialized records:
		if(is_array($this->checkout_plugin_data)) $this->checkout_plugin_data=serialize($this->checkout_plugin_data);
		if(is_array($this->recur_arr)) $this->recur_arr=serialize($this->recur_arr);
		
		// dates & defaults
		if(empty($this->due_date)) $this->due_date=time();
		if(empty($this->date_orig)) $this->date_orig=time();
		if(empty($this->date_last)) $this->date_last=time();
		if(empty($this->notice_next_date)) $this->notice_next_date=$this->due_date+86400;
	  					
		// net terms  
		if ($this->net_term && !$this->billing_status && $this->total_amt>0) {
			include_once(PATH_MODULES.'net_term/net_term.inc.php');
			$net=new net_term; 
			$this->net_term_id = $net->termsAllowed($this->account_id, $this->checkout_plugin_id);
			if(empty($this->net_term_date_last)) $this->net_term_date_last=time();
		} 
		 			
		// insert invoice
		$fields=Array(
			'date_orig'=>$this->date_orig,
			'date_last'=>$this->date_last, 
			'parent_id'=>$this->parent_id, 
			'type'=>$this->type, 
			'process_status'=>$this->process_status, 
			'billing_status'=>$this->billing_status, 
			'suspend_billing'=>$this->suspend_billing,  
			'refund_status'=>$this->refund_status, 
			'print_status'=>$this->print_status, 
			'account_id'=>$this->account_id, 
			'account_billing_id'=>$this->account_billing_id, 
			'affiliate_id'=>$this->affiliate_id, 
			'campaign_id'=>$this->campaign_id, 
			'reseller_id'=>$this->reseller_id, 
			'checkout_plugin_id'=>$this->checkout_plugin_id, 
			'checkout_plugin_data'=>$this->checkout_plugin_data,
			'tax_amt'=>$this->tax_amt, 
			'discount_amt'=>$this->discount_amt, 
			'total_amt'=>$this->total_amt, 
			'billed_amt'=>$this->billed_amt, 
			'recur_amt'=>$this->recur_amt, 
			'recur_arr'=>$this->recur_arr, 
			'actual_billed_amt'=>$this->actual_billed_amt, 
			'billed_currency_id'=>$this->billed_currency_id, 
			'actual_billed_currency_id'=>$this->actual_billed_currency_id, 
			'notice_count'=>$this->notice_count, 
			'notice_max'=>$this->notice_max, 
			'notice_next_date'=>$this->notice_next_date,
			'due_date'=>$this->due_date, 
			'grace_period'=>$this->grace_period,   
			'net_term_id'=>$this->net_term_id, 
			'net_term_date_last'=>$this->net_term_date_last, 
			'net_term_intervals'=>$this->net_term_intervals, 
			'ip'=>$this->ip 
		);
		$db->Execute($sql=sqlInsert($db, "invoice", $fields, $this->record_id));
		
		// loop through invoice items
		if(is_array($this->invoice_item)) {
			foreach($this->invoice_item as $id=>$fields) {
				// get an invoice_item id
				$invoice_item_id = sqlGenID($db, "invoice_item");
				
				// domain sku's
				if ($fields['item_type'] == 2) {
					$fields['sku'] = "DOMAIN-".strtoupper($fields['domain_type']);	
					$fields['price_type'] = '0';
				}
				
				// build e-mail item details
				if($email) {
					$email_instructions='';
					if($fields['item_type']<2 && !empty($fields['product_id'])) {  
						// product, get email instructions and translated name
						$translate_prod=$db->Execute(sqlSelect($db,"product_translate","email_template,name","product_id={$fields['product_id']} and language_id=::".SESS_LANGUAGE."::"));
						if($translate_prod && $translate_prod->RecordCount()) {
							$instructions=$translate_prod->fields['email_template'];
							$name=$translate_prod->fields['name'];
						} else {
							$name=$fields["sku"];
						}
					} elseif ($fields['item_type'] == 2) { 
						$name=strtoupper($fields['domain_name'].".".$fields['domain_tld']);
					} else {
						if(!empty($fields['product_name']))	$name=$fields['product_name']; else $name=$fields['sku'];
					}
					// add to e-mail array	
					$email_arr[] = array('Qty' => '('.$fields["quantity"].')', 'Item' => 'SKU '.$fields["sku"], 'Price' => number_format($fields["total_amt"],2), 'Name' => $name, 'Instructions' => $instructions);											
				} 
				
				// insert the invoice item_id 
				$fields['invoice_id']=$this->record_id;
				$fields['date_orig']=time();
				$attr = serialize($fields['product_attr']);
				$fields['product_attr']=$fields['product_attr_cart'];
				$fields['product_attr_cart']=$attr; 
				$db->Execute($sql=sqlInsert($db, "invoice_item", $fields, $invoice_item_id));
			 
				// insert taxes
				if($this->tax && $this->tax_amt > 0 && !empty($this->tax_arr[$id])) {
					$taxObj->invoice_item($this->record_id, $invoice_item_id, $this->account_id, $this->tax_arr[$id]);
				}
				
				// insert discounts
				if($this->discount && $this->discount_amt>0 && !empty($this->discount_arr[$id])) {
					$discountObj->invoice_item($this->record_id, $invoice_item_id, $this->account_id, $this->discount_arr[$id]);				
				} 
			}
		}
		 
		// complete DB transaction
		$db->CompleteTrans();
		
		// complete building e-mail notices and send
		if($email) { 
			include_once(PATH_MODULES.'email_template/email_template.inc.php');
			
        	// Create the products order list for the e-mail:
        	$e_itm_usr = '';
        	$e_itm_adm = '';
        	if(is_array($email_arr)) {
	        	foreach($email_arr as $i=>$em) {
	        		$e_itm_usr .= $em['Qty'].'  '.$em['Item'].' ('.$em['Name'].')  '.$em['Price'];
	        		$e_itm_adm .= $em['Qty'].'  '.$em['Item'].' ('.$em['Name'].')  '.$em['Price']."\r\n";
	        		if(!empty($email_arr[$i]['Instructions'])) $e_itm_usr .= "\r\n	* " . $email_item_arr[$i]['Instructions'];	        		
	        		$e_itm_usr .= "\r\n";   		
	        	} 
	        	$e_arr_user = Array('%products%' => $e_itm_usr);
	        	$e_arr_adm  = Array('%products%' => $e_itm_adm);
        	}
        	
        	// e-mail invoice creation confirmation 
        	$mail = new email_template;
    		$mail->send('invoice_confirm_user',  $this->account_id, $this->record_id, $this->checkout_plugin_id, $e_arr_user); 
    		$email = new email_template;
    		$email->send('admin->invoice_confirm_admin', $this->account_id, $this->record_id, $this->checkout_plugin_id, $e_arr_adm); 
		}
		
		// net terms?
		if($this->net_term_id) {
			$this->approveInvoice(array('id'=>$this->record_id), $this);  
			return $this->record_id;
		}	
    	   
		// Determine the approval status by checkout plugin type & settings:
		if($email && $this->billing_status == 0 && $this->billed_amt > 0 ) {
			global $C_list;
			if($this->checkout_type == 'redirect') {
				// User e-mail alert of due invoice
				$email = new email_template;
				$email->send('invoice_due_user', $this->account_id, $this->record_id, $user_currency, $C_list->date($this->due_date));
			} elseif ($this->checkout_type == 'other') {
				// Admin e-mail alert of manual payment processing
				$email = new email_template;
				$email->send('admin->invoice_due_admin', $this->account_id, $this->record_id, $admin_currency, $C_list->date($this->due_date));
			}
		} elseif($this->billed_amt>0 ) {
			if($email) {
				// User alert of payment processed
				$email = new email_template;
				$email->send('invoice_paid_user', $this->account_id, $this->record_id, $this->billed_currency_id, '');
				// Admin alert of payment processed
				$email = new email_template;
				$email->send('admin->invoice_paid_admin', $this->account_id, $this->record_id, $this->billed_currency_id, '');
			}
			$this->autoApproveInvoice($this->record_id);
		} elseif($this->billed_amt == 0 && $this->billing_status == 1 ) {
			$this->autoApproveInvoice($this->record_id);
		}
		
		// return invoice id
		return $this->record_id;
	}
  
	/**
	 * Add an invoice item
	 *
	 * @param int $id 					Reference ID for use in Cart or false
	 * @param object $taxObj			Object for Tax Calculation
	 * @param object $discountObj		Object for Discount Calculation
	 * @param int $item_type 			0/1=Product/Service/Hosting  2=Domain  3=Add Hoc 
	 * @param string $taxable			True, False, or 'validate' to locate the specified $product id and verify
	 * @param int $service_id			If this is for a service upgrade, this will be defined
	 * @param int $parent_id			Item Parent Id
	 * @param int $product_id			Item Product Id
	 * @param array $product_attr		Item attributes from the cart/prev service
	 * @param string $product_name		Item product name
	 * @param string $sku				Item Product SKU
	 * @param int $quantity				Item Quantity
	 * @param float $price_base			Item Base price 
	 * @param float $price_setup		Item Setup Price
	 * @param float $discount_manual	Ad Hoc Discount Amount
	 * @param int $recurring_schedule	Item recurring schedule, 0=week, 1=month, 2=quarter, 3=semi-annual, 4=annual, 5=bi-year
	 * @param int $date_start			Date service started
	 * @param int $date_stop			Date service stops
	 * @param string $domain_name		Domain name
	 * @param string $domain_tld		Domain TLD
	 * @param int $domain_term			Domain Term
	 * @param string $domain_type		Domain Type (register, transfer, renew, park, ns_transfer)	 
	 */
	function addItem($id, &$taxObj, &$discountObj, $item_type, $taxable=false, $service_id=false, $parent_id=false, $product_id=false, $product_attr=false, $product_name=false, $sku=false, $quantity=1, $price_base=false, $price_setup=false, $discount_manual=false, $recurring_schedule=false, $date_start=false, $date_stop=false, $domain_name=false, $domain_tld=false, $domain_term=false, $domain_type=false) {
		$tax_amt=0;
		$total_amt=0;
		$discount_amt=0;
		
		// define correct qty
		if($quantity<=0) $quantity=1;
		
		// determine the reference id for this item 
		if($id>0) {
			$this->item_id=$id;
		} else {
			$this->item_id++;
		}
		  
		// get the product details
		if($product_id && $item_type<2) { 
			$db=&DB();
			$product=$db->Execute(sqlSelect($db,"product","*","id=$product_id"));
			if($product && $product->RecordCount()) {
				$taxable = $product->fields['taxable'];
				$this->product["$this->item_id"] = $product->fields;
			}
		
		// get the tld details
		} elseif($item_type==2) { 
			$db=&DB();
			$tld=$db->Execute(sqlSelect($db,"host_tld","*","name=::$domain_tld::"));	
			if($tld && $tld->RecordCount())
				$taxable = $tld->fields['taxable'];
		}
		
		// get the product pricing details if product
		$price_type=0;
		if($price_base===false && $price_setup===false && $product_id && $item_type<2) {						
			if($product && $product->RecordCount()) {
				$price_type=$product->fields['price_type'];
				$sku=$product->fields['sku'];
				include_once(PATH_MODULES.'product/product.inc.php');
				$productObj=new product;

				// get pricing for this product:
				$prod_price  = $productObj->price_prod($product->fields, $recurring_schedule, $this->account_id);
				$price_base  = $prod_price["base"];
				$price_setup = $prod_price["setup"];
				 			
				// calculate any product attributes fees
	 			$attr_price  = $productObj->price_attr($product->fields, $product_attr, $recurring_schedule, $this->account_id);
				$price_base  += $attr_price["base"];
				$price_setup += $attr_price["setup"];

				// determine price type for checkout
				if ($product->fields["price_type"] == '0')
					$this->any_new=true;
				else if ($product->fields["price_type"] == '1')
					$this->any_recurring=true;
				else if ($product->fields["price_type"] == '2')
					$this->any_trial=true;	
							
			} else { 
				$this->any_new=true;
			}
		} else { 
			$this->any_new=true;
		}
		
		// get the TLD pricing details if domain
		if($price_base===false && $price_setup===false && $domain_tld && $domain_term && $domain_type) {
			include_once(PATH_MODULES.'host_tld/host_tld.inc.php');
			$tldObj = new host_tld; 
			$tldprice = $tldObj->price_tld_arr($domain_tld, $domain_type, false, false, false, $this->account_id);   
			if($domain_type == "park") {
				$price_base = $tldprice;
			} else {
				$price_base = $tldprice["$domain_term"];
				$this->tld_arr["$this->item_id"] = $tldprice;
			}
		}
		 	
		// set total amount for this line item before attributes, taxes, or discounts
		$price_base *= $quantity;
		$price_setup *= $quantity;
		$total_amt = ($price_setup + $price_base);
		
		// format product attributes for storage
		$product_attr_cart=false; 
		if(($item_type==0 || $item_type>2) && is_array($product_attr)) $product_attr_cart = $this->get_product_attr_cart($product_attr); 
			 
		// recurring taxes and arrays
		if($price_base>0 && $price_type==1) 
		{		
			// increment the total invoice recurring amount
			$this->recur_amt += $price_base;
							
			// determine taxes for the recurring amount
			if($this->tax && $taxable && $price_base>0 && $this->account_id) { 
				$recur_tax_arr = $taxObj->calculate($price_base, $this->country_id, $this->state);				   
				if(is_array($recur_tax_arr)) foreach($recur_tax_arr as $tx) $this->recur_amt += $tx['rate'];
			}			
 
			// get the recurring arrays for price and invoice
			if($product && $product->RecordCount()) {
				$this->price_arr["$this->item_id"] = $productObj->price_recurr_arr($product->fields, $this->account_id);  
				$this->recur_arr[] = Array (
					'price' 		 => $price_base*$quantity,
					'recurr_schedule'=> $recurring_schedule,
					'recurr_type' 	 => $product->fields['price_recurr_type'],
					'recurr_weekday' => $product->fields['price_recurr_weekday'],
					'recurr_week' 	 => $product->fields['price_recurr_week']
				);		
			}	 								
		}
		 			
		// calculate any ad-hoc line item level (admin) discounts
		if($this->discount && $discount_manual>0) {
			$total_amt -= $discount_manual;
			$discount_amt += $discount_manual;
			$this->discount_amt += $discount_amt; 
			$discountObj->add_manual_discount($discount_manual,'MISC',$this->item_id);
		}
				
		// account level discounts
		if($this->discount && $this->account_id)  {		
			// calculate any database level discounts for this item (both account specific and session specific)
			$discount_amt = $discountObj->calc_all_discounts(0, $this->item_id, $product_id, $total_amt, $this->account_id, $this->total_amt+$total_amt);
			$total_amt -= $discount_amt; 
			$this->discount_amt += $discount_amt; 			
		}
				
		// add to total discount array
		if(is_array($discountObj->discount_arr)) {
			$this->discount_arr["$this->item_id"] = $discountObj->discount_arr;
		}
		
		// increment invoice total amount
		$this->total_amt += $total_amt;
 
		// calculate any taxes for current item 
		if($this->tax && $taxable && $total_amt>0 && $this->account_id) {  
			$tax_arr = $taxObj->calculate($total_amt, $this->country_id, $this->state);				   
			if(is_array($tax_arr)) {
				foreach($tax_arr as $tx) $tax_amt += $tx['rate']; 
				$this->item_tax["$this->item_id"] = $tax_arr;
				$this->tax_arr["$this->item_id"] = $tax_arr; 
			}
			$this->tax_amt += $tax_amt;
			$this->total_amt += $tax_amt;		
		}
		
		// store the fields to an array
		$this->invoice_item["$this->item_id"]=Array(
			'item_type'=>$item_type,
			'price_type'=>$price_type,
			'taxable'=>$taxable,
			'service_id'=>$service_id,
			'parent_id'=>$parent_id,
			'product_id'=>$product_id,
			'product_attr'=>$product_attr,
			'product_attr_cart'=>$product_attr_cart,
			'product_name'=>$product_name,
			'sku'=>$sku,
			'quantity'=>$quantity,
			'price_base'=>$price_base,
			'price_setup'=>$price_setup,
			'recurring_schedule'=>$recurring_schedule,
			'date_start'=>$date_start,
			'date_stop'=>$date_stop,
			'domain_name'=>$domain_name,
			'domain_tld'=>$domain_tld,
			'domain_term'=>$domain_term,
			'domain_type'=>$domain_type, 
			'total_amt'=>$total_amt,
			'tax_amt'=>$tax_amt,
			'discount_amt'=>$discount_amt
		);
	}
	
	/**
	 * Group all taxes to lump sums
	 */
	function group_taxes() { 
		if(is_array($this->tax_arr)) {
			foreach($this->tax_arr as $taxarr) foreach($taxarr as $taxes) $arr[$taxes["name"]]+=$taxes["rate"];  
			if(is_array($arr)) {
				foreach($arr as $a=>$b) $ret[] = Array('name'=>$a, 'rate'=>$b);	
				return $ret;		 
			} 
		} 
	}
	  
	/**
	 * Group all discounts to lump sums
	 */
	function group_discounts() { 
		if(is_array($this->discount_arr)) { 
			foreach($this->discount_arr as $discarr) foreach($discarr as $discounts) $arr[$discounts["discount"]]+=$discounts["amount"];  
			if(is_array($arr)) {
				foreach($arr as $a=>$b) $ret[] = Array('name'=>$a, 'total'=>$b);	
				return $ret;		 
			}
		} 
	}	
	
	/**
	 * Build a formatted product attribute list
	 *
	 * @param array $attributes
	 * @return string Formatted product attribute list
	 */
	function get_product_attr_cart($attributes) {
		# Set the attribute array:
		if(!empty($attributes) && is_array($attributes)) {
			$db=&DB();
			$product_attr = false; 
			foreach($attributes as $id=>$value) {
				if (!empty($value)) {
					if(is_numeric($id)) { 
						$attr = $db->Execute(sqlSelect($db,"product_attr","name","id=$id"));
						if ($attr && $attr->RecordCount()) $product_attr .= "{$attr->fields['name']}==".ereg_replace("\r\n", "<br>", $value)."\r\n";
					} else {
						$product_attr .= "{$id}=={$value}\r\n";
					}
				}
			}
		}
		return $product_attr;
	}
	
	    			
	
	/** Custom Tracking
	*/
	function custom_tracking($VAR)
	{
		# Get the invoice id
		if(SESS_LOGGED == false)
		return false;

		# Check if we are in the iframe
		if(empty($VAR['_escape']) || empty($VAR['confirm']))
		{
			echo '<iframe id="custom_ecom_track" style="border:0px; width:0px; height:0px;"scrolling="auto" '.
			'frameborder="0" SRC="?_page=core:blank&_escape=1&confirm=1&do[]=invoice:custom_tracking&rand='.md5(microtime()).'"></iframe>';
			return;
		}

		# Get the un-tracked invoice details
		$db = &DB();
		$sql = "SELECT * FROM ".AGILE_DB_PREFIX."invoice WHERE
					(   custom_affiliate_status IS NULL OR
						custom_affiliate_status = 0 ) 
					AND billing_status = ".$db->qstr(1)." 
					AND site_id = ".$db->qstr(DEFAULT_SITE)." 
					AND account_id = ".$db->qstr(SESS_ACCOUNT); 
		$result = $db->Execute($sql);
		if ($result === false) {
			global $C_debug;
			$C_debug->error('','', $db->ErrorMsg(). "\r\n\r\n". $sql);
			return false;
		}
		if($result->RecordCount() == 0) {
			echo 'none';
			return false;
		}

		# Get the totals
		$invoice = '';
		$total_amount = false;
		while(!$result->EOF)
		{
			if(!empty($invoice))
			$invoice .= '-';
			$invoice .= $result->fields['id'];
			$amt = $result->fields["total_amt"];
			$total_amount += $amt;
			$result->MoveNext();
		}

		# echo the custom tracking code to the screen:
		if(!is_file(PATH_FILES.'tracking.txt')) return false;
		$tracking = file_get_contents(PATH_FILES.'tracking.txt');
		$tracking = ereg_replace('%%amount%%', "$total_amount", $tracking);
		$tracking = ereg_replace('%%invoice%%', $invoice, $tracking);
		$tracking = ereg_replace('%%affiliate%%', SESS_AFFILIATE, $tracking);
		$tracking = ereg_replace('%%campaign%%', SESS_CAMPAIGN, $tracking);
		$tracking = ereg_replace('%%account%%', SESS_ACCOUNT, $tracking);
		echo $tracking;
 
		# Update the record so it is not tracked again
		$sql = "UPDATE ".AGILE_DB_PREFIX."invoice
					SET 
						custom_affiliate_status = ".$db->qstr('1')."
					WHERE 
						account_id = ".$db->qstr(SESS_ACCOUNT)." 
					AND
						billing_status = ".$db->qstr(1)." 
					AND
						site_id = ".$db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		if ($rs === false) {
			global $C_debug;
			$C_debug->error('','', $db->ErrorMsg(). "\r\n\r\n". $sql);
		}
		return true;
	}

 


	/** Performance:  (for the admin dashboard)
	*/
	function performance($VAR)
	{
		global $smarty, $C_list, $C_translate;

			
		# Get the period type, default to month
		if (empty($VAR['period']))
		$p = 'm';
		else
		$p = $VAR['period'];

		# Determine the correct period language:
		if($p=='' or $p == 'm') 
		{
			$pTrans = $C_translate->translate('thismonth','invoice','') . ' '.
					  $C_translate->translate('vs','invoice','') . ' ' .
					  $C_translate->translate('lastmonth','invoice','');
			$pFore = $C_translate->translate('thismonth','invoice','');
		} 
		elseif ($p == 'w') 
		{
			$pTrans = $C_translate->translate('thisweek','invoice','') . ' '.
					  $C_translate->translate('vs','invoice','') . ' ' .
					  $C_translate->translate('lastweek','invoice','');	
			$pFore = $C_translate->translate('thisweek','invoice','');	
		}
		elseif ($p == 'y') 
		{
			$pTrans = $C_translate->translate('thisyear','invoice','') . ' '.
					  $C_translate->translate('vs','invoice','') . ' ' .
					  $C_translate->translate('lastyear','invoice','');	
			$pFore = $C_translate->translate('thisyear','invoice','');
		}
		
		$smarty->assign('period_compare', $pTrans);
		$smarty->assign('period_forcast', $pFore); 
		
		 	
		
		# Get the period start & end
		switch ($p) {
			case 'w':
			$dow   = date('w');
			$this_start = mktime(0,0,0,date('m'),      date('d')-$dow,             date('y'));
			$this_end   = mktime(23,59,59,date('m'),   date('d'),                  date('y'));
			$last_start = mktime(0,0,0,date('m'),      date('d',  $this_start)-7,  date('y'));
			$last_end   = $this_start-1;
			break;

			case 'm':
			$this_start = mktime(0,0,0,date('m'), 1,                                date('y'));
			$this_end   = mktime(23,59,59,date('m'),   date('d'),                   date('y'));
			$last_start = mktime(0,0,0,                date('m', $this_start)-1, 1, date('y'));
			$last_end   = $this_start-1;
			break;

			case 'y':
			$this_start = mktime(0,0,0,1,1,                            date('y', time()));
			$this_end   = mktime(23,59,59,     date('m'),  date('d'),  date('y'));
			$last_start = mktime(0,0,0,1,1,                            date('y', $this_start)-1);
			$last_end   = $this_start-1;
			break;
		}

		##############################
		# Get sales for this period
		##############################
		$db     = &DB();
		$this_amt = 0;
		$sql    = 'SELECT total_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                       date_orig    >=  ' . $db->qstr( $this_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $this_end ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		while( !$rs->EOF ) {
			$this_amt += $rs->fields['total_amt'];
			$rs->MoveNext();
		}
		$smarty->assign('sales_current', $this_amt);

		###############################
		# Get sales for last period
		###############################
		$last_amt = 0;
		$sql    = 'SELECT total_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                       date_orig    >=  ' . $db->qstr( $last_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $last_end ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		while( !$rs->EOF ) {
			$last_amt += $rs->fields['total_amt'];
			$rs->MoveNext();
		}
		$smarty->assign('sales_previous', $last_amt);

		###############################
		# Get sales change percentage
		###############################
		if($last_amt > 0)
		$sales_change = $this_amt/$last_amt *100 -100;
		else
		$sales_change = 0;

		if($sales_change == 0)
		$sales_change = '';
		elseif($sales_change < 0)
		$sales_change = '<font color="#990000">' . number_format($sales_change, 1). '%</font>';
		else
		$sales_change = '+'.number_format($sales_change, 1). '%';


		$smarty->assign('sales_change', $sales_change);

		#################################
		# Get forcast for current period
		#################################
		switch ($p) {
			case 'w':
			$dow = date('w')+1;
			$forcast_daily = $this_amt/$dow ;
			$forcast_l_daily = $last_amt / 7;
			@$forcast_change  = $forcast_daily / $forcast_1_daily *100 -100;
			$forcast_current = $forcast_daily * 7;
			break;

			case 'm':
			$forcast_daily = $this_amt / date('d');
			$forcast_1_daily = $last_amt / date('t', mktime(0,0,0,date('m')-1, 1, date('y')));
			@$forcast_change  = $forcast_daily / $forcast_1_daily *100 -100;
			$forcast_current = $forcast_daily * date('t');
			break;

			case 'y':
			$forcast_daily = $this_amt / date('z');
			$forcast_1_daily = $last_amt / 356;
			@$forcast_change  = $forcast_daily / $forcast_1_daily *100 -100;
			$forcast_current = $forcast_daily * 365;
			break;
		}

		$smarty->assign('forcast_current', $forcast_current);

		###############################
		# Get forcast change percentage
		###############################
		if($last_amt > 0  )
		@$forcast_change = $forcast_daily/$forcast_1_daily *100;
		else
		$forcast_change = 0;


		if($forcast_change == 0)
		$forcast_change = '-';
		elseif($forcast_change < 0)
		$forcast_change = '<font color="#990000">' . number_format($forcast_change, 1). '%</font>';
		else
		$forcast_change = '+'.number_format($forcast_change, 1). '%';


		$smarty->assign('forcast_change', $forcast_change);


		####################################################
		# Get Quota for Today to meet Forcasted sales:
		####################################################
		$smarty->assign('quota_current',  $forcast_daily);

		##############################
		# Get AR credits for this period
		##############################
		$this_billed_amt = 0;
		$sql    = 'SELECT billed_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                       date_orig    >=  ' . $db->qstr( $this_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $this_end ) . ' AND
                       billed_amt   >  ' . $db->qstr( 0 ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		while( !$rs->EOF ) {
			$this_billed_amt += $rs->fields['billed_amt'];
			$rs->MoveNext();
		}
		$smarty->assign('ar_credits_current', $this_billed_amt);

		###############################
		# Get AR credits for last period
		###############################
		$last_billed_amt = 0;
		$sql    = 'SELECT billed_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                       date_orig    >=  ' . $db->qstr( $last_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $last_end ) . ' AND
                       billed_amt   >  ' . $db->qstr( 0 ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		while( !$rs->EOF ) {
			$last_billed_amt += $rs->fields['billed_amt'];
			$rs->MoveNext();
		}
		$smarty->assign('ar_credits_previous', $last_billed_amt);

		###############################
		# Get AR Credits change percentage
		###############################
		if($last_billed_amt > 0)
		$ar_change = $this_billed_amt/$last_billed_amt *100 -100;
		else
		$ar_change = 0;

		if($ar_change == 0)
		$ar_change = '-';
		elseif($ar_change < 0)
		$ar_change = '<font color="#990000">' . number_format($ar_change, 1). '%</font>';
		else
		$ar_change = '+'.number_format($ar_change, 1). '%';

		$smarty->assign('ar_credit_change', $ar_change);

		##########################################
		# Get AR Balance
		##########################################
		$this_ar_balance = $this_billed_amt - $this_amt;
		$last_ar_balance = $last_billed_amt - $last_amt;

		$smarty->assign('ar_balance_current', $this_ar_balance);
		$smarty->assign('ar_balance_last',    $last_ar_balance);

		#########################################
		# Get Users  (current)
		#########################################
		$sql    = 'SELECT id FROM ' . AGILE_DB_PREFIX . 'account WHERE
                       date_orig    >=  ' . $db->qstr( $this_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $this_end ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		$users_current = $rs->RecordCount();
		$smarty->assign('users_current', $users_current);

		#########################################
		# Get Users  (previous)
		#########################################
		$sql    = 'SELECT id FROM ' . AGILE_DB_PREFIX . 'account WHERE
                       date_orig    >=  ' . $db->qstr( $last_start ) . ' AND
                       date_orig    <=  ' . $db->qstr( $last_end ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		$users_previous = $rs->RecordCount();
		$smarty->assign('users_previous', $users_previous);

		###############################
		# Get users change percentage
		###############################
		if($users_previous > 0)
		@$users_change = $users_current/$users_current *100 -100;
		else
		$users_change = 0;

		if($users_change == 0)
		$users_change = '-';
		elseif($users_change < 0)
		$users_change = '<font color="#990000">' . number_format($users_change, 1). '%</font>';
		else
		$users_change = '+'.number_format($users_change, 1). '%';

		$smarty->assign('users_change', $users_change);

		# Get Tickets
		if( $C_list->is_installed('ticket') )
		{
			$smarty->assign('show_tickets', true);

			#########################################
			# Get Tickets  (current)
			#########################################
			$sql    = 'SELECT id FROM ' . AGILE_DB_PREFIX . 'ticket WHERE
                           date_orig    >=  ' . $db->qstr( $this_start ) . ' AND
                           date_orig    <=  ' . $db->qstr( $this_end ) . ' AND
                           site_id      =  ' . $db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($sql);
			$tickets_current = $rs->RecordCount();
			$smarty->assign('tickets_current', $tickets_current);

			#########################################
			# Get Tickets  (previous)
			#########################################
			$sql    = 'SELECT id FROM ' . AGILE_DB_PREFIX . 'ticket WHERE
                           date_orig    >=  ' . $db->qstr( $last_start ) . ' AND
                           date_orig    <=  ' . $db->qstr( $last_end ) . ' AND
                           site_id      =  ' . $db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($sql);
			$tickets_previous = $rs->RecordCount();
			$smarty->assign('tickets_previous', $tickets_previous);

			###############################
			# Get Tickets change percentage
			###############################
			if($tickets_previous > 0)
			@$tickets_change = $tickets_current/$tickets_current *100 -100;
			else
			$tickets_change = 0;

			if($tickets_change == 0)
			$tickets_change = '-';
			elseif($tickets_change < 0)
			$tickets_change = '<font color="#990000">' . number_format($tickets_change, 1). '%</font>';
			else
			$tickets_change = '+'.number_format($tickets_change, 1). '%';

			$smarty->assign('tickets_change', $tickets_change);
		}

		# Get Affiliate stats
		if( $C_list->is_installed('affiliate') )
		{
			$smarty->assign('show_affiliates', true);

			###########################################
			# Get affiliate sales for this period
			###########################################
			$this_amt = 0;
			$sql    = 'SELECT total_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                           date_orig    >=  ' . $db->qstr( $this_start ) . ' AND
                           date_orig    <=  ' . $db->qstr( $this_end ) . ' AND
                           affiliate_id !=  ' . $db->qstr( 0 ) . ' AND
                           affiliate_id !=  ' . $db->qstr( '' ) . ' AND
                           site_id      =  ' . $db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($sql);
			while( !$rs->EOF ) {
				$this_amt += $rs->fields['total_amt'];
				$rs->MoveNext();
			}
			$smarty->assign('affiliate_sales_current', $this_amt);

			##########################################
			# Get affiliate sales for last period
			##########################################
			$last_amt = 0;
			$sql    = 'SELECT total_amt FROM ' . AGILE_DB_PREFIX . 'invoice WHERE
                           date_orig    >=  ' . $db->qstr( $last_start ) . ' AND
                           date_orig    <=  ' . $db->qstr( $last_end ) . ' AND
                           affiliate_id !=  ' . $db->qstr( 0 ) . ' AND
                           affiliate_id !=  ' . $db->qstr( '' ) . ' AND
                           site_id      =  ' . $db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($sql);
			while( !$rs->EOF ) {
				$last_amt += $rs->fields['total_amt'];
				$rs->MoveNext();
			}
			$smarty->assign('affiliate_sales_previous', $last_amt);

			###########################################
			# Get affiliate sales change percentage
			###########################################
			if($last_amt > 0)
			$sales_change = $this_amt/$last_amt *100 -100;
			else
			$sales_change = 0;

			if($sales_change == 0)
			$sales_change = '-';
			elseif($sales_change < 0)
			$sales_change = '<font color="#990000">' . number_format($sales_change, 1). '%</font>';
			else
			$sales_change = '+'.number_format($sales_change, 1). '%';

			$smarty->assign('affiliate_sales_change', $sales_change);
		}
		 
		
		/** Get VoIP Performance Data
		*/ 
		if( $C_list->is_installed('voip') )
		{		
			 
			# Avg. Call Duration for this period
			$rs = $db->Execute(sqlSelect($db, "voip_cdr", "avg(ceiling(billsec/60))", "disposition='ANSWERED' AND date_orig >= $this_start AND date_orig <= $this_end"));
			if(empty($rs->fields[0])) $acd=0; else $acd = $rs->fields[0]; 
			$smarty->assign('acd',$acd);		
			
			# Avg. Call Duration for last period
			$rs = $db->Execute(sqlSelect($db, "voip_cdr", "avg(ceiling(billsec/60))", "disposition='ANSWERED' AND date_orig >= $last_start AND date_orig <= $last_end"));
			if(empty($rs->fields[0])) $acd_last=0; else $acd_last = $rs->fields[0]; 
			$smarty->assign('acd_last',$acd_last);
			
			# Get Avg. Call Duration change Percentage
			if($acd > 0) $acd_change = $acd/$acd_last*100-100; else $acd_change = 0;
			if($acd_change == 0) 
				$acd_change = '-';
			elseif ($acd_change < 0)
				$acd_change = '<font color="#990000">' . number_format($acd_change, 1). '%</font>';
			else 
				$acd_change  = '+'.number_format($acd_change, 1). '%';
			$smarty->assign('acd_change', $acd_change);
			
			
			# Avg. Successful Rate for this period
			$rs = $db->Execute(sqlSelect($db, "voip_cdr", "count(*)", "disposition='ANSWERED' AND date_orig >= $this_start AND date_orig <= $this_end"));
			$rs1 = $db->Execute(sqlSelect($db, "voip_cdr", "count(*)", "date_orig >= $this_start AND date_orig <= $this_end"));
			if ($rs->fields[0])
				$asr = number_format(($rs->fields[0] / $rs1->fields[0]) * 100,3)." %";
			else
				$asr = "-";
			$smarty->assign('asr', $asr); 
			
			# Number of CDRs for this period
			$cdrs = $rs1->fields[0];
			$smarty->assign('cdrs', number_format($cdrs,0));		
			 
			# Avg. Successful Rate for last period
			$rs = $db->Execute(sqlSelect($db, "voip_cdr", "count(*)", "disposition='ANSWERED' AND date_orig >= $last_start AND date_orig <= $last_end"));
			$rs1 = $db->Execute(sqlSelect($db, "voip_cdr", "count(*)", "date_orig >= $last_start AND date_orig <= $last_end"));
			if ($rs->fields[0])
				$asr_last = number_format(($rs->fields[0] / $rs1->fields[0]) * 100,3)." %";
			else
				$asr_last = "-";
			$smarty->assign('asr_last', $asr_last); 
			
			# Number of CDRS for last period
			$cdrs_last = $rs1->fields[0];
			$smarty->assign('cdrs_last', number_format($cdrs_last,0));			
			 
			# Get Avg. Successful Rate change Percentage
			if($asr > 0) $asr_change = $asr/$asr_last*100-100; else $asr_change = 0;
			if($asr_change == 0) 
				$asr_change = '-';
			elseif ($asr_change < 0)
				$asr_change = '<font color="#990000">' . number_format($asr_change, 1). '%</font>';
			else 
				$asr_change  = '+'.number_format($asr_change, 1). '%';
			$smarty->assign('asr_change', $asr_change);
	
				
			# Get Number of CDRs change Percentage
			if($cdrs > 0) $cdrs_change = $cdrs/$cdrs_last*100-100; else $cdrs_change = 0;
			if($cdrs_change == 0) 
				$cdrs_change = '-';
			elseif ($cdrs_change < 0)
				$cdrs_change = '<font color="#990000">' . number_format($cdrs_change, 1). '%</font>';
			else 
				$cdrs_change  = '+'.number_format($cdrs_change, 1). '%';
			$smarty->assign('cdrs_change', $cdrs_change);
		}
					
		
		# Generate the Calendar Overview
		include_once(PATH_MODULES.'core/calendar.inc.php');
		$calendar = new calendar;		 
		$start = $calendar->start;
		$end   = $calendar->end; 
		
		global $C_list;
		$C_list->currency(DEFAULT_CURRENCY);
		$currency_symbol=$C_list->format_currency[DEFAULT_CURRENCY]['symbol'];
 		
		# Get the paid/due invoice statistics
		$rs = $db->Execute($sql=sqlSelect($db,"invoice","date_orig,total_amt,billing_status,refund_status,billed_amt,suspend_billing","date_orig >= $start && date_orig <= $end"));
		if($rs && $rs->RecordCount()) { 
			while(!$rs->EOF) {
				$day = date("j", $rs->fields['date_orig']);
				
				if($rs->fields['billed_amt'] > 0 && ($rs->fields['billing_status'] == 1 || $rs->fields['refund_status'] != 1)) {					
					$paid[$day] += $rs->fields['billed_amt'];
				}
				if ($rs->fields['billing_status'] != 1 && $rs->fields['refund_status'] != 1 ) {
					$amt = $rs->fields['total_amt'] - $rs->fields['billed_amt'];
					$due[$day] += $amt;
				}				
				
				$rs->MoveNext();
			} 
			
			if(is_array($paid))
				foreach($paid as $day=>$item)  
					$calendar->add("<b>Paid</b> - {$currency_symbol}". number_format($item,2), $day, 'green', 'green');
			
			if(is_array($due))
				foreach($due as $day=>$item) 
					$calendar->add("<b>Due</b> - {$currency_symbol}". number_format($item,2), $day,'red','red');			
		}
	 
		# Get the upcoming due services		
		$rs = $db->Execute($sql=sqlSelect($db,"service","date_next_invoice,price","price > 0 AND date_next_invoice >= $start AND date_next_invoice <= $end AND suspend_billing <> 1"));
		if($rs && $rs->RecordCount()) { 
			while(!$rs->EOF) {
				$day = date("j", $rs->fields['date_next_invoice']); 
				$sdue[$day] += $rs->fields['price']; 	
				$rs->MoveNext();
			} 
			foreach($sdue as $day=>$item) {
				$calendar->add("<b>Recurring</b> - {$currency_symbol}". number_format($item,2), $day, 'grey', 'grey');
			}			
		}  
		
		$calout=  $calendar->generate();
		$smarty->assign("calendar", $calout);
				
		return;
	}

	/** AUTO APPROVE INVOICE
	*/
	function autoApproveInvoice($invoice_id)
	{
		$do = false;

		# Get the invoice details:
		$db = &DB();
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."invoice WHERE
	        		id = ".$db->qstr($invoice_id)." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$invoice = $db->Execute($q);
		if ($invoice === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','autoApproveInvoice', $db->ErrorMsg());
			return false;
		}

		# Get the checkout details:
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."checkout WHERE
	        		id = ".$db->qstr($invoice->fields['checkout_plugin_id'])." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$checkout = $db->Execute($q);
		if ($checkout === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','autoApproveInvoice', $db->ErrorMsg());
			return false;
		}

		# Get the account details:
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."account WHERE
	        		id = ".$db->qstr($invoice->fields['account_id'])." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$account = $db->Execute($q);
		if ($account === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','autoApproveInvoice', $db->ErrorMsg());
			return false;
		}

		# is this a recurring invoices, and is manual approvale req?
		if ( $invoice->fields['type'] == 1 && $checkout->fields['manual_approval_recur'] != 1) {
			$do = true;
		}

		# Manual approval required for all?
		if( $invoice->fields['type'] != 1 && $checkout->fields['manual_approval_all'] != 1) {
			$do = true;
		}

		if(!$do) {

			# manual approval req. for invoice amount?
			if(!empty($checkout->fields['manual_approval_amount']) && $do == true)
			if($checkout->fields['manual_approval_amount'] <= $invoice->fields['total_amt'])
			$do = false;

			# manual approval req. for user's country?
			if(!empty($checkout->fields['manual_approval_country']) && $do == true) {
				$arr = unserialize($checkout->fields['manual_approval_country']);
				for($i=0; $i<count($arr); $i++) {
					if($account->fields['country_id'] == $arr[$i])
					$do = false;
				}
			}

			# manual approval req. for user's currency?
			if(!empty($checkout->fields['manual_approval_currency']) && $do == true) {
				$arr = unserialize($checkout->fields['manual_approval_currency']);
				for($i=0; $i<count($arr); $i++) {
					if($invoice->fields['actual_billed_currency_id'] == $arr[$i])
					$do = false;
				}
			}

			# manual approval req. for user's group(s)?
			if(!empty($checkout->fields['manual_approval_group']) && $do == true) {
				# Get the group details:
				$q = "SELECT group_id FROM ".AGILE_DB_PREFIX."account_group WHERE
    		        		account_id = ".$db->qstr($invoice->fields['account_id'])." AND
    		        		active	   = ".$db->qstr('1')." AND
    		        		site_id = ".$db->qstr(DEFAULT_SITE);
				$groups = $db->Execute($q);
				if ($groups === false) {
					global $C_debug;
					$C_debug->error('invoice.inc.php','autoApproveInvoice', $db->ErrorMsg());
				}

				$arr = unserialize($checkout->fields['manual_approval_group']);
				while(!$groups->EOF) {
					for($i=0; $i<count($arr); $i++) {
						$idx = $groups->fields["group_id"];
						if($idx == $arr[$i])
						$do = false;
					}
					$groups->MoveNext();
				}
			}
		}

		if ($do)
		{
			# Approve the invoice
			$arr['id'] = $invoice_id;
			$this->approveInvoice($arr, $this);
		}
		else
		{
			# Admin manual approval notice
			include_once(PATH_MODULES.'email_template/email_template.inc.php');
			$mail = new email_template;
			$mail->send('invoice_manual_auth_admin',  $invoice->fields['account_id'], $invoice->fields['id'], $invoice->fields['checkout_plugin_id'], '');
		}
	}


	/*	APPROVE INVOICE
	*/
	function approveInvoice($VAR)
	{
		# Get the invoice details:
		$db = &DB();
		$q = "SELECT * FROM ".AGILE_DB_PREFIX."invoice WHERE
	        		id = ".$db->qstr($VAR['id'])." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$invoice = $db->Execute($q);
		if ($invoice === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
			return false;
		}

		# Validate invoice exists & needs approval:
		if($invoice->fields['id'] != $VAR['id'] || $invoice->fields['process_status'] == '1') return false;

		# Update the invoice approval status:
		$q = "UPDATE ".AGILE_DB_PREFIX."invoice SET
	        		date_last 		= ".$db->qstr(time()).",
	        		process_status 	= ".$db->qstr('1')." WHERE
	        		id		 		= ".$db->qstr($VAR['id'])." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$result = $db->Execute($q);
		if ($result === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
			return false;
		}

		# Send approval notice to user:
		include_once(PATH_MODULES.'email_template/email_template.inc.php');
		$mail = new email_template;
		$mail->send('invoice_approved_user',  $invoice->fields['account_id'], $VAR['id'], '', '');

		# Include the service class
		include_once(PATH_MODULES.'service/service.inc.php');
		$srvc = new service;

		# Determine if services have already been created for this invoice:
		if($invoice->fields['type'] != 1 )
		{
			$q = "SELECT id FROM ".AGILE_DB_PREFIX."service WHERE
    	        		invoice_id = ".$db->qstr($VAR['id'])." AND
    	        		site_id = ".$db->qstr(DEFAULT_SITE);
			$service = $db->Execute($q);
			if ($service === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
				return false;
			}
			if ($service->RecordCount() > 0)
			{
				# Update services to approved status:
				while(!$service->EOF)
				{
					$srvc->approveService($service->fields['id']);
					$service->MoveNext();
				}
				return true;
			}

			# Get the parent items in this invoice :
			$q = "SELECT * FROM ".AGILE_DB_PREFIX."invoice_item WHERE
    	        	  ( parent_id = 0 OR parent_id IS NULL OR parent_id = '') AND
    	        	  invoice_id =  ".$db->qstr($VAR['id'])." AND
    	        	  site_id 	 =  ".$db->qstr(DEFAULT_SITE);
			$ii = $db->Execute($q);
			if ($ii === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
				return false;
			}
			while(!$ii->EOF)
			{
				if(empty($ii->fields['service_id']))
				{
					# Add the service
					$srvc->invoiceItemToService($ii->fields['id'], $invoice);

					# Check for any children items in this invoice:
					$q = "SELECT * FROM ".AGILE_DB_PREFIX."invoice_item WHERE
	    		        	  parent_id  =  ".$db->qstr($ii->fields['id'])." AND
	    		        	  invoice_id =  ".$db->qstr($VAR['id'])." AND
	    		        	  site_id 	 =  ".$db->qstr(DEFAULT_SITE);
					$iii = $db->Execute($q);
					if ($iii === false) {
						global $C_debug;
						$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
						return false;
					}
					while(!$iii->EOF)
					{
						# Add the service
						$srvc->invoiceItemToService($ii->fields['id'], $invoice);
						$iii->MoveNext();
					}
				}
				else
				{
					$srvc = new service;
					if($ii->fields['item_type'] == 2 && $ii->fields['domain_type'] == 'renew' ) {
						# this is a domain renewal
						$srvc->renewDomain(  $ii, $invoice->fields['account_billing_id'] );
					} else {
						# this is an upgrade for an existing service
						$srvc->modifyService( $ii, $invoice->fields['account_billing_id'] );
					}
				}
				$ii->MoveNext();
			}
		}
		elseif ($invoice->fields['type'] == 1 )
		{
			# recurring invoice, just update assoc services
			# Loop through invoice items & approve assoc services
			$q = "SELECT service_id FROM ".AGILE_DB_PREFIX."invoice_item WHERE
                        invoice_id = ".$db->qstr($VAR['id'])." AND
                        site_id = ".$db->qstr(DEFAULT_SITE);
			$service = $db->Execute($q);
			if ($service === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','voidInvoice', $db->ErrorMsg());
				return false;
			}
			if ($service->RecordCount() > 0)
			{
				# Include the service class
				include_once(PATH_MODULES.'service/service.inc.php');
				$srvc = new service;

				# Update services to inactive status:
				while(!$service->EOF) {
					$srvc->approveService($service->fields['service_id']);
					$service->MoveNext();
				}
			}
		}

		# get account id
		if(defined("SESS_ACCOUNT"))
		$account_id = SESS_ACCOUNT;
		else
		$account_id = 0;

		# if approved, create a memo
		$id = $db->GenID(AGILE_DB_PREFIX . 'invoice_memo_id');
		$q = "INSERT INTO ".AGILE_DB_PREFIX."invoice_memo
		    		SET
		    		id 		= ".$db->qstr($id).",
		    		site_id 	= ".$db->qstr(DEFAULT_SITE).",
		    		date_orig 	= ".$db->qstr(time()).",
		    		invoice_id	= ".$db->qstr($VAR['id']).",
		    		account_id	= ".$db->qstr($account_id).",
		    		type		= ".$db->qstr('approval').",
		    		memo		= ".$db->qstr('NA');
		$memo = $db->Execute($q);
		if ($memo === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','approveInvoice', $db->ErrorMsg());
			return false;
		}
		return true;
	}


	/** VOID INVOICE
	*/
	function voidInvoice($VAR)
	{
		# Update the invoice approval status:
		$db = &DB();
		$q = "UPDATE ".AGILE_DB_PREFIX."invoice SET
	        		date_last 		= ".$db->qstr(time()).",
	        		process_status 	= ".$db->qstr('0')." WHERE
	        		id		 		= ".$db->qstr($VAR['id'])." AND
	        		site_id 		= ".$db->qstr(DEFAULT_SITE);
		$update = $db->Execute($q);
		if ($update === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','voidInvoice', $db->ErrorMsg());
			return false;
		}

		# Determine if services have already been created for this invoice and deactivate:
		$q = "SELECT id FROM ".AGILE_DB_PREFIX."service WHERE
	        		invoice_id = ".$db->qstr($VAR['id'])." AND
	        		site_id = ".$db->qstr(DEFAULT_SITE);
		$service = $db->Execute($q);
		if ($service === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','voidInvoice', $db->ErrorMsg());
			return false;
		}
		if ($service->RecordCount() > 0)
		{
			# Include the service class
			include_once(PATH_MODULES.'service/service.inc.php');
			$srvc = new service;

			# Update services to inactive status:
			while(!$service->EOF) {
				$srvc->voidService($service->fields['id']);
				$service->MoveNext();
			}
		}

		# Loop through invoice items & delete assoc services
		$q = "SELECT service_id FROM ".AGILE_DB_PREFIX."invoice_item WHERE
                    invoice_id = ".$db->qstr($VAR['id'])." AND
                    site_id = ".$db->qstr(DEFAULT_SITE);
		$service = $db->Execute($q);
		if ($service === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','voidInvoice', $db->ErrorMsg());
			return false;
		}
		if ($service->RecordCount() > 0)
		{
			# Include the service class
			include_once(PATH_MODULES.'service/service.inc.php');
			$srvc = new service;

			# Update services to inactive status:
			while(!$service->EOF) {
				$srvc->voidService($service->fields['service_id']);
				$service->MoveNext();
			}
		} 

		# if voided, create a memo
		$id = $db->GenID(AGILE_DB_PREFIX . 'invoice_memo_id');
		$q = "INSERT INTO ".AGILE_DB_PREFIX."invoice_memo
		    		SET
		    		id 			= ".$db->qstr($id).",
		    		site_id 	= ".$db->qstr(DEFAULT_SITE).",
		    		date_orig 	= ".$db->qstr(time()).",
		    		invoice_id	= ".$db->qstr($VAR['id']).",
		    		account_id	= ".$db->qstr(SESS_ACCOUNT).",
		    		type		= ".$db->qstr('void').",
		    		memo		= ".$db->qstr('NA');
		$insert = $db->Execute($q);
		if ($insert === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','voidInvoice', $db->ErrorMsg());
			return false;
		}
		return true;
	}


	/** RECONCILE INVOICE
	*/
	function reconcile($VAR)
	{
		global $C_translate, $C_debug;

		# validate amt
		if( $VAR['amount'] <= 0) {
			$C_debug->alert( "Payment amount to low!" );
			return false;
		}

		# get the invoice details
		$db     = &DB();
		$sql    = 'SELECT * FROM   ' . AGILE_DB_PREFIX . 'invoice WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		if ($rs === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','reconcileInvoice', $db->ErrorMsg());
			return false;
		}

		if(@$rs->RecordCount() == 0) return false;

		$amt                        = $VAR['amount'];
		$total_amt                  = $rs->fields['total_amt'];
		$billed_amt                 = $rs->fields['billed_amt'];
		$billed_currency_id         = $rs->fields['billed_currency_id'];
		$actual_billed_amt          = $rs->fields['actual_billed_amt'];
		$actual_billed_currency_id  = $rs->fields['actual_billed_currency_id'];
		$due                        = $total_amt - $billed_amt;

		$overpaid = false;
		if($amt > $due)
		{
			$billed = 1;
			$update   = $total_amt;

			$overpaid = $amt - $due;

			$C_translate->value['invoice']['amt'] = number_format($overpaid, 2);
			$alert = $C_translate->translate('rec_over','invoice','');
		}
		elseif ($amt == $due)
		{
			$billed = 1;
			$update = $total_amt;
		}
		else
		{
			$billed = 0;
			$update = $amt + $billed_amt;
		}

		# Update the invoice record
		$sql    = 'UPDATE  ' . AGILE_DB_PREFIX . 'invoice
                       SET
                       billed_amt   =  ' . $db->qstr( $update ) . ',
                       billing_status =  ' . $db->qstr( $billed ) . '
                       WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$db->Execute($sql);
	  
		# Create a memo
		$id = $db->GenID(AGILE_DB_PREFIX . 'invoice_memo_id');
		$q = "INSERT INTO ".AGILE_DB_PREFIX."invoice_memo
                    SET
                    id          = ".$db->qstr($id).",
                    site_id     = ".$db->qstr(DEFAULT_SITE).",
                    date_orig   = ".$db->qstr(time()).",
                    invoice_id  = ".$db->qstr($VAR['id']).",
                    account_id  = ".$db->qstr(SESS_ACCOUNT).",
                    type        = ".$db->qstr('reconcile').",
                    memo        = ".$db->qstr('+ '.number_format($VAR['amount'],2) . " \r\n" . @$VAR['memo']);
		$db->Execute($q);
	 
		
		# Reciept printing
		include_once PATH_MODULES.'invoice/receipt_print.php';
		$receipt = new receipt_print;
		$receipt->add($rs, number_format($VAR['amount'],2), number_format($update,2));
		

		# Auto update if billed complete
		if($billed)
		{
			$this->autoApproveInvoice($VAR['id']);

			# Get the default currency ISO
			$q = "SELECT ".AGILE_DB_PREFIX."invoice_memo
	                    SET
	                    id          = ".$db->qstr($id).",
	                    site_id     = ".$db->qstr(DEFAULT_SITE).",
	                    date_orig   = ".$db->qstr(time()).",
	                    invoice_id  = ".$db->qstr($VAR['id']).",
	                    account_id  = ".$db->qstr(SESS_ACCOUNT).",
	                    type        = ".$db->qstr('reconcile').",
	                    memo        = ".$db->qstr('+ '.number_format($VAR['amount'],2) . " \r\n" . @$VAR['memo']);
			$currency = $db->Execute($q);

			# User invoice creation confirmation
			include_once(PATH_MODULES.'email_template/email_template.inc.php');
			$email = new email_template;
			$email->send('invoice_paid_user', $rs->fields['account_id'], $VAR['id'], $rs->fields['billed_currency_id'], '');

			# Admin alert of payment processed
			$email = new email_template;
			$email->send('admin->invoice_paid_admin', $rs->fields['account_id'], $VAR['id'], $rs->fields['billed_currency_id'], '');
		}

		# Redirect
		if(!empty($VAR['redirect']))
		{
			echo '
                <script language="JavaScript">
                    window.parent.location=\''.$VAR['redirect'].'\';  ';
			if(!empty($alert))
			echo 'alert(\''.$alert.'\');';
			echo '</script>';
			exit;
		}

		$msg = $C_translate->translate('ref_comp','invoice','');
		$C_debug->alert( $msg );
		return;
	}


	/** REFUND INVOICE 
	*/
	function refund($VAR)
	{
		global $C_translate, $C_debug;

		# validate amt
		if( $VAR['amount'] <= 0) {
			$C_debug->alert( "Refund amount to low!" );
			return false;
		}

		# get the invoice details
		$db     = &DB();
		$sql    = 'SELECT * FROM   ' . AGILE_DB_PREFIX . 'invoice WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($sql);
		if(@$rs->RecordCount() == 0) return false;

		$amt                        = $VAR['amount'];
		$total_amt                  = $rs->fields['total_amt'];
		$billed_amt                 = $rs->fields['billed_amt'];
		$billed_currency_id         = $rs->fields['billed_currency_id'];
		$actual_billed_amt          = $rs->fields['actual_billed_amt'];
		$actual_billed_currency_id  = $rs->fields['actual_billed_currency_id'];
		$update                     = $billed_amt - $amt;

		if($update>0) $billing_status=1; else $billing_status=0;
		
		# Update the invoice record
		echo $sql    = 'UPDATE  ' . AGILE_DB_PREFIX . 'invoice
                       SET
                       billed_amt   =  ' . $db->qstr( $update ) . ',
                       billing_status = '.$billing_status.', 
                       suspend_billing = 1,
                       refund_status = 1                   
                       WHERE
                       id           =  ' . $db->qstr( $VAR['id'] ) . ' AND
                       site_id      =  ' . $db->qstr(DEFAULT_SITE);
		$update2 = $db->Execute($sql);
		if ($update2 === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','refundInvoice', $db->ErrorMsg());
			return false;
		}

		# Create a memo
		$id = $db->GenID(AGILE_DB_PREFIX . 'invoice_memo_id');
		$q = "INSERT INTO ".AGILE_DB_PREFIX."invoice_memo
                    SET
                    id          = ".$db->qstr($id).",
                    site_id     = ".$db->qstr(DEFAULT_SITE).",
                    date_orig   = ".$db->qstr(time()).",
                    invoice_id  = ".$db->qstr($VAR['id']).",
                    account_id  = ".$db->qstr(SESS_ACCOUNT).",
                    type        = ".$db->qstr('refund').",
                    memo        = ".$db->qstr('- '.number_format($VAR['amount'],2) . " \r\n" . @$VAR['memo']);
		$insert = $db->Execute($q);
		if ($insert === false) {
			global $C_debug;
			$C_debug->error('invoice.inc.php','refundInvoice', $db->ErrorMsg());
			return false;
		}

		# Void:
		$this->voidInvoice($VAR, $this);
		
		# Call into the checkout plugin and attempt realtime refund 
		$billing = $db->Execute($sql=sqlSelect($db, array('account_billing','checkout'), 'A.*,B.checkout_plugin',
					"A.id = ::{$rs->fields['account_billing_id']}:: AND A.checkout_plugin_id=B.id"));
		if($billing && $billing->RecordCount() && !empty($billing->fields['checkout_plugin'])) {
			$plugin_file = PATH_PLUGINS.'checkout/'. $billing->fields['checkout_plugin'] .'.php';
			if(is_file($plugin_file)) {
				include_once ( $plugin_file );
				eval( '$PLG = new plg_chout_' . $billing->fields['checkout_plugin'] . '("'.$billing->fields['checkout_plugin_id'].'");');
				if(is_callable(Array($PLG,"refund"))) $PLG->refund($rs->fields, $billing->fields, $amt);
			}
		}

		# Redirect
		if(!empty($VAR['redirect'])) {
			echo ' <script language="JavaScript">  window.parent.location=\''.$VAR['redirect'].'\'; </script>';
			return;
		}
		 
		$msg = $C_translate->translate('ref_comp','invoice','');
		$C_debug->alert( $msg );
		return;
	}

	# Get translated/hardcoded line item description for PDF invoice
	function getLineItemDesc($sku,$id,$domain=false,$item_name) 
	{
		if(!empty($item_name)) return $item_name;
		global $C_translate;
		if(!empty($sku) && $sku == 'DOMAIN-PARK' || $sku == 'DOMAIN-TRANSFER' || $sku == 'DOMAIN-REGISTER' || $sku == 'DOMAIN-RENEW') {			 
			if($sku == 'DOMAIN-REGISTER') $name=$C_translate->translate('register','cart',''); 
			elseif ($sku == 'DOMAIN-TRANSFER') $name=$C_translate->translate('transfer','cart',''); 
			elseif ($sku == 'DOMAIN-PARK') $name=$C_translate->translate('park','cart','');
			elseif ($sku == 'DOMAIN-RENEW') $name=$C_translate->translate('renew','cart','');
			if($domain) return "$domain \r\n ( $name )";
			else return $name;			
		} else { 
			include_once(PATH_CORE.'list.inc.php');
			$C_list=new CORE_list;
			if(empty($this->product_desc["$id"])) {
				$desc = $C_list->translate("product_translate", "name", "product_id", $id, "translate_product");
				$this->product_desc["$id"] = $desc['name'];
			}
			if(!empty($this->product_desc["$id"]))
			return $this->product_desc["$id"];
			else 
			return $sku;
		} 
		return $sku;
	}

	/**
	* Task based function to e-mail or store printable PDF of all unprinted invoices
	*/
	function delivery_task() 
	{		
		# get all unprinted invoices
		$db=&DB();
		$invcfg = $db->Execute(sqlSelect($db,"setup_invoice","*","id=::".DEFAULT_SITE."::"));				
		$rs = $db->SelectLimit($sql=sqlSelect($db,Array("invoice","account"),
			"A.id,B.email,B.first_name,B.last_name,B.invoice_delivery,B.invoice_show_itemized",
			"(A.billing_status=0 OR A.billing_status IS NULL) AND (A.print_status=0 OR A.print_status=NULL) and A.account_id=B.id and (B.invoice_delivery is not null AND B.invoice_delivery>0)"),100);		 
		if($rs && $rs->RecordCount()) 
		{ 
			//define('FPDF_FONTPATH', PATH_INCLUDES.'pdf/font/');
			require_once(PATH_INCLUDES.'pdf/fpdi.php');
			require_once(PATH_INCLUDES.'pdf/fpdf_tpl.php');
			require_once(PATH_INCLUDES.'pdf/fpdf.php');
			require_once(PATH_INCLUDES.'pdf/pdf_invoice_'.$invcfg->fields['invoice_pdf_plugin'].'.inc.php');
			
			// Send the e-mail....
			require_once(PATH_INCLUDES."phpmailer/class.phpmailer.php");

			$mail = new PHPMailer();
			$mail->From     = SITE_EMAIL;
			$mail->FromName = SITE_NAME;

			/*
			$mail->SMTPAuth = true;
			$mail->Host     = "smtp.domain.com";
			$mail->Username = "user";
			$mail->Password = "pass";
			$mail->Mailer   = "smtp";
			$mail->Debug	= true;
			*/
				 
			while(!$rs->EOF) 
			{ 
				$pdf = new pdf_invoice_overview();  
		 	 	$pdf->companyName = SITE_NAME;
		 	 	$pdf->companyAddress = SITE_ADDRESS;
		 	 	$pdf->companyCity = SITE_CITY;
		 	 	$pdf->companyState = SITE_STATE;
		 	 	$pdf->companyZip = SITE_ZIP;
 	 	
		 	 	# load the setup_invoice fields into the pdf class
		 	 	$pdf->load_setup($invcfg);
 	 			 	 					
				$pagecount = $pdf->setSourceFile($pdf->getTemplate());
				$tplidx = $pdf->ImportPage(1);					
 
				$pdf->addPage();
				$pdf->useTemplate($tplidx);
				
				# override the show itemized, based on the customers choice
				if($rs->fields['invoice_show_itemized'] == 0 || $rs->fields['invoice_show_itemized'] == 1)
					$pdf->show_itemized = $rs->fields['invoice_show_itemized'];
				 
				$this->pdfInvoiceSummary($rs->fields['id'], $pdf); 

				$file = tempnam(PATH_FILES, "pdf_inv_".$rs->fields['id']).".pdf"; 
				$pdf->Output($file,'F'); 
				$pdf->closeParsers();	
				
				if($rs->fields['invoice_delivery'] == 1) { 
					$mail->AddAddress($rs->fields['email'], $rs->fields['first_name']. ' ' . $rs->fields['last_name']);
					$mail->Subject 	  = "Printable Invoice No. ". $rs->fields['id'];
					$mail->Body    	  = "Please find the printable version of invoice number {$rs->fields['id']} attached.\r\n\r\nThank you,\r\n".SITE_NAME;
					$mail->AddAttachment($file, "INVOICE_{$rs->fields['id']}.pdf");   
 			
				    if($mail->Send()) {
				    	$fields=Array('print_status'=>1);
				    	$db->Execute(sqlUpdate($db,"invoice",$fields,"id=".$rs->fields['id']));
				    } else {
				    	echo "Unable to email invoice # {$rs->fields['id']} to {$rs->fields['email']}<BR>";				    
				    }
				    
					$mail->ClearAddresses();
					$mail->ClearAttachments();
				} else if($rs->fields['invoice_delivery'] == 2) {
					if(copy($file,AGILE_PDF_INVOICE_PATH."invoice_".$rs->fields['id'].".pdf")) {
						$fields=Array('print_status'=>1);
				    	$db->Execute(sqlUpdate($db,"invoice",$fields,"id=".$rs->fields['id']));
					}
				}
								
				// delete tmp file and clean up vars used
				unlink($file);				
				unset($pdf->itemsSummary); 
				unset($pdf); unset($tplidx); unset($pagecount);
									
				$rs->MoveNext();	
			}	 			
		}		
	}
	
	
	/** Display a PDF invoice
	*/
	function pdf($VAR)
	{ 
		# Check invoice
		if(empty($VAR['id'])) {
			echo 'No Invoice Specified.';
			return false;
		}

		# Check admin authentication:
		global $C_auth;
		if ($C_auth->auth_method_by_name('invoice','view') == false) {
			# Validate on account level
			$db = &DB();
			$rs = $db->Execute(sqlSelect($db,"invoice","account_id", "id = ::{$VAR['id']}::")); 
			if ($rs->fields['account_id'] != SESS_ACCOUNT) {
				// todo: redirect to login page if not logged
				return false;
			}
		}

		$db =& DB();
		$invcfg = $db->Execute(sqlSelect($db,"setup_invoice","*","id=::".DEFAULT_SITE."::"));
		
		if (!defined('FPDF_FONTPATH'))
			define('FPDF_FONTPATH', PATH_INCLUDES.'pdf/font/');
		require_once(PATH_INCLUDES.'pdf/fpdi.php');
		require_once(PATH_INCLUDES.'pdf/fpdf_tpl.php');
		require_once(PATH_INCLUDES.'pdf/fpdf.php');
		require_once(PATH_INCLUDES.'pdf/pdf_invoice_'.$invcfg->fields['invoice_pdf_plugin'].'.inc.php');
		
		ob_start();
		
		$pdf = new pdf_invoice_overview();  
 	 	$pdf->companyName = SITE_NAME;
 	 	$pdf->companyAddress = SITE_ADDRESS;
 	 	$pdf->companyCity = SITE_CITY;
 	 	$pdf->companyState = SITE_STATE;
 	 	$pdf->companyZip = SITE_ZIP;
 	 	
 	 	# load the setup_invoice
 	 	$pdf->load_setup($invcfg);
 	 			
		$pagecount = $pdf->setSourceFile($pdf->getTemplate());
		$tplidx = $pdf->ImportPage(1);
		$pdf->addPage();
		$pdf->useTemplate($tplidx); 
		$this->pdfInvoiceSummary($VAR['id'], $pdf); 
		$pdf->Output('invoice.pdf','D');
		$pdf->closeParsers();
		
		ob_end_flush(); 
	}

	/** Export multiple invoices */
	function pdfExport(&$rs) 
	{				
		$db =& DB();
		$invcfg = $db->Execute(sqlSelect($db,"setup_invoice","*",""));
				
		define('FPDF_FONTPATH', PATH_INCLUDES.'pdf/font/');
		require_once(PATH_INCLUDES.'pdf/fpdi.php');
		require_once(PATH_INCLUDES.'pdf/fpdf_tpl.php');
		require_once(PATH_INCLUDES.'pdf/fpdf.php');
		require_once(PATH_INCLUDES.'pdf/pdf_invoice_'.$invcfg->fields['invoice_pdf_plugin'].'.inc.php');
		
		ob_start(); 
		$pdf = new pdf_invoice_overview(); 
		$pdf->companyName = SITE_NAME;
		$pdf->companyAddress = SITE_ADDRESS;
		$pdf->companyCity = SITE_CITY;
		$pdf->companyState = SITE_STATE;
		$pdf->companyZip = SITE_ZIP; 	
 	 	$pdf->load_setup($invcfg);			
		$pagecount = $pdf->setSourceFile($pdf->getTemplate());
		$tplidx = $pdf->ImportPage(1);					
		while(!$rs->EOF) { 
			$pdf->addPage();
			$pdf->useTemplate($tplidx); 
			$this->pdfInvoiceSummary($rs->fields['id'], $pdf); 
 			$rs->MoveNext();
 			unset($pdf->itemsSummary);
		} 
		$pdf->Output();
		$pdf->closeParsers();	  
		ob_end_flush();  
	}
	
	

	function pdfInvoiceSummary($id, &$pdf)
	{  
		# Invoice details:
		$db = &DB(); 
		$invoice = $db->Execute( $sql = sqlSelect($db, array("invoice", "currency"), "A.*, B.symbol", "A.id = ::$id:: AND B.id = A.billed_currency_id"));		
		$pdf->setInvoiceFields($invoice->fields);
		$pdf->setDueAmt($invoice->fields['total_amt'] - $invoice->fields['billed_amt']);	
		$pdf->setCurrency($invoice->fields['symbol']);
		$pdf->setDateRange( mktime(0,0,0,date('m',$invoice->fields['due_date'])-1, date('d',$invoice->fields['due_date']), date('Y',$invoice->fields['due_date'])), $invoice->fields['due_date']);		
		$pdf->drawInvoiceNo();
		$pdf->drawInvoiceDueDate();
		$pdf->drawInvoiceTotalAmt();
		$pdf->drawInvoiceDueAmt();
		$pdf->drawInvoicePaidAmt();
		$pdf->drawInvoiceDiscountAmt();
		$pdf->drawInvoiceTaxAmt();
		$pdf->drawInvoiceShippingAmt();
		$pdf->drawInvoiceRange();		 
		if($invoice->fields['billing_status'] !=1 && $invoice->fields['suspend_billing'] != 1 && $invoice->fields['due_date'] <= time()) 
		$pdf->drawInvoiceDueNotice();
		elseif($invoice->fields['billing_status'] == 1)
		$pdf->drawInvoicePaidNotice();		
			 
		# Account details:
		$account = $db->Execute("SELECT * FROM ".AGILE_DB_PREFIX."account WHERE id = ".$db->qstr($invoice->fields['account_id'])." AND site_id = ".$db->qstr(DEFAULT_SITE));
 	 	$pdf->setAccountFields($account->fields);
		$pdf->drawAccountId();
 	 	$pdf->drawAccountUsername();
 	 	$pdf->drawAccountName();
 	 	$pdf->drawAccountMailing();
 	 	 
		# Company details:
		$pdf->drawCompanyAddress(); 
		$pdf->drawCompanyLogo(); 
		
		## Get the summary items
		$items = & $db->Execute("select sku, item_type, product_name, product_id, sum(quantity) as quantity, (sum(total_amt)) as amount, price_setup, price_base from ".AGILE_DB_PREFIX."invoice_item where invoice_id=".$db->qstr($id)." group by sku, item_type");		
		$i = 0;
		if($items && $items->RecordCount()) {
			while (!$items->EOF) {
				$items_arr[$i] = $items->fields;
				$desc = $this->getLineItemDesc($items->fields['sku'], $items->fields['product_id'], false, $items->fields['product_name']);
				$items_arr[$i]['name'] = $desc;		
				$i++;
				if ($items->fields['price_setup']) {
					$items_arr[$i]['name'] = $desc." Set-up Charge";
					$items_arr[$i]['amount'] = $items->fields['price_setup'];
					$i++;
				}
				$items->MoveNext();
			}
		}
		if ($invoice->fields['discount_amt']) {
			$items_arr[$i]['name'] = 'Discount';
			$items_arr[$i]['amount'] = -($invoice->fields['discount_amt']);
			$i++;
		}
		if ($invoice->fields['tax_amt']) { 
			$trs = $db->Execute($sql=sqlSelect($db, Array('invoice_item_tax','tax'),"A.amount,B.description","A.tax_id=B.id AND A.invoice_id=::$id::")); 
            if($trs && $trs->RecordCount()) { 
            	unset($taxes);
            	while(!$trs->EOF) { 
                	$taxes["{$trs->fields['description']}"] += $trs->fields["amount"]; 
                    $trs->MoveNext(); 
                } 
                foreach($taxes as $txds=>$txamt) {
                    $items_arr[$i]['name'] = $txds;
					$items_arr[$i]['amount'] = $txamt;
					$i++;
                }
            } 
		} 		
		$pdf->drawSummaryLineItems($items_arr);
		unset($items_arr);
		unset($pdf->itemsSummary);
		
		## BEGIN loop for enumerating information in multiple ways on the invoice
		$iteration = 0;
		while($pdf->drawLineItems_pre($iteration)) {		
			## Get the line items: 
			$items = & $db->Execute( sqlSelect($db, "invoice_item", "*", "invoice_id = ::$id::") );
			if ($items && $items->RecordCount()) { 
				while ( !$items->EOF ) {
					#$items_arr[] = $items->fields;
					// get the date range if set
					if(!empty($items->fields['date_start']) && !empty($items->fields['date_stop'])) {
						global $C_translate;
						$C_translate->value('invoice','start', date(UNIX_DATE_FORMAT,$items->fields['date_start']));
						$C_translate->value('invoice','stop', date(UNIX_DATE_FORMAT,$items->fields['date_stop']));
						#$smart_items[$ii]['range'] = $C_translate->translate('recur_date_range','invoice','');
					}
					
					$cost = $items->fields['price_base'];
					$total = $cost * $items->fields['quantity'];
					$desc = $this->getLineItemDesc($items->fields['sku'],$items->fields['product_id'], strtolower($items->fields['domain_name'].'.'.$items->fields['domain_tld']), $items->fields['product_name']); 
					$line = array(
						"name" => $desc, 
						'amount' => $cost, 
						'sku'=>$items->fields['sku'], 
						'qty'=>$items->fields['quantity'], 
						'cost'=>$cost, 
						'attr'=>$items->fields['product_attr'], 
						'price_type'=>$items->fields['price_type'], 
						'price_base'=>$items->fields['price_base'], 
						'item_type'=>$items->fields['item_type'],
						'total_amt'=>$items->fields['total_amt']
					); 
					$pdf->drawLineItems($db, $line);
					if ($items->fields['price_setup']) {
						$desc .= " Set-up Charge";
						$total = $items->fields['price_setup'];
						$line = array("name" => $desc, 'amount' => $total, 'qty'=>'1', 'sku'=>$items->fields['sku'], 'cost'=>$total, 'price_base'=>$total, 'price_type'=>999); 
						$pdf->drawLineItems($db, $line);
					}
					$items->MoveNext();
				}
			}		 
			if ($invoice->fields['discount_amt']) {
				$desc = 'Discount';
				$total = -($invoice->fields['discount_amt']);
				$line = array("name" => $desc, 'amount' => $total, 'qty'=>'1', 'cost'=>$total, 'price_base'=>$total, 'price_type'=>999); 
				$pdf->drawLineItems($db, $line);
			}
			if ($invoice->fields['tax_amt']) { 
				$trs = $db->Execute($sql=sqlSelect($db, Array('invoice_item_tax','tax'),"A.amount,B.description","A.tax_id=B.id AND A.invoice_id=::$id::")); 
	            if($trs && $trs->RecordCount()) { 
	            	unset($taxes);
	            	while(!$trs->EOF) { 
	                	$taxes["{$trs->fields['description']}"] += $trs->fields["amount"]; 
	                    $trs->MoveNext(); 
	                } 
	                foreach($taxes as $txds=>$txamt) {
						$line = array("name" => $txds, 'amount' => $txamt, 'qty'=>'1', 'cost'=>$txamt, 'price_base'=>$txamt, 'price_type'=>999);
						$pdf->drawLineItems($db, $line);
	                }
	            } 
			}
			# Increment the iteration
			++$iteration;
		}
		# Custom functions:
		$pdf->drawCustom();	 
		unset($db); 	 
	}


	/** RESEND DUE NOTICE 
	*/
	function resend($VAR)
	{
		global $C_debug;
	
		# User invoice creation confirmation
		include_once(PATH_MODULES.'email_template/email_template.inc.php');
		$mail = new email_template;
		$mail->send('invoice_resend', $VAR['account_id'],  $VAR['id'], '', '');
	
		# Alert
		$C_debug->alert('Sent payment due notice to user');
	
		# Update invoice
		$db = &DB();
		$q  = "SELECT notice_count FROM ".AGILE_DB_PREFIX."invoice  WHERE
	    				id = ".$db->qstr($VAR['id'])." AND
	    				site_id = ".$db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($q);
		$count = $rs->fields['notice_count'] + 1;
		$q  = "UPDATE ".AGILE_DB_PREFIX."invoice SET
	    				notice_count = ".$db->qstr($count)." WHERE
	    				id = ".$db->qstr($VAR['id'])." AND
	    				site_id = ".$db->qstr(DEFAULT_SITE);
		$rs = $db->Execute($q);
	}

  
	/**
	 * Generate all invoices for recurring services/charges/domains 
	 */
	function generate() {
		
		// check if charge module installed
		global $C_list;
		$charge_installed = $C_list->is_installed('charge');
				 
		// get services to be billed grouped by account and date
		if(MAX_INV_GEN_PERIOD <= 0) $max_date = time()+86400; else $max_date = time()+(MAX_INV_GEN_PERIOD*86400); 
		$p=AGILE_DB_PREFIX; $s=DEFAULT_SITE;
		$ids=false;
		$account=false;
		$date=false;
		$invoice=false;
		$sql = "SELECT DISTINCT service.id as serviceId, account.id as accountId, invoice.id as invoiceId, from_unixtime(service.date_next_invoice,'%Y-%m-%d') as dayGroup 
				FROM {$p}service as service 
				JOIN {$p}account as account ON ( service.account_id=account.id and account.site_id={$s} )
				LEFT JOIN {$p}invoice as invoice ON ( service.invoice_id=invoice.id and invoice.site_id={$s} )
				WHERE service.site_id={$s} 
				AND service.active = 1  
				AND ( service.suspend_billing IS NULL OR service.suspend_billing = 0 )  
				AND ( service.date_next_invoice > 0 AND service.date_next_invoice IS NOT NULL )
				AND  
				((
				    ( account.invoice_advance_gen!='' OR account.invoice_advance_gen is not null ) AND service.date_next_invoice <= (UNIX_TIMESTAMP(CURDATE())+(account.invoice_advance_gen*86400))
				 ) OR (
				    ( account.invoice_advance_gen='' OR account.invoice_advance_gen is null ) AND service.date_next_invoice <= {$max_date}
				))
				ORDER BY accountId, dayGroup, serviceId";   			
		$db=&DB();
		#$db->debug=true;
		$rs=$db->Execute($sql);
		if($rs === false) {global $C_debug; $C_debug->error('invoice.inc.php','generate()', $sql . " \r\n\r\n " . @$db->ErrorMsg()); }
		if($rs && $rs->RecordCount()) {
			while(!$rs->EOF) { 
				if( $ids && ($rs->fields['accountId'] != $account ) || ($rs->fields['dayGroup'] != $date) ) { 
					$this->generateInvoices($ids, $account, $invoice, $charge_installed);
					$ids=false;
				}
				
				// set the current account and date
				$account=$rs->fields['accountId'];
				$invoice=$rs->fields['invoiceId'];
				$date=$rs->fields['dayGroup'];
				 
				// add to id list 
				if($ids) $ids.=",";
				$ids.=$rs->fields['serviceId']; 
				$rs->MoveNext();
			}
			if($ids) $this->generateInvoices($ids, $account, $invoice, $charge_installed); 
		}
		
		// Generate invoices for any domains expiring in X days. 
		if($C_list->is_installed('host_tld')) $this->generateDomains();  
		
		return true;
	}
	
	
	function generateInvoices($ids, $account_id, $invoice_id, $charge_installed=false) {

		if(empty($ids)) return false;
		
		# load required elements
		include_once(PATH_MODULES . 'service/service.inc.php');
		include_once(PATH_MODULES . 'discount/discount.inc.php');
		include_once(PATH_MODULES . 'tax/tax.inc.php');
		$taxObj = new tax;
		$serviceObj = new service;		
		   	 
		# start a transaction
		$db=&DB();
		#$db->debug=true;	
		if(AGILE_DB_TYPE == 'mysqlt') {
			$db->StartTrans();
			if(!$db->hasTransactions) { 
				global $C_debug;
				$msg=  "Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver";
				$C_debug->alert($msg);
				$C_debug->error('invoice.inc.php','generateInvoices()',  "Transactions not supported in 'mysql' driver. Use 'mysqlt' or 'mysqli' driver");  
				return false;  
			}
		}
	
		# generate an invoice id		
		$invoice = sqlGenID($db, 'invoice');
	
		# check for any discounts for the parent invoice or account_id 
		# applied at checkout and should continue to be applied if recurring type discount
		$discountObj = new discount;  
		$discountObj->available_discounts($account_id, 1, $invoice_id);
		 	
		# beginning totals
		$sub_total=0;
		$taxable_amount=0;
		$tax_amt=0;
		$discount_amt=0;	 	
				
		# get the full account and service and invoice details
		$p=AGILE_DB_PREFIX; $s=DEFAULT_SITE;
		$sql = "SELECT DISTINCT 
		service.id, service.parent_id, service.invoice_id, service.invoice_item_id, service.account_id, service.account_billing_id, service.product_id,
		service.sku, service.active, service.bind, service.type, service.price, service.price_type, service.taxable, service.date_last_invoice, service.date_next_invoice,
		service.recur_type, service.recur_schedule, service.recur_weekday, service.recur_week, service.domain_name,
		service.domain_tld, service.domain_type, service.domain_term, service.prod_attr, service.prod_attr_cart,
		account.currency_id, account.first_name, account.last_name, account.country_id, account.state, account.invoice_grace, account.invoice_advance_gen, account.affiliate_id as account_affiliate_id,
		invoice.affiliate_id, invoice.campaign_id, invoice.reseller_id, invoice.checkout_plugin_id, invoice.checkout_plugin_data, invoice.billed_currency_id, invoice.actual_billed_currency_id 
		FROM {$p}service as service 
		JOIN {$p}account as account ON ( service.account_id=account.id and account.site_id={$s} )
		LEFT JOIN {$p}invoice as invoice ON ( service.invoice_id=invoice.id and invoice.site_id={$s} )
		WHERE service.id in ({$ids})"; 
		$service=$db->Execute($sql); 
		if($service === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()1', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }
		if($service && $service->RecordCount()) {
			while(!$service->EOF) {
				
				if(empty($service->fields['billed_currency_id'])) $service->fields['billed_currency_id'] = DEFAULT_CURRENCY; 
				if(empty($service->fields['actual_billed_currency_id'])) $service->fields['actual_billed_currency_id'] = $service->fields['billed_currency_id'];
				
				$this->account_id=$service->fields['account_id'];
				$this->parent_id=$service->fields['invoice_id'];
				$this->account_billing_id=$service->fields['account_billing_id'];				
				if(!empty($service->fields['account_affiliate_id']))
				$this->affiliate_id=$service->fields['account_affiliate_id']; 
				else 
				$this->affiliate_id=$service->fields['affiliate_id']; 				
				$this->campaign_id=$service->fields['campaign_id'];
				$this->reseller_id=$service->fields['reseller_id'];
				$this->checkout_plugin_id=$service->fields['checkout_plugin_id'];
				$this->checkout_plugin_data=$service->fields['checkout_plugin_data'];
				$this->billed_currency_id=$service->fields['billed_currency_id'];
				$this->actual_billed_currency_id=$service->fields['actual_billed_currency_id'];
				$this->invoice_grace=$service->fields['invoice_grace'];
				
				$item_tax_amt=0;
				$item_total_amt=0;
				$item_discount_amt=0;
				
				# gen item_id
				$item = sqlGenID($db, "invoice_item"); 
				  
				# Calculate any recurring discounts for this item
				$item_total_amt = $service->fields['price']; 
				$item_discount_amt = $discountObj->calc_all_discounts(1, $item, $service->fields['product_id'], $service->fields['price'], $service->fields['account_id'], $sub_total+$service->fields['price']);
				$item_total_amt -= $item_discount_amt;
				$sub_total += $item_total_amt;
				$discount_amt += $item_discount_amt;
			 								
				# calculate any taxes for this item
				if($service->fields['taxable'] == 1)  {
					$item_tax_amt=0;   
					$item_tax_arr = $taxObj->calculate($item_total_amt, $service->fields['country_id'], $service->fields['state']); 
					if(is_array($item_tax_arr)) foreach($item_tax_arr as $tx) $item_tax_amt += $tx['rate']; 
					$tax_amt += $item_tax_amt; 
				}
		
				# Calculate next invoice date
				$next_invoice = $serviceObj->calcNextInvoiceDate($service->fields['date_next_invoice'], $service->fields['recur_schedule'], $service->fields['recur_type'], $service->fields['recur_weekday']);
				$due_date = $service->fields['date_next_invoice'];
				$recur_schedule=0;
				if(!empty($service->fields['recur_schedule'])) $recur_schedule = $service->fields['recur_schedule'];
				
				// create the invoice item
				$sql="INSERT INTO {$p}invoice_item SET
					id=$item,
					site_id=$s, 
					date_orig=".time().", 
					invoice_id = $invoice, 
					account_id={$service->fields['account_id']}, 
					service_id={$service->fields['id']}, 	
					product_id={$service->fields['product_id']}, 
					product_attr=".$db->qstr($service->fields['prod_attr']).", 
					product_attr_cart=".$db->qstr($service->fields['prod_attr_cart']).", 	
					sku=".$db->qstr($service->fields['sku']).", 
					quantity=1, 
					item_type=0, 
					price_type={$service->fields['price_type']}, 
					price_base={$service->fields['price']}, 
					price_setup=0, 
					recurring_schedule={$recur_schedule}, 
					date_start={$service->fields['date_next_invoice']}, 
					date_stop=$next_invoice, 
					domain_name=".$db->qstr($service->fields['domain_name']).", 
					domain_tld=".$db->qstr($service->fields['domain_tld']).", 
					domain_type=".$db->qstr($service->fields['domain_type']).", 
					tax_amt=$tax_amt, 
					total_amt=$item_total_amt, 
					discount_amt=$item_discount_amt";
				$itemrs=$db->Execute($sql);
			  	if($itemrs === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()2', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }
			  	
				// Insert tax records
				$taxObj->invoice_item($invoice, $item, $service->fields['account_id'], @$item_tax_arr);		
		
				# Insert discount records
				$discountObj->invoice_item($invoice, $item, $service->fields['account_id'], @$discountObj->discount_arr);
									
				// Update the last & next invoice date for this service
				$sql="UPDATE {$p}service 
					SET 
					date_last_invoice = {$service->fields['date_next_invoice']}, 
					date_next_invoice = $next_invoice 
					WHERE
					site_id=$s AND id = {$service->fields['id']} ";
				$srvsrs = $db->Execute($sql);
			 	if($srvsrs === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()3', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }
			 	
				// get any charges for this service and create them as invoice items 	
				if($charge_installed) {
					$sql = "SELECT * FROM ".AGILE_DB_PREFIX."charge WHERE (status=0 or status is null) and site_id=".DEFAULT_SITE." AND service_id = ".$service->fields['id']." AND date_orig < ". $service->fields['date_next_invoice'];
					$charge = $db->Execute($sql);
					if($charge && $charge->RecordCount()) {  
						while(!$charge->EOF) {  
							$item_tax_amt=0;
							$item_total_amt=0;
							$item_discount_amt=0;
										
							// Calculate any recurring discounts for this charge item
							$item_total_amt = ($charge->fields['quantity']*$charge->fields['amount']); 
							$item_discount_amt = $discountObj->calc_all_discounts(1, $item, $charge->fields['product_id'], $item_total_amt, $service->fields['account_id'], $sub_total+$item_total_amt);
							$item_total_amt -= $item_discount_amt;
							$sub_total += $item_total_amt;
							$discount_amt += $item_discount_amt;		
							 	 
							// calculate any taxes for this item
							if($charge->fields['taxable'] == 1)  {
								$item_tax_amt=0;   
								$item_tax_arr = $taxObj->calculate($chargeamt, $service->fields['country_id'], $service->fields['state']); 
								if(is_array($item_tax_arr)) foreach($item_tax_arr as $tx) $item_tax_amt += $tx['rate']; 
								$tax_amt += $item_tax_amt; 
							}				
			  
							// create the invoice item
							$charge_item_id = sqlGenID($db, 'invoice_item');
							
							$sql = "INSERT INTO {$p}invoice_item SET 
								id			= $charge_item_id,		
								site_id		= $s,
								charge_id	= {$charge->fields['id']},
								date_orig 	= ".time().",
								invoice_id 	= $invoice, 
								account_id 	= ".$this->account_id.", 
								service_id 	= ".$db->qstr($service->fields['id']).",
								product_id 	= ".$db->qstr($charge->fields['product_id']).", 
								product_attr= ".$db->qstr($charge->fields['attributes']).",  	
								sku 		= ".$db->qstr($service->fields['sku']).", 
								price_base 	= ".$db->qstr($charge->fields['amount']).", 
								quantity 	= ".$charge->fields['quantity'].",  
								item_type 	= 5,
								price_type 	= 0,
								price_setup = 0,  
								tax_amt 	= $item_tax_amt, 
								total_amt 	= $item_total_amt, 
								discount_amt= $item_discount_amt";
							$itemrs=$db->Execute($sql);						
							if($itemrs === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()4', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }
							
							# Insert tax records
							$taxObj->invoice_item($invoice, $charge_item_id, $charge->fields['account_id'], @$item_tax_arr);	
							
							# Insert discount records
							$discountObj->invoice_item($invoice, $charge_item_id, $charge->fields['account_id'], @$discountObj->discount_arr);
																								
							# update charge status
							$chargers=$db->Execute("UPDATE ".AGILE_DB_PREFIX."charge set status=1 WHERE id={$charge->fields['id']} AND site_id=".DEFAULT_SITE);
							if($chargers === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()2', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }
							
							$charge->MoveNext();
						}			
					} 		
				}	 
				$service->MoveNext(); 
			}
			
			// add any taxes
			@$total = $sub_total + $tax_amt; 
			
			// get invoice grace period from global/account
			if(!empty($this->invoice_grace)) $grace_period=$this->invoice_grace; else $grace_period=GRACE_PERIOD;
			 
			$sql = "INSERT INTO {$p}invoice SET 
				id=$invoice,
				site_id=$s,
				date_orig = ".time().", 
				date_last = ".time().",  
				notice_next_date = ".time().",
				type = 1, 
				process_status = 0, 
				billing_status = 0, 
				suspend_billing = 0, 
				print_status = 0, 
				refund_status = 0, 
				billed_amt = 0, 
				actual_billed_amt = 0, 
				notice_count = 0, 
				parent_id = ".$db->qstr($this->parent_id).",
				account_id = {$this->account_id},  
				account_billing_id = ".$db->qstr($this->account_billing_id).",
				affiliate_id = ".$db->qstr($this->affiliate_id).",
				campaign_id = ".$db->qstr($this->campaign_id).",
				reseller_id = ".$db->qstr($this->reseller_id).",
				checkout_plugin_id = ".$db->qstr($this->checkout_plugin_id).",
				checkout_plugin_data = ".$db->qstr($this->checkout_plugin_data).",
				actual_billed_currency_id = ".$db->qstr($this->actual_billed_currency_id).",
				billed_currency_id = ".$db->qstr($this->billed_currency_id).",
				notice_max = ".$db->qstr(MAX_BILLING_NOTICE).",  
				grace_period = ".$db->qstr($grace_period).",
				tax_amt = ".$tax_amt.",  
				discount_amt = ".$discount_amt.",  
				total_amt = ".$total.",     
				due_date = $due_date"; 
			$invoicers=$db->Execute($sql); 
			if($invoicers === false) {global $C_debug; $C_debug->error('invoice.inc.php','generateInvoices()2', $sql . " \r\n\r\n " . @$db->ErrorMsg()); $db->FailTrans(); return false; }		
		}				 
		
		if(AGILE_DB_TYPE == 'mysqlt') $db->CompleteTrans();
	}
 	
 
	
	
	/** Invoice expiring domains 
	*/
	function generateDomains()
	{ 
		$db = &DB();
		define('DEFAULT_DOMAIN_INVOICE', 30); //how far out to generate expiring domain invoices
		$expire = time() + (DEFAULT_DOMAIN_INVOICE*86400);
	
		### Get domains expiring soon:
		$rs = $db->Execute( sqlSelect( $db, 'service', '*', " active=1 AND domain_date_expire <= $expire AND type = 'domain' AND queue = 'none' AND
	        	( domain_type = 'register' OR domain_type = 'transfer' OR domain_type = 'renew' ) AND
	        	( suspend_billing = 0 OR suspend_billing IS NULL) " ) );
	
		if($rs && $rs->RecordCount() > 0 ) {
			while(!$rs->EOF)  {
				# Check that this domain has not already been invoiced
				$invoiced = $db->Execute( sqlSelect ($db, Array('invoice_item','invoice'), Array('A.*','B.*'),
				" A.invoice_id = B.id AND A.service_id = {$rs->fields['id']} AND A.sku = 'DOMAIN-RENEW' AND domain_type = 'renew' AND
			        			  date_start = {$rs->fields['date_last_invoice']} AND date_stop = {$rs->fields['domain_date_expire']}" ) );        			
	
				if($invoiced && $invoiced->RecordCount() == 0) {
					# Not previously invoiced, generate now!
					$this->generatedomaininvoice( $rs->fields, $this );
		        }	        	
	       		$rs->MoveNext();
	       	}
	    }
	}
		        
	/** Invoice expiring domains p2 
	*/
	function generatedomaininvoice( $VAR )
	{
		include_once(PATH_MODULES . 'tax/tax.inc.php');
		$taxObj = new tax;
	
		$db = &DB();
	
		if( is_array( $VAR ) )  {
			$expire = time();
			$rs = $db->Execute( sqlSelect($db, 'service', '*', " id = ::{$VAR['id']}:: AND active=1
	        				AND type = 'domain' AND queue = 'none' AND
	        				( domain_type = 'register' OR domain_type = 'transfer' OR domain_type = 'renew'  ) AND
	        				( suspend_billing = 0 OR suspend_billing IS NULL ) " ));
			$service = $rs->fields;
		} else {
			$service = $VAR;
		} 
	
		if(empty($service['id'])) {
			global $C_debug;
			$C_debug->alert("Unable to generate domain renweal invoice due to domain status.");
			return false;
		}
	
		# Get the parent invoice details:
		if(!empty($service['invoice_id'])) {
			$rs = $db->Execute( sqlSelect($db, 'invoice', '*', " id = {$service['invoice_id']} ",  "" ) );
			$invoice = $rs->fields;
		} else {
			$invoice = false;
		}
	
		# Get the account details:
		$rs = $db->Execute( sqlSelect($db, 'account', '*', " id = {$service['account_id']} ", ""  ) );
		$account = $rs->fields;
	
	  	# Get the account price
		include_once(PATH_MODULES.'host_tld/host_tld.inc.php');
		$tldObj=new host_tld;			
		$tld_arr = $tldObj->price_tld_arr($service['domain_tld'], 'renew', false, false, false, $service['account_id']);			
		foreach($tld_arr as $term => $price) break;
		 
		# Calculate taxes:
		$rs = $db->Execute($sql=sqlSelect($db,"host_tld","taxable","name = ::{$service['domain_tld']}::")); 
		if( $service['taxable'] || @$rs->fields['taxable'] ) {
			$tax_arr = $taxObj->calculate($price, $account["country_id"], $account["state"]);
		} else {
			$tax_arr = false;
		}
		  
		$total = $price;
	
		$tax_amt = 0;
		if(is_array($tax_arr)) {
			foreach($tax_arr as $tx) {
				$tax_amt += $tx['rate'];
			}
			$total += $tax_amt;
		}
	
		# calculate the dates
		$expire = $service['domain_date_expire'] + ($term*86400);
		$due_date = $service['domain_date_expire'] - (86400*3);
	
		# Create the invoice
		$id = sqlGenID( $db, "invoice" );
		$insert = $db->Execute( $sql = sqlInsert($db, "invoice",
		Array(
		'date_orig' 		=> time(),
		'date_last' 		=> time(),
		'type' 				=> 2,
		'process_status' 	=> 0,
		'billing_status' 	=> 0,
		'suspend_billing' 	=> 0,
		'print_status' 		=> 0,
		'parent_id' 		=> $service['invoice_id'],
		'account_id' 		=> $service['account_id'],
		'account_billing_id'=> $service['account_billing_id'],
		'affiliate_id' 		=> @$invoice['affiliate_id'],
		'campaign_id' 		=> @$invoice['campaign_id'],
		'reseller_id' 		=> @$invoice['reseller_id'],
		'checkout_plugin_id'=> @$invoice['checkout_plugin_id'],
		'tax_amt' 			=> $tax_amt,
		'discount_arr' 		=> serialize(@$discount_arr),
		'discount_amt' 		=> @$discount_amt,
		'total_amt' 		=> $total,
		'billed_amt' 		=> 0,
		'billed_currency_id'=> DEFAULT_CURRENCY,
		'actual_billed_amt' => 0,
		'actual_billed_currency_id' => @$invoice['actual_billed_currency_id'],
		'notice_count' 		=> 0,
		'notice_next_date' 	=> time(),
		'notice_max' 		=> MAX_BILLING_NOTICE,
		'grace_period' 		=> 0,
		'due_date' 			=> $due_date
		),  $id ) ) ;
	
		# create the invoice item:
		if($insert) {
			$db->Execute ( $idx = sqlInsert($db, "invoice_item",
			Array(
			'date_orig' 		=> time(),
			'invoice_id'		=> $id,
			'account_id'		=> $service['account_id'],
			'service_id' 		=> $service['id'],
			'sku' 				=> 'DOMAIN-RENEW',
			'quantity' 			=> 1,
			'item_type' 		=> 2,
			'price_type' 		=> 0,
			'price_base'		=> $price,
			'price_setup' 		=> 0,
			'domain_type' 		=> 'renew',
			'date_start' 		=> $service['domain_date_expire'],
			'date_stop'			=> $expire,
			'domain_name' 		=> $service['domain_name'],
			'domain_tld' 		=> $service['domain_tld'],
			'domain_term' 		=> $term,
			'tax_amt'			=> $tax_amt,
			'total_amt'			=> $price
			) ) );
			
			# Insert tax records
			$taxObj->invoice_item($id, $idx, $service['account_id'], @$item_tax_arr);			
		
			# Update the service record
			$fields=Array('active' => 0);
			$db->Execute(sqlUpdate($db,"service",$fields,"id = {$service['id']}"));
			
			global $C_debug;
			$C_debug->alert("Generated domain renewal invoice for {$service['domain_name']}.{$service['domain_tld']}");
			return $id;
		}
	}
  
 

	/** Run AutoBilling and Due Notices
        */
	function autobill($VAR)
	{
		global $VAR, $C_debug, $C_list;

		# User invoice creation confirmation
		include_once(PATH_MODULES.'email_template/email_template.inc.php');
		$mail = new email_template;

		# get all due invoices
		$db = &DB(); 
		#$db->debug = true;
				
		if(empty($VAR['invoice_id']))
		{
			$this->bill_one = false;
			$sql    = 'SELECT * FROM ' . AGILE_DB_PREFIX . 'invoice
	          		   WHERE  notice_next_date <=  ' . $db->qstr( time() ) . ' 
	                   AND (
	                      	billing_status =  0 OR
	                       	billing_status IS NULL
	                   ) AND (
	                   		suspend_billing =  0 OR
	                   		suspend_billing IS NULL
	                   )
	                   AND  site_id  =   ' . $db->qstr(DEFAULT_SITE);
			$invoice = $db->Execute($sql);
			if($invoice->RecordCount() == 0) {
				$C_debug->alert('No Invoices to Autobill');
				return false;
			}
		} else {
			# get the specified invoice:
			$this->bill_one = true;
			$sql    = 'SELECT * FROM ' . AGILE_DB_PREFIX . 'invoice
	                   WHERE (
	                    	billing_status =  0 OR
	                      	billing_status IS NULL
	                   )  
	                   AND  id  =   ' . $db->qstr($VAR['invoice_id']) . '
	                   AND  site_id =   ' . $db->qstr(DEFAULT_SITE);
			$invoice = $db->Execute($sql);
		}

		# Check for results
		if($invoice->RecordCount() == 0) {
			$C_debug->alert('Invoice could not be billed!');
			return false;
		}

		# Loop through results
		while(!$invoice->EOF)
		{
			$db->StartTrans();
			
			$due = true;

			# get currency code
			$cyid = $invoice->fields['actual_billed_currency_id'];
			$billed_currency_id = $invoice->fields['billed_currency_id'];
			if(empty($this->currency_iso[$cyid]))
			{
				$q = "SELECT three_digit,convert_array FROM ". AGILE_DB_PREFIX ."currency WHERE
                    id       = ". $db->qstr($cyid)." AND
                    site_id     = ". $db->qstr(DEFAULT_SITE);
				$currb   = $db->Execute($q);
				$this->format_currency[$cyid] = Array ( 'convert' => unserialize($currb->fields["convert_array"]),
				'iso' => $currb->fields["three_digit"]);
			}

			# get the currency codes (default)
			if(empty($this->format_currency[$billed_currency_id]))
			{
				# Get the billed currency id currency info:
				$q  = "SELECT three_digit,convert_array FROM ".AGILE_DB_PREFIX."currency WHERE
                    id       = ".$db->qstr($billed_currency_id)." AND
                    site_id = ".$db->qstr(DEFAULT_SITE);
				$currb = $db->Execute($q);
				$this->format_currency[$billed_currency_id] = Array ( 'convert' => unserialize($currb->fields["convert_array"]),
				'iso' => $currb->fields["three_digit"]);
			}

			# attempt to autobill?
			if(!empty( $invoice->fields['account_billing_id'] ))
			{
				# get checkout plugin details:
				$billing =& $db->Execute($sql=sqlSelect($db, array('account_billing','checkout'), 'A.*,B.checkout_plugin',
				"A.id = ::{$invoice->fields['account_billing_id']}:: AND A.checkout_plugin_id=B.id"));

				if($billing && $billing->RecordCount() == 1 && !empty($billing->fields['checkout_plugin'])) {
					$plugin_file = PATH_PLUGINS.'checkout/'. $billing->fields['checkout_plugin'] .'.php';
					if(!is_file($plugin_file)) {
						$err = $plugin_file .' missing when autobilling invoice id ' . $invoice->fields['id'];
						$C_debug->error('invoice.inc.php','autobill()', $err);
					} else {
						include_once ( $plugin_file );
						eval( '$PLG = new plg_chout_' . $billing->fields['checkout_plugin'] . '("'.$billing->fields['checkout_plugin_id'].'");');
					}
				} else {
					$err = 'account_billing.id '.$invoice->fields['account_billing_id'].' empty or not associated with a checkout plugin when autobilling invoice id ' . $invoice->fields['id'];
					$C_debug->error('invoice.inc.php','autobill()', $err);
				}
			}

			# get the actual billed amount
			$amount = $invoice->fields['total_amt'] - $invoice->fields['billed_amt'];
			$billed_amt = $invoice->fields['total_amt'];
			$actual_billed_amt = $invoice->fields['total_amt'];
			if($amount <= 0) $due = false;

			if(!empty($PLG) && is_object($PLG) && $PLG->type == 'gateway' && $amount > 0)
			{ 
				# attempt autobilling if account billing exists and gateway plugin
				if($invoice->fields['account_billing_id'] > 0 )
				{
					/* get the account details */
					$account = $db->Execute(sqlSelect($db,"account","id,email","id=::{$invoice->fields['account_id']}"));
				  
					/* Convert the invoice amount to the actual billed currency amount */
					if($cyid != $invoice->fields['billed_currency_id']) { 
						$conversion = $this->format_currency[$billed_currency_id]["convert"][$cyid]["rate"];
						$amount     *= $conversion;
						$actual_billed_amt = $invoice->fields['actual_billed_amt'] + $amount;
					}
					
					/* load the billing details from the database */ 
					$PLG->setBillingFromDBObj($billing, true);
		  
					/* attempt to auto-bill */
					if(!$checkout_plugin_data = $PLG->bill_checkout( number_format($amount,2), $invoice->fields['id'], $this->format_currency[$cyid]['iso'], $account->fields, 0,0) ) {
						$due = true; 
						$email = new email_template;
						$email->send('invoice_decline_user', $invoice->fields['account_id'], $invoice->fields['id'],$C_list->format_currency($invoice->fields['total_amt'],$cyid), $C_list->date($invoice->fields['due_date']));
						$email = new email_template;
						$email->send('admin->invoice_decline_admin', $invoice->fields['account_id'], $invoice->fields['id'], $C_list->format_currency($invoice->fields['total_amt'],''), $C_list->date($invoice->fields['due_date']));
					} else {
						$due = false;
					}
				}
			}

			# send proper alert & manage services
			if ($due)
			{
				# determine if overdue
				$due = $invoice->fields['due_date'];
				$grace = $invoice->fields['grace_period'];
				if(time() < $due+(86400*$grace))
				{
					if($invoice->fields['notice_count'] <= 0)
					{
						# send out first alert - new invoice created!
						$email = new email_template;
						$email->send('invoice_recur_user', $invoice->fields['account_id'], $invoice->fields['id'], $C_list->format_currency($invoice->fields['total_amt'],$cyid), $C_list->date($invoice->fields['due_date']));
						$email = new email_template;
						$email->send('admin->invoice_recur_admin', $invoice->fields['account_id'], $invoice->fields['id'], $C_list->format_currency($invoice->fields['total_amt'],''), $C_list->date($invoice->fields['due_date']));
					}
					else
					{
						# send out payment due notice
						if(empty($PLG) || $PLG->type == 'gateway') {
							$email = new email_template;
							$email->send('invoice_due_user', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$cyid]["iso"], $C_list->date($invoice->fields['due_date']));
							$email = new email_template;
							$email->send('admin->invoice_due_admin', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$billed_currency_id]["iso"], $C_list->date($invoice->fields['due_date']));
						}
						elseif($PLG->type == 'redirect')  {
							$email = new email_template;
							$email->send('invoice_due_user', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$cyid]["iso"], $C_list->date($invoice->fields['due_date']));
						} elseif ($PLG->type == 'other') {
							$email = new email_template;
							$email->send('admin->invoice_due_admin', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$billed_currency_id]["iso"], $C_list->date($invoice->fields['due_date']));
						}
					}

					# increment notice counter
					$sql    = 'UPDATE ' . AGILE_DB_PREFIX . 'invoice SET
                            notice_count    =  ' . $db->qstr($invoice->fields['notice_count']+1) . ',
                            notice_next_date = ' . $db->qstr(time()+86400*3) . '
                            WHERE
                            id           =  ' . $db->qstr( $invoice->fields['id'] ) . ' AND
                            site_id      =  ' . $db->qstr(DEFAULT_SITE);
					$db->Execute($sql);
				}
				else
				{
					# send service cancelation notice
					$email = new email_template;
					$email->send('service_suspend_user', $invoice->fields['account_id'], $invoice->fields['id'], $C_list->format_currency($invoice->fields['total_amt'],$cyid), $C_list->date($invoice->fields['due_date']));
					$email = new email_template;
					$email->send('admin->service_suspend_admin', $invoice->fields['account_id'], $invoice->fields['id'], $C_list->format_currency($invoice->fields['total_amt'],''), $C_list->date($invoice->fields['due_date']));

					# overdue - cancel services
					$vara['id'] = $invoice->fields['id'];
					$this->voidInvoice($vara, $this);

					# suspend billing activity
					$sql    = 'UPDATE ' . AGILE_DB_PREFIX . 'invoice SET
                            notice_count    =  ' . $db->qstr($invoice->fields['notice_count']+1) . ',
                            suspend_billing = ' . $db->qstr('1') . '
                            WHERE
                            id           =  ' . $db->qstr( $invoice->fields['id'] ) . ' AND
                            site_id      =  ' . $db->qstr(DEFAULT_SITE);
					$db->Execute($sql); 
				}			
			}
			else
			{
				# update billing stauts
				$sql = 'UPDATE ' . AGILE_DB_PREFIX . 'invoice SET
                            notice_count    =  ' . $db->qstr($invoice->fields['notice_count']+1) . ',
                            billing_status  = ' . $db->qstr('1') . ',
                            billed_amt       = ' . $db->qstr($billed_amt) . ',
                            actual_billed_amt = ' . $db->qstr($actual_billed_amt) . '
                            WHERE
                            id           =  ' . $db->qstr( $invoice->fields['id'] ) . ' AND
                            site_id      =  ' . $db->qstr(DEFAULT_SITE);
				$db->Execute($sql);

				# update invoice via autoapproveInvoice
				$this->autoApproveInvoice($invoice->fields['id']);

				# User alert of payment processed
				$email = new email_template;
				$email->send('invoice_paid_user', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$cyid]['iso'], '');

				# Admin alert of payment processed
				$email = new email_template;
				$email->send('admin->invoice_paid_admin', $invoice->fields['account_id'], $invoice->fields['id'], $this->format_currency[$billed_currency_id]['iso'], '');
			}
			$invoice->MoveNext();
			unset($PLG);
			
			/* finish transaction */
			$db->CompleteTrans();
		}
	}
 
	

	/**
	 * Find out if a user has unpaid invoices
	 */
	function has_unpaid($VAR) {
		if(!SESS_LOGGED) return false;
		$db=&DB();
		$inv=$db->Execute($sql=sqlSelect($db,"invoice","SUM(total_amt-billed_amt) as total",
		"account_id=".SESS_ACCOUNT." AND billing_status=0 AND refund_status=0"));  
		if($inv && $inv->RecordCount() && $inv->fields['total']>0) {
			global $smarty, $C_list;
			//$act= $db->Execute(sqlSelect($db,"account","currency_id","id=));
			$smarty->assign('has_unpaid', $C_list->format_currency_num($inv->fields['total'],SESS_CURRENCY)); 
		}			
	}

	
	/**
	 * Get the totals for multiple invoices or for a group of invoices stored in temp
	 */
	function multiple_invoice_total($invoice,$account_id=SESS_ACCOUNT) 
	{
		$db = &DB();
		if(empty($invoice) || eregi(",", $invoice)) { 
			$id_list='';
			if(!empty($invoice)) {
				$id = split(',', $invoice);
				for($i=0; $i<count($id); $i++) {
					if($id[$i] != '') {
						if($i == 0) {
							$id_list .= " id = " .$db->qstr($id[$i])." ";
							$ii++;
						} else {
							$id_list .= " OR id = " .$db->qstr($id[$i]). " ";
							$ii++;
						}
					}
				}
				if(!empty($id_list)) $id_list = "( $id_list ) AND ";
			}
			// get invoice totals
			$total=0;
			$inv=$db->Execute($sql=sqlSelect($db,"invoice","id,total_amt,billed_amt", "$id_list account_id=".SESS_ACCOUNT." AND billing_status=0 AND refund_status=0"));
			if($inv && $inv->RecordCount()) {
				while(!$inv->EOF) {
					$this->invoice[] = $inv->fields['id'];
					$total += ($inv->fields['total_amt']-$inv->fields['billed_amt']);
					$inv->MoveNext();
				}
				return $total;
			} else {
				return false;
			}
		} else {
			// get total from temp data
			$inv=$db->Execute($sql=sqlSelect($db,"temporary_data","data,field1 as total","field2=::$invoice::")); 
			if($inv && $inv->RecordCount() && $inv->fields['total'] > 0) {
				if(!empty($inv->fields['field2'])) $this->invoice=unserialize($inv->fields['data']);
				return $inv->fields['total'];
			} else {
				return false;
			}			
		}
		return false;		
	}

	/**
	 * Preview checkout of multiple invoices
	 */
	function checkout_multiple_preview($VAR) 
	{
		global $smarty,$C_list;
		if(!SESS_LOGGED) return false;
		 
		$db=&DB();
		$total = $this->multiple_invoice_total(@$VAR['id'],SESS_ACCOUNT);
		if($total > 0 && count($this->invoice) > 1) 
		{
			// get country id for checkout options
			$account=sqlSelect($db, "account", "country_id", "id=".SESS_ACCOUNT);
			 			
			// get payment options
			include_once(PATH_MODULES.'checkout/checkout.inc.php');
			$checkout = new checkout;
			$checkoutoptions = $checkout->get_checkout_options(SESS_ACCOUNT, $total, false, $account->fields['country_id'],true);
			 
			// get a temporary id (48 hours)
			$id=sqlGenID($db, "temporary_data");
			$invoice["id"] = "MULTI-$id";
			$invoice["total"] = $total;
			$fields=Array('date_orig'=>time(), 'date_expire'=>time()+86400*3, 'field2'=> $invoice['id'], 'field1'=>$total, 'data'=>serialize($this->invoice));
			$id = & $db->Execute(sqlInsert($db,"temporary_data",$fields));
			
			$smarty->assign('invoice', $invoice); 
			$smarty->assign('total', $C_list->format_currency_num($total,SESS_CURRENCY)); 
			$smarty->assign('checkoutoptions', $checkoutoptions);
		
		} elseif (count($this->invoice) == 1) {	
			$id = $this->invoice[0];
			echo "<script language=javascript>document.location.href='?_page=invoice:user_view&id=".$id."';</script>";
		} else {
			echo "No due invoices selected for payment.";
		}
	}
		
	
	/** Run checkout plugin for billing
    */
	function checkoutnow($VAR)
	{
		global $C_translate, $smarty, $C_list, $VAR;
 
		# Validate user logged in:
		if(SESS_LOGGED != '1') {
			echo '<script language="JavaScript">alert("You must be logged in to complete this purchase! Please refresh this page in your browser to login now...");</script>';
			return false;
		}
		 
		$db     = &DB();
		if(eregi("MULTI-", @$VAR['invoice_id'])) {
			// get multi-invoice details
			$total = $this->multiple_invoice_total(@$VAR['invoice_id'],SESS_ACCOUNT);
			if(!$total) return false;
			$recur_amt=false;
			$recur_arr=false;
			$account_id=SESS_ACCOUNT;
			$this->invoice[]=$VAR['invoice_id'];
			$this->invoice_id=$VAR['invoice_id'];
			$CURRENCY = DEFAULT_CURRENCY;
			$multi=true;
		} else {
			# Validate the invoice selected, & get the totals:
			$sql    = 'SELECT * FROM ' . AGILE_DB_PREFIX . 'invoice WHERE site_id = '.$db->qstr(DEFAULT_SITE).' AND id = ' . $db->qstr($VAR['invoice_id']);
			$result = $db->Execute($sql); 
			if(!$result  || $result->RecordCount() == 0) return false;
	
			# Determine the price & currency
			if($result->fields['billed_currency_id'] != $result->fields['actual_billed_currency_id']) {
				global $C_list;
				$CURRENCY = $result->fields['actual_billed_currency_id'];
				if($result->fields['billed_amt'] <= 0)
				$total = $C_list->format_currency_decimal ($result->fields['total_amt'], $CURRENCY);
				else
				$total = $C_list->format_currency_decimal ($result->fields['total_amt'], $CURRENCY) - $result->fields['actual_billed_amt'];
			} else {
				$CURRENCY = $result->fields['billed_currency_id'];
				$total = $result->fields['total_amt']-$result->fields['billed_amt'];
			}
			$recur_amt=$result->fields['recur_amt'];
			if($recur_amt>0) $recur_amt = $C_list->format_currency_decimal ($recur_amt, $CURRENCY);
			@$recur_arr=unserialize($result->fields['recur_arr']);
			$account_id=$result->fields['account_id'];
			$this->invoice_id=$result->fields['id'];
			$this->invoice[]=$result->fields['id'];
			$multi=false;
		}
		$amount = round($total, 2);

		# Get the account details:
		$sql    = 'SELECT * FROM ' . AGILE_DB_PREFIX . 'account WHERE site_id = ' . $db->qstr(DEFAULT_SITE) . ' AND id = ' . $db->qstr($account_id);
		$account = $db->Execute($sql);
		if (!$account || !$account->RecordCount()) return false;
  
		# Validate checkout option selected is allowed for purchase:
		$q  = "SELECT * FROM ".AGILE_DB_PREFIX."checkout WHERE site_id = ".$db->qstr(DEFAULT_SITE)." AND id = ".$db->qstr(@$VAR['option'])." AND active = 1 AND ";
		if($recur_amt>0 && @$billed_amt == 0 ) $q .= "allow_recurring = 1 "; else $q .= "allow_new = 1 ";
		$chopt = $db->Execute($q);
		if (!$chopt || !$chopt->RecordCount()) return false;
		if($chopt && $chopt->RecordCount()) {
			$show = true;
			if ( @$chopt->fields["total_maximum"] != "" && $total > $chopt->fields["total_maximum"] )   $show = false;  
			if ( @$chopt->fields["total_miniumum"] != "" && $total < $chopt->fields["total_miniumum"] ) $show = false; 
		}
		if(!$show) {
			echo '<script language=Javascript> alert("Unable to checkout with the selected method, please select another."); </script> ';
			return false;
		}		 
		
		# Load the checkout plugin:
		$plugin_file = PATH_PLUGINS . 'checkout/'. $chopt->fields["checkout_plugin"] . '.php';
		include_once ( $plugin_file );
		eval ( '$PLG = new plg_chout_' .   $chopt->fields["checkout_plugin"] . '("'.@$VAR["option"].'");');
 
		if(!empty($VAR['account_billing_id']) && @$VAR['new_card']==2) {  
			/* validate credit card on file details */
			$account_billing_id=$VAR['account_billing_id']; 
			if(!$PLG->setBillingFromDB($account_id, $account_billing_id, $VAR['option'])) { 
				global $C_debug;
				$C_debug->alert("Sorry, we cannot use that billing record for this purchase.");
				return false;
			}
		} else {
			/* use passed in vars */
			$PLG->setBillingFromParams($VAR);
		}
				
		# Set Invoice Vars:
		$this->total_amt					= $amount;
		$this->currency_iso 				= $C_list->currency_iso($CURRENCY);
		$this->currency_iso_admin			= $C_list->currency_iso($CURRENCY); 
		$this->account_id					= $account_id; 
		$this->actual_billed_currency_id	= $CURRENCY;
		$this->billed_currency_id			= $CURRENCY;
		$this->checkout_plugin_id           = @$VAR["option"];
				
		# Run the plugin bill_checkout() method:
		$this->checkout_plugin_data = $PLG->bill_checkout( $amount, $this->invoice_id, $this->currency_iso, $account->fields, $recur_amt, $recur_arr);

		# redirect
		if(!empty($this->checkout_plugin_data['redirect'])) echo $this->checkout_plugin_data['redirect'];
  
		# determine results
		if( $this->checkout_plugin_data === false ) {
			if(!empty($PLG->redirect)) echo $PLG->redirect; 
			return false; 
		} elseif ($PLG->type == "gateway" && empty($PLG->redirect)) {  		 
			if(empty($this->admin_checkout)) {
				$VAR['_page'] = "invoice:thankyou";
			} else {
				$VAR['_page'] = "invoice:view";
			}   
		} elseif ($PLG->type == "redirect") {
			echo "<html><head></head><body><center>
				Please wait while we redirect you to the secure payment site....
				{$PLG->redirect}</center></body></html>"; 
		} 
		 
		# Call the Plugin method for storing the checkout data, if new data entered:
		$this->account_billing_id = $PLG->store_billing($VAR, $PLG);

		# Load the email template module
		include_once(PATH_MODULES.'email_template/email_template.inc.php');
		$mail = new email_template;

		# Update billing details for this invoice, if realtime billing succeeded:
		if($PLG->type == 'gateway' || $amount == 0) {
			$q  = "UPDATE ".AGILE_DB_PREFIX."invoice
		        		SET
		        			account_billing_id		= " .$db->qstr($this->account_billing_id). ",
		        			billing_status			= " .$db->qstr(1). ",
		        			billed_amt 		  		= " .$db->qstr($total). ",
		        			actual_billed_amt 		= " .$db->qstr($amount). ",
		        			date_last		  		= " .$db->qstr(time()). ",
		        			checkout_plugin_id		= " .$db->qstr($this->checkout_plugin_id) .",
		        			checkout_plugin_data	= " .$db->qstr(serialize($this->checkout_plugin_data)). "
		        		WHERE
		        			site_id   = ".$db->qstr(DEFAULT_SITE)." AND
		        			id 		  = ".$db->qstr($this->invoice_id);	 
			$rst = $db->Execute($q);
			if ($rst === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','checkoutnow', $db->ErrorMsg());
				return false;
			}

			// loop through each invoice paid
			foreach($this->invoice as $this->invoice_id) { 
				# Send billed e-mail notice to user
				$email = new email_template;
				$email->send('invoice_paid_user', $this->account_id, $this->invoice_id, $this->currency_iso, '');
	
				# Admin alert of payment processed
				$email = new email_template;
				$email->send('admin->invoice_paid_admin', $this->account_id, $this->invoice_id, $this->currency_iso_admin, '');
	
				# Submit the invoice for approval
				$arr['id'] = $this->invoice_id;
				$this->approveInvoice($arr, $this);
			}

		} else {

			# Just update the last_date and plugin data
			$q  = "UPDATE ".AGILE_DB_PREFIX."invoice
		        		SET 
		        			account_billing_id		= " .$db->qstr($this->account_billing_id). ",
		        			date_last		  		= " .$db->qstr(time()). ",
		        			checkout_plugin_id		= " .$db->qstr($this->checkout_plugin_id) .",
		        			checkout_plugin_data	= " .$db->qstr(serialize($this->checkout_plugin_data)). "
		        		WHERE
		        			site_id   = ".$db->qstr(DEFAULT_SITE)." AND
		        			id 		  = ".$db->qstr($this->invoice_id);	 
			$rst = $db->Execute($q);
			if ($rst === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','checkoutnow', $db->ErrorMsg());
				return false;
			}

			# Admin e-mail alert of manual payment processing
			if ( $PLG->name == 'MANUAL' ) { 
				$date_due = $C_list->date(time());
				foreach($this->invoice as $this->invoice_id) { 
					$email = new email_template;
					$email->send('admin->invoice_due_admin', $this->account_id, $this->invoice_id, '', $date_due);
				}
				
				global $C_debug;
				$C_debug->alert($C_translate->translate('manual_alert','checkout'));				
			}  
		}
	}	
		

	/** create modified array for invoice summarization
    */
	function summarizeLineItems($smart_items)
	{
		//$ignore['SKU']=true;
		$sum=false;
		if(is_array($smart_items)) {
			foreach($smart_items as $it)  {
				if(empty($sum["{$it["sku"]}"])) {
					// unique line item
					if(empty($ignore["{$it["sku"]}"])) $sum["{$it["sku"]}"][] = $it;
				} else {
					// is unique price/attributes?
					$unique=true;
					foreach($sum["{$it["sku"]}"] as $sid => $flds) {
						if(	$flds["price_base"] == $it["price_base"] && $flds["price_setup"] == $it["price_setup"] && $flds['product_attr'] == $it['product_attr']  ) {
							$sum["{$it["sku"]}"]["$sid"]["quantity"] += 1;
							$unique = false;
							break;
						}
					}
					// unique line item
					if($unique) $sum["{$it["sku"]}"][] = $it;
				}
			}
		}

		if(!empty($sum)) {
			unset($smart_items);
			foreach($sum as $sku => $item) foreach($item as $sitem)  $smart_items[] = $sitem;
			return $smart_items;
		}
	}
        
            	
    	
	/** VIEW
    */
	function view($VAR)
	{
		global $C_translate, $C_list;
		$this->invoice_construct();
		$type = "view";
		$this->method["$type"] = split(",", $this->method["$type"]);

		$db = &DB();

		# set the field list for this method:
		$arr = $this->method[$type];

		# loop through the field list to create the sql queries
		$field_list = '';
		$i=0;
		while (list ($key, $value) = each ($arr))
		{
			if($i == 0)
			{
				$field_var =  $this->table . '_' . $value;
				$field_list .= $value;
			}
			else
			{
				$field_var =  $this->table . '_' . $value;
				$field_list .= "," . $value;
			}
			$i++;
		}

		if(isset($VAR["id"]))
		{
			$id = split(',',$VAR["id"]);
			for($i=0; $i<count($id); $i++)
			{
				if($id[$i] != '')
				{
					if($i == 0)
					{
						$id_list .= " id = " .$db->qstr($id[$i])." ";
						$ii++;
					}
					else
					{
						$id_list .= " OR id = " .$db->qstr($id[$i]). " ";
						$ii++;
					}
				}
			}
		}

		if($ii>0)
		{ 
			$any_trial			= false;
			$any_recurring		= false;
			$any_new			= false;

			# generate the full query
			$q = "SELECT
		        	  $field_list
		        	  FROM
		        	  ".AGILE_DB_PREFIX."$this->table
					  WHERE					
		        	  $id_list
		        	  AND site_id = '" . DEFAULT_SITE . "'
		        	  ORDER BY $this->order_by 
		        	  LIMIT 0,1";
			$result = $db->Execute($q);
			if ($result === false)
			{
				global $C_debug;
				$C_debug->error('invoice.inc.php','view', $db->ErrorMsg());

				if(isset($this->trigger["$type"]))
				{
					include_once(PATH_CORE   . 'trigger.inc.php');
					$trigger    = new CORE_trigger;
					$trigger->trigger($this->trigger["$type"], 0, $VAR);
				}
				return;
			}

			### Set it as a class variable:
			$this->result = $result;

			# put the results into a smarty accessable array
			$i=0;
			$ii=0;
			$class_name = TRUE;
			while (!$result->EOF)
			{
				$smart[$i] = $result->fields;

				## get the product plugin name
				if(!empty($result->fields['checkout_plugin_id'])) {
					$cplg = $db->Execute(sqlSelect($db,"checkout","name","id = {$result->fields['checkout_plugin_id']}"));
					if($cplg && $cplg->RecordCount()) $smart[$i]['checkout_plugin'] = $cplg->fields['name'];
				}

				if($result->fields['total_amt'] == 0)
				$smart[$i]['balance'] = 0;
				else
				$smart[$i]['balance'] = $result->fields['total_amt'] - $result->fields['billed_amt'];

				## Get the tax details
				if( !empty($result->fields['tax_amt']) ) {
					$trs = $db->Execute($sql=sqlSelect($db, Array('invoice_item_tax','tax'),"A.amount,B.description","A.tax_id=B.id AND A.invoice_id={$result->fields['id']}"));
					if($trs && $trs->RecordCount()) {
						while(!$trs->EOF) {
							$taxes["{$trs->fields['description']}"] += $trs->fields["amount"];
							$trs->MoveNext();
						}
						foreach($taxes as $txds=>$txamt)
						$smart[$i]["tax_arr"][] = Array('description'=>$txds, 'amount'=>$txamt);
					}
				}

				## Get the discount details
				if( !empty($result->fields['discount_amt']) ) {
					$drs = $db->Execute($sql=sqlSelect($db, 'invoice_item_discount',"amount,discount","invoice_id={$result->fields['id']}"));
					if($drs && $drs->RecordCount()) {
						while(!$drs->EOF) {
							$discounts["{$drs->fields['discount']}"] += $drs->fields["amount"];
							$drs->MoveNext();
						}
						$dhtml='';
						foreach($discounts as $dsds=>$dsamt) $dhtml .= '<a href=\'?_page=core:search&module=discount&discount_name='.$dsds.'\'>'.$dsds.'</a> - '. number_format($dsamt, 2) . "<br>";					
						$smart[$i]['discount_popup'] = $dhtml; 
						$dhtml='';
						foreach($discounts as $dsds=>$dsamt) $dhtml .= $dsds.' - '. number_format($dsamt, 2) . "<br>";					
						$smart[$i]['discount_popup_user'] = $dhtml; 
						
					}
				} 

				## Get the checkout plugin details:
				if(!empty($result->fields['checkout_plugin_data'])) {
					$plugin_data = unserialize($result->fields['checkout_plugin_data']);
					if(is_array($plugin_data)) {
						$smart[$i]['checkout_plugin_data'] = $plugin_data;
					} else {
						$smart[$i]['checkout_plugin_data'] = Array(0 => $result->fields['checkout_plugin_data']);
					}
				}

				## Get the line items:
				$q = "SELECT * FROM ".AGILE_DB_PREFIX."invoice_item WHERE
                                invoice_id          = ". $db->qstr($result->fields['id'])." AND
                                site_id           = ". $db->qstr(DEFAULT_SITE);
				if($C_list->is_installed('voip')) {
					$q .= " AND item_type!=5";
				}				
				$items = $db->Execute($q);
				if ($items === false) {
					global $C_debug;
					$C_debug->error('invoice.inc.php','view', $db->ErrorMsg());
					return false;
				}
				$ii =0;
				while ( !$items->EOF )
				{
					$smart_items[$ii] = $items->fields;

					// get the product attribs
					if(!empty($items->fields['product_attr']))
					{
						@$attrib = explode("\r\n", $items->fields['product_attr']);
						$js='';
						for($attr_i = 0; $attr_i < count( $attrib ); $attr_i++)
						{
							$attributei = explode('==', $attrib[$attr_i]);
							if(!empty($attributei[0]) && !empty($attributei[1])) {
								$js .= "<u>" . $attributei[0] . "</u> : ". $attributei[1] . " <BR>";
							}
						}
						$smart_items[$ii]['attribute_popup'] = $js;
					}

					// get the date range if set
					if(!empty($items->fields['date_start']) && !empty($items->fields['date_stop']))
					{
						$C_translate->value('invoice','start', date(UNIX_DATE_FORMAT,$items->fields['date_start']));
						$C_translate->value('invoice','stop', date(UNIX_DATE_FORMAT,$items->fields['date_stop']));
						$smart_items[$ii]['range'] = $C_translate->translate('recur_date_range','invoice','');
					}

					// Set charge type for payment option list:
					$any_new = true;
					if ($items->fields["price_type"] == '1' && !empty($result->fields['recurr_arr']) && is_array(unserialize($result->fields['recurr_arr'])))
					$any_recurring 	= true;			
					$items->MoveNext();
					$ii++;
				}

				## Create a summary (for duplicate skus w/identical price,and attributes, roll into a single value
				if($this->summarizeInvoice) {
					$tmp = $smart_items;
					unset($smart_items);
					$smart_items = $this->summarizeLineItems($tmp);
				}

				### GET THE CHECKOUT (PAYMENT) OPTIONS
				if($VAR['_page'] != 'invoice:view')
				{
					# get the converted amount due:
					if($result->fields['billed_currency_id'] != $result->fields['actual_billed_currency_id'])
					{
						global $C_list;
						$CURRENCY = $result->fields['actual_billed_currency_id'];
						if($result->fields['billed_amt'] <= 0)
						$total = $C_list->format_currency_decimal ($result->fields['total_amt'], $CURRENCY);
						else
						$total = $C_list->format_currency_decimal ($result->fields['total_amt'], $CURRENCY) - $result->fields['actual_billed_amt'];
					} else {
						$CURRENCY = $result->fields['billed_currency_id'];
						$total = $result->fields['total_amt']-$result->fields['billed_amt'];
					}

					$q  = "SELECT * FROM ".AGILE_DB_PREFIX."checkout WHERE site_id = ". DEFAULT_SITE ." AND active = 1";
					if($any_trial) 		$q .= " AND allow_trial		= ".$db->qstr('1');
					if($any_recurring) 	$q .= " AND allow_recurring	= ".$db->qstr('1');
					if($any_new) 		$q .= " AND allow_new		= ".$db->qstr('1');
					$chopt = $db->Execute($q);
					if ($chopt === false) {
						global $C_debug;
						$C_debug->error('invoice.inc.php','view', $db->ErrorMsg());
						return false;
					}
					if($chopt != false && $chopt->RecordCount() > 0) {
						while( !$chopt->EOF ) {
							$show = true;

							# Check that the cart total is not to high:
							if ( $chopt->fields["total_maximum"] != "" &&
							$result->fields['total_amt'] >= $chopt->fields["total_maximum"] ) {
								$show = false;
							}

							# Check that the cart total is not to low:
							if ( $chopt->fields["total_miniumum"] != "" &&
							$result->fields['total_amt'] <= $chopt->fields["total_miniumum"] ) {
								$show = false;
							}

							# Check that the group requirement is met:
							if ( $show && !empty ( $chopt->fields["required_groups"] ) ) {
								global $C_auth;
								$arr = unserialize ( $chopt->fields["required_groups"] );
								if(count($arr) > 0 && !empty($arr[0])) $show = false;
								for ( $i=0; $i<count($arr); $i++ )  {
									if($C_auth->auth_group_by_id($arr[$i])) {
										$show = true;
										$i=count($arr);
									}
								}
							}

							# Check that the customer is not ordering a blocked SKU:
							if ( $show && !empty ( $chopt->fields["excluded_products"] ) ) {
								$arr = unserialize ( $chopt->fields["excluded_products"] );
								if(count($arr) > 0)  {
									for($i=0; $i<count($smart_items); $i++)  {
										for($isk=0; $isk<count($arr); $isk++) {
											if($smart_items[$i]['product_id'] == $arr[$isk] && !empty($arr[$isk]) && !empty($smart_items[$i]['product_id']) ) {
												$show = false;
												$i=count($smart);
												$isk=count($arr);
											}
										}
									}
								}
							}


							$list_ord = 100;
							if ( $show ) {

								# Check if this method should be the default method:
								# By Amount:
								if ( !empty ( $chopt->fields["default_when_amount"] ) ) {
									$arr = unserialize ( $chopt->fields["default_when_amount"] );
									for ( $idx=0; $idx<count($arr); $idx++ ) {
										if ( $total >= $arr[$idx] ) $list_ord--;
										$idx=count($arr);
									}
								}

								# By Currency
								if ( !empty ( $chopt->fields["default_when_currency"] ) ) {
									$arr = unserialize ( $chopt->fields["default_when_currency"] );
									for ( $idx=0; $idx<count($arr); $idx++ ) {
										if ( $CURRENCY == $arr[$idx] ) $list_ord--;
										$idx=count($arr);
									}
								}

								# By Group
								if ( !empty ( $chopt->fields["default_when_group"] ) ) {
									$arr = unserialize ( $chopt->fields["default_when_group"] );
									global $C_auth;
									for ( $idx=0; $idx<count($arr); $idx++ ) {
										if ( $C_auth->auth_group_by_id( $arr[$idx] ) ) $list_ord--;
										$idx=count($arr);
									}
								}

								# By Country
								if ( !empty ( $chopt->fields["default_when_country"] ) ) {
									$arr = unserialize ( $chopt->fields["default_when_country"] );
									for ( $idx=0; $idx<count($arr); $idx++ ) {
										if ( $account->fields["country_id"] == $arr[$idx] ) $list_ord--;
										$idx=count($arr);
									}
								}

								# Add to the array
								$checkout_optionsx[] = Array ('sort'   => $list_ord,
								'fields' => $chopt->fields);
							}
							$chopt->MoveNext();
						}

						# Sort the checkout_options array by the [fields] element
						if(count($checkout_optionsx) > 0 ) {
							foreach ( $checkout_optionsx as $key => $row )
							$sort[$key] = $row["sort"];
							array_multisort ( $sort, SORT_ASC, $checkout_optionsx );
						}
					}
				}

				$result->MoveNext();
				$i++;
			}


			# get the result count:
			$results = $result->RecordCount();

			### No results:
			if($result->RecordCount() == 0)
			{
				global $C_debug;
				$C_debug->error("CORE:database.inc.php", "view()", "The selected record does not
                    exist any longer, or your account is not authorized to view it");
				return;
			}

			# define the DB vars as a Smarty accessible block
			global $smarty;

			# define the results
			$smarty->assign('cart', $smart_items);
			$smarty->assign($this->table, $smart);
			#$smarty->assign('results', 	$search->results);
			$smarty->assign('checkoutoptions', $checkout_optionsx);
		}
	}

	/** UPDATE
        */
	function update($VAR)
	{
		$this->invoice_construct();
		$type = "update";
		$this->method["$type"] = split(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->update($VAR, $this, $type);
	}

	/** DELETE
        */
	function delete($VAR)
	{
		$this->invoice_construct();
		$dbx = new CORE_database;
		$db = &DB();

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

		### Load the service module
		include_once(PATH_MODULES.'service/service.inc.php');
		$service = new service;

		### Loop:
		for($i=0; $i<count($id); $i++)
		{
			### Loop through all services for this invoice and delete:
			$q = "SELECT * FROM  ".AGILE_DB_PREFIX."service WHERE
				        invoice_id  = ".$db->qstr( $id[$i]  )." AND
				        site_id     = ".$db->qstr(DEFAULT_SITE);;
			$rs = $db->Execute($q);
			if ($rs === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','delete', $db->ErrorMsg());
				return false;
			}
			if (@$rs->RecordCount() > 0) {
				while ( !$rs->EOF ) {
					$arr['id'] = $rs->fields['id'];
					$service->delete($arr, $service);
					$rs->MoveNext();
				}
			}

			### Delete the service record
			$arr['id'] = $id[$i];
			$this->associated_DELETE[] = Array ('table' => 'invoice_commission', 'field' => 'invoice_id');
			$this->associated_DELETE[] = Array ('table' => 'invoice_item',       'field' => 'invoice_id');
			$this->associated_DELETE[] = Array ('table' => 'invoice_memo',       'field' => 'invoice_id');
			$this->associated_DELETE[] = Array ('table' => 'service',	 		 'field' => 'invoice_id');
			$this->associated_DELETE[] = Array ('table' => 'invoice_item_tax',	 'field' => 'invoice_id');
			$this->associated_DELETE[] = Array ('table' => 'invoice_item_discount', 'field' => 'invoice_id');
			$dbx->mass_delete($arr, $this, "");
		}
	}

	/** SEARCH FORM
        */
	function search_form($VAR)
	{
		$this->invoice_construct();
		$type = "search";
		$this->method["$type"] = split(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->search_form($VAR, $this, $type);
	}

	/** SEARCH
        */
	function search($VAR)
	{
		$this->invoice_construct();
		$type = "search";
		$this->method["$type"] = split(",", $this->method["$type"]);

		$db = &DB();

		include_once(PATH_CORE . 'validate.inc.php');
		$validate = new CORE_validate;

		# set the search criteria array
		$arr = $VAR;

		# convert invoice_discount_arr
		if(!empty($VAR['invoice_discount_arr']))
		$arr['invoice_discount_arr'] = '%"'.$VAR['invoice_discount_arr'].'"%';


		# loop through the submitted field_names to get the WHERE statement
		$where_list = '';
		$i=0;
		while (list ($key, $value) = each ($arr))
		{
			if($i == 0)
			{
				if($value != '')
				{
					$pat = "^" . $this->module . "_";
					if(eregi($pat, $key))
					{
						$field = eregi_replace($pat,"",$key);
						if(eregi('%',$value))
						{
							# do any data conversion for this field (date, encrypt, etc...)
							if(isset($this->field["$field"]["convert"])  && $this->field["$field"]["convert"] != 'array')
							{
								$value = $validate->convert($field, $value, $this->field["$field"]["convert"]);
							}

							$where_list .= " WHERE ".AGILE_DB_PREFIX."invoice.".$field . " LIKE " . $db->qstr($value, get_magic_quotes_gpc());
							$i++;
						}
						else
						{
							# check if array
							if(is_array($value))
							{
								for($i_arr=0; $i_arr < count($value); $i_arr++)
								{
									if($value["$i_arr"] != '')
									{
										# determine any field options (=, >, <, etc...)
										$f_opt = '=';
										$pat_field = $this->module.'_'.$field;
										$VAR['field_option']["$pat_field"]["$i_arr"];
										if(isset($VAR['field_option']["$pat_field"]["$i_arr"]))
										{
											$f_opt = $VAR['field_option']["$pat_field"]["$i_arr"];
											# error checking, safety precaution
											if($f_opt != '='  && $f_opt != '>'  && $f_opt != '<' && $f_opt != '>=' && $f_opt != '<=' && $f_opt != '!=')
											$f_opt = '=';
										}

										# do any data conversion for this field (date, encrypt, etc...)
										if(isset($this->field["$field"]["convert"]) && $this->field["$field"]["convert"] != 'array')
										{
											$value["$i_arr"] = $validate->convert($field, $value["$i_arr"], $this->field["$field"]["convert"]);
										}


										if($i_arr == 0)
										{
											$where_list .= " WHERE ".AGILE_DB_PREFIX."invoice.".$field . " $f_opt " . $db->qstr($value["$i_arr"], get_magic_quotes_gpc());
											$i++;
										}
										else
										{
											$where_list .= " AND ".AGILE_DB_PREFIX."invoice.".$field . " $f_opt " . $db->qstr($value["$i_arr"], get_magic_quotes_gpc());
											$i++;
										}
									}
								}
							}
							else
							{
								$where_list .= " WHERE ".AGILE_DB_PREFIX."invoice.".$field . " = " . $db->qstr($value, get_magic_quotes_gpc());
								$i++;
							}
						}
					}
				}
			}
			else
			{
				if($value != '')
				{
					$pat = "^" . $this->module . "_";
					if(eregi($pat, $key))
					{
						$field = eregi_replace($pat,"",$key);
						if(eregi('%',$value))
						{
							# do any data conversion for this field (date, encrypt, etc...)
							if(isset($this->field["$field"]["convert"])  && $this->field["$field"]["convert"] != 'array')
							{
								$value = $validate->convert($field, $value, $this->field["$field"]["convert"]);
							}

							$where_list .= " AND ".AGILE_DB_PREFIX."invoice.".$field . " LIKE " . $db->qstr($value, get_magic_quotes_gpc());
							$i++;
						}
						else
						{
							# check if array
							if(is_array($value))
							{
								for($i_arr=0; $i_arr < count($value); $i_arr++)
								{
									if($value["$i_arr"] != '')
									{
										# determine any field options (=, >, <, etc...)
										$f_opt = '=';
										$pat_field = $this->module.'_'.$field;
										if(isset($VAR['field_option']["$pat_field"]["$i_arr"]))
										{
											$f_opt = $VAR['field_option']["$pat_field"]["$i_arr"];

											# error checking, safety precaution
											if($f_opt != '='  && $f_opt != '>'  && $f_opt != '<' && $f_opt != '>=' && $f_opt != '<=' && $f_opt != '!=')
											$f_opt = '=';
										}

										# do any data conversion for this field (date, encrypt, etc...)
										if(isset($this->field["$field"]["convert"]) && $this->field["$field"]["convert"] != 'array')
										{
											$value["$i_arr"] = $validate->convert($field, $value["$i_arr"], $this->field["$field"]["convert"]);
										}

										$where_list .= " AND ".AGILE_DB_PREFIX."invoice.". $field . " $f_opt " . $db->qstr($value["$i_arr"], get_magic_quotes_gpc());
										$i++;
									}
								}
							}
							else
							{
								$where_list .=  " AND ".AGILE_DB_PREFIX."invoice.". $field . " = ". $db->qstr($value, get_magic_quotes_gpc());
								$i++;
							}
						}
					}
				}
			}
		}



		# Code for attribute searches:
		if(!empty($VAR['join_product_id']) && !empty($VAR['item_attributes']))
		{
			$attr_arr = $VAR['item_attributes'];
			for($ati=0; $ati<count($attr_arr); $ati++)
			{
				if(!empty($attr_arr[$ati]['0']))
				{
					if($where_list == '')
					$where_list .= ' WHERE ';
					else
					$where_list .= ' AND ';
					$where_list .= AGILE_DB_PREFIX."invoice_item.product_attr LIKE " .
					$db->qstr("%{$attr_arr[$ati]['0']}=={$attr_arr[$ati]['1']}%");
				}
			}
		}

		# get limit type
		if(isset($VAR['limit']))
		{
			$limit = $VAR['limit'];
		}
		else
		{
			$limit = $this->limit;
		}

		# get order by
		if(isset($VAR['order_by']))
		{
			$order_by = $VAR['order_by'];
		}
		else
		{
			$order_by = $this->order_by;
		}


		## SELECT FROM
		$p 		=		AGILE_DB_PREFIX;
		$q 		= 		"SELECT DISTINCT {$p}invoice.id FROM ".AGILE_DB_PREFIX."invoice ";
		$q_save = 		"SELECT DISTINCT %%fieldList%%,{$p}invoice.id FROM {$p}invoice ";

		## LEFT JOIN
		if( !empty($VAR['join_product_id']) || !empty($VAR['join_service_id']) ||
		!empty($VAR['join_domain_name']) || !empty($VAR['join_domain_tld']) ||
		!empty($VAR['join_memo_text']) )
		{
			# JOIN ON PRODUCT DETAILS:
			if(!empty($VAR['join_product_id']) || !empty($VAR['join_service_id']) || !empty($VAR['join_domain_name']) || !empty($VAR['join_domain_tld']))
			{
				$q .= 		" LEFT JOIN {$p}invoice_item ON {$p}invoice_item.invoice_id = {$p}invoice.id";
				$q_save .= 	" LEFT JOIN {$p}invoice_item ON {$p}invoice_item.invoice_id = {$p}invoice.id";

				if($where_list == '') {
					$q .= 		" WHERE {$p}invoice_item.site_id  = " . $db->qstr(DEFAULT_SITE);
					$q_save .= 	" WHERE {$p}invoice_item.site_id  = " . $db->qstr(DEFAULT_SITE);
				} else {
					$q .= 		$where_list ." AND {$p}invoice_item.site_id  = " . $db->qstr(DEFAULT_SITE);
					$q_save .= 	$where_list ." AND {$p}invoice_item.site_id  = " . $db->qstr(DEFAULT_SITE);
				}

				# AND (invoice_item.product_id)
				if(!empty($VAR['join_product_id'])) {
					$q .= 		" AND {$p}invoice_item.product_id = " . $db->qstr($VAR['join_product_id']);
					$q_save .=	" AND {$p}invoice_item.product_id = " . $db->qstr($VAR['join_product_id']);
				}

				# AND (invoice_item.service_id)
				if(!empty($VAR['join_service_id'])) {
					$q .= 		" AND {$p}invoice_item.service_id = " . $db->qstr($VAR['join_service_id']);
					$q_save .=	" AND {$p}invoice_item.service_id = " . $db->qstr($VAR['join_service_id']);
				}

				# AND (invoice_item.domain_name)
				if(!empty($VAR['join_domain_name'])) {
					if(!ereg('%',$VAR['join_domain_name']) ) $qtype = ' = '; else $qtype = ' LIKE ';
					$q .= 		" AND {$p}invoice_item.domain_name $qtype " . $db->qstr($VAR['join_domain_name']);
					$q_save .=	" AND {$p}invoice_item.domain_name $qtype " . $db->qstr($VAR['join_domain_name']);

				}

				# AND (invoice_item.domain_tld)
				if(!empty($VAR['join_domain_tld'])) {
					if(!ereg('%',$VAR['join_domain_tld']) ) $qtype = ' = '; else $qtype = ' LIKE ';
					$q .= 		" AND {$p}invoice_item.domain_tld $qtype " . $db->qstr($VAR['join_domain_tld']);
					$q_save .=	" AND {$p}invoice_item.domain_tld $qtype " . $db->qstr($VAR['join_domain_tld']);
				}
			}

			# JOIN ON MEMO TEXT:
			if(!empty($VAR['join_memo_text']))
			{
				$q .= 		" LEFT JOIN {$p}invoice_memo ON {$p}invoice_memo.invoice_id = {$p}invoice.id";
				$q_save .= 	" LEFT JOIN {$p}invoice_memo ON {$p}invoice_memo.invoice_id = {$p}invoice.id";

				if($where_list == '') {
					$q .= 		" WHERE {$p}invoice_memo.site_id  = " . $db->qstr(DEFAULT_SITE);
					$q_save .= 	" WHERE {$p}invoice_memo.site_id  = " . $db->qstr(DEFAULT_SITE);
				} else {
					$q .= 		$where_list ." AND {$p}invoice_memo.site_id  = " . $db->qstr(DEFAULT_SITE);
					$q_save .= 	$where_list ." AND {$p}invoice_memo.site_id  = " . $db->qstr(DEFAULT_SITE);
				}

				$q .= 		" AND {$p}invoice_memo.memo LIKE " . $db->qstr('%'. $VAR['join_memo_text'] .'%');
				$q_save .=	" AND {$p}invoice_memo.memo LIKE " . $db->qstr('%'. $VAR['join_memo_text'] .'%');
			}

			$q .= " AND {$p}invoice.site_id = ". DEFAULT_SITE;
			$q_save .=  ' AND ';
		}
		else
		{
			if($where_list == '') {
				$q .= "WHERE {$p}invoice.site_id = ". DEFAULT_SITE;
				$q_save .=  ' WHERE ';
			}
			else
			{
				$q .= $where_list . " AND {$p}invoice.site_id = ". DEFAULT_SITE;
				$q_save .= $where_list . ' AND ';
			}
		}


		///////////////// debug
		#echo $q;
		#exit;

		# run the database query
		$result = $db->Execute($q);

		# error reporting
		if ($result === false)
		{
			global $C_debug;
			$C_debug->error('invoice.inc.php','search', $db->ErrorMsg());
			return false;
		}

		# get the result count:
		$results = $result->RecordCount();

		# get the first record id:
		if($results == 1)  $record_id = $result->fields['id'];

		# define the DB vars as a Smarty accessible block
		global $smarty;

		# Create the definition for fast-forwarding to a single record:
		if ($results == 1 && !isset($this->fast_forward))
		{
			$smarty->assign('record_id', $record_id);
		}

		# create the search record:
		if($results > 0)
		{
			# create the search record
			include_once(PATH_CORE   . 'search.inc.php');
			$search = new CORE_search;
			$arr['module'] 	= $this->module;
			$arr['sql']		= $q_save;
			$arr['limit']  	= $limit;
			$arr['order_by']= $order_by;
			$arr['results']	= $results;
			$search->add($arr);

			# define the search id and other parameters for Smarty
			$smarty->assign('search_id', $search->id);

			# page:
			$smarty->assign('page', '1');

			# limit:
			$smarty->assign('limit', $limit);

			# order_by:
			$smarty->assign('order_by', $order_by);
		}

		# define the result count
		$smarty->assign('results', $results);
	}

	/** SEARCH SHOW
        */
	function search_show($VAR)
	{
		$this->invoice_construct();
		$type = "search";
		$this->method["$type"] = split(",", $this->method["$type"]);

		# set the field list for this method:
		$arr = $this->method[$type];
		$field_list = '';
		$i=0;
		while (list ($key, $value) = each ($arr))
		{
			if($i == 0)
			{
				$field_var =  $this->table . '_' . $value;
				$field_list .= AGILE_DB_PREFIX . "invoice" . "." . $value;

				// determine if this record is linked to another table/field
				if($this->field[$value]["asso_table"] != "")
				{
					$this->linked[] = array('field' => $value, 'link_table' => $this->field[$value]["asso_table"], 'link_field' => $this->field[$value]["asso_field"]);
				}
			}
			else
			{
				$field_var =  $this->table . '_' . $value;
				$field_list .= "," . AGILE_DB_PREFIX . "invoice" . "." . $value;

				// determine if this record is linked to another table/field
				if($this->field[$value]["asso_table"] != "")
				{
					$this->linked[] = array('field' => $value, 'link_table' => $this->field[$value]["asso_table"], 'link_field' => $this->field[$value]["asso_field"]);
				}
			}
			$i++;
		}


		# get the search details:
		if(isset($VAR['search_id'])) {
			include_once(PATH_CORE   . 'search.inc.php');
			$search = new CORE_search;
			$search->get($VAR['search_id']);
		} else {
			# invalid search!
			echo '<BR> The search terms submitted were invalid!';       # translate... # alert

			if(isset($this->trigger["$type"])) {
				include_once(PATH_CORE   . 'trigger.inc.php');
				$trigger    = new CORE_trigger;
				$trigger->trigger($this->trigger["$type"], 0, $VAR);
			}
		}

		# get the sort order details:
		if(isset($VAR['order_by']) && $VAR['order_by'] != "") {
			$order_by = ' ORDER BY ' . $VAR['order_by'];
			$smarty_order =  $VAR['order_by'];
		} else  {
			$order_by = ' ORDER BY ' . $this->order_by;
			$smarty_order =  $search->order_by;
		}


		# determine the sort order
		if(isset($VAR['desc'])) {
			$order_by .= ' DESC';
			$smarty_sort = 'desc=';
		} else if(isset($VAR['asc']))  {
			$order_by .= ' ASC';
			$smarty_sort = 'asc=';
		} else {
			if (!eregi('date',$smarty_order)) {
				$order_by .= ' ASC';
				$smarty_sort = 'asc=';
			} else {
				$order_by .= ' DESC';
				$smarty_sort = 'desc=';
			}
		}


		# determine the offset & limit
		$current_page=1;
		$offset=-1;
		if (!empty($VAR['page'])) $current_page = $VAR['page'];
		if (empty($search->limit)) $search->limit=25; 
        if($current_page>1) $offset = (($current_page * $search->limit) - $search->limit);            
		 	  
		# generate the full query
		$db = &DB();
		$q = eregi_replace("%%fieldList%%", $field_list, $search->sql);
		$q = eregi_replace("%%tableList%%", AGILE_DB_PREFIX.$construct->table, $q);
		$q = eregi_replace("%%whereList%%", "", $q);
		$q .= " ".AGILE_DB_PREFIX . "invoice.site_id = '" . DEFAULT_SITE . "'";
		$q .= $order_by;

		//////////////////
		#echo "<BR> $q <BR>";

		$result = $db->SelectLimit($q, $search->limit, $offset);

		# error reporting
		if ($result === false)
		{
			global $C_debug;
			$C_debug->error('invoice.inc.php','search', $db->ErrorMsg());

			if(isset($this->trigger["$type"]))
			{
				include_once(PATH_CORE   . 'trigger.inc.php');
				$trigger    = new CORE_trigger;
				$trigger->trigger($this->trigger["$type"], 0, $VAR);
			}
			return;
		}


		# put the results into a smarty accessable array
		$i=0;
		$class_name = TRUE;
		while (!$result->EOF) {
			$smart[$i] = $result->fields;
			$amount += $result->fields['total_amt'];
			if($class_name)
			{
				$smart[$i]['_C'] = 'row1';
				$class_name = FALSE;
			} else {
				$smart[$i]['_C'] = 'row2';
				$class_name = TRUE;
			}

			$result->MoveNext();
			$i++;
		}


		# get any linked fields
		if($i > 0)
		{
			$db_join = new CORE_database;
			$this->result = $db_join->join_fields($smart, $this->linked);
		}
		else
		{
			$this->result = $smart;
		}

		# get the result count:
		$results = $result->RecordCount();

		# define the DB vars as a Smarty accessible block
		global $smarty;

		# define the results
		$smarty->assign($this->table, $this->result);
		$smarty->assign('page',		$VAR['page']);
		$smarty->assign('order',	$smarty_order);
		$smarty->assign('sort',		$smarty_sort);
		$smarty->assign('limit',	$search->limit);
		$smarty->assign('search_id',$search->id);
		$smarty->assign('results', 	$search->results);

		global $C_list;
		$smarty->assign('total_amount', $C_list->format_currency($amount,DEFAULT_CURRENCY));

		# get the total pages for this search:
		if(empty($search->limit))
		$this->pages = 1;
		else
		$this->pages = intval($search->results / $search->limit);
		if ($search->results % $search->limit) $this->pages++;

		# total pages
		$smarty->assign('pages', 	$this->pages);

		# current page
		$smarty->assign('page', 	$current_page);
		$page_arr = '';
		for($i=0; $i <= $this->pages; $i++)
		{
			if ($this->page != $i) 	$page_arr[] = $i;
		}

		# page array for menu
		$smarty->assign('page_arr',	$page_arr);
	}

	/** USER SEARCH
        */ 
	function user_search($VAR)
	{
		if(!SESS_LOGGED) return false; 
		$VAR['invoice_account_id'] = SESS_ACCOUNT;
		$this->invoice_construct();
		$type = "search";
		$this->method["$type"] = split(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->search($VAR, $this, $type);
	}

	/** USER SEARCH SHOW
        */
	function user_search_show($VAR)
	{
		if(!SESS_LOGGED) return false;
		$this->invoice_construct();
		$type = "search";
		$this->method["$type"] = split(",", $this->method["$type"]);
		$db = new CORE_database;
		$db->search_show($VAR, $this, $type);
	}

	/** USER VIEW
        */
	function user_view($VAR)
	{
		global $C_auth;
		if(!SESS_LOGGED) return false;

		// verify the account_id for this order is the SESS_ACCOUNT
		if ( $C_auth->auth_method_by_name('invoice','view') == false)
		{
			$id = split(',',$VAR['id']);
			$db = &DB();
			$q = "SELECT account_id FROM ".AGILE_DB_PREFIX."invoice WHERE
	        			id = ".$db->qstr($id[0])." AND
	        			site_id = ".$db->qstr(DEFAULT_SITE);
			$rs = $db->Execute($q);
			if ($rs === false) {
				global $C_debug;
				$C_debug->error('invoice.inc.php','user_view', $db->ErrorMsg());
				return false;
			}
			if ($rs->fields['account_id'] != SESS_ACCOUNT) return false;
		}
		$this->view($VAR, $this);
	}

	/** SEARCH EXPORT
        */    	
	function search_export($VAR)
	{
		$this->invoice_construct();

		# require the export class
		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"] = split(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_excel($VAR, $this, $type);
		}

		else if ($VAR["format"] == "pdf")
		{
			$type = "export_pdf";
			$this->method["$type"] = split(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->pdf_invoice($VAR, $this, $type);
		}

		else if ($VAR["format"] == "xml")
		{
			$type = "export_xml";
			$this->method["$type"] = split(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_xml($VAR, $this, $type);
		}

		else if ($VAR["format"] == "csv")
		{
			$type = "export_csv";
			$this->method["$type"] = split(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_csv($VAR, $this, $type);
		}

		else if ($VAR["format"] == "tab")
		{
			$type = "export_tab";
			$this->method["$type"] = split(",", $this->method["$type"]);
			$export = new CORE_export;
			$export->search_tab($VAR, $this, $type);
		}
	}

	function invoice_construct()
	{
		$this->module = "invoice";
		$this->xml_construct = PATH_MODULES . "" . $this->module . "/" . $this->module . "_construct.xml";
		include_once(PATH_CORE.'xml.inc.php');
		$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"];
	}
} 
?>