* @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:Discount */ /** * The main AgileBill Discount Class * * @package AgileBill * @subpackage Module:Discount */ class discount extends OSB_module { # Array of available discounts private $discounts = array(); # The cumulative amount of the current discount private $discount_total = 0; # The array of discounts applied to the line_items public $discount_arr = array(); # Array that contains list of discount plugins in the /plugins/discount/ directory to load private $plugins = array(); /** * Load a specific discount plugin and validate the results * * @param string $plugin The plugin name * @param string $discount The discount code * @return bool */ private function plugin_validate($plugin,$code) { $plugin_file = sprintf('%sdiscount/%s.php',PATH_PLUGINS,$plugin); if (is_file($plugin_file)) { include_once($plugin_file); eval('$plg = new plgn_discount_'. $plugin .';'); if (is_object($plg) && is_callable(array($plg,'validate'))) { $plg->discount = $code; return $plg->validate($code); } } return false; } /** * Add a discount code to our session record, it'll be used/evaluated later to calculate the discounts. * * @param array $VAR * @return bool * @uses session */ public function add_cart_discount($VAR) { global $C_debug, $C_translate, $smarty; $db = &DB(); # Validate input if (empty($VAR['discount'])) { $C_debug->alert(_('The discount code was entered incorrectly or is not valid.')); return false; } $discount_code = $VAR['discount']; # Check the supplied discount $check = $this->sql_GetRecords( array('where'=>sprintf('(date_start IS NULL OR date_start=0 OR date_start>%s) AND date_expire<=%s AND name=::%s::',time(),time(),$discount_code))); # Local check failed, attempt any discount plugins if (! $check || count($check)>1 || $check[0]['status'] != '1') { $plg = false; foreach ($this->plugins as $plugin) { if ($discount_code = $this->plugin_validate($plugin,$discount_code)) { $plg = true; break; } } # No plugins returned true... if (! $plg) { $C_debug->alert(_('The discount code was entered incorrectly or is not valid.')); return false; } } # Get existing discounts: require_once(PATH_MODULES.'session/session.inc.php'); $seo = new session(SESS); $arr = array(); if (is_string($seo->getRecordAttr('discounts'))) $arr = unserialize($seo->getRecordAttr('discounts')); # Check for duplicates if (is_array($arr)) foreach ($arr as $key=>$discount) if ($discount == $discount_code) return true; # Update session data array_push($arr,$discount_code); $seo->setRecordAttr('discounts',serialize($arr)); $seo->sql_SaveRecord(true,true); return true; } /** * Commit current discounts to the database (call after creating an invoice_item record) */ public function invoice_item($invoice_id,$invoice_item_id,$account_id,$discount_arr=false) { if ($discount_arr && is_array($discount_arr)) $this->discount_arr = $discount_arr; if (is_array($this->discount_arr)) { $db =& DB(); foreach ($this->discount_arr as $dsc) $db->Execute(sqlInsert($db,'invoice_item_discount', array('invoice_id'=>$invoice_id,'account_id'=>$account_id,'invoice_item_id'=>$invoice_item_id,'discount'=>$dsc['discount'],'amount'=>$dsc['amount']))); } } /** * Get the avialable discounts for an account, session, or service * * @param $account * @param $type 0=initial order, 1=recurring charge */ private function available_discounts($account,$type=0,$invoice=false) { $db =& DB(); # Get account specific discounts if ($type) $sqltype = 'recurr_status=1'; else $sqltype = 'new_status=1'; foreach ($this->sql_GetRecords(array('where'=>sprintf('avail_account_id=%s AND status=1 AND %s',$account,$sqltype))) as $record) $this->discounts[$record['name']] = $record; # Get session discounts from cart if ($type == 0) { $rs = $db->Execute( sqlSelect('session','discounts',array('where'=>sprintf('(account_id=%s OR id=::%s::) AND discounts!=:::: AND discounts IS NOT NULL',$account,SESS)))); if ($rs && $rs->RecordCount()) { $arr = unserialize($rs->fields['discounts']); if (is_array($arr)) foreach ($arr as $discount) if (! empty($discount)) foreach ($this->sql_GetRecords(array('where'=>sprintf('name=%s AND status=1',$discount))) as $record) $this->discounts[$record['name']] = $record; } # Get recurring discounts } elseif ($type==1 && $invoice) { $rs = $db->Execute( sqlSelect($db,array('invoice_item_discount','discount'),'B.*',sprintf('A.invoice_id=%s AND A.discount=B.name AND status=1 AND %s',$invoice,$sqltype))); if ($rs && $rs->RecordCount()) { while (! $rs->EOF) { $this->discounts[$rs->fields['name']] = $rs->fields; $rs->MoveNext(); } } } } /** * Calculate all applicable discounts for the current line item * * @param $type bool 0=initial product, 1=recurring product, 2=initial_domain, 3=recurring domain * @param $invoice_item_id int The invoice item id for the discount * @param $product_id int The product ID if type=0,1 or The TLD if type = 2,3 * @param $account_id int The account ID * @param $invoice_amt float The cumulative invoice amount * @param $prod_amt float The product price before any discounts */ public function calc_all_discounts($type=0,$pid,$pamt,$aid,$iamt) { # Populate our discounts $this->available_discounts($aid,$type,$iamt); if (! count($this->discounts)) return array(); foreach ($this->discounts as $did => $discount) { $amt = $this->calc_item_discount($type,$did,$pid,$aid,$iamt,$pamt); if ($amt > 0) array_push($this->discount_arr,array('discount'=>$discount['name'],'amount'=>$amt)); } return $this->discount_arr; } /** * Calculate Recurring Discount * * @param $type bool 0=initial product, 1=recurring product, 2=initial_domain, 3=recurring domain * @param $did string The Discount ID (must be set to $this->discount["$discounts"] containing the fields of the discount) * @param $pid int The product ID if type=0,1 or The TLD if type = 2,3 * @param $aid int The account ID * @param $iamt float The cumulative invoice amount * @param $pamt float The product price before any discounts */ private function calc_item_discount($type,$did,$pid,$aid,$iamt,$pamt) { if (empty($this->discounts[$did])) return false; $discount = $this->discounts[$did]; if ($type == 0 || $type == 2) { $rate_type = $discount['new_type']; $rate = $discount['new_rate']; $min_cost = $discount['new_min_cost']; $max_usage_amt = $discount['new_max_discount']; } else { $rate_type = $discount['recurr_type']; $rate = $discount['recurr_rate']; $min_cost = $discount['recurr_min_cost']; $max_usage_amt = $discount['recurr_max_discount']; } if ((! empty($discount['date_start']) && $discount['date_start']>time()) || (! empty($discount['date_expire']) && $discount['date_expire'] $iamt)) return 0; if (($discount['max_usage_account']>0 || $discount['max_usage_global']>0) && (! $this->discount_check_usage($discount['max_usage_account'],$discount['max_usage_global'],$aid,$did))) return 0; if (! empty($discount['avail_account_id']) && $discount['avail_account_id'] != $aid) return 0; if ($type==0 || $type==2) { if (! empty($discount['avail_account_id']) && $discount['avail_account_id'] != $aid) { return 0; } else { if (! empty($discount['avail_group_id'])) { $arr = unserialize($discount['avail_group_id']); if (is_array($arr) && count($arr) > 0 && ! empty($arr[0])) { global $C_auth; $do = false; for ($i=0; $iauth_group_by_id($arr[$i])) { $do=true; $i=count($arr); } } if (! $do) return 0; } } } } if ($type<2 && ! empty($pid) && ! empty($discount['avail_product_id'])) { $arr = unserialize($discount['avail_product_id']); if (is_array($arr) && count($arr)>0 && ! in_array($pid,$arr)) return 0; } elseif ($type>1) { if (! empty($discount['avail_tld_id'])) { $do = false; $tld = $pid; $db = &DB(); $rstld = $db->Execute(sqlSelect($db,"host_tld","id","name=::$tld::")); if($rstld && $rstld->RecordCount()) { $tld_id = $rstld->fields['id']; $arr = unserialize($discount['avail_tld_id']); if (is_array($arr) && count($arr) > 0 && ! empty($arr[0])) { for ($i=0; $i $max_usage_amt) $discount_amt = $max_usage_amt; } else { $discount_amt = $rate; } if (! empty($max_usage_amt)) { if ($discount_amt+$this->discount_total > $max_usage_amt) $discount_amt = $max_usage_amt-$this->discount_total; } $this->discount_total += $discount_amt; return round($discount_amt,2); } /** * Check discount usage for account/global restrictions */ private function discount_check_usage($max_acct,$max_global,$account_id,$did) { if (! isset($this->discounts[$did])) return 0; $db = &DB(); $rs = $db->Execute( sqlSelect('invoice_item_discount','account_id,COUNT(account_id) AS count', array('where'=>array('discount'=>$this->discounts[$did]['name']),'groupby'=>'account_id'))); if ($rs && $rs->RecordCount()) { # Check global usage if (! empty($max_global) && $max_global>0 && $rs->RecordCount()>=$max_global) return false; # Check usage by this account if (! empty($max_acct) && $max_acct>0) { $i = 0; while (! $rs->EOF) { if ($rs->fields['account_id'] == $account_id && $rs->fields['count']>=$max_acct) return false; $rs->MoveNext(); } } } return true; } public function search_show($VAR) { $db =& DB(); $smart = parent::search_show($VAR); for ($i=0; $iExecute(sqlSelect(array('invoice','invoice_item_discount'),'SUM(A.total_amt) as sum', array('where'=>sprintf('B.invoice_id=A.id AND A.billing_status=1 AND B.discount=::%s::',$smart[$i]['name']),'distinct'=>true))); if ($rs && $rs->RecordCount()) $smart[$i]['revenue'] = $rs->fields['sum']; $rs = $db->Execute(sqlSelect('invoice_item_discount','invoice_id,amount',array('where'=>sprintf('discount=::%s::',$smart[$i]['name'])))); if ($rs && $rs->RecordCount()>0) { while (! $rs->EOF) { $smart[$i]['savings'] += $rs->fields['amount']; if (empty($invoices[$rs->fields['invoice_id']])) { $smart[$i]['orders']++; $invoices[$rs->fields['invoice_id']]=true; } $rs->MoveNext(); } } } global $smarty; $smarty->clear_assign('discount'); $smarty->assign('discount',$smart); } } ?>