Open Source Billing

This commit is contained in:
Deon George
2013-10-10 13:44:53 +11:00
commit b02d70adf0
2344 changed files with 392978 additions and 0 deletions

View File

@@ -0,0 +1,914 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Admin Service functions
*
* @package Service
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
// @todo This "module" menu items should belong in the module dir.
protected $secure_actions = array(
'ajaxlist'=>TRUE,
'ajaxjson_traffic'=>TRUE,
'adslstat'=>TRUE,
'list'=>TRUE,
'listbycheckout'=>TRUE,
'listadslbilling'=>TRUE,
'listadslservices'=>TRUE,
'listexpiring'=>TRUE,
'listdomainservices'=>TRUE,
'listdomainservicesbysupplier'=>TRUE,
'listdomainservicesbydnshost'=>TRUE,
'listhostservices'=>TRUE,
'listhostservicesbysupplier'=>TRUE,
'listhspaservices'=>TRUE,
'listwebservices'=>TRUE,
'listinvoicesoon'=>TRUE,
'update'=>TRUE,
'view'=>TRUE,
);
public function action_ajaxlist() {
$result = array();
$result += ORM::factory('Service')->list_autocomplete(
isset($_REQUEST['term']) ? $_REQUEST['term'] : '',
'id',
isset($_REQUEST['aid']) ? array(array('account_id','=',$_REQUEST['aid'])) : array());
$this->auto_render = FALSE;
$this->response->headers('Content-Type','application/json');
$this->response->body(json_encode(array_values($result)));
}
public function action_ajaxjson_traffic() {
$result = array();
$svs = ORM::factory('Service')->list_bylistgroup('ADSL');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('ComboChart')
->stacked(TRUE);
foreach ($data['data'] as $key => $values)
$google->sdata(array('yl'=>$key),array($key=>$values));
$google->sdata(array('yr'=>'services'),array('services'=>$data['svs']));
$this->auto_render = FALSE;
$this->response->headers('Content-Type','application/json');
$this->response->body($google->json());
}
/**
* Show a list of services
*/
public function action_list() {
$id = $this->request->param('id');
$svs = ORM::factory('Service');
if ($id)
$svs->where_active()->and_where('account_id','=',$id);
Block::add(array(
'title'=>_('Customer Services'),
'body'=>Table::display(
$svs->find_all(),
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>URL::link('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')->list_active() 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'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>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'=>URL::link('user','service/view'),
)),
));
foreach (ORM::factory('Checkout')->list_active() 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'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>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'=>URL::link('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() {
$id = $this->request->param('id');
$svs = $list = ORM::factory('Service')->list_bylistgroup('ADSL');
if ($id) {
$svs = array();
foreach ($list as $so)
if ($so->account_id == $id)
array_push($svs,$so);
}
$google = GoogleChart::factory('ComboChart')
->dataurl(URL::link('admin','service/ajaxjson_traffic',TRUE))
->title(sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
Block::add(array('body'=>(string)$google));
Block::add(array(
'title'=>_('ADSL Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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("status")'=>array('label'=>'Current'),
),
array(
'type'=>'select',
'form'=>URL::link('user','service/view'),
)),
));
}
/**
* Show a list of services that are expiring or have expired
*/
public function action_listexpiring() {
$svs = ORM::factory('Service')->list_expiring();
Sort::MAsort($svs,'expire()');
Block::add(array(
'title'=>_('ADSL Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Service'),
'expire(TRUE)'=>array('label'=>'Expires'),
'due(TRUE)'=>array('label'=>'Due'),
),
array(
'type'=>'select',
'form'=>URL::link('user','service/view'),
)),
));
}
public function action_listhspaservices() {
$svs = ORM::factory('Service')->list_bylistgroup('HSPA');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('Legacy')
->type('vertical_bar')
->title(sprintf('HSPA traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
foreach ($data['data'] as $key => $values)
$google->data(array('yl'=>$key),array($key=>$values));
$google->data(array('yr'=>'services'),array('services'=>$data['svs']));
Block::add(array('body'=>(string)$google));
Block::add(array(
'title'=>_('HSPA Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('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'=>URL::link('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'=>URL::link('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'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
public function action_listdomainservicesbydnshost() {
$svs = ORM::factory('Service')->list_bylistgroup('DOMAIN');
Sort::MAsort($svs,'plugin()->service_plugin_host,name()');
$list = array();
foreach ($svs as $so)
$list[$so->plugin()->service_plugin_host->host_server_id][] = $so;
foreach (array_keys($list) as $sid)
Block::add(array(
'title'=>sprintf(_('Domain Names by DNS Host [%s]'),$sid),
'body'=>Table::display(
$list[$sid],
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'plugin()->domain_registrar->id'=>array('label'=>'SID'),
'plugin()->domain_registrar->name'=>array('label'=>'Supplier'),
'display("date_next_invoice")'=>array('label'=>'Next Invoice'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>URL::link('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'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
public function action_listhostservicesbysupplier() {
$svs = ORM::factory('Service')->list_bylistgroup('HOST');
Sort::MAsort($svs,'plugin()->host_server,name()');
$list = array();
foreach ($svs as $so)
$list[$so->plugin()->host_server_id][] = $so;
foreach (array_keys($list) as $sid)
Block::add(array(
'title'=>sprintf(_('Hosting by Supplier %s'),$sid),
'body'=>Table::display(
$list[$sid],
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('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'=>URL::link('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'=>URL::link('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 = $this->request->param('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->data() 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;
$result = 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;
}
$record = explode(',',$line);
// 1 = Item ID (ignore)
// 2 = Reference ID (ignore - its not useful for us)
// 3 = Category (always appears blank)
// 4 = Item Description (has our service number, rental and excess charges description)
// 0nnnnnnnnn - Monthly Internet Charge On Plan XXXXX For billing period (dd/mm/yyyy - dd/mm/yyyy) (7 FIELDED LINES)
// 0nnnnnnnnn - Excess usage charges for March 2013 (8 FIELDED LINES)
// 5 = Quantity
// Always 1 for Plan Fees
// 0nnnnnnnnn@graytech.net.au Excess Usage y GB (for excess charges)
// 6 = Unit Price
// Always 1 for Excess Usage (probably quantity)
// 7 = Total Price
// Unit price for Excess Usage
// 8 = Total Price for Excess Usage
if (! count($record) >= 7)
throw Kohana_Exception('Format of CSV file changed? (:record)',array(':record'=>$record));
if (preg_match('/Monthly Internet 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]);
} elseif (preg_match('/VISP Credit/',$record[3])) {
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
$result[$service]['credit'] = str_replace('$','',$record[6]);
} elseif (preg_match('/Excess usage charges for /',$record[3])) {
list($service,$description) = explode(':',(preg_replace('/([0-9]+)\s+-\s+(.*)$/',"$1:$2",$record[3])));
$result[$service]['excess'] = str_replace('$','',$record[7]);
// Ignore Payment For Invoice lines
} elseif (preg_match('/Payment For Invoice:/',$record[3])) {
} else {
try {
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;
}
}
}
return $result;
}
/**
* 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'=>URL::link('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'),
'status'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>URL::link('user','service/view'),
)),
));
}
public function action_update() {
$id = $this->request->param('id');
$so = ORM::factory('Service',$id);
if (! $so->loaded())
HTTP::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)) {
$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->invoice->status) continue;
// @todo This hard coding of 3070 should be removed.
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']))
HTTP::redirect(URL::link('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,
));
}
}
?>

View File

@@ -0,0 +1,287 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Affiliate Service functions
*
* @package Service
* @category Controllers/Affiliate
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Affiliate_Service extends Controller_TemplateDefault_Affiliate {
protected $secure_actions = array(
'list'=>TRUE,
'listbycheckout'=>TRUE,
'listadslservices'=>TRUE,
'listdomainservices'=>TRUE,
'listhostservices'=>TRUE,
'listhspaservices'=>TRUE,
);
/**
* Show a list of services
*/
public function action_list() {
Block::add(array(
'title'=>_('Customer Services'),
'body'=>Table::display(
$this->filter(ORM::factory('Service')->list_active(),$this->ao->RTM->customers($this->ao->RTM),NULL,'account_id'),
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>array('label'=>'Active'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>URL::link('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')->list_active() as $ao)
if ($ao->has_any('group',array($go)))
foreach ($this->filter($ao->service->list_active(),$this->ao->RTM->customers($this->ao->RTM),NULL,'account_id') 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'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>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'=>URL::link('user','service/view'),
)),
));
else
Block::add(array(
'title'=>'Services that should be auto-billed',
'body'=>_('None found'),
));
foreach (ORM::factory('Checkout')->list_active() as $co) {
$svs = array();
foreach ($co->account->find_all() as $ao)
foreach ($this->filter($ao->service->list_active(),$this->ao->RTM->customers($this->ao->RTM),NULL,'account_id') 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'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>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'=>URL::link('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 = $this->filter(ORM::factory('Service')->list_bylistgroup('ADSL'),$this->ao->RTM->customers($this->ao->RTM),'name()','account_id');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('Legacy')
->type('vertical_bar')
->title(sprintf('ADSL traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
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::add(array(
'title'=>_('ADSL Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
public function action_listhspaservices() {
$svs = $this->filter(ORM::factory('Service')->list_bylistgroup('HSPA'),$this->ao->RTM->customers($this->ao->RTM),'name()','account_id');
$data = $this->consoltraffic($svs,time());
$google = GoogleChart::factory('Legacy')
->type('vertical_bar')
->title(sprintf('HSPA traffic as at %s',date('Y-m-d',strtotime('yesterday'))));
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::add(array(
'title'=>_('HSPA Services'),
'body'=>Table::display(
$svs,
NULL,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
public function action_listdomainservices() {
Block::add(array(
'title'=>_('Domain Names'),
'body'=>Table::display(
$this->filter(ORM::factory('Service')->list_bylistgroup('DOMAIN'),$this->ao->RTM->customers($this->ao->RTM),'name()','account_id'),
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
public function action_listhostservices() {
Block::add(array(
'title'=>_('Hosting Services'),
'body'=>Table::display(
$this->filter(ORM::factory('Service')->list_bylistgroup('HOST'),$this->ao->RTM->customers($this->ao->RTM),'name()','account_id'),
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('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'=>URL::link('user','service/view'),
)),
));
}
}
?>

View File

@@ -0,0 +1,14 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides service management
*
* @package Service
* @category Controllers
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Service extends Controller_TemplateDefault {
}
?>

View File

@@ -0,0 +1,47 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides OSB service task capabilities.
*
* @package Service
* @category Controllers/Task
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Task_Service extends Controller_Task {
private function _traffic_suppliers($active=FALSE) {
$suppliers = ORM::factory('ADSL_Supplier');
return $active ? $suppliers->list_active() : $suppliers->find_all();
}
/**
* List all services by their default checkout method
*/
public function action_gettraffic() {
foreach ($this->_traffic_suppliers(TRUE) as $aso) {
if (Minion_CLI::options('verbose'))
echo $aso->name."\n";
$traffic = Service_Traffic_Adsl::instance($aso->name)->update_traffic();
}
}
/**
* Charges for excess traffic usage
*/
public function action_chargetraffic() {
foreach ($this->_traffic_suppliers(TRUE) as $aso)
$traffic = Service_Traffic_Adsl::instance($aso->name)->charge_excess_traffic();
}
/**
* Send alerts to users when they exceed their traffic allowance
*/
public function action_alerttraffic() {
foreach ($this->_traffic_suppliers(TRUE) as $aso)
$traffic = Service_Traffic_Adsl::instance($aso->name)->alert_traffic();
}
}
?>

View File

@@ -0,0 +1,78 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides User Service functions
*
* @package Service
* @category Controllers/User
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_User_Service extends Controller_TemplateDefault_User {
protected $secure_actions = array(
'ajaxmanage'=>TRUE,
'list'=>TRUE,
'view'=>TRUE,
);
public function action_ajaxmanage() {
$this->auto_render = FALSE;
$so = ORM::factory('Service',$this->request->param('id'));
$k = Session::instance()->get_once('manage_button');
$amo = $so->plugin(isset($_REQUEST['t']) ? $_REQUEST['t'] : '');
$o = array(
'u'=>$amo->username_value() ? $amo->username_value() : strtolower($amo->name()),
'p'=>(! $k OR ! $this->request->is_ajax() OR ! $so->loaded() OR ! isset($_REQUEST['k']) OR $k != $_REQUEST['k']) ? Random::char() : $amo->password_value(),
);
$this->response->headers('Content-Type','application/json');
$this->response->body(json_encode($o));
}
/**
* Show a list of services
*/
public function action_list() {
Block::add(array(
'title'=>sprintf('%s: %s - %s',_('Services For'),$this->ao->accnum(),$this->ao->name(TRUE)),
'body'=>Table::display(
$this->ao->service->find_all(),
25,
array(
'id'=>array('label'=>'ID','url'=>URL::link('user','service/view/')),
'service_name()'=>array('label'=>'Details'),
'recur_schedule'=>array('label'=>'Billing'),
'price(TRUE,TRUE)'=>array('label'=>'Price','class'=>'right'),
'status'=>array('label'=>'Active'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>URL::link('user','service/view'),
)),
));
}
public function action_view() {
list($id,$output) = Table::page(__METHOD__);
$so = ORM::factory('Service',$id);
if (! $so->loaded() OR ! Auth::instance()->authorised($so->account)) {
$this->template->content = 'Unauthorised or doesnt exist?';
return FALSE;
}
$output .= View::factory($this->viewpath())
->set('so',$so);
Block::add(array(
'title'=>sprintf('%s: %s',$so->id(),$so->service_name()),
'body'=>$output,
));
}
}
?>

View File

@@ -0,0 +1,268 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports Services
*
* @package Service
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*
* Fields:
* + queue: PROVISION (to be provisioned)
*/
class Model_Service extends ORM_OSB {
// Relationships
protected $_has_one = array(
'service_billing'=>array('far_key'=>'account_billing_id','foreign_key'=>'id'),
);
protected $_has_many = array(
'invoice_item'=>array('far_key'=>'id'),
'invoice'=>array('through'=>'invoice_item'),
'service_change'=>array('far_key'=>'id'),
);
protected $_belongs_to = array(
'product'=>array(),
'account'=>array(),
);
/**
* Filters used to format the display of values into friendlier values
*/
protected $_display_filters = array(
'date_last_invoice'=>array(
array('Config::date',array(':value')),
),
'date_next_invoice'=>array(
array('Config::date',array(':value')),
),
'recur_schedule'=>array(
array('StaticList_RecurSchedule::display',array(':value')),
),
'status'=>array(
array('StaticList_YesNo::display',array(':value')),
),
);
/**
* Return the object of the product plugin
*/
public function plugin($type='') {
if (! $this->product->prod_plugin_file)
return NULL;
if (! is_numeric($this->product->prod_plugin_data))
throw new Kohana_Exception('Missing plugin_id for :product (:type)',array(':product'=>$this->product->id,':type'=>$this->product->prod_plugin_file));
$o = ORM::factory(Kohana::classname(sprintf('Service_Plugin_%s',$this->product->prod_plugin_file)),array('service_id'=>$this->id));
return $type ? $o->$type : $o;
}
/**
* Display the service number
*/
public function id() {
return sprintf('%05s',$this->id);
}
/**
* Display the service product name
*/
public function name() {
return is_null($plugin=$this->plugin()) ? $this->product->name() : $plugin->name();
}
public function pending_change() {
return count($this->service_change->where_active()->where_open()->and_where('complete','!=',1)->or_where('complete','IS',null)->where_close()->find_all()->as_array());
}
/**
* Display how much is due on this service
*/
public function due($format=FALSE) {
$total = 0;
foreach ($this->list_invoices(TRUE) as $io)
$total += $io->due();
return $format ? Currency::display($total) : $total;
}
/**
* When does this service expire
*/
public function expire($format=FALSE) {
// For plugins the plugin determins expiry
$expire = (is_null($plugin=$this->plugin()) ? NULL : $plugin->expire());
// If $expire is NULL, we'll use the next invoice date
$expire = is_null($expire) ? $this->date_next_invoice-86400 : $expire;
return $format ? Config::date($expire) : $expire;
}
/**
* Determine if a service expires in the next $days.
*/
public function expiring($days) {
return time()+$days*86400 > $this->expire();
}
/**
* Return the service charge
*/
public function price($tax=FALSE,$format=FALSE) {
$x = $this->product->keyget('price_group',$this->recur_schedule);
// @todo This index shouldnt be hard coded.
$p = $this->price ? $this->price : $x[$this->price_group]['price_base'];
if ($tax)
$p = Tax::add($p);
return $format ? Currency::display($p) : $p;
}
public function service_name() {
return is_null($plugin=$this->plugin()) ? $this->name() : $plugin->service_name();
}
public function service_view() {
return is_null($plugin=$this->plugin()) ? HTML::nbsp('') : $plugin->service_view();
}
/**
* Display the product feature summary
*/
public function product_feature_summary() {
return $this->product->feature_summary();
}
/**
* Render some details for specific calls, eg: invoice
*/
public function details($type) {
switch ($type) {
case 'invoice_detail_items':
if (is_null($plugin = $this->plugin()))
return array();
else
return $plugin->_details($type);
break;
default:
throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type));
}
}
/**
* Enable the plugin to store data
*/
public function admin_update() {
if (is_null($plugin = $this->plugin()))
return NULL;
else
return $plugin->admin_update();
}
public function transactions() {
return $this->invoice_item->order_by('date_start,date_stop');
}
// @todo To implement
public function charges_new() {
return 0;
}
/** LIST FUNCTIONS **/
/**
* Search for services matching a term
*/
public function list_autocomplete($term,$index='id',array $limit=array()) {
$result = array();
$this->clear();
$this->where_active();
$value = 'service_name()';
// Build our where clause
$this->where_open()
->where('id','like','%'.$term.'%')
->where_close();
foreach ($limit as $w) {
list($k,$s,$v) = $w;
$this->and_where($k,$s,$v);
}
foreach ($this->find_all() as $o)
$result[$o->$index] = array(
'value'=>$o->$index,
'label'=>sprintf('SVC %s: %s',$o->id,Table::resolve($o,$value)),
);
return $result;
}
public function list_bylistgroup($cat) {
$result = array();
$cats = ORM::factory('Product_Category')->list_bylistgroup($cat);
foreach ($this->list_active() as $so)
if (array_intersect($so->product->avail_category,array_keys($cats)))
array_push($result,$so);
return $result;
}
/**
* List services expiring
*/
public function list_expiring($days=14) {
$result = array();
foreach ($this->list_active() as $so)
if ($so->expiring($days))
array_push($result,$so);
return $result;
}
/**
* List invoices for this service
*/
public function list_invoices($due=FALSE) {
$result = array();
foreach ($this->invoice->find_all() as $io)
if (! $due OR $io->due())
array_push($result,$io);
return $result;
}
/**
* List services that need to be billed.
*
* @param $days int Additional number of days to add to the query, above the module config.
*/
public function list_invoicesoon($days=0) {
return $this->_where_active()
->where_open()->where('suspend_billing','IS',NULL)->or_where('suspend_billing','=','0')->where_close()
->where('date_next_invoice','<',time()+(ORM::factory('Invoice')->config('GEN_DAYS')+$days)*86400)
->find_all();
}
/**
* List services that need to be provisioned
*/
public function list_provision() {
return $this->_where_active()->where('queue','=','PROVISION');
}
}
?>

View File

@@ -0,0 +1,20 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports Service Billing.
*
* @package Service
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Service_Billing extends ORM_OSB {
protected $_table_name = 'account_billing';
// Relationships
protected $_has_one = array(
'checkout'=>array('far_key'=>'checkout_plugin_id','foreign_key'=>'id'),
);
}
?>

View File

@@ -0,0 +1,48 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports Service Product Changes.
*
* @package Service
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Service_Change extends ORM_OSB {
protected $_table_name = 'service_change';
// Relationships
protected $_belongs_to = array(
'product'=>array(),
'service'=>array(),
);
/**
* Filters used to format the display of values into friendlier values
*/
protected $_display_filters = array(
'date_ordered'=>array(
array('Config::date',array(':value')),
),
'date_effective'=>array(
array('Config::date',array(':value')),
),
);
public function list_details() {
if ($this->_db_pending) {
$output = array();
foreach ($this->find_all() as $sco) {
array_push($output,sprintf('%s %s',$sco->product->name(),$sco->display('date_effective')));
}
} else {
throw new Kohana_Exception('Shouldnt be here!');
}
return join('|',$output);
}
}
?>

View File

@@ -0,0 +1,97 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports Service Plugins.
*
* @package Service
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
abstract class Model_Service_Plugin extends ORM_OSB {
// Reset any sorting that may be defined in our parent
protected $_sorting = array();
/**
* Form info for admins to update
*/
abstract public function admin_update();
/**
* 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.
*/
public function service_name() {
return sprintf('%s - %s',$this->service->product->name(),$this->name());
}
/**
* View details of the service
*/
abstract public function service_view();
/**
* The table attributes that provide username/password values
*/
abstract public function username_value();
abstract public function password_value();
public function manage_button() {
if (! $this->service->status)
return FALSE;
static $k = '';
// If $k is already set, we've rendered this JS
if ($k)
return TRUE;
$k = Random::char();
Session::instance()->set('manage_button',$k);
Script::add(array('type'=>'stdin','data'=>'
$(document).ready(function() {
var x=0;
$("button[name=submit]").click(function() {
var t=$(this).val().split(":");
if (x++) { alert("Session expired, please refresh the page!"); return false; }
$.getJSON("'.URL::link('user','service/ajaxmanage/'.$this->service_id,TRUE).'", { k: "'.$k.'",t: t[1] }, function(data) {
$.each(data, function(key, val) { $("#"+key+"_"+t[0]+"_"+t[1]).val(val); });
}).error(function() { alert("There was a problem with the request"); return false; }).success(
function() { $("form[id=id_"+t[0]+"_"+t[1]+"]").submit(); });
});
});'
));
return TRUE;
}
/**
* 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));
}
}
}
?>

View File

@@ -0,0 +1,64 @@
<!-- @todo NEEDS TO BE TRANSLATED -->
<?php echo Form::open(); ?>
<table width="100%">
<tr>
<td style="width: 50%; vertical-align: top;">
<table width="100%">
<tr>
<td style="width: 40%;">Service Active</td>
<td style="width: 60%;" class="data"><?php echo StaticList_YesNo::form('status',$so->status); ?></td>
</tr>
<tr>
<td>Queue</td>
<td class="data"><?php echo $so->display('queue'); ?></td>
</tr>
<tr>
<td>Billing Period</td>
<td class="data"><?php echo StaticList_RecurSchedule::form('recure_schedule',$so->product,$so->recur_schedule);?></td>
</tr>
<tr>
<td>Date Last Invoice</td>
<td class="data"><?php echo $so->display('date_last_invoice'); ?></td>
</tr>
<tr>
<td>Date Next Invoice</td>
<td class="data">
<?php echo Form::input('date_next_invoice',$so->date_next_invoice,array('id'=>'date_next_invoice')); ?>
<?php echo HTML::anchor('#',
HTML::image($mediapath->uri(array('file'=>'img/calendar.png')),array('alt'=>_('Calendar'),'style'=>'cursor: pointer;')),
array('title'=>'Click to popup a dialog to select a date graphically','onclick'=>"dateSelector('date_next_invoice')")); ?>
<script type="text/javascript">defaults['date_next_invoice'] = '%s';</script>
</td>
</tr>
<tr>
<td>Taxable</td>
<td class="data"><?php echo StaticList_YesNo::form('taxable',$so->taxable); ?></td>
</tr>
<tr>
<td>Type</td>
<td class="data"><?php echo StaticList_RecurType::display($so->recur_type); ?></td>
</tr>
<tr>
<td>User Can Change Schedule</td>
<td class="data"><?php echo StaticList_YesNo::form('recur_schedule_change',$so->recur_schedule_change); ?></td>
</tr>
<tr>
<td>User Can Cancel</td>
<td class="data"><?php echo StaticList_YesNo::form('recur_cancel',$so->recur_cancel); ?></td>
</tr>
<tr>
<td>User Can Modify</td>
<td class="data"><?php echo StaticList_YesNo::form('recur_modify',$so->recur_modify); ?></td>
</tr>
<tr>
<td>Suspend Billing</td>
<td class="data"><?php echo StaticList_YesNo::form('suspend_billing',$so->suspend_billing); ?></td>
</tr>
</table>
<?php if ($plugin_form) { echo '<br/>'.$plugin_form; } ?>
<!-- END Service Information -->
</td>
</tr>
</table>
<?php echo Form::submit('submit',_('Update'),array('class'=>'form_button')); ?>
<?php echo Form::close(); ?>

View File

@@ -0,0 +1,40 @@
<?php if ($so->pending_change()) {?>
Pending change to: <?php echo $so->service_change->list_details(); ?>
<?php } ?>
<br/>
<!-- @todo NEEDS TO BE TRANSLATED -->
<table class="list-box-left">
<tr class="list-head">
<th>ID</th>
<th>Invoice</th>
<th>Product</th>
<th>IT</th>
<th>RS</th>
<th>Start</th>
<th>Stop</th>
<th>Desc</th>
<th>Quantity</th>
<th>Charge</th>
</tr>
<?php $lp=NULL; foreach ($so->transactions()->find_all() as $iio) { ?>
<?php if (! is_null($iio->product_id) AND $lp != $iio->product_id) {
$lp = $iio->product_id; ?>
<tr class="list-sub-head">
<th class="id"><?php echo $iio->product_id; ?></th>
<th colspan="9"><?php echo $iio->product->name(); ?></th>
</tr>
<?php } ?>
<tr class="list-data">
<td class="id"><?php echo $iio->id; ?></td>
<td><?php echo HTML::anchor(URL::link('user','invoice/view/'.$iio->invoice_id),$iio->invoice_id); ?></td>
<td><?php echo $iio->display('product_id'); ?></td>
<td><?php echo $iio->display('item_type'); ?></td>
<td><?php echo $iio->display('recurring_schedule'); ?></td>
<td><?php echo $iio->display('date_start'); ?></td>
<td><?php echo $iio->display('date_stop'); ?></td>
<td><?php echo $iio->display('product_name'); ?></td>
<td><?php echo $iio->display('quantity'); ?></td>
<td class="right"><?php echo $iio->display('price_base'); ?></td>
</tr>
<?php } ?>
</table>

View File

@@ -0,0 +1,80 @@
<!-- @todo NEEDS TO BE TRANSLATED -->
<table width="100%">
<tr>
<td>
<!-- Service Information -->
<table class="box-full">
<tr>
<td style="width: 40%;">Account</td>
<td style="width: 60%;" class="data"><?php printf('%s (%s)',$so->account->name(),$so->account->accnum()); ?></td>
</tr>
<tr>
<td>Service Active</td>
<td class="data"><?php echo $so->display('status'); ?></td>
</tr>
<tr>
<td>Billing Period</td>
<td class="data"><?php echo $so->display('recur_schedule');?></td>
</tr>
<tr>
<td>Cost</td>
<td class="data"><?php echo $so->price(TRUE,TRUE); if ($so->pending_change()) echo ' *'; ?></td>
</tr>
<?php if (is_null($so->price) OR ($so->price<=$so->product->price($so->price_group,$so->recur_schedule,'price_base'))) { ?>
<tr>
<td>Service</td>
<td class="data"><?php echo HTML::anchor('product/view/'.$so->product_id,$so->product->name()); ?></td>
</tr>
<?php } ?>
<tr>
<td>Date Next Invoice</td>
<td class="data"><?php echo $so->display('date_next_invoice'); ?></td>
</tr>
<tr>
<td>Current Invoices Due</td>
<td class="data"><?php echo Currency::display($so->account->invoices_due_total()); ?></td>
</tr>
<?php if ($so->service_billing->loaded()) { ?>
<tr>
<td>Direct Debit</td>
<td class="data"><?php echo $so->service_billing->checkout->name; ?></td>
</tr>
<?php } ?>
</table>
<!-- END Service Information -->
</td>
<td style="vertical-align: top;">
<!-- Product Summary Info -->
<?php echo $so->product_feature_summary(); ?>
<!-- END Product Summary Info -->
</td>
</tr>
</table>
<br/>
<?php echo $so->service_view(); ?>
<br/>
<table class="box-left" width="50%">
<tr>
<td class="head" colspan="2">Invoices for this service</td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
<tr>
<td colspan="2">Number</td>
<td>Invoice Date</td>
<td>Due Date</td>
<td>Total</td>
<td>Balance</td>
</tr>
<?php $i=0; foreach ($so->invoice->distinct('id')->order_by('id DESC')->find_all() as $io) { ?>
<tr class="<?php echo $i++%2 ? 'odd' : 'even'; ?>">
<td class="icon" width="20px"><?php echo HTML::anchor(URL::link('user','invoice/download/'.$io->id),HTML::image('media/img/gnome-pdf.png',array('alt'=>_('Download'),'width'=>20))); ?></td>
<td class="data"><?php echo HTML::anchor(URL::link('user','invoice/view/'.$io->id),$io->id()); ?></td>
<td class="data"><?php echo $io->display('date_orig'); ?></td>
<td class="data"><?php echo $io->display('due_date'); ?></td>
<td class="data"><?php echo $io->total(TRUE); ?></td>
<td class="data"><?php echo $io->due(TRUE); ?></td>
</tr>
<?php } ?>
</table>