diff --git a/application/bootstrap.php b/application/bootstrap.php index bb024a19..32e476a2 100644 --- a/application/bootstrap.php +++ b/application/bootstrap.php @@ -117,6 +117,7 @@ Kohana::modules(array( // 'codebench' => SMDPATH.'codebench', // Benchmarking tool 'database' => SMDPATH.'database', // Database access 'gchart' => MODPATH.'gchart', // Google Chart Module + 'highchart' => MODPATH.'highchart', // Highcharts Chart Module // 'image' => SMDPATH.'image', // Image manipulation 'khemail' => SMDPATH.'khemail', // Email module for Kohana 3 PHP Framework 'minion' => SMDPATH.'minion', // CLI Tasks diff --git a/application/classes/Company.php b/application/classes/Company.php index 0ff9b283..dd0dc4f3 100644 --- a/application/classes/Company.php +++ b/application/classes/Company.php @@ -10,6 +10,8 @@ * @license http://dev.osbill.net/license.html */ class Company { + public static $instance = array(); + // Our Company Setup object private $so; @@ -23,7 +25,12 @@ class Company { } public static function instance() { - return new Company(ORM::factory('Setup',array('url'=>URL::base('http')))); + $x = URL::base('http'); + + if (! isset(Company::$instance[$x])) + Company::$instance[$x] = new Company(ORM::factory('Setup',array('url'=>$x))); + + return Company::$instance[$x]; } public function admin() { diff --git a/application/classes/ORM/OSB.php b/application/classes/ORM/OSB.php index f46840d7..fe3f218b 100644 --- a/application/classes/ORM/OSB.php +++ b/application/classes/ORM/OSB.php @@ -136,9 +136,8 @@ abstract class ORM_OSB extends ORM { else $array[$k] = $x; - } else - if (! $v) - unset($array[$k]); + } elseif (! $v AND $v !== 0 AND $v !== '0') + unset($array[$k]); } @@ -207,15 +206,6 @@ abstract class ORM_OSB extends ORM { // Find any fields that have changed, and process them. if ($this->_changed) foreach ($this->_changed as $c) { - // Convert to NULL - if (in_array($c,$this->_nullifempty)) { - if (is_array($this->_object[$c])) - $this->_object[$c] = $this->_nullifempty($this->_object[$c]); - - elseif (! $this->_object[$c]) - $this->_object[$c] = NULL; - } - // Any fields that are blobs, and encode them. if (! is_null($this->_object[$c]) AND $this->_table_columns[$c]['data_type'] == 'blob') { $this->_object[$c] = $this->_blob($this->_object[$c],TRUE); @@ -246,6 +236,24 @@ abstract class ORM_OSB extends ORM { return TRUE; } + /** + * Override the Kohana processing so we can null values if required. + */ + public function values(array $values,array $expected=NULL) { + foreach ($values as $k=>$v) { + // Convert to NULL + if (in_array($k,$this->_nullifempty)) { + if (is_array($v)) + $values[$k] = $this->_nullifempty($v); + + elseif (! $v AND $v !== 0 AND $v !== '0') + $values[$k] = NULL; + } + } + + return parent::values($values,$expected); + } + /** * Function help to find records that are active */ diff --git a/modules/adsl/classes/ADSL.php b/modules/adsl/classes/ADSL.php index f7135d6c..d599fcc0 100644 --- a/modules/adsl/classes/ADSL.php +++ b/modules/adsl/classes/ADSL.php @@ -32,7 +32,7 @@ class ADSL { if (preg_match('/^a:/',$data)) throw new Kohana_Exception('Data shouldnt be a serialized array'); - $ao = ORM::factory('ADSL_Plan',$data); + $ao = ORM::factory('Product_Plugin_Adsl',$data); $output = View::factory('adsl/contract_view') ->set('record',$ao) ->set('price_base',$price_base) diff --git a/modules/adsl/classes/Adsl/Billing/Exetelvisp.php b/modules/adsl/classes/Adsl/Billing/Exetelvisp.php index 12ac310b..8dbe52cd 100644 --- a/modules/adsl/classes/Adsl/Billing/Exetelvisp.php +++ b/modules/adsl/classes/Adsl/Billing/Exetelvisp.php @@ -73,6 +73,7 @@ class ADSL_Billing_Exetelvisp { $start = $end = FALSE; $result = array(); + $c = 0; foreach (preg_split("/\n/",$data) as $line) { // Items start after "Item ID" if (! $start && preg_match('/^Item ID,/',$line)) { @@ -113,7 +114,7 @@ class ADSL_Billing_Exetelvisp { list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3]))); $result[$service]['cost'] = str_replace('$','',$record[6]); - } elseif (preg_match('/Monthly Charge On Plan /',$record[3])) { + } elseif (preg_match('/([0-9]+)\s+-\s+Monthly Charge On Plan /',$record[3])) { list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3]))); $result[$service]['cost'] = str_replace('$','',$record[6]); $result[$service]['info'] = 'Other Service'; @@ -139,7 +140,7 @@ class ADSL_Billing_Exetelvisp { list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3]))); $result[$service]['info'] = $line; } catch (Exception $e) { - $result['000']['info'] = $line; + $result["000-".$c++]['info'] = $line; } } } @@ -148,7 +149,7 @@ class ADSL_Billing_Exetelvisp { // @todo This could be optimised better. foreach ($aso->services(TRUE) as $so) - $this->haveService($so->plugin()->service_number,$so->plugin()->product()->adsl_supplier_plan->total()); + $this->haveService($so->plugin()->service_number,$so->plugin()->admin_plan()->adsl_supplier_plan->display('base_cost')); return $this; } diff --git a/modules/adsl/classes/Controller/Admin/Adsl.php b/modules/adsl/classes/Controller/Admin/Adsl.php index e852ed4d..722202e8 100644 --- a/modules/adsl/classes/Controller/Admin/Adsl.php +++ b/modules/adsl/classes/Controller/Admin/Adsl.php @@ -12,6 +12,8 @@ class Controller_Admin_Adsl extends Controller_Adsl { protected $secure_actions = array( 'index'=>TRUE, + 'edit'=>TRUE, + 'list'=>TRUE, 'traffic'=>TRUE, ); @@ -29,6 +31,94 @@ class Controller_Admin_Adsl extends Controller_Adsl { ->body($output); } + public function action_list() { + Block::factory() + ->title('ADSL Plans') + ->title_icon('icon-th-list') + ->body(Table::factory() + ->data(ORM::factory('Product_Plugin_Adsl')->find_all()) + ->jssort('adsl') + ->columns(array( + 'id'=>'ID', + 'adsl_supplier_plan->name()'=>'Plan', + 'adsl_supplier_plan->display("status")'=>'Avail', + 'base_down_peak'=>'PD', + 'base_down_offpeak'=>'OPD', + 'base_up_peak'=>'PU', + 'base_up_offpeak'=>'OPU', + 'extra_charged'=>'$', + 'extra_down_peak'=>'EPD', + 'extra_down_offpeak'=>'EOPD', + 'extra_up_peak'=>'EPU', + 'extra_up_offpeak'=>'EOPU', + 'contract_term'=>'Cont', + )) + ->prepend(array( + 'id'=>array('url'=>URL::link('admin','adsl/edit/')), + )) + ); + } + + public function action_edit() { + $apo = ORM::factory('Product_Plugin_Adsl',$this->request->param('id')); + $test_result = array(); + + if (! $apo->loaded()) + HTTP::redirect(URL::link('admin','adsl/list')); + + if ($_POST) { + // Entry updated + if ($apo->values($_POST)->check() AND $apo->changed()) { + try { + if ($apo->save()) + SystemMessage::factory() + ->title('Record updated') + ->type('success') + ->body(_('Your Charge record has been recorded/updated.')); + + } catch (ORM_Validation_Exception $e) { + $errors = $e->errors('models'); + + SystemMessage::factory() + ->title('Record NOT updated') + ->type('error') + ->body(join('
',array_values($errors))); + + $sco->reload(); + } + } + + if (isset($_POST['test'])) { + $charge = isset($_POST['test']['charge']) ? $_POST['test']['charge'] : FALSE; + $test_result = $apo->allowance($_POST['test'],FALSE,$charge,1000); + } + } + + Block::factory() + ->type('form-horizontal') + ->title('Update ADSL Plan') + ->title_icon('icon-wrench') + ->body(View::factory('adsl/admin/edit') + ->set('test_result',$test_result) + ->set('o',$apo)); + + Block::factory() + ->title('Products Using this Plan') + ->title_icon('icon-th-list') + ->body(Table::factory() + ->jssort('traffic') + ->data($apo->products()->find_all()) + ->columns(array( + 'id'=>'ID', + 'title()'=>'Name', + 'status'=>'Active', + )) + ->prepend(array( + 'id'=>array('url'=>URL::link('admin','product/edit/')), + )) + ); + } + /** * Reconcile billing for an ADSL supplier */ diff --git a/modules/adsl/classes/Controller/Reseller/Adsl.php b/modules/adsl/classes/Controller/Reseller/Adsl.php index 0ee4cff4..214fef5b 100644 --- a/modules/adsl/classes/Controller/Reseller/Adsl.php +++ b/modules/adsl/classes/Controller/Reseller/Adsl.php @@ -31,6 +31,7 @@ class Controller_Reseller_Adsl extends Controller_Adsl { ->rule('csv','Upload::type',array(':value',array('csv'))) ->rule('csv','Upload::size',array(':value','10M')); + $csv = NULL; if ($files->check()) foreach ($files->data() as $file) { $csv = $aso->billing()->process($aso,$file); diff --git a/modules/adsl/classes/Controller/Reseller/Service/Adsl.php b/modules/adsl/classes/Controller/Reseller/Service/Adsl.php index dcde6671..e8780b3c 100644 --- a/modules/adsl/classes/Controller/Reseller/Service/Adsl.php +++ b/modules/adsl/classes/Controller/Reseller/Service/Adsl.php @@ -14,68 +14,64 @@ class Controller_Reseller_Service_Adsl extends Controller_Service { 'list'=>TRUE, ); - private function consoltraffic($svs,$date) { - $data = array(); + private function consoltraffic($period) { + $result = array(); - foreach ($svs as $so) { - $c = array(); - foreach ($so->plugin()->get_traffic_data_monthly($date) as $metric => $ma) { - foreach ($ma as $month => $traffic) { - // Only count the service once, not for each metric. - if (! isset($c[$month])) { - if (isset($data['svs'][$month])) - $data['svs'][$month] += 1; - else - $data['svs'][$month] = 1; + $periodstart = mktime(0,0,0,date('m',$period),1,date('Y',$period)-1); - $c[$month] = 1; - } + $t = ORM::factory('Service_Plugin_Adsl_Traffic') + ->select(array('date_format(date,\'%y-%m\')','month')) + ->selectsummetric() + ->join('service__adsl','INNER') + ->on('service__adsl.service_username','=','service_plugin_adsl_traffic.service') + ->on('service__adsl.site_id','=','service_plugin_adsl_traffic.site_id') + ->join('service','INNER') + ->on('service__adsl.service_id','=','service.id') + ->on('service__adsl.site_id','=','service.site_id') + ->where_authorised($this->ao,'service.account_id') + ->and_where('date','>=',date('Y-m-d',$periodstart)) + ->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period)))) + ->group_by('date_format(date,\'%Y-%m\')'); - if (isset($data['data'][$metric][$month])) - $data['data'][$metric][$month] += (int)$traffic; - else - $data['data'][$metric][$month] = (int)$traffic; - } - } - } + foreach ($t->find_all() as $to) + foreach ($to->traffic_data() as $metric => $v) + $result[$metric][$to->month] = $v; - ksort($data['svs']); - foreach ($data['data'] as $metric => $details) - ksort($data['data'][$metric]); - - return $data; + return $result; } public function action_list() { - $svs = ORM::factory('Service')->where_authorised($this->ao)->list_byplugin('ADSL'); + $highchart = HighChart::factory('Combo'); + $highchart->title(sprintf('Monthly DSL traffic usage as at %s',date('Y-m-d',strtotime('yesterday')))); - $data = $this->consoltraffic($svs,time()); + $c = 0; + foreach ($this->consoltraffic(time()) as $k => $details) { + $highchart->series('column','yl') + ->name($k) + ->data($details) + ->index($c) + ->order($c++*-1); // This is a kludge to get around highcharts rendering from the bottom up. - $google = GoogleChart::factory('Legacy') - ->type('vertical_bar') - ->title(sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday')))); + $highchart->autopie($k); + } - foreach ($data['data'] as $key => $values) - $google->sdata(array('yl'=>$key),array($key=>$values)); - - $google->sdata(array('yr'=>'services'),array('services'=>$data['svs'])); - - Block::add(array('body'=>(string)$google)); + Block::factory() + ->body((string)$highchart); Block::factory() ->title('ADSL Services') ->title_icon('icon-th-list') ->body(Table::factory() ->jssort('adsl') - ->data($svs) + ->data(ORM::factory('Service')->where_authorised($this->ao)->list_byplugin('ADSL')) ->columns(array( 'id'=>'ID', 'name()'=>'Service', 'plugin()->ipaddress()'=>'IP Address', - 'product->plugin()->adsl_supplier_plan->speed'=>'Speed', - 'product->plugin()->allowance()'=>'Allowance', - 'plugin()->traffic_thismonth()'=>'This Month', - 'plugin()->traffic_lastmonth()'=>'Last Month', + 'product->plugin()->supplier_plan->speed'=>'Speed', + 'product->plugin()->allowance(array(),TRUE,TRUE,1000)'=>'Allowance', + 'plugin()->traffic_month(strtotime("yesterday"),TRUE,1000)'=>'This Month', + 'plugin()->traffic_month(strtotime("last month"),TRUE,1000)'=>'Last Month', 'recur_schedule'=>'Billing', 'price(TRUE,TRUE)'=>'Price', 'account->accnum()'=>'Cust ID', diff --git a/modules/adsl/classes/Model/ADSL/Plan.php b/modules/adsl/classes/Model/ADSL/Plan.php deleted file mode 100644 index 655bda18..00000000 --- a/modules/adsl/classes/Model/ADSL/Plan.php +++ /dev/null @@ -1,48 +0,0 @@ -array('model'=>'ADSL_Supplier_Plan'), - ); - - protected $_display_filters = array( - 'extra_down_peak'=>array( - array('Tax::add',array(':value')), - array('Currency::display',array(':value')), - ), - 'extra_down_offpeak'=>array( - array('Tax::add',array(':value')), - array('Currency::display',array(':value')), - ), - 'extra_up_peak'=>array( - array('Tax::add',array(':value')), - array('Currency::display',array(':value')), - ), - 'extra_up_offpeak'=>array( - array('Tax::add',array(':value')), - array('Currency::display',array(':value')), - ), - ); - - public function products($active) { - $x = ORM::factory('Product') - ->where('prod_plugin_file','=','ADSL') - ->and_where('prod_plugin_data','=',$this); - - if ($active) - $x->where_active(); - - return $x; - } -} -?> diff --git a/modules/adsl/classes/Model/ADSL/Supplier.php b/modules/adsl/classes/Model/ADSL/Supplier.php index 492183a0..182b068d 100644 --- a/modules/adsl/classes/Model/ADSL/Supplier.php +++ b/modules/adsl/classes/Model/ADSL/Supplier.php @@ -28,7 +28,7 @@ class Model_ADSL_Supplier extends ORM_OSB { $result = array(); foreach ($this->find_plans($active)->find_all() as $po) - foreach ($po->adsl_plan->find_all() as $apo) + foreach ($po->admin_plan()->find_all() as $apo) $result[$apo->id] = $apo; return $result; @@ -62,7 +62,7 @@ class Model_ADSL_Supplier extends ORM_OSB { $result = array(); foreach ($this->find_plans(FALSE)->find_all() as $aspo) { - foreach ($aspo->adsl_plan->find_all() as $apo) { + foreach ($aspo->plan->find_all() as $apo) { foreach ($apo->products(FALSE)->find_all() as $po) { foreach ($po->services($active)->find_all() as $so) { array_push($result,$so); diff --git a/modules/adsl/classes/Model/ADSL/Supplier/Plan.php b/modules/adsl/classes/Model/ADSL/Supplier/Plan.php index 8fea1a51..17ee7e22 100644 --- a/modules/adsl/classes/Model/ADSL/Supplier/Plan.php +++ b/modules/adsl/classes/Model/ADSL/Supplier/Plan.php @@ -11,30 +11,100 @@ */ class Model_ADSL_Supplier_Plan extends ORM_OSB { // Relationships - protected $_has_many = array( - 'adsl_plan'=>array('model'=>'ADSL_Plan','far_key'=>'id'), - ); protected $_belongs_to = array( - 'adsl_supplier'=>array('model'=>'ADSL_Supplier','foreign_key'=>'supplier_id'), + 'supplier'=>array('model'=>'ADSL_Supplier'), + ); + protected $_has_many = array( + 'plan'=>array('model'=>'Product_Plugin_Adsl','far_key'=>'id'), + ); + + protected $_sorting = array( + 'product_id'=>'ASC', + 'product_desc'=>'ASC', + ); + + protected $_form = array('id'=>'id','value'=>'name()'); + + /** + * Filters used to format the display of values into friendlier values + */ + protected $_display_filters = array( + 'base_cost'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_down_peak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_down_offpeak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_up_peak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_up_offpeak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'status'=>array( + array('StaticList_YesNo::get',array(':value',TRUE)), + ), + ); + + // Map the table fields + private $_map = array( + 'base_up_offpeak'=>'extra_up_offpeak', + 'base_down_offpeak'=>'extra_down_offpeak', + 'base_up_peak'=>'extra_up_peak', + 'base_down_peak'=>'extra_down_peak', + ); + + private $_metrics = array( + 'base_up_peak'=>'base_up_peak', + 'base_up_offpeak'=>'base_up_offpeak', + 'base_down_peak'=>'base_down_peak', + 'base_down_offpeak'=>'base_down_offpeak', ); /** - * Show the ADSL allowance as a peak/offpeak metric + * Display the cost for extra traffic */ - public function allowance() { - return sprintf('%s/%s',$this->base_down_peak+$this->base_up_peak,$this->base_down_offpeak+$this->base_up_offpeak); + public function cost_extra(array $data=array(),$format=FALSE) { + $result = array(); + + if (! $data) + $data = $this->traffic_data(); + + foreach ($data as $k => $v) + $result[$k] = $this->display($this->_map[$k]); + + return $format ? join('/',array_values($result)) : $result; } + /** + * ADSL Plan Name + */ public function name() { - return $this->product_id; + return sprintf('%s: %s',$this->product_id,$this->product_desc); } - public function tax() { - return Tax::amount($this->base_cost); - } + /** + * Collapse our traffic data into an array as per $this->_metric + */ + public function traffic_data() { + $result = array(); - public function total($format=FALSE) { - return $format ? Currency::display($this->base_cost+$this->tax()) : Currency::round($this->base_cost+$this->tax()); + foreach ($this->_metrics as $metric=>$v) { + if (! isset($result[$v])) + $result[$v] = 0; + + $result[$v] += $this->{$metric}; + } + + return $result; } } ?> diff --git a/modules/adsl/classes/Model/Product/Plugin/Adsl.php b/modules/adsl/classes/Model/Product/Plugin/Adsl.php index deed99c1..12ead200 100644 --- a/modules/adsl/classes/Model/Product/Plugin/Adsl.php +++ b/modules/adsl/classes/Model/Product/Plugin/Adsl.php @@ -12,10 +12,20 @@ class Model_Product_Plugin_Adsl extends Model_Product_Plugin { protected $_table_name = 'adsl_plan'; + // We dont use the update column + protected $_updated_column = FALSE; + + // Relationships protected $_belongs_to = array( - 'adsl_supplier_plan'=>array('model'=>'ADSL_Supplier_Plan'), + 'supplier_plan'=>array('model'=>'ADSL_Supplier_Plan','foreign_key'=>'adsl_supplier_plan_id'), + ); + protected $_has_many = array( + 'product'=>array('far_key'=>'id','foreign_key'=>'prod_plugin_data'), ); + /** + * Filters used to format the display of values into friendlier values + */ protected $_display_filters = array( 'extra_down_peak'=>array( array('Tax::add',array(':value')), @@ -25,45 +35,178 @@ class Model_Product_Plugin_Adsl extends Model_Product_Plugin { array('Tax::add',array(':value')), array('Currency::display',array(':value')), ), + 'extra_up_peak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_up_offpeak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_charged'=>array( + array('StaticList_YesNo::get',array(':value',TRUE)), + ), ); + protected $_nullifempty = array( + 'base_down_peak', + 'base_down_offpeak', + 'base_up_peak', + 'base_up_offpeak', + 'extra_down_peak', + 'extra_down_offpeak', + 'extra_up_peak', + 'extra_up_offpeak', + ); + + // Map the table fields + private $_map = array( + 'base_up_offpeak'=>'extra_up_offpeak', + 'base_down_offpeak'=>'extra_down_offpeak', + 'base_up_peak'=>'extra_up_peak', + 'base_down_peak'=>'extra_down_peak', + ); + + // Our required abstract methods public function admin_update() { return ''; } - // Our required abstract methods public function cost($annual=FALSE) { - $x = $this->adsl_supplier_plan->total(); + $x = $this->supplier_plan->display('base_cost'); return $annual ? $x*12 : $x; } public function feature_summary() { - // @todo This view should render based on the the results of this::allowance(); - return View::factory('product/plugin/adsl/feature_summary') - ->set('po',$this); + return View::factory(sprintf('product/plugin/%s/feature_summary',$this->plugin())) + ->set('o',$this); } public function supplier() { - return $this->adsl_supplier_plan->supplier_id; + return $this->supplier_plan->supplier_id; + } + + /** LOCAL FUNCTIONS **/ + + /** + * Calculate the allowance array or traffic used array + * + * If: + * + * + UPLOADS are charged and there are no PEAK/OFFPEAK periods (therefore all + * traffic is charged), the allowance will be shown as 1 metric - TRAFFIC. + * + UPLOADS are charged and there are PEAK/OFFPEAK periods the allowance + * will be shown as 2 metrics - PEAK/OFFPEAK. + * + UPLOADS are NOT charged and there are no PEAK/OFFPEAK periods the allowance + * will be shown as 1 metrics - TRAFFIC. + * + UPLOADS are NOT charged and there are PEAK/OFFPEAK periods the allowance + * will be shown as 2 metrics - PEAK/OFFPEAK. + * + * Thus: + * + * + If base_x_Y is NULL, all Y traffic is FREE (ignore respective extra_x_Y setting). + * + If base_x_Y is a number, all Y traffic is FREE up to the number (evaluate extra_x_Y setting). + * + If extra_x_Y is a number, charge this amount for traffic over base_x_Y. + * + If extra_down_peak is NULL this is invalid, treat base_down_peak as NULL + * + If extra_down_offpeak is NULL add traffic_down_offpeak to traffic_down_peak + * + If extra_up_peak is NULL add traffic_up_peak to traffic_down_peak + * + If extra_up_offpeak is NULL add traffic_up_offpeak to traffic_down_offpeak + * + * @param array Traffic Used in each metric. + * @param bool Whether the output should be a string + * @param bool Display the over alloance numbers + * @param int Divide the numbers + */ + public function allowance(array $data=array(),$format=FALSE,$over=FALSE,$divide=0) { + $result = $x = array(); + + // Do we invert the result - showing allowance + $invert = $data ? FALSE : TRUE; + + // Map the NULL relationships + $merge = array( + 'extra_up_offpeak'=>'base_down_offpeak', + 'extra_down_offpeak'=>'base_down_peak', + 'extra_up_peak'=>'base_down_peak', + 'extra_down_peak'=>'base_down_peak', + ); + + // Work out if we charge each period + foreach ($this->_map as $k => $v) { + // Anything NULL is not counted + if (is_null($this->{$k})) + continue; + + if (! isset($data[$k])) + $data[$k] = 0; + + if ($over) + $data[$k] = $invert ? $this->{$k} : $data[$k]-$this->{$k}; + + // Do we charge for extra, or merge it to another field + if (! is_null($this->{$v})) { + if (isset($x[$k])) + $x[$k] += $data[$k]; + else + $x[$k] = $data[$k]; + + } else { + if (isset($x[$merge[$v]])) { + $x[$merge[$v]] += $data[$k]; + + } else { + $x[$merge[$v]] = $data[$k]; + + // If we had any data already in A for this merge, we'll need to add it too. + if (isset($x[$k])) { + $x[$merge[$v]] += $x[$k]; + + unset($x[$k]); + } + } + } + } + + // Return the output sorted + foreach (array_keys(Model_Service_Plugin_Adsl_Traffic::$metrics) as $k) { + $k = 'base_'.$k; + + if (isset($x[$k])) + $result[$k] = $divide ? ceil($x[$k]/$divide) : $x[$k]; + } + + return $format ? join('/',array_values($result)) : $result; + } + + public function cost_extra(array $data=array(),$format=FALSE) { + $result = array(); + + foreach ($this->allowance($data) as $k => $v) + $result[$k] = $format ? $this->display($this->_map[$k]) : $this->{$this->_map[$k]}; + + return $format ? join('/',array_values($result)) : $result; + } + + public function hasOffpeak() { + if ((($this->base_down_offpeak || $this->base_down_offpeak == 0) AND (! is_null($this->extra_down_offpeak))) + OR (($this->base_up_offpeak || $this->base_up_offpeak == 0) AND (! is_null($this->extra_up_offpeak)))) + + return TRUE; } /** - * Show the ADSL allowance as a peak/offpeak metric + * Get all the products using this plan */ - public function allowance($string=TRUE) { - $output = ADSL::allowance(array( - 'base_down_peak'=>$this->base_down_peak, - 'base_down_offpeak'=>$this->base_down_offpeak, - 'base_up_peak'=>$this->base_up_peak, - 'base_up_offpeak'=>$this->base_up_offpeak, - 'extra_down_peak'=>$this->extra_down_peak, - 'extra_down_offpeak'=>$this->extra_down_offpeak, - 'extra_up_peak'=>$this->extra_up_peak, - 'extra_up_offpeak'=>$this->extra_up_offpeak, - )); + public function products($active=FALSE) { + $x = ORM::factory('Product') + ->where('prod_plugin_file','=','ADSL') + ->and_where('prod_plugin_data','=',$this); - return $string ? implode('/',$output) : $output; + if ($active) + $x->where_active(); + + return $x; } } ?> diff --git a/modules/adsl/classes/Model/Service/Plugin/Adsl.php b/modules/adsl/classes/Model/Service/Plugin/Adsl.php index 91eeb904..91b904c9 100644 --- a/modules/adsl/classes/Model/Service/Plugin/Adsl.php +++ b/modules/adsl/classes/Model/Service/Plugin/Adsl.php @@ -19,7 +19,11 @@ class Model_Service_Plugin_Adsl extends Model_Service_Plugin { ); protected $_has_one = array( - 'adsl_plan'=>array('model'=>'ADSL_Plan','far_key'=>'provided_adsl_plan_id','foreign_key'=>'id'), + 'provided_plan'=>array('model'=>'Product_Plugin_Adsl','far_key'=>'provided_adsl_plan_id','foreign_key'=>'id'), + ); + + protected $_has_many = array( + 'traffic'=>array('model'=>'Service_Plugin_Adsl_Traffic','foreign_key'=>'service','far_key'=>'service_username'), ); protected $_display_filters = array( @@ -33,7 +37,7 @@ class Model_Service_Plugin_Adsl extends Model_Service_Plugin { // Required abstract functions public function expire() { - // @todo This should work out if the invoices are currently due, then the expire is the invoice date, otherwise the next invoice date. + // We'll leave it to the Service record to determine when this service expires return NULL; } @@ -41,6 +45,15 @@ class Model_Service_Plugin_Adsl extends Model_Service_Plugin { return $this->service_number; } + public function username_value() { + return $this->service_username; + } + + public function password_value() { + return $this->service_password; + } + + // Override our parent function to include some JS. public function admin_update() { Script::factory() ->type('stdin') @@ -68,24 +81,130 @@ $(document).ready(function() { return parent::admin_update(); } - public function product() { - if ($this->provided_adsl_plan_id) - return $this->adsl_plan; - else - return $this->service->product->plugin(); + /** LOCAL FUNCTIONS **/ + + /** + * If we override the plan that the customers gets (from what the supplier provides). + */ + public function admin_plan() { + return $this->provided_adsl_plan_id ? $this->provided_plan : $this->service->product->plugin(); } - public function service_view() { - return View::factory('service/user/plugin/adsl/view') - ->set('o',$this); + /** + * Calculate our contract start and end dates + */ + public function contract_date_start($format=FALSE) { + return $format ? Config::date($this->service_contract_date) : $this->service_contract_date; } - public function username_value() { - return $this->service_username; + public function contract_date_end($format=FALSE) { + $x = strtotime(sprintf('+%s months',$this->contract_term),$this->service_contract_date); + + return $format ? Config::date($x) : $x; } - public function password_value() { - return $this->service_password; + /** + * This function will return the months that have traffic data. + * This array can be used in a select list to display the traffic for that month + */ + public function get_traffic_months() { + $x = array_keys($this->get_traffic_monthlytype()); + + if (! $x) + return array(); + + $months = array_combine($x,$x); + + arsort($months); + + return $months; + } + + /** + * Get daily traffic data, broken down by type + * @todo This needs to get the plan that was invoiced, not the current plan.. + */ + public function get_traffic_dailytype($period) { + $result = array(); + + if (is_null($period)) + $period = strtotime('yesterday'); + + $t = $this->traffic + ->where('date','>=',date('Y-m-d',mktime(0,0,0,date('m',$period),1,date('Y',$period)))) + ->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period)))); + + foreach ($t->find_all() as $to) { + $day = date('j',strtotime($to->date)); + + $result[$day] = $this->plan()->allowance($to->traffic_data()); + } + + return $result; + } + + /** + * Get monthly traffic data, broken down by type + * @todo This needs to get the plan that was invoiced, not the current plan.. + */ + public function get_traffic_monthlytype($period=NULL,$periodstart=NULL,$format=FALSE,$divide=0) { + $result = array(); + + if (is_null($period)) + $period = strtotime('yesterday'); + if (is_null($periodstart)) + $periodstart = mktime(0,0,0,date('m',$period),1,date('Y',$period)-1); + + $t = $this->traffic + ->select(array('date_format(date,\'%y-%m\')','month')) + ->selectsummetric() + ->and_where('date','>=',date('Y-m-d',$periodstart)) + ->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period)))) + ->group_by('date_format(date,\'%Y-%m\')'); + + foreach ($t->find_all() as $to) { + $index = $to->month; + + $result[$index] = $this->plan()->allowance($to->traffic_data(),$format,FALSE,$divide); + } + + return $result; + } + + /** + * Get traffic type data, broken down by day + */ + public function get_traffic_typedaily($period=NULL) { + $result = array(); + + if (is_null($period)) + $period = strtotime('yesterday'); + + foreach ($this->get_traffic_dailytype($period) as $day => $data) + foreach ($data as $metric => $value) + $result[$metric][$day] = $value; + + return $result; + } + + /** + * Get traffic type data, broken down by month + */ + public function get_traffic_typemonthly($period=NULL) { + $result = array(); + + if (is_null($period)) + $period = strtotime('yesterday'); + + foreach ($this->get_traffic_monthlytype($period) as $day => $data) + foreach ($data as $metric => $value) + $result[$metric][$day] = $value; + + return $result; + } + + public function hasOffPeak() { + return $this->plan()->hasOffPeak(); } /** @@ -95,147 +214,130 @@ $(document).ready(function() { return $this->ipaddress ? $this->ipaddress : _('Dynamic'); } - public function contract_date_start() { - return Config::date($this->service_contract_date); - } - - public function contract_date_end($format=FALSE) { - $x = strtotime(sprintf('+%s months',$this->contract_term),$this->service_contract_date); - - return $format ? Config::date($x) : $x; - } - - public function hasOffpeak() { - return ((is_null($this->product()->base_down_offpeak) OR $this->product()->base_down_offpeak) AND (is_null($this->product()->base_up_offpeak) OR $this->product()->base_up_offpeak)) ? TRUE : FALSE; + /** + * If we override the plan that the customers gets (from what the supplier provides). + * @todo This needs to get the plan that was invoiced, not the current plan.. + */ + public function plan($month=NULL) { + return is_null($month) ? $this->service->product->plugin() : $this->plandate($month); } /** - * This function will return the months that have traffic data. - * This array can be used in a select list to display the traffic for that month + * For a particular month, select the PLAN that the user was on */ - public function get_traffic_months() { - $keys = $months = array(); - - foreach ($this->get_traffic_data_monthly() as $metric => $data) - $keys = array_unique(array_merge($keys,array_keys($data))); - - foreach ($keys as $v) - $months[$v] = $v; - - arsort($months); - - return $months; + public function plandate($month) { + throw new Kohana_Exception('This function hasnt been written yet.'); } /** - * Calculate the total traffic used in a month + * Calculate the Excess Traffic Charges */ - private function get_traffic_data_month($period=NULL,$cache=0) { + public function traffic_excess($period=NULL,$charge=FALSE,$format=FALSE) { $result = array(); - foreach ($this->get_traffic_data_daily($period,TRUE,$cache) as $tdata) - foreach ($tdata as $k => $v) - if (isset($result[$k])) - $result[$k] += $v; - else - $result[$k] = $v; + if ($this->plan()->extra_charged) { + if (is_null($period)) + $period = strtotime('yesterday'); - return $result; - } + if ($x=$this->get_traffic_monthlytype(strtotime('last day of '.date('M Y',$period)),strtotime('first day of '.date('M Y',$period)))) { - /** - * Return an array of the data used in a month by day - */ - public function get_traffic_data_daily($period=NULL,$bydate=FALSE,$cache=0) { - $result = array(); + $c = $this->plan()->cost_extra(); + foreach ($this->plan()->allowance(array_pop($x),FALSE,TRUE,1000) as $k=>$v) + if (isset($c[$k]) AND $v > 0) { + $result[$k] = $charge ? $c[$k]*$v : $v; - if (is_null($period)) - $period = strtotime('yesterday'); - - $t = ORM::factory('Service_Plugin_Adsl_Traffic') - ->where('service','=',$this->service_username) - ->and_where('date','>=',date('Y-m-d',mktime(0,0,0,date('m',$period),1,date('Y',$period)))) - ->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period)))) - ->cached($cache); - - foreach ($t->find_all() as $to) { - $day = date('d',strtotime($to->date)); - - if ($bydate) - $result[$day] = $to->traffic($this->service->product->plugin()); - else - foreach ($to->traffic($this->service->product->plugin()) as $k => $v) - $result[$k][$day] = $v; - } - - return $result; - } - - /** - * Return an array of the data used in a year by month - */ - public function get_traffic_data_monthly($period=NULL,$bydate=FALSE) { - $result = array(); - - if (is_null($period)) - $period = strtotime('yesterday'); - - $t = ORM::factory('Service_Plugin_Adsl_Traffic') - ->select( - array('date_format(date,\'%y-%m\')','month'), - array('sum(up_peak)','up_peak'), - array('sum(up_offpeak)','up_offpeak'), - array('sum(down_peak)','down_peak'), - array('sum(down_offpeak)','down_offpeak') - ) - ->where('service','=',$this->service_username) - ->and_where('date','>=',date('Y-m-d',mktime(0,0,0,date('m',$period),1,date('Y',$period)-1))) - ->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period)))) - ->group_by('date_format(date,\'%Y-%m\')'); - - foreach ($t->find_all() as $to) - if ($bydate) - $result[$to->month] = $to->traffic($this->service->product->plugin()); - else - foreach ($to->traffic($this->service->product->plugin()) as $k => $v) - $result[$k][$to->month] = $v; - - return $result; - } - - public function traffic_month($month,$string=TRUE,$cache=0) { - return $string ? implode('/',$this->get_traffic_data_month($month,$cache)) : - $this->get_traffic_data_month($month,$cache); - } - - public function traffic_lastmonth($string=TRUE) { - // We need it to be last month as of yesterday - return $this->traffic_month(strtotime('last month')-86400,$string); - } - - public function traffic_thismonth($string=TRUE) { - return $this->traffic_month(strtotime('yesterday'),$string); - } - - public function traffic_lastmonth_exceed($all=FALSE,$date=NULL) { - $result = array(); - - if (is_null($date)) - $date = strtotime('last month')-86400; - - foreach ($this->traffic_month($date,FALSE,0) as $k => $v) { - // We shouldnt need to eval for nulls, since the traffic calc does that - if ($all OR ($v > $this->service->product->plugin()->$k)) { - $result[$k]['allowance'] = $this->service->product->plugin()->$k; - $result[$k]['used'] = $v; - $result[$k]['shaped'] = (! empty($this->service->product->plugin()->extra_shaped) AND $this->service->product->plugin()->extra_shaped AND $v > $this->service->product->plugin()->$k) ? TRUE : FALSE; - $result[$k]['excess'] = (! empty($this->service->product->plugin()->extra_charged) AND $this->service->product->plugin()->extra_charged AND $v > $this->service->product->plugin()->$k) ? $v-$this->service->product->plugin()->$k : 0; - $result[$k]['rate'] = $this->service->product->plugin()->{ADSL::map($k)}; - $result[$k]['charge'] = ceil(($result[$k]['excess'])/1000)*$result[$k]['rate']; + if ($charge AND $format) + $result[$k] = Currency::display(Tax::add($result[$k])); + } } } - return $result; + if (! $result AND $charge AND $format) + $result = array(Currency::display(0)); + + return $format ? join('/',$result) : $result; + } + + /** + * Render a chart of traffic + */ + public function traffic_graph($month=NULL) { + $highchart = HighChart::factory('Combo'); + + $c=0; + // If we came in via a post to show a particular month, then show that, otherwise show the yearly result + if (! is_null($month) AND trim($month)) { + $highchart->title(sprintf('DSL traffic usage for %s',$_POST['month'])); + $x = $this->get_traffic_typedaily(strtotime($_POST['month'].'-01')); + + } else { + $highchart->title(sprintf('Monthly DSL traffic usage as at %s',$this->traffic->find_last()->date)); + $x = $this->get_traffic_typemonthly(); + } + + foreach ($x as $k => $details) { + $highchart->series('column','yl') + ->name($this->traffic->friendly($k)) + ->data($x[$k]) + ->index($c) + ->order($c++*-1); // This is a kludge to get around highcharts rendering from the bottom up. + + $highchart->autopie($k); + } + + + return (string)$highchart; + } + + /** + * Get the traffic for a month + */ + public function traffic_month($period,$format=FALSE,$divide=0) { + $x = $this->get_traffic_monthlytype(strtotime('last day of '.date('M Y',$period)),strtotime('first day of '.date('M Y',$period)),$format,$divide); + + return $x ? array_pop($x) : 0; + } + + /** + * Render a table of traffic + */ + public function traffic_table($month=NULL) { + // If we came in via a post to show a particular month, then show that, otherwise show the yearly result + if (! is_null($month) AND trim($month)) { + $x = $this->get_traffic_dailytype(strtotime($_POST['month'].'-01')); + $index = 'Date'; + + } else { + $x = $this->get_traffic_monthlytype(); + $index = 'Month'; + } + + return View::factory(sprintf('service/user/plugin/%s/table_traffic',$this->plugin())) + ->set('index',$index) + ->set('th',array_keys($this->plan()->allowance())) + ->set('td',$x) + ->set('o',$this->traffic); + } + + /** + * Search for services matching a term + */ + public function list_autocomplete($term,$index,$value,array $label,array $limit=array(),array $options=NULL) { + // We only show service numbers. + if (! is_numeric($term)) + return array(); + + $ao = Auth::instance()->get_user(); + + $options['key'] = 'id'; + $options['object'] = DB::select($this->_table_name.'.id',$this->_table_name.'.service_number') + ->from($this->_table_name) + ->join('service') + ->on('service.id','=',$this->_table_name.'.service_id') + ->where('service.account_id','IN',$ao->RTM->customers($ao->RTM)) + ->and_where($this->_table_name.'.service_number','like','%'.$term.'%'); + + return parent::list_autocomplete($term,$index,$value,$label,$limit,$options); } public function template_variables($array) { @@ -307,7 +409,7 @@ $(document).ready(function() { /** * This function will take an array of numbers and change it into a cumulative array */ - public function cumulative($array) { + private function cumulative($array) { $result = array(); $s = 0; @@ -330,7 +432,6 @@ $(document).ready(function() { throw new Kohana_Exception('Huh? How did this get called, for a non ADSL product (:ppf)',array(':ppf'=>$this->service_id)); $allowance = $this->service->product->plugin()->allowance(FALSE); - $period = strtotime('yesterday'); $traffic_data = $this->get_traffic_data_daily($period,FALSE); $traffic = $this->get_traffic_data_month($period); @@ -379,59 +480,45 @@ $(document).ready(function() { } /** - * Render a google chart of traffic + * Return an array of the data used in a month by day */ - public function graph_traffic($month=NULL) { - $google = GoogleChart::factory('Legacy') - ->type('vertical_bar'); + private function get_traffic_data_daily($period=NULL,$bydate=FALSE) { + $result = array(); - // If we came in via a post to show a particular month, then show that, otherwise show the yearly result - if (! is_null($month) AND trim($month)) { - $google->title(sprintf('DSL traffic usage for %s',$_POST['month'])); - $traffic_data = $this->get_traffic_data_daily(strtotime($_POST['month'].'-01')); + if (is_null($period)) + $period = strtotime('yesterday'); - foreach ($traffic_data as $k => $details) - $google->sdata(array('yl'=>($x=isset($friendly[$k]) ? $friendly[$k] : $k)),array($x=>$traffic_data[$k])); + $t = ORM::factory('Service_Plugin_Adsl_Traffic') + ->where('service','=',$this->service_username) + ->and_where('date','>=',date('Y-m-d',mktime(0,0,0,date('m',$period),1,date('Y',$period)))); - foreach ($traffic_data as $k => $details) - $google->sdata(array('yr'=>($x=isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)),array($x=>$this->cumulative($traffic_data[$k]))); + foreach ($t->find_all() as $to) { + $day = (string)date('d',strtotime($to->date)); - } else { - // @todo Change the date to the last record date - $google->title(sprintf('Monthly DSL traffic usage as at %s',Config::date(strtotime('yesterday')))); - $traffic_data = $this->get_traffic_data_monthly(); - - foreach ($traffic_data as $k => $details) - $google->sdata(array('yl'=>($x=isset($friendly[$k]) ? $friendly[$k] : $k)),array($x=>$traffic_data[$k])); + if ($bydate) + $result[$day] = $to->traffic($this->service->product->plugin()); + else + foreach ($to->traffic($this->service->product->plugin()) as $k => $v) + $result[$k][$day] = (int)$v; } - return (string)$google; - } - - public function table_traffic($month=NULL) { - return View::factory('service/user/plugin/adsl/table_traffic') - ->set('traffic',$this->traffic_month((! is_null($month) AND trim($month)) ? strtotime($month.'-01') : NULL,FALSE)); + return $result; } /** - * Search for services matching a term + * Calculate the total traffic used in a month */ - public function list_autocomplete($term,$index,$value,array $label,array $limit=array(),array $options=NULL) { - // We only show invoice numbers. - if (! is_numeric($term)) - return array(); + private function get_traffic_data_month($period=NULL,$cache=0) { + $result = array(); - $ao = Auth::instance()->get_user(); + foreach ($this->get_traffic_data_daily($period,TRUE,$cache) as $tdata) + foreach ($tdata as $k => $v) + if (isset($result[$k])) + $result[$k] += $v; + else + $result[$k] = $v; - $options['key'] = 'id'; - $options['object'] = DB::select($this->_table_name.'.id',$this->_table_name.'.service_number') - ->from($this->_table_name) - ->join('service') - ->on('service.id','=',$this->_table_name.'.service_id') - ->where('service.account_id','IN',$ao->RTM->customers($ao->RTM)) - ->and_where($this->_table_name.'.service_number','like','%'.$term.'%'); - - return parent::list_autocomplete($term,$index,$value,$label,$limit,$options); + return $result; } } ?> diff --git a/modules/adsl/classes/Model/Service/Plugin/Adsl/Traffic.php b/modules/adsl/classes/Model/Service/Plugin/Adsl/Traffic.php index 33723b01..1635a387 100644 --- a/modules/adsl/classes/Model/Service/Plugin/Adsl/Traffic.php +++ b/modules/adsl/classes/Model/Service/Plugin/Adsl/Traffic.php @@ -21,13 +21,22 @@ class Model_Service_Plugin_Adsl_Traffic extends ORM_OSB { 'plan'=>array('model'=>'Service_Plugin_Adsl','foreign_key'=>'service_username','far_key'=>'service'), ); - private $metrics = array( - 'up_peak', - 'up_offpeak', - 'down_peak', - 'down_offpeak', - 'peer', - 'internal', + public static $metrics = array( + 'down_peak'=>'base_down_peak', + 'down_offpeak'=>'base_down_offpeak', + 'up_peak'=>'base_up_peak', + 'up_offpeak'=>'base_up_offpeak', + 'peer'=>'base_down_peak', + 'internal'=>'base_down_offpeak', + ); + + private $_friendly = array( + 'base_up_peak'=>'UP Peak', + 'base_up_offpeak'=>'UP Offpeak', + 'base_down_peak'=>'DOWN Peak', + 'base_down_offpeak'=>'DOWN Offpeak', + 'base_peer'=>'Peer', + 'base_internal'=>'Internal', ); public function rules() { @@ -39,12 +48,16 @@ class Model_Service_Plugin_Adsl_Traffic extends ORM_OSB { return $result; } + public function friendly($name) { + return isset($this->_friendly[$name]) ? $this->_friendly[$name] : $name; + } + public function save(Validation $validation = NULL) { // If all our values are zero/NULL, we'll ignore if our previous one was. if ($this->total() !== 0) return parent::save($validation); - $l = ORM::factory($this->_object_name)->where('service','=',$this->service)->where('supplier_id','=',$this->supplier_id)->list_last(); + $l = ORM::factory($this->_object_name)->where('service','=',$this->service)->where('supplier_id','=',$this->supplier_id)->find_last(); if ($l->total() !== 0) return parent::save($validation); @@ -57,10 +70,23 @@ class Model_Service_Plugin_Adsl_Traffic extends ORM_OSB { return $this; } + /** + * Generate the select statements to SUM up the traffic metrics that we use + */ + public function selectsummetric() { + foreach (Model_Service_Plugin_Adsl_Traffic::$metrics as $metric=>$v) + $this->select(array("sum(".$metric.")",$metric)); + + return $this; + } + + /** + * Total up the traffic into the metrics we use + */ public function total() { $result = 0; - foreach ($this->metrics as $metric) { + foreach (array_keys(Model_Service_Plugin_Adsl_Traffic::$metrics) as $metric) { if (is_null($this->$metric)) $this->$metric = 0; @@ -70,21 +96,26 @@ class Model_Service_Plugin_Adsl_Traffic extends ORM_OSB { return $result; } - public function traffic(Model_Product_Plugin_Adsl $plan) { - // Roll up the charges according to the product plan configuration - return ADSL::allowance(array( - 'base_down_peak'=>is_null($plan->base_down_peak) ? NULL : $this->down_peak, - 'base_down_offpeak'=>is_null($plan->base_down_offpeak) ? NULL : $this->down_offpeak, - 'base_up_peak'=>is_null($plan->base_up_peak) ? NULL : $this->up_peak, - 'base_up_offpeak'=>is_null($plan->base_up_offpeak) ? NULL : $this->up_offpeak, - 'extra_down_peak'=>$plan->extra_down_peak, - 'extra_down_offpeak'=>$plan->extra_down_offpeak, - 'extra_up_peak'=>$plan->extra_up_peak, - 'extra_up_offpeak'=>$plan->extra_up_offpeak, - )); + /** + * Collapse our traffic data into an array as per $this->_metric + */ + public function traffic_data() { + $result = array(); + + foreach (Model_Service_Plugin_Adsl_Traffic::$metrics as $metric=>$v) { + if (! isset($result[$v])) + $result[$v] = 0; + + $result[$v] += $this->{$metric}; + } + + return $result; } - public function list_last() { + /** + * Find the last traffic record + */ + public function find_last() { return $this->order_by('date','DESC')->order_by('time','DESC')->find(); } } diff --git a/modules/adsl/classes/Service/Traffic/Adsl.php b/modules/adsl/classes/Service/Traffic/Adsl.php index 16516dc0..644db4c3 100644 --- a/modules/adsl/classes/Service/Traffic/Adsl.php +++ b/modules/adsl/classes/Service/Traffic/Adsl.php @@ -106,42 +106,6 @@ abstract class Service_Traffic_Adsl { } } - public function charge_excess_traffic() { - $date = strtotime('last month'); - - // @todo need a way to find out services that have traffic charges dynamically. - foreach ($this->aso->services() as $so) { - if ($charge = $so->plugin()->traffic_lastmonth_exceed(FALSE,$date)) { - foreach ($charge as $metric => $details) { - $co = ORM::factory('Charge'); - - $co->status = 0; - $co->sweep_type = 6; - $co->account_id = $so->account_id; - $co->service_id = $so->id; - // @todo This needs to be calculated. - $co->type = 5; - $co->amount = $details['rate']; - // @todo This needs to be calculated. - $co->taxable = TRUE; - $co->quantity = ceil($details['excess']/1000); - $co->description = _('Excess Traffic'); - // @todo This need to be improved = strtotime function should be the one used in the function call - $co->attributes = implode("\n",array( - sprintf('ADSL Service==%s',$so->plugin()->service_number), - sprintf('Allowance==%s',$details['allowance']), - sprintf('Metric==%s',$metric), - sprintf('Used==%s',$details['used']), - sprintf('Month==%s',date('Y-m',$date)), - )); - - $co->check(); - $co->save(); - } - } - } - } - public function alert_traffic() { foreach ($this->aso->services() as $so) { if (! $so->plugin()->report_traffic()) diff --git a/modules/adsl/classes/Task/Adsl/Trafficcharge.php b/modules/adsl/classes/Task/Adsl/Trafficcharge.php index 9ee75983..c1b822e9 100644 --- a/modules/adsl/classes/Task/Adsl/Trafficcharge.php +++ b/modules/adsl/classes/Task/Adsl/Trafficcharge.php @@ -15,12 +15,50 @@ class Task_Adsl_Trafficcharge extends Task_Adsl_Trafficget { ); protected function _execute(array $params) { - foreach ($this->_traffic_suppliers(TRUE) as $aso) { - if ($params['verbose']) - echo $aso->name."\n"; + $date = strtotime('last month'); + $c = array(); - Service_Traffic_Adsl::instance($aso->name)->charge_excess_traffic(); + // @todo Pick up services that are no longer active, but were inactive < 30 days ago. + foreach (ORM::factory('Service')->list_byplugin('ADSL') as $so) { + if ($params['verbose']) + echo $so->service_name()."\n"; + + if ($x=$so->plugin()->traffic_excess($date)) { + $po = $so->plugin()->plan(); + $cost = $po->cost_extra(); + $allowance = $po->allowance(array(),FALSE,TRUE,1000); + $used = $so->plugin()->traffic_month($date,FALSE,1000); + + foreach ($x as $k=>$v) { + $co = ORM::factory('Charge'); + + // @todo This needs to be calculated. + $co->type = 5; + $co->sweep_type = 6; + + $co->account_id = $so->account_id; + $co->service_id = $so->id; + $co->amount = $cost[$k]; + $co->taxable = $so->product->taxable; + $co->quantity = $v; + $co->date_charge = time(); + $co->description = sprintf('Excess Traffic %s',date('Y-m',$date)); + + $co->attributes = array( + 'Allowance'=>$allowance[$k], + 'Used'=>$used[$k], + 'Metric'=>$so->plugin()->traffic->friendly($k), + 'Month'=>date('Y-m',$date), + ); + + $co->save(); + } + + array_push($c,$so->id); + } } + + return sprintf('%s excess charges created (%s)',count($c),join('|',$c)); } } ?> diff --git a/modules/adsl/views/adsl/reseller/billing.php b/modules/adsl/views/adsl/reseller/billing.php index 393cfec9..62ae3e7d 100644 --- a/modules/adsl/views/adsl/reseller/billing.php +++ b/modules/adsl/views/adsl/reseller/billing.php @@ -12,16 +12,16 @@ services(TRUE) as $so) : ?> - plugin(); $po = $p->product(); $service_number = $p->service_number; ?> + plugin(); $po = $p->admin_plan(); $service_number = $p->service_number; ?> - + id),$so->id); ?> adsl_supplier_plan->name().($p->provided_adsl_plan_id ? '*' : ''); ?> contract_date_start(TRUE); ?> contract_date_end(TRUE); ?> - adsl_supplier_plan->total()); ?> + adsl_supplier_plan->display('base_cost'); ?> charge($service_number)); ?> excess($service_number)); ?> diff --git a/modules/adsl/views/product/plugin/adsl/feature_summary.php b/modules/adsl/views/product/plugin/adsl/feature_summary.php index ba5d04b7..f1c2190c 100644 --- a/modules/adsl/views/product/plugin/adsl/feature_summary.php +++ b/modules/adsl/views/product/plugin/adsl/feature_summary.php @@ -4,14 +4,14 @@
Speed
-
adsl_supplier_plan->display('speed'); ?>
+
supplier_plan->display('speed'); ?>
Peak Downloads Included
-
base_down_peak/1000; ?> GB, excess display('extra_down_peak'); ?>/GB. +
base_down_peak/1000; ?> GB, excess display('extra_down_peak'); ?>/GB. - base_down_offpeak OR $po->extra_down_offpeak) : ?> + hasOffpeak()) : ?>
OffPeak Downloads Included
-
base_down_offpeak/1000; ?> GB, excess display('extra_down_offpeak'); ?>/GB. +
base_down_offpeak/1000; ?> GB, excess display('extra_down_offpeak'); ?>/GB.
diff --git a/modules/adsl/views/service/user/plugin/adsl/table_traffic.php b/modules/adsl/views/service/user/plugin/adsl/table_traffic.php index a90e29e8..4ef2b235 100644 --- a/modules/adsl/views/service/user/plugin/adsl/table_traffic.php +++ b/modules/adsl/views/service/user/plugin/adsl/table_traffic.php @@ -1,9 +1,19 @@ - - - $usage) { ?> - - - - - +
+ + + + + + + + $details) : ?> + + + + + + + + +
friendly($i); ?>
diff --git a/modules/adsl/views/service/user/plugin/adsl/view.php b/modules/adsl/views/service/user/plugin/adsl/view.php index ebc6f5d8..b035c141 100644 --- a/modules/adsl/views/service/user/plugin/adsl/view.php +++ b/modules/adsl/views/service/user/plugin/adsl/view.php @@ -39,22 +39,42 @@ Traffic Usage
-
This Month
-
 traffic_month(NULL); ?>
