diff --git a/application/classes/Controller/Reseller/Account.php b/application/classes/Controller/Reseller/Account.php index e504a3e8..c1ca76f7 100644 --- a/application/classes/Controller/Reseller/Account.php +++ b/application/classes/Controller/Reseller/Account.php @@ -11,27 +11,11 @@ */ class Controller_Reseller_Account extends Controller_Account { protected $secure_actions = array( - 'ajaxlist'=>TRUE, 'list'=>TRUE, 'listlog'=>TRUE, 'view'=>TRUE, ); - /** - * Used by AJAX calls to find accounts - * @note list_autocomplete() will limit to authorised accounts - */ - public function action_ajaxlist() { - $result = array(); - - if (isset($_REQUEST['term']) AND trim($_REQUEST['term'])) - $result += ORM::factory('Account')->list_autocomplete($_REQUEST['term']); - - $this->auto_render = FALSE; - $this->response->headers('Content-Type','application/json'); - $this->response->body(json_encode(array_values($result))); - } - /** * Show a list of accounts */ @@ -138,7 +122,7 @@ class Controller_Reseller_Account extends Controller_Account { ->title(sprintf('Next Invoice Items for Account: %s',$ao->accnum())) ->title_icon('icon-info-sign') ->span(6) - ->body($i->render('html','body')); + ->body($i->render('html','body',array('noid'=>TRUE))); } } ?> diff --git a/application/classes/Controller/TemplateDefault.php b/application/classes/Controller/TemplateDefault.php index 431e9f49..56653065 100644 --- a/application/classes/Controller/TemplateDefault.php +++ b/application/classes/Controller/TemplateDefault.php @@ -41,7 +41,7 @@ class Controller_TemplateDefault extends lnApp_Controller_TemplateDefault { // @todo To rework public function after() { - $dc = 'u/welcome/index'; + $dc = URL::link('user','welcome/index'); $m = sprintf('%s/%s',Request::current()->directory(),Request::current()->controller()); BreadCrumb::URL(Request::current()->directory(),sprintf('%s/%s',Request::current()->directory(),$dc),FALSE); diff --git a/application/classes/Controller/User/Search.php b/application/classes/Controller/User/Search.php index 4c612ad6..3ef0e2d1 100644 --- a/application/classes/Controller/User/Search.php +++ b/application/classes/Controller/User/Search.php @@ -21,12 +21,12 @@ class Controller_User_Search extends Controller_Search { $result = array(); if (isset($_REQUEST['term']) AND trim($_REQUEST['term'])) { - $result = Arr::merge($result,ORM::factory('Account')->list_autocomplete($_REQUEST['term'],'url','id',array('ACC %s: %s'=>array('id','name(TRUE)')),array(),array('urlprefix'=>'r/account/view/'))); - $result = Arr::merge($result,ORM::factory('Service')->list_autocomplete($_REQUEST['term'],'url','id',array('SVC %s: %s'=>array('id','service_name()')),array(),array('urlprefix'=>'u/service/view/'))); - $result = Arr::merge($result,ORM::factory('Invoice')->list_autocomplete($_REQUEST['term'],'url','id',array('INV %s: %s'=>array('id','account->name(TRUE)')),array(),array('urlprefix'=>'u/invoice/view/'))); + $result = Arr::merge($result,ORM::factory('Account')->list_autocomplete($_REQUEST['term'],'url','id',array('ACC %s: %s'=>array('id','name(TRUE)')),array(),array('urlprefix'=>URL::link('reseller','account/view/')))); + $result = Arr::merge($result,ORM::factory('Service')->list_autocomplete($_REQUEST['term'],'url','id',array('SVC %s: %s'=>array('id','service_name()')),array(),array('urlprefix'=>URL::link('user','service/view/')))); + $result = Arr::merge($result,ORM::factory('Invoice')->list_autocomplete($_REQUEST['term'],'url','id',array('INV %s: %s'=>array('id','account->name(TRUE)')),array(),array('urlprefix'=>URL::link('user','invoice/view/')))); foreach (array('Service_Plugin_Adsl','Service_Plugin_Domain','Service_Plugin_Host') as $o) - $result = Arr::merge($result,ORM::factory($o)->list_autocomplete($_REQUEST['term'],'url','service_id',array('SVC %s: %s'=>array('service_id','service_name()')),array(),array('urlprefix'=>'u/service/view/'))); + $result = Arr::merge($result,ORM::factory($o)->list_autocomplete($_REQUEST['term'],'url','service_id',array('SVC %s: %s'=>array('service_id','service_name()')),array(),array('urlprefix'=>URL::link('user','service/view/')))); } $this->response->headers('Content-Type','application/json'); diff --git a/application/classes/Minion/Task.php b/application/classes/Minion/Task.php index 2bb1182c..cfe2a236 100644 --- a/application/classes/Minion/Task.php +++ b/application/classes/Minion/Task.php @@ -10,11 +10,17 @@ * @license http://dev.osbill.net/license.html */ abstract class Minion_Task extends Kohana_Minion_Task { - protected $_options = array( + protected $_sysoptions = array( 'site'=>NULL, - 'id'=>NULL, - 'force'=>FALSE, 'verbose'=>FALSE, ); + + /** + * Override our __construct so that we can specify options in each class file + */ + protected function __construct() { + // Populate $_accepted_options based on keys from $_options + $this->_accepted_options = array_keys(Arr::merge($this->_sysoptions,$this->_options)); + } } ?> diff --git a/application/classes/Model/Country.php b/application/classes/Model/Country.php index 232ea6d0..4465db21 100644 --- a/application/classes/Model/Country.php +++ b/application/classes/Model/Country.php @@ -14,6 +14,10 @@ class Model_Country extends ORM_OSB { 'currency'=>array('far_key'=>'id'), ); + protected $_sorting = array( + 'name'=>'ASC', + ); + protected $_form = array('id'=>'id','value'=>'name'); public static function icon() { diff --git a/application/classes/Model/Currency.php b/application/classes/Model/Currency.php index c00b8879..23943eb6 100644 --- a/application/classes/Model/Currency.php +++ b/application/classes/Model/Currency.php @@ -10,6 +10,10 @@ * @license http://dev.osbill.net/license.html */ class Model_Currency extends ORM_OSB { + protected $_sorting = array( + 'name'=>'ASC', + ); + protected $_form = array('id'=>'id','value'=>'name'); } ?> diff --git a/application/classes/Model/Language.php b/application/classes/Model/Language.php index 22d28446..212c178b 100644 --- a/application/classes/Model/Language.php +++ b/application/classes/Model/Language.php @@ -10,6 +10,10 @@ * @license http://dev.osbill.net/license.html */ class Model_Language extends ORM_OSB { + protected $_sorting = array( + 'name'=>'ASC', + ); + protected $_form = array('id'=>'id','value'=>'name'); } ?> diff --git a/application/classes/ORM/OSB.php b/application/classes/ORM/OSB.php index 2abb3add..2776ffdd 100644 --- a/application/classes/ORM/OSB.php +++ b/application/classes/ORM/OSB.php @@ -27,6 +27,11 @@ abstract class ORM_OSB extends ORM { // Our attribute values that need to be stored as serialized protected $_serialize_column = array(); + // If we need to load any sub items on loading this model + protected $_sub_items = array(); + protected $_sub_items_load = array(); + protected $_sub_items_sorted = FALSE; + // Rules to assist with site ID and getting next record ID for inserts. public function rules() { return array( @@ -101,6 +106,25 @@ abstract class ORM_OSB extends ORM { return parent::__get($column); } + /** + * Intercept our object load, so that we can load our subitems + */ + protected function _load_values(array $values) { + parent::_load_values($values); + + $sort = FALSE; + if ($this->_loaded AND $this->_sub_items_load AND count($this->_sub_items_load) == 1) + foreach ($this->_sub_items_load as $item => $sort) + $this->_sub_items = $this->$item->find_all()->as_array(); + + if ($sort) { + Sort::MAsort($this->_sub_items,$sort); + $this->_sub_items_sorted = TRUE; + } + + return $this; + } + /** * If a column is marked to be nullified if it is empty, this is where it is done. */ @@ -266,7 +290,7 @@ abstract class ORM_OSB extends ORM { if ($this->_form AND array_intersect(array('id','value'),$this->_form)) foreach ($this->find_all() as $o) - $result[$o->{$this->_form['id']}] = $o->{$this->_form['value']}; + $result[$o->{$this->_form['id']}] = $o->resolve($this->_form['value']); return $result; } diff --git a/application/classes/StaticList/ItemType.php b/application/classes/StaticList/ItemType.php index 7643f1d3..a2c934df 100644 --- a/application/classes/StaticList/ItemType.php +++ b/application/classes/StaticList/ItemType.php @@ -12,8 +12,15 @@ class StaticList_ItemType extends StaticList { protected function _table() { return array( - 0=>'MAIN', // Line Charge Topic on Invoice, eg: Service Name - 5=>'EXCESS', // Excess Service Item, of item 0 + 0=>_('Product/Service Charge'), // Line Charge Topic on Invoice, eg: Service Name + 1=> _('Hardware'), + 2=> _('Service Relocation Fee'), + 3=> _('Service Change Fee'), + 4=> _('Service Connection Fee'), + 5=>_('Excess Usage'), // Excess Service Item, of item 0 + 125=>_('Payment Fee'), // Payment processing fee + 126=> _('Rounding'), + 127=> _('Late Payment Fee'), ); } diff --git a/application/config/auth.php b/application/config/auth.php index a691bae8..0a0001f0 100644 --- a/application/config/auth.php +++ b/application/config/auth.php @@ -4,7 +4,6 @@ * OSB Configuration - Authentication * * @package OSB - * @subpackage Authentication * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/config/cache.php b/application/config/cache.php index e9a7cb52..75b1c734 100644 --- a/application/config/cache.php +++ b/application/config/cache.php @@ -4,7 +4,6 @@ * OSB Caching * * @package OSB - * @subpackage System * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/config/config.php b/application/config/config.php index 615c1da6..8dc487a8 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -4,7 +4,6 @@ * OSB Configuration - System Default Configurable Items. * * @package OSB - * @subpackage System * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/config/database.php b/application/config/database.php index d7fa4015..392c2260 100644 --- a/application/config/database.php +++ b/application/config/database.php @@ -4,7 +4,6 @@ * OSB Configuration - Database Driver * * @package OSB - * @subpackage Database * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/config/debug.php b/application/config/debug.php index 29c54770..b9f27082 100644 --- a/application/config/debug.php +++ b/application/config/debug.php @@ -4,7 +4,6 @@ * OSB Configuration - Debug Settings * * @package OSB - * @subpackage Debug * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/config/pwgen.php b/application/config/pwgen.php index 70feb2aa..2cfbe160 100644 --- a/application/config/pwgen.php +++ b/application/config/pwgen.php @@ -4,7 +4,6 @@ * OSB Configuration - PWGen Configuration * * @package OSB - * @subpackage PWGen * @category PWGen * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/application/tests/classes/PeriodTest.php b/application/tests/classes/PeriodTest.php new file mode 100644 index 00000000..741baa6a --- /dev/null +++ b/application/tests/classes/PeriodTest.php @@ -0,0 +1,187 @@ +TIME = strtotime($this->DATE); + $this->w = date('w',$this->TIME); + } + + // Weekly + public function test_weekly() { + # +7 week + foreach (array(0,2,6) as $x) { + $p = Period::details(0,NULL,$this->TIME+86400*$x,TRUE,FALSE); + $this->assertEquals(date('d-M-Y',$this->TIME+86400*(7+$x-1)),$p['end']); + } + + # +7 week, SUN + foreach (array(0,2,6) as $x) { + $p = Period::details(0,0,$this->TIME+86400*$x,TRUE,FALSE); + $this->assertEquals(date('d-M-Y',$this->TIME+86400*(7-($this->w+$x)%7+$x-1)),$p['end']); // Sat + } + + # +7 week, TUE + foreach (array(0,2,6) as $x) { + $p = Period::details(0,2,$this->TIME+86400*$x,TRUE,FALSE); + $this->assertEquals(date('d-M-Y',$this->TIME+86400*(7-($this->w+$x)%7+$x-1+2)),$p['end']); // Sat + } + } + + public function test_monthly() { + # +1 month + $p = Period::details(1,NULL,strtotime('2012-07-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-08-19')),$p['end']); + + $p = Period::details(1,NULL,strtotime('2012-07-01'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(1,NULL,strtotime('2012-07-31'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-08-30')),$p['end']); + + # +1 month, 1st of the month + $p = Period::details(1,1,strtotime('2012-07-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(1,1,strtotime('2012-07-01'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(1,1,strtotime('2012-07-31'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + # +1 month, 15th of the month + $p = Period::details(1,15,strtotime('2012-07-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-08-14')),$p['end']); + + $p = Period::details(1,15,strtotime('2012-02-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-03-14')),$p['end']); + + $p = Period::details(1,15,strtotime('2012-02-14'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-02-14')),$p['end']); + + $p = Period::details(1,15,strtotime('2012-02-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-03-14')),$p['end']); + + # +1 month, 1st of the month, strict doesnt have any affect + $p = Period::details(1,1,strtotime('2012-07-20'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(1,1,strtotime('2012-07-01'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(1,1,strtotime('2012-07-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + } + + public function test_quarterly() { + # +3 months + $p = Period::details(2,NULL,strtotime('2012-02-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-05-14')),$p['end']); + + $p = Period::details(2,NULL,strtotime('2012-11-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2013-02-14')),$p['end']); + + $p = Period::details(2,NULL,strtotime('2012-08-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-11-19')),$p['end']); + + # +3 months, 1st of the month + $p = Period::details(2,1,strtotime('2012-08-20'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-10-31')),$p['end']); + + $p = Period::details(2,1,strtotime('2012-11-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2013-01-31')),$p['end']); + + # +3 months, 1st of the month, strict JAN-MAR,APR-JUN,JUL-SEP,OCT-DEC + $p = Period::details(2,1,strtotime('2012-11-15'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-12-31')),$p['end']); + + $p = Period::details(2,1,strtotime('2013-01-01'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-03-31')),$p['end']); + } + + public function test_halfyearly() { + # +6 months + $p = Period::details(3,NULL,strtotime('2012-02-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-08-14')),$p['end']); + + $p = Period::details(3,NULL,strtotime('2011-09-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-03-14')),$p['end']); + + # +6 months, 1st of the month + $p = Period::details(3,1,strtotime('2012-02-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-07-31')),$p['end']); + + $p = Period::details(3,1,strtotime('2011-09-15'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2012-02-29')),$p['end']); + + # +6 months, 1st of the month, strict JAN-JUN,JUL-DEC + $p = Period::details(3,1,strtotime('2012-02-15'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-06-30')),$p['end']); + + $p = Period::details(3,1,strtotime('2012-01-01'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-06-30')),$p['end']); + } + + public function test_yearly() { + # +1 year + $p = Period::details(4,NULL,strtotime('2012-02-29'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2013-02-28')),$p['end']); + + # +1 year, 1st of the month + $p = Period::details(4,1,strtotime('2012-02-29'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2013-01-31')),$p['end']); + + $p = Period::details(4,1,strtotime('2012-02-01'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2013-01-31')),$p['end']); + + # +1 year, 1st of the month, strict JAN-DEC + $p = Period::details(4,1,strtotime('2012-02-29'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-12-31')),$p['end']); + + $p = Period::details(4,1,strtotime('2012-12-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2012-12-31')),$p['end']); + + $p = Period::details(4,1,strtotime('2013-02-28'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-12-31')),$p['end']); + + $p = Period::details(4,1,strtotime('2013-12-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-12-31')),$p['end']); + } + + public function test_twoyear() { + # +2 year + $p = Period::details(5,NULL,strtotime('2012-02-29'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2014-02-28')),$p['end']); + + # +2 year, 1st of the month + $p = Period::details(5,1,strtotime('2012-02-29'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2014-01-31')),$p['end']); + + $p = Period::details(5,1,strtotime('2012-02-01'),TRUE,FALSE); + $this->assertEquals(date('d-M-Y',strtotime('2014-01-31')),$p['end']); + + # +2 year, 1st of the month, strict JAN/YEAR%2 + $p = Period::details(5,1,strtotime('2012-02-29'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-12-31')),$p['end']); + + $p = Period::details(5,1,strtotime('2012-01-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-12-31')),$p['end']); + + $p = Period::details(5,1,strtotime('2013-01-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2014-12-31')),$p['end']); + + $p = Period::details(5,1,strtotime('2012-12-31'),TRUE,TRUE); + $this->assertEquals(date('d-M-Y',strtotime('2013-12-31')),$p['end']); + } +} +?> diff --git a/application/views/account/user/edit.php b/application/views/account/user/edit.php index 507ef24b..3715576c 100644 --- a/application/views/account/user/edit.php +++ b/application/views/account/user/edit.php @@ -9,11 +9,9 @@ display('email'),array('label'=>'Email','class'=>'input-xxlarge','placeholder'=>'Email Address','type'=>'email','required')); ?> -
-
- display('title'),array('class'=>'input-small','label'=>'Title','required')); ?> -
+ display('title'),array('class'=>'input-small','label'=>'Title','required')); ?> +
display('first_name'),array('label'=>'','class'=>'input-medium','placeholder'=>'First Name','required')); ?>
@@ -21,7 +19,6 @@
display('last_name'),array('label'=>'','class'=>'input-large','placeholder'=>'Last Name','required')); ?>
-
display('company'),array('label'=>'Company','class'=>'input-xxlarge','placeholder'=>'Company Name')); ?> diff --git a/modules/adsl/classes/Task/Adsl/Trafficget.php b/modules/adsl/classes/Task/Adsl/Trafficget.php index 139bf07d..5cae66e9 100644 --- a/modules/adsl/classes/Task/Adsl/Trafficget.php +++ b/modules/adsl/classes/Task/Adsl/Trafficget.php @@ -18,7 +18,7 @@ class Task_Adsl_Trafficget extends Minion_Task { protected function _execute(array $params) { foreach ($this->_traffic_suppliers(TRUE) as $aso) { - if (Minion_CLI::options('verbose')) + if ($params['verbose']) echo $aso->name."\n"; Service_Traffic_Adsl::instance($aso->name)->update_traffic(); diff --git a/modules/charge/classes/Controller/Admin/Charge.php b/modules/charge/classes/Controller/Admin/Charge.php deleted file mode 100644 index 17f6845c..00000000 --- a/modules/charge/classes/Controller/Admin/Charge.php +++ /dev/null @@ -1,133 +0,0 @@ -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'=>URL::link('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'=>URL::link('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($this->viewpath()); - $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'=>'media/js/jquery.ui/css/smoothness/jquery-ui-1.8.16.custom.css', - )); - - Script::add(array( - 'type'=>'file', - 'data'=>'media/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::link('admin','account/ajaxlist').'", - 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::link('admin','service/ajaxlist',TRUE).'", - 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 = ""; - $(row).appendTo("select[name=service_id]"); - - // Fill sub category select - $.each(data, function(i, j){ - var row = ""; - $(row).appendTo("select[name=service_id]"); - }); - } - }); - } - }); - });' - )); - - Block::add(array( - 'title'=>_('Add Customer Charges'), - 'body'=>$output, - )); - } -} -?> diff --git a/modules/charge/classes/Controller/Charge.php b/modules/charge/classes/Controller/Charge.php new file mode 100644 index 00000000..e55daace --- /dev/null +++ b/modules/charge/classes/Controller/Charge.php @@ -0,0 +1,14 @@ + diff --git a/modules/charge/classes/Controller/Reseller/Charge.php b/modules/charge/classes/Controller/Reseller/Charge.php new file mode 100644 index 00000000..ba8a4009 --- /dev/null +++ b/modules/charge/classes/Controller/Reseller/Charge.php @@ -0,0 +1,171 @@ +TRUE, + 'ajaxlist'=>TRUE, + 'ajaxlistservice'=>TRUE, + 'edit'=>TRUE, + 'list'=>TRUE, + ); + + public function action_add() { + Block::factory() + ->type('form-horizontal') + ->title('Add/View Charge') + ->title_icon('icon-wrench') + ->body($this->add_edit()); + } + + public function action_ajaxlist() { + $result = array(); + + if (isset($_REQUEST['term']) AND trim($_REQUEST['term'])) { + $result = Arr::merge($result,ORM::factory('Account')->list_autocomplete($_REQUEST['term'],'id','id',array('ACC %s: %s'=>array('id','name(TRUE)')))); + $result = Arr::merge($result,ORM::factory('Service')->list_autocomplete($_REQUEST['term'],'account_id','id',array('ACC %s: %s (%s)'=>array('account_id','account->name()','name()')))); + + foreach (array('Service_Plugin_Adsl','Service_Plugin_Domain','Service_Plugin_Host') as $o) + $result = Arr::merge($result,ORM::factory($o)->list_autocomplete($_REQUEST['term'],'account_id','service->account_id',array('ACC %s: %s (%s)'=>array('service->account_id','service->account->name()','name()')))); + } + + $this->response->headers('Content-Type','application/json'); + $this->response->body(json_encode(array_values($result))); + } + + public function action_ajaxlistservice() { + $result = array(); + + if (isset($_REQUEST['key']) AND trim($_REQUEST['key'])) + $result = Arr::merge($result,ORM::factory('Service')->list_autocomplete('','id','id',array('SVC %s: %s'=>array('id','service_name()')),array(array('account_id','=',$_REQUEST['key'])))); + + $this->response->headers('Content-Type','application/json'); + $this->response->body(json_encode(array_values($result))); + } + + private function add_edit($id=NULL,$output='') { + $co = ORM::factory('Charge',$id); + + if ($_POST) { + // Entry updated + if ($co->values($_POST)->check() AND $co->save()) + SystemMessage::factory() + ->title('Record updated') + ->type('success') + ->body(_('Your Charge record has been recorded/updated.')); + } + + Script::factory() + ->type('file') + ->data('media/theme/bootstrap/vendor/datepicker/js/bootstrap-datepicker.js'); + + Style::factory() + ->type('file') + ->data('media/theme/bootstrap/vendor/datepicker/css/datepicker.css'); + + Script::factory() + ->type('stdin') + ->data(' +$(document).ready(function() { + var nowTemp = new Date(); + var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0); + + $("#date_charge_label").datepicker({ + autoclose : true, + endDate : now, + format : "dd-mm-yyyy", + todayBtn : true, + }).on("hide",function(ev) { + $("input[name=date_charge]").val(ev.date.valueOf()/1000); + }); + + $("input[name=account_id_label]").typeahead({ + minLength: 2, + source: function (query,process) { + search("'.URL::link('reseller','charge/ajaxlist').'",query,process); + }, + + matcher: function () { return true; }, + + updater: function (item) { + $("input[name=account_id]").val(users[item]); + + // Send the request and update sub category dropdown + $.ajax({ + type: "GET", + data: "key="+users[item], + dataType: "json", + cache: false, + url: "'.URL::link('reseller','charge/ajaxlistservice',TRUE).'", + timeout: 2000, + error: function(x) { + alert("Failed to submit"); + }, + success: function(data) { + $.each(data, function(i, j){ + var row = ""; + $(row).appendTo("select[name=service_id]"); + }); + } + }); + + return item; + }, + + }); + +}); + '); + + return View::factory('charge/reseller/add_edit') + ->set('o',$co); + } + + public function action_edit() { + list($id,$output) = Table::page(__METHOD__); + + Block::factory() + ->type('form-horizontal') + ->title(sprintf('%s: %s',_('View Charges'),$id)) + ->title_icon('icon-wrench') + ->body($this->add_edit($id,$output)); + } + + /** + * Show a list of invoices + */ + public function action_list() { + Block::factory() + ->title('Customer Charges') + ->title_icon('icon-th-list') + ->body(Table::factory() + ->page_items(50) + ->data(ORM::factory('Charge')->where('account_id','IN',$this->ao->RTM->customers($this->ao->RTM))->order_by('id DESC')->find_all()) + ->columns(array( + 'id'=>'ID', + 'date_orig'=>'Processed', + 'date_charge'=>'Date', + 'sweep_type'=>'Sweep', + 'status'=>'Status', + 'quantity'=>'Quantity', + 'amount'=>'Amount', + 'description'=>'Description', + 'service_id'=>'Service', + 'account->accnum()'=>'Cust ID', + 'account->name()'=>'Customer', + )) + ->prepend(array( + 'id'=>array('url'=>URL::link('reseller','charge/edit/')), + )) + ); + } +} +?> diff --git a/modules/charge/classes/Model/Charge.php b/modules/charge/classes/Model/Charge.php index 73d8b3f8..be0ccabc 100644 --- a/modules/charge/classes/Model/Charge.php +++ b/modules/charge/classes/Model/Charge.php @@ -10,21 +10,25 @@ * @license http://dev.osbill.net/license.html */ class Model_Charge extends ORM_OSB { - // Charge doesnt use the update column - protected $_updated_column = FALSE; + protected $_belongs_to = array( + 'account'=>array(), + ); + + protected $_nullifempty = array( + 'attributes', + ); protected $_serialize_column = array( 'attributes', ); - protected $_belongs_to = array( - 'account'=>array(), - ); - protected $_display_filters = array( 'date_orig'=>array( array('Config::date',array(':value')), ), + 'date_charge'=>array( + array('Config::date',array(':value')), + ), 'amount'=>array( array('Currency::display',array(':value')), ), diff --git a/modules/charge/views/charge/admin/add.php b/modules/charge/views/charge/admin/add.php deleted file mode 100644 index 052395ad..00000000 --- a/modules/charge/views/charge/admin/add.php +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account
Service
Sweep
Quantity
Amount
Taxable
Description
Attributes
diff --git a/modules/charge/views/charge/reseller/add_edit.php b/modules/charge/views/charge/reseller/add_edit.php new file mode 100644 index 00000000..977ea867 --- /dev/null +++ b/modules/charge/views/charge/reseller/add_edit.php @@ -0,0 +1,31 @@ +
+
+ +
+ Charge Details + +
+ display('date_charge'),array('class'=>'span2','label'=>'Date of Charge','add-on'=>'','disabled')); ?> +
+ + date_charge); ?> + account->name(),array('class'=>'span5','label'=>'Account','placeholder'=>'Account','data-provide'=>'typeahead','required')); ?> + account_id); ?> + account_id ? $o->account->service->list_select() : array(),$o->service_id,array('class'=>'span5','label'=>'Service')); ?> + sweep_type) ? 6 : $o->sweep_type,FALSE,array('label'=>'Sweep')); ?> + type) ? 6 : $o->type,FALSE,array('label'=>'Item Type')); ?> + + quantity,array('class'=>'span1','label'=>'Quantity','placeholder'=>'Quantity')); ?> + amount,array('class'=>'span1','label'=>'Amount','placeholder'=>'Total',)); ?> + taxable) ? TRUE : $o->taxable,FALSE,array('label'=>'Taxable','class'=>'span1')); ?> + description,array('class'=>'span5','label'=>'Description','placeholder'=>'Any notes about this charge?')); ?> + + + attributes[$i]) ? $o->attributes[$i] : "",array('class'=>'span5','label'=>'Attributes')); + endfor ?> +
+ + 'btn btn-primary')); ?> +
+
diff --git a/modules/domain/views/service/user/plugin/domain/view.php b/modules/domain/views/service/user/plugin/domain/view.php index 77553058..10e3cdbb 100644 --- a/modules/domain/views/service/user/plugin/domain/view.php +++ b/modules/domain/views/service/user/plugin/domain/view.php @@ -10,7 +10,7 @@
display('domain_expire'); ?>
Domain Auth Password
-
display('registrar_auth_password'); ?>
+
service->expiring() ? $o->display('registrar_auth_password') : 'EXPIRED'; ?>
manage_button()) : ?>
Registrar
diff --git a/modules/invoice/classes/Controller/Task/Invoice.php b/modules/invoice/classes/Controller/Task/Invoice.php index 69da9959..71d84d3d 100644 --- a/modules/invoice/classes/Controller/Task/Invoice.php +++ b/modules/invoice/classes/Controller/Task/Invoice.php @@ -12,155 +12,6 @@ class Controller_Task_Invoice extends Controller_Task { public $auto_render = FALSE; - /** - * Email a customers a reminder of their upcoming invoices that are due. - */ - public function action_remind_due() { - $action = array(); - $key = 'remind_due'; - $days = ORM::factory('Invoice')->config('REMIND_DUE'); - - foreach (ORM::factory('Invoice')->list_due(time()+86400*$days) as $io) { - // @todo Use another option to supress reminders - // 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')) OR ($io->account->invoice_delivery != 1)) - continue; - - // Send our email - $et = Email_Template::instance('task_invoice_'.$key); - - $et->to = array('account'=>array($io->account_id)); - $et->variables = array( - 'DUE'=>$io->due(TRUE), - 'DUE_DATE'=>$io->display('due_date'), - 'FIRST_NAME'=>$io->account->first_name, - 'INV_NUM'=>$io->refnum(), - 'INV_URL'=>URL::site(URL::link('user','invoice/view/'.$io->id),'http'), - 'SITE_NAME'=>Company::instance()->name(), - ); - - // @todo Record email log id if possible. - if ($et->send()) { - $io->set_remind($key,time()); - array_push($action,(string)$io); - } - } - - $this->response->body(_('Due Reminders Sent: ').join('|',$action)); - } - - /** - * Generate our services invoices, based on the service next invoice date - * - * @param int ID Service ID to generate invoice for (optional) - multiple services colon separated - */ - public function action_services() { - // Used to only process X invoices in a row. - $max = ($x=Kohana::$config->load('debug')->invoice) ? $x : ORM::factory('Invoice')->config('GEN_INV_MAX'); - // Our service next billing dates that need to be updated if this is successful. - $snd = array(); - // Our charges that need to be updated if this is successful. - $chgs = array(); - // If we are invoicing a specific service - $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()->as_array(); - Sort::MAsort($svs,'account_id,date_next_invoice'); - - $aid = $due = $io = NULL; - $max_count = 0; - foreach ($svs as $so) { - // 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))) { - // Close this invoice. - 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 AND (++$max_count > $max)) - break; - - // Start a new invoice. - $io = ORM::factory('Invoice'); - $io->due_date = $due = $io->min_due($so->date_next_invoice); - $io->account_id = $aid = $so->account_id; - $io->status = TRUE; - } - - $pdata = Period::details($so->recur_schedule,$so->product->price_recurr_weekday,$so->date_next_invoice,TRUE); - - $iio = $io->add_item(); - $iio->service_id = $so->id; - $iio->product_id = $so->product_id; - $iio->quantity = $pdata['prorata']; - $iio->item_type = 0; // Service Billing - $iio->discount_amt = NULL; // @todo - $iio->price_base = $so->price(); - $iio->recurring_schedule = $so->recur_schedule; - $iio->date_start = $pdata['start_time']; - $iio->date_stop = $pdata['end_time']; - - // 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 - // We'll mark any charges as temporarily processed, although they should be set to status=1 later. - $co->status=2; - $co->save(); - array_push($chgs,$co->id); - } - } - - // Save our invoice. - if ($io AND ! $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. - // @todo Catch any update errors - foreach ($snd as $sid=>$date) { - $so = ORM::factory('Service',$sid); - $so->date_next_invoice = $date; - $so->save(); - } - - // Update any processed charges as such - // @todo Catch any update errors - foreach ($chgs as $cid) { - $co = ORM::factory('Charge',$cid); - $co->status=1; - $co->save(); - } - - $this->response->body(_('Services Invoiced: ').join('|',array_keys($snd))); - } - public function action_send() { // Used to only process X invoices in a row. $max = ORM::factory('Invoice')->config('EMAIL_INV_MAX'); diff --git a/modules/invoice/classes/Controller/User/Invoice.php b/modules/invoice/classes/Controller/User/Invoice.php index 902a85c5..e3702507 100644 --- a/modules/invoice/classes/Controller/User/Invoice.php +++ b/modules/invoice/classes/Controller/User/Invoice.php @@ -67,7 +67,7 @@ class Controller_User_Invoice extends Controller_Invoice { ->set('o',$io); if ($io->due() AND ! $io->cart_exists()) - $output .= View::factory('/invoice/user/view/pay') + $output .= View::factory('invoice/user/view/pay') ->set('mid',$io->mid()) ->set('o',$io); diff --git a/modules/invoice/classes/Invoicable.php b/modules/invoice/classes/Invoicable.php deleted file mode 100644 index 4e8f32bd..00000000 --- a/modules/invoice/classes/Invoicable.php +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/modules/invoice/classes/Invoice.php b/modules/invoice/classes/Invoice.php index 4cfe7448..2452562b 100644 --- a/modules/invoice/classes/Invoice.php +++ b/modules/invoice/classes/Invoice.php @@ -13,8 +13,26 @@ class Invoice { // This invoice Object private $io; + private $_methods = array( + 'min_due', + 'save', + 'saved', + ); + + // Enable passing calls to ORM::Invoice() + public function __call($method,array $args) { + if (in_array($method,$this->_methods)) + return call_user_func_array(array($this->io,$method),$args); + else + throw HTTP_Exception::factory(501,'Unknown/unauthorised method :method',array(':method'=>$method)); + } + public function __construct(Model_Invoice $io=NULL) { $this->io = is_null($io) ? ORM::factory('Invoice') : $io; + + // Set our invoice as valid + if (is_null($io)) + $this->io->status = 1; } public static function instance(Model_Invoice $io=NULL) { @@ -27,6 +45,19 @@ class Invoice { * @param $so Model_Servie */ public function add_service(Model_Service $so) { + if ($this->io->loaded()) + throw HTTP_Exception::factory(501,'Cannot use add service :service to an existing invoice :invoice ',array(':service'=>$so->id,':invoice'=>$this->io->id)); + + if (! $this->io->account_id) + $this->io->account_id = $so->account_id; + + else if ($this->io->account_id != $so->account_id) + throw HTTP_Exception::factory(501,'Cannot add service :service to invoice - it is for a different account',array(':service'=>$so->id)); + + // Set the invoice due date + if (! $this->io->due_date OR $this->io->due_date > $this->io->min_due($so->date_next_invoice)) + $this->io->due_date = $this->io->min_due($so->date_next_invoice); + $pdata = Period::details($so->recur_schedule,$so->product->price_recurr_day,$so->invoiced_to()+86400,FALSE,$so->product->price_recurr_strict); $iio = ORM::factory('Invoice_Item'); @@ -39,13 +70,14 @@ class Invoice { $iio->date_stop = $pdata['end_time']; // Service Billing - $iio->item_type = StaticList_ItemType::index('MAIN'); + $iio->item_type = 0; $this->io->add_item($iio); // Check if there are any charges $c = ORM::factory('Charge') ->where('service_id','=',$so->id) + ->where('void','is',NULL) ->where('processed','is',NULL); foreach ($c->find_all() as $co) { @@ -57,9 +89,7 @@ class Invoice { $iio->price_base = $co->amount; $iio->date_start = $co->date_orig; $iio->date_stop = $co->date_orig; - - // @todo This should be $co->item_type; - $iio->item_type = StaticList_ItemType::index('EXCESS'); + $iio->item_type = $co->type; $this->io->add_item($iio); } @@ -67,12 +97,13 @@ class Invoice { return $this; } - public function render($type,$section) { + public function render($type,$section,$args=array()) { switch ($type) { case 'html': switch ($section) { case 'body': return View::factory('invoice/user/view/body') + ->set('show_id',(isset($args['noid']) AND $args['noid']) ? FALSE : TRUE) ->set('o',$this->io); break; diff --git a/modules/invoice/classes/Model/Invoice.php b/modules/invoice/classes/Model/Invoice.php index 903f4de4..abbc0f20 100644 --- a/modules/invoice/classes/Model/Invoice.php +++ b/modules/invoice/classes/Model/Invoice.php @@ -38,8 +38,9 @@ class Model_Invoice extends ORM_OSB implements Cartable { ); // Items belonging to an invoice - private $invoice_items = array(); - private $invoice_items_unsorted = TRUE; + protected $_sub_items_load = array( + 'invoice_item'=>'service_id,item_type', + ); /** INTERFACE REQUIREMENTS **/ public function cart_item() { @@ -53,32 +54,21 @@ class Model_Invoice extends ORM_OSB implements Cartable { return count(Cart::instance()->get($this->mid(),$this->id)); } - /** - * Intercept our object load, so that we can load our subitems - */ - protected function _load_values(array $values) { - parent::_load_values($values); - - if ($this->_loaded) - $this->invoice_items = $this->invoice_item->find_all()->as_array(); - - return $this; - } - /** * Add an item to an invoice */ public function add_item(Model_Invoice_Item $iio=NULL) { // @todo This is the old calling of this function, which should be removed if (is_null($iio)) { - $c = count($this->invoice_items); + $c = count($this->_sub_items); - $this->invoice_items[$c] = ORM::factory('Invoice_Item'); + $this->_sub_items[$c] = ORM::factory('Invoice_Item'); - return $this->invoice_items[$c]; + return $this->_sub_items[$c]; } - array_push($this->invoice_items,$iio); + array_push($this->_sub_items,$iio); + $this->_sub_items_sorted = FALSE; } /** @@ -86,7 +76,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { * * @param $period Return an Array of items for that period */ - public function items_periods($period=NULL) { + public function items_periods($period=FALSE) { $result = array(); foreach ($this->items() as $ito) { @@ -94,15 +84,60 @@ class Model_Invoice extends ORM_OSB implements Cartable { if ($ito->item_type != 0) continue; - if (is_null($period) AND ! in_array($ito->recurring_schedule,$result)) + if ($period === FALSE AND ! in_array($ito->recurring_schedule,$result)) array_push($result,$ito->recurring_schedule); - elseif ($ito->recurring_schedule == $period) - array_push($result,$ito); + + elseif ($ito->recurring_schedule == $period) { + // If we have this service already, we'll skip + if (! Object::in_array('service_id',$ito->service_id,$result)) + array_push($result,$ito); + } } return $result; } + /** + * Return the item titles that are on an invoice + * + * @param $title Return an Array of items for that title + */ + public function items_titles($title=NULL,$period=NULL) { + $result = array(); + + foreach ($this->items() as $ito) { + if ($ito->service_id) { + if (is_null($title) AND ! in_array($ito->title(),$result) AND (is_null($period) OR ($period == $ito->recurring_schedule))) + array_push($result,$ito->title()); + elseif (($ito->title() == $title AND (is_null($period) OR ($period == $ito->recurring_schedule))) OR is_null($ito->recurring_schedule)) + array_push($result,$ito); + + } else { + throw HTTP_Exception::factory(501,'Not handled non-services'); + } + } + + return $result; + } + + /** + * Return the service items on an invoice relating to an invoice_item + * IE: item_type == 0 + */ + public function service_items(Model_Invoice_Item $o) { + $result = array(); + + // At the moment, we only return items pertaining to a service + if (! $o->service_id) + return $result; + + foreach ($this->items() as $iio) + if ($iio->item_type == 0 AND $iio->service_id == $o->service_id) + array_push($result,$iio); + + return $result; + } + /** * Return the extra items on an invoice relating to an invoice_item * IE: item_type != 0 @@ -195,7 +230,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function items($type='ALL') { $result = array(); - foreach ($this->invoice_items as $ito) { + foreach ($this->_sub_items as $ito) { $return = FALSE; switch ($type) { @@ -429,9 +464,11 @@ class Model_Invoice extends ORM_OSB implements Cartable { // Our items will be clobbered once we save the object, so we need to save it here. $items = $this->items(); + // If this is a new invoice, we'll need to do more work + $new = ! $this->loaded(); + // Save the invoice - if ($this->changed()) - parent::save($validation); + parent::save($validation); // Need to save the associated items and their taxes if ($this->loaded()) { @@ -453,6 +490,27 @@ class Model_Invoice extends ORM_OSB implements Cartable { throw new Kohana_Exception('Problem saving invoice_item for invoice :invoice - Failed save()',array(':invoice'=>$this->id)); } + // If this is a new invoice, we'll need to update some other items + if ($new) { + // Update next invoice date + if ($iio->item_type == 0) { + $iio->service->date_next_invoice = $iio->date_stop+86400; + + // @todo Mark invoice as cancelled and write a memo, then... + if (! $iio->service->save()) + throw new Kohana_Exception('Problem saving service :service for invoice_item :invoice_item - Failed save()',array(':invoice_item'=>$iio->id,':service'=>$iio->service_id)); + } + + // Update charge iteme + if ($iio->charge_id) { + $iio->charge->processed = 1; + + // @todo Mark invoice as cancelled and write a memo, then... + if (! $iio->charge->save()) + throw new Kohana_Exception('Problem saving charge :charge for invoice_item :invoice_item - Failed save()',array(':invoice_item'=>$iio->id,':charge'=>$iio->charge_id)); + } + } + // @todo Need to save discount information } diff --git a/modules/invoice/classes/Model/Invoice/Item.php b/modules/invoice/classes/Model/Invoice/Item.php index 04d133d9..c485b8f0 100644 --- a/modules/invoice/classes/Model/Invoice/Item.php +++ b/modules/invoice/classes/Model/Invoice/Item.php @@ -12,7 +12,7 @@ * Column Definitions: * + 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 Model_Invoice_Item extends ORM_OSB implements Invoicable { +class Model_Invoice_Item extends ORM_OSB { // Relationships protected $_belongs_to = array( 'product'=>array(), @@ -38,11 +38,21 @@ class Model_Invoice_Item extends ORM_OSB implements Invoicable { ), ); - // Items belonging to an invoice - private $subitems = array(); - private $subitems_loaded = FALSE; + // Items belonging to an invoice item + protected $_sub_items_load = array( + 'invoice_item_tax'=>FALSE, + ); + + /** + * The title for invoice items + */ + public function title() { + if ($this->product_id AND $this->service_id) + return $this->product_id ? sprintf('%s: %s',$this->product->title(),$this->service->name()) : $this->service->name; + else + return 'Unknown Item'; + } - /** INTERFACE REQUIREMENTS **/ public function invoice_line() { if ($this->charge_id) return $this->charge->description.' '.$this->period(); @@ -52,23 +62,6 @@ class Model_Invoice_Item extends ORM_OSB implements Invoicable { throw HTTP_Exception::factory(501,'Unable to render invoice item :id',array(':id'=>$this->id)); } - public function __construct($id = NULL) { - // Load our model. - parent::__construct($id); - - return $this->load_sub_items(); - } - - private function load_sub_items() { - // Load our sub items - if (! $this->subitems_loaded AND $this->loaded()) { - $this->subitems['tax'] = $this->invoice_item_tax->find_all()->as_array(); - $this->subitems_loaded = TRUE; - } - - return $this; - } - // Display a transaction number public function trannum() { return sprintf('%03s-%06s',$this->item_type,$this->id); @@ -125,7 +118,7 @@ class Model_Invoice_Item extends ORM_OSB implements Invoicable { } public function total($format=FALSE) { - $result = Currency::round($this->subtotal()+$this->tax()-$this->discount()); + $result = $this->void ? 0 : Currency::round($this->subtotal()+$this->tax()-$this->discount()); return $format ? Currency::display($result) : $result; } @@ -205,9 +198,9 @@ class Model_Invoice_Item extends ORM_OSB implements Invoicable { if ($this->saved()) { $iito = ORM::factory('Invoice_Item_Tax'); - if ($this->subitems_loaded) { + if ($this->_sub_items) { foreach (array('tax') as $i) - foreach ($this->subitems[$i] as $io) + foreach ($this->_sub_items[$i] as $io) if ($io->changed()) $io->save(); diff --git a/modules/invoice/classes/Task/Invoice/Service.php b/modules/invoice/classes/Task/Invoice/Service.php new file mode 100644 index 00000000..ebb7a2d2 --- /dev/null +++ b/modules/invoice/classes/Task/Invoice/Service.php @@ -0,0 +1,64 @@ +1, // Days in advance to generate for + 'id'=>NULL, // Service invoice to generate for + ); + + protected function _execute(array $params) { + // Used to only process X invoices in a row. + $max = ($x=Kohana::$config->load('debug')->invoice) ? $x : ORM::factory('Invoice')->config('GEN_INV_MAX'); + // Sort our service by account_id, then we can generate 1 invoice. + $svs = ORM::factory('Service')->list_invoicesoon($params['days'])->as_array(); + Sort::MAsort($svs,'account_id'); + + $sids = $params['id'] ? explode(':',$params['id']) : array(); + + $aid = $io = NULL; + $max_count = 0; + $action = array(); + + foreach ($svs as $so) { + // If we are generating an invoice for a service, skip to that service. + if ($sids AND ! in_array($so->id,$sids)) + continue; + + // Close off invoice, and start a new one, if this is for a different account. + if (is_null($io) OR (! is_null($aid) AND $aid != $so->account_id)) { + // Close and save this invoice. + if (is_object($io) AND ! $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 AND (++$max_count > $max)) + break; + + // Start a new invoice. + $io = Invoice::instance(); + $aid = $so->account_id; + } + + $io->add_service($so); + + // Record what we have updated. + array_push($action,$so->id); + } + + // Save our invoice. + if ($io AND ! $io->saved() AND ! $io->save()) + throw new Kohana_Exception('Failed to save invoice :invoice for service :service',array(':invoice'=>$io->id,':service'=>$so->id)); + + return _('Services Invoiced: ').join('|',$action); + } +} +?> diff --git a/modules/invoice/config/invoice.php b/modules/invoice/config/invoice.php index 4bb6afbd..080e1a6c 100644 --- a/modules/invoice/config/invoice.php +++ b/modules/invoice/config/invoice.php @@ -4,7 +4,6 @@ * OSB Configuration - Invoice Driver * * @package OSB - * @subpackage Invoice * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/invoice/views/invoice/user/view.php b/modules/invoice/views/invoice/user/view.php index 0cca3f09..62bb2ebf 100644 --- a/modules/invoice/views/invoice/user/view.php +++ b/modules/invoice/views/invoice/user/view.php @@ -29,7 +29,7 @@
display('due_date'); ?>
Current Charges
total_charges(TRUE); ?>
-
Payments Recieved
+
Payments Received
payments_total(TRUE); ?>
Credits Applied
total_credits(TRUE); ?>
@@ -45,55 +45,7 @@

Charge Details

- - - items_periods() as $rs) : ?> - - - items_periods($rs) as $iio) : ?> - service_id) : ?> - - - - - - - - - - - - - - - - service_items_extra($iio)) : ?> - - - - - - - - - - - - - - - - - - - - - - - - - -
 service_id),$iio->service->id()).' '.$iio->product->title(); ?> 
service_items_total($iio,TRUE); ?>
  invoice_line(),$iio->display('id')); ?>
subtotal(TRUE); ?>
 
  invoice_line(),$eiio->display('id')); ?>
subtotal(TRUE); ?>
 
  
service_items_tax($iio,TRUE); ?>
 
+ render('html','body'); ?>
diff --git a/modules/invoice/views/invoice/user/view/body.php b/modules/invoice/views/invoice/user/view/body.php index 09fed5e1..0f37c8e1 100644 --- a/modules/invoice/views/invoice/user/view/body.php +++ b/modules/invoice/views/invoice/user/view/body.php @@ -7,22 +7,28 @@ service_id) : ?>   - service->service_name(); ?> + service_id),$iio->service->id()).' ' : '').$iio->service->service_name(); ?>  
service_items_total($iio,TRUE); ?>
- -   - invoice_line(); ?> -
total(TRUE); ?>
-   - + + service_items($iio)) : ?> + + +   +   + invoice_line().($show_id ? " ($eiio->id)" : ''); ?> +
total(TRUE); ?>
+   + + + service_items_extra($iio)) : ?>   - invoice_line(); ?> + invoice_line().($show_id ? " ($eiio->id)" : ''); ?>
total(TRUE); ?>
  diff --git a/modules/lnApp b/modules/lnApp index d3627ace..9e191ab0 160000 --- a/modules/lnApp +++ b/modules/lnApp @@ -1 +1 @@ -Subproject commit d3627ace8fc427abe493044f387a802dfc7f09d1 +Subproject commit 9e191ab0be69f296ae3cd2f079aafa80210f6928 diff --git a/modules/oauth/config/facebook.php b/modules/oauth/config/facebook.php index c640e2e4..20ac9e14 100644 --- a/modules/oauth/config/facebook.php +++ b/modules/oauth/config/facebook.php @@ -4,7 +4,6 @@ * OAuth Configuration - System Default Configurable Items. * * @package OAuth - * @subpackage Facebook * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/payment/classes/Controller/Admin/Payment.php b/modules/payment/classes/Controller/Admin/Payment.php index f5e59b31..a49378c2 100644 --- a/modules/payment/classes/Controller/Admin/Payment.php +++ b/modules/payment/classes/Controller/Admin/Payment.php @@ -131,7 +131,7 @@ $(document).ready(function() { $("input[name=account_id_label]").typeahead({ minLength: 2, source: function (query,process) { - search("a/payment/ajaxlist",query,process); + search("'.URL::link('admin','payment/ajaxlist').'",query,process); }, matcher: function () { return true; }, diff --git a/modules/payment/classes/Model/Payment.php b/modules/payment/classes/Model/Payment.php index 09156c35..f809af4b 100644 --- a/modules/payment/classes/Model/Payment.php +++ b/modules/payment/classes/Model/Payment.php @@ -39,34 +39,23 @@ class Model_Payment extends ORM_OSB { ), ); - // Items belonging to an invoice - private $payment_items = array(); - private $payment_items_unsorted = TRUE; - - /** - * Intercept our object load, so that we can load our subitems - */ - protected function _load_values(array $values) { - parent::_load_values($values); - - if ($this->_loaded) - $this->payment_items = $this->payment_item->find_all()->as_array(); - - return $this; - } + // Items belonging to a payment + protected $_sub_items_load = array( + 'payment_item'=>FALSE, + ); /** * Add an item to an payment */ public function add_item(Model_Payment_Item $p=NULL) { - $this->payment_items_unsorted = TRUE; + $this->_sub_items_sorted = FALSE; // Make sure this payment item for an invoice is not already in the list - foreach ($this->payment_items as $pio) + foreach ($this->_sub_items as $pio) if ($pio->invoice_id == $p->invoice_id) return $pio; - array_push($this->payment_items,$p); + array_push($this->_sub_items,$p); return $p; } @@ -101,7 +90,7 @@ class Model_Payment extends ORM_OSB { public function invoices() { $result = array(); - foreach ($this->payment_items as $pio) + foreach ($this->_sub_items as $pio) array_push($result,$pio->invoice); return $result; @@ -117,14 +106,14 @@ class Model_Payment extends ORM_OSB { * @see payment_items */ public function items($type='ALL') { - if ($this->payment_items_unsorted) { - Sort::MAsort($this->payment_items,'invoice_id'); - $this->payment_items_unsorted = FALSE; + if (! $this->_sub_items_sorted) { + Sort::MAsort($this->_sub_items,'invoice_id'); + $this->_sub_items_sorted = TRUE; } $result = array(); - foreach ($this->payment_items as $pio) { + foreach ($this->_sub_items as $pio) { $return = FALSE; switch ($type) { diff --git a/modules/service/classes/Controller/Admin/Service.php b/modules/service/classes/Controller/Admin/Service.php index e005e081..0e022010 100644 --- a/modules/service/classes/Controller/Admin/Service.php +++ b/modules/service/classes/Controller/Admin/Service.php @@ -11,7 +11,6 @@ */ class Controller_Admin_Service extends Controller_Service { protected $secure_actions = array( - 'ajaxlist'=>TRUE, 'ajaxjson_traffic'=>TRUE, 'adslstat'=>TRUE, 'listadslbilling'=>TRUE, @@ -25,22 +24,6 @@ class Controller_Admin_Service extends Controller_Service { 'view'=>TRUE, ); - public function action_ajaxlist() { - $result = array(); - - $result += ORM::factory('Service')->list_autocomplete( - isset($_REQUEST['term']) ? $_REQUEST['term'] : '', - 'id', - 'id', - array('SVC %s: %s'=>array('id','service_name(TRUE)')), - 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'); @@ -54,7 +37,6 @@ class Controller_Admin_Service extends Controller_Service { $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()); } @@ -538,7 +520,7 @@ class Controller_Admin_Service extends Controller_Service { if (is_null($bt)) $bt = $iio->date_start; - $pdata = Period::details($iio->recurring_schedule,$a ? NULL : $iio->product->price_recurr_weekday,$bt,TRUE); + $pdata = Period::details($iio->recurring_schedule,$a ? NULL : $iio->product->price_recurr_weekday,$bt,TRUE,TRUE); switch ($iio->recurring_schedule) { case 1: diff --git a/modules/service/classes/Model/Service.php b/modules/service/classes/Model/Service.php index c2ae6822..9761ac91 100644 --- a/modules/service/classes/Model/Service.php +++ b/modules/service/classes/Model/Service.php @@ -46,6 +46,8 @@ class Model_Service extends ORM_OSB { ), ); + protected $_form = array('id'=>'id','value'=>'service_name()'); + /** * Get the additional charges associated with this service */ @@ -63,11 +65,11 @@ class Model_Service extends ORM_OSB { if ($unprocessed) $x->where_open() - ->where('processed','IS',NULL) - ->or_where('processed','=',FALSE) - ->where_close(); + ->where('processed','IS',NULL) + ->or_where('processed','=',FALSE) + ->where_close(); - return $x->find_all(); + return $x->and_where('void','IS',NULL)->find_all(); } /** @@ -132,9 +134,24 @@ class Model_Service extends ORM_OSB { * Show the date we are invoiced to */ public function invoiced_to($format=FALSE) { - $x = $this->invoice_item->order_by('date_stop','DESC')->limit(1)->find(); + $iio = $this->last_invoice_item() + ->limit(1); - return $format ? $x->display('date_stop') : $x->date_stop; + $iio->find(); + + $x = (! $iio->loaded() AND $this->date_next_invoice) ? $this->date_next_invoice-86400 : $iio->date_stop; + + return $format ? Config::date($x) : $x; + } + + private function last_invoice_item() { + return ORM::factory('Invoice_Item')->join('invoice') + ->on('invoice.id','=','invoice_item.invoice_id') + ->on('invoice.status','=',1) + ->on('invoice_item.service_id','=',$this) + ->on('invoice_item.item_type','=',0) + ->on('invoice_item.void','is','null') + ->order_by('date_stop','DESC'); } /** @@ -150,7 +167,7 @@ class Model_Service extends ORM_OSB { public function paid_to($format=FALSE) { $x = NULL; - foreach ($this->invoice_item->order_by('date_stop','DESC')->order_by('date_orig','DESC')->find_all() as $iio) + foreach ($this->last_invoice_item()->order_by('date_orig','DESC')->find_all() as $iio) if ($iio->invoice->due() == 0) { $x = $iio; break; @@ -203,7 +220,7 @@ class Model_Service extends ORM_OSB { $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']; + $p = ! is_null($this->price) ? $this->price : $x[$this->price_group]['price_base']; if ($tax) $p = Tax::add($p); @@ -250,7 +267,7 @@ class Model_Service extends ORM_OSB { */ public function list_autocomplete($term,$index,$value,array $label,array $limit=array(),array $options=NULL) { // We only show service numbers. - if (! is_numeric($term)) + if (! is_numeric($term) AND (! $limit)) return array(); $ao = Auth::instance()->get_user(); diff --git a/modules/service/classes/Model/Service/Plugin.php b/modules/service/classes/Model/Service/Plugin.php index 985031ee..9fcb4390 100644 --- a/modules/service/classes/Model/Service/Plugin.php +++ b/modules/service/classes/Model/Service/Plugin.php @@ -47,7 +47,7 @@ abstract class Model_Service_Plugin extends ORM_OSB { abstract public function password_value(); public function manage_button() { - if (! $this->service->status) + if (! $this->service->status OR $this->service->expiring()) return FALSE; static $k = ''; diff --git a/modules/service/views/service/user/view.php b/modules/service/views/service/user/view.php index 6e8a6f66..707b0ccc 100644 --- a/modules/service/views/service/user/view.php +++ b/modules/service/views/service/user/view.php @@ -90,6 +90,6 @@
Next Invoice Charges - add_service($o)->render('html','body'); ?> + add_service($o)->render('html','body',array('noid'=>TRUE)); ?>
diff --git a/modules/ssl/classes/Task/Ssl/Renew.php b/modules/ssl/classes/Task/Ssl/Renew.php index cc90c4f1..61a727d5 100644 --- a/modules/ssl/classes/Task/Ssl/Renew.php +++ b/modules/ssl/classes/Task/Ssl/Renew.php @@ -10,6 +10,10 @@ * @license http://dev.osbill.net/license.html */ class Task_SSL_Renew extends Minion_Task { + protected $_options = array( + 'id'=>NULL, + ); + /** * Renew a certificate */ diff --git a/modules/ssl/config/ssl.php b/modules/ssl/config/ssl.php index 4083b4a1..729a4970 100644 --- a/modules/ssl/config/ssl.php +++ b/modules/ssl/config/ssl.php @@ -4,7 +4,6 @@ * OSB Configuration - SSL Config Items * * @package OSB - * @subpackage System * @category Configuration * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/ssl/messages/models/ssl_ca.php b/modules/ssl/messages/models/ssl_ca.php index 5a325003..42c62ea0 100644 --- a/modules/ssl/messages/models/ssl_ca.php +++ b/modules/ssl/messages/models/ssl_ca.php @@ -4,7 +4,6 @@ * SSL Certificate validation messages. * * @package OSB - * @subpackage SSL * @category Validation * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/static_page/classes/controller/staticpage.php b/modules/static_page/classes/controller/staticpage.php index 017a6fe5..4b8374ca 100644 --- a/modules/static_page/classes/controller/staticpage.php +++ b/modules/static_page/classes/controller/staticpage.php @@ -4,7 +4,6 @@ * This class provides static pages * * @package OSB - * @subpackage Page * @category Controllers * @author Deon George * @copyright (c) 2010 Deon George diff --git a/modules/static_page/classes/controller/staticpage/category.php b/modules/static_page/classes/controller/staticpage/category.php index bca01f95..fee3d482 100644 --- a/modules/static_page/classes/controller/staticpage/category.php +++ b/modules/static_page/classes/controller/staticpage/category.php @@ -4,7 +4,6 @@ * This class provides static menu categories * * @package OSB - * @subpackage Page * @category Controllers * @author Deon George * @copyright (c) 2010 Deon George diff --git a/modules/static_page/classes/model/staticpage.php b/modules/static_page/classes/model/staticpage.php index bcd8ed16..5008fab0 100644 --- a/modules/static_page/classes/model/staticpage.php +++ b/modules/static_page/classes/model/staticpage.php @@ -4,7 +4,6 @@ * This class provides static pages. * * @package OSB - * @subpackage Static Page * @category Models * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/static_page/classes/model/staticpage/category.php b/modules/static_page/classes/model/staticpage/category.php index 74ef8aec..41d90706 100644 --- a/modules/static_page/classes/model/staticpage/category.php +++ b/modules/static_page/classes/model/staticpage/category.php @@ -4,7 +4,6 @@ * This class provides static page categories * * @package OSB - * @subpackage Static Page * @category Models * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/static_page/classes/model/staticpage/translate.php b/modules/static_page/classes/model/staticpage/translate.php index ecb7a5a3..0f149ad0 100644 --- a/modules/static_page/classes/model/staticpage/translate.php +++ b/modules/static_page/classes/model/staticpage/translate.php @@ -4,7 +4,6 @@ * This class provides Static Page Translation * * @package OSB - * @subpackage Static Page * @category Models * @author Deon George * @copyright (c) 2010 Open Source Billing diff --git a/modules/task/classes/Task/Task/Run.php b/modules/task/classes/Task/Task/Run.php index fc798dc8..767af935 100644 --- a/modules/task/classes/Task/Task/Run.php +++ b/modules/task/classes/Task/Task/Run.php @@ -10,6 +10,11 @@ * @license http://dev.osbill.net/license.html */ class Task_Task_Run extends Minion_Task { + protected $_options = array( + 'id'=>NULL, + 'force'=>FALSE, + ); + protected function _execute(array $params) { if ($params['id']) { $to = ORM::factory('Task',$params['id']);