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 .= '';
// @todo To limit to Reseller accounts only
foreach ($eo->export_module->find_all() as $emo)
- $output .= sprintf('- %s
',$c++ ? '' : 'active',$emo->id,ucfirst($emo->module->module()->object_name()));
+ $output .= sprintf('- %s
',$c++ ? '' : 'active',$emo->id,ucfirst($emo->module->instance()->object_name()));
$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 @@
-
-
-
-
-
-
- logo(); ?> |
- name(); ?> address(); ?> contacts(); ?> |
-
-
- |
- |
-
-
-
- TAX INVOICE |
- id(); ?> |
-
-
- Issue Date |
- display('date_orig'); ?> |
-
-
- Due Date |
- display('due_date'); ?> |
-
-
- Current Charges |
- total(TRUE); ?> |
-
-
- Payments Received to Date |
- payments_total(TRUE); ?> |
-
-
- Total Charges Due This Invoice |
- due(TRUE); ?> |
-
-
- |
-
- |
-
-
-
-
- Charges Detail: |
-
- items_service_periods() as $rs => $items) { ?>
-
- + |
-
- |
- |
-
- Other Items |
-
- |
-
-
- |
-
-
-
-
- items_services($items) as $sid => $ito) { ?>
-
-
-
- id(); ?> |
- service_name(); ?> (product_id; ?>) |
- items_service_total($so->id));?> |
-
-
-
- items_service($sid) as $ito) { ?>
-
-
- |
- trannum();?> |
- name();?> |
- detail();?> |
- period();?> |
- subtotal());?> |
-
-
-
- discount_amt) { ?>
-
- |
- |
- (items_service_discount($so->id));?>) |
-
-
-
-
- |
- |
- items_service_tax($so->id));?> |
-
-
-
-
-
-
-
- |
-
-
-
- Sub Total of Items: |
- subtotal(TRUE); ?> |
-
- total_discounts()) { ?>
-
- Discounts: |
- (total_discounts(TRUE); ?>) |
-
-
-
- Taxes Included: |
-
- tax_summary() as $tid => $amount) {
- $m = ORM::factory('Tax',$tid);
- ?>
-
- |
- 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_id),$iio->service->id()).' ' : '').$iio->title(); ?> |
+ |
+ service_items_total($iio,TRUE); ?> |
+
+
+ service_items($iio)) : ?>
+
+
+ |
+ |
+ invoice_line().($show_id ? " ({$eiio->trannum()})" : ''); ?> |
+ total(TRUE); ?> |
+ |
+
+
+
+
+ service_items_extra($iio)) : ?>
+
+
+ |
+ invoice_line().($show_id ? " ({$eiio->trannum()})" : ''); ?> |
+ total(TRUE); ?> |
+ |
+
+
+
+
+
+
+ |
+ title(); ?> |
+ |
+ service_items_total($iio,TRUE); ?> |
+
+
+
+
+
+
+
+
+
+ items_periods(NULL)) : ?>
- items_periods($rs) as $iio) : ?>
- service_id) : ?>
-
- |
- service_id),$iio->service->id()).' ' : '').$iio->title(); ?> |
- |
- service_items_total($iio,TRUE); ?> |
-
-
- service_items($iio)) : ?>
-
-
- |
- |
- invoice_line().($show_id ? " ($eiio->id)" : ''); ?> |
- total(TRUE); ?> |
- |
-
-
-
-
- service_items_extra($iio)) : ?>
-
-
- |
- 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;