* @copyright 2009 Deon George * @link http://osb.leenooks.net * * @link http://www.agileco.com/ * @copyright 2004-2008 Agileco, LLC. * @license http://www.agileco.com/agilebill/license1-4.txt * @author Tony Landis * @package AgileBill * @subpackage Module:Charge */ /** * The main AgileBill Charge Class * * @package AgileBill * @subpackage Module:Charge */ class charge extends OSB_module { var $xmlrpc=false; function sweep_daily() { $this->sweep('0'); } function sweep_weekly() { $this->sweep('1'); } function sweep_monthly() { $this->sweep('2'); } function sweep_quarterly() { $this->sweep('3'); } function sweep_semi_annually() { $this->sweep('4'); } function sweep_annually() { $this->sweep('5'); } function sweep($type) { include_once(PATH_MODULES.'account_billing/account_billing.inc.php'); $account_billing = new account_billing; include_once(PATH_MODULES.'tax/tax.inc.php'); $taxObj = new tax; include_once(PATH_MODULES.'discount/discount.inc.php'); $db = &DB(); $sql = "SELECT DISTINCT ".AGILE_DB_PREFIX."charge.id, ".AGILE_DB_PREFIX."charge.account_id, ".AGILE_DB_PREFIX."charge.service_id, ".AGILE_DB_PREFIX."charge.amount, ".AGILE_DB_PREFIX."charge.taxable, ".AGILE_DB_PREFIX."charge.attributes, ".AGILE_DB_PREFIX."charge.quantity, ".AGILE_DB_PREFIX."charge.product_id, ".AGILE_DB_PREFIX."charge.description, ".AGILE_DB_PREFIX."account.affiliate_id, ".AGILE_DB_PREFIX."account.reseller_id, ".AGILE_DB_PREFIX."account.country_id, ".AGILE_DB_PREFIX."account.currency_id, ".AGILE_DB_PREFIX."account.state FROM ".AGILE_DB_PREFIX."charge LEFT JOIN ".AGILE_DB_PREFIX."account ON ".AGILE_DB_PREFIX."account.id = " . AGILE_DB_PREFIX."charge.account_id WHERE ".AGILE_DB_PREFIX."charge.site_id = " . $db->qstr(DEFAULT_SITE) . " AND ".AGILE_DB_PREFIX."account.site_id = " . $db->qstr(DEFAULT_SITE) . " AND ".AGILE_DB_PREFIX."charge.status = " . $db->qstr('0') ." AND ".AGILE_DB_PREFIX."charge.sweep_type = " . $db->qstr($type) ." ORDER BY ".AGILE_DB_PREFIX."charge.account_id"; $rs = $db->Execute($sql); if ($rs === false) { global $C_debug; $C_debug->error('charge.inc.php','charge :: sweep()', $db->ErrorMsg(). "\r\n\r\n". $sql); return false; } $account_id = false; $invoice_id = false; $i = false; $i_total = false; $invoice_count = 0; $sweep_count = 0; while(!$rs->EOF) { if( $rs->fields['account_id'] != $account_id ) { $account_id = $rs->fields['account_id']; $i=0; $i_total = $this->count_account_charges($account_id, $rs->CurrentRow(), $rs); $sub_total = 0; $taxable_amount = 0; $this_discount_total = 0; $tax_amt = 0; $discount_amt = 0; # Start a new transaction $trans = &DB(); $trans->StartTrans(); # Start a new invoice $invoice_id = $db->GenID(AGILE_DB_PREFIX . 'invoice_id'); # 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; # get parent invoice id if service specified (for discount checking) $parent_invoice_id = false; if($rs->fields['service_id']) { $parentinv = $db->Execute(sqlSelect($db,"service","invoice_id","id={$rs->fields['service_id']}")); if($parentinv && $parentinv->RecordCount()) { $parent_invoice_id = $parentinv->fields['invoice_id']; } } # get available discounts to this account/service $discountObj->available_discounts($account_id, 1, $parent_invoice_id); } ########################### ##### LINE ITEM ACTIONS ### ########################### if( !empty($account_id) ) { ### Get the line item id $invoice_item_id = $db->GenID(AGILE_DB_PREFIX . 'invoice_item_id'); ### Set the invoice item details: $product_id = $rs->fields['product_id']; if(!empty($product_id) && empty($this->product["$product_id"])) { $sql = "SELECT sku FROM ".AGILE_DB_PREFIX."product WHERE id = " . $db->qstr($product_id) . " AND site_id = " . $db->qstr(DEFAULT_SITE); $prod = $db->Execute($sql); if($prod->RecordCount() > 0) { $sku = $prod->fields['sku']; $this->product["$product_id"] = $sku; $product_attr = ''; if(!empty($rs->fields['description'])) $product_attr = "Description=={$rs->fields['description']}\r\n"; $product_attr .= $rs->fields['attributes']; } else { $sku = $rs->fields['description']; $this->product["$product_id"] = $sku; $product_attr = $rs->fields['attributes']; } } elseif (!empty($this->product["$product_id"])) { $sku = $this->product["$product_id"]; $product_attr = $rs->fields['attributes']; } else { $sku = $rs->fields['description']; $product_attr = $rs->fields['attributes']; } $quantity = $rs->fields['quantity']; $price_base = $rs->fields['amount']; $item_total_amt = ($price_base * $quantity); // Calculate any recurring discounts for this account $item_discount_amt = $discountObj->calc_all_discounts(1, $invoice_item_id, $rs->fields['product_id'], $item_total_amt, $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 $item_tax_amt=0; if($rs->fields['taxable']) { $item_tax_arr = $taxObj->calculate($item_total_amt, $rs->fields['country_id'], $rs->fields['state']); if(is_array($item_tax_arr)) foreach($item_tax_arr as $tx) $item_tax_amt += $tx['rate']; $tax_amt += $item_tax_amt; } ### Add line item to new invoice $sql = "INSERT INTO ".AGILE_DB_PREFIX."invoice_item SET id = ".$db->qstr( $invoice_item_id ) .", site_id = ".$db->qstr( DEFAULT_SITE ).", invoice_id = ".$db->qstr( $invoice_id ).", account_id = ".$db->qstr( $account_id ).", date_orig = ".$db->qstr( time() ).", product_id = ".$db->qstr( $product_id ).", sku = ".$db->qstr( $sku ).", quantity = ".$db->qstr( $quantity ).", item_type = ".$db->qstr( '0' ).", product_attr = ".$db->qstr( $product_attr ).", price_type = ".$db->qstr( '0' ).", price_base = ".$db->qstr( $price_base ).", price_setup = ".$db->qstr( 0 ) .", tax_amt = ".$db->qstr($item_tax_amt) . ", total_amt = ".$db->qstr($item_total_amt) . ", discount_amt = ".$db->qstr($item_discount_amt); $trans->Execute($sql); # Insert tax records $taxObj->invoice_item($invoice_id, $invoice_item_id, $account_id, @$item_tax_arr); # Insert discount records $discountObj->invoice_item($invoice_id, $invoice_item_id, $account_id); ### Update this charge status to billed $sql = "UPDATE ".AGILE_DB_PREFIX."charge SET status = ".$db->qstr( '1' ) ." WHERE site_id = ".$db->qstr( DEFAULT_SITE )." AND id = ".$db->qstr( $rs->fields['id'] ) ; $trans->Execute($sql); $i++; $sweep_count++; } ####################### ### INVOICE ACTIONS ### ####################### if($i_total == $i || $i == $rs->RecordCount()) { if( $invoice_id ) { ### Get the most recent billing id for this client: if(!isset($billing_id["$account_id"])) { $billing_arr = $account_billing->default_billing($account_id); $billing_id["$account_id"] = $billing_arr['billing_id']; $checkout_plugin_id["$account_id"] = $billing_arr['checkout_plugin_id']; } ### Affiliate & Reseller info: $affiliate_id = $rs->fields['affiliate_id']; $reseller_id = $rs->fields['reseller_id']; $actual_billed_currency_id = $rs->fields['currency_id']; # calculate any taxes @$total = $sub_total + $tax_amt; if($total <= 0) { $process_status = 1; $billing_status = 1; } else { $process_status = 0; $billing_status = 0; } ### Generate the invoice insert SQL: $sql = "INSERT INTO ".AGILE_DB_PREFIX."invoice SET id = ".$db->qstr($invoice_id).", site_id = ".$db->qstr(DEFAULT_SITE).", date_orig = ".$db->qstr(time()).", date_last = ".$db->qstr(time()).", process_status = ".$db->qstr($process_status).", billing_status = ".$db->qstr($billing_status).", print_status = ".$db->qstr('0').", account_id = ".$db->qstr($account_id).", account_billing_id = ".$db->qstr($billing_id["$account_id"]).", affiliate_id = ".$db->qstr($affiliate_id).", reseller_id = ".$db->qstr($reseller_id).", checkout_plugin_id = ".$db->qstr($checkout_plugin_id["$account_id"]).", tax_amt = ".$db->qstr($tax_amt).", discount_amt = ".$db->qstr($discount_amt).", actual_billed_currency_id = ".$db->qstr($actual_billed_currency_id).", actual_billed_amt = ".$db->qstr('0').", billed_currency_id = ".$db->qstr(DEFAULT_CURRENCY).", billed_amt = ".$db->qstr('0').", total_amt = ".$db->qstr($total).", notice_count = ".$db->qstr('0').", notice_max = ".$db->qstr(MAX_BILLING_NOTICE).", notice_next_date = ".$db->qstr(time()).", grace_period = ".$db->qstr(GRACE_PERIOD).", due_date = ".$db->qstr(time()); $trans->Execute($sql); ### Close this transaction $trans->CompleteTrans(); $i_total = false; $i = false; $account_id = false; $invoice_id = false; $discount = false; $cookie = false; $invoice_count++; } } $rs->MoveNext(); } global $C_debug; $C_debug->alert("Swept $sweep_count Charge(s) into $invoice_count Invoice(s)."); return true; } ### Get total charges for an account function count_account_charges($account, $start_pos, &$rs) { $rs->Move($start_pos); $i = 0; while(!$rs->EOF) { if($rs->fields['account_id'] != $account) { $rs->Move($start_pos); return $i; } $i++; $rs->MoveNext(); } $rs->Move($start_pos); return $i; } ############################## ## API ## ############################## function api($VAR) { $db = &DB(); # amount if(@$VAR['amount'] <= 0) { return $this->api_return(0,'','Invalid value supplied for the \'amount\' parameter, must be greater than 0'); } else { $amount = $VAR['amount']; } # sweep_type if(@$VAR['sweep_type'] <= 6) { $sweep_type = $VAR['sweep_type']; } else { return $this->api_return(0,'','Invalid value supplied for the \'sweep_type\' parameter, must be 0-6'); } # account_id OR service_id if(empty($VAR['account_id']) && empty($VAR['service_id'])) { return $this->api_return(0,'','Either the \'account_id\' or \'service_id\' parameter must be provided'); } else { # check the account id if(!empty($VAR['account_id'])) { $sql = "SELECT * FROM ".AGILE_DB_PREFIX."account WHERE id = " . $db->qstr($VAR['account_id']) . " OR username = " . $db->qstr($VAR['account_id']) . " AND site_id = " . $db->qstr(DEFAULT_SITE); $rs = $db->Execute($sql); if ($rs === false) { global $C_debug; $C_debug->error('charge.inc.php','charge :: api()', $db->ErrorMsg(). "\r\n\r\n". $sql); } if($rs->RecordCount() == 1) { $account_id = $rs->fields['id']; } else { return $this->api_return(0,'','The \'account_id\' value provided does not exist'); } } # check the service id elseif(!empty($VAR['service_id'])) { $sql = "SELECT id,account_id FROM ".AGILE_DB_PREFIX."service WHERE site_id = " . $db->qstr(DEFAULT_SITE) . " AND id = " . $db->qstr($VAR['service_id']); $rs = $db->Execute($sql); if ($rs === false) { global $C_debug; $C_debug->error('charge.inc.php','charge :: api()', $db->ErrorMsg(). "\r\n\r\n". $sql); } if($rs->RecordCount() == 1) { $service_id = $VAR['service_id']; $account_id = $rs->fields['account_id']; } else { return $this->api_return(0,'','The \'service_id\' value provided does not exist'); } } } # taxable if(empty($VAR['taxable'])) $taxable = 0; else $taxable = $VAR['taxable']; # attributes if(!empty($VAR['attributes'])) { @$attributes = ereg_replace("@@", "\r\n", $VAR['attributes']); @$attributes = ereg_replace("--", "==", $attributes); } else { $attributes = false; } # quantity if(empty($VAR['quantity'])) $quantity = 1; else $quantity = $VAR['quantity']; # product id if(empty($VAR['product_id'])) { $product_id = false; } else { $product_id = $VAR['product_id']; } # description if(empty($VAR['description'])) { $description = false; } else { $description = $VAR['description']; } /* Start: SQL Insert Statement */ $sql = "SELECT * FROM ".AGILE_DB_PREFIX."charge WHERE id = -1"; $rs = $db->Execute($sql); $id = $db->GenID(AGILE_DB_PREFIX . 'charge_id'); $insert = Array ( 'id' => $id, 'site_id' => DEFAULT_SITE, 'date_orig' => time(), 'status' => 0, 'sweep_type' => $sweep_type, 'account_id' => @$account_id, 'service_id' => @$service_id, 'product_id' => @$product_id, 'amount' => $amount, 'quantity' => $quantity, 'taxable' => $taxable, 'attributes' => $attributes, 'description' => $description ); $sql = $db->GetInsertSQL($rs, $insert); $result = $db->Execute($sql); if ($result === false) { global $C_debug; $C_debug->error('charge.inc.php','charge :: api()', $db->ErrorMsg(). "\r\n\r\n". $sql); return $this->api_return(0,'','The SQL insert failed!'); } else { return $this->api_return(1,$id,''); } return true; } function api_return($status=0,$id='',$error='') { if (! $this->xmlrpc) { echo "status=={$status}++charge_id={$id}++error=={$error}"; } else { $arr = array('status'=>$status, 'charge_id'=>$id, 'error'=> $error); return $arr; } } /** * Add a record */ function add($VAR) { if (! empty($VAR['attributes'])) { $attr = ''; for ($i=0; $iadd($VAR); } /** * Import records */ function import($VAR) { include_once(PATH_CORE.'import.inc.php'); $import = new CORE_import; if (! empty($VAR['confirm'])) $import->do_import($VAR,$this); else $import->prepare_import($VAR,$this); } } ?>