diff --git a/application/classes/company.php b/application/classes/company.php
index 1188832a..8a7a5fae 100644
--- a/application/classes/company.php
+++ b/application/classes/company.php
@@ -50,6 +50,10 @@ class Company {
return Config::instance()->so->site_details('fax');
}
+ public static function email() {
+ return Config::instance()->so->site_details('email');
+ }
+
public static function contacts() {
return 'Tel: '.static::phone();
}
diff --git a/application/classes/currency.php b/application/classes/currency.php
index 0850b702..a3a9153f 100644
--- a/application/classes/currency.php
+++ b/application/classes/currency.php
@@ -12,8 +12,12 @@
class Currency {
public static function display($amount) {
// @todo $cid and therefore precision should come from a global session value.
+ return Num::format(Currency::round($amount),2,TRUE);
+ }
+
+ public static function round($amount) {
// @todo This rounding needs to be system configurable.
- return Num::format(round($amount,2),2,TRUE);
+ return Num::round($amount,2);
}
}
?>
diff --git a/application/classes/lnapp/systemmessage.php b/application/classes/lnapp/systemmessage.php
index 237bf01c..909f7744 100644
--- a/application/classes/lnapp/systemmessage.php
+++ b/application/classes/lnapp/systemmessage.php
@@ -69,7 +69,7 @@ class lnApp_SystemMessage extends HTMLRender {
/**
* Render an image for the System Message
*/
- private static function image($type,$raw=false,$big=false,$alt='') {
+ public static function image($type,$raw=false,$big=false,$alt='') {
$mediapath = Route::get(static::$_media_path);
switch ($type) {
diff --git a/application/classes/ormosb.php b/application/classes/ormosb.php
index 6c8149a2..e1a4097d 100644
--- a/application/classes/ormosb.php
+++ b/application/classes/ormosb.php
@@ -129,7 +129,7 @@ abstract class ORMOSB extends ORM {
if ($this->_changed)
foreach ($this->_changed as $c)
if ($this->_table_columns[$c]['data_type'] == 'blob') {
- $this->$c = $this->blob($this->$c,TRUE);
+ $this->_object[$c] = $this->blob($this->_object[$c],TRUE);
// We need to reset our auto_convert flag
if (isset($this->_table_columns[$c]['auto_convert']))
diff --git a/modules/invoice/classes/controller/user/invoice.php b/modules/invoice/classes/controller/user/invoice.php
index 30c6c479..d7c71ef8 100644
--- a/modules/invoice/classes/controller/user/invoice.php
+++ b/modules/invoice/classes/controller/user/invoice.php
@@ -53,6 +53,7 @@ class Controller_User_Invoice extends Controller_TemplateDefault_User {
if (! $io->loaded() OR (! Auth::instance()->authorised($io->account_id,$io->affiliate_id) AND ! in_array($this->ao->affiliate->id,$io->service_affiliates()))) {
$this->template->content = 'Unauthorised or doesnt exist?';
+
return FALSE;
}
@@ -60,6 +61,58 @@ class Controller_User_Invoice extends Controller_TemplateDefault_User {
->set('mediapath',Route::get('default/media'))
->set('io',$io);
+ if (! $io->status) {
+ // Add a gribber popup
+ // @todo Make a gribber popup a class on its own.
+ Style::add(array(
+ 'type'=>'file',
+ 'data'=>'css/jquery.gritter.css',
+ 'media'=>'screen',
+ ));
+ Script::add(array(
+ 'type'=>'file',
+ 'data'=>'js/jquery.gritter-1.5.js',
+ ));
+ Script::add(array(
+ 'type'=>'stdin',
+ 'data'=>sprintf(
+'$(document).ready(function() {
+ $.extend($.gritter.options, {
+ fade_in_speed: "medium",
+ fade_out_speed: 2000,
+ time: "3000",
+ sticky: false,
+ });
+ $.gritter.add({
+ title: "%s",
+ text: "%s",
+ image: "%s",
+});});',
+ 'Cancelled','Invoice CANCELLED',URL::site().SystemMessage::image('info',true)
+ )
+ ));
+
+ Style::add(array(
+ 'type'=>'stdin',
+ 'data'=>'
+#watermark {
+ color: #800000;
+ font-size: 4em;
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(-45deg);
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ z-index: 1;
+ left:350px;
+ top:-200px;
+}
+ '));
+
+ $output .= '
';
+ }
+
Block::add(array(
'title'=>sprintf('%s: %s - %s',_('Invoice'),$io->refnum(),$io->account->name()),
'body'=>$output,
diff --git a/modules/invoice/classes/model/invoice.php b/modules/invoice/classes/model/invoice.php
index eba515e0..4469667b 100644
--- a/modules/invoice/classes/model/invoice.php
+++ b/modules/invoice/classes/model/invoice.php
@@ -49,6 +49,33 @@ class Model_Invoice extends ORMOSB {
// Items belonging to an invoice
private $invoice_items = array();
+ private $subitems_load = FALSE;
+
+ public function __construct($id = NULL) {
+ // Load our model.
+ parent::__construct($id);
+
+ return $this->load_sub_items();
+ }
+
+ private function load_sub_items() {
+ // Load our sub items
+ if (! $this->subitems_load AND $this->loaded()) {
+ $this->invoice_items = $this->invoice_item->find_all()->as_array();
+ $this->subitems_load = TRUE;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return a list of invoice items for this payment.
+ */
+ public function items() {
+ $this->load_sub_items();
+
+ return $this->invoice_items;
+ }
/**
* Display the Invoice Number
@@ -69,20 +96,13 @@ class Model_Invoice extends ORMOSB {
*/
public function due($format=FALSE) {
// If the invoice is active calculate the due amount
- $result = $this->status ? round($this->total()-$this->payments_total(),2) : 0;
+ $result = $this->status ? $this->total()-$this->payments_total() : 0;
- return $format ? Currency::display($result) : $result;
- }
+ // @todo This should not be required.
+ if ((Currency::round($result) == .01) or Currency::round($result) == .02)
+ $result = 0;
- /**
- * Return a list of invoice items for this invoice.
- */
- public function items() {
- // If we havent been changed, we'll load the records from the DB.
- if ($this->loaded() AND ! $this->_changed)
- return $this->invoice_item->order_by('service_id,item_type,module_id')->find_all()->as_array();
- else
- return $this->invoice_items;
+ return $format ? Currency::display($result) : Currency::round($result);
}
/**
@@ -91,31 +111,28 @@ class Model_Invoice extends ORMOSB {
public function subtotal($format=FALSE) {
$result = 0;
- // @todo This rounding should be a system config.
foreach ($this->items() as $ito)
- $result += round($ito->subtotal(),2);
+ $result += $ito->subtotal();
- return $format ? Currency::display($result) : $result;
+ return $format ? Currency::display($result) : Currency::round($result);
}
public function discount($format=FALSE) {
$result = 0;
- // @todo This rounding should be a system config.
foreach ($this->items() as $ito)
- $result += round($ito->discount_amt,2);
+ $result += $ito->discount_amt;
- return $format ? Currency::display($result) : $result;
+ return $format ? Currency::display($result) : Currency::round($result);
}
public function tax($format=FALSE) {
$result = 0;
- // @todo This rounding should be a system config.
foreach ($this->items() as $ito)
- $result += round($ito->tax(),2);
+ $result += $ito->tax();
- return $format ? Currency::display($result) : $result;
+ return $format ? Currency::display($result) : Currency::round($result);
}
/**
@@ -124,18 +141,17 @@ class Model_Invoice extends ORMOSB {
public function total($format=FALSE) {
$result = 0;
- // @todo This rounding should be a system config.
foreach ($this->items() as $ito)
- $result += round($ito->total(),2);
+ $result += $ito->total();
// Reduce by any credits
- $result -= round($this->credit_amt,2);
+ $result -= $this->credit_amt;
- return $format ? Currency::display($result) : $result;
+ return $format ? Currency::display($result) : Currency::round($result);
}
public function payments() {
- return ($this->loaded() AND ! $this->_changed) ? $this->payment_item->find_all() : array();
+ return $this->payment_item->find_all();
}
public function payments_total($format=FALSE) {
@@ -144,7 +160,44 @@ class Model_Invoice extends ORMOSB {
foreach ($this->payments() as $po)
$result += $po->alloc_amt;
- return $format ? Currency::display($result) : $result;
+ return $format ? Currency::display($result) : Currency::round($result);
+ }
+
+ /**
+ * 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->items() 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();
}
/**
@@ -152,6 +205,7 @@ class Model_Invoice extends ORMOSB {
*
* 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)
@@ -164,21 +218,33 @@ class Model_Invoice extends ORMOSB {
return $result;
}
- /**
- * Return all invoice items for a service optionally by recurring schedule
- */
- public function items_service($sid,$rs=NULL) {
+// @todo to retire
+ public function items_invoice() {
$result = array();
$items = $this->items();
- Sort::MAsort($items,'item_type');
foreach ($items as $ito)
- if ($ito->service_id == $sid AND (is_null($rs) OR $ito->recurring_schedule == $rs))
- array_push($result,$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
*
@@ -207,6 +273,7 @@ class Model_Invoice extends ORMOSB {
*
* We summaries based on product.
*/
+// @todo
public function items_summary() {
$result = array();
@@ -303,6 +370,7 @@ class Model_Invoice extends ORMOSB {
}
public function min_due($date) {
+ // @todo This should be a DB confirm item
return ($date < time()) ? time()+Kohana::config('config.invoice.min_due_days')*86400 : $date;
}
diff --git a/modules/invoice/classes/model/invoice/item.php b/modules/invoice/classes/model/invoice/item.php
index 19b68f25..9b6ff5eb 100644
--- a/modules/invoice/classes/model/invoice/item.php
+++ b/modules/invoice/classes/model/invoice/item.php
@@ -63,41 +63,83 @@ class Model_Invoice_Item extends ORMOSB {
if (! $result)
$result += round($this->subtotal() *.1,2);
- return $result;
+ return Currency::round($result);
}
// This total of this item before discounts and taxes
public function subtotal() {
- return $this->price_base*$this->quantity;
+ return Currency::round($this->price_base*$this->quantity);
}
// The total of all discounts
public function discount() {
- return $this->discount_amt;
+ return Currency::round($this->discount_amt);
}
public function total() {
- // @todo This rounding should be a system config
- return round($this->subtotal()+$this->tax()-$this->discount(),2);
+ return Currency::round($this->subtotal()+$this->tax()-$this->discount());
}
+ /**
+ * Name for an invoice item
+ */
public function name() {
- return $this->product_name ? $this->product_name : ($this->item_type == 0 ? _('Service') : _('Other'));
+ 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() {
- return ($this->item_type == 0 OR $this->quantity == 1) ? HTML::nbsp('') : sprintf('%s@%3.2f',$this->quantity,$this->price_base);
+ 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 126: return _('Rounding');
+
+ case 127: return _('Late Payment Fee');
+
+ default: '';
+ }
}
public function invoice_detail_items() {
- // @todo To fix up this function - needs to be better formed.
- if ($this->item_type == 5)
- return $this->charge->details('invoice_detail_items');
-
- if ($this->item_type != 0)
- return;
-
- return $this->service->details('invoice_detail_items');
+ switch ($this->item_type) {
+ case 0:
+ return $this->service->details('invoice_detail_items');
+ case 4:
+ return array('Charge'=>_('Service Connection Fee'));
+ case 5:
+ return $this->charge->details('invoice_detail_items');
+ default:
+ return array('Item'=>$this->item_type);
+ }
}
public function save(Validation $validation = NULL) {
@@ -107,6 +149,7 @@ class Model_Invoice_Item extends ORMOSB {
// Need to save the taxes and discounts associated with the invoice_item
// @todo This needs to only check if the records have previously been saved, and update them.
if ($this->saved()) {
+//@todo When updating a record, we shouldnt create a new tax item.
$iito = ORM::factory('invoice_item_tax');
// Save TAX details
diff --git a/modules/invoice/views/invoice/user/view.php b/modules/invoice/views/invoice/user/view.php
index cb828d9e..82f46c69 100644
--- a/modules/invoice/views/invoice/user/view.php
+++ b/modules/invoice/views/invoice/user/view.php
@@ -5,7 +5,14 @@
@@ -42,102 +49,162 @@
|
-
+
Charges Detail: |
- items_service_periods() as $rs => $items) { ?>
+ items_index('period') as $rs => $items) { ?>
uri(array('file'=>'img/toggle-closed.png')),array('alt'=>'+')); ?> |
|
- |
-
- Other Items |
-
+ |
|
+
+ Other Items |
+
|
-
- items_services($items) as $sid => $ito) { ?>
-
-
- service_id,$ito->service->id()); ?> |
- product->name(),$ito->service->name()); ?> (product_id; ?>) |
- items_service_total($ito->service_id));?> |
-
-
-
- items_service($sid) as $ito) { ?>
-
-
- |
- trannum();?> |
- name();?> |
- detail();?> |
- period();?> |
- subtotal());?> |
-
-
+ $service_id) {
+ $i = 0;
+ $lp = NULL;
+ $ito_tax = NULL;
+ foreach ($io->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; ?>
+
+
+ service_id,$ito->service->id()); ?> |
+ product->name(),$ito->service->name()); ?> (product_id; ?>) |
+ items_service_total($ito->service_id)) : ' ');?> |
+
+
+
+
+
+ |
+ trannum();?> |
+ name();?> |
+ detail();?> |
+ period();?> |
+ subtotal());?> |
+
+
+
+ discount_amt) { ?>
+
+ |
+ |
+ (items_service_discount($ito->service_id));?>) |
+
+
+
-
- discount_amt) { ?>
-
- |
- |
- (items_service_discount($ito->service_id));?>) |
-
+
+
+
+ |
+ |
+ items_service_tax($ito->service_id));?> |
+
+
-
-
- |
- |
- items_service_tax($ito->service_id));?> |
-
-
|
+ |
-
- Sub Total of Items: |
- subtotal(TRUE); ?> |
-
- discount()) { ?>
-
- Discounts: |
- (discount(TRUE); ?>) |
-
+ items_index('account')) { ?>
+
+ uri(array('file'=>'img/toggle-closed.png')),array('alt'=>'+')); ?> |
+ Other Invoice Items |
+ |
+
+
+ |
+
+
+
+ items_index('account') as $id => $ito) { ?>
+
+ |
+ trannum();?> |
+ name();?> |
+ detail();?> |
+ period();?> |
+ subtotal());?> |
+
+
+
+ |
+ |
+ items_service_tax($ito->service_id));?> |
+
+
+
+
+
+ |
+ |
+
+
+
+ Sub Total of Items: |
+ subtotal(TRUE); ?> |
+
+
+ credit_amt) { ?>
+
+
+ Credits Received: |
+ display('credit_amt'); ?> |
+
+
+
+ discount()) { ?>
+
+
+ Discounts: |
+ (discount(TRUE); ?>) |
+
+
+
+
Taxes Included: |
- tax_summary() as $tid => $amount) {
- $m = ORM::factory('tax',$tid);
- ?>
+ tax_summary() as $tid => $amount) {
+ $m = ORM::factory('tax',$tid); ?>
|
- description; ?> |
- |
+ description; ?> |
+ |
-
+
+
- Total This Invoice: |
- total(TRUE); ?> |
+ Total This Invoice: |
+ total(TRUE); ?> |
+
+
- Total Outstanding This Account: |
- account->invoices_due_total(NULL,TRUE); ?> |
+ Total Outstanding This Account: |
+ account->invoices_due_total(NULL,TRUE); ?> |
+
diff --git a/modules/payment/classes/model/payment.php b/modules/payment/classes/model/payment.php
index d9c99a3f..219bc7d2 100644
--- a/modules/payment/classes/model/payment.php
+++ b/modules/payment/classes/model/payment.php
@@ -167,7 +167,7 @@ class Model_Payment extends ORMOSB {
if ($pio->changed()) {
$old_pio = ORM::factory('payment_item',$pio->id);
- if ($it = $pio->invoice->due()+ORM::factory('payment_item',$pio->id)->alloc_amt-$pio->alloc_amt < 0)
+ if (($it = $pio->invoice->due()+ORM::factory('payment_item',$pio->id)->alloc_amt-$pio->alloc_amt) < 0)
$msg .= ($msg ? ' ' : '').sprintf('Invoice %s over allocated by %3.2f.',$pio->invoice_id,$it);
}
diff --git a/modules/service/classes/controller/admin/service.php b/modules/service/classes/controller/admin/service.php
index abe810d9..5b38faff 100644
--- a/modules/service/classes/controller/admin/service.php
+++ b/modules/service/classes/controller/admin/service.php
@@ -24,6 +24,7 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
'listwebservices'=>TRUE,
'listinvoicesoon'=>TRUE,
'update'=>TRUE,
+ 'view'=>TRUE,
);
public function action_autolist() {
@@ -591,5 +592,35 @@ class Controller_Admin_Service extends Controller_TemplateDefault_Admin {
'data'=>'css/dhtml.calendar.css',
));
}
+
+ public function action_view() {
+ list($id,$output) = Table::page(__METHOD__);
+
+ $so = ORM::factory('service',$id);
+
+ if (! $so->loaded() OR ! Auth::instance()->authorised($so->account_id,$so->affiliate_id)) {
+ $this->template->content = 'Unauthorised or doesnt exist?';
+ return FALSE;
+ }
+
+ $loutput = '';
+
+ $loutput .= View::factory($this->viewpath())
+ ->set('so',$so);
+
+ Block::add(array(
+ 'title'=>sprintf('Transaction History for %s: %s',$so->id(),$so->name()),
+ 'body'=>$loutput,
+ ));
+
+ $output .= View::factory('service/user/view')
+ ->set('so',$so);
+
+ Block::add(array(
+ 'title'=>sprintf('%s: %s',$so->id(),$so->service_name()),
+ 'body'=>$output,
+ ));
+
+ }
}
?>
diff --git a/modules/service/classes/model/service.php b/modules/service/classes/model/service.php
index c382eaef..3514d3e7 100644
--- a/modules/service/classes/model/service.php
+++ b/modules/service/classes/model/service.php
@@ -20,6 +20,7 @@ class Model_Service extends ORMOSB {
'service_billing'=>array('far_key'=>'account_billing_id','foreign_key'=>'id'),
);
protected $_has_many = array(
+ 'invoice_item'=>array('far_key'=>'id'),
'invoice'=>array('through'=>'invoice_item'),
);
protected $_belongs_to = array(
@@ -118,6 +119,10 @@ class Model_Service extends ORMOSB {
return $plugin->admin_update();
}
+ public function transactions() {
+ return $this->invoice_item->order_by('date_start,date_stop')->find_all();
+ }
+
/** LIST FUNCTIONS **/
private function _list_active() {
diff --git a/modules/service/views/service/admin/view.php b/modules/service/views/service/admin/view.php
new file mode 100644
index 00000000..2c540a05
--- /dev/null
+++ b/modules/service/views/service/admin/view.php
@@ -0,0 +1,41 @@
+
+
+
+ Transactions for this service |
+
+
+ |
+
+
+ ID |
+ Invoice |
+ Product |
+ IT |
+ RS |
+ Start |
+ Stop |
+ Desc |
+ Charge |
+ Quantity |
+
+ transactions() as $iio) { ?>
+ product_id) AND $lp != $iio->product_id) {
+ $lp = $iio->product_id; ?>
+
+ product_id,$iio->product->name()); ?> |
+
+
+
+ id; ?> |
+ invoice_id,$iio->invoice_id); ?> |
+ display('product_id'); ?> |
+ display('item_type'); ?> |
+ display('recurring_schedule'); ?> |
+ display('date_start'); ?> |
+ display('date_stop'); ?> |
+ display('product_name'); ?> |
+ display('price_base'); ?> |
+ display('quantity'); ?> |
+
+
+
|