This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
khosb/modules/service/classes/controller/admin/service.php
2012-06-20 20:01:08 +10:00

757 lines
23 KiB
PHP

<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Admin Service functions
*
* @package OSB
* @subpackage Service
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
protected $secure_actions = array(
'autolist'=>FALSE, // @todo To Change
'adslstat'=>TRUE,
'list'=>TRUE,
'listbycheckout'=>TRUE,
'listadslbilling'=>TRUE,
'listadslservices'=>TRUE,
'listdomainservices'=>TRUE,
'listdomainservicesbysupplier'=>TRUE,
'listhostservices'=>TRUE,
'listhspaservices'=>TRUE,
'listwebservices'=>TRUE,
'listinvoicesoon'=>TRUE,
'update'=>TRUE,
'view'=>TRUE,
);
public function action_autolist() {
$return = array();
$s = ORM::factory('service')->where('active','=',1);
if (isset($_REQUEST['aid']))
$s = $s->where('account_id','=',$_REQUEST['aid']);
// @todo This should limit the results so that users dont see other users services.
foreach ($s->find_all() as $so)
array_push($return,array(
'value'=>$so->id,
'text'=>sprintf('%s: %s',$so->id,$so->service_name()),
));
$this->auto_render = FALSE;
$this->response->headers('Content-Type','application/json');
$this->response->body(json_encode($return));
}
/**
* Show a list of services
*/
public function action_list() {
Block::add(array(
'title'=>_('Customer Services'),
'body'=>Table::display(
ORM::factory('service')->find_all(),
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'active'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
/**
* List all services by their default checkout method
*/
public function action_listbycheckout() {
$svs = array();
// @todo This needs to be configurable
$go = ORM::factory('group',array('name'=>'Personal'));
foreach (ORM::factory('account')->where('status','=',1)->find_all() as $ao)
if ($ao->has_any('group',array($go)))
foreach ($ao->service->list_active() as $so)
if (! $so->service_billing->checkout_plugin_id)
array_push($svs,$so);
if ($svs)
Block::add(array(
'title'=>'Services that should be auto-billed',
'body'=>Table::display(
$svs,
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'active'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'date_next_invoice'=>array('label'=>'Next Invoice'),
'account->invoices_due_total(NULL,TRUE)'=>array('label'=>'Due Invoices'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
foreach (ORM::factory('checkout')->where('active','=',1)->find_all() as $co) {
$svs = array();
foreach ($co->account->find_all() as $ao)
foreach ($ao->service->list_active() as $so)
if ($so->service_billing->checkout_plugin_id == $co->id)
array_push($svs,$so);
if ($svs)
Block::add(array(
'title'=>$co->name,
'body'=>Table::display(
$svs,
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'active'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'date_next_invoice'=>array('label'=>'Next Invoice'),
'account->invoices_due_total(NULL,TRUE)'=>array('label'=>'Due Invoices'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
}
private function consoltraffic($svs,$date) {
$data = 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;
$c[$month] = 1;
}
if (isset($data['data'][$metric][$month]))
$data['data'][$metric][$month] += (int)$traffic;
else
$data['data'][$metric][$month] = (int)$traffic;
}
}
}
ksort($data['svs']);
foreach ($data['data'] as $metric => $details)
ksort($data['data'][$metric]);
return $data;
}
public function action_listadslservices() {
$svs = ORM::factory('service')->list_bylistgroup('ADSL');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('vertical_bar');
$google->title = sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday')));
$google->series(array('title'=>array_keys($data['data']),'axis'=>'x','data'=>$data['data']));
$google->series(array('title'=>'Services','axis'=>'r','data'=>array('Services'=>$data['svs'])));
Block::add(array('body'=>$google));
Block::add(array(
'title'=>_('ADSL Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'name()'=>array('label'=>'Service'),
'plugin()->ipaddress()'=>array('label'=>'IP Address'),
'product->plugin()->speed'=>array('label'=>'Speed'),
'product->plugin()->allowance()'=>array('label'=>'Allowance'),
'plugin()->traffic_thismonth()'=>array('label'=>'This Month'),
'plugin()->traffic_lastmonth()'=>array('label'=>'Last Month'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'date_next_invoice'=>array('label'=>'Next Invoice'),
'product->display("active")'=>array('label'=>'Current'),
),
array(
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_listhspaservices() {
$svs = ORM::factory('service')->list_bylistgroup('HSPA');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('vertical_bar');
$google->title = sprintf('HSPA traffic as at %s',date('Y-m-d',strtotime('yesterday')));
$google->series(array('title'=>array_keys($data['data']),'axis'=>'x','data'=>$data['data']));
$google->series(array('title'=>'Services','axis'=>'r','data'=>array('Services'=>$data['svs'])));
Block::add(array('body'=>$google));
Block::add(array(
'title'=>_('HSPA Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'name()'=>array('label'=>'Service'),
'plugin()->ipaddress()'=>array('label'=>'IP Address'),
'product->plugin()->speed'=>array('label'=>'Speed'),
'product->plugin()->allowance()'=>array('label'=>'Allowance'),
'plugin()->traffic_thismonth()'=>array('label'=>'This Month'),
'plugin()->traffic_lastmonth()'=>array('label'=>'Last Month'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'date_next_invoice'=>array('label'=>'Next Invoice'),
),
array(
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_listdomainservices() {
$svs = ORM::factory('service')->list_bylistgroup('DOMAIN');
Sort::MAsort($svs,'name()');
Block::add(array(
'title'=>_('Domain Names'),
'body'=>Table::display(
$svs,
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'plugin()->display("domain_expire")'=>array('label'=>'Expire'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_listdomainservicesbysupplier() {
$svs = ORM::factory('service')->list_bylistgroup('DOMAIN');
Sort::MAsort($svs,'plugin()->domain_registrar_id,name()');
$list = array();
foreach ($svs as $so)
$list[$so->plugin()->domain_registrar_id][] = $so;
foreach (array_keys($list) as $sid)
Block::add(array(
'title'=>sprintf(_('Domain Names by Supplier %s'),$sid),
'body'=>Table::display(
$list[$sid],
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'plugin()->display("domain_expire")'=>array('label'=>'Expire'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_listhostservices() {
$svs = ORM::factory('service')->list_bylistgroup('HOST');
Sort::MAsort($svs,'name()');
Block::add(array(
'title'=>_('Hosting Services'),
'body'=>Table::display(
$svs,
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'plugin()->display("host_expire")'=>array('label'=>'Expire'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_listwebservices() {
$svs = ORM::factory('service')->list_bylistgroup('WEB');
Sort::MAsort($svs,'name()');
Block::add(array(
'title'=>_('SSL Services'),
'body'=>Table::display(
$svs,
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
/**
* Reconcile billing for an ADSL supplier
*
* @todo this should really be in a different class, since adsl wont be part of the main app
*/
public function action_listadslbilling($id) {
$aso = ORM::factory('adsl_supplier',$id);
// Process upload
// @todo This should be separated out by supplier in case each supplier has a different format
if ($_FILES) {
$files = Validation::factory($_FILES)
->rule('csv','Upload::valid')
->rule('csv','Upload::not_empty')
->rule('csv','Upload::type',array(':value',array('csv')))
->rule('csv','Upload::size',array(':value','10M'));
if ($files->check())
foreach ($files as $file)
$csv = $this->process($file);
}
// @todo add a display if there are no items
$i = $j = 0;
$total = 0;
$summary = '';
$output = View::factory($this->viewpath().'/head');
$output .= '<table class="box-left">';
foreach ($aso->services(TRUE) as $so) {
$po = $so->plugin()->product();
// Reset our uploaded data
$uploaded = array();
$uploaded['excess'] = empty($csv[$so->plugin()->service_number]['excess']) ? 0 : $csv[$so->plugin()->service_number]['excess'];
// If our uploaded file has some cost data.
if (! empty($csv[$so->plugin()->service_number])) {
$uploaded['amount'] =
(empty($csv[$so->plugin()->service_number]['cost']) ? 0 : $csv[$so->plugin()->service_number]['cost']) +
(empty($csv[$so->plugin()->service_number]['credit']) ? 0 : $csv[$so->plugin()->service_number]['credit']);
// Record the the exception if the cost is not expected
if (round($po->adsl_supplier_plan->base_cost+$po->adsl_supplier_plan->tax(),2) != $uploaded['amount']) {
$summary .= View::factory($this->viewpath().'/summary')
->set('service',$so)
->set('plan',$po)
->set('planoverride',$so->plugin()->provided_adsl_plan_id ? TRUE : FALSE)
->set('amount',$uploaded['amount'])
->set('i',$j++%2);
$uploaded['checked'] = '';
} else {
$uploaded['checked'] = 'checked="checked"';
}
unset($csv[$so->plugin()->service_number]);
} else {
$uploaded['checked'] = '';
$uploaded['amount'] = 0;
}
$total += $uploaded['amount'];
$output .= View::factory($this->viewpath().'/body')
->set('service',$so)
->set('plan',$po)
->set('planoverride',$so->plugin()->provided_adsl_plan_id ? TRUE : FALSE)
->set('checked',$uploaded['checked'])
->set('amount',$uploaded['amount'])
->set('excess',$uploaded['excess'])
->set('adsl',$so->plugin())
->set('i',$i++%2);
}
$output .= View::factory($this->viewpath().'/foot')
->set('total',$total);
$output .= '</table>';
// Summary Report of remaining CSV items.
if (! empty($csv))
foreach ($csv as $service => $item) {
$summary .= View::factory($this->viewpath().'/summary_exception')
->set('service',$service)
->set('item',$item)
->set('i',$j++%2);
}
$output .= Form::open(NULL,array('enctype'=>'multipart/form-data'));
$output .= '<div>';
$output .= Form::file('csv');
$output .= Form::submit('submit','upload',array('class'=>'form_button'));
$output .= '</div>';
$output .= Form::close();
Block::add(array(
'title'=>_('ADSL Services'),
'body'=>$output,
));
if ($summary)
Block::add(array(
'title'=>_('Exception Charges'),
'body'=>'<table class="box-left">'.$summary.'</table>',
));
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
}
private function process(array $file) {
$data = file_get_contents($file['tmp_name']);
if (! $data)
return;
$start = $end = FALSE;
$return = array();
foreach (preg_split("/\n/",$data) as $line) {
// Items start after "Item ID"
if (! $start && preg_match('/^Item ID,/',$line)) {
$start = true;
continue;
// Items end after "Subtotal"
} elseif ($start && ! $end && preg_match('/^Subtotal:,/',$line)) {
$end = true;
continue;
// If we havent started or not ended, continue
} elseif (! $start || $end) {
continue;
}
list($id,$ref,$unknown,$linedata,$q,$cost,$total) = explode(',',$line);
// Extract the phone number from the $linedata
@list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$linedata)));
// If the description says Monthly Charge, we know its the monthly fee.
if (preg_match('/^Monthly Charge/',$description))
$return[$service]['cost'] = preg_replace('/\$/','',$total);
// If the description says VISP credit, we know this is commission.
elseif (preg_match('/^VISP Credit/',$description))
$return[$service]['credit'] = preg_replace('/\$/','',$total);
// If the description says Excess, we know this is excess charges.
elseif (preg_match('/^Excess usage/',$description))
$return[$service]['excess'] = preg_replace('/\$/','',$total);
else
$return[$service]['info'] = $line;
}
return $return;
}
/**
* List services that need to be invoiced.
*/
public function action_listinvoicesoon() {
Block::add(array(
'title'=>_('Services to Invoice'),
'body'=>Table::display(
ORM::factory('service')->list_invoicesoon(ORM::factory('invoice')->config('GEN_SOON_DAYS')),
25,
array(
'id'=>array('label'=>'ID','url'=>'user/service/view/'),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'date_next_invoice'=>array('label'=>'Next Invoice'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'charges_new()'=>array('label'=>'Charges','class'=>'right'),
'active'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/service/view',
)),
));
}
public function action_update() {
$id = $this->request->param('id');
$so = ORM::factory('service',$id);
if (! $so->loaded())
Request::current()->redirect('welcome/index');
if ($_POST) {
if (isset($_POST['plugin']) AND $_POST['plugin'])
if (! $so->plugin()->values($_POST['plugin'])->update()->saved())
throw new Kohana_Exception('Failed to save updates to plugin data for record :record',array(':record'=>$so->id()));
if (! $so->values($_POST)->update()->saved())
throw new Kohana_Exception('Failed to save updates to service data for record :record',array(':record'=>$so->id()));
}
Block::add(array(
'title'=>sprintf('%s %s:%s',_('Update Service'),$so->id(),$so->name()),
'body'=>View::factory($this->viewpath())
->set('so',$so)
->set('mediapath',Route::get('default/media'))
->set('plugin_form',$so->admin_update()),
));
// @todo Investigate a better way of preparing for jscalendar
Script::add(array(
'type'=>'file',
'data'=>'js/dhtml.calendar.js',
));
Script::add(array(
'type'=>'file',
'data'=>'js/dhtml.calendar-setup.js',
));
Script::add(array(
'type'=>'file',
'data'=>'js/dhtml.calendar-en.js',
));
Script::add(array(
'type'=>'file',
'data'=>'js/dhtml.date_selector.js',
));
Style::add(array(
'type'=>'file',
'data'=>'css/dhtml.calendar.css',
));
}
public function action_view() {
list($id,$output) = Table::page(__METHOD__);
$so = ORM::factory('service',$id);
if (! $so->loaded() OR ! Auth::instance()->authorised($so->account_id,$so->affiliate_id)) {
$this->template->content = 'Unauthorised or doesnt exist?';
return FALSE;
}
$doutput = $loutput = '';
$loutput .= View::factory($this->viewpath())
->set('so',$so);
// Validate the transactions
$bt = NULL;
$save = (isset($_REQUEST['go']) && $_REQUEST['go']=1) ? 1 : 0;
$xsid=197;
foreach ($so->transactions()->where('item_type','=',0)->find_all() as $iio) {
if ($iio->service_id == $xsid AND $iio->invoice_id < 3070) continue;
if ($iio->quantity < 0 OR $iio->price_base < 0)
continue;
if (in_array($iio->id,array(960)))
continue;
if ($iio->invoice_id > 4000 OR $iio->product->prod_plugin_file=="ADSL")
$a = FALSE;
else
$a = TRUE;
if (is_null($bt))
$bt = $iio->date_start;
// $doutput .= sprintf('%s: BT is: %s [%s]<br/>',$iio->id,Config::date($bt),$bt);
$pdata = Period::details($iio->recurring_schedule,$a ? NULL : $iio->product->price_recurr_weekday,$bt,TRUE);
switch ($iio->recurring_schedule) {
case 1:
case 2:
case 4:
case 5:
if ($iio->date_start != $pdata['start_time']) {
$doutput .= sprintf('%s: Set start_time: %s [%s]<br/>',$iio->id,Config::date($pdata['start_time']),$pdata['start_time']);
$iio->date_start=$pdata['start_time'];
}
if ($iio->date_stop != $pdata['end_time']) {
$doutput .= sprintf('%s: Set end_time: %s [%s] <br/>',$iio->id,Config::date($pdata['end_time']),$pdata['end_time']);
$iio->date_stop=$pdata['end_time'];
}
$bt = $pdata['end_time']+86400;
//$doutput .= sprintf('%s: BT now: %s (%s) [%s]<br/>',$iio->id,Config::date($bt),Config::date($pdata['end_time']),$bt);
break;
default:
$doutput .= sprintf('%s: %s Not handled',$iio->id,$iio->recurring_schedule);
}
//$doutput .= '<br/>';
if ($save) {
$iio->save();
}
}
if (isset($_REQUEST['go']))
$this->request->redirect('admin/service/view/'.$so->id);
Block::add(array(
'title'=>sprintf('Transaction History for %s: %s',$so->id(),$so->name()),
'body'=>$loutput,
));
Block::add(array(
'title'=>sprintf('Transaction Debug for %s: %s',$so->id(),$so->name()),
'body'=>$doutput,
));
$output .= View::factory('service/user/view')
->set('so',$so);
Block::add(array(
'title'=>sprintf('%s: %s',$so->id(),$so->service_name()),
'body'=>$output,
));
}
public function action_adslstat() {
$output = '';
$svs = ORM::factory('service')->list_bylistgroup('ADSL');
$stats = array();
// @todo This needs to be configurable.
$traffic = array(1000,2000,5000,10000,25000,50000,75000,100000);
$ts = 0;
foreach ($svs as $a=>$so) {
// Number of services
if (! isset($stats[$so->product->plugin()->speed]['c']))
$stats[$so->product->plugin()->speed]['c'] = 0;
$stats[$so->product->plugin()->speed]['c']++;
$ts++;
// Amount of traffic
$t = array_sum($so->plugin()->traffic_lastmonth(FALSE));
$a = 0;
foreach (array_reverse($traffic) as $i) {
if ($i < $t)
break;
$a = $i;
}
if (! isset($stats[$so->product->plugin()->speed]['d'][$a]))
$stats[$so->product->plugin()->speed]['d'][$a] = 0;
$stats[$so->product->plugin()->speed]['d'][$a]++;
}
if (count($stats)) {
$output .= View::factory($this->viewpath().'/head')
->set('name','SPEED');
$output .= View::factory($this->viewpath().'/head_data')
->set('name','#');
foreach ($traffic as $i)
$output .= View::factory($this->viewpath().'/head_data')
->set('name',$i);
$output .= View::factory($this->viewpath().'/head_data')
->set('name','Other');
$output .= View::factory($this->viewpath().'/head_end');
foreach ($stats as $speed => $details) {
$output .= View::factory($this->viewpath().'/body_head')
->set('count',$details['c'])
->set('percent',sprintf('%2.1f',$details['c']/$ts*100))
->set('speed',$speed);
foreach ($traffic as $i) {
$output .= View::factory($this->viewpath().'/body_data')
->set('count',$c=isset($details['d'][$i]) ? $details['d'][$i] : 0)
->set('percent',sprintf('%2.1f',$c/$details['c']*100));
}
$output .= View::factory($this->viewpath().'/body_data')
->set('count',$c=isset($details['d'][0]) ? $details['d'][0] : 0)
->set('percent',sprintf('%2.1f',$c/$details['c']*100));
$output .= View::factory($this->viewpath().'/body_end');
}
$output .= View::factory($this->viewpath().'/foot');
}
Block::add(array(
'title'=>_('ADSL Traffic Summary Stats - Last Month'),
'body'=>$output,
));
}
}
?>