Minor fixes to statement, services and internal things

Many misc updates
This commit is contained in:
Deon George
2011-10-14 16:44:12 +11:00
parent 7876a16413
commit 56c11507f4
71 changed files with 2192 additions and 677 deletions

View File

@@ -82,10 +82,10 @@ class Model_Service_Plugin_ADSL extends Model_Service_Plugin {
/**
* Calculate the total traffic used in a month
*/
private function get_traffic_data_month($period=NULL) {
private function get_traffic_data_month($period=NULL,$cache=NULL) {
$return = array();
foreach ($this->get_traffic_data_daily($period,TRUE) as $tdata)
foreach ($this->get_traffic_data_daily($period,TRUE,$cache) as $tdata)
foreach ($tdata as $k => $v)
if (isset($return[$k]))
$return[$k] += $v;
@@ -98,49 +98,30 @@ class Model_Service_Plugin_ADSL extends Model_Service_Plugin {
/**
* Return an array of the data used in a month by day
*/
public function get_traffic_data_daily($period=NULL,$bydate=FALSE) {
$cacheable = TRUE;
public function get_traffic_data_daily($period=NULL,$bydate=FALSE,$cache=NULL) {
$return = array();
// @temp - caching is broken?
$cache=0;
if (is_null($period))
$period = strtotime('yesterday');
$cache = $this->service_id.date('M-Y',$period).$bydate;
// @todo This cache needs to be improved, so that we cache the query regardless of bydate setting
if ($cacheable AND $return = Cache::instance(Config::cachetype())->get($cache))
return $return;
$return = array();
$to = ORM::factory('service_plugin_adsl_traffic')
$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))));
->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period))))
->cached($cache);
foreach ($to->find_all() as $traffic) {
// Roll up the charges according to the configuration
$data = ADSL::allowance(array(
'base_down_peak'=>is_null($this->service->product->plugin()->base_down_peak) ? NULL : $traffic->down_peak,
'base_down_offpeak'=>is_null($this->service->product->plugin()->base_down_offpeak) ? NULL : $traffic->down_offpeak,
'base_up_peak'=>is_null($this->service->product->plugin()->base_up_peak) ? NULL : $traffic->up_peak,
'base_up_offpeak'=>is_null($this->service->product->plugin()->base_up_offpeak) ? NULL : $traffic->up_offpeak,
'extra_down_peak'=>$this->service->product->plugin()->extra_down_peak,
'extra_down_offpeak'=>$this->service->product->plugin()->extra_down_offpeak,
'extra_up_peak'=>$this->service->product->plugin()->extra_up_peak,
'extra_up_offpeak'=>$this->service->product->plugin()->extra_up_offpeak,
));
foreach ($t->find_all() as $to) {
$day = date('d',strtotime($to->date));
$day = date('d',strtotime($traffic->date));
if ($bydate)
$return[$day] = $data;
$return[$day] = $to->traffic($this->service->product->plugin());
else
foreach ($data as $k => $v)
foreach ($to->traffic($this->service->product->plugin()) as $k => $v)
$return[$k][$day] = $v;
}
// Cache our data
// @todo Our cache time should be a configuration parameter
Cache::instance(Config::cachetype())->set($cache,$return,43200);
return $return;
}
@@ -148,19 +129,12 @@ class Model_Service_Plugin_ADSL extends Model_Service_Plugin {
* Return an array of the data used in a year by month
*/
public function get_traffic_data_monthly($period=NULL,$bydate=FALSE) {
$cacheable = TRUE;
$return = array();
if (is_null($period))
$period = strtotime('yesterday');
$cache = $this->service_id.date('M-Y',$period).$bydate.__METHOD__;
// @todo This cache needs to be improved, so that we cache the query regardless of bydate setting
if ($cacheable AND $return = Cache::instance(Config::cachetype())->get($cache))
return $return;
$return = array();
$to = ORM::factory('service_plugin_adsl_traffic')
$t = ORM::factory('service_plugin_adsl_traffic')
->select(
array('date_format(date,\'%y-%m\')','month'),
array('sum(up_peak)','up_peak'),
@@ -173,36 +147,19 @@ class Model_Service_Plugin_ADSL extends Model_Service_Plugin {
->and_where('date','<=',date('Y-m-d',strtotime('last day of '.date('M Y',$period))))
->group_by('date_format(date,\'%Y-%m\')');
foreach ($to->find_all() as $traffic) {
// Roll up the charges according to the configuration
$data = ADSL::allowance(array(
'base_down_peak'=>is_null($this->service->product->plugin()->base_down_peak) ? NULL : $traffic->down_peak,
'base_down_offpeak'=>is_null($this->service->product->plugin()->base_down_offpeak) ? NULL : $traffic->down_offpeak,
'base_up_peak'=>is_null($this->service->product->plugin()->base_up_peak) ? NULL : $traffic->up_peak,
'base_up_offpeak'=>is_null($this->service->product->plugin()->base_up_offpeak) ? NULL : $traffic->up_offpeak,
'extra_down_peak'=>$this->service->product->plugin()->extra_down_peak,
'extra_down_offpeak'=>$this->service->product->plugin()->extra_down_offpeak,
'extra_up_peak'=>$this->service->product->plugin()->extra_up_peak,
'extra_up_offpeak'=>$this->service->product->plugin()->extra_up_offpeak,
));
foreach ($t->find_all() as $to)
if ($bydate)
$return[$traffic->month] = $data;
$return[$to->month] = $to->traffic($this->service->product->plugin());
else
foreach ($data as $k => $v)
$return[$k][$traffic->month] = $v;
}
// Cache our data
// @todo Our cache time should be a configuration parameter
Cache::instance(Config::cachetype())->set($cache,$return,43200);
foreach ($to->traffic($this->service->product->plugin()) as $k => $v)
$return[$k][$to->month] = $v;
return $return;
}
public function traffic_month($month,$string=TRUE) {
return $string ? implode('/',$this->get_traffic_data_month($month)) :
$this->get_traffic_data_month($month);
public function traffic_month($month,$string=TRUE,$cache=NULL) {
return $string ? implode('/',$this->get_traffic_data_month($month,$cache)) :
$this->get_traffic_data_month($month,$cache);
}
public function traffic_lastmonth($string=TRUE) {
@@ -218,9 +175,9 @@ class Model_Service_Plugin_ADSL extends Model_Service_Plugin {
$return = array();
if (is_null($date))
$date = strtotime('last month');
$date = strtotime('last month')-86400;
foreach ($this->traffic_month($date,FALSE) as $k => $v) {
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)) {
$return[$k]['allowance'] = $this->service->product->plugin()->$k;

View File

@@ -25,5 +25,19 @@ class Model_Service_Plugin_ADSL_Traffic extends ORMOSB {
),
);
}
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,
));
}
}
?>

View File

@@ -43,8 +43,8 @@ class Service_Traffic_ADSL_ExetelPE extends Service_Traffic_ADSL {
// Start Session
$request = Request::factory($this->so->stats_url)
->method('POST')
->post($this->login_user_field,$so->plugin()->service_username)
->post($this->login_pass_field,$so->plugin()->service_password)
->post($this->login_user_field,$so->plugin()->service_username == NULL ? '' : $so->plugin()->service_username)
->post($this->login_pass_field,$so->plugin()->service_password == NULL ? '' : $so->plugin()->service_password)
->post('doLogin',1)
->post('submit','Login');

View File

@@ -0,0 +1,134 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides charge capabilities.
*
* @package OSB
* @subpackage Charge
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Admin_Charge extends Controller_TemplateDefault_Admin {
protected $secure_actions = array(
'add'=>TRUE,
'list'=>TRUE,
'auditinvoiceitems'=>TRUE,
);
/**
* Show a list of invoices
*/
public function action_list() {
Block::add(array(
'title'=>_('Customer Charges'),
'body'=>Table::display(
ORM::factory('charge')->where('sweep_type','>=',0)->order_by('date_orig DESC')->find_all(),
25,
array(
'id'=>array('label'=>'ID','url'=>'user/charge/view/'),
'date_orig'=>array('label'=>'Date'),
'sweep_type'=>array('label'=>'Sweep'),
'status'=>array('label'=>'Status'),
'quantity'=>array('label'=>'Quantity','class'=>'right'),
'amount'=>array('label'=>'Total','class'=>'right'),
'description'=>array('label'=>'Description'),
'service_id'=>array('label'=>'Service'),
'account->accnum()'=>array('label'=>'Cust ID'),
'account->name()'=>array('label'=>'Customer'),
'attributes'=>array('label'=>'Attributes'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/charge/view',
)),
));
}
public function action_add() {
$output = '';
$co = ORM::factory('charge');
if ($_POST) {
// Trim down our attributes
if (is_array($_POST['attributes']))
foreach ($_POST['attributes'] as $k=>$v)
if (! trim($v))
unset($_POST['attributes'][$k]);
if ($co->values($_POST)->check()) {
$co->status=0;
// Entry updated
if (! $co->save())
throw new Kohana_Exception('Unable to save charge');
}
}
$output .= Form::open();
$output .= View::factory('charge/admin/add');
$output .= Form::submit('submit','submit');
$output .= Form::close();
Style::add(array(
'type'=>'stdin',
'data'=>'.ui-autocomplete-loading { background: white url("'.URL::site('media/img/ui-anim_basic_16x16.gif').'") right center no-repeat; }'
));
Style::add(array(
'type'=>'file',
'data'=>'js/jquery.ui/css/smoothness/jquery-ui-1.8.16.custom.css',
));
Script::add(array(
'type'=>'file',
'data'=>'js/jquery-ui-1.8.16.custom.min.js',
));
Script::add(array('type'=>'stdin','data'=>'
$(document).ready(function() {
$("input[name=account_id]").autocomplete({
source: "'.URL::site('admin/account/autocomplete').'",
minLength: 2,
change: function(event,ui) {
// Send the request and update sub category dropdown
$.ajax({
type: "GET",
data: "aid="+$(this).val(),
dataType: "json",
cache: false,
url: "'.URL::site('admin/service/autolist').'",
timeout: 2000,
error: function() {
alert("Failed to submit");
},
success: function(data) {
// Clear all options from sub category select
$("select[name=service_id] option").remove();
// Prepopulate a blank
var row = "<option value=\"\">&nbsp;</option>";
$(row).appendTo("select[name=service_id]");
// Fill sub category select
$.each(data, function(i, j){
var row = "<option value=\"" + j.value + "\">" + j.text + "</option>";
$(row).appendTo("select[name=service_id]");
});
}
});
}
});
});'
));
Block::add(array(
'title'=>_('Add Customer Charges'),
'body'=>$output,
));
}
}
?>

View File

@@ -11,19 +11,37 @@
* @license http://dev.osbill.net/license.html
*/
class Model_Charge extends ORMOSB {
// Charge doesnt use the update column
protected $_updated_column = FALSE;
protected $_belongs_to = array(
'account'=>array(),
);
protected $_display_filters = array(
'date_orig'=>array(
array('Config::date',array(':value')),
),
'amount'=>array(
'Currency::display',array(':value')
array('Currency::display',array(':value')),
),
);
public function rules() {
return array_merge(parent::rules(),array(
'attributes'=>array(
array('ORMOSB::serialize_array',array(':model',':field',':value')),
),
));
}
/**
* Render some details for specific calls, eg: invoice
*/
public function details($type) {
switch ($type) {
case 'invoice':
return sprintf('%s (%s@%s)',$this->description,$this->quantity,Currency::display($this->amount));
case 'invoice_detail_items':
return array('Other Charge'=>sprintf('%s (%s@%s)',$this->description,$this->quantity,Currency::display($this->amount)));
default:
throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type));

View File

@@ -12,7 +12,8 @@
*/
class Model_Checkout extends ORMOSB {
protected $_has_many = array(
'payment' => array()
'account'=>array('through'=>'account_billing','foreign_key'=>'checkout_plugin_id'),
'payment'=>array(),
);
/**

View File

@@ -13,77 +13,48 @@
class Controller_Admin_Export extends Controller_TemplateDefault_Admin {
protected $control_title = 'Export';
protected $secure_actions = array(
'index'=>TRUE,
'export'=>TRUE,
'add'=>TRUE,
'edit'=>TRUE,
);
/**
* Export plugins must define an export action.
* Add Export Maping items
*/
public function action_export() {
if (empty($_POST['plugin']))
$this->request->redirect('admin/export');
public function action_add() {
$eo = ORM::factory('export');
$output = '';
$sc = sprintf('Export_%s',$_POST['plugin']);
if (! class_exists($sc))
throw new Kohana_Exception('Export Class doesnt exist for :plugin',array(':plugin'=>$_POST['plugin']));
else
$export = new $sc;
if ($_POST AND $eo->values($_POST)->check()) {
$eo->module_id = ORM::factory('module',array('name'=>'product'))->id; // @todo This probably should be in the form.
$eo->plugin_name = 'quicken'; // @todo This should be in the form.
// Entry updated
if (! $eo->save())
throw new Kohana_Exception('Unable to save data :post',array(':post'=>serialize($_POST)));
$export->export();
SystemMessage::add(array(
'title'=>_('Record add'),
'type'=>'info',
'body'=>_('Export Map entry added.')
));
}
$output .= Form::open();
$output .= View::factory('export/admin/map/add')
->set('eo',$eo);
$output .= '<div>'.Form::submit('submit',_('Add'),array('class'=>'form_button')).'</div>';
$output .= Form::close();
Block::add(array(
'title'=>_('Add Export Map'),
'body'=>$output,
));
}
/**
* This is the main call to export, providing a list of items to export and
* setting up the page to call the export plugin when submitted.
* Edit Export Maping items
*/
public function action_index($daysago) {
// @todo this should come from a file list
$TBRexportplugins = array('quicken'=>'Export to Quicken');
if (! $daysago)
$daysago = 30;
$payments = ORM::factory('payment')
->export($daysago);
if (count($payments)) {
$output = Form::open(Request::current()->uri(array('action'=>'export')));
$output .= '<table class="box-left">';
$output .= View::factory('export/payment/header')
->set('plugins',$TBRexportplugins);
$i = 0;
foreach ($payments as $payment) {
$output .= View::factory('export/payment/body')
->set('payment',$payment)
->set('i',$i++%2);
}
$output .= '</table>';
$output .= Form::submit('submit','export',array('class'=>'form_button'));
$output .= Form::close();
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
Block::add(array(
'title'=>_('Payments to Export'),
'body'=>$output,
));
# Nothing to export
} else {
SystemMessage::add(array(
'title'=>_('No payments to export'),
'type'=>'info',
'body'=>sprintf(_('There are no payments within the last %s days (since %s) to show.'),
$daysago,date(Kohana::config('osb')->get('date_format'),$daysago*86400+time())),
));
}
public function action_edit() {
}
}
?>

View File

@@ -22,7 +22,7 @@ class Controller_Affiliate_Export extends Controller_TemplateDefault_Affiliate {
*/
public function action_export() {
if (empty($_POST['plugin']))
$this->request->redirect('affiliate/export');
$this->request->redirect('affiliate/export/index');
$sc = sprintf('Export_%s',$_POST['plugin']);
if (! class_exists($sc))
@@ -38,43 +38,40 @@ class Controller_Affiliate_Export extends Controller_TemplateDefault_Affiliate {
* This is the main call to export, providing a list of items to export and
* setting up the page to call the export plugin when submitted.
*/
public function action_index($daysago) {
public function action_index() {
// @todo this should come from a file list
$TBRexportplugins = array('quicken'=>'Export to Quicken');
if (! $daysago)
$daysago = 30;
// @todo: Need to limit this to affiliate acounts
$payments = ORM::factory('payment')
->export($daysago);
if (count($payments)) {
$output = Form::open(Request::current()->uri(array('action'=>'export')));
$output .= '<table class="box-left">';
$output .= View::factory('export/payment/header')
->set('plugins',$TBRexportplugins);
$i = 0;
foreach ($payments as $payment) {
$output .= View::factory('export/payment/body')
->set('payment',$payment)
->set('i',$i++%2);
}
$output .= '</table>';
$output .= Form::submit('submit','export',array('class'=>'form_button'));
$output .= Form::close();
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
$p = ORM::factory('payment');
if ($p->find_all()->count()) {
Block::add(array(
'title'=>_('Payments to Export'),
'body'=>$output,
'body'=>Table::display(
$p->find_all(),
25,
array(
'id'=>array('label'=>'ID'),
'date_payment'=>array('label'=>'Date'),
'checkout->display("name")'=>array('label'=>'Method'),
'account->accnum()'=>array('label'=>'Acc Num'),
'account->name()'=>array('label'=>'Account'),
'total_amt'=>array('label'=>'Total','class'=>'right'),
'balance(TRUE)'=>array('label'=>'Balance','class'=>'right'),
'invoicelist()'=>array('label'=>'Invoices'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'affiliate/export/export',
'hidden'=>array(
Form::hidden('plugin','quicken'),
),
'button'=>array(
Form::submit('submit',_('Export'),array('class'=>'form_button')),
),
)),
));
# Nothing to export

View File

@@ -12,16 +12,16 @@
*/
class Export_Quicken extends Export {
public function export() {
if (! empty($_POST['payment_id']) AND count($_POST['payment_id'])) {
if (! empty($_POST['id']) AND count($_POST['id'])) {
$qo = new Quicken;
foreach ($_POST['payment_id'] as $pid) {
$mpo = ORM::factory('payment',$pid);
foreach ($_POST['id'] as $pid) {
$po = ORM::factory('payment',$pid);
if ($mpo->loaded()) {
if ($po->loaded()) {
$invoice_ids = array();
foreach ($mpo->payment_item->find_all() as $pio) {
foreach ($po->payment_item->find_all() as $pio) {
// If our invoice ID is not blank, then the payment was applied to an invoice
if ($pio->invoice->id) {
// Record our invoice IDs for the summary
@@ -34,23 +34,23 @@ class Export_Quicken extends Export {
$qio->CLEAR = 'N';
$qio->TOPRINT = 'N';
$qio->PAID = 'N';
$qio->ADDR1 = $mpo->account->address1;
$qio->ADDR2 = $mpo->account->address2;
$qio->ADDR3 = sprintf('%s, %s %s',$mpo->account->city,$mpo->account->state,$mpo->account->zip);
$qio->ADDR1 = $po->account->address1;
$qio->ADDR2 = $po->account->address2;
$qio->ADDR3 = sprintf('%s, %s %s',$po->account->city,$po->account->state,$po->account->zip);
// @todo - should be configurable
$qio->TERMS = '7 Days';
// @todo - should be configurable
$qio->INVTITLE = 'Graytech Hosting Invoice';
$qio->INVTITLE = Company::name().' Invoice';
// @todo - should be configurable
$qio->INVMEMO = 'Thank you for using Graytech Hosting';
$qio->INVMEMO = 'Thank you for using '.Company::name();
$qio->DOCNUM = sprintf('%06s',$pio->invoice->id);
$qio->DUEDATE = date('m/d/Y',$pio->invoice->due_date);
$qio->AMOUNT = sprintf('%3.2f',$pio->invoice->total_amt);
$qio->AMOUNT = sprintf('%3.2f',$pio->invoice->total());
if ($mpo->account->company)
$qio->NAME = $mpo->account->company;
if ($po->account->company)
$qio->NAME = $po->account->company;
else
$qio->NAME = sprintf('%s %s',$mpo->account->last_name,$mpo->account->first_name);
$qio->NAME = sprintf('%s %s',$po->account->last_name,$po->account->first_name);
// Other Quicken fields not used.
#$qio->CLASS = '';
@@ -70,41 +70,37 @@ class Export_Quicken extends Export {
foreach ($pio->invoice->invoice_item->find_all() as $iio) {
$qto = new Quicken_InvoiceItem;
if ($iio->date_start OR $iio->date_stop)
$daterange = sprintf('%s-%s',date('d-m-Y',$iio->date_start),date('d-m-Y',$iio->date_stop));
if ($iio->period())
$daterange = $iio->period();
// @todo This should go.
elseif ($iio->product_attr && preg_match('/^a/',$iio->product_attr)) {
echo 'Uncaptured';die();
// @todo This should go.
} elseif ($iio->product_attr && preg_match('/^s/',$iio->product_attr))
$daterange = preg_replace("/\r?\n/",' ',unserialize($iio->product_attr));
else
$daterange = '';
if (! $iio->product_id && preg_match('/^DOMAIN/',$iio->sku)) {
$qto->ACCNT = 'Internet:Domain Name';
$qto->INVITEM = sprintf('Domain:%s',
($iio->domain_tld) ? strtoupper($iio->domain_tld) : 'Unknown');
$qto->MEMO = sprintf('Domain: %s.%s (%s)',
strtoupper($iio->domain_name),strtoupper($iio->domain_tld),$daterange);
} elseif ($iio->product_id) {
$module = ORM::factory('module',array('name'=>'product'));
$export = ORM::factory('export','module')
if ($iio->product_id) {
$mo = ORM::factory('module',array('name'=>'product'));
$eo = ORM::factory('export')
->where('plugin_name','=',$this->plugin)
->and_where('module_id','=',$module->id)
->and_where('module_id','=',$mo->id)
->and_where('item_id','=',$iio->product_id)
->find();
if ($export->loaded()) {
$map_data = unserialize($export->map_data);
if ($eo->loaded()) {
$map_data = unserialize($eo->map_data);
$qto->ACCNT = $map_data['account'];
$qto->INVITEM = $map_data['item'];
} else {
throw new Kohana_Exception('Missing product map data for :product (:id)',
array(':product'=>$iio->product->name(),':id'=>$iio->product_id));
$qto->ACCNT = 'Other Income';
$qto->INVITEM = 'Product:Unknown';
}
@@ -122,18 +118,19 @@ class Export_Quicken extends Export {
$qto->CLEAR = 'N';
$qto->QNTY = -1;
if ($pio->invoice->tax_amt) {
if ($pio->invoice->tax()) {
$qto->TAXABLE = 'Y';
# @todo, get this from OSB
$qto->TAXCODE = 'GST';
$qto->TAXRATE = sprintf('%3.2f%%','0.10');
$qto->TAXAMOUNT = sprintf('%3.2f',$iio->tax_amt*-1);
$qto->TAXAMOUNT = sprintf('%3.2f',$iio->tax()*-1);
} else {
$qto->TAXAMOUNT = 0;
}
$qto->PRICE = sprintf('%3.2f',$iio->total_amt-$iio->tax_amt);
$qto->AMOUNT = sprintf('%3.2f',($iio->total_amt-$iio->tax_amt)*-1);
// @todo This rounding should be a system config.
$qto->PRICE = sprintf('%3.2f',round($iio->subtotal()-$iio->discount(),2));
$qto->AMOUNT = sprintf('%3.2f',round($iio->subtotal()-$iio->discount(),2)*-1);
$qio->addInvoiceItem($qto);
}
@@ -143,21 +140,21 @@ class Export_Quicken extends Export {
}
$qpo = new Quicken_Payment;
$qpo->AMOUNT = sprintf('%3.2f',$mpo->total_amt);
$qpo->TRNSID = sprintf('P%06s',$mpo->id);
$qpo->DATE = date('m/d/Y',$mpo->date_payment);
$qpo->AMOUNT = sprintf('%3.2f',$po->total_amt);
$qpo->TRNSID = sprintf('P%06s',$po->id);
$qpo->DATE = date('m/d/Y',$po->date_payment);
// @todo this should be from a function - when no invoice is paid we cant use $qio
if ($mpo->account->company)
$qpo->NAME = $mpo->account->company;
if ($po->account->company)
$qpo->NAME = $po->account->company;
else
$qpo->NAME = sprintf('%s %s',$mpo->account->last_name,$mpo->account->first_name);
$qpo->NAME = sprintf('%s %s',$po->account->last_name,$po->account->first_name);
$qpo->CLEAR = 'N';
$qpo->MEMO = sprintf('Payment for invoice(s) %s (%s)',implode(':',$invoice_ids),$mpo->checkout->name);
$qpo->MEMO = sprintf('Payment for invoice(s) %s (%s)',implode(':',$invoice_ids),$po->checkout->name);
// @todo Accounts/Payment should be configurable
switch ($mpo->checkout->checkout_plugin) {
switch ($po->checkout->checkout_plugin) {
// @todo this is direct debit
case 'MANUAL':
$qpo->PAYMETH = 'DirectDebit';
@@ -184,7 +181,8 @@ class Export_Quicken extends Export {
$qpo->ACCNT = 'Undeposited Funds';
}
$qio->addPayment($qpo);
if (isset($qio))
$qio->addPayment($qpo);
}
}
}

View File

@@ -11,5 +11,26 @@
* @license http://dev.osbill.net/license.html
*/
class Model_Export extends ORMOSB {
public function rules() {
return array_merge(parent::rules(),array(
'map_data'=>array(
array('ORMOSB::serialize_array',array(':model',':field',':value')),
),
));
}
public function list_itemsnoexport() {
$result = array();
$mo = ORM::factory('module',array('name'=>'product'));
$p = ORM::factory('product')
->order_by('id');
foreach ($p->find_all() as $po)
if (! ORM::factory('export')->where('module_id','=',$mo->id)->where('item_id','=',$po->id)->find()->loaded())
$result[$po->id] = $po;
return $result;
}
}
?>

View File

@@ -0,0 +1,18 @@
<table>
<tr>
<td>Product</td>
<td><?php echo Form::select('item_id',$eo->list_itemsnoexport()); ?></td>
</tr>
<tr>
<td>Export</td>
<td><?php echo Form::input('plugin_name','quicken',array('disabled'=>'disabled')); ?></td>
</tr>
<tr>
<td>Account ID</td>
<td><?php echo Form::input('map_data[account]','Internet:ADSL Supply'); ?></td>
</tr>
<tr>
<td>Item ID</td>
<td><?php echo Form::input('map_data[item]','ADSL:0256/064'); ?></td>
</tr>
</table>

View File

@@ -1,7 +0,0 @@
<tr class="<?php echo $i ? 'odd' : 'even'; ?>">
<td><?php echo $payment->display('date_payment'); ?></td>
<td><?php printf('%s (%s)',$payment->checkout->name,$payment->checkout->id); ?></td>
<td><?php echo $payment->display('total_amt'); ?></td>
<td><?php echo $payment->account->name(); ?></td>
<td><?php echo Form::checkbox('payment_id[]',$payment->id); ?></td>
</tr>

View File

@@ -1,12 +0,0 @@
<tr>
<td colspan="3"><?php echo Form::select('plugin',$plugins); ?></td>
<td colspan="2" style="text-align: right;"><?php echo Form::submit('submit','export',array('class'=>'form_button')); ?></td>
</tr>
<!-- // @todo To translate -->
<tr>
<td class="heading">Date</td>
<td class="heading">Method</td>
<td class="heading">Pay/Amount</td>
<td class="heading">Customer</td>
<td class="heading">Export</td>
</tr>

View File

@@ -9,6 +9,10 @@
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*
* Column Definitions:
* + price_type: 0=One Time, 1=Recurring, 2=Trial, 3=Extra Item
* + item_type: 0=MAIN Service Item,2=?,3=?,4=Connection/Setup,5=Excess Service Item,6=Change Service,126=Payment Fee,127=Late Fee
*/
class Controller_Admin_Invoice extends Controller_TemplateDefault_Admin {
protected $secure_actions = array(

View File

@@ -20,15 +20,15 @@ class Controller_Task_Invoice extends Controller_Task {
public function action_list() {
$mode = $this->request->param('id');
$io = ORM::factory('invoice');
$i = ORM::factory('invoice');
$tm = 'list_'.$mode;
if (! method_exists($io,$tm))
if (! method_exists($i,$tm))
throw new Kohana_Exception('Unknown Task List command :command',array(':command'=>$mode));
$total = $numinv = 0;
$duelist = View::factory('invoice/task/'.$tm.'_head');
foreach ($io->$tm() as $t) {
foreach ($i->$tm() as $t) {
$duelist .= View::factory('invoice/task/'.$tm.'_body')
->set('io',$t);
@@ -60,10 +60,9 @@ class Controller_Task_Invoice extends Controller_Task {
$action = array();
// @todo This should go in a config somewhere
$days = 5;
$i = ORM::factory('invoice');
$key = 'remind_due';
foreach ($i->list_due(time()+86400*$days) as $io) {
foreach (ORM::factory('invoice')->list_due(time()+86400*$days) as $io) {
// If we have already sent a reminder, we'll skip to the next one.
if ($io->remind($key) AND (is_null($x=$this->request->param('id')) OR $x != 'again'))
continue;
@@ -96,7 +95,6 @@ class Controller_Task_Invoice extends Controller_Task {
*/
public function action_remind_overdue() {
$action = array();
$i = ORM::factory('invoice');
$notice = $this->request->param('id');
$x = NULL;
@@ -126,7 +124,7 @@ class Controller_Task_Invoice extends Controller_Task {
$key = 'remind_overdue_'.$notice;
foreach ($i->list_overdue_billing(time()-86400*$days,FALSE) as $io) {
foreach (ORM::factory('invoice')->list_overdue_billing(time()-86400*$days,FALSE) as $io) {
// If we have already sent a reminder, we'll skip to the next one.
if ($io->remind($key) AND (is_null($x) OR $x != 'again'))
continue;
@@ -162,29 +160,38 @@ class Controller_Task_Invoice extends Controller_Task {
*
* @param int ID Service ID to generate invoice for (optional)
*/
public function action_serviceinvoices() {
public function action_services() {
// Used to only process X invoices in a row.
// @todo This should be a configuration item.
$max = 10;
$action = array();
$snd = array(); // Our service next billing dates that need to be updated if this is successful.
$sid = $this->request->param('id');
$sid = is_null($this->request->param('id')) ? NULL : explode(':',$this->request->param('id'));
// Sort our service by account_id, then we can generate 1 invoice.
$svs = ORM::factory('service')->list_invoicesoon();
Sort::MAsort($svs,'account_id,date_next_invoice');
$aid = $due = $io = NULL;
$max_count = 0;
foreach ($svs as $so) {
if (! is_null($sid) AND $sid != $so->id)
// If we are generating an invoice for a service, skip to that service.
if (! is_null($sid) AND ! in_array($so->id,$sid))
continue;
// Close off invoice, and start a new one.
if (is_null($io) OR (is_null($aid) AND $aid != $so->account_id) OR (is_null($due) AND $due != $io->min_due($so->date_next_invoice))) {
if (is_null($io) OR (! is_null($aid) AND $aid != $so->account_id) OR (! is_null($due) AND $due != $io->min_due($so->date_next_invoice))) {
// Close this invoice.
if (! is_null($io)) {
if (is_object($io)) {
// Save our invoice.
if (! $io->save())
throw new Kohana_Exception('Failed to save invoice :invoice for service :service',array(':invoice'=>$io->id,':service'=>$so->id));
}
// If we have issued the max number of invoices this round, finish.
if (++$max_count > $max)
break;
// Start a new invoice.
$io = ORM::factory('invoice');
$io->due_date = $due = $io->min_due($so->date_next_invoice);
@@ -199,12 +206,12 @@ class Controller_Task_Invoice extends Controller_Task {
$iio->service_id = $so->id;
$iio->product_id = $so->product_id;
$iio->quantity = 1;
$iio->quantity = $pdata['prorata'];
$iio->item_type = 0;
$iio->discount_amt = null; // @todo
$iio->price_type = $so->price_type; // @todo Do we need this?
$iio->price_type = $so->product->price_type; // @todo Do we need this?
// @todo Might be a better way to do this
$iio->price_base = isset($ppa[$so->recur_schedule]['price_base']) ? $ppa[$so->recur_schedule]['price_base'] : 0;
$iio->price_base = $so->price ? $so->price : (isset($ppa[$so->recur_schedule]['price_base']) ? $ppa[$so->recur_schedule]['price_base'] : 0);
$iio->recurring_schedule = $so->recur_schedule;
$iio->date_start = $pdata['start_time']; // @todo
$iio->date_stop = $pdata['end_time']; // @todo
@@ -212,12 +219,38 @@ class Controller_Task_Invoice extends Controller_Task {
// Our service next billing date, if this invoice generation is successful.
$snd[$so->id] = $pdata['end_time']+86400;
// Check if there are any charges
$c = ORM::factory('charge')
->where('service_id','=',$so->id)
->where('status','=',0)
->where('sweep_type','=',6); // @todo This needs to be dynamic, not "6"
foreach ($c->find_all() as $co) {
$iio = $io->add_item();
$iio->service_id = $co->service_id;
$iio->product_id = $co->product_id;
$iio->charge_id = $co->id;
$iio->quantity = $co->quantity;
$iio->item_type = 5; // @todo This probably should not be hard coded as "5".
$iio->discount_amt = null; // @todo
$iio->price_base = $co->amount;
$iio->date_start = $co->date_orig;
$iio->date_stop = $co->date_orig; // @todo
// @todo Temp
$co->status=2;
$co->save();
}
array_push($action,(string)$so->id);
}
// Save our invoice.
if (! $io->save())
if (! $io->saved() AND ! $io->save()) {
print_r($io->items());
throw new Kohana_Exception('Failed to save invoice :invoice for service :service',array(':invoice'=>$io->id,':service'=>$so->id));
}
// Update our service next billing dates.
foreach ($snd as $sid=>$date) {
@@ -229,7 +262,10 @@ class Controller_Task_Invoice extends Controller_Task {
$this->response->body(_('Services Invoiced: ').join('|',$action));
}
public function action_invoice_send() {
public function action_send() {
// Used to only process X invoices in a row.
// @todo This should be a configuration item.
$max = 2;
$action = array();
$iid = $this->request->param('id');
$x = NULL;
@@ -237,16 +273,21 @@ class Controller_Task_Invoice extends Controller_Task {
if (preg_match('/:/',$iid))
list($iid,$x) = explode(':',$iid);
// @todo The parameter for email should be a system CONSTANT?
$i = $iid ? ORM::factory('invoice')->where('id','=',$iid) : ORM::factory('invoice')->tosend(1);
// Get our list of invoices to send
$i = $iid ? ORM::factory('invoice')->where('id','=',$iid) : ORM::factory('invoice')->list_tosend();
$key = 'send';
$max_count = 0;
foreach ($i->find_all() as $io) {
// If we have already sent a reminder, we'll skip to the next one.
if ($io->remind($key) AND (is_null($x) OR $x != 'again'))
continue;
// If we have issued the max number of invoices this round, finish.
if (++$max_count > $max)
break;
// Send our email
$et = Email_Template::instance('task_invoice_'.$key);
$token = ORM::factory('module_method_token')
@@ -271,6 +312,7 @@ class Controller_Task_Invoice extends Controller_Task {
// @todo Record email log id if possible.
if ($et->send()) {
$io->print_status = 1;
$io->set_remind($key,time());
array_push($action,(string)$io);
}

View File

@@ -60,7 +60,7 @@ class Controller_User_Invoice extends Controller_TemplateDefault_User {
$io = ORM::factory('invoice',$id);
if (! $io->loaded() OR ! Auth::instance()->authorised($io->account_id,$io->affiliate_id)) {
if (! $io->loaded() OR (! Auth::instance()->authorised($io->account_id,$io->affiliate_id) AND ! in_array($this->ao->affiliate->id,$io->service_affiliates()))) {
$this->template->content = 'Unauthorised or doesnt exist?';
return FALSE;
}

View File

@@ -322,7 +322,7 @@ class Invoice_TCPDF_Default extends Invoice_TCPDF {
$this->SetXY($x-1,$y-1);
$this->Cell(0,5*(
1+1+1+3+($this->io->discount_amt ? 1 : 0)+($this->io->billed_amt ? 1 : 0)+($this->io->credit_amt ? 1 : 0)+$box
)+1,'',1,0,'',1);
)+1+4,'',1,0,'',1);
$this->SetFont('helvetica','B',11);
$this->SetXY($x,$y);
@@ -335,7 +335,7 @@ class Invoice_TCPDF_Default extends Invoice_TCPDF {
foreach ($items as $name => $line) {
if ($i < $this->itemsSummaryMax) {
$this->SetX($x);
$this->Cell(0,0,$line['quantity']);
$this->Cell(0,0,sprintf('%3.2f',$line['quantity']));
$this->SetX($x+8);
$this->Cell(0,0,$name);
$this->SetX($x+135);
@@ -362,7 +362,7 @@ class Invoice_TCPDF_Default extends Invoice_TCPDF {
if (round($this->io->subtotal(),Kohana::config('config.currency_format')) != $subtotal) {
$this->SetFont('helvetica','',9);
$this->SetX($x);
$this->Cell(0,0,'Rounding');
$this->Cell(0,0,'Other');
$this->SetX($x+135);
$this->Cell(0,0,Currency::display($this->io->subtotal()-$subtotal),0,0,'R');
@@ -404,7 +404,7 @@ class Invoice_TCPDF_Default extends Invoice_TCPDF {
$this->SetY($y);
$this->SetX($x+8);
$this->Cell(0,0,'Total Charges');
$this->Cell(0,0,'Total Charges This Invoice');
$this->SetX($x+135);
$this->Cell(0,0,Currency::display($this->io->total()),0,0,'R');
@@ -496,7 +496,7 @@ class Invoice_TCPDF_Default extends Invoice_TCPDF {
}
$this->SetX($x+130);
$this->Cell(0,0,Currency::display($ito->total_amt),0,0,'R');
$this->Cell(0,0,Currency::display($ito->total()),0,0,'R');
if ($this->show_service_range && $ito->period()) {
$this->SetFont('helvetica','I',7);

View File

@@ -148,7 +148,7 @@ class Model_Invoice extends ORMOSB {
}
/**
* Get a list of services on an invoice
* Get a list of invoice_items for a service_id on an invoice
*
* We use this to list details by service on an invoice.
*/
@@ -158,15 +158,15 @@ class Model_Invoice extends ORMOSB {
$items = $this->items();
foreach ($items as $ito)
if ($ito->service_id AND empty($result[$ito->service_id]))
$result[$ito->service_id] = $ito->service;
if ($ito->service_id AND empty($result[$ito->service_id]))
$result[$ito->service_id] = $ito;
return $result;
}
/**
* Return all invoice items for a service optionally by recurring schedule
*/
*/
public function items_service($sid,$rs=NULL) {
$result = array();
$items = $this->items();
@@ -211,11 +211,11 @@ class Model_Invoice extends ORMOSB {
$result = array();
foreach ($this->items() as $ito) {
// We conly summaries item_type=0
// We only summarise item_type=0
if (! $ito->item_type == 0)
continue;
$t = $ito->product_id;
$t = $ito->product->name();
if (! isset($result[$t])) {
$result[$t]['quantity'] = 0;
@@ -303,14 +303,17 @@ class Model_Invoice extends ORMOSB {
}
public function min_due($date) {
// @todo This should be configurable;
return ($date < time()) ? time() : $date;
return ($date < time()) ? time()+Kohana::config('config.invoice.min_due_days')*86400 : $date;
}
public function save(Validation $validation = NULL) {
// Our items will be clobbered once we save the object, so we need to save it here.
$items = $this->items();
// @todo This is added here so we can do payments
$this->total_amt = $this->total();
$this->billed_amt = 0;
// Save the invoice
parent::save($validation);
@@ -331,12 +334,14 @@ class Model_Invoice extends ORMOSB {
throw new Kohana_Exception('Problem saving invoice_item for invoice :invoice - Failed save()',array(':invoice'=>$invoice->id));
}
// @todo Need to save tax information
// @todo Need to save discount information
}
} else
throw new Kohana_Exception('Couldnt save invoice for some reason?');
return TRUE;
}
/**
@@ -387,13 +392,29 @@ class Model_Invoice extends ORMOSB {
return $this->saved();
}
/**
* Return a list of affiliates associated with this invoice (via the service)
*/
public function service_affiliates() {
$return = array();
foreach ($this->items() as $io)
array_push($return,$io->service->affiliate_id);
return $return;
}
/** LIST FUNCTIONS **/
private function _list_active() {
return ORM::factory('invoice')->where('status','=',1);
}
private function _list_due() {
static $result = array();
if (! $result)
foreach (ORM::factory('invoice')->where('status','=',1)->find_all() as $io)
foreach ($this->_list_active()->find_all() as $io)
if ($io->due())
array_push($result,$io);
@@ -445,12 +466,20 @@ class Model_Invoice extends ORMOSB {
if ($io->due_date > time())
if (is_null($time))
array_push($result,$io);
elseif ($this->due_date <= $time)
elseif ($io->due_date <= $time)
array_push($result,$io);
return $result;
}
/**
* Return a list of invoices that need to be sent.
* @todo This should be optimised a little to return only invoices to send, instead of looking for them.
*/
public function list_tosend() {
return ORM::factory('invoice')->where('status','=',1)->where_open()->where('print_status','is',NULL)->or_where('print_status','!=',1)->where_close();
}
public function html() {
// @todo This should be in a config file.
$css = '<style type="text/css">';

View File

@@ -19,11 +19,17 @@ class Model_Invoice_Item extends ORMOSB {
'invoice'=>array(),
'service'=>array()
);
protected $_has_one = array(
'charge'=>array('far_key'=>'charge_id','foreign_key'=>'id')
);
protected $_has_many = array(
'invoice_item_tax'=>array('far_key'=>'id')
);
protected $_display_filters = array(
'date_orig'=>array(
array('Config::date',array(':value')),
),
'date_start'=>array(
array('Config::date',array(':value')),
),
@@ -83,8 +89,11 @@ class Model_Invoice_Item extends ORMOSB {
return ($this->item_type == 0 OR $this->quantity == 1) ? HTML::nbsp('') : sprintf('%s@%3.2f',$this->quantity,$this->price_base);
}
// @todo This might not be required.
public function invoice_detail_items() {
// @todo To fix up this function - needs to be better formed.
if ($this->item_type == 5)
return $this->charge->details('invoice_detail_items');
if ($this->item_type != 0)
return;
@@ -96,6 +105,7 @@ class Model_Invoice_Item extends ORMOSB {
parent::save($validation);
// Need to save the taxes and discounts associated with the invoice_item
// @todo This needs to only check if the records have previously been saved, and update them.
if ($this->saved()) {
$iito = ORM::factory('invoice_item_tax');

View File

@@ -63,7 +63,7 @@
<div id="detail_toggle_<?php echo $rs; ?>">
<table class="box-full" border="0">
<?php if ($items) { ?>
<?php foreach ($io->items_services($items) as $sid) { ?>
<?php foreach ($io->items_services($items) as $sid => $ito) { ?>
<?php $so = ORM::factory('service',$sid); ?>
<!-- Product Information -->
<tr class="head">
@@ -84,7 +84,6 @@
<td class="right"><?php echo Currency::display($ito->subtotal());?>&nbsp;</td>
</tr>
<!-- End Product Sub Information -->
<?php } ?>
<?php if ($ito->discount_amt) { ?>
<tr>
@@ -101,6 +100,7 @@
</tr>
<!-- Product End Sub Items Tax -->
<?php } ?>
<?php } ?>
<?php } ?>
</table>
</div>

View File

@@ -63,13 +63,12 @@
<div id="detail_toggle_<?php echo $rs; ?>">
<table class="box-full" border="0">
<?php if ($items) { ?>
<?php foreach ($io->items_services($items) as $sid) { ?>
<?php $so = ORM::factory('service',$sid); ?>
<?php foreach ($io->items_services($items) as $sid => $ito) { ?>
<!-- Product Information -->
<tr class="head">
<td><?php echo HTML::anchor('/user/service/view/'.$so->id,$so->id()); ?></td>
<td colspan="5"><?php echo $so->service_name(); ?> (<?php echo $so->product_id; ?>)</td>
<td class="right"><?php echo Currency::display($io->items_service_total($so->id));?></td>
<td><?php echo HTML::anchor('/user/service/view/'.$ito->service_id,$ito->service->id()); ?></td>
<td colspan="5"><?php printf('%s - %s',$ito->product->name(),$ito->service->name()); ?> (<?php echo $ito->product_id; ?>)</td>
<td class="right"><?php echo Currency::display($io->items_service_total($ito->service_id));?></td>
</tr>
<!-- End Product Information -->
@@ -90,14 +89,14 @@
<tr>
<td colspan="4">&nbsp;</td>
<td><?php echo _('Discounts'); ?></td>
<td class="right">(<?php echo Currency::display($io->items_service_discount($so->id));?>)</td>
<td class="right">(<?php echo Currency::display($io->items_service_discount($ito->service_id));?>)</td>
</tr>
<?php } ?>
<!-- Product Sub Items Tax -->
<tr>
<td colspan="4">&nbsp;</td>
<td><?php echo _('Taxes'); ?></td>
<td class="right"><?php echo Currency::display($io->items_service_tax($so->id));?>&nbsp;</td>
<td class="right"><?php echo Currency::display($io->items_service_tax($ito->service_id));?>&nbsp;</td>
</tr>
<!-- Product End Sub Items Tax -->
<?php } ?>

View File

@@ -80,7 +80,7 @@ class Model_Payment extends ORMOSB {
// @todo database suffix needs to be dynamically calculated
foreach (DB::Query(Database::SELECT,
sprintf('SELECT A.id AS id,A.total_amt as total_amt FROM ab_%s A,ab_%s B WHERE A.site_id=B.site_id AND A.id=B.payment_id GROUP BY B.payment_id HAVING ROUND(SUM(B.alloc_amt),2)!=A.total_amt ORDER BY account_id,payment_id','payment','payment_item'))
sprintf('SELECT A.id AS id,A.total_amt as total_amt FROM ab_%s A LEFT JOIN ab_%s B ON (A.site_id=B.site_id AND A.id=B.payment_id) WHERE (A.refund_status=0 OR A.refund_status IS NULL) GROUP BY A.id HAVING ROUND(SUM(IFNULL(B.alloc_amt,0)),2)!=A.total_amt ORDER BY account_id,payment_id','payment','payment_item'))
->execute() as $values) {
array_push($pi,$values['id']);

View File

@@ -1,7 +1,7 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Admin Product functions
* This class provides Admin Product management
*
* @package OSB
* @subpackage Product
@@ -16,16 +16,16 @@ class Controller_Admin_Product extends Controller_TemplateDefault_Admin {
);
/**
* Show a list of services
* Show a list of products
*/
public function action_list() {
Block::add(array(
'title'=>_('Customer Products'),
'body'=>Table::display(
ORM::factory('product')->order_by('prod_plugin_file')->find_all(),
ORM::factory('product')->order_by('active DESC,prod_plugin_file')->find_all(),
25,
array(
'id'=>array('label'=>'ID','url'=>'user/product/view/'),
'id'=>array('label'=>'ID','url'=>'product/view/'),
'name()'=>array('label'=>'Details'),
'active'=>array('label'=>'Active'),
'prod_plugin'=>array('label'=>'Plugin'),
@@ -34,11 +34,13 @@ class Controller_Admin_Product extends Controller_TemplateDefault_Admin {
'price_type'=>array('label'=>'Price Type'),
'price_base'=>array('label'=>'Price Base'),
'taxable'=>array('label'=>'Taxable'),
'services_count()'=>array('label'=>'Services'),
'invoices_count()'=>array('label'=>'Invoices'),
),
array(
'page'=>TRUE,
'type'=>'select',
'form'=>'user/product/view',
'form'=>'product/view',
)),
));
}

View File

@@ -9,6 +9,9 @@
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*
* Column Definitions:
* + price_type: 0=One Time, 1=Recurring, 2=Trial
*/
class Model_Product extends ORMOSB {
// @todo this doesnt have our site_id when getting the translation

View File

@@ -12,17 +12,38 @@
*/
class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
protected $secure_actions = array(
'autolist'=>FALSE, // @todo To Change
'list'=>TRUE,
'listbycheckout'=>TRUE,
'listadslbilling'=>TRUE,
'listadslservices'=>TRUE,
'listdomainservices'=>TRUE,
'listdomainservicesbysupplier'=>TRUE,
'listhostservices'=>TRUE,
'listhspaservices'=>TRUE,
'listinvoicesoon'=>TRUE,
'update'=>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
*/
@@ -53,73 +74,72 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
* List all services by their default checkout method
*/
public function action_listbycheckout() {
// @todo need to add the DB prefix here
// @todo need to remove the explicit references to the group_id
$services = DB::query(Database::SELECT,'
SELECT c.id AS cid,c.name as checkout_plugin_name,s.id AS sid,a.company,a.first_name,a.last_name,a.id as aid
FROM ab_service s LEFT JOIN ab_account_billing ab ON (s.account_billing_id=ab.id) LEFT JOIN ab_checkout c ON (ab.checkout_plugin_id=c.id),ab_account a, ab_account_group ag
WHERE s.active=1 AND s.price > 0 AND s.account_id=a.id AND a.id=ag.account_id AND ((s.account_billing_id IS NOT NULL AND ag.group_id!=2 ) OR (a.id=ag.account_id and ag.group_id=1003))
ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name
')
->execute();
$svs = array();
// @todo This needs to be configurable
$go = ORM::factory('group',array('name'=>'Personal'));
// @todo If no items, show a nice message. This is not correct for ORM.
if (! count($services)) {
echo Kohana::debug('No services with account_billing');
die();
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'=>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'=>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',
)),
));
}
$last_checkout = '';
$last_account = '';
$i = 0;
$sc = $st = 0;
$output = '<table class="box-left">';
foreach ($services as $service) {
$so = ORM::factory('service',$service['sid']);
$si = $so->account_id.$so->recur_schedule;
if (($si != $last_account) AND $last_account) {
if ($sc > 1)
$output .= View::factory('service/admin/list/bycheckout_subtotal')
->set('subtotal',Currency::display($st))
->set('i',$i++%2);
$sc = $st = 0;
}
if (($service['cid'] != $last_checkout) OR (! is_null($last_checkout) AND ! $last_checkout)) {
$output .= View::factory('service/admin/list/bycheckout_header')
->set('checkout_name',$service['checkout_plugin_name'])
->set('last_checkout',$last_checkout);
}
$last_checkout = $service['cid'];
$last_account = $si;
// @todo This rounding should be a system default
$st += round($so->price+$so->tax(),2);
$sc++;
$output .= View::factory('service/admin/list/bycheckout_body')
->set('service',$so)
->set('i',$i++%2);
}
// Last subtotal
if ($sc > 1)
$output .= View::factory('service/admin/list/bycheckout_subtotal')
->set('subtotal',$st)
->set('i',$i++%2);
$output .= '</table>';
Block::add(array(
'title'=>_('List all Services by Default Payment Method'),
'body'=>$output,
));
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
}
private function consoltraffic($svs,$date) {
@@ -255,6 +275,39 @@ ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name
));
}
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'=>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()');
@@ -298,7 +351,7 @@ ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name
->rule('csv','Upload::type',array(':value',array('csv')))
->rule('csv','Upload::size',array(':value','10M'));
if ($files->check())
if ($files->check())
foreach ($files as $file)
$csv = $this->process($file);
}
@@ -318,7 +371,7 @@ ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name
// If our uploaded file has some cost data.
if (! empty($csv[$so->plugin()->service_number])) {
$uploaded['amount'] =
$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']);

View File

@@ -50,74 +50,77 @@ class Controller_Affiliate_Service extends Controller_TemplateDefault_Affiliate
* List all services by their default checkout method
*/
public function action_listbycheckout() {
// @todo need to add the DB prefix here
// @todo need to remove the explicit references to the group_id
// @todo need to restrict this to affiliate services
$services = DB::query(Database::SELECT,'
SELECT c.id AS cid,c.name as checkout_plugin_name,s.id AS sid,a.company,a.first_name,a.last_name,a.id as aid
FROM ab_service s LEFT JOIN ab_account_billing ab ON (s.account_billing_id=ab.id) LEFT JOIN ab_checkout c ON (ab.checkout_plugin_id=c.id),ab_account a, ab_account_group ag
WHERE s.active=1 AND s.price > 0 AND s.account_id=a.id AND a.id=ag.account_id AND ((s.account_billing_id IS NOT NULL AND ag.group_id!=2 ) OR (a.id=ag.account_id and ag.group_id=1003))
ORDER BY c.id,s.recur_schedule,c.name,a.company,a.last_name,a.first_name
')
->execute();
$svs = array();
// @todo This needs to be configurable
$go = ORM::factory('group',array('name'=>'Personal'));
// @todo If no items, show a nice message. This is not correct for ORM.
if (! count($services)) {
echo Kohana::debug('No services with account_billing');
die();
}
foreach (ORM::factory('account')->where('status','=',1)->find_all() as $ao)
if ($ao->has_any('group',array($go)))
foreach ($this->filter($ao->service->list_active(),$this->ao->affiliate->id,'name()') as $so)
if (! $so->service_billing->checkout_plugin_id)
array_push($svs,$so);
$last_checkout = '';
$last_account = '';
$i = 0;
$sc = $st = 0;
$output = '<table class="box-left">';
foreach ($services as $service) {
$so = ORM::factory('service',$service['sid']);
$si = $so->account_id.$so->recur_schedule;
if (($si != $last_account) AND $last_account) {
if ($sc > 1)
$output .= View::factory('service/admin/list/bycheckout_subtotal')
->set('subtotal',Currency::display($st))
->set('i',$i++%2);
$sc = $st = 0;
}
if (($service['cid'] != $last_checkout) OR (! is_null($last_checkout) AND ! $last_checkout)) {
$output .= View::factory('service/admin/list/bycheckout_header')
->set('checkout_name',$service['checkout_plugin_name'])
->set('last_checkout',$last_checkout);
}
$last_checkout = $service['cid'];
$last_account = $si;
// @todo This rounding should be a system default
$st += round($so->price+$so->tax(),2);
$sc++;
$output .= View::factory('service/admin/list/bycheckout_body')
->set('service',$so)
->set('i',$i++%2);
}
// Last subtotal
if ($sc > 1)
$output .= View::factory('service/admin/list/bycheckout_subtotal')
->set('subtotal',$st)
->set('i',$i++%2);
$output .= '</table>';
Block::add(array(
'title'=>_('List all Services by Default Payment Method'),
'body'=>$output,
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'=>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',
)),
));
else
Block::add(array(
'title'=>'Services that should be auto-billed',
'body'=>_('None found'),
));
Style::add(array(
'type'=>'file',
'data'=>'css/list.css',
));
foreach (ORM::factory('checkout')->where('active','=',1)->find_all() as $co) {
$svs = array();
foreach ($co->account->find_all() as $ao)
foreach ($this->filter($ao->service->list_active(),$this->ao->affiliate->id,'name()') 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'=>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) {

View File

@@ -12,6 +12,9 @@
*/
class Model_Service extends ORMOSB {
// Relationships
protected $_has_one = array(
'service_billing'=>array('far_key'=>'account_billing_id','foreign_key'=>'id'),
);
protected $_has_many = array(
'invoice'=>array('through'=>'invoice_item'),
);
@@ -108,14 +111,6 @@ class Model_Service extends ORMOSB {
return $plugin->_admin_update();
}
// @todo To implement
/**
* Calculate the tax for this item
*/
public function tax() {
return $this->price * .1;
}
/** LIST FUNCTIONS **/
public function list_active() {

View File

@@ -0,0 +1,16 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports Service Billing.
*
* @package OSB
* @subpackage Product/Service
* @category Models
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Model_Service_Billing extends ORMOSB {
protected $_table_name = 'account_billing';
}
?>

View File

@@ -1,9 +0,0 @@
<tr class="<?php echo $i ? 'odd' : 'even'; ?>">
<td><?php echo $service->account->accnum(); ?></td>
<td><?php echo $service->account->name(); ?></td>
<td><?php printf('%s (%s)',$service->name(),$service->id); ?></td>
<td><?php echo $service->display('recur_schedule'); ?></td>
<td><?php echo $service->display('price'); ?></td>
<td><?php echo $service->account->invoices_due_total(); ?></td>
<td><?php echo $service->display('date_next_invoice'); ?></td>
</tr>

View File

@@ -1,16 +0,0 @@
<!-- Print out the heading for the first record -->
<!-- // @todo This needs to be translated -->
<?php if (! $last_checkout); {?>
<tr class="title">
<td colspan="7"><?php echo $checkout_name; ?></td>
</tr>
<tr class="head">
<td>Account ID</td>
<td>Account</td>
<td>Service</td>
<td>Frequency</td>
<td>Amount</td>
<td>Invoices Due</td>
<td>Next Payment Date</td>
</tr>
<?php } ?>

View File

@@ -1,5 +0,0 @@
<tr class="<?php echo $i ? 'odd' : 'even'; ?>">
<td colspan="4">&nbsp;</td>
<td><b><?php echo $subtotal; ?></b></td>
<td colspan="2">&nbsp;</td>
</tr>

View File

@@ -44,7 +44,7 @@ class Controller_User_Statement extends Controller_TemplateDefault_User {
}
if (isset($v['invoice']))
$t += $v['invoice']->total_amt;
$t += $v['invoice']->total();
elseif (isset($v['payment']))
$t -= $v['payment']->total_amt;

View File

@@ -3,7 +3,7 @@
<td><?php echo $o['invoice']->display('date_orig'); ?></td>
<td>Invoice</td>
<td><?php echo HTML::anchor('user/invoice/view/'.$o['invoice']->id,$o['invoice']->id()); ?></td>
<td class="right"><?php echo $o['invoice']->display('total_amt'); ?></td>
<td class="right"><?php echo $o['invoice']->total(TRUE); ?></td>
<?php } elseif (isset($o['payment'])) { ?>
<td><?php echo $o['payment']->display('date_payment'); ?></td>
<td>Payment</td>