From e19518c505a25c53b392aaee3dc36f3fa22774f0 Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 20 Dec 2013 10:00:32 +1100 Subject: [PATCH] Improvements to invoice display and other misc items --- application/classes/Config.php | 12 +- application/classes/Menu.php | 4 +- application/classes/Model/Account.php | 4 +- application/classes/Model/Module.php | 5 +- application/classes/ORM/OSB.php | 4 - application/classes/StaticList.php | 6 +- application/classes/StaticList/ItemType.php | 20 +- application/classes/StaticList/PriceType.php | 2 +- .../classes/StaticList/RecurSchedule.php | 2 +- application/classes/StaticList/RecurType.php | 2 +- application/classes/StaticList/SweepType.php | 2 +- application/classes/StaticList/Title.php | 2 +- application/classes/StaticList/YesNo.php | 2 +- application/classes/URL.php | 2 +- modules/adsl/views/adsl/reseller/billing.php | 27 +- modules/charge/classes/Model/Charge.php | 6 +- .../classes/Checkout/Plugin/Paypal.php | 1 + .../classes/Model/Checkout/Notify.php | 12 +- .../classes/Task/Checkout/Notify/List.php | 37 + .../classes/Controller/Admin/Export.php | 2 +- .../classes/Controller/Reseller/Export.php | 3 +- .../export/classes/Model/Export/Module.php | 2 +- .../export/views/export/module/admin/edit.php | 2 +- .../classes/Controller/Task/Invoice.php | 61 -- .../classes/Controller/User/Invoice.php | 63 +- modules/invoice/classes/Invoicable.php | 16 + modules/invoice/classes/Invoice.php | 168 ++-- .../invoice/classes/Invoice/Tcpdf/Default.php | 86 ++- modules/invoice/classes/Model/Invoice.php | 716 +++++++----------- .../invoice/classes/Model/Invoice/Item.php | 149 ++-- .../invoice/classes/Task/Invoice/Email.php | 63 ++ .../classes/Task/Invoice/Reminddue.php | 3 + .../classes/Task/Invoice/Remindoverdue1.php | 3 + modules/invoice/views/invoice/user/email.php | 145 ---- modules/invoice/views/invoice/user/view.php | 3 - .../invoice/views/invoice/user/view/body.php | 97 ++- .../invoice/views/invoice/user/view/pay.php | 2 +- .../invoice/views/invoice/user/viewemail.php | 219 ++++++ modules/lnApp | 2 +- modules/product/classes/Model/Product.php | 20 +- modules/service/classes/Model/Service.php | 18 +- modules/ssl/classes/SSL.php | 6 +- modules/tax/classes/Tax.php | 8 +- 43 files changed, 1122 insertions(+), 887 deletions(-) create mode 100644 modules/checkout/classes/Task/Checkout/Notify/List.php create mode 100644 modules/invoice/classes/Invoicable.php create mode 100644 modules/invoice/classes/Task/Invoice/Email.php delete mode 100644 modules/invoice/views/invoice/user/email.php create mode 100644 modules/invoice/views/invoice/user/viewemail.php diff --git a/application/classes/Config.php b/application/classes/Config.php index b18cfe22..387d864b 100644 --- a/application/classes/Config.php +++ b/application/classes/Config.php @@ -73,10 +73,10 @@ class Config extends Kohana_Config { /** * Show a date using a site configured format - * @note We need this function here, since we call static:: methods, which need to resolve to the child class. + * @note We need this function here, since we call self:: methods, which need to resolve to the child class. */ public static function datetime($date) { - return sprintf('%s %s',static::date($date),static::time($date)); + return sprintf('%s %s',self::date($date),self::time($date)); } public static function language() { @@ -96,13 +96,13 @@ class Config extends Kohana_Config { } public static function logo() { - return HTML::image(static::logo_uri(),array('class'=>'headlogo','alt'=>_('Logo'))); + return HTML::image(self::logo_uri(),array('class'=>'headlogo','alt'=>_('Logo'))); } public static function logo_uri($protocol=NULL) { - list ($path,$suffix) = explode('.',static::$logo); + list ($path,$suffix) = explode('.',self::$logo); - return URL::site(Route::get('default/media')->uri(array('file'=>$path.'.'.$suffix),array('alt'=>static::sitename())),$protocol); + return URL::site(Route::get('default/media')->uri(array('file'=>$path.'.'.$suffix),array('alt'=>self::sitename())),$protocol); } /** @@ -138,7 +138,7 @@ class Config extends Kohana_Config { } public static function module_exist($module) { - return array_key_exists(strtolower($module),static::modules()) ? TRUE : FALSE; + return array_key_exists(strtolower($module),self::modules()) ? TRUE : FALSE; } /** diff --git a/application/classes/Menu.php b/application/classes/Menu.php index cfea2d05..41779183 100644 --- a/application/classes/Menu.php +++ b/application/classes/Menu.php @@ -43,7 +43,7 @@ class Menu { if (empty($result[$mmo->id])) $result[$mmo->id] = $mmo; - return static::collapse($result); + return self::collapse($result); } public static function ul(array $result,array $append=NULL,$sub=FALSE) { @@ -51,7 +51,7 @@ class Menu { foreach ($result as $k => $v) if (is_array($v)) { - $output .= sprintf(''); + $output .= sprintf(''); } else $output .= '
  • '.HTML::anchor($v->url(),$v->menu_display(),array('tabindex'=>-1,'nocg'=>TRUE)).'
  • '; diff --git a/application/classes/Model/Account.php b/application/classes/Model/Account.php index 97e8c092..b102640b 100644 --- a/application/classes/Model/Account.php +++ b/application/classes/Model/Account.php @@ -173,10 +173,12 @@ class Model_Account extends Model_Auth_UserDefault { list($fn,$ln) = explode(' ',$term,2); $this->where_open() + ->where_open() ->where('first_name','like','%'.$fn.'%') ->and_where('last_name','like','%'.$ln.'%') ->where_close() - ->or_where('company','like','%'.$term.'%'); + ->or_where('company','like','%'.$term.'%') + ->where_close(); } elseif (is_numeric($term)) { $this->where('id','like','%'.$term.'%'); diff --git a/application/classes/Model/Module.php b/application/classes/Model/Module.php index dcae16b4..68e56310 100644 --- a/application/classes/Model/Module.php +++ b/application/classes/Model/Module.php @@ -42,7 +42,10 @@ class Model_Module extends ORM_OSB { * @param $id PK of Model */ public function instance($id=NULL) { - return ORM::factory(ucwords($this->name),$id); + if (! $this->loaded()) + throw new Kohana_Exception('Cant call an instance of a model when it is not loaded'); + + return ORM::factory(Kohana::classname($this->name),$id); } public function list_external() { diff --git a/application/classes/ORM/OSB.php b/application/classes/ORM/OSB.php index cea60df9..1043951a 100644 --- a/application/classes/ORM/OSB.php +++ b/application/classes/ORM/OSB.php @@ -205,10 +205,6 @@ abstract class ORM_OSB extends ORM { return array_key_exists($key,$this->$column) ? $this->{$column}[$key] : NULL; } - final public function module() { - return ORM::factory(Kohana::classname($this->name)); - } - final public function mid() { return ORM::factory('Module',array('name'=>$this->_table_name)); } diff --git a/application/classes/StaticList.php b/application/classes/StaticList.php index 1d9335e4..b51b009f 100644 --- a/application/classes/StaticList.php +++ b/application/classes/StaticList.php @@ -44,7 +44,7 @@ abstract class StaticList { * @param string Default value to populate in the Form input. */ public static function form($name,$default='',$addblank=FALSE,array $attributes=NULL) { - $table = static::factory()->_table(); + $table = self::factory()->_table(); if ($addblank) $table = array_merge(array(''=>' '),$table); @@ -56,11 +56,11 @@ abstract class StaticList { * Lists our available keys */ public static function keys() { - return array_keys(static::factory()->_table()); + return array_keys(self::factory()->_table()); } public static function table() { - return static::factory()->_table(); + return self::factory()->_table(); } } ?> diff --git a/application/classes/StaticList/ItemType.php b/application/classes/StaticList/ItemType.php index a7dc928b..bd97c17c 100644 --- a/application/classes/StaticList/ItemType.php +++ b/application/classes/StaticList/ItemType.php @@ -13,24 +13,26 @@ class StaticList_ItemType extends StaticList { protected function _table() { return array( 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'), + 1=>_('Hardware'), + 2=>_('Service Relocation Fee'), + 3=>_('Service Change Fee'), + 4=>_('Service Connection Fee'), 5=>_('Excess Usage'), // Excess Service Item, of item 0 - 6=> _('Service Cancellation Fee'), + 6=>_('Service Cancellation Fee'), + 7=>_('Extra Product/Service Charge'), // Service Billing in advance + 124=>_('Late Payment Fee'), 125=>_('Payment Fee'), // Payment processing fee - 126=> _('Rounding'), - 127=> _('Late Payment Fee'), + 126=>_('Other'), + 127=>_('Rounding'), ); } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } public static function index($key) { - return array_search($key,static::factory()->table()); + return array_search($key,self::factory()->table()); } } ?> diff --git a/application/classes/StaticList/PriceType.php b/application/classes/StaticList/PriceType.php index fd7f3af8..a8269865 100644 --- a/application/classes/StaticList/PriceType.php +++ b/application/classes/StaticList/PriceType.php @@ -19,7 +19,7 @@ class StaticList_PriceType extends StaticList { } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } } ?> diff --git a/application/classes/StaticList/RecurSchedule.php b/application/classes/StaticList/RecurSchedule.php index 4cd38d0c..ea7e032d 100644 --- a/application/classes/StaticList/RecurSchedule.php +++ b/application/classes/StaticList/RecurSchedule.php @@ -23,7 +23,7 @@ class StaticList_RecurSchedule extends StaticList { } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } } ?> diff --git a/application/classes/StaticList/RecurType.php b/application/classes/StaticList/RecurType.php index 0b670ed1..450f57d7 100644 --- a/application/classes/StaticList/RecurType.php +++ b/application/classes/StaticList/RecurType.php @@ -18,7 +18,7 @@ class StaticList_RecurType extends StaticList { } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } } ?> diff --git a/application/classes/StaticList/SweepType.php b/application/classes/StaticList/SweepType.php index 1a27965c..1f3855c3 100644 --- a/application/classes/StaticList/SweepType.php +++ b/application/classes/StaticList/SweepType.php @@ -23,7 +23,7 @@ class StaticList_SweepType extends StaticList { } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } } ?> diff --git a/application/classes/StaticList/Title.php b/application/classes/StaticList/Title.php index 0afa8045..791d229d 100644 --- a/application/classes/StaticList/Title.php +++ b/application/classes/StaticList/Title.php @@ -22,7 +22,7 @@ class StaticList_Title extends StaticList { } public static function get($value) { - return static::factory()->_get($value); + return self::factory()->_get($value); } } ?> diff --git a/application/classes/StaticList/YesNo.php b/application/classes/StaticList/YesNo.php index 04f36dc3..e684f0e3 100644 --- a/application/classes/StaticList/YesNo.php +++ b/application/classes/StaticList/YesNo.php @@ -23,7 +23,7 @@ class StaticList_YesNo extends StaticList { return $format ? View::factory(Config::theme().'/label/bool') ->set('label',$value ? 'label-success' : '') - ->set('column',static::factory()->_get($value)) : $value; + ->set('column',self::factory()->_get($value)) : $value; } } ?> diff --git a/application/classes/URL.php b/application/classes/URL.php index aed8363e..49fcfcac 100644 --- a/application/classes/URL.php +++ b/application/classes/URL.php @@ -57,7 +57,7 @@ class URL extends Kohana_URL { public static function navbar() { $result = array(); - foreach (array_reverse(static::$method_directory) as $k=>$v) + foreach (array_reverse(self::$method_directory) as $k=>$v) switch ($k) { case 'admin': $result[$k] = array('name'=>'Administrator','icon'=>'icon-globe'); break; diff --git a/modules/adsl/views/adsl/reseller/billing.php b/modules/adsl/views/adsl/reseller/billing.php index b35894b5..8903807a 100644 --- a/modules/adsl/views/adsl/reseller/billing.php +++ b/modules/adsl/views/adsl/reseller/billing.php @@ -8,13 +8,23 @@ Supplier Cost Invoiced Extras + Total + Charging + Profit services(TRUE) as $so) : ?> plugin(); $po = $p->admin_plan(); $service_number = $p->service_number; ?> + charge($service_number); $excess = $o->excess($service_number); $charge = Period::multiple($so->recur_schedule)*$so->price(TRUE)/12; ?> - + supplier_plan->display('base_cost')) : ?> + + + + + + id),$so->id); ?> @@ -22,12 +32,21 @@ contract_date_start(TRUE); ?> contract_date_end(TRUE); ?> supplier_plan->display('base_cost'); ?> - charge($service_number)); ?> - excess($service_number)); ?> + + + + + -  totalcharge(TRUE); ?> totalexcess(TRUE); ?> + +   + totalcharge(TRUE); ?> + totalexcess(TRUE); ?> + totalcharge()+$o->totalexcess()); ?> +   + diff --git a/modules/charge/classes/Model/Charge.php b/modules/charge/classes/Model/Charge.php index a03eed2d..4535b855 100644 --- a/modules/charge/classes/Model/Charge.php +++ b/modules/charge/classes/Model/Charge.php @@ -9,7 +9,7 @@ * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html */ -class Model_Charge extends ORM_OSB { +class Model_Charge extends ORM_OSB implements Invoicable { protected $_belongs_to = array( 'account'=>array(), 'service'=>array(), @@ -77,6 +77,10 @@ class Model_Charge extends ORM_OSB { } } + public function invoice_item($item_type) { + return sprintf('%s %s',StaticList_ItemType::get($item_type),$this->display('date_charge')); + } + /** * Render some details for specific calls, eg: invoice */ diff --git a/modules/checkout/classes/Checkout/Plugin/Paypal.php b/modules/checkout/classes/Checkout/Plugin/Paypal.php index 1332e5c3..b684f777 100644 --- a/modules/checkout/classes/Checkout/Plugin/Paypal.php +++ b/modules/checkout/classes/Checkout/Plugin/Paypal.php @@ -110,6 +110,7 @@ abstract class Checkout_Plugin_Paypal extends Checkout_Plugin { $iio = ORM::factory('Invoice_Item'); $iio->quantity = 1; $iio->module_id = $cno->mid()->id; + $iio->module_ref = $cno->id; $iio->item_type = 125; // Payment Fee $iio->price_base = (++$j==count($amts)) ? $cno->data['mc_gross_'.$c]-$i : Currency::round($pio->alloc_amt/array_sum($amts)*$cno->data['mc_gross_'.$c]); $iio->date_start = $iio->date_stop = time(); diff --git a/modules/checkout/classes/Model/Checkout/Notify.php b/modules/checkout/classes/Model/Checkout/Notify.php index 9b1130f3..ca8ec6a4 100644 --- a/modules/checkout/classes/Model/Checkout/Notify.php +++ b/modules/checkout/classes/Model/Checkout/Notify.php @@ -9,12 +9,22 @@ * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html */ -class Model_Checkout_Notify extends ORM_OSB { +class Model_Checkout_Notify extends ORM_OSB implements Invoicable { // Relationships protected $_has_one = array( 'checkout'=>array('far_key'=>'checkout_id','foreign_key'=>'id'), ); + protected $_display_filters = array( + 'date_orig'=>array( + array('Config::date',array(':value')), + ), + ); + + public function invoice_item() { + return sprintf('Payment Fee: %s',$this->checkout->name); + } + public function process() { return $this->checkout->plugin()->notify($this); } diff --git a/modules/checkout/classes/Task/Checkout/Notify/List.php b/modules/checkout/classes/Task/Checkout/Notify/List.php new file mode 100644 index 00000000..eb383339 --- /dev/null +++ b/modules/checkout/classes/Task/Checkout/Notify/List.php @@ -0,0 +1,37 @@ +find_all() as $cno) + $output .= sprintf($header, + $cno->id, + $cno->display('date_orig'), + $cno->checkout_id, + $cno->checkout->name, + $cno->status ? 'A' : 'I', + $cno->processed ? 'A' : 'I', + $cno->data['mc_gross'], + $cno->data['mc_fee'], + $cno->data['payer_id'], + $cno->data['custom'], + $cno->data['txn_id'], + $cno->data['item_name1'] + ); + + return $output; + } +} +?> diff --git a/modules/export/classes/Controller/Admin/Export.php b/modules/export/classes/Controller/Admin/Export.php index 08a50772..725fb33f 100644 --- a/modules/export/classes/Controller/Admin/Export.php +++ b/modules/export/classes/Controller/Admin/Export.php @@ -38,7 +38,7 @@ class Controller_Admin_Export extends Controller_Export { ->body(View::factory('export/admin/add') ->set('o',$edo) ->set('emo',ORM::factory('Export_Module',$_POST['export_module_id'])) - ->set('module',ORM::factory('Module',$_POST['module_id'])->module())); + ->set('module',ORM::factory('Module',$_POST['module_id'])->instance())); } /** diff --git a/modules/export/classes/Controller/Reseller/Export.php b/modules/export/classes/Controller/Reseller/Export.php index d557426c..66cde2ea 100644 --- a/modules/export/classes/Controller/Reseller/Export.php +++ b/modules/export/classes/Controller/Reseller/Export.php @@ -66,7 +66,7 @@ class Controller_Reseller_Export extends Controller_Export { $output .= ''; $c = 0; @@ -81,7 +81,6 @@ class Controller_Reseller_Export extends Controller_Export { 'id'=>'ID', 'account->name(TRUE)'=>'Account', 'date_orig'=>'Date', - 'status'=>'Active', 'total(TRUE)'=>'Total', 'exported'=>'Exported', ),$emo->display ? $emo->display : array())) diff --git a/modules/export/classes/Model/Export/Module.php b/modules/export/classes/Model/Export/Module.php index 6d17e94b..c0b0f598 100644 --- a/modules/export/classes/Model/Export/Module.php +++ b/modules/export/classes/Model/Export/Module.php @@ -30,7 +30,7 @@ class Model_Export_Module extends ORM_OSB { if (! is_numeric($days)) $days = 90; - $o = $this->module->module(); + $o = $this->module->instance(); return $o ->select(array($this->export_item->table_name().'.date_orig','exported')) diff --git a/modules/export/views/export/module/admin/edit.php b/modules/export/views/export/module/admin/edit.php index 9a772a71..0320cc03 100644 --- a/modules/export/views/export/module/admin/edit.php +++ b/modules/export/views/export/module/admin/edit.php @@ -5,7 +5,7 @@

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

    data($o->module->module()->table_columns()) + ->data($o->module->instance()->table_columns()) ->jssort(FALSE) ->columns(array( 'column_name'=>'Name', diff --git a/modules/invoice/classes/Controller/Task/Invoice.php b/modules/invoice/classes/Controller/Task/Invoice.php index 71d84d3d..c0ee930c 100644 --- a/modules/invoice/classes/Controller/Task/Invoice.php +++ b/modules/invoice/classes/Controller/Task/Invoice.php @@ -12,67 +12,6 @@ class Controller_Task_Invoice extends Controller_Task { public $auto_render = FALSE; - public function action_send() { - // Used to only process X invoices in a row. - $max = ORM::factory('Invoice')->config('EMAIL_INV_MAX'); - - $action = array(); - $iid = $this->request->param('id'); - $x = NULL; - - if (preg_match('/:/',$iid)) - list($iid,$x) = explode(':',$iid); - - // 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 or we dont email invoices we'll skip to the next one. - if (($io->remind($key) AND (is_null($x) OR $x != 'again')) OR ($io->account->invoice_delivery != 1)) - 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') - ->method(array('invoice','user_download')) - ->account($io->account) - ->expire(time()+86400*21) - ->uses(3) - ->generate(); - - $et->to = array('account'=>array($io->account_id)); - $et->variables = array( - 'DUE'=>$io->due(TRUE), - 'DUE_DATE'=>$io->display('due_date'), - 'EMAIL'=>Company::instance()->email(), - 'FIRST_NAME'=>$io->account->first_name, - 'HTML_INVOICE'=>$io->html(), - 'INV_NUM'=>$io->refnum(), - 'INV_URL'=>URL::site(URL::link('user','invoice/view/'.$io->id),'http'), - 'INV_URL_DOWNLOAD'=>URL::site(URL::link('user',sprintf('invoice/download/%s?token=%s',$io->id,$token)),'http'), - 'SITE_NAME'=>Company::instance()->name(), - ); - - // @todo Record email log id if possible. - if ($et->send()) { - $io->print_status = 1; - $io->set_remind($key,time(),($x=='again' ? TRUE : FALSE)); - array_push($action,(string)$io); - } - } - - $this->response->body(_('Invoices Sent: ').join('|',$action)); - } - - /** END **/ - public function action_audit_invoice_items() { $output = ''; diff --git a/modules/invoice/classes/Controller/User/Invoice.php b/modules/invoice/classes/Controller/User/Invoice.php index e6c75969..535c4477 100644 --- a/modules/invoice/classes/Controller/User/Invoice.php +++ b/modules/invoice/classes/Controller/User/Invoice.php @@ -12,6 +12,7 @@ class Controller_User_Invoice extends Controller_Invoice { protected $secure_actions = array( 'download'=>TRUE, + 'email'=>TRUE, 'list'=>TRUE, 'view'=>TRUE, ); @@ -33,11 +34,41 @@ class Controller_User_Invoice extends Controller_Invoice { $imo->memo = 'Invoice Downloaded.'; $imo->save(); - $this->response->body(Invoice::instance($io)->pdf()->Output(sprintf('%s.pdf',$io->refnum()),'D')); + $this->response->body(Invoice::instance($io)->render('pdf','all',array('download'=>sprintf('%s.pdf',$io->refnum())))); $this->response->headers(array('Content-Type' => 'application/pdf')); $this->auto_render = FALSE; } + /** + * Email an invoice + */ + public function action_email() { + $io = ORM::factory('Invoice',$this->request->param('id')); + + if (! $io->loaded() OR ! Auth::instance()->authorised($io->account)) + throw HTTP_Exception::factory(403,'Service either doesnt exist, or you are not authorised to see it'); + + if ($x=Invoice::instance($io)->render('email','all')) { + // Log the emailling + $imo = $io->invoice_memo; + $imo->invoice_id = $io->id; + $imo->account_id = $this->ao->id; + $imo->type = 'email'; + $imo->memo = 'Invoice Emailed.'; + $imo->save(); + + SystemMessage::factory() + ->title('Invoice') + ->type('success') + ->body(sprintf('Invoice :%s sent via email',$io->refnum())); + + HTTP::redirect(URL::link('user','email/view/'.$x)); + + } else { + HTTP::redirect(URL::link('user','invoice/view/'.$io->id)); + } + } + /** * Show a list of invoices */ @@ -74,8 +105,12 @@ class Controller_User_Invoice extends Controller_Invoice { if (! $io->loaded() OR ! Auth::instance()->authorised($io->account)) throw HTTP_Exception::factory(403,'Service either doesnt exist, or you are not authorised to see it'); - $output .= View::factory('invoice/user/view') - ->set('o',$io); + $output .= Invoice::instance($io)->render('html','all'); + + $output .= '
    '; + + $output .= HTML::anchor(URL::link('user','invoice/email/'.$io->id),'Email',array('class'=>'btn pull-right')); + $output .= HTML::anchor(URL::link('user','invoice/download/'.$io->id),'Download',array('class'=>'btn pull-right')); if ($io->due() AND ! $io->cart_exists()) $output .= View::factory('invoice/user/view/pay') @@ -100,6 +135,7 @@ class Controller_User_Invoice extends Controller_Invoice { Block::factory() ->title('Invoice Memos') ->title_icon('icon-list-alt') + ->span(6) ->body(Table::factory() ->data($x) ->columns(array( @@ -108,6 +144,27 @@ class Controller_User_Invoice extends Controller_Invoice { 'account->name()'=>'Account', 'memo'=>'Memo', ))); + + $x = $io->email()->find_all(); + if ($x->count()) + Block::factory() + ->title('Invoice Emails') + ->title_icon('icon-list-alt') + ->span(6) + ->body(Table::factory() + ->data($x) + ->columns(array( + 'id'=>'ID', + 'date_orig'=>'Date', + 'resolve("subject")'=>'Subject', + )) + ->prepend(array( + 'id'=>array('url'=>URL::link('user','email/view/')), + )) + ->postproc(array( + 'resolve("subject")'=>array('trim'=>55), + )) + ); } } ?> diff --git a/modules/invoice/classes/Invoicable.php b/modules/invoice/classes/Invoicable.php new file mode 100644 index 00000000..d37eef25 --- /dev/null +++ b/modules/invoice/classes/Invoicable.php @@ -0,0 +1,16 @@ + diff --git a/modules/invoice/classes/Invoice.php b/modules/invoice/classes/Invoice.php index 4d8b7f55..a74ab59d 100644 --- a/modules/invoice/classes/Invoice.php +++ b/modules/invoice/classes/Invoice.php @@ -15,7 +15,6 @@ class Invoice { private $_methods = array( 'dump', - 'min_due', 'save', 'saved', 'total', @@ -37,10 +36,6 @@ class Invoice { $this->_io->status = 1; } - public static function instance(Model_Invoice $io=NULL) { - return new Invoice($io); - } - /** * Add a Service to an Invoice * @@ -57,14 +52,15 @@ class Invoice { 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); + if (! $this->_io->due_date OR $this->_io->due_date > $this->min_due($so->date_next_invoice)) + $this->_io->due_date = $this->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'); $iio->service_id = $so->id; - $iio->product_id = $so->product_id; + $iio->module_id = $so->product->mid(); + $iio->module_ref = $so->product_id; $iio->quantity = $pdata['prorata']; $iio->price_base = $so->price(); $iio->recurring_schedule = $so->recur_schedule; @@ -85,8 +81,8 @@ class Invoice { foreach ($c->find_all() as $co) { $iio = ORM::factory('Invoice_Item'); $iio->service_id = $co->service_id; - $iio->product_id = $co->product_id; - $iio->charge_id = $co->id; + $iio->module_id = $co->mid(); + $iio->module_ref = $co->id; $iio->quantity = $co->quantity; $iio->price_base = $co->amount; $iio->date_start = $co->date_orig; @@ -99,8 +95,82 @@ class Invoice { return $this; } + /** + * Draw an invoice with a summary first page, and a detail subsequent pages + */ + private function draw_summary_invoice(Invoice_TCPDF $pdfo) { + // Draw Invoice Basics + $pdfo->drawCompanyLogo(); + $pdfo->drawCompanyAddress(); + $pdfo->drawInvoiceHeader(); + // @todo Get news from DB + $pdfo->drawNews(''); + $pdfo->drawRemittenceStub(); + $pdfo->drawPaymentMethods(); + + if ($this->_io->billing_status !=1 && $this->_io->due_date <= time()) + $pdfo->drawInvoiceDueNotice(); + elseif ($this->_io->billing_status == 1) + $pdfo->drawInvoicePaidNotice(); + + if ($this->_io->account->invoices_due_total()) + $pdfo->drawSummaryInvoicesDue(); + + $pdfo->drawSummaryLineItems(); + + // Next Page + $pdfo->drawDetailLineItems(); + + // Draw any Custom functions: + $pdfo->drawCustom(); + } + + public static function instance(Model_Invoice $io=NULL) { + return new Invoice($io); + } + + public function min_due($date) { + return strtotime(date('Y-M-d',($date < time()) ? time()+ORM::factory('Invoice')->config('DUE_DAYS_MIN')*86400 : $date)); + } + public function render($type,$section,$args=array()) { + $this->_io->pre_render(); + switch ($type) { + case 'email': + switch ($section) { + case 'all': + $token = ORM::factory('Module_Method_Token') + ->method(array('invoice','user_download')) + ->account($this->_io->account) + ->expire(time()+86400*21) + ->uses(3) + ->generate(); + + $et = Email_Template::instance('task_invoice_send'); + $et->to = array('account'=>array($this->_io->account_id)); + $et->variables = array( + 'DUE'=>$this->_io->due(TRUE), + 'DUE_DATE'=>$this->_io->display('due_date'), + 'EMAIL'=>Company::instance()->email(), + 'FIRST_NAME'=>$this->_io->account->first_name, + 'HTML_INVOICE'=>View::factory('invoice/user/viewemail')->set('html',$this->render_html()), + 'INV_NUM'=>$this->_io->refnum(), + 'INV_URL'=>URL::site(URL::link('user','invoice/view/'.$this->_io->id),'http'), + 'INV_URL_DOWNLOAD'=>URL::site(URL::link('user',sprintf('invoice/download/%s?token=%s',$this->_io->id,$token)),'http'), + 'SITE_NAME'=>Company::instance()->name(), + ); + + return $et->send(); + + break; + + default: + throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section)); + } + + break; + case 'html': switch ($section) { case 'body': @@ -109,6 +179,26 @@ class Invoice { ->set('o',$this->_io); break; + case 'all': + return $this->render_html(); + + break; + + default: + throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section)); + } + + break; + + case 'pdf': + switch ($section) { + case 'all': + if (isset($args['download'])) + return $this->render_pdf()->Output($args['download'],'D'); + else + return $this->render_pdf(); + break; + default: throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section)); } @@ -121,55 +211,37 @@ class Invoice { } /** - * Generate a PDF invoice + * Renders the invoice in HTML */ - public function pdf() { - $invoice_class = Kohana::classname('Invoice_TCPDF_'.Kohana::$config->load('invoice')->driver); + private function render_html() { + return View::factory('invoice/user/view') + ->set('o',$this->_io); + } - $pdf = new $invoice_class($this->_io); + /** + * Renders the invoice as a PDF and returns the PDF object + * + * @return Object Invoice_TCPDF + */ + private function render_pdf() { + $class = Kohana::classname('Invoice_TCPDF_'.Kohana::$config->load('invoice')->driver); + $pdfo = new $class($this->_io); - if ($pdf->getTemplate()) { - $pagecount = $pdf->setSourceFile($pdf->getTemplate()); - $tplidx = $pdf->ImportPage(1); + if ($pdfo->getTemplate()) { + $pagecount = $pdfo->setSourceFile($pdfo->getTemplate()); + $tplidx = $pdfo->ImportPage(1); } - $pdf->addPage(); + $pdfo->addPage(); # If we are using FPDI if (isset($tplidx)) - $pdf->useTemplate($tplidx); + $pdfo->useTemplate($tplidx); - $this->draw_summary_invoice($pdf); + $this->draw_summary_invoice($pdfo); # If we get here, all is OK. - return $pdf; - } - - private function draw_summary_invoice($pdf) { - // Draw Invoice Basics - $pdf->drawCompanyLogo(); - $pdf->drawCompanyAddress(); - $pdf->drawInvoiceHeader(); - // @todo Get news from DB - $pdf->drawNews(''); - $pdf->drawRemittenceStub(); - $pdf->drawPaymentMethods(); - - if ($this->_io->billing_status !=1 && $this->_io->due_date <= time()) - $pdf->drawInvoiceDueNotice(); - elseif($this->_io->billing_status == 1) - $pdf->drawInvoicePaidNotice(); - - if ($this->_io->account->invoices_due_total()) - $pdf->drawSummaryInvoicesDue(); - - $pdf->drawSummaryLineItems(); - - // Next Page - $pdf->drawDetailLineItems(); - - // Draw any Custom functions: - $pdf->drawCustom(); + return $pdfo; } } ?> diff --git a/modules/invoice/classes/Invoice/Tcpdf/Default.php b/modules/invoice/classes/Invoice/Tcpdf/Default.php index defbedf3..818717e7 100644 --- a/modules/invoice/classes/Invoice/Tcpdf/Default.php +++ b/modules/invoice/classes/Invoice/Tcpdf/Default.php @@ -46,7 +46,9 @@ class Invoice_TCPDF_Default extends Invoice_Tcpdf { $y += 2; $this->SetXY($x,$y); $this->Cell(0,0,'Phone:'); $this->SetXY($x+16,$y); $this->Cell(0,0,$this->co->phone()); $y += 4; - $this->SetXY($x,$y); $this->Cell(0,0,'Fax:'); $this->SetXY($x+16,$y); $this->Cell(0,0,$this->co->fax()); $y += 4; + if ($this->co->fax()) { + $this->SetXY($x,$y); $this->Cell(0,0,'Fax:'); $this->SetXY($x+16,$y); $this->Cell(0,0,$this->co->fax()); $y += 4; + } $this->SetXY($x,$y); $this->Cell(0,0,'Web:'); $this->SetXY($x+16,$y); $this->addHtmlLink(URL::base(TRUE,TRUE),URL::base(TRUE,TRUE)); $y += 4; } @@ -332,7 +334,7 @@ class Invoice_TCPDF_Default extends Invoice_Tcpdf { foreach ($items as $name => $line) { if ($i < $this->itemsSummaryMax) { $this->SetX($x); - $this->Cell(0,0,sprintf('%3.2f',$line['quantity'])); + $this->Cell(0,0,sprintf('%3.0f',$line['quantity'])); $this->SetX($x+8); $this->Cell(0,0,$name); $this->SetX($x+135); @@ -439,8 +441,19 @@ class Invoice_TCPDF_Default extends Invoice_Tcpdf { */ public function drawDetailLineItems() { $this->i = 0; - foreach ($this->io->subitems() as $io) - $this->drawLineItem($io); + + foreach ($this->io->items_periods() as $rs) { + $sids=array(); + + foreach ($this->io->items_periods($rs) as $iio) { + if (in_array($iio->service_id,$sids)) + continue; + + array_push($sids,$iio->service_id); + + $this->drawLineItem($iio); + } + } } /** @@ -478,35 +491,60 @@ class Invoice_TCPDF_Default extends Invoice_Tcpdf { $this->SetFont('helvetica','',8); $this->SetX($x); - $this->Cell(0,0,sprintf('%s - %s',$ito->product->title(),$ito->service->name())); + $this->Cell(0,0,$ito->title()); + $this->SetFont('helvetica','',7); - if ($ito->price_base) { - $this->SetX($x+160); - $this->Cell(10,0,Currency::display($ito->price_base),0,0,'R'); + if ($z=$this->io->service_items($ito)) { + foreach ($z as $eiio) { + $this->y += 3; + $this->SetY($this->y); + $this->SetX($x+10); + $this->Cell(10,0,$eiio->invoice_line()); + + if ($eiio->price_base) { + $this->SetX($x+160); + $this->Cell(10,0,Currency::display($eiio->price_base),0,0,'R'); + } + + if ($eiio->quantity) { + $this->SetX($x+130); + $this->Cell(10,0,$eiio->quantity,0,0,'R'); + } + + $this->SetX($x+130); + $this->Cell(0,0,$eiio->total(TRUE),0,0,'R'); + } } - if ($ito->quantity) { - $this->SetX($x+130); - $this->Cell(10,0,$ito->quantity,0,0,'R'); - } - - $this->SetX($x+130); - $this->Cell(0,0,Currency::display($ito->total()),0,0,'R'); - - if ($this->show_service_range && $ito->period()) { - $this->SetFont('helvetica','I',7); - $this->y += 3; - $this->SetXY($x+10,$this->y); $this->Cell(0,0,'Service Period'); - $this->SetFont('helvetica','',7); - $this->SetXY($x+40,$this->y); $this->Cell(0,0,$ito->period()); + if ($z=$this->io->service_items_extra($ito)) { + foreach ($z as $eiio) { + $this->y += 3; + $this->SetY($this->y); + $this->SetX($x+10); + $this->Cell(10,0,$eiio->invoice_line()); + + if ($eiio->price_base) { + $this->SetX($x+160); + $this->Cell(10,0,Currency::display($eiio->price_base),0,0,'R'); + } + + if ($eiio->quantity) { + $this->SetX($x+130); + $this->Cell(10,0,$eiio->quantity,0,0,'R'); + } + + $this->SetX($x+130); + $this->Cell(0,0,$eiio->total(TRUE),0,0,'R'); + } } + $this->y += 2; if ($ito->invoice_detail_items()) foreach ($ito->invoice_detail_items() as $k=>$v) { - $this->SetFont('helvetica','I',7); + $this->SetFont('helvetica','I',6); $this->y += 3; $this->SetXY($x+10,$this->y); $this->Cell(0,0,$k); - $this->SetFont('helvetica','',7); + $this->SetFont('helvetica','',6); $this->SetXY($x+40,$this->y); $this->Cell(0,0,$v); } diff --git a/modules/invoice/classes/Model/Invoice.php b/modules/invoice/classes/Model/Invoice.php index 11e302f6..d29bd7e1 100644 --- a/modules/invoice/classes/Model/Invoice.php +++ b/modules/invoice/classes/Model/Invoice.php @@ -40,10 +40,13 @@ class Model_Invoice extends ORM_OSB implements Cartable { // Items belonging to an invoice protected $_sub_items_load = array( - 'invoice_item'=>'service_id,item_type', + 'invoice_item'=>'service_id,item_type,date_start,date_stop', ); - /** INTERFACE REQUIREMENTS **/ + private $_render = array(); + + // Our required Interface Methods + public function cart_item() { return new Cart_Item(1,sprintf('Invoice: %s',$this->refnum()),$this->due()); } @@ -55,26 +58,83 @@ class Model_Invoice extends ORM_OSB implements Cartable { return count(Cart::instance()->get($this->mid(),$this->id)); } + // Our local methods + + /** + * Return a list of valid checkout options for this invoice + */ + public function checkout() { + $due = $this->due(); + + return ORM::factory('Checkout') + ->where_active() + ->where('amount_min','<=',$due) + ->where_open() + ->and_where('amount_max','>=',$due) + ->or_where('amount_max','is',null) + ->where_close()->find_all(); + } + + /** + * Display the amount due + */ + public function due($format=FALSE) { + // If the invoice is active calculate the due amount + $result = $this->status ? Currency::round($this->total()-$this->payments_total(),1) : 0; + + return $format ? Currency::display($result) : $result; + } + + public function email() { + return ORM::factory('Email_Log') + ->where('module_id','=',$this->mid()) + ->where('module_data','=',$this); + } + + /** + * Display the Invoice Number + */ + public function id() { + return sprintf('%06s',$this->id); + } + /** * Return the recurring schedules that are on an invoice * * @param $period Return an Array of items for that period */ public function items_periods($period=FALSE) { + if (is_null($period)) + return isset($this->_render['OTHER']) ? $this->_render['OTHER'] : array(); + + elseif ($period) + return isset($this->_render['SCHED'][$period]) ? $this->_render['SCHED'][$period] : array(); + + else + return array_keys($this->_render['SCHED']); + } + + /** + * Return a summary of all itemss on an invoice + */ + public function items_summary() { $result = array(); - foreach ($this->subitems() as $ito) { - // We are only interested in item_type=0 - if ($ito->item_type != 0) + foreach ($this->subitems() as $iio) { + // We only summarise item_type=0 + if (! $iio->item_type == 0) continue; - if ($period === FALSE AND ! in_array($ito->recurring_schedule,$result)) - array_push($result,$ito->recurring_schedule); + if ($iio->module() instanceof Model_Product) { + $p = $iio->module()->title(); - 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); + if (! isset($result[$p])) { + $result[$p]['quantity'] = 0; + $result[$p]['subtotal'] = 0; + } + + $result[$p]['quantity']++; + $result[$p]['subtotal'] += $iio->subtotal(); } } @@ -104,6 +164,67 @@ class Model_Invoice extends ORM_OSB implements Cartable { return $result; } + public function payments() { + return $this->payment_item->find_all(); + } + + public function payments_total($format=FALSE) { + $result = 0; + + foreach ($this->payments() as $po) + $result += $po->alloc_amt; + + return $format ? Currency::display($result) : $result; + } + + /** + * This function takes care of all the invoice items so that they are rendered only once + * + * We organise items by + * + recurring_schedule for service items item_type=0 + * + recurring_schedule for service items for item_type!=0 (that have an item_type=0 previously selected) + * + Other Items + */ + public function pre_render() { + $this->_render = array(); + $this->_render['SCHED'] = array(); + $processed = array(); + + foreach ($this->subitems() as $iio) { + if (in_array($iio->id,$processed)) + continue; + + if ($iio->recurring_schedule) { + $this->_render['SCHED'][$iio->recurring_schedule][] = $iio; + + } elseif (Object::in_array('service_id',$iio->service_id,$this->_render['SCHED'],TRUE)) { + // Do nothing + + } else { + $this->_render['OTHER'][] = $iio; + } + + array_push($processed,$iio->id); + } + } + + /** + * Display the Invoice Reference Number + */ + public function refnum() { + return sprintf('%s-%06s',$this->account->accnum(),$this->id); + } + + /** + * Check the reminder value + */ + public function remind($key) { + if (isset($this->reminders[$key])) + return (is_array($this->reminders[$key])) ? end($this->reminders[$key]) : $this->reminders[$key]; + else + return FALSE; + } + /** * Returns the array of Email Template Objects */ @@ -128,361 +249,6 @@ class Model_Invoice extends ORM_OSB implements Cartable { } } - /** - * 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->subitems() 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 - */ - public function service_items_extra(Model_Invoice_Item $o) { - $result = array(); - - // At the moment, we only return extra items pertaining to a service - if (! $o->service_id) - return $result; - - foreach ($this->subitems() as $iio) - if ($iio->item_type != 0 AND $iio->service_id == $o->service_id) - array_push($result,$iio); - - return $result; - } - - /** - * Return the total of all items relating to a service - */ - public function service_items_tax(Model_Invoice_Item $o,$format=FALSE) { - $result = 0; - - // At the moment, we only return extra items pertaining to a service - if (! $o->service_id) - return $result; - - foreach ($this->subitems() as $iio) - if ($iio->service_id == $o->service_id) - $result += $iio->tax(); - - return $format ? Currency::display($result) : $result; - } - - /** - * Return the total of all items relating to a service - */ - public function service_items_total(Model_Invoice_Item $o,$format=FALSE) { - $result = 0; - - // At the moment, we only return extra items pertaining to a service - if (! $o->service_id) - return $result; - - foreach ($this->subitems() as $iio) - if ($iio->service_id == $o->service_id) - $result += $iio->total(); - - return $format ? Currency::display($result) : $result; - } - - /** - * Add an item to an invoice - */ - public function subitem_add(Model_Invoice_Item $iio,Model_Country $co,$taxed=FALSE) { - $iio->subitem_add($co,$taxed); - - array_push($this->_sub_items,$iio); - - $this->_sub_items_sorted = FALSE; - } - - /** - * Return a list of invoice items for this invoice. - * @param type [CHARGE|CREDIT|ALL] - * @see invoice_items - */ - public function subitems($type='ALL') { - $result = array(); - - foreach ($this->_sub_items as $ito) { - // Ignore voided items - if ($ito->void) - continue; - - $return = FALSE; - - switch ($type) { - case 'CHARGE': - if ($ito->product_id OR $ito->quantity > 0) - $return = TRUE; - break; - - case 'CREDIT': - if (! $ito->product_id AND $ito->quantity < 0) - $return = TRUE; - break; - - case 'ALL': - default: - $return = TRUE; - break; - } - - if ($return) - array_push($result,$ito); - } - - return $result; - } - - /** - * Return a list of valid checkout options for this invoice - */ - public function checkout() { - $due = $this->due(); - - return ORM::factory('Checkout') - ->where_active() - ->where('amount_min','<=',$due) - ->where_open() - ->and_where('amount_max','>=',$due) - ->or_where('amount_max','is',null) - ->where_close()->find_all(); - } - - /** - * Display the amount due - */ - public function due($format=FALSE) { - // If the invoice is active calculate the due amount - $result = $this->status ? $this->total()-$this->payments_total() : 0; - - return $format ? Currency::display($result) : Currency::round($result); - } - - /** - * Display the Invoice Number - */ - public function id() { - return sprintf('%06s',$this->id); - } - - /** - * Provide a sorted list of items by an index - */ - public function items_index($index) { - static $result = array(); - - // We'll return a cached result for quicker processing - if (! $this->_changed AND array_key_exists($index,$result)) - return $result[$index]; - - foreach ($this->subitems() as $ito) { - switch ($index) { - case 'account': - if (! $ito->service_id) - $result[$index][$ito->id] = $ito; - - break; - - case 'period': - // We only show the services in this period - if (! is_null($ito->recurring_schedule) AND (empty($result[$index][$ito->recurring_schedule]) OR ! in_array($ito->service_id,$result[$index][$ito->recurring_schedule]))) - $result[$index][$ito->recurring_schedule][] = $ito->service_id; - - break; - - case 'service': - default: - if ($ito->service_id) - $result[$index][$ito->service_id][] = $ito; - - break; - } - } - - return array_key_exists($index,$result) ? $result[$index] : array(); - } - - /** - * Get a list of invoice_items for a service_id on an invoice - * - * We use this to list details by service on an invoice. - */ -// @todo to retire - public function items_services(array $items=array()) { - $result = array(); - if (! $items) - $items = $this->subitems(); - - foreach ($items as $ito) - if ($ito->service_id AND empty($result[$ito->service_id])) - $result[$ito->service_id] = $ito; - - return $result; - } - -// @todo to retire - public function items_invoice() { - $result = array(); - $items = $this->subitems(); - - foreach ($items as $ito) - if (! $ito->service_id AND empty($result[$ito->id])) - $result[$ito->id] = $ito; - - return $result; - } - - /** - * Return all invoice items for a specific service - */ - public function items_service($service_id) { - $svs = $this->items_index('service'); - - if (array_key_exists($service_id,$svs)) { - Sort::MAsort($svs[$service_id],'item_type'); - - return $svs[$service_id]; - } else - return array(); - } - -// @todo to retire - /** - * Return a list of periods and services - * - * This is so that we can list items summarised by billing period - */ - public function items_service_periods() { - $result = array(); - - $c = array(); - foreach ($this->subitems() as $ito) - if ($ito->service_id) { - // If we have already covered a service with no recurring_schedule - if (! $ito->recurring_schedule AND in_array($ito->service_id,$c)) - continue; - - array_push($c,$ito->service_id); - - $result[$ito->recurring_schedule][] = $ito; - } - - return $result; - } - - /** - * Summarise the items on an invoice - * - * We summaries based on product. - */ -// @todo - public function items_summary() { - $result = array(); - - foreach ($this->subitems() as $ito) { - // We only summarise item_type=0 - if (! $ito->item_type == 0) - continue; - - $t = $ito->product->title(); - - if (! isset($result[$t])) { - $result[$t]['quantity'] = 0; - $result[$t]['subtotal'] = 0; - } - - $result[$t]['quantity'] += $ito->quantity; - $result[$t]['subtotal'] += $ito->subtotal(); - } - - return $result; - } - - /** - * Calculate the total for items for a service - */ - public function items_service_total($sid) { - $total = 0; - - foreach ($this->items_service($sid) as $ito) - $total += $ito->total(); - - return $total; - } - - /** - * Calculate the tax of items for a service - */ - public function items_service_tax($sid) { - $total = 0; - - foreach ($this->items_service($sid) as $ito) - $total += $ito->tax(); - - return $total; - } - - /** - * Calculate the discounts of items for a service - */ - public function items_service_discount($sid) { - $total = 0; - - foreach ($this->items_service($sid) as $ito) - $total += $ito->discount(); - - return $total; - } - - public function min_due($date) { - return strtotime(date('Y-M-d',($date < time()) ? time()+ORM::factory('Invoice')->config('DUE_DAYS_MIN')*86400 : $date)); - } - - /** - * Display the Invoice Reference Number - */ - public function refnum() { - return sprintf('%s-%06s',$this->account->accnum(),$this->id); - } - - public function payments() { - return $this->payment_item->find_all(); - } - - public function payments_total($format=FALSE) { - $result = 0; - - foreach ($this->payments() as $po) - $result += $po->alloc_amt; - - return $format ? Currency::display($result) : Currency::round($result); - } - - /** - * Check the reminder value - */ - public function remind($key) { - if (isset($this->reminders[$key])) - return (is_array($this->reminders[$key])) ? end($this->reminders[$key]) : $this->reminders[$key]; - else - return FALSE; - } - public function save(Validation $validation = NULL) { // Our items will be clobbered once we save the object, so we need to save it here. $subitems = $this->subitems(); @@ -522,12 +288,12 @@ class Model_Invoice extends ORM_OSB implements Cartable { } // Update charge iteme - if ($iio->charge_id) { - $iio->charge->processed = 1; + if (($x=$iio->module()) AND ($x instanceof Model_Charge)) { + $x->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)); + if (! $x->save()) + throw new Kohana_Exception('Problem saving charge :charge for invoice_item :invoice_item - Failed save()',array(':invoice_item'=>$iio->id,':charge'=>$x->id)); } } @@ -541,6 +307,72 @@ class Model_Invoice extends ORM_OSB implements Cartable { return $this; } + /** + * 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->subitems() 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 + */ + public function service_items_extra(Model_Invoice_Item $o) { + $result = array(); + + foreach ($this->subitems() as $iio) + if ($iio->item_type != 0 AND (! $o->service_id OR ($iio->service_id == $o->service_id))) + array_push($result,$iio); + + return $result; + } + + /** + * Return the total of all items relating to a service + */ + public function service_items_tax(Model_Invoice_Item $o,$format=FALSE) { + $result = 0; + + // At the moment, we only return extra items pertaining to a service + if (! $o->service_id) + return $result; + + foreach ($this->subitems() as $iio) + if ($iio->service_id == $o->service_id) + $result += $iio->tax(); + + return $format ? Currency::display($result) : $result; + } + + /** + * Return the total of all items relating to a service + */ + public function service_items_total(Model_Invoice_Item $o,$format=FALSE) { + $result = 0; + + // At the moment, we only return extra items pertaining to a service + if (! $o->service_id) + return $result; + + foreach ($this->subitems() as $iio) + if ($iio->service_id == $o->service_id) + $result += $iio->total(); + + return $format ? Currency::display($result) : $result; + } + public function set_remind($key,$value,$add=FALSE) { $x = $this->reminders; @@ -563,6 +395,56 @@ class Model_Invoice extends ORM_OSB implements Cartable { return $this->saved(); } + /** + * Add an item to an invoice + */ + public function subitem_add(Model_Invoice_Item $iio,Model_Country $co,$taxed=FALSE) { + $iio->subitem_add($co,$taxed); + + array_push($this->_sub_items,$iio); + + $this->_sub_items_sorted = FALSE; + } + + /** + * Return a list of invoice items for this invoice. + * @param type [CHARGE|CREDIT|ALL] + * @see invoice_items + */ + public function subitems($type='ALL') { + $result = array(); + + foreach ($this->_sub_items as $iio) { + // Ignore voided items + if ($iio->void) + continue; + + $return = FALSE; + + switch ($type) { + case 'CHARGE': + if ($iio->quantity > 0) + $return = TRUE; + break; + + case 'CREDIT': + if ($iio->quantity < 0) + $return = TRUE; + break; + + case 'ALL': + default: + $return = TRUE; + break; + } + + if ($return) + array_push($result,$iio); + } + + return $result; + } + /** * Return the subtotal of all items */ @@ -572,7 +454,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems() as $ito) $result += $ito->subtotal(); - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } public function tax($format=FALSE) { @@ -581,7 +463,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems() as $ito) $result += $ito->tax(); - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } /** @@ -591,12 +473,12 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function tax_summary() { $summary = array(); - foreach ($this->subitems() as $ito) { - foreach ($ito->invoice_item_tax->find_all() as $item_tax) { - if (! isset($summary[$item_tax->tax_id])) - $summary[$item_tax->tax_id] = $item_tax->amount; + foreach ($this->subitems() as $iio) { + foreach ($iio->tax->find_all() as $iito) { + if (! isset($summary[$iito->tax_id])) + $summary[$iito->tax_id] = $iito->amount; else - $summary[$item_tax->tax_id] += $item_tax->amount; + $summary[$iito->tax_id] += $iito->amount; } } @@ -617,7 +499,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems() as $ito) $result += $ito->total(); - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } public function total_charges($format=FALSE) { @@ -626,7 +508,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems('CHARGE') as $ito) $result += $ito->subtotal()+$ito->tax(); - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } public function total_credits($format=FALSE) { @@ -635,7 +517,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems('CREDIT') as $ito) $result += ($ito->subtotal()+$ito->tax())*-1; - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } public function total_discounts($format=FALSE) { @@ -644,10 +526,18 @@ class Model_Invoice extends ORM_OSB implements Cartable { foreach ($this->subitems() as $ito) $result += $ito->discount(); - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } - /** LIST FUNCTIONS **/ + private function _where_unprocessed() { + return $this->where_open()->where('process_status','!=',1)->or_where('process_status','is',NULL)->where_close(); + } + + public function where_unprocessed() { + return $this->_where_unprocessed(); + } + + // Our list function /** * Search for invoices matching a term @@ -687,12 +577,26 @@ class Model_Invoice extends ORM_OSB implements Cartable { return $result; } - private function _where_unprocessed() { - return $this->where_open()->where('process_status','!=',1)->or_where('process_status','is',NULL)->where_close(); + /** + * Return a list of invoices that are due, excluding overdue. + */ + public function list_due($time=NULL,$authorised=TRUE) { + $result = array(); + + foreach ($this->_list_due($authorised) as $io) + if (is_null($time) OR $io->due_date > $time) + array_push($result,$io); + + return $result; } - public function where_unprocessed() { - return $this->_where_unprocessed(); + public function list_due_total($format=FALSE,$time=NULL,$authorised=TRUE) { + $result = 0; + + foreach ($this->list_due($time,$authorised) as $io) + $result += $io->due(); + + return $format ? Currency::display($result) : $result; } /** @@ -730,28 +634,6 @@ class Model_Invoice extends ORM_OSB implements Cartable { return $result; } - /** - * Return a list of invoices that are due, excluding overdue. - */ - public function list_due($time=NULL,$authorised=TRUE) { - $result = array(); - - foreach ($this->_list_due($authorised) as $io) - if (is_null($time) OR $io->due_date > $time) - array_push($result,$io); - - return $result; - } - - public function list_due_total($format=FALSE,$time=NULL,$authorised=TRUE) { - $result = 0; - - foreach ($this->list_due($time,$authorised) as $io) - $result += $io->due(); - - return $format ? Currency::display($result) : Currency::round($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. @@ -759,23 +641,5 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function list_tosend() { return ORM::factory('Invoice')->where_active()->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 = ''; - - $output = View::factory('invoice/user/email') - ->set('mediapath',Route::get('default/media')) - ->set('io',$this); - - return $css.$output; - } } ?> diff --git a/modules/invoice/classes/Model/Invoice/Item.php b/modules/invoice/classes/Model/Invoice/Item.php index 3f9f17ce..0447c41d 100644 --- a/modules/invoice/classes/Model/Invoice/Item.php +++ b/modules/invoice/classes/Model/Invoice/Item.php @@ -8,22 +8,15 @@ * @author Deon George * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html - * - * 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 { // Relationships protected $_belongs_to = array( - 'product'=>array(), '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') + 'tax'=>array('model'=>'Invoice_Item_Tax','far_key'=>'id') ); protected $_display_filters = array( @@ -40,7 +33,7 @@ class Model_Invoice_Item extends ORM_OSB { // Items belonging to an invoice item protected $_sub_items_load = array( - 'invoice_item_tax'=>FALSE, + 'tax'=>FALSE, ); // The total of all discounts @@ -48,6 +41,63 @@ class Model_Invoice_Item extends ORM_OSB { return Currency::round($this->discount_amt); } + /** + * The line that will be printed on an invoice + * + * @todo This method includes some database format validation routines, which can be removed when the database + * is completly transformed. + */ + public function invoice_line() { + $ii = NULL; + + // Our module is responsible for rending the invoice line + $ii = ($this->module_id AND method_exists($this->module(),'invoice_item')) ? $this->module()->invoice_item($this->item_type) : StaticList_ItemType::get($this->item_type); + + switch ($this->item_type) { + // Service Charges + case 0: + case 7: + return ((! $this->service_id OR $this->product_id OR $this->charge_id OR $this->product_name OR ! $this->recurring_schedule OR ! $this->date_start OR ! $this->date_stop) ? '+ ' : '').$ii.' '.$this->period(); + + case 1: + // @todo + return $ii; + + case 2: + case 3: + case 4: + case 5: + case 6: + return ((! $this->service_id OR $this->product_id OR $this->charge_id OR $this->product_name OR $this->recurring_schedule OR ! $this->date_start OR ! $this->date_stop) ? '+ ' : '').$ii; + + case 124: + case 125: + case 126: + case 127: + // @todo + return $ii; + + // @todo DB records to fix. + default: + if ($this->charge_id) + return '*'.($ii ? $ii : $this->charge->description).' '.$this->period(); + else + throw HTTP_Exception::factory(501,'Unable to render invoice item :id',array(':id'=>$this->id)); + } + } + + /** + * Return an instance of the Model of this charge + */ + public function module() { + $x = ORM::factory('Module',$this->module_id); + + if (! $x->loaded()) + throw new Kohana_Exception('Module :module doesnt exist?',array(':module'=>$this->module_id)); + + return ORM::factory('Module',$this->module_id)->instance($this->module_ref); + } + // Display the period that a transaction applies public function period() { return ($this->date_start == $this->date_stop) ? Config::date($this->date_start) : sprintf('%s -> %s',Config::date($this->date_start),Config::date($this->date_stop)); @@ -71,13 +121,13 @@ class Model_Invoice_Item extends ORM_OSB { $iito->save(); if (! $iito->saved()) { - $this->void = 1; - $this->save(); - - break; - } + $this->void = 1; + $this->save(); + break; + } } + } else throw new Kohana_Exception('Couldnt save invoice_item for some reason?'); @@ -119,7 +169,7 @@ class Model_Invoice_Item extends ORM_OSB { public function subtotal($format=FALSE) { $result = $this->price_base*$this->quantity; - return $format ? Currency::display($result) : Currency::round($result); + return $format ? Currency::display($result) : $result; } // Sum up the tax that applies to this invoice item @@ -149,8 +199,12 @@ class Model_Invoice_Item extends ORM_OSB { * 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; + if ($this->service_id AND $this->module_id AND method_exists($this->module(),'invoice_title')) + return $this->module_ref ? sprintf('%s: %s',$this->module()->invoice_title(),$this->service->name()) : $this->service->name(); + elseif ($x=$this->module() AND ($x instanceof Model_Charge) AND $x->product_id) + return $x->product->title().' '.$this->service->name(); + elseif ($this->product_id) + return sprintf('* %s: %s',$this->product->invoice_title(),$this->service->name()); else return 'Unknown Item'; } @@ -166,67 +220,6 @@ class Model_Invoice_Item extends ORM_OSB { return sprintf('%03s-%06s',$this->item_type,$this->id); } - public function invoice_line() { - if ($this->charge_id) - return $this->charge->description.' '.$this->period(); - elseif ($this->service_id) - return 'Service '.$this->period(); - else - throw HTTP_Exception::factory(501,'Unable to render invoice item :id',array(':id'=>$this->id)); - } - - /** - * Name for an invoice item - * @deprecated, use StaticList_ItemType() - */ - public function name() { - switch ($this->item_type) { - case 0: return _('Service'); - - case 1: return _('Item'); - - case 2: - case 3: - case 4: - case 6: - case 126: - case 127: return _('Charge'); - - case 5: return $this->charge->description; - - default: return _('Other'); - } - } - - /** - * Detail behind an invoice item - */ - public function detail() { - switch ($this->item_type) { - case 0: return ''; - - case 1: return _('Hardware'); - - case 2: return _('Service Relocation Fee'); - - case 3: return _('Service Change Fee'); - - case 4: return _('Service Connection Fee'); - - case 5: return sprintf('%s@%3.2f',$this->quantity,$this->price_base); - - case 6: return _('Service Excess Fee'); - - case 125: return _('Payment Fee'); - - case 126: return _('Rounding'); - - case 127: return _('Late Payment Fee'); - - default: ''; - } - } - public function invoice_detail_items() { switch ($this->item_type) { case 0: diff --git a/modules/invoice/classes/Task/Invoice/Email.php b/modules/invoice/classes/Task/Invoice/Email.php new file mode 100644 index 00000000..e99ed41a --- /dev/null +++ b/modules/invoice/classes/Task/Invoice/Email.php @@ -0,0 +1,63 @@ +NULL, // Force emailing if already sent + 'id'=>NULL, // Invoice to email + ); + + protected function _execute(array $params) { + // Used to only process X invoices in a row. + $max = ORM::factory('Invoice')->config('EMAIL_INV_MAX'); + + $iids = $params['id'] ? explode(':',$params['id']) : array(); + + $max_count = 0; + $action = array(); + + // Get our list of invoices to send + $i = $iids ? ORM::factory('Invoice')->where('id','IN',$iids) : ORM::factory('Invoice')->list_tosend(); + + $key = 'send'; + + foreach ($i->find_all() as $io) { + // If we have already sent a reminder or we dont email invoices we'll skip to the next one. + if (($io->remind($key) AND (is_null($params['force']) OR $params['force'] != 'again')) OR ($io->account->invoice_delivery != 1)) + continue; + + // If we have issued the max number of invoices this round, finish. + if (++$max_count > $max) + break; + + if (Invoice::instance($io)->render('email','all')) { + // Log the emailling + $imo = $io->invoice_memo; + $imo->invoice_id = $io->id; + $imo->type = 'email'; + $imo->memo = 'Invoice Emailed.'; + $imo->save(); + + $io->print_status = 1; + $io->set_remind($key,time(),($params['force']=='again' ? TRUE : FALSE)); + $io->save(); + + array_push($action,(string)$io); + + } else { + throw new Kohana_Exception('Unable to send invoice :io',array(':io'=>$io->id)); + } + } + + return _('Invoiced emailed: ').join('|',$action); + } +} +?> diff --git a/modules/invoice/classes/Task/Invoice/Reminddue.php b/modules/invoice/classes/Task/Invoice/Reminddue.php index be786a4f..eb40fe6f 100644 --- a/modules/invoice/classes/Task/Invoice/Reminddue.php +++ b/modules/invoice/classes/Task/Invoice/Reminddue.php @@ -42,6 +42,9 @@ class Task_Invoice_Reminddue extends Minion_Task { 'SITE_NAME'=>Company::instance()->name(), ); + $et->module = $io->mid(); + $et->module_data = $io->id; + // @todo Record email log id if possible. if ($et->send()) { $io->set_remind($key,time()); diff --git a/modules/invoice/classes/Task/Invoice/Remindoverdue1.php b/modules/invoice/classes/Task/Invoice/Remindoverdue1.php index f0ce36da..887332e5 100644 --- a/modules/invoice/classes/Task/Invoice/Remindoverdue1.php +++ b/modules/invoice/classes/Task/Invoice/Remindoverdue1.php @@ -59,6 +59,9 @@ tr.even { background-color: #F6F6F8; } 'SITE_NAME'=>Company::instance()->name(), ); + $et->module = $io->mid(); + $et->module_data = $io->id; + // @todo Record email log id if possible. if ($eloid = $et->send()) { $io->set_remind($key,time(),FALSE); diff --git a/modules/invoice/views/invoice/user/email.php b/modules/invoice/views/invoice/user/email.php deleted file mode 100644 index cc5145c7..00000000 --- a/modules/invoice/views/invoice/user/email.php +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - -
    - - - - - -
    name(); ?>
    address(); ?>
    contacts(); ?>
    -
      - - - - - - - - - - - - - - - - - - - - - - - - - -
    TAX INVOICEid(); ?>
    Issue Datedisplay('date_orig'); ?>
    Due Datedisplay('due_date'); ?>
    Current Chargestotal(TRUE); ?>
    Payments Received to Datepayments_total(TRUE); ?>
    Total Charges Due This Invoicedue(TRUE); ?>
    -
     
    - - - - - items_service_periods() as $rs => $items) { ?> - - - - - - - - - - - - - - - - - - - - total_discounts()) { ?> - - - - - - - - - tax_summary() as $tid => $amount) { - $m = ORM::factory('Tax',$tid); - ?> - - - - - - - - - - - - - - - -
    Charges Detail:
    +Other Items 
      -
    - - - items_services($items) as $sid => $ito) { ?> - - - - - - - - - - items_service($sid) as $ito) { ?> - - - - - - - - - - - - discount_amt) { ?> - - - - - - - - - - - - - - - - -
    id(); ?>service_name(); ?> (product_id; ?>)items_service_total($so->id));?>
     trannum();?>name();?>detail();?>period();?>subtotal());?> 
     (items_service_discount($so->id));?>)
     items_service_tax($so->id));?> 
    -
    -
    Sub Total of Items:subtotal(TRUE); ?> 
    Discounts:(total_discounts(TRUE); ?>)
    Taxes Included:
     description; ?> 
    Total This Invoice:total(TRUE); ?> 
    Total Outstanding This Account:account->invoices_due_total(NULL,TRUE); ?> 
    -
    diff --git a/modules/invoice/views/invoice/user/view.php b/modules/invoice/views/invoice/user/view.php index 6105b6ec..0a581df5 100644 --- a/modules/invoice/views/invoice/user/view.php +++ b/modules/invoice/views/invoice/user/view.php @@ -102,6 +102,3 @@ - -
    -id),'Download Invoice',array('class'=>'btn pull-right')); ?> diff --git a/modules/invoice/views/invoice/user/view/body.php b/modules/invoice/views/invoice/user/view/body.php index 68e754f0..c849fab6 100644 --- a/modules/invoice/views/invoice/user/view/body.php +++ b/modules/invoice/views/invoice/user/view/body.php @@ -1,43 +1,70 @@ items_periods() as $rs) : ?> - + + + + + items_periods($rs) as $iio) : ?> + service_id) : ?> + + service_id,$sids)) continue; array_push($sids,$iio->service_id); ?> + + + + + + + + service_items($iio)) : ?> + + + + + + + + + + + + service_items_extra($iio)) : ?> + + + + + + + + + + + + + + + + + + + + + + + + + + items_periods(NULL)) : ?> - items_periods($rs) as $iio) : ?> - service_id) : ?> - - - - - - - - service_items($iio)) : ?> - - - - - - - - - - - - service_items_extra($iio)) : ?> - - - - - - - - - - + + + + + + + - - +
     service_id),$iio->service->id()).' ' : '').$iio->title(); ?> 
    service_items_total($iio,TRUE); ?>
      invoice_line().($show_id ? " ({$eiio->trannum()})" : ''); ?>
    total(TRUE); ?>
     
     invoice_line().($show_id ? " ({$eiio->trannum()})" : ''); ?>
    total(TRUE); ?>
     
     title(); ?> 
    service_items_total($iio,TRUE); ?>
     service_id),$iio->service->id()).' ' : '').$iio->title(); ?> 
    service_items_total($iio,TRUE); ?>
      invoice_line().($show_id ? " ($eiio->id)" : ''); ?>
    total(TRUE); ?>
     
     invoice_line().($show_id ? " ($eiio->id)" : ''); ?>
    total(TRUE); ?>
     
     invoice_line(); ?>
    total(TRUE); ?>
    diff --git a/modules/invoice/views/invoice/user/view/pay.php b/modules/invoice/views/invoice/user/view/pay.php index 6970a37f..2bad5ffd 100644 --- a/modules/invoice/views/invoice/user/view/pay.php +++ b/modules/invoice/views/invoice/user/view/pay.php @@ -3,7 +3,7 @@ echo Form::open('cart/add'); echo Form::hidden('module_id',$mid,array('nocg'=>TRUE)); echo Form::hidden('module_item',$o->id,array('nocg'=>TRUE)); - echo Form::button('submit','Add to Cart',array('class'=>'btn btn-primary','nocg'=>TRUE)); + echo Form::button('submit','Pay Now',array('class'=>'btn btn-primary','nocg'=>TRUE)); echo Form::close('cart/add'); ?> diff --git a/modules/invoice/views/invoice/user/viewemail.php b/modules/invoice/views/invoice/user/viewemail.php new file mode 100644 index 00000000..b7bc5769 --- /dev/null +++ b/modules/invoice/views/invoice/user/viewemail.php @@ -0,0 +1,219 @@ + +
    +
    +
    + +
    +
    +
    diff --git a/modules/lnApp b/modules/lnApp index f3066fd3..bf3e9b1c 160000 --- a/modules/lnApp +++ b/modules/lnApp @@ -1 +1 @@ -Subproject commit f3066fd383a9211336c035acde462e32c38546e2 +Subproject commit bf3e9b1c78838c198368dc93cbb12b29356642d9 diff --git a/modules/product/classes/Model/Product.php b/modules/product/classes/Model/Product.php index 6e85750c..8b916c40 100644 --- a/modules/product/classes/Model/Product.php +++ b/modules/product/classes/Model/Product.php @@ -12,7 +12,7 @@ * Column Definitions: * + price_type: 0=One Time, 1=Recurring, 2=Trial */ -class Model_Product extends ORM_OSB { +class Model_Product extends ORM_OSB implements Invoicable { protected $_has_many = array( 'invoice'=>array('through'=>'invoice_item'), 'service'=>array('far_key'=>'id'), @@ -48,6 +48,24 @@ class Model_Product extends ORM_OSB { protected $_save_message = TRUE; + // Our required Interface Methods + + public function invoice_item($item_type) { + switch ($item_type) { + case 0: + case 7: + return 'Service'; + default: + return 'Product Charge'; + } + } + + public function invoice_title() { + return $this->title(); + } + + // Our local methods + // Our database index for pricing values private $_price_options = array( 'base'=>'price_base', diff --git a/modules/service/classes/Model/Service.php b/modules/service/classes/Model/Service.php index 305336cf..c9e77991 100644 --- a/modules/service/classes/Model/Service.php +++ b/modules/service/classes/Model/Service.php @@ -175,12 +175,13 @@ class Model_Service extends ORM_OSB { } 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') + return ORM::factory('Invoice_Item') + ->where('item_type','IN',array(0,7)) + ->where('service_id','=',$this) + ->where('invoice_item.void','IS',NULL) + ->join('invoice') + ->on('invoice.id','=','invoice_item.invoice_id') + ->on('invoice.status','=',1) ->order_by('date_stop','DESC'); } @@ -195,15 +196,16 @@ class Model_Service extends ORM_OSB { * Returns the date that an item has been paid to */ public function paid_to($format=FALSE) { - $x = NULL; + $x = $metric = NULL; foreach ($this->last_invoice_item()->order_by('date_orig','DESC')->find_all() as $iio) if ($iio->invoice->due() == 0) { $x = $iio; + $metric = ($iio->total() < 0) ? $iio->date_start-86400 : $iio->date_stop; break; } - return $format ? ($x ? $x->display('date_stop') : ' ') : ($x ? $x->date_stop : NULL); + return $format ? ($x ? Config::date($metric) : ' ') : ($x ? $metric : NULL); } /** diff --git a/modules/ssl/classes/SSL.php b/modules/ssl/classes/SSL.php index 01a13f4b..a7527b64 100644 --- a/modules/ssl/classes/SSL.php +++ b/modules/ssl/classes/SSL.php @@ -201,12 +201,8 @@ class SSL { return $this->_details('version'); } - public static function xexpire($cert,$format=FALSE) { - return static::instance($cert)->get_expire($format); - } - public static function subject($cert) { - return static::instance($cert)->get_subject(); + return self::instance($cert)->get_subject(); } public static function csrsubject($csr) { diff --git a/modules/tax/classes/Tax.php b/modules/tax/classes/Tax.php index 6203e614..5b5a4b45 100644 --- a/modules/tax/classes/Tax.php +++ b/modules/tax/classes/Tax.php @@ -11,14 +11,14 @@ */ class Tax { public static function add($value) { - return Currency::round($value+static::amount($value)); + return Currency::round($value+self::amount($value)); } /** * Return the total of tax for an amount */ - public static function amount($value) { - return Currency::round(static::total(Company::instance()->country()->id,NULL,$value)); + public static function amount($value,$round=FALSE) { + return Currency::round(self::total(Company::instance()->country()->id,NULL,$value)); } /** @@ -54,7 +54,7 @@ class Tax { public static function total($cid,$zone,$value) { $total = 0; - foreach (static::detail($cid,$zone,$value) as $tax) + foreach (self::detail($cid,$zone,$value) as $tax) $total += $tax['amount']; return $total;