diff --git a/application/classes/controller/lnapp/templatedefault.php b/application/classes/controller/lnapp/templatedefault.php index 83620d1b..eb77e050 100644 --- a/application/classes/controller/lnapp/templatedefault.php +++ b/application/classes/controller/lnapp/templatedefault.php @@ -195,7 +195,8 @@ abstract class Controller_lnApp_TemplateDefault extends Controller_Template { parent::after(); // Generate and check the ETag for this file - $this->response->check_cache(NULL,$this->request); + if (Kohana::$environment === Kohana::PRODUCTION) + $this->response->check_cache(NULL,$this->request); } /** diff --git a/application/classes/model/module.php b/application/classes/model/module.php index 0d72a32e..a7bb8f68 100644 --- a/application/classes/model/module.php +++ b/application/classes/model/module.php @@ -16,10 +16,10 @@ class Model_Module extends ORMOSB { // Relationships protected $_has_many = array( - 'module_method'=>array(), + 'module_method'=>array('far_key'=>'id'), ); protected $_has_one = array( - 'record_id'=>array() + 'record_id'=>array('far_key'=>'id'), ); protected $_sorting = array( diff --git a/application/classes/ormosb.php b/application/classes/ormosb.php index f9105477..cd282b62 100644 --- a/application/classes/ormosb.php +++ b/application/classes/ormosb.php @@ -31,16 +31,6 @@ abstract class ORMOSB extends ORM { ); } - /** - * Our child models should provide an invoice display, this is shown - * on printed invoices. - * - * @todo This is no longer used I think? - */ - public function invoice_display() { - throw new Kohana_Exception(':module has not configured an :method, but has made the call',array(':module'=>get_class($this),'method'=>__METHOD__)); - } - /** * This function will enhance the [Validate::filter], since it always passes * the value as the first argument and sometimes functions need that to not @@ -101,5 +91,20 @@ abstract class ORMOSB extends ORM { return TRUE; } + + /** + * Generate a view path to help View::factory() calls + * + * The purpose of this method is to ensure that we have a consistant + * layout for our view files, including those that are needed by + * plugins + * + * @param string Plugin Name (optional) + */ + public function viewpath($plugin='') { + $request = Request::current(); + + return $plugin ? sprintf('%s/%s/%s/%s',$request->controller(),$request->directory(),$plugin,$request->action()) : sprintf('%s/%s/%s',$request->controller(),$request->directory(),$request->action()); + } } ?> diff --git a/application/classes/staticlist/recurschedule.php b/application/classes/staticlist/recurschedule.php index 079bc091..424bd007 100644 --- a/application/classes/staticlist/recurschedule.php +++ b/application/classes/staticlist/recurschedule.php @@ -36,7 +36,7 @@ class StaticList_RecurSchedule extends StaticList { * * @uses product */ - public static function form($name,$product='',$addblank=FALSE) { + public static function form($name,$product='',$default='',$addblank=FALSE) { if (empty($product)) throw new Kohana_Exception('Product is a required field for :method',array(':method'=>__METHOD__)); @@ -50,7 +50,7 @@ class StaticList_RecurSchedule extends StaticList { $x[$term] .= sprintf(' + %s %s',Currency::display($price['price_setup']),_('Setup')); } - return Form::select($name,$x,$product->price_recurr_default); + return Form::select($name,$x,$default); } } ?> diff --git a/application/classes/staticlist/recurtype.php b/application/classes/staticlist/recurtype.php new file mode 100644 index 00000000..8982629b --- /dev/null +++ b/application/classes/staticlist/recurtype.php @@ -0,0 +1,29 @@ +_('Bill on Aniversary Date of Subscription'), + 1=>_('Bill on Fixed Schedule'), + ); + } + + public static function factory() { + return new StaticList_RecurType; + } + + public static function display($value) { + return static::_display($value); + } +} +?> diff --git a/application/config/config.php b/application/config/config.php index 3553db50..31c102fd 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -11,6 +11,7 @@ */ return array( 'cache_type' => 'file', + 'currency_format' => '2', 'date_format' => 'd-M-Y', 'email_admin_only'=> array( 'adsl_traffic_notice'=>array('deon@c5t61p.leenooks.vpn'=>'Deon George'), diff --git a/application/config/invoice.php b/application/config/invoice.php index f8fbf4b6..f0a7b615 100644 --- a/application/config/invoice.php +++ b/application/config/invoice.php @@ -10,6 +10,6 @@ * @license http://dev.osbill.net/license.html */ return array( - 'driver' => 'TCPDF', + 'driver' => 'default', ); ?> diff --git a/application/media/img/gnome-pdf.png b/application/media/img/gnome-pdf.png new file mode 100644 index 00000000..f2c8b21f Binary files /dev/null and b/application/media/img/gnome-pdf.png differ diff --git a/modules/account/classes/model/account.php b/modules/account/classes/model/account.php index 1cebeea4..c42650c1 100644 --- a/modules/account/classes/model/account.php +++ b/modules/account/classes/model/account.php @@ -75,23 +75,20 @@ class Model_Account extends Model_Auth_UserDefault { * Get a list of all invoices for this account */ public function invoices() { - $return = array(); - - foreach ($this->invoice->distinct('id')->find_all() as $invoice) - $return[$invoice->id] = $invoice; - - return $return; + return $this->invoice->distinct('id')->find_all(); } /** * Get a list of due invoices for this account + * + * @param int Date (in secs) to only retrieve invoices prior to this date */ - public function invoices_due() { + public function invoices_due($date=NULL) { $return = array(); - foreach ($this->invoices() as $invoice) - if ($invoice->due()) - $return[$invoice->id] = $invoice; + foreach ($this->invoices() as $io) + if ((is_null($date) OR $io->date_orig < $date) AND $io->due()) + $return[$io->id] = $io; return $return; } @@ -99,16 +96,13 @@ class Model_Account extends Model_Auth_UserDefault { /** * Calculate the total of invoices due for this account */ - public function invoices_due_total($format=FALSE) { + public function invoices_due_total($date=NULL,$format=FALSE) { $result = 0; - foreach ($this->invoices_due() as $invoice) - $result += $invoice->due(); + foreach ($this->invoices_due($date) as $io) + $result += $io->due(); - if ($format) - return Currency::display($result); - else - return $result; + return $format ? Currency::display($result) : $result; } } ?> diff --git a/modules/adsl/classes/adsl.php b/modules/adsl/classes/adsl.php index 26edb2e4..73dd1b10 100644 --- a/modules/adsl/classes/adsl.php +++ b/modules/adsl/classes/adsl.php @@ -28,22 +28,6 @@ class ADSL { return new ADSL; } - /** - * Return the additional information used by product_view - */ - public function product_view($data) { - // @todo - this test shouldnt be required - if (preg_match('/^a:/',$data)) - throw new Kohana_Exception('Data shouldnt be a serialized array'); - - $ao = ORM::factory('adsl_plan',$data); - - $output = View::factory('adsl/product_view') - ->set('record',$ao); - - return $output; - } - public function contract_view($data,$price_base,$price_setup) { // @todo - this test shouldnt be required if (preg_match('/^a:/',$data)) diff --git a/modules/adsl/classes/model/adsl/plan.php b/modules/adsl/classes/model/adsl/plan.php index 027c35d1..1b5ef076 100644 --- a/modules/adsl/classes/model/adsl/plan.php +++ b/modules/adsl/classes/model/adsl/plan.php @@ -37,23 +37,5 @@ class Model_ADSL_Plan extends ORMOSB { 'Currency::display'=>array(), ), ); - - /** - * Show the ADSL allowance as a peak/offpeak metric - */ - public function allowance($string=TRUE) { - $output = ADSL::allowance(array( - 'base_down_peak'=>$this->base_down_peak, - 'base_down_offpeak'=>$this->base_down_offpeak, - 'base_up_peak'=>$this->base_up_peak, - 'base_up_offpeak'=>$this->base_up_offpeak, - 'extra_down_peak'=>$this->extra_down_peak, - 'extra_down_offpeak'=>$this->extra_down_offpeak, - 'extra_up_peak'=>$this->extra_up_peak, - 'extra_up_offpeak'=>$this->extra_up_offpeak, - )); - - return $string ? implode('/',$output) : $output; - } } ?> diff --git a/modules/adsl/views/adsl/product_view.php b/modules/adsl/views/adsl/product_view.php deleted file mode 100644 index f31fbf03..00000000 --- a/modules/adsl/views/adsl/product_view.php +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -
Speeddisplay('speed'); ?>
 
 PeakOff Peak
Included Download Trafficbase_down_peak/1000; ?> GBbase_down_offpeak/1000; ?> GB
Extra Download Trafficdisplay('extra_down_peak'); ?>/GBdisplay('extra_down_offpeak'); ?>/GB
diff --git a/modules/charge/classes/model/charge.php b/modules/charge/classes/model/charge.php index 14e5c4f0..c60a4bee 100644 --- a/modules/charge/classes/model/charge.php +++ b/modules/charge/classes/model/charge.php @@ -11,14 +11,10 @@ * @license http://dev.osbill.net/license.html */ class Model_Charge extends ORMOSB { - protected $_formats = array( - 'amount'=>array('Currency::display'=>array()), + protected $_display_filters = array( + 'amount'=>array( + 'Currency::display',array(':value') + ), ); - - // Show our description on the invoice. - public function invoice_display() { - // @todo The rounding should be a global config - return sprintf('%s: %2s x %s (%s)',Config::date($this->date_orig),$this->quantity,$this->description,$this->display('amount')); - } } ?> diff --git a/modules/invoice/classes/controller/user/invoice.php b/modules/invoice/classes/controller/user/invoice.php index 86f1a6d4..1cce3806 100644 --- a/modules/invoice/classes/controller/user/invoice.php +++ b/modules/invoice/classes/controller/user/invoice.php @@ -56,7 +56,9 @@ class Controller_User_Invoice extends Controller_TemplateDefault { public function action_download($id) { $io = ORM::factory('invoice',$id); - return Invoice::instance()->pdf($io)->Output(sprintf('%s.pdf',$io->refnum()),'D'); + $this->response->body(Invoice::instance($io)->pdf()->Output(sprintf('%s.pdf',$io->refnum()),'D')); + $this->response->headers(array('Content-Type' => 'application/pdf')); + $this->auto_render = FALSE; } } ?> diff --git a/modules/invoice/classes/invoice.php b/modules/invoice/classes/invoice.php index 1f7dd829..1dd74afe 100644 --- a/modules/invoice/classes/invoice.php +++ b/modules/invoice/classes/invoice.php @@ -14,8 +14,12 @@ class Invoice { // This invoice Object private $io; - public static function instance() { - return new Invoice; + public function __construct($io) { + $this->io = $io; + } + + public static function instance($io) { + return new Invoice($io); } /** @@ -89,14 +93,10 @@ SELECT i.id AS iid,i.due_date AS due FROM ab_invoice i,ab_invoice_item ii WHERE /** * Generate a PDF invoice */ - public function pdf($io) { - $invoice_class = sprintf('invoice_pdf_%s',Kohana::config('invoice.driver')); + public function pdf() { + $invoice_class = sprintf('invoice_tcpdf_%s',Kohana::config('invoice.driver')); - if (! class_exists($invoice_class)) - throw new Kohana_Exception('Invoice class :class doesnt exist',array(':class'=>$invoice_class)); - - $this->io = $io; - $pdf = new $invoice_class($io); + $pdf = new $invoice_class($this->io); if ($pdf->getTemplate()) { $pagecount = $pdf->setSourceFile($pdf->getTemplate()); @@ -125,105 +125,21 @@ SELECT i.id AS iid,i.due_date AS due FROM ab_invoice i,ab_invoice_item ii WHERE $pdf->drawRemittenceStub(); $pdf->drawPaymentMethods(); - if ($this->io->billing_status !=1 && $this->io->suspend_billing != 1 && $this->io->due_date <= time()) - $pdf->drawInvoiceDueNotice(); - elseif($this->io->billing_status == 1) - $pdf->drawInvoicePaidNotice(); + if ($this->io->billing_status !=1 && $this->io->suspend_billing != 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(); + if ($this->io->account->invoices_due_total()) + $pdf->drawSummaryInvoicesDue(); - $pdf->drawSummaryLineItems(); -return; - #unset($pdf->itemsSummary); + $pdf->drawSummaryLineItems(); - # BEGIN loop for enumerating information in multiple ways on the invoice - $iteration = 0; - while ($pdf->drawLineItems_pre($iteration)) { - foreach ($this->sInvoiceItems() as $index => $items) { - # Get the date range if set - if (! empty($items['date_start']) && ! empty($items['date_stop'])) { - global $C_translate; - $C_translate->value('invoice','start',date(UNIX_DATE_FORMAT,$items['date_start'])); - $C_translate->value('invoice','stop',date(UNIX_DATE_FORMAT,$items['date_stop'])); - } + // Next Page + $pdf->drawDetailLineItems(); - $line = array( - 'name'=>$this->sLineItemDesc($index), - 'domain'=>$items['domain_name'], - 'amount'=>$items['price_base'], - 'sku'=>$items['sku'], - 'qty'=>$items['quantity'], - 'cost'=>$items['price_base'], - 'attr'=>$items['product_attr'], - 'price_type'=>$items['price_type'], - 'price_base'=>$items['price_base'], - 'item_type'=>$items['item_type'], - 'total_amt'=>$items['total_amt'] - ); - - if ($items['date_start'] && $items['date_stop']) - if ($items['date_start'] == $items['date_stop']) - $line['daterange'] = sprintf('%s',date(UNIX_DATE_FORMAT,$items['date_start'])); - else - $line['daterange'] = sprintf('%s - %s',date(UNIX_DATE_FORMAT,$items['date_start']),date(UNIX_DATE_FORMAT,$items['date_stop'])); - - $pdf->drawLineItems($db,$line,$this->getRecordAttr('id')); - - if ($items['price_setup']) { - $line = array( - 'name'=>sprintf('%s - %s',$this->sLineItemDesc($index),_('Setup Charge')), - 'amount'=>$items['price_setup'], - 'qty'=>'1', - 'sku'=>$items['sku'], - 'cost'=>$items['price_setup'], - 'price_base'=>$items['price_setup'], - 'price_type'=>999 - ); - - $pdf->drawLineItems($db,$line,$this->getRecordAttr('id')); - } - } - - if ($this->print['invoice']['discount_amt']) { - $line = array( - 'name'=>_('Discount'), - 'amount'=>-($this->print['invoice']['discount_amt']), - 'qty'=>'1', - 'cost'=>-($this->print['invoice']['discount_amt']), - 'price_base'=>-($this->print['invoice']['discount_amt']), - 'price_type'=>999); - - $pdf->drawLineItems($db,$line,$this->getRecordAttr('id')); - } - - if ($this->print['invoice']['tax_amt']) { - $rs = $db->Execute(sqlSelect($db,array('invoice_item_tax','tax'),'A.amount,B.description',sprintf('A.tax_id=B.id AND A.invoice_id=%s',$this->getRecordAttr('id')))); - if ($rs && $rs->RecordCount()) { - $taxes = array(); - - while (! $rs->EOF) { - if (! isset($taxes[$rs->fields['description']])) - $taxes[$rs->fields['description']] = $rs->fields['amount']; - else - $taxes[$rs->fields['description']] += $rs->fields['amount']; - - $rs->MoveNext(); - } - - foreach ($taxes as $txds => $txamt) { - $line = array('name'=>$txds,'amount'=>$txamt,'total_amt'=>$txamt,'price_type'=>999); - $pdf->drawLineItems($db,$line,$this->getRecordAttr('id')); - } - } - } - - # Increment the iteration - ++$iteration; - } - - # Custom functions: - $pdf->drawCustom(); + // Draw any Custom functions: + $pdf->drawCustom(); } } ?> diff --git a/modules/invoice/classes/invoice/pdf/tcpdf.php b/modules/invoice/classes/invoice/pdf/tcpdf.php deleted file mode 100644 index 8d21c980..00000000 --- a/modules/invoice/classes/invoice/pdf/tcpdf.php +++ /dev/null @@ -1,539 +0,0 @@ -Image($logo,$x,$y,$size); - } - - /** - * Draw the Company Address - */ - public function drawCompanyAddress() { - # Add the company address next to the logo - $x = 40; $y = 7; - - $this->SetFont('helvetica','B',10); - $this->SetXY($x,$y); $this->Cell(0,0,Config::sitename()); $y += 4; - - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,Company::taxid()); $y += 6; - - $this->SetXY($x,$y); $this->Cell(0,0,Company::street()); $y += 4; - $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s, %s %s',Company::city(),Company::state(),Company::pcode())); $y += 4; - - $y += 2; - $this->SetXY($x,$y); $this->Cell(0,0,'Phone:'); $this->SetXY($x+16,$y); $this->Cell(0,0,Company::phone()); $y += 4; - $this->SetXY($x,$y); $this->Cell(0,0,'Fax:'); $this->SetXY($x+16,$y); $this->Cell(0,0,Company::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; - } - - public function drawRemittenceStub() { - # Draw the remittance line - $this->Line(9,195,200,195); - - $x = 18; $y = 200; - - $this->SetFont('helvetica','B',13); - $this->SetXY($x,$y); $this->Cell(0,0,_('Payment Remittence')); $y +=5; - - $this->SetFont('helvetica','',8); - $this->SetXY($x,$y); $this->Cell(0,0,_('Please return this portion with your cheque or money order')); $y +=3; - $this->SetXY($x,$y); $this->Cell(0,0,_('made payable to').' '.Config::sitename()); - - # Due Date - $x = 110; $y = 200; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Issue Date')); - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('date_orig'),0,0,'R'); - - # Account ID - $y = 205; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Account Number')); - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->account->accnum(),0,0,'R'); - - # Invoice number - $y = 210; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Invoice Number')); - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->invnum(),0,0,'R'); - - # Company Address - $y = 216; - $this->SetFont('helvetica','',10); - $this->SetXY(18,$y); $this->Cell(0,0,Config::sitename()); $y += 4; - $this->SetXY(18,$y); $this->Cell(0,0,Company::street()); $y += 4; - $this->SetXY(18,$y); $this->Cell(0,0,sprintf('%s, %s %s',Company::city(),Company::state(),Company::pcode())); $y += 4; - - # Previous Due - $y = 215; - $this->SetFont('helvetica','',9); - $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Due')); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->other_due(TRUE),0,0,'R'); - - $y = 219; - $this->SetFont('helvetica','',9); - $this->SetXY($x,$y); $this->Cell(0,0,_('Amount Due').' '.$this->io->display('due_date')); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->due(TRUE),0,0,'R'); - - # Total Due - $y = 224; - $this->SetFont('helvetica','B',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Total Due')); - $this->SetXY($x,$y); $this->Cell(0,0,Currency::display($this->io->due() ? $this->io->account->invoices_due_total() : 0),0,0,'R'); - - # Draw the Customers Address - $x = 25; $y = 248; - - $this->SetFont('helvetica','B',12); - - if ($this->billToCompany && ! empty($this->io->account->company)) - $name = $this->io->account->company; - else - $name = $this->io->account->name(); - - $this->SetXY($x,$y); $this->Cell(0,0,html_entity_decode($name,ENT_NOQUOTES)); $y += 5; - $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s %s ',$this->io->account->address1,$this->io->account->address2)); $y += 5; - $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s, %s %s',$this->io->account->city,$this->io->account->state,$this->io->account->zip)); $y += 5; - } - - public function drawInvoiceHeader() { - $x = 125; $y = 10; - - # Draw a box. - $this->SetFillColor(245); - $this->SetXY($x-1,$y-1); $this->Cell(0,35+($this->io->billed_amt ? 5 : 0)+($this->io->credit_amt ? 5 : 0),'',1,0,'',1); - - # Draw a box around the invoice due date and amount due. - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,'TAX INVOICE'); - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->invnum(),0,0,'R'); - - # Invoice number at top of page. - $y += 7; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Issue Date')); $y += 5; - $this->SetXY($x,$y); $this->Cell(0,0,_('Amount Due')); - - $y -= 5; - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('date_orig'),0,0,'R'); $y += 5; - $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('due_date'),0,0,'R'); - - $y += 5; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Due')); - $this->SetFont('helvetica','B',11); - $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->other_due(TRUE),0,0,'R'); - - $y += 5; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,_('Current Charges')); - $this->SetFont('helvetica','B',11); - $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->total(TRUE),0,0,'R'); - - if ($this->io->billed_amt) { - $y += 5; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,'Payments Received'); - $this->SetFont('helvetica','B',11); - $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->display('billed_amt'),0,0,'R'); - } - - if ($this->io->credit_amt) { - $y += 5; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,'Credits Received'); - $this->SetFont('helvetica','B',11); - $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->display('credit_amt'),0,0,'R'); - } - - $y += 5; - $this->SetFont('helvetica','',10); - $this->SetXY($x,$y); $this->Cell(0,0,'Total Payable'); - $this->SetFont('helvetica','B',11); - $this->SetXY($x+55,$y); $this->Cell(0,0,Currency::display($this->io->due() ? $this->io->account->invoices_due_total() : 0),0,0,'R'); - } - - #@todo Limit the size of the news to 6 lines - public function drawNews($news) { - if (! $news) - return; - - $x = 9; $y = 170; - - # Draw a box. - $this->SetFillColor(243); - $this->SetXY($x-1,$y-1); $this->Cell(0,20,'',1,0,'',1); - - $this->SetFont('helvetica','',8); - $this->SetXY($x,$y); $this->MultiCell(0,3,str_replace('\n',"\n",$news),0,'L',0); - } - - #@todo make this list dynamic - public function drawPaymentMethods() { - $x = 120; $y = 242; - - # Draw a box. - $this->SetFillColor(235); - $this->SetXY($x-1,$y-2); $this->Cell(0,32,'',1,0,'',1); - - $this->SetFont('helvetica','B',8); - $this->SetXY($x,$y); $this->Cell(0,0,'This invoice can also be paid by:'); $y += 4; - - # Direct Credit - $logo = Kohana::find_file('media','img/invoice-payment-dd','png'); - $this->Image($logo,$x+1,$y,8); - $this->SetFont('helvetica','B',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'Direct Credit to our Bank Account'); $y += 3; - $this->SetFont('helvetica','',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'BSB:'); $y += 3; - $this->SetXY($x+10,$y); $this->Cell(0,0,'ACCOUNT:'); $y += 3; - $this->SetXY($x+10,$y); $this->Cell(0,0,'REF:'); $y += 3; - - $y -= 9; - $this->SetFont('helvetica','B',8); - $this->SetXY($x+30,$y); $this->Cell(0,0,Company::bsb()); $y += 3; - $this->SetXY($x+30,$y); $this->Cell(0,0,Company::account()); $y += 3; - $this->SetXY($x+30,$y); $this->Cell(0,0,$this->io->refnum()); $y += 3; - -/* - # Direct Debit - $y += 3; - $logo = sprintf('%s/%s',PATH_THEMES.DEFAULT_THEME,'invoice/invoice-payment-dd.png'); - $this->Image($logo,$x+1,$y,8); - $this->SetFont('helvetica','B',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'Direct Debit'); $y += 3; - $this->SetFont('helvetica','',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'Please visit '); $this->SetXY($x+30,$y); $this->addHtmlLink($inv->print['site']['URL'].'?_page=invoice:user_view&id='.$inv->getPrintInvoiceNum(),$inv->print['site']['URL']); $y += 3; -*/ - - # Paypal - $y += 3; - $logo = Kohana::find_file('media','img/invoice-payment-pp','png'); - $this->Image($logo,$x+1,$y,8); - $this->SetFont('helvetica','B',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'Pay Pal/Credit Card'); $y += 3; - $this->SetFont('helvetica','',8); - $this->SetXY($x+10,$y); $this->Cell(0,0,'Please visit '); $this->SetXY($x+30,$y); $this->addHtmlLink(URL::base(TRUE,TRUE),URL::base(TRUE,TRUE)); $y += 3; - } - - /** - * Draw previous invoices due - */ - public function drawSummaryInvoicesDue() { - $x = 125; $y = $this->sum_y ? $this->sum_y : 50; - - $items = $this->io->account->invoices_due(); - - # Calculate the box size - $box = count($items) < $this->itemsPreviousMax ? count($items) : $this->itemsPreviousMax; - - # Draw a box. - $this->SetFillColor(245); - $this->SetXY($x-1,$y-1); $this->Cell(0,5*(1+$box)+1,'',1,0,'',1); - - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Invoices due')); $y += 5; - - $this->SetFont('helvetica','',11); - $i = 0; - $sum_total = 0; - foreach ($items as $line) { - if (++$i < $this->itemsPreviousMax) { - $this->SetXY($x,$y); - $this->Cell(0,0,sprintf('%s #%s',$line->display('date_orig'),$line->invnum())); - $this->Cell(0,0,$line->due(TRUE),0,0,'R'); $y += 5; - - } else { - $sum_total += $line->due(); - } - } - - if ($sum_total) { - $this->SetXY($x,$y); - $this->SetFont('helvetica','I',11); - $this->Cell(0,0,'Other invoices'); - $this->SetFont('helvetica','',11); - $this->Cell(0,0,Currency::display($sum_total),0,0,'R'); $y += 5; - } - - $this->sum_y = $y+5; - } - - /** - * Called before begining to loop the invoice_item table. Used to set initial values. - */ - public function drawLineItems($iteration) { - $this->iteration = $iteration; - if ($iteration>1) - return false; - - return true; - } - - /** - * Called once per line item to add to the PDF invoice. This function serves to - * direct each iteration to a different function which handles a specific piece - * of the PDF building puzzle. - */ - public function drawLineItem($line) { - switch($this->iteration) { - case 0: - $this->drawLineItems_0($line); - break; - - default: - echo 'Unknown PDF iteration encountered. Halting.'; - exit; - } - } - - /** - * Draws the non-VoIP related items for iteration 0. - * @todo need to make sure that this pages well, when there are many items (with many sub details). - * @tood Need to replicate this to the other fpdf files - */ - private function drawLineItems_0($line) { - if ($line['price_type'] == 0 && $line['item_type'] == 5) - return; - - $x = 10; - if ($this->i == 0 || $this->i%51 == 0) { - $this->y = 5; - $this->AddPage(); - - $this->SetFont('helvetica','B',12); - $this->SetXY($x,$this->y); $this->Cell(0,0,_('Itemised Charges')); - $this->Cell(0,0,_('Page #').$this->PageNo(),0,0,'R'); - $this->SetXY($x,$this->y); $this->Cell(0,0,_('Invoice #').$invnum,0,0,'C'); - - # Draw table headers - $this->y += 10; - $this->SetFont('helvetica','B',8); - $this->SetXY($x,$this->y); - $this->Cell(0,0,_('Description')); - $this->SetX($x+135); - $this->Cell(0,0,_('Quantity')); - $this->SetX($x+160); - $this->Cell(10,0,_('Unit Cost'),0,0,'R'); - $this->SetX($x+135); - $this->Cell(0,0,_('Amount'),0,0,'R'); - $this->Line($x,$this->y+4,200,$this->y+4); - $this->y += 5; - $this->SetY($this->y); - } - - $this->SetFont('helvetica','',8); - $this->SetX($x); - $this->Cell(0,0,$line['name']); - - if (isset($line['price_base'])) { - $this->SetX($x+160); - $this->Cell(10,0,$this->_currency($line['price_base']),0,0,'R'); - } - - if (isset($line['qty'])) { - $this->SetX($x+130); - $this->Cell(10,0,$line['qty'],0,0,'R'); - } - - $this->SetX($x+130); - $this->Cell(0,0,$this->_currency($line['total_amt']),0,0,'R'); - - if ($this->show_service_range && $line['daterange']) { - $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,$line['daterange']); - } - - if ($line['domain']) { - $this->SetFont('helvetica','I',7); - $this->y += 3; - $this->SetXY($x+10,$this->y); $this->Cell(0,0,'Domain'); - $this->SetFont('helvetica','',7); - $this->SetXY($x+40,$this->y); $this->Cell(0,0,$line['domain']); - } - - if ($line['attr']) { - $showchars = 20; - if (preg_match('/^a:/',$line['attr'])) - $a = unserialize($line['attr']); - else { - $x = explode("\n",$line['attr']); - $a = array(); - foreach ($x as $y) - if ($y) { - list($c,$d) = explode('==',$y); - $a[$c] = $d; - } - } - - foreach ($a as $field=>$value) { - - if (in_array($field,array('service_account_name','service_address'))) - continue; - - $this->SetFont('helvetica','I',7); - $this->y += 3; - $this->SetXY(20,$this->y); $this->Cell(0,0,strlen($field) > $showchars ? substr($field,0,$showchars-2).'...' : $field); - $this->SetFont('helvetica','',7); - $this->SetXY(50,$this->y); $this->Cell(0,0,$value); - } - } - - $this->y += 5; - $this->SetY($this->y); - $this->i++; - } - - /** - * This will draw the Summary Box, with the summary of the items - * on the invoice. - */ - public function drawSummaryLineItems() { - if (! $this->show_itemized) - return; - - $items = $this->io->items_summary(); - - # Calculate the box size - $box = count($items) < $this->itemsSummaryMax ? count($items) : $this->itemsSummaryMax; - - $x = 10; $y = $this->sum_y ? $this->sum_y : 55; - - # Draw a box. - $this->SetFillColor(245); - $this->SetXY($x-1,$y-1); $this->Cell(0,5*( - 1+1+1+3+($this->io->discount_amt ? 1 : 0)+($this->io->billed_amt ? 1 : 0)+($this->io->credit_amt ? 1 : 0)+$box - )+1,'',1,0,'',1); - - $this->SetFont('helvetica','B',11); - $this->SetXY($x,$y); $this->Cell(0,0,_('Current Charges Summary for')); $y += 5; - - $this->SetY($y); - $this->SetFont('helvetica','',9); - - $i=0; - foreach($items as $line) { - $this->SetX($x); - - $q = $line->quantity; - if (empty($q)) - $q = 1; - - $this->Cell(0,0,$q); - $this->SetX($x+8); - $this->Cell(0,0,sprintf('%s (%s)',$line->product->product_translate->find()->name,Currency::display($line->price_base))); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display($line->price_base*$line->quantity+$line->price_setup),0,0,'R'); - $y += 5; - $this->SetY($y); - $i++; - if ($i > $this->itemsSummaryMax) { - $this->SetFont('helvetica','B',11); - $this->SetX($x); - $this->Cell(0,0,_('The above is just a summary. To view a detailed list of charges, please visit our website.')); - break; - } - } - - # Calculate our rounding error - $subtotal = 0; - foreach($items as $line) - $subtotal += $line->price_base*$line->quantity+$line->price_setup; - - $subtotal -= $this->io->discount_amt; - $subtotal = round($subtotal,2); - - if (round($this->io->total_amt-$this->io->tax_amt,2) != $subtotal) { - $this->SetFont('helvetica','',9); - $this->SetX($x); - $this->Cell(0,0,'Rounding'); - $this->SetX($x+135); - $this->Cell(0,0, - Currency::display($this->io->total_amt-$this->io->tax_amt-$subtotal),0,0,'R'); - $y += 5; - $this->SetY($y); - } - - # Draw Discounts. - if ($this->io->discount_amt) { - $y += 5; - $this->SetY($y); - $this->SetFont('helvetica','B',9); - $this->SetX($x+8); - $this->Cell(0,0,_('Discount')); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display(-$this->io->discount_amt),0,0,'R'); - } - - # Sub total and tax. - $y += 5; - $this->SetY($y); - $this->SetFont('helvetica','B',9); - $this->SetX($x+8); - $this->Cell(0,0,'Sub Total'); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display($this->io->total_amt-$this->io->tax_amt),0,0,'R'); - - $y += 5; - $this->SetY($y); - $this->SetX($x+8); - $this->Cell(0,0,'Taxes'); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display($this->io->tax_amt),0,0,'R'); - - $y += 5; - $this->SetY($y); - $this->SetX($x+8); - $this->Cell(0,0,'Total Charges'); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display($this->io->total_amt),0,0,'R'); - - # Show payments already received for this invoice - if ($this->io->billed_amt) { - $y += 5; - $this->SetY($y); - $this->SetX($x+8); - $this->Cell(0,0,'Payments Received'); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display($this->io->billed_amt),0,0,'R'); - } - - if ($this->io->credit_amt) { - $y += 5; - $this->SetY($y); - $this->SetFont('helvetica','B',9); - $this->SetX($x+8); - $this->Cell(0,0,_('Less Credits')); - $this->SetX($x+135); - $this->Cell(0,0,Currency::display(-$this->io->credit_amt),0,0,'R'); - } - - $y += 5; - $this->SetY($y); - $this->SetX($x+8); - $this->Cell(0,0,'Balance Due'); - $this->SetX($x+135); - $this->Cell(0,0,$this->io->due(TRUE),0,0,'R'); - } -} -?> diff --git a/modules/invoice/classes/invoice/pdf.php b/modules/invoice/classes/invoice/tcpdf.php similarity index 85% rename from modules/invoice/classes/invoice/pdf.php rename to modules/invoice/classes/invoice/tcpdf.php index 9d445c72..71de4f6e 100644 --- a/modules/invoice/classes/invoice/pdf.php +++ b/modules/invoice/classes/invoice/tcpdf.php @@ -1,5 +1,14 @@ -SetCreator('Open Source Billing'); $this->SetAuthor(Config::sitename()); $this->SetTitle(sprintf('%s Invoice',Config::sitename())); - $this->SetSubject(sprintf('Invoice #%06s',$this->io->invnum())); - $this->SetKeywords($this->io->invnum()); + $this->SetSubject(sprintf('Invoice #%06s',$this->io->id())); + $this->SetKeywords($this->io->id()); $this->SetAutoPageBreak(TRUE,25); $this->SetHeaderMargin(1); $this->SetFooterMargin(10); @@ -58,20 +66,6 @@ abstract class Invoice_PDF extends TCPDF { abstract public function drawCompanyLogo(); abstract public function drawCompanyAddress(); abstract public function drawInvoiceHeader(); - - /** - * Enable re-iteration of the invoices items, so that they can be displayed many ways - */ - abstract public function drawLineItems($iteration); - - /** - * This is called for each line item. - */ - abstract public function drawLineItem($line); - - /** - * Draws the summary on the first page - */ abstract public function drawSummaryLineItems(); abstract public function drawPaymentMethods(); abstract public function drawRemittenceStub(); diff --git a/modules/invoice/classes/invoice/tcpdf/default.php b/modules/invoice/classes/invoice/tcpdf/default.php new file mode 100644 index 00000000..61c87671 --- /dev/null +++ b/modules/invoice/classes/invoice/tcpdf/default.php @@ -0,0 +1,523 @@ +Image($logo,$x,$y,$size); + } + + /** + * Draw the Company Address + */ + public function drawCompanyAddress() { + // Add the company address next to the logo + $x = 40; $y = 7; + + $this->SetFont('helvetica','B',10); + $this->SetXY($x,$y); $this->Cell(0,0,Config::sitename()); $y += 4; + + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,Company::taxid()); $y += 6; + + $this->SetXY($x,$y); $this->Cell(0,0,Company::street()); $y += 4; + $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s, %s %s',Company::city(),Company::state(),Company::pcode())); $y += 4; + + $y += 2; + $this->SetXY($x,$y); $this->Cell(0,0,'Phone:'); $this->SetXY($x+16,$y); $this->Cell(0,0,Company::phone()); $y += 4; + $this->SetXY($x,$y); $this->Cell(0,0,'Fax:'); $this->SetXY($x+16,$y); $this->Cell(0,0,Company::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; + } + + /** + * Draw the remmittence stub + */ + public function drawRemittenceStub() { + // Draw the remittance line + $this->Line(9,195,200,195); + + $x = 18; $y = 200; + + $this->SetFont('helvetica','B',13); + $this->SetXY($x,$y); $this->Cell(0,0,_('Payment Remittence')); $y +=5; + + $this->SetFont('helvetica','',8); + $this->SetXY($x,$y); $this->Cell(0,0,_('Please return this portion with your cheque or money order')); $y +=3; + $this->SetXY($x,$y); $this->Cell(0,0,_('made payable to').' '.Config::sitename()); + + // Due Date + $x = 110; $y = 200; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Issue Date')); + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('date_orig'),0,0,'R'); + + // Account ID + $y = 205; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Account Number')); + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->account->accnum(),0,0,'R'); + + // Invoice number + $y = 210; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Invoice Number')); + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->id(),0,0,'R'); + + // Company Address + $y = 216; + $this->SetFont('helvetica','',10); + $this->SetXY(18,$y); $this->Cell(0,0,Config::sitename()); $y += 4; + $this->SetXY(18,$y); $this->Cell(0,0,Company::street()); $y += 4; + $this->SetXY(18,$y); $this->Cell(0,0,sprintf('%s, %s %s',Company::city(),Company::state(),Company::pcode())); $y += 4; + + // Previous Due + $y = 215; + $this->SetFont('helvetica','',9); + $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Due')); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->account->invoices_due_total($this->io->date_orig,TRUE),0,0,'R'); + + $y = 219; + $this->SetFont('helvetica','',9); + $this->SetXY($x,$y); $this->Cell(0,0,_('Amount Due').' '.$this->io->display('due_date')); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->due(TRUE),0,0,'R'); + + // Total Due + $y = 224; + $this->SetFont('helvetica','B',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Total Payable')); + $this->SetXY($x,$y); $this->Cell(0,0,Currency::display($this->io->due() ? $this->io->total()+$this->io->account->invoices_due_total($this->io->date_orig,TRUE) : 0),0,0,'R'); + + // Draw the Customers Address + $x = 25; $y = 248; + + $this->SetFont('helvetica','B',12); + + if ($this->billToCompany && ! empty($this->io->account->company)) + $name = $this->io->account->company; + else + $name = $this->io->account->name(); + + $this->SetXY($x,$y); $this->Cell(0,0,html_entity_decode($name,ENT_NOQUOTES)); $y += 5; + $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s %s ',$this->io->account->address1,$this->io->account->address2)); $y += 5; + $this->SetXY($x,$y); $this->Cell(0,0,sprintf('%s, %s %s',$this->io->account->city,$this->io->account->state,$this->io->account->zip)); $y += 5; + } + + /** + * Draw the invoice header + */ + public function drawInvoiceHeader() { + $x = 125; $y = 10; + + // Draw a box. + $this->SetFillColor(245); + $this->SetXY($x-1,$y-1); $this->Cell(0,35+($this->io->billed_amt ? 5 : 0)+($this->io->credit_amt ? 5 : 0),'',1,0,'',1); + + // Draw a box around the invoice due date and amount due. + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,'TAX INVOICE'); + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->id(),0,0,'R'); + + // Invoice number at top of page. + $y += 7; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Issue Date')); $y += 5; + $this->SetXY($x,$y); $this->Cell(0,0,_('Amount Due')); + + $y -= 5; + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('date_orig'),0,0,'R'); $y += 5; + $this->SetXY($x,$y); $this->Cell(0,0,$this->io->display('due_date'),0,0,'R'); + + $y += 5; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Due')); + $this->SetFont('helvetica','B',11); + $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->account->invoices_due_total($this->io->date_orig,TRUE),0,0,'R'); + + $y += 5; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,_('Current Charges')); + $this->SetFont('helvetica','B',11); + $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->total(TRUE),0,0,'R'); + + if ($this->io->billed_amt) { + $y += 5; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,'Payments Received'); + $this->SetFont('helvetica','B',11); + $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->display('billed_amt'),0,0,'R'); + } + + if ($this->io->credit_amt) { + $y += 5; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,'Credits Received'); + $this->SetFont('helvetica','B',11); + $this->SetXY($x+55,$y); $this->Cell(0,0,$this->io->display('credit_amt'),0,0,'R'); + } + + $y += 5; + $this->SetFont('helvetica','',10); + $this->SetXY($x,$y); $this->Cell(0,0,'Total Payable'); + $this->SetFont('helvetica','B',11); + $this->SetXY($x+55,$y); $this->Cell(0,0,Currency::display($this->io->due() ? $this->io->total()+$this->io->account->invoices_due_total($this->io->date_orig) : 0),0,0,'R'); + } + + /** + * Draw any news messages + * @todo Limit the size of the news to 6 lines + */ + public function drawNews($news) { + if (! $news) + return; + + $x = 9; $y = 170; + + # Draw a box. + $this->SetFillColor(243); + $this->SetXY($x-1,$y-1); $this->Cell(0,20,'',1,0,'',1); + + $this->SetFont('helvetica','',8); + $this->SetXY($x,$y); $this->MultiCell(0,3,str_replace('\n',"\n",$news),0,'L',0); + } + + /** + * Draw our available payment methods + * @todo make this list dynamic + */ + public function drawPaymentMethods() { + $x = 120; $y = 242; + + # Draw a box. + $this->SetFillColor(235); + $this->SetXY($x-1,$y-2); $this->Cell(0,32,'',1,0,'',1); + + $this->SetFont('helvetica','B',8); + $this->SetXY($x,$y); $this->Cell(0,0,'This invoice can be paid by:'); $y += 4; + + # Direct Credit + $logo = Kohana::find_file('media','img/invoice-payment-dd','png'); + $this->Image($logo,$x+1,$y,8); + $this->SetFont('helvetica','B',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'Direct Credit to our Bank Account'); $y += 3; + $this->SetFont('helvetica','',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'BSB:'); $y += 3; + $this->SetXY($x+10,$y); $this->Cell(0,0,'ACCOUNT:'); $y += 3; + $this->SetXY($x+10,$y); $this->Cell(0,0,'REF:'); $y += 3; + + $y -= 9; + $this->SetFont('helvetica','B',8); + $this->SetXY($x+30,$y); $this->Cell(0,0,Company::bsb()); $y += 3; + $this->SetXY($x+30,$y); $this->Cell(0,0,Company::account()); $y += 3; + $this->SetXY($x+30,$y); $this->Cell(0,0,$this->io->refnum()); $y += 3; + +/* + # Direct Debit + $y += 3; + $logo = sprintf('%s/%s',PATH_THEMES.DEFAULT_THEME,'invoice/invoice-payment-dd.png'); + $this->Image($logo,$x+1,$y,8); + $this->SetFont('helvetica','B',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'Direct Debit'); $y += 3; + $this->SetFont('helvetica','',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'Please visit '); $this->SetXY($x+30,$y); $this->addHtmlLink($inv->print['site']['URL'].'?_page=invoice:user_view&id='.$inv->getPrintInvoiceNum(),$inv->print['site']['URL']); $y += 3; +*/ + + # Paypal + $y += 3; + $logo = Kohana::find_file('media','img/invoice-payment-pp','png'); + $this->Image($logo,$x+1,$y,8); + $this->SetFont('helvetica','B',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'Pay Pal/Credit Card'); $y += 3; + $this->SetFont('helvetica','',8); + $this->SetXY($x+10,$y); $this->Cell(0,0,'Please visit '); $this->SetXY($x+30,$y); $this->addHtmlLink(URL::base(TRUE,TRUE),URL::base(TRUE,TRUE)); $y += 3; + } + + /** + * Draw previous invoices due + */ + public function drawSummaryInvoicesDue() { + $x = 125; $y = $this->sum_y ? $this->sum_y : 50; + + $items = $this->io->account->invoices_due($this->io->date_orig); + + # Calculate the box size + $box = count($items) < $this->itemsPreviousMax ? count($items) : $this->itemsPreviousMax; + + # Draw a box. + $this->SetFillColor(245); + $this->SetXY($x-1,$y-1); $this->Cell(0,5*(1+$box)+1,'',1,0,'',1); + + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); $this->Cell(0,0,_('Previous Invoices Due')); $y += 5; + + $this->SetFont('helvetica','',11); + $i = 0; + $sum_total = 0; + foreach ($items as $line) { + if (++$i < $this->itemsPreviousMax) { + $this->SetXY($x,$y); + $this->Cell(0,0,sprintf('%s %s',$line->display('date_orig'),$line->id())); + $this->Cell(0,0,$line->due(TRUE),0,0,'R'); $y += 5; + + } else { + $sum_total += $line->due(); + } + } + + if ($sum_total) { + $this->SetXY($x,$y); + $this->SetFont('helvetica','I',11); + $this->Cell(0,0,'Other invoices'); + $this->SetFont('helvetica','',11); + $this->Cell(0,0,Currency::display($sum_total),0,0,'R'); $y += 5; + } + + $this->sum_y = $y+5; + } + + /** + * This will draw the Summary Box, with the summary of the items + * on the invoice. + */ + public function drawSummaryLineItems() { + if (! $this->show_itemized) + return; + + $items = $this->io->items_summary(); + + // Calculate the box size + $box = count($items) < $this->itemsSummaryMax ? count($items) : $this->itemsSummaryMax; + + // Our starting position + $x = 10; $y = $this->sum_y ? $this->sum_y : 55; + + // Draw a box. + $this->SetFillColor(245); + $this->SetXY($x-1,$y-1); + $this->Cell(0,5*( + 1+1+1+3+($this->io->discount_amt ? 1 : 0)+($this->io->billed_amt ? 1 : 0)+($this->io->credit_amt ? 1 : 0)+$box + )+1,'',1,0,'',1); + + $this->SetFont('helvetica','B',11); + $this->SetXY($x,$y); + $this->Cell(0,0,_('Current Charges Summary')); $y += 5; + + $this->SetY($y); + $this->SetFont('helvetica','',9); + + $i = $subtotal = 0; + foreach ($items as $name => $line) { + if ($i < $this->itemsSummaryMax) { + $this->SetX($x); + $this->Cell(0,0,$line['quantity']); + $this->SetX($x+8); + $this->Cell(0,0,$name); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($line['subtotal']),0,0,'R'); + + $y += 5; + $this->SetY($y); + } + + $i++; + if ($i == $this->itemsSummaryMax) { + $this->SetFont('helvetica','B',11); + $this->SetX($x); + $this->Cell(0,0,_('The above is just a summary. To view a detailed list of charges, please visit our website.')); + } + + $subtotal += $line['subtotal']; + } + + // Calculate our rounding error + // @todo This shouldnt be required. + $subtotal = round($subtotal-$this->io->discount_amt,Kohana::config('config.currency_format')); + + if (round($this->io->subtotal(),Kohana::config('config.currency_format')) != $subtotal) { + $this->SetFont('helvetica','',9); + $this->SetX($x); + $this->Cell(0,0,'Rounding'); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($this->io->subtotal()-$subtotal),0,0,'R'); + + $y += 5; + $this->SetY($y); + } + + // Draw Discounts. + if ($this->io->discount_amt) { + $y += 5; + $this->SetY($y); + + $this->SetFont('helvetica','B',9); + $this->SetX($x+8); + $this->Cell(0,0,_('Discount')); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display(-$this->io->discount_amt),0,0,'R'); + } + + // Subtotal and tax. + $y += 5; + $this->SetY($y); + + $this->SetFont('helvetica','B',9); + $this->SetX($x+8); + $this->Cell(0,0,'Sub Total'); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($this->io->subtotal()),0,0,'R'); + + $y += 5; + $this->SetY($y); + + $this->SetX($x+8); + $this->Cell(0,0,'Taxes'); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($this->io->tax()),0,0,'R'); + + $y += 5; + $this->SetY($y); + + $this->SetX($x+8); + $this->Cell(0,0,'Total Charges'); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($this->io->total()),0,0,'R'); + + // Show payments already received for this invoice + if ($this->io->billed_amt) { + $y += 5; + $this->SetY($y); + + $this->SetX($x+8); + $this->Cell(0,0,'Payments Received'); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display($this->io->payments()),0,0,'R'); + } + + if ($this->io->credit_amt) { + $y += 5; + $this->SetY($y); + + $this->SetFont('helvetica','B',9); + $this->SetX($x+8); + $this->Cell(0,0,_('Less Credits')); + $this->SetX($x+135); + $this->Cell(0,0,Currency::display(-$this->io->credit_amt),0,0,'R'); + } + + $y += 5; + $this->SetY($y); + $this->SetX($x+8); + $this->Cell(0,0,'Balance Due'); + $this->SetX($x+135); + $this->Cell(0,0,$this->io->due(TRUE),0,0,'R'); + } + + /** + * This will draw the Summary Box, with the summary of the items + * on the invoice. + */ + public function drawDetailLineItems() { + $this->i = 0; + foreach ($this->io->items() as $io) + $this->drawLineItem($io); + } + + /** + * Draws Invoice Detail Item + * + * @todo need to make sure that this pages well, when there are many items (with many sub details). + */ + private function drawLineItem($ito) { + $x = 10; + if ($this->i == 0 || $this->i%$this->max_lines_page == 0) { + $this->y = 5; + $this->AddPage(); + + $this->SetFont('helvetica','B',12); + $this->SetXY($x,$this->y); $this->Cell(0,0,_('Itemised Charges')); + $this->Cell(0,0,_('Page #').$this->PageNo(),0,0,'R'); + $this->SetXY($x,$this->y); $this->Cell(0,0,_('Invoice #').$this->io->id(),0,0,'C'); + + // Draw table headers + $this->y += 10; + $this->SetFont('helvetica','B',8); + $this->SetXY($x,$this->y); + $this->Cell(0,0,_('Description')); + $this->SetX($x+135); + $this->Cell(0,0,_('Quantity')); + $this->SetX($x+160); + $this->Cell(10,0,_('Unit Cost'),0,0,'R'); + $this->SetX($x+135); + $this->Cell(0,0,_('Amount'),0,0,'R'); + $this->Line($x,$this->y+4,200,$this->y+4); + + $this->y += 5; + $this->SetY($this->y); + } + + $this->SetFont('helvetica','',8); + $this->SetX($x); + $this->Cell(0,0,$ito->service->service_name()); + + if ($ito->price_base) { + $this->SetX($x+160); + $this->Cell(10,0,Currency::display($ito->price_base),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_amt),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 ($ito->invoice_detail_items()) + foreach ($ito->invoice_detail_items() as $k=>$v) { + $this->SetFont('helvetica','I',7); + $this->y += 3; + $this->SetXY($x+10,$this->y); $this->Cell(0,0,$k); + $this->SetFont('helvetica','',7); + $this->SetXY($x+40,$this->y); $this->Cell(0,0,$v); + } + + $this->y += 5; + $this->SetY($this->y); + $this->i++; + } +} +?> diff --git a/modules/invoice/classes/model/invoice.php b/modules/invoice/classes/model/invoice.php index 80156450..9a104eec 100644 --- a/modules/invoice/classes/model/invoice.php +++ b/modules/invoice/classes/model/invoice.php @@ -37,19 +37,31 @@ class Model_Invoice extends ORMOSB { 'tax_amt'=>array('calc_tax'), ); - protected $_formats = array( - 'date_orig'=>array('Config::date'=>array()), - 'due_date'=>array('Config::date'=>array()), - 'billed_amt'=>array('Currency::display'=>array()), - 'credit_amt'=>array('Currency::display'=>array()), - 'status'=>array('StaticList_YesNo::display'=>array()), - 'total_amt'=>array('Currency::display'=>array()), + protected $_display_filters = array( + 'date_orig'=>array( + array('Config::date',array(':value')), + ), + 'due_date'=>array( + array('Config::date',array(':value')), + ), + 'billed_amt'=>array( + array('Currency::display',array(':value')), + ), + 'credit_amt'=>array( + array('Currency::display',array(':value')), + ), + 'status'=>array( + array('StaticList_YesNo::display',array(':value')), + ), + 'total_amt'=>array( + array('Currency::display',array(':value')), + ), ); /** * Display the Invoice Number */ - public function invnum() { + public function id() { return sprintf('%06s',$this->id); } @@ -64,127 +76,115 @@ class Model_Invoice extends ORMOSB { * Display the amount due */ public function due($format=FALSE) { - $result = 0; // If the invoice is active calculate the due amount - if ($this->status) - // @todo This rounding should be a system setting - $result = round($this->total_amt-$this->credit_amt-$this->billed_amt,2); + $result = $this->status ? round($this->total_amt-$this->credit_amt-$this->billed_amt,Kohana::config('config.currency_format')) : 0; - if ($format) - return Currency::display($result); - else - return $result; - } - - /** - * Return a total invoices overdue excluding this invoice - */ - public function other_due($format=FALSE) { - $result = $this->account->invoices_due_total()-$this->due(); - - if ($format) - return Currency::display($result); - else - return $result; - } - - public function subtotal($format=FALSE) { - $result = 0; - - foreach ($this->items() as $item) - $result += $item->subtotal(); - - if ($format) - return Currency::display($result); - else - return $result; - } - - public function total($format=FALSE) { - $result = 0; - - foreach ($this->items() as $item) - $result += $item->total(); - - // Reduce any credits - $result -= $this->credit_amt; - - if ($format) - return Currency::display($result); - else - return $result; + return $format ? Currency::display($result) : $result; } /** * Return a list of invoice items for this invoice. + * + * We only return the items, if the invoice hasnt been changed. */ public function items() { - // Get our invoice items for an existing invoice - if ($this->id AND $this->loaded() AND ! $this->_changed) - return $this->invoice_item->order_by('service_id,item_type,module_id')->find_all(); + return ($this->loaded() AND ! $this->_changed) ? $this->invoice_item->order_by('service_id,item_type,module_id')->find_all() : NULL; + } - echo kohana::debug(array('BEFORE'=>$this->invoice_item)); - if (! $this->invoice_items) - $this->invoice_items = $this->invoice_item; + /** + * Return the subtotal of all items + */ + public function subtotal($format=FALSE) { + $result = 0; - echo kohana::debug(array('AFTER'=>$this->invoice_items));die(); - return $this->invoice_items; + foreach ($this->items() as $ito) + $result += $ito->subtotal(); + + return $format ? Currency::display($result) : $result; + } + + public function tax($format=FALSE) { + $result = 0; + + foreach ($this->items() as $ito) + $result += $ito->tax_amt; + + return $format ? Currency::display($result) : $result; + } + + /** + * Return the total of all items + */ + public function total($format=FALSE) { + $result = 0; + + foreach ($this->items() as $ito) + $result += $ito->total(); + + // Reduce by any credits + $result -= $this->credit_amt; + + return $format ? Currency::display($result) : $result; + } + + public function payments() { + return ($this->loaded() AND ! $this->_changed) ? $this->payments->find_all() : NULL; + } + + public function payments_total($format=FALSE) { + $result = 0; + + foreach ($this->payments() as $po) + $result += $po->total_amt; + + return $format ? Currency::display($result) : $result; } /** * Return a list of our main invoice items (item_id=0) */ public function items_main() { - if ($this->id AND $this->loaded() AND ! $this->_changed) - return $this->invoice_item->where('item_type','=',0)->find_all(); + return ($this->loaded() AND ! $this->_changed) ? $this->invoice_item->where('item_type','=',0)->find_all() : NULL; } /** * Return a list of our sub invoice items (item_id!=0) + * + * @param sid int Service ID */ public function items_sub($sid) { - if ($this->id AND $this->loaded() AND ! $this->_changed) - return $this->invoice_item->where('service_id','=',$sid)->and_where('item_type','<>',0)->find_all(); + return ($this->loaded() AND ! $this->_changed) ? $this->invoice_item->where('service_id','=',$sid)->and_where('item_type','<>',0)->find_all() : NULL; } /** * Summarise the items on an invoice */ public function items_summary() { - $sum = array(); + $result = array(); - foreach ($this->items_main() as $item) { + foreach ($this->items_main() as $ito) { $unique = TRUE; - # Unique line item - if (isset($sum[$item->product->sku])) { - # Is unique price/attributes? - - foreach ($sum[$item->product->sku] as $sid => $flds) { - if ($flds->price_base == $item->price_base && - $flds->price_setup == $item->price_setup) { - - $sum[$item->product->sku][$sid]->quantity += $item->quantity; - $unique = FALSE; - - break; - } - } + $t = $ito->product->summary(); + if (! isset($result[$t])) { + $result[$t]['quantity'] = 0; + $result[$t]['subtotal'] = 0; } - # Unique line item - if ($unique) - $sum[$item->product->sku][] = $item; + $result[$t]['quantity'] += $ito->quantity; + $result[$t]['subtotal'] += $ito->subtotal(); } - if (count($sum)) { - $items = array(); - foreach ($sum as $sku => $item) - foreach ($item as $sitem) - array_push($items,$sitem); + return $result; + } - return $items; - } + /** + * Find all the invoice items relating to a service + * + * @param int Service ID + */ + private function items_service($sid) { + return $this->invoice_item->where('service_id','=',$sid)->find_all(); } /** @@ -193,8 +193,8 @@ class Model_Invoice extends ORMOSB { public function items_service_total($sid) { $total = 0; - foreach ($this->invoice_item->where('service_id','=',$sid)->find_all() as $item) - $total += $item->total(); + foreach ($this->items_service($sid) as $ito) + $total += $ito->total(); return $total; } @@ -205,8 +205,8 @@ class Model_Invoice extends ORMOSB { public function items_service_tax($sid) { $total = 0; - foreach ($this->invoice_item->where('service_id','=',$sid)->find_all() as $item) - $total += $item->tax_amt; + foreach ($this->items_service($sid) as $ito) + $total += $ito->tax_amt; return $total; } @@ -217,8 +217,8 @@ class Model_Invoice extends ORMOSB { public function sorted_service_items($index) { $summary = array(); - foreach ($this->items() as $item) { - $key = $item->service->$index; + foreach ($this->items() as $ito) { + $key = $ito->service->$index; if (! isset($summary[$key]['items'])) { $summary[$key]['items'] = array(); @@ -226,10 +226,10 @@ class Model_Invoice extends ORMOSB { } // Only record items with item_type=0 - if ($item->item_type == 0) - array_push($summary[$key]['items'],$item); + if ($ito->item_type == 0) + array_push($summary[$key]['items'],$ito); - $summary[$key]['total'] += $item->total(); + $summary[$key]['total'] += $ito->total(); } return $summary; @@ -241,8 +241,8 @@ class Model_Invoice extends ORMOSB { public function tax_summary() { $summary = array(); - foreach ($this->items() as $item) { - foreach ($item->invoice_item_tax->find_all() as $item_tax) { + foreach ($this->items() 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; else @@ -253,6 +253,9 @@ class Model_Invoice extends ORMOSB { return $summary; } + /** + * Add an item to an invoice + */ public function add_item() { $c = count($this->invoice_items); @@ -299,8 +302,8 @@ class Model_Invoice extends ORMOSB { $array[$field] = 0; // @todo Rounding here should come from a global config - foreach ($this->invoice_items as $item) - $array[$field] += round($item->price_base+$item->price_setup,2); + foreach ($this->invoice_items as $ito) + $array[$field] += round($ito->price_base+$ito->price_setup,2); $this->_changed[$field] = $field; } @@ -311,8 +314,8 @@ class Model_Invoice extends ORMOSB { // @todo Rounding here should come from a global config // @todo tax should be evaluated per item // @todo tax parameters should come from user session - foreach ($this->invoice_items as $item) - $array[$field] += round(Tax::total(61,NULL,$item->price_base+$item->price_setup),2); + foreach ($this->invoice_items as $ito) + $array[$field] += round(Tax::total(61,NULL,$ito->price_base+$ito->price_setup),2); $this->_changed[$field] = $field; } diff --git a/modules/invoice/classes/model/invoice/item.php b/modules/invoice/classes/model/invoice/item.php index 1be5c864..cd320cef 100644 --- a/modules/invoice/classes/model/invoice/item.php +++ b/modules/invoice/classes/model/invoice/item.php @@ -32,16 +32,14 @@ class Model_Invoice_Item extends ORMOSB { return sprintf('%s: %s',_('Date'),Config::date($this->date_start)); else - return sprintf('%s: %s -> %s',_('Period'),Config::date($this->date_start),Config::date($this->date_stop)); + return sprintf('%s -> %s',Config::date($this->date_start),Config::date($this->date_stop)); } - /** - * On invoices where there are multiple charges for the same item - * (eg spanning the next invoice period), this should show the period - * range for the additional sub-items. - */ - public function invoice_display() { - return $this->period(); + public function invoice_detail_items() { + if ($this->item_type != 0) + return; + + return $this->service->details('invoice'); } public function subtotal() { diff --git a/modules/invoice/views/invoice/html.php b/modules/invoice/views/invoice/html.php index c3c2191d..be3bb3f3 100644 --- a/modules/invoice/views/invoice/html.php +++ b/modules/invoice/views/invoice/html.php @@ -14,7 +14,7 @@ - + @@ -61,7 +61,7 @@ - + diff --git a/modules/invoice/views/invoice/list.php b/modules/invoice/views/invoice/list.php index cf456017..22b46266 100644 --- a/modules/invoice/views/invoice/list.php +++ b/modules/invoice/views/invoice/list.php @@ -10,7 +10,7 @@ - + diff --git a/modules/module/classes/controller/admin/module.php b/modules/module/classes/controller/admin/module.php index 4e83d7d8..8c69c7c7 100644 --- a/modules/module/classes/controller/admin/module.php +++ b/modules/module/classes/controller/admin/module.php @@ -45,6 +45,7 @@ class Controller_Admin_Module extends Controller_Module { * Edit a Module Configuration * * @param int $mid Module ID + * @todo Highlight those methods that have security, but the class does not have auth_required set to YES or the method isnt defined in secure_actions */ public function action_edit($mid) { $mo = ORM::factory('module',$mid); diff --git a/modules/module/classes/controller/module.php b/modules/module/classes/controller/module.php index 844222a3..18a4e5c3 100644 --- a/modules/module/classes/controller/module.php +++ b/modules/module/classes/controller/module.php @@ -15,7 +15,7 @@ class Controller_Module extends Controller_TemplateDefault { 'edit'=>TRUE, 'list'=>TRUE, 'menu'=>TRUE, - ); + ); public function action_menu() { // Redirect us to the admin menu, no user facilities here! diff --git a/modules/product/classes/model/product.php b/modules/product/classes/model/product.php index 0d9765ce..e8abb1d5 100644 --- a/modules/product/classes/model/product.php +++ b/modules/product/classes/model/product.php @@ -21,12 +21,71 @@ class Model_Product extends ORMOSB { 'sku'=>'asc', ); - protected $_formats = array( + protected $_display_format = array( 'price_type'=>array('StaticList_PriceType::display'=>array()), ); + /** + * The feature summary should be implemented in child objects. + * It is displayed on the product overview page, as a summary of the products features. + */ + protected function _feature_summary() { + throw new Kohana_Exception(':method not defined in child class :class',array(':method'=>__METHOD__,':class'=>get_class($this))); + } + + /** + * The summary should be implemented in child objects. + */ + protected function _summary() { + return _('No Description'); + } + + /** + * Return the object of the product plugin + */ + private function plugin() { + if (! $this->prod_plugin_file) + return NULL; + + if (! is_numeric($this->prod_plugin_data)) + throw new Kohana_Exception('Missing plugin_id for :product (:type)',array(':product'=>$this->id,':type'=>$this->prod_plugin_file)); + + $spn = sprintf('%s_%s',get_class($this),$this->prod_plugin_file); + return new $spn($this->prod_plugin_data); + } + + /** + * Get the product name, after translating + */ + public function name() { + return $this->product_translate->find()->display('name'); + } + + /** + * This will render the product feature summary information + */ + public function feature_summary() { + if (is_null($plugin = $this->plugin())) + return HTML::nbsp(''); + else + return $plugin->_feature_summary(); + } + + /** + * Get the summary description + * + * Generally this is used on the invoice summary page + */ + public function summary() { + if (is_null($plugin = $this->plugin())) + return _('Other'); + else + return $plugin->_summary(); + } + /** * Return the products for a given category + * @todo This shouldnt be here. */ public function category($cat) { $results = array(); diff --git a/modules/product/classes/model/product/adsl.php b/modules/product/classes/model/product/adsl.php new file mode 100644 index 00000000..2d8ea318 --- /dev/null +++ b/modules/product/classes/model/product/adsl.php @@ -0,0 +1,58 @@ +array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + 'extra_down_offpeak'=>array( + array('Tax::add',array(':value')), + array('Currency::display',array(':value')), + ), + ); + + protected function _feature_summary() { + return View::factory('product/adsl/feature_summary') + ->set('po',$this); + } + + protected function _summary() { + return sprintf('%s: %s %s','ADSL Services',$this->speed,$this->allowance(TRUE)); + } + + /** + * Show the ADSL allowance as a peak/offpeak metric + */ + public function allowance($string=TRUE) { + $output = ADSL::allowance(array( + 'base_down_peak'=>$this->base_down_peak, + 'base_down_offpeak'=>$this->base_down_offpeak, + 'base_up_peak'=>$this->base_up_peak, + 'base_up_offpeak'=>$this->base_up_offpeak, + 'extra_down_peak'=>$this->extra_down_peak, + 'extra_down_offpeak'=>$this->extra_down_offpeak, + 'extra_up_peak'=>$this->extra_up_peak, + 'extra_up_offpeak'=>$this->extra_up_offpeak, + )); + + return $string ? implode('/',$output) : $output; + } +} +?> diff --git a/modules/product/views/product/adsl/feature_summary.php b/modules/product/views/product/adsl/feature_summary.php new file mode 100644 index 00000000..14b55700 --- /dev/null +++ b/modules/product/views/product/adsl/feature_summary.php @@ -0,0 +1,28 @@ + +
TAX INVOICEinvnum(); ?>id(); ?>
Issue Date
service->id,$item->service->svcnum()); ?>service->id,$item->service->id()); ?> product->product_translate->find()->name; ?> (product_id; ?>) items_service_total($item->service_id));?>
id,$invoice->invnum()); ?>id,$invoice->id()); ?> display('total_amt'); ?> display('credit_amt'); ?> display('billed_amt'); ?>
+ + + + + + + + + + + base_down_offpeak OR $po->extra_down_offpeak) { ?> + + + + + + + + + + + + + + +
Speeddisplay('speed'); ?>
 
 PeakOff Peak
Included Download Trafficbase_down_peak/1000; ?> GBbase_down_offpeak ? ($po->base_down_offpeak/1000).'GB' : HTML::nbsp(''); ?>
Extra Download Trafficdisplay('extra_down_peak'); ?>/GBextra_down_offpeak ? $po->display('extra_down_offpeak').'/GB' : HTML::nbsp(''); ?>
diff --git a/modules/service/classes/controller/admin/service.php b/modules/service/classes/controller/admin/service.php index 7dae0257..4d9fc7c2 100644 --- a/modules/service/classes/controller/admin/service.php +++ b/modules/service/classes/controller/admin/service.php @@ -9,6 +9,7 @@ * @author Deon George * @copyright (c) 2010 Open Source Billing * @license http://dev.osbill.net/license.html + * @todo Replace View::factory files to use $this->viewpath() */ class Controller_Admin_Service extends Controller_TemplateDefault { protected $control = array('Services'=>'services'); @@ -17,6 +18,7 @@ class Controller_Admin_Service extends Controller_TemplateDefault { 'listbycheckout'=>TRUE, 'listadslservices'=>TRUE, 'listhspaservices'=>TRUE, + 'update'=>TRUE, ); /** @@ -355,7 +357,7 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID Block::add(array( 'title'=>_('ADSL Services'), 'body'=>$output, - )); + )); if ($summary) Block::add(array( @@ -411,5 +413,28 @@ GROUP BY DATE_FORMAT(DATE,"%%Y-%%m"),SID return $return; } + + public function action_update($id) { + $so = ORM::factory('service',$id); + + if (! $so->loaded()) + Request::current()->redirect('welcome/index'); + + if ($_POST) { + if (isset($_POST['plugin']) AND $_POST['plugin']) + if (! $so->plugin()->values($_POST['plugin'])->update()->saved()) + throw new Kohana_Exception('Failed to save updates to plugin data for record :record',array(':record'=>$so->id())); + + if (! $so->values($_POST)->update()->saved()) + throw new Kohana_Exception('Failed to save updates to plugin data for record :record',array(':record'=>$so->id())); + } + + Block::add(array( + 'title'=>sprintf('%s %s:%s',_('Update Service'),$so->id(),$so->name()), + 'body'=>View::factory($so->viewpath()) + ->set('so',$so) + ->set('plugin_form',$so->admin_update()), + )); + } } ?> diff --git a/modules/service/classes/controller/user/service.php b/modules/service/classes/controller/user/service.php index d22d5737..cbd96e05 100644 --- a/modules/service/classes/controller/user/service.php +++ b/modules/service/classes/controller/user/service.php @@ -40,66 +40,15 @@ class Controller_User_Service extends Controller_TemplateDefault { public function action_view($id) { $so = ORM::factory('service',$id); - if (! $so->loaded() OR ! Auth::instance()->authorised($so->account_id)) { - $this->template->content = 'Unauthorised or doesnt exist?'; - return FALSE; - } - - $graph = $graph_data = $product_info = $product_detail = ''; - - // @todo Work this out dynamically - if ($so->product->prod_plugin AND in_array($so->product->prod_plugin_file,array('ADSL'))) { - - $google = GoogleChart::factory('vertical_bar'); - - // If we came in via a post to show a particular month, then show that, otherwise show the yearly result - if ($_POST AND isset($_POST['month'])) { - $google->title = sprintf('DSL traffic usage for %s',$_POST['month']); - $traffic_data = $so->service_adsl->get_traffic_data_daily(strtotime($_POST['month'].'-01')); - - foreach ($traffic_data as $k => $details) - $google->series(array( - 'title'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)), - 'axis'=>'x', - 'data'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)=>$traffic_data[$k]))); - - foreach ($traffic_data as $k => $details) - $google->series(array( - 'title'=>array((isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)), - 'axis'=>'r', - 'data'=>array((isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)=>$so->service_adsl->cumulative($traffic_data[$k])))); - - $graph_data = View::factory('service/view_detail_adsl_traffic') - ->set('traffic',$so->service_adsl->traffic_month(strtotime($_POST['month'].'-01'),FALSE)); - - } else { - // @todo Change the date to the last record date - $google->title = sprintf('Monthly DSL traffic usage as at %s',Config::date(strtotime('yesterday'))); - $traffic_data = $so->service_adsl->get_traffic_data_monthly(); - - foreach ($traffic_data as $k => $details) - $google->series(array( - 'title'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)), - 'axis'=>'x', - 'data'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)=>$traffic_data[$k]))); - } - - $graph = (string)$google; - - $product_info = ADSL::instance()->product_view($so->service_adsl->adsl_plan_id,$so->price,$so->product->price_setup); - $product_detail = View::factory('service/view_detail_adsl') - ->set('graph',$graph) - ->set('graph_data',$graph_data) - ->set('service',$so); + if (! $so->loaded() OR ! Auth::instance()->authorised($so->account_id)) { + $this->template->content = 'Unauthorised or doesnt exist?'; + return FALSE; } - // @todo need to get the monthly price Block::add(array( - 'title'=>sprintf('%06s: %s',$so->id,$so->product->product_translate->find()->name), + 'title'=>sprintf('%s: %s',$so->id(),$so->product->name()), 'body'=>View::factory('service/view') - ->set('service',$so) - ->set('product_info',$product_info) - ->set('product_detail',$product_detail), + ->set('so',$so), )); } } diff --git a/modules/service/classes/model/service.php b/modules/service/classes/model/service.php index 0b66271e..0d19e9ce 100644 --- a/modules/service/classes/model/service.php +++ b/modules/service/classes/model/service.php @@ -14,10 +14,10 @@ class Model_Service extends ORMOSB { // Relationships protected $_has_many = array( 'invoice'=>array('through'=>'invoice_item'), - 'adsl_plan'=>array('through'=>'service__adsl'), ); protected $_has_one = array( 'service_adsl'=>array('far_key'=>'id'), + 'service_domain'=>array('far_key'=>'id'), ); protected $_belongs_to = array( 'product'=>array(), @@ -31,6 +31,9 @@ class Model_Service extends ORMOSB { 'active'=>array( array('StaticList_YesNo::display',array(':value')), ), + 'date_last_invoice'=>array( + array('Config::date',array(':value')), + ), 'date_next_invoice'=>array( array('Config::date',array(':value')), ), @@ -43,23 +46,107 @@ class Model_Service extends ORMOSB { ), ); + /** + * The service_name should be implemented in child objects. + * It renders the name of the service, typically used on invoice + */ + protected function _service_name() { + throw new Kohana_Exception(':method not defined in child class :class',array(':method'=>__METHOD__,':class'=>get_class($this))); + } + + /** + * The service_view should be implemented in child objects. + * It renders the details of the ordered service + */ + protected function _service_view() { + throw new Kohana_Exception(':method not defined in child class :class',array(':method'=>__METHOD__,':class'=>get_class($this))); + } + + /** + * The _details should be implemented in child objects. + */ + protected function _details($type) { + throw new Kohana_Exception(':method not defined in child class :class',array(':method'=>__METHOD__,':class'=>get_class($this))); + } + + protected function _admin_update() { + throw new Kohana_Exception(':method not defined in child class :class',array(':method'=>__METHOD__,':class'=>get_class($this))); + } + + /** + * Return the object of the product plugin + */ + public function plugin() { + if (! $this->product->prod_plugin_file) + return NULL; + + if (! is_numeric($this->product->prod_plugin_data)) + throw new Kohana_Exception('Missing plugin_id for :product (:type)',array(':product'=>$this->product->id,':type'=>$this->product->prod_plugin_file)); + + $spn = sprintf('%s_%s',get_class($this),$this->product->prod_plugin_file); + return new $spn(array('service_id'=>$this->id)); + } + /** * Display the service number */ - public function svcnum() { + public function id() { return sprintf('%05s',$this->id); } - // Nothing to directly display on invoices for this module. - public function invoice_display() { - if ($this->sku) - return sprintf('%s: %s',_('Service'),$this->sku); - else - return ''; + /** + * Display the service product name + */ + public function name() { + return $this->product->name(); } - public function name() { - return $this->product->product_translate->find()->name; + /** + * Display the product feature summary + */ + public function product_feature_summary() { + return $this->product->feature_summary(); + } + + /** + * Display the service details + */ + public function service_view() { + if (is_null($plugin = $this->plugin())) + return HTML::nbsp(''); + else + return $plugin->_service_view(); + } + + public function service_name() { + if (is_null($plugin = $this->plugin())) + return $this->name(); + else + return $plugin->_service_name(); + } + + /** + * Render some details for specific calls, eg: invoice + */ + public function details($type) { + switch ($type) { + case 'invoice': + if (is_null($plugin = $this->plugin())) + return array(); + else + return $plugin->_details($type); + break; + + default: + throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type)); + } + } + + public function admin_update() { + if (is_null($plugin = $this->plugin())) + return NULL; + else + return $plugin->_admin_update(); } // @todo To implement diff --git a/modules/service/classes/model/service/adsl.php b/modules/service/classes/model/service/adsl.php index e501fdd7..38967ef4 100644 --- a/modules/service/classes/model/service/adsl.php +++ b/modules/service/classes/model/service/adsl.php @@ -10,7 +10,7 @@ * @copyright (c) 2010 Open Source Billing * @license http://dev.osbill.net/license.html */ -class Model_Service_ADSL extends ORMOSB { +class Model_Service_ADSL extends Model_Service { protected $_table_name = 'service__adsl'; protected $_updated_column = FALSE; @@ -20,6 +20,13 @@ class Model_Service_ADSL extends ORMOSB { 'service'=>array(), ); + protected $_display_filters = array( + 'service_connect_date'=>array( + array('Config::date',array(':value')), + ), + + ); + /** * Return the IP Address for the service */ @@ -27,14 +34,8 @@ class Model_Service_ADSL extends ORMOSB { return $this->ipaddress ? $this->ipaddress : _('Dynamic'); } - public function contract_date_start() { - //@todo Use the system configured date format - return date('Y-m-d',$this->service_connect_date); - } - public function contract_date_end() { - //@todo Use the system configured date format - return date('Y-m-d',strtotime(sprintf('+%s months',$this->adsl_plan->contract_term),$this->service_connect_date)); + return Config::date(strtotime(sprintf('+%s months',$this->contract_term),$this->service_connect_date)); } /** @@ -324,5 +325,79 @@ class Model_Service_ADSL extends ORMOSB { // If we get here, then we dont need to report usage. return FALSE; } + + protected function _service_name() { + return sprintf('%s - %s',$this->service->product->name(),$this->service_number); + } + + protected function _service_view() { + return View::factory('service/adsl/view') + ->set('so',$this); + } + + /** + * Get specific service details for use in other modules + * For Example: Invoice + * + * @todo Make the rendered items configurable + */ + protected function _details($type) { + switch ($type) { + case 'invoice': + return array( + _('Service Address')=>$this->display('service_address'), + _('Contact Until')=>$this->contract_date_end(), + ); + break; + default: + throw new Kohana_Exception('Unkown detail request :type',array(':type'=>$type)); + } + } + + protected function _admin_update() { + return View::factory($this->viewpath(strtolower($this->service->prod_plugin_name))) + ->set('so',$this); + } + + /** + * Render a google chart of traffic + */ + public function graph_traffic($month=null) { + $google = GoogleChart::factory('vertical_bar'); + + // If we came in via a post to show a particular month, then show that, otherwise show the yearly result + if (! is_null($month) AND trim($month)) { + $google->title = sprintf('DSL traffic usage for %s',$_POST['month']); + $traffic_data = $this->get_traffic_data_daily(strtotime($_POST['month'].'-01')); + + foreach ($traffic_data as $k => $details) + $google->series(array( + 'title'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)), + 'axis'=>'x', + 'data'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)=>$traffic_data[$k]))); + + foreach ($traffic_data as $k => $details) + $google->series(array( + 'title'=>array((isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)), + 'axis'=>'r', + 'data'=>array((isset($friendly['cumulative'.$k]) ? $friendly['cumulative'.$k] : 'cumulative'.$k)=>$this->cumulative($traffic_data[$k])))); + + $graph_data = View::factory('service/view_detail_adsl_traffic') + ->set('traffic',$this->traffic_month(strtotime($_POST['month'].'-01'),FALSE)); + + } else { + // @todo Change the date to the last record date + $google->title = sprintf('Monthly DSL traffic usage as at %s',Config::date(strtotime('yesterday'))); + $traffic_data = $this->get_traffic_data_monthly(); + + foreach ($traffic_data as $k => $details) + $google->series(array( + 'title'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)), + 'axis'=>'x', + 'data'=>array((isset($friendly[$k]) ? $friendly[$k] : $k)=>$traffic_data[$k]))); + } + + return (string)$google; + } } ?> diff --git a/modules/service/service_construct.xml b/modules/service/service_construct.xml index be8915e3..58d22240 100644 --- a/modules/service/service_construct.xml +++ b/modules/service/service_construct.xml @@ -19,7 +19,7 @@ site_id,date_orig site_id,parent_id - site_id,invoice_id + site_id site_id,invoice_item_id site_id,account_id site_id,account_billing_id @@ -132,9 +132,6 @@ I4 - - I4 - User Change Schedule L @@ -143,26 +140,9 @@ User May Cancel C(16) - - X2 - array - - - I4 - - - I4 - I4 - - X2 - array - - - C(16) - Hosting User Name C(128) @@ -200,9 +180,6 @@ array X2 - - X2 - C(128) @@ -214,23 +191,15 @@ User May Modify L - - L - - id,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_week,recur_schedule_change,recur_cancel,group_grant,group_type,group_days,host_server_id,host_provision_plugin_data,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify - id,date_last,account_id,account_billing_id,product_id,sku,active,type,price,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_week,recur_schedule_change,recur_cancel,group_grant,group_type,group_days,host_server_id,host_provision_plugin_data,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr + id,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_schedule_change,recur_cancel,host_server_id,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify + id,date_last,account_id,account_billing_id,product_id,sku,active,type,price,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_schedule_change,recur_cancel,host_server_id,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr id - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_week,recur_schedule_change,recur_cancel,group_grant,group_type,group_days,host_server_id,host_provision_plugin_data,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_week,recur_schedule_change,recur_cancel,group_grant,group_type,group_days,host_server_id,host_provision_plugin_data,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr - id,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,host_server_id,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,host_server_id,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,host_server_id,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,host_server_id,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing - id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,host_server_id,host_ip,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing + id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_schedule_change,recur_cancel,host_server_id,host_username,host_password,domain_name,domain_tld,domain_term,domain_type,domain_date_expire,domain_host_tld_id,domain_host_registrar_id,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr + id,date_orig,date_last,account_id,account_billing_id,product_id,sku,active,type,price,price_type,taxable,queue,date_last_invoice,date_next_invoice,recur_type,recur_schedule,recur_weekday,recur_schedule_change,recur_cancel,suspend_billing,prod_plugin_name,prod_plugin_data,recur_modify,prod_attr diff --git a/modules/service/views/service/admin/adsl/update.php b/modules/service/views/service/admin/adsl/update.php new file mode 100644 index 00000000..ce66d54f --- /dev/null +++ b/modules/service/views/service/admin/adsl/update.php @@ -0,0 +1,36 @@ + + + + + + + + +
Plugin Details
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Service Numberservice_number); ?>
Service Addressservice_address); ?>
Service Connect Dateservice_connect_date); ?>
Service Usernameservice_username); ?>
Service Passwordservice_password); ?>
Service IP Addressipaddress); ?>
+
diff --git a/modules/service/views/service/admin/update.php b/modules/service/views/service/admin/update.php new file mode 100644 index 00000000..facd6a10 --- /dev/null +++ b/modules/service/views/service/admin/update.php @@ -0,0 +1,62 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service Activeactive); ?>
Queuedisplay('queue'); ?>
Billing Periodproduct,$so->recur_schedule);?>
Date Last Invoicedisplay('date_last_invoice'); ?>
Date Next Invoicedisplay('date_next_invoice'); ?>
Taxabletaxable); ?>
Typerecur_type); ?>
Recur on Weekdayrecur_weekday,array('size'=>4)); ?>
User Can Change Schedulerecur_schedule_change); ?>
User Can Cancelrecur_cancel); ?>
User Can Modifyrecur_modify); ?>
Suspend Billingsuspend_billing); ?>
+ '.$plugin_form; } ?> + +
+ + diff --git a/modules/service/views/service/adsl/view.php b/modules/service/views/service/adsl/view.php new file mode 100644 index 00000000..31613f7a --- /dev/null +++ b/modules/service/views/service/adsl/view.php @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + +
Service Details
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service Numberdisplay('service_number'); ?>
Service Addressdisplay('service_address'); ?>
Contract Termdisplay('contract_term'); ?>
Connect Datedisplay('service_connect_date'); ?>
Contract End Datecontract_date_end(); ?>
Service Usernamedisplay('service_username'); ?>
Service Passworddisplay('service_password'); ?>
Service IPipaddress(); ?>
+
+ + + + + + + + + +
Traffic Used This Monthtraffic_month(null); ?>
Traffic Used Last Monthtraffic_lastmonth(); ?>
+
+ + + + + +
View Daily Traffic for Monthget_traffic_months()),(isset($_POST['month']) ? $_POST['month'] : '')); echo Form::submit('submit',_('Show')); echo Form::close(); ?>
+
graph_traffic(isset($_POST['month']) ? $_POST['month'] : ''); ?> +
diff --git a/modules/service/views/service/view.php b/modules/service/views/service/view.php index e458e334..63c00226 100644 --- a/modules/service/views/service/view.php +++ b/modules/service/views/service/view.php @@ -3,37 +3,62 @@ - +
- - - - - - + + - + - + - + - +
Service Nameproduct->product_translate->find()->name; ?>
Service Activedisplay('active'); ?>Service Activedisplay('active'); ?>
Billing Perioddisplay('recur_schedule');?>display('recur_schedule');?>
Costdisplay('price'); ?>display('price'); ?>
Date Next Invoicedate_next_invoice); ?>display('date_next_invoice'); ?>
Current Invoices Dueaccount->invoices_due_total()); ?>account->invoices_due_total()); ?>
+ - - + + product_feature_summary(); ?> + - +
+service_view(); ?> +
+ + + + + + + + + + + + + + + invoice->distinct('id')->order_by('id DESC')->find_all() as $io) { ?> + + + + + + + + + +
Invoices for this service
 
NumberInvoice DateDue DateTotalBalance
id,HTML::image('media/img/gnome-pdf.png',array('alt'=>_('Download'),'width'=>20))); ?>id,$io->id()); ?>display('date_orig'); ?>display('due_date'); ?>total(TRUE); ?>due(TRUE); ?>
diff --git a/modules/service/views/service/view_detail_adsl.php b/modules/service/views/service/view_detail_adsl.php deleted file mode 100644 index 0f04a4a7..00000000 --- a/modules/service/views/service/view_detail_adsl.php +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
View Daily Traffic for Monthservice_adsl->get_traffic_months(),(isset($_POST['month']) ? $_POST['month'] : '')); echo Form::submit('submit',_('Show')); echo Form::close(); ?>
 
- - - - - - - - - - - - - -
ADSL Serviceservice_adsl->service_number; ?>
Contract Termservice_adsl->contract_term; ?>
Contract Endservice_adsl->contract_date_end(); ?>
-