-
Last Month
-
 traffic_lastmonth(); ?>
+
 traffic_month(strtotime('last month'),TRUE,1000); ?> GB
+ +
This Month
+
 traffic_month(strtotime('yesterday'),TRUE,1000); ?> GB
+ +
Excess Traffic
+
 $traffic_excess(strtotime('yesterday'),TRUE,TRUE); ?>
- - - - - -
View Daily Traffic for Monthget_traffic_months()),(isset($_POST['month']) ? $_POST['month'] : '')); echo Form::submit('submit',_('Show'),array('class'=>'form_button')); echo Form::close(); ?>
+
+
+
View Traffic for
+
get_traffic_months()),(isset($_POST['month']) ? $_POST['month'] : ''),array('class'=>'input-small','nocg'=>TRUE)); ?> +
+
+
+ +
+ + +
+
+ traffic_graph(isset($_POST['month']) ? $_POST['month'] : ''); ?> +
+ +
+ traffic_table(isset($_POST['month']) ? $_POST['month'] : ''); ?> +
+ +
+
- graph_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?>
- table_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?>
diff --git a/modules/charge/classes/Model/Charge.php b/modules/charge/classes/Model/Charge.php index 8098f6ff..b422e33c 100644 --- a/modules/charge/classes/Model/Charge.php +++ b/modules/charge/classes/Model/Charge.php @@ -42,7 +42,7 @@ class Model_Charge extends ORM_OSB { array('StaticList_SweepType::get',array(':value')), ), 'void'=>array( - array('StaticList_YesNo::get',array(':value')), + array('StaticList_YesNo::get',array(':value',TRUE)), ), ); diff --git a/modules/domain/classes/Model/Service/Plugin/Domain.php b/modules/domain/classes/Model/Service/Plugin/Domain.php index a514acd4..a3553b08 100644 --- a/modules/domain/classes/Model/Service/Plugin/Domain.php +++ b/modules/domain/classes/Model/Service/Plugin/Domain.php @@ -52,11 +52,6 @@ class Model_Service_Plugin_Domain extends Model_Service_Plugin { return sprintf('%s - %s',_('Domain Name License'),$this->name()); } - public function service_view() { - return View::factory('service/user/plugin/domain/view') - ->set('o',$this); - } - public function username_value() { return $this->registrar_username; } diff --git a/modules/email/classes/Model/Email/Template.php b/modules/email/classes/Model/Email/Template.php index de015a4f..9e1d8cc4 100644 --- a/modules/email/classes/Model/Email/Template.php +++ b/modules/email/classes/Model/Email/Template.php @@ -23,7 +23,7 @@ class Model_Email_Template extends ORM_OSB { protected $_display_filters = array( 'status'=>array( - array('StaticList_YesNo::get',array(':value')), + array('StaticList_YesNo::get',array(':value',TRUE)), ), ); diff --git a/modules/export/views/export/module/admin/edit.php b/modules/export/views/export/module/admin/edit.php index 336db3fb..b2437c12 100644 --- a/modules/export/views/export/module/admin/edit.php +++ b/modules/export/views/export/module/admin/edit.php @@ -3,7 +3,7 @@
Export Module Update -

Available module->table_name(); ?> Columns to display

+

Available module->table_name(); ?> Columns to display

data($o->module->module()->table_columns()) diff --git a/modules/host/classes/Model/Service/Plugin/Host.php b/modules/host/classes/Model/Service/Plugin/Host.php index f980c2ca..131515bd 100644 --- a/modules/host/classes/Model/Service/Plugin/Host.php +++ b/modules/host/classes/Model/Service/Plugin/Host.php @@ -48,11 +48,6 @@ class Model_Service_Plugin_Host extends Model_Service_Plugin { return sprintf('%s.%s',$this->display('domain_name'),$this->domain_TLD->display('name')); } - public function service_view() { - return View::factory('service/user/plugin/host/view') - ->set('o',$this); - } - public function username_value() { return $this->host_username; } diff --git a/modules/invoice/classes/Model/Invoice.php b/modules/invoice/classes/Model/Invoice.php index 192e157d..5f8483f1 100644 --- a/modules/invoice/classes/Model/Invoice.php +++ b/modules/invoice/classes/Model/Invoice.php @@ -34,7 +34,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { array('Config::date',array(':value')), ), 'status'=>array( - array('StaticList_YesNo::get',array(':value')), + array('StaticList_YesNo::get',array(':value',TRUE)), ), ); diff --git a/modules/invoice/views/invoice/user/view/body.php b/modules/invoice/views/invoice/user/view/body.php index 0f37c8e1..68e754f0 100644 --- a/modules/invoice/views/invoice/user/view/body.php +++ b/modules/invoice/views/invoice/user/view/body.php @@ -7,7 +7,7 @@ service_id) : ?>   - service_id),$iio->service->id()).' ' : '').$iio->service->service_name(); ?> + service_id),$iio->service->id()).' ' : '').$iio->title(); ?>  
service_items_total($iio,TRUE); ?>
diff --git a/modules/product/classes/Model/Product.php b/modules/product/classes/Model/Product.php index 62703b96..d5e0d73c 100644 --- a/modules/product/classes/Model/Product.php +++ b/modules/product/classes/Model/Product.php @@ -28,10 +28,10 @@ class Model_Product extends ORM_OSB { array('StaticList_PriceType::get',array(':value')), ), 'status'=>array( - array('StaticList_YesNo::get',array(':value')), + array('StaticList_YesNo::get',array(':value',TRUE)), ), 'taxable'=>array( - array('StaticList_YesNo::get',array(':value')), + array('StaticList_YesNo::get',array(':value',TRUE)), ), ); diff --git a/modules/product/classes/Model/Product/Plugin.php b/modules/product/classes/Model/Product/Plugin.php index a5f097df..e6ac9df1 100644 --- a/modules/product/classes/Model/Product/Plugin.php +++ b/modules/product/classes/Model/Product/Plugin.php @@ -31,5 +31,12 @@ abstract class Model_Product_Plugin extends ORM_OSB { abstract public function feature_summary(); abstract public function supplier(); + + /** + * Return the name of the plugin + */ + protected function plugin() { + return strtolower(preg_replace('/(.*)_([a-zA-Z]+)$/',"$2",get_class($this))); + } } ?> diff --git a/modules/service/classes/Model/Service.php b/modules/service/classes/Model/Service.php index 2ebed119..82b7ad97 100644 --- a/modules/service/classes/Model/Service.php +++ b/modules/service/classes/Model/Service.php @@ -52,6 +52,9 @@ class Model_Service extends ORM_OSB { protected $_form = array('id'=>'id','value'=>'service_name()'); + // Cache our calls to our plugins + public static $plugin = array(); + /** * Get the additional charges associated with this service */ @@ -201,9 +204,10 @@ class Model_Service extends ORM_OSB { if (! $this->product->prod_plugin_file) return NULL; - $o = ORM::factory(Kohana::classname(sprintf('Service_Plugin_%s',$this->product->prod_plugin_file)),array('service_id'=>$this->id)); + if (! isset(Model_Service::$plugin[$this->id])) + Model_Service::$plugin[$this->id] = ORM::factory(Kohana::classname(sprintf('Service_Plugin_%s',$this->product->prod_plugin_file)),array('service_id'=>$this->id)); - return $type ? $o->$type : $o; + return $type ? Model_Service::$plugin[$this->id]->$type : Model_Service::$plugin[$this->id]; } public function revenue($annual=FALSE) { @@ -237,7 +241,7 @@ class Model_Service extends ORM_OSB { if (! $original AND ! is_null($this->price_override)) $p = $this->price_override; - + if ($tax) $p = Tax::add($p); @@ -266,10 +270,7 @@ class Model_Service extends ORM_OSB { * Enable the plugin to store data */ public function admin_update() { - if (is_null($plugin = $this->plugin())) - return NULL; - else - return $plugin->admin_update(); + return (is_null($plugin = $this->plugin())) ? NULL : $plugin->admin_update(); } public function transactions() { diff --git a/modules/service/classes/Model/Service/Plugin.php b/modules/service/classes/Model/Service/Plugin.php index 1e1572c0..12736098 100644 --- a/modules/service/classes/Model/Service/Plugin.php +++ b/modules/service/classes/Model/Service/Plugin.php @@ -13,33 +13,39 @@ abstract class Model_Service_Plugin extends ORM_OSB { // Reset any sorting that may be defined in our parent protected $_sorting = array(); - /** - * Our service name as defined in the DB - */ - abstract public function name(); - /** * When does our service expire */ abstract public function expire(); /** - * Show our service name as defined in the DB with product suffix. + * Our service name as defined in the DB */ - public function service_name() { - return sprintf('%s - %s',$this->service->product->title(),$this->name()); - } - - /** - * View details of the service - */ - abstract public function service_view(); + abstract public function name(); /** * The table attributes that provide username/password values */ - abstract public function username_value(); abstract public function password_value(); + abstract public function username_value(); + + /** + * Get specific service details for use in other modules + * For Example: Invoice + * + * @todo Make the rendered items configurable + * @todo Change this method name, now that it is public + */ + public function _details($type) { + switch ($type) { + // Nothing to add for invoices + case 'invoice_detail_items': + return array(); + + default: + throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type)); + } + } /** * Form info for admins to update @@ -49,6 +55,9 @@ abstract class Model_Service_Plugin extends ORM_OSB { ->set('o',$this); } + /** + * Provide the button that launches the management of this service, generally from a 3rd party + */ public function manage_button() { if (! $this->service->status OR $this->service->expiring()) return FALSE; @@ -79,26 +88,26 @@ function() { $("form[id=id_"+t[0]+"_"+t[1]+"]").submit(); }); return TRUE; } + /** + * Return the name of the plugin + */ protected function plugin() { return strtolower(preg_replace('/(.*)_([a-zA-Z]+)$/',"$2",get_class($this))); } /** - * Get specific service details for use in other modules - * For Example: Invoice - * - * @todo Make the rendered items configurable - * @todo Change this method name, now that it is public + * Show our service name as defined in the DB with product suffix. */ - public function _details($type) { - switch ($type) { - // Nothing to add for invoices - case 'invoice_detail_items': - return array(); + public function service_name() { + return sprintf('%s - %s',$this->service->product->title(),$this->name()); + } - default: - throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type)); - } + /** + * View details of the service + */ + public function service_view() { + return View::factory(sprintf('service/user/plugin/%s/view',$this->plugin())) + ->set('o',$this); } } ?> diff --git a/modules/ssl/classes/Controller/Reseller/Ssl.php b/modules/ssl/classes/Controller/Reseller/Ssl.php index c7f7b86d..c6fed9b4 100644 --- a/modules/ssl/classes/Controller/Reseller/Ssl.php +++ b/modules/ssl/classes/Controller/Reseller/Ssl.php @@ -124,7 +124,7 @@ class Controller_Reseller_SSL extends Controller_SSL { HTTP::redirect('welcome'); } - + $so->plugin()->renew(); HTTP::redirect(URL::link('user','service/view/'.$so->id)); } diff --git a/modules/ssl/classes/Model/Service/Plugin/Ssl.php b/modules/ssl/classes/Model/Service/Plugin/Ssl.php index 1db5a578..273abd60 100644 --- a/modules/ssl/classes/Model/Service/Plugin/Ssl.php +++ b/modules/ssl/classes/Model/Service/Plugin/Ssl.php @@ -40,12 +40,6 @@ class Model_Service_Plugin_Ssl extends Model_Service_Plugin { } public function password_value() {} // Not used - - public function service_view() { - return View::factory('service/user/plugin/ssl/view') - ->set('o',$this); - } - public function username_value() {} // Not used private $_so = NULL;