From 6875dc3693876e48d6badec9bc02d4a1c0f656d7 Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 13 Jun 2013 23:35:19 +1000 Subject: [PATCH] Improvements to invoice --- .../classes/Controller/Reseller/Account.php | 11 ++ .../classes/Model/Auth/UserDefault.php | 2 +- application/classes/StaticList/ItemType.php | 28 ++++ .../classes/Controller/Task/Invoice.php | 1 - .../classes/Controller/User/Invoice.php | 1 - modules/invoice/classes/Invoicable.php | 16 ++ modules/invoice/classes/Invoice.php | 114 +++++++------ modules/invoice/classes/Model/Invoice.php | 90 +++++++++- .../invoice/classes/Model/Invoice/Item.php | 30 +++- modules/invoice/views/invoice/user/view.php | 154 ++++++------------ .../invoice/views/invoice/user/view/body.php | 36 ++++ .../invoice/views/invoice/user/view/pay.php | 12 +- .../classes/Controller/Admin/Product.php | 3 + .../service/views/service/admin/update.php | 16 -- modules/service/views/service/user/view.php | 7 +- 15 files changed, 321 insertions(+), 200 deletions(-) create mode 100644 application/classes/StaticList/ItemType.php create mode 100644 modules/invoice/classes/Invoicable.php create mode 100644 modules/invoice/views/invoice/user/view/body.php diff --git a/application/classes/Controller/Reseller/Account.php b/application/classes/Controller/Reseller/Account.php index d38fcb0e..e504a3e8 100644 --- a/application/classes/Controller/Reseller/Account.php +++ b/application/classes/Controller/Reseller/Account.php @@ -128,6 +128,17 @@ class Controller_Reseller_Account extends Controller_Account { 'id'=>array('url'=>URL::link('user','service/view/')), )) ); + + $i = Invoice::instance(); + foreach ($ao->service->list_active() as $io) + if (! $io->suspend_billing) + $i->add_service($io); + + Block::factory() + ->title(sprintf('Next Invoice Items for Account: %s',$ao->accnum())) + ->title_icon('icon-info-sign') + ->span(6) + ->body($i->render('html','body')); } } ?> diff --git a/application/classes/Model/Auth/UserDefault.php b/application/classes/Model/Auth/UserDefault.php index 7414250e..490b513e 100644 --- a/application/classes/Model/Auth/UserDefault.php +++ b/application/classes/Model/Auth/UserDefault.php @@ -15,7 +15,7 @@ class Model_Auth_UserDefault extends Model_Auth_User { 'username' => array( array('not_empty'), array('min_length', array(':value', 4)), - array('max_length', array(':value', 32)), + array('max_length', array(':value', 256)), ), 'email' => array( array('not_empty'), diff --git a/application/classes/StaticList/ItemType.php b/application/classes/StaticList/ItemType.php new file mode 100644 index 00000000..7643f1d3 --- /dev/null +++ b/application/classes/StaticList/ItemType.php @@ -0,0 +1,28 @@ +'MAIN', // Line Charge Topic on Invoice, eg: Service Name + 5=>'EXCESS', // Excess Service Item, of item 0 + ); + } + + public static function get($value) { + return static::factory()->_get($value); + } + + public static function index($key) { + return array_search($key,static::factory()->table()); + } +} +?> diff --git a/modules/invoice/classes/Controller/Task/Invoice.php b/modules/invoice/classes/Controller/Task/Invoice.php index 1930adf8..e238ed56 100644 --- a/modules/invoice/classes/Controller/Task/Invoice.php +++ b/modules/invoice/classes/Controller/Task/Invoice.php @@ -201,7 +201,6 @@ class Controller_Task_Invoice extends Controller_Task { $iio->quantity = $pdata['prorata']; $iio->item_type = 0; // Service Billing $iio->discount_amt = NULL; // @todo - $iio->price_type = $so->product->price_type; $iio->price_base = $so->price(); $iio->recurring_schedule = $so->recur_schedule; $iio->date_start = $pdata['start_time']; diff --git a/modules/invoice/classes/Controller/User/Invoice.php b/modules/invoice/classes/Controller/User/Invoice.php index 63175267..902a85c5 100644 --- a/modules/invoice/classes/Controller/User/Invoice.php +++ b/modules/invoice/classes/Controller/User/Invoice.php @@ -64,7 +64,6 @@ class Controller_User_Invoice extends Controller_Invoice { throw HTTP_Exception::factory(403,'Service either doesnt exist, or you are not authorised to see it'); $output .= View::factory('invoice/user/view') - ->set('mediapath',Route::get('default/media')) ->set('o',$io); if ($io->due() AND ! $io->cart_exists()) diff --git a/modules/invoice/classes/Invoicable.php b/modules/invoice/classes/Invoicable.php new file mode 100644 index 00000000..4e8f32bd --- /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 124df9e5..4cfe7448 100644 --- a/modules/invoice/classes/Invoice.php +++ b/modules/invoice/classes/Invoice.php @@ -13,80 +13,78 @@ class Invoice { // This invoice Object private $io; - public function __construct($io) { - $this->io = $io; + public function __construct(Model_Invoice $io=NULL) { + $this->io = is_null($io) ? ORM::factory('Invoice') : $io; } - public static function instance($io) { + public static function instance(Model_Invoice $io=NULL) { return new Invoice($io); } /** - * Return a list of invoices for an service + * Add a Service to an Invoice * - * @param $id int Service ID - * @param $paid boolean Optionally only list the ones that are not paid. - * @return array + * @param $so Model_Servie */ - // @todo Function Not Used - public static function servicelist($id,$paid=TRUE) { - // @todo need to add the db prefix - $invoices = DB::Query(Database::SELECT,' -SELECT i.id AS iid,i.due_date AS due FROM ab_invoice i,ab_invoice_item ii WHERE ii.invoice_id=i.id AND service_id=:id GROUP BY i.id - ') - ->param(':id',$id) - ->execute(); + public function add_service(Model_Service $so) { + $pdata = Period::details($so->recur_schedule,$so->product->price_recurr_day,$so->invoiced_to()+86400,FALSE,$so->product->price_recurr_strict); - $service_invoices = array(); - foreach ($invoices as $item) { - if ($bal = Invoice::balance($item['iid']) OR $paid) { - $service_invoices[$item['iid']]['id'] = $item['iid']; - $service_invoices[$item['iid']]['total'] = $bal; - $service_invoices[$item['iid']]['due'] = $item['due']; - } + $iio = ORM::factory('Invoice_Item'); + $iio->service_id = $so->id; + $iio->product_id = $so->product_id; + $iio->quantity = $pdata['prorata']; + $iio->price_base = $so->price(); + $iio->recurring_schedule = $so->recur_schedule; + $iio->date_start = $pdata['start_time']; + $iio->date_stop = $pdata['end_time']; + + // Service Billing + $iio->item_type = StaticList_ItemType::index('MAIN'); + + $this->io->add_item($iio); + + // Check if there are any charges + $c = ORM::factory('Charge') + ->where('service_id','=',$so->id) + ->where('processed','is',NULL); + + 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->quantity = $co->quantity; + $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'); + + $this->io->add_item($iio); } - return $service_invoices; + return $this; } - /** - * Return the total of amount outstanding for a service - * - * @param $id int Service ID - * @param $paid boolean Optionally only list the ones that are not paid. - * @return real Total amount outstanding - * @see Invoice::listservice() - */ - // @todo Function Not Used - public static function servicetotal($id,$paid=TRUE) { - $total = 0; + public function render($type,$section) { + switch ($type) { + case 'html': + switch ($section) { + case 'body': + return View::factory('invoice/user/view/body') + ->set('o',$this->io); + break; - foreach (Invoice::servicelist($id,$paid) as $item) - $total += $item['total']; + default: + throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section)); + } - return $total; - } + break; - /** - * Return the earliest due date of an outstanding invoice - * - * @param $id int Service ID - * @return datetime - */ - // @todo Function Not Used - public static function servicedue($id) { - $due = 0; - - foreach (Invoice::servicelist($id,FALSE) as $item) - if ($due < $item['due']) - $due = $item['due']; - - return $due; - } - - // @todo Function Not Used - public static function balance($id) { - return ORM::factory('Invoice',$id)->due(); + default: + throw HTTP_Exception::factory(501,'Unknown render type :type',array(':type'=>$type)); + } } /** diff --git a/modules/invoice/classes/Model/Invoice.php b/modules/invoice/classes/Model/Invoice.php index 66b0134c..3ac5f0be 100644 --- a/modules/invoice/classes/Model/Invoice.php +++ b/modules/invoice/classes/Model/Invoice.php @@ -75,15 +75,95 @@ class Model_Invoice extends ORM_OSB implements Cartable { /** * Add an item to an invoice */ - public function add_item() { + public function add_item(Model_Invoice_Item $iio=NULL) { + // Just to check if an invoice is called from the DB, that it should have items with it. if ($this->loaded() and ! $this->invoice_items) - throw new Kohana_Exception('Need to load invoice_items?'); + throw HTTP_Exception::factory(501,'Need dto load invoice_items?'); - $c = count($this->invoice_items); + // @todo This is the old calling of this function, which should be removed + if (is_null($iio)) { + $c = count($this->invoice_items); - $this->invoice_items[$c] = ORM::factory('Invoice_Item'); + $this->invoice_items[$c] = ORM::factory('Invoice_Item'); - return $this->invoice_items[$c]; + return $this->invoice_items[$c]; + } + + array_push($this->invoice_items,$iio); + } + + /** + * Return the recurring schedules that are on an invoice + * + * @param $period Return an Array of items for that period + */ + public function items_periods($period=NULL) { + $result = array(); + + foreach ($this->items() as $ito) { + // We are only interested in item_type=0 + if ($ito->item_type != 0) + continue; + + if (is_null($period) AND ! in_array($ito->recurring_schedule,$result)) + array_push($result,$ito->recurring_schedule); + elseif ($ito->recurring_schedule == $period) + array_push($result,$ito); + } + + 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->items() 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->items() 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->items() as $iio) + if ($iio->service_id == $o->service_id) + $result += $iio->total(); + + return $format ? Currency::display($result) : $result; } /** diff --git a/modules/invoice/classes/Model/Invoice/Item.php b/modules/invoice/classes/Model/Invoice/Item.php index 2b85f970..3b3ff6e4 100644 --- a/modules/invoice/classes/Model/Invoice/Item.php +++ b/modules/invoice/classes/Model/Invoice/Item.php @@ -9,7 +9,7 @@ * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html */ -class Model_Invoice_Item extends ORM_OSB { +class Model_Invoice_Item extends ORM_OSB implements Invoicable { // Relationships protected $_belongs_to = array( 'product'=>array(), @@ -39,6 +39,16 @@ class Model_Invoice_Item extends ORM_OSB { private $subitems = array(); private $subitems_loaded = FALSE; + /** INTERFACE REQUIREMENTS **/ + 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)); + } + public function __construct($id = NULL) { // Load our model. parent::__construct($id); @@ -71,7 +81,7 @@ class Model_Invoice_Item extends ORM_OSB { } // Sum up the tax that applies to this invoice item - public function tax() { + public function tax($format=FALSE) { $result = 0; foreach ($this->invoice_item_tax->find_all() as $iit) @@ -81,7 +91,9 @@ class Model_Invoice_Item extends ORM_OSB { if (! $result) $result += round($this->price_base*$this->quantity*.1,2); - return Currency::round($result); + $result = Currency::round($result); + + return $format ? Currency::display($result) : $result; } public function tax_items() { @@ -98,8 +110,10 @@ class Model_Invoice_Item extends ORM_OSB { } // This total of this item before discounts and taxes - public function subtotal() { - return Currency::round($this->price_base*$this->quantity); + public function subtotal($format=FALSE) { + $result = Currency::round($this->price_base*$this->quantity); + + return $format ? Currency::display($result) : $result; } // The total of all discounts @@ -107,8 +121,10 @@ class Model_Invoice_Item extends ORM_OSB { return Currency::round($this->discount_amt); } - public function total() { - return Currency::round($this->subtotal()+$this->tax()-$this->discount()); + public function total($format=FALSE) { + $result = Currency::round($this->subtotal()+$this->tax()-$this->discount()); + + return $format ? Currency::display($result) : $result; } /** diff --git a/modules/invoice/views/invoice/user/view.php b/modules/invoice/views/invoice/user/view.php index 6b36df10..e3b6a437 100644 --- a/modules/invoice/views/invoice/user/view.php +++ b/modules/invoice/views/invoice/user/view.php @@ -20,7 +20,7 @@
-
+
Tax Invoice
id(); ?>
Issue Date
@@ -45,112 +45,54 @@

Charge Details

-
-
+ + + items_periods() as $rs) : ?> + - items_index('period') as $rs => $items) : ?> -
- - - -
+ items_periods($rs) as $iio) : ?> + service_id) : ?> + + + + + + + + + + + + + - -
-
 service_id),$iio->service->id()).' '.$iio->service->service_name(); ?> 
service_items_total($iio,TRUE); ?>
  invoice_line(),$iio->display('id')); ?>
subtotal(TRUE); ?>
 
- - $service_id) : - $i = 0; - $lp = NULL; - $ito_tax = NULL; - - foreach ($o->items_service($service_id) as $ito) { - $ito_tax = $ito; - // Our first line we show the Service Details - if ($ito->item_type == 0 AND $ito->product_id != $lp) { - $lp = $ito->product_id; ?> - - - - - - - - - - - - - - - - - - - - discount_amt) { ?> - - - - - - - - - - - - - - - - - - + service_items_extra($iio)) : ?> + + + + + + + + -
service_id),$ito->service->id()); ?>product->title(),$ito->service->name()); ?> (product_id; ?>)items_service_total($ito->service_id)) : ' ');?>
 trannum();?>name();?>detail();?>period();?>subtotal());?> 
 (items_service_discount($ito->service_id));?>)
 items_service_tax($ito->service_id));?> 
  invoice_line(),$eiio->display('id')); ?>
subtotal(TRUE); ?>
 
-
+ +   +   + +
service_items_tax($iio,TRUE); ?>
+   + + + + + + - - items_index('account')) : ?> - - -
- - - items_index('account') as $id => $ito) : ?> - - - - - - - - - - - - - - - - - -
 trannum();?>name();?>detail();?>period();?>subtotal());?> 
 items_service_tax($ito->service_id));?> 
-
- - -
-
+ +
@@ -162,7 +104,7 @@
-
+
Sub Total
subtotal(TRUE); ?>
@@ -191,11 +133,12 @@ -
Total This Invoice:
+
Total Invoice:
total(TRUE); ?>
+ -
Total Outstanding This Account:
+
Account Due:
account->invoices_due_total(NULL,TRUE); ?>
@@ -205,4 +148,5 @@
-id),'Download detailed invoice',array('class'=>'btn btn-primary pull-right')); ?> +
+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 new file mode 100644 index 00000000..8f0bee7c --- /dev/null +++ b/modules/invoice/views/invoice/user/view/body.php @@ -0,0 +1,36 @@ + + + items_periods() as $rs) : ?> + + + items_periods($rs) as $iio) : ?> + service_id) : ?> + + + + + + + + + + + + + + service_items_extra($iio)) : ?> + + + + + + + + + + + + + + +
 service->service_name(); ?> 
service_items_total($iio,TRUE); ?>
 invoice_line(); ?>
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 6149db1f..6970a37f 100644 --- a/modules/invoice/views/invoice/user/view/pay.php +++ b/modules/invoice/views/invoice/user/view/pay.php @@ -1,7 +1,9 @@ +
id); + 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::close('cart/add'); ?> -Add to cart for payment: - +
diff --git a/modules/product/classes/Controller/Admin/Product.php b/modules/product/classes/Controller/Admin/Product.php index fb1a9dbe..a8abc48c 100644 --- a/modules/product/classes/Controller/Admin/Product.php +++ b/modules/product/classes/Controller/Admin/Product.php @@ -185,6 +185,9 @@ $(document).ready(function() { public function action_view() { $po = ORM::factory('Product',$this->request->param('id')); + if (! $po->loaded()) + throw HTTP_Exception::factory(403,'Product either doesnt exist, or you are not authorised to see it'); + Block::factory() ->title(sprintf('%s: %s',_('Current Services Using this Product'),$po->title())) ->title_icon('icon-th-list') diff --git a/modules/service/views/service/admin/update.php b/modules/service/views/service/admin/update.php index c44d0719..951edfaa 100644 --- a/modules/service/views/service/admin/update.php +++ b/modules/service/views/service/admin/update.php @@ -34,22 +34,6 @@ Taxable taxable); ?> - - Type - recur_type); ?> - - - User Can Change Schedule - recur_schedule_change); ?> - - - User Can Cancel - recur_cancel); ?> - - - User Can Modify - recur_modify); ?> - Suspend Billing suspend_billing); ?> diff --git a/modules/service/views/service/user/view.php b/modules/service/views/service/user/view.php index 5b0108a4..6e8a6f66 100644 --- a/modules/service/views/service/user/view.php +++ b/modules/service/views/service/user/view.php @@ -84,7 +84,12 @@ 'due(TRUE)'=>'Due', )) ->prepend(array( - 'id'=>array('url'=>URL::link('user','invoice/download/')), + 'id'=>array('url'=>URL::link('user','invoice/view/')), )); ?> + +
+ Next Invoice Charges + add_service($o)->render('html','body'); ?> +