<?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"]; } } ?>