From 778eada7f0eb0e538e0f758a0a634efaf02f5d4f Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 5 Dec 2013 16:22:23 +1100 Subject: [PATCH] Improvements to taxing --- application/bootstrap.php | 4 + application/classes/Currency.php | 4 +- application/classes/Model/Account.php | 12 +- application/classes/Model/Country.php | 4 + application/classes/ORM/OSB.php | 15 ++ .../classes/Checkout/Plugin/Paypal.php | 10 +- .../export/classes/Export/Plugin/Quicken.php | 4 +- modules/invoice/classes/Invoice.php | 38 ++-- .../invoice/classes/Invoice/Tcpdf/Default.php | 2 +- modules/invoice/classes/Model/Invoice.php | 152 ++++++------- .../invoice/classes/Model/Invoice/Item.php | 210 ++++++++++-------- modules/payment/classes/Model/Payment.php | 8 +- .../views/payment/admin/ajaxitemlist.php | 2 +- 13 files changed, 253 insertions(+), 212 deletions(-) diff --git a/application/bootstrap.php b/application/bootstrap.php index 32e476a2..6b792784 100644 --- a/application/bootstrap.php +++ b/application/bootstrap.php @@ -168,4 +168,8 @@ if (file_exists(APPPATH.'cache/CLEAR_APC_CACHE') AND function_exists('apc_clear_ if (! apc_clear_cache() OR ! unlink(APPPATH.'cache/CLEAR_APC_CACHE')) throw new Kohana_Exception('Unable to clear the APC cache.'); } + +// If we are a CLI, set our session dir +if (PHP_SAPI === 'cli') + session_save_path('tmp/'); ?> diff --git a/application/classes/Currency.php b/application/classes/Currency.php index 95a0582e..2f29d0cf 100644 --- a/application/classes/Currency.php +++ b/application/classes/Currency.php @@ -17,8 +17,8 @@ class Currency { return Num::format($amount,Company::instance()->decimals(),TRUE); } - public static function round($amount) { - return Num::round($amount,Company::instance()->decimals()); + public static function round($amount,$decimals=0) { + return Num::round($amount,Company::instance()->decimals()+$decimals); } } ?> diff --git a/application/classes/Model/Account.php b/application/classes/Model/Account.php index c891a0ed..97e8c092 100644 --- a/application/classes/Model/Account.php +++ b/application/classes/Model/Account.php @@ -12,13 +12,13 @@ class Model_Account extends Model_Auth_UserDefault { // Relationships protected $_has_many = array( - 'user_tokens' => array('model' => 'user_token'), - 'service_billing' => array('far_key'=>'id'), - 'email_log' => array('far_key'=>'id'), - 'group' => array('through' => 'account_group'), - 'invoice' => array('far_key'=>'id'), + 'user_tokens'=>array('model'=>'user_token'), + 'service_billing'=>array('far_key'=>'id'), + 'email_log'=>array('far_key'=>'id'), + 'group'=>array('through'=>'account_group'), + 'invoice'=>array('far_key'=>'id'), 'payment'=>array('far_key'=>'id'), - 'service' => array('far_key'=>'id'), + 'service'=>array('far_key'=>'id'), ); protected $_has_one = array( diff --git a/application/classes/Model/Country.php b/application/classes/Model/Country.php index 4465db21..5fa25e79 100644 --- a/application/classes/Model/Country.php +++ b/application/classes/Model/Country.php @@ -14,6 +14,10 @@ class Model_Country extends ORM_OSB { 'currency'=>array('far_key'=>'id'), ); + protected $_has_many = array( + 'tax'=>array('far_key'=>'id'), + ); + protected $_sorting = array( 'name'=>'ASC', ); diff --git a/application/classes/ORM/OSB.php b/application/classes/ORM/OSB.php index 3ae56306..cea60df9 100644 --- a/application/classes/ORM/OSB.php +++ b/application/classes/ORM/OSB.php @@ -163,6 +163,17 @@ abstract class ORM_OSB extends ORM { return empty($mc[$key]) ? '' : $mc[$key]; } + public function dump() { + $result = array(); + + $result['this'] = $this->object(); + + foreach ($this->_sub_items as $o) + $result['sub'][] = $o->dump(); + + return $result; + } + /** * Get Next record id * @@ -240,6 +251,10 @@ abstract class ORM_OSB extends ORM { return TRUE; } + public function subitems() { + return $this->_sub_items; + } + /** * Override the Kohana processing so we can null values if required. */ diff --git a/modules/checkout/classes/Checkout/Plugin/Paypal.php b/modules/checkout/classes/Checkout/Plugin/Paypal.php index 5f482132..1332e5c3 100644 --- a/modules/checkout/classes/Checkout/Plugin/Paypal.php +++ b/modules/checkout/classes/Checkout/Plugin/Paypal.php @@ -104,20 +104,22 @@ abstract class Checkout_Plugin_Paypal extends Checkout_Plugin { // We assume fees are added to $po->items() which are invoices. if (preg_match('/^0:/',$cno->data['item_number'.$c])) { $i = $j = 0; - foreach ($po->items() as $pio) { + foreach ($po->subitems() as $pio) { $io = ORM::factory('Invoice',$pio->invoice_id); - // @todo Need to do tax. - $iio = $io->add_item(); + + $iio = ORM::factory('Invoice_Item'); $iio->quantity = 1; $iio->module_id = $cno->mid()->id; $iio->item_type = 125; // Payment Fee $iio->price_base = (++$j==count($amts)) ? $cno->data['mc_gross_'.$c]-$i : Currency::round($pio->alloc_amt/array_sum($amts)*$cno->data['mc_gross_'.$c]); $iio->date_start = $iio->date_stop = time(); + $io->subitem_add($iio,$io->account->country,TRUE); + // @todo Validate Save $io->save(); - $pio->alloc_amt = ($pio->alloc_amt+$iio->price_base > $pio->invoice->total()) ? $pio->alloc_amt : $pio->alloc_amt+$iio->price_base; + $pio->alloc_amt = ($pio->alloc_amt+$iio->total() > $pio->invoice->total()) ? $pio->alloc_amt : $pio->alloc_amt+$iio->total(); $i += $iio->price_base; } diff --git a/modules/export/classes/Export/Plugin/Quicken.php b/modules/export/classes/Export/Plugin/Quicken.php index e150731c..6f74b6c5 100644 --- a/modules/export/classes/Export/Plugin/Quicken.php +++ b/modules/export/classes/Export/Plugin/Quicken.php @@ -83,7 +83,7 @@ class Export_Plugin_Quicken extends Export_Plugin { $c = 0; // Add the items to the invoice - foreach ($io->items('CHARGE') as $iio) { + foreach ($io->subitems('CHARGE') as $iio) { // Skip any zero amount items not relating to a service if ($iio->total() == 0 and ! $iio->service_id) continue; @@ -141,7 +141,7 @@ class Export_Plugin_Quicken extends Export_Plugin { } // Add credits as a other item - foreach ($io->items('CREDIT') as $iio) { + foreach ($io->subitems('CREDIT') as $iio) { $items[$c]['ACCNT'] = 'Other Income'; $items[$c]['INVITEM'] = 'Product:Unknown'; $items[$c]['CLEAR'] = 'N'; diff --git a/modules/invoice/classes/Invoice.php b/modules/invoice/classes/Invoice.php index 2452562b..4d8b7f55 100644 --- a/modules/invoice/classes/Invoice.php +++ b/modules/invoice/classes/Invoice.php @@ -11,28 +11,30 @@ */ class Invoice { // This invoice Object - private $io; + private $_io; private $_methods = array( + 'dump', 'min_due', 'save', 'saved', + 'total', ); // Enable passing calls to ORM::Invoice() public function __call($method,array $args) { if (in_array($method,$this->_methods)) - return call_user_func_array(array($this->io,$method),$args); + return call_user_func_array(array($this->_io,$method),$args); else throw HTTP_Exception::factory(501,'Unknown/unauthorised method :method',array(':method'=>$method)); } public function __construct(Model_Invoice $io=NULL) { - $this->io = is_null($io) ? ORM::factory('Invoice') : $io; + $this->_io = is_null($io) ? ORM::factory('Invoice') : $io; // Set our invoice as valid if (is_null($io)) - $this->io->status = 1; + $this->_io->status = 1; } public static function instance(Model_Invoice $io=NULL) { @@ -45,18 +47,18 @@ class Invoice { * @param $so Model_Servie */ public function add_service(Model_Service $so) { - if ($this->io->loaded()) - throw HTTP_Exception::factory(501,'Cannot use add service :service to an existing invoice :invoice ',array(':service'=>$so->id,':invoice'=>$this->io->id)); + if ($this->_io->loaded()) + throw HTTP_Exception::factory(501,'Cannot use add service :service to an existing invoice :invoice ',array(':service'=>$so->id,':invoice'=>$this->_io->id)); - if (! $this->io->account_id) - $this->io->account_id = $so->account_id; + if (! $this->_io->account_id) + $this->_io->account_id = $so->account_id; - else if ($this->io->account_id != $so->account_id) + else if ($this->_io->account_id != $so->account_id) throw HTTP_Exception::factory(501,'Cannot add service :service to invoice - it is for a different account',array(':service'=>$so->id)); // Set the invoice due date - if (! $this->io->due_date OR $this->io->due_date > $this->io->min_due($so->date_next_invoice)) - $this->io->due_date = $this->io->min_due($so->date_next_invoice); + if (! $this->_io->due_date OR $this->_io->due_date > $this->_io->min_due($so->date_next_invoice)) + $this->_io->due_date = $this->_io->min_due($so->date_next_invoice); $pdata = Period::details($so->recur_schedule,$so->product->price_recurr_day,$so->invoiced_to()+86400,FALSE,$so->product->price_recurr_strict); @@ -72,7 +74,7 @@ class Invoice { // Service Billing $iio->item_type = 0; - $this->io->add_item($iio); + $this->_io->subitem_add($iio,$this->_io->account->country); // Check if there are any charges $c = ORM::factory('Charge') @@ -91,7 +93,7 @@ class Invoice { $iio->date_stop = $co->date_orig; $iio->item_type = $co->type; - $this->io->add_item($iio); + $this->_io->subitem_add($iio,$this->_io->account->country); } return $this; @@ -104,7 +106,7 @@ class Invoice { case 'body': return View::factory('invoice/user/view/body') ->set('show_id',(isset($args['noid']) AND $args['noid']) ? FALSE : TRUE) - ->set('o',$this->io); + ->set('o',$this->_io); break; default: @@ -124,7 +126,7 @@ class Invoice { public function pdf() { $invoice_class = Kohana::classname('Invoice_TCPDF_'.Kohana::$config->load('invoice')->driver); - $pdf = new $invoice_class($this->io); + $pdf = new $invoice_class($this->_io); if ($pdf->getTemplate()) { $pagecount = $pdf->setSourceFile($pdf->getTemplate()); @@ -153,12 +155,12 @@ class Invoice { $pdf->drawRemittenceStub(); $pdf->drawPaymentMethods(); - if ($this->io->billing_status !=1 && $this->io->due_date <= time()) + if ($this->_io->billing_status !=1 && $this->_io->due_date <= time()) $pdf->drawInvoiceDueNotice(); - elseif($this->io->billing_status == 1) + elseif($this->_io->billing_status == 1) $pdf->drawInvoicePaidNotice(); - if ($this->io->account->invoices_due_total()) + if ($this->_io->account->invoices_due_total()) $pdf->drawSummaryInvoicesDue(); $pdf->drawSummaryLineItems(); diff --git a/modules/invoice/classes/Invoice/Tcpdf/Default.php b/modules/invoice/classes/Invoice/Tcpdf/Default.php index 7d30ac22..defbedf3 100644 --- a/modules/invoice/classes/Invoice/Tcpdf/Default.php +++ b/modules/invoice/classes/Invoice/Tcpdf/Default.php @@ -439,7 +439,7 @@ class Invoice_TCPDF_Default extends Invoice_Tcpdf { */ public function drawDetailLineItems() { $this->i = 0; - foreach ($this->io->items() as $io) + foreach ($this->io->subitems() as $io) $this->drawLineItem($io); } diff --git a/modules/invoice/classes/Model/Invoice.php b/modules/invoice/classes/Model/Invoice.php index c2ac392c..11e302f6 100644 --- a/modules/invoice/classes/Model/Invoice.php +++ b/modules/invoice/classes/Model/Invoice.php @@ -55,23 +55,6 @@ class Model_Invoice extends ORM_OSB implements Cartable { return count(Cart::instance()->get($this->mid(),$this->id)); } - /** - * Add an item to an invoice - */ - public function add_item(Model_Invoice_Item $iio=NULL) { - // @todo This is the old calling of this function, which should be removed - if (is_null($iio)) { - $c = count($this->_sub_items); - - $this->_sub_items[$c] = ORM::factory('Invoice_Item'); - - return $this->_sub_items[$c]; - } - - array_push($this->_sub_items,$iio); - $this->_sub_items_sorted = FALSE; - } - /** * Return the recurring schedules that are on an invoice * @@ -80,7 +63,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function items_periods($period=FALSE) { $result = array(); - foreach ($this->items() as $ito) { + foreach ($this->subitems() as $ito) { // We are only interested in item_type=0 if ($ito->item_type != 0) continue; @@ -106,7 +89,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function items_titles($title=NULL,$period=NULL) { $result = array(); - foreach ($this->items() as $ito) { + foreach ($this->subitems() as $ito) { if ($ito->service_id) { if (is_null($title) AND ! in_array($ito->title(),$result) AND (is_null($period) OR ($period == $ito->recurring_schedule))) array_push($result,$ito->title()); @@ -156,7 +139,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { if (! $o->service_id) return $result; - foreach ($this->items() as $iio) + foreach ($this->subitems() as $iio) if ($iio->item_type == 0 AND $iio->service_id == $o->service_id) array_push($result,$iio); @@ -174,7 +157,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { if (! $o->service_id) return $result; - foreach ($this->items() as $iio) + foreach ($this->subitems() as $iio) if ($iio->item_type != 0 AND $iio->service_id == $o->service_id) array_push($result,$iio); @@ -191,7 +174,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { if (! $o->service_id) return $result; - foreach ($this->items() as $iio) + foreach ($this->subitems() as $iio) if ($iio->service_id == $o->service_id) $result += $iio->tax(); @@ -208,13 +191,63 @@ class Model_Invoice extends ORM_OSB implements Cartable { if (! $o->service_id) return $result; - foreach ($this->items() as $iio) + foreach ($this->subitems() as $iio) if ($iio->service_id == $o->service_id) $result += $iio->total(); return $format ? Currency::display($result) : $result; } + /** + * Add an item to an invoice + */ + public function subitem_add(Model_Invoice_Item $iio,Model_Country $co,$taxed=FALSE) { + $iio->subitem_add($co,$taxed); + + array_push($this->_sub_items,$iio); + + $this->_sub_items_sorted = FALSE; + } + + /** + * Return a list of invoice items for this invoice. + * @param type [CHARGE|CREDIT|ALL] + * @see invoice_items + */ + public function subitems($type='ALL') { + $result = array(); + + foreach ($this->_sub_items as $ito) { + // Ignore voided items + if ($ito->void) + continue; + + $return = FALSE; + + switch ($type) { + case 'CHARGE': + if ($ito->product_id OR $ito->quantity > 0) + $return = TRUE; + break; + + case 'CREDIT': + if (! $ito->product_id AND $ito->quantity < 0) + $return = TRUE; + break; + + case 'ALL': + default: + $return = TRUE; + break; + } + + if ($return) + array_push($result,$ito); + } + + return $result; + } + /** * Return a list of valid checkout options for this invoice */ @@ -247,45 +280,6 @@ class Model_Invoice extends ORM_OSB implements Cartable { return sprintf('%06s',$this->id); } - /** - * Return a list of invoice items for this invoice. - * @param type [CHARGE|CREDIT|ALL] - * @see invoice_items - */ - public function items($type='ALL') { - $result = array(); - - foreach ($this->_sub_items as $ito) { - // Ignore voided items - if ($ito->void) - continue; - - $return = FALSE; - - switch ($type) { - case 'CHARGE': - if ($ito->product_id OR $ito->quantity > 0) - $return = TRUE; - break; - - case 'CREDIT': - if (! $ito->product_id AND $ito->quantity < 0) - $return = TRUE; - break; - - case 'ALL': - default: - $return = TRUE; - break; - } - - if ($return) - array_push($result,$ito); - } - - return $result; - } - /** * Provide a sorted list of items by an index */ @@ -296,7 +290,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { if (! $this->_changed AND array_key_exists($index,$result)) return $result[$index]; - foreach ($this->items() as $ito) { + foreach ($this->subitems() as $ito) { switch ($index) { case 'account': if (! $ito->service_id) @@ -332,7 +326,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function items_services(array $items=array()) { $result = array(); if (! $items) - $items = $this->items(); + $items = $this->subitems(); foreach ($items as $ito) if ($ito->service_id AND empty($result[$ito->service_id])) @@ -344,7 +338,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { // @todo to retire public function items_invoice() { $result = array(); - $items = $this->items(); + $items = $this->subitems(); foreach ($items as $ito) if (! $ito->service_id AND empty($result[$ito->id])) @@ -377,7 +371,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { $result = array(); $c = array(); - foreach ($this->items() as $ito) + foreach ($this->subitems() as $ito) if ($ito->service_id) { // If we have already covered a service with no recurring_schedule if (! $ito->recurring_schedule AND in_array($ito->service_id,$c)) @@ -400,7 +394,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function items_summary() { $result = array(); - foreach ($this->items() as $ito) { + foreach ($this->subitems() as $ito) { // We only summarise item_type=0 if (! $ito->item_type == 0) continue; @@ -491,7 +485,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function save(Validation $validation = NULL) { // Our items will be clobbered once we save the object, so we need to save it here. - $items = $this->items(); + $subitems = $this->subitems(); // If this is a new invoice, we'll need to do more work $new = ! $this->loaded(); @@ -501,7 +495,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { // Need to save the associated items and their taxes if ($this->loaded()) { - foreach ($items as $iio) { + foreach ($subitems as $iio) { $iio->invoice_id = $this->id; if (! $iio->changed()) @@ -510,8 +504,10 @@ class Model_Invoice extends ORM_OSB implements Cartable { $iio->save(); if (! $iio->saved()) { - // @todo Mark invoice as cancelled and write a memo, then... - throw new Kohana_Exception('Problem saving invoice_item for invoice :invoice - Failed save()',array(':invoice'=>$this->id)); + $this->void = 1; + $this->save(); + + break; } // If this is a new invoice, we'll need to update some other items @@ -573,7 +569,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function subtotal($format=FALSE) { $result = 0; - foreach ($this->items() as $ito) + foreach ($this->subitems() as $ito) $result += $ito->subtotal(); return $format ? Currency::display($result) : Currency::round($result); @@ -582,7 +578,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function tax($format=FALSE) { $result = 0; - foreach ($this->items() as $ito) + foreach ($this->subitems() as $ito) $result += $ito->tax(); return $format ? Currency::display($result) : Currency::round($result); @@ -595,7 +591,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function tax_summary() { $summary = array(); - foreach ($this->items() as $ito) { + foreach ($this->subitems() as $ito) { foreach ($ito->invoice_item_tax->find_all() as $item_tax) { if (! isset($summary[$item_tax->tax_id])) $summary[$item_tax->tax_id] = $item_tax->amount; @@ -618,7 +614,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { $result = 0; // This will include charges and credits - foreach ($this->items() as $ito) + foreach ($this->subitems() as $ito) $result += $ito->total(); return $format ? Currency::display($result) : Currency::round($result); @@ -627,7 +623,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function total_charges($format=FALSE) { $result = 0; - foreach ($this->items('CHARGE') as $ito) + foreach ($this->subitems('CHARGE') as $ito) $result += $ito->subtotal()+$ito->tax(); return $format ? Currency::display($result) : Currency::round($result); @@ -636,7 +632,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function total_credits($format=FALSE) { $result = 0; - foreach ($this->items('CREDIT') as $ito) + foreach ($this->subitems('CREDIT') as $ito) $result += ($ito->subtotal()+$ito->tax())*-1; return $format ? Currency::display($result) : Currency::round($result); @@ -645,7 +641,7 @@ class Model_Invoice extends ORM_OSB implements Cartable { public function total_discounts($format=FALSE) { $result = 0; - foreach ($this->items() as $ito) + foreach ($this->subitems() as $ito) $result += $ito->discount(); return $format ? Currency::display($result) : Currency::round($result); diff --git a/modules/invoice/classes/Model/Invoice/Item.php b/modules/invoice/classes/Model/Invoice/Item.php index 8cd1c51e..3f9f17ce 100644 --- a/modules/invoice/classes/Model/Invoice/Item.php +++ b/modules/invoice/classes/Model/Invoice/Item.php @@ -43,6 +43,108 @@ class Model_Invoice_Item extends ORM_OSB { 'invoice_item_tax'=>FALSE, ); + // The total of all discounts + public function discount() { + return Currency::round($this->discount_amt); + } + + // Display the period that a transaction applies + public function period() { + return ($this->date_start == $this->date_stop) ? Config::date($this->date_start) : sprintf('%s -> %s',Config::date($this->date_start),Config::date($this->date_stop)); + } + + public function save(Validation $validation = NULL) { + // Our items will be clobbered once we save the object, so we need to save it here. + $subitems = $this->subitems(); + + // Save the invoice item + parent::save($validation); + + // Need to save the associated items and their taxes + if ($this->loaded()) { + foreach ($subitems as $iito) { + $iito->invoice_item_id = $this->id; + + if (! $iito->changed()) + continue; + + $iito->save(); + + if (! $iito->saved()) { + $this->void = 1; + $this->save(); + + break; + } + + } + } else + throw new Kohana_Exception('Couldnt save invoice_item for some reason?'); + + return $this; + } + + /** + * Add tax to our item + * + * @param Model_Country the country to get the tax rates + * @param Boolean Is this price inc/ex tax + */ + public function subitem_add(Model_Country $co,$taxed=FALSE) { + $orig = $this->subtotal(); + $tax = 0; + + foreach ($co->tax->find_all() as $to) { + $iito = ORM::factory('Invoice_Item_Tax'); + + $iito->tax_id = $to->id; + $iito->amount = Currency::round($taxed ? ($orig-$orig/(1+$to->rate)) : $orig*$to->rate); + + $tax += $iito->amount; + array_push($this->_sub_items,$iito); + } + + // If taxed, we need to reduce our base_rate to a pre-tax amount + if ($taxed) { + $this->price_base -= Currency::round($tax/$this->quantity,1); + + // If there is any rounding, we'll take it off the last IITO + $iito->amount += $orig-$this->total(); + } + + $this->_sub_items_sorted = FALSE; + } + + // This total of this item before discounts and taxes + public function subtotal($format=FALSE) { + $result = $this->price_base*$this->quantity; + + return $format ? Currency::display($result) : Currency::round($result); + } + + // Sum up the tax that applies to this invoice item + public function tax($format=FALSE) { + $result = 0; + + foreach ($this->subitems() as $iito) + $result += $iito->amount; + + return $format ? Currency::display($result) : Currency::round($result); + } + + public function tax_items() { + $result = array(); + + foreach ($this->subitems() as $iito) { + if (! isset($result[$iit->tax_id])) + $result[$iit->tax_id] = 0; + + $result[$iito->tax_id] += $iito->amount; + } + + return $result; + } + /** * The title for invoice items */ @@ -53,6 +155,17 @@ class Model_Invoice_Item extends ORM_OSB { return 'Unknown Item'; } + public function total($format=FALSE) { + $result = $this->void ? 0 : $this->subtotal()+$this->tax()-$this->discount(); + + return $format ? Currency::display($result) : Currency::round($result); + } + + // Display a transaction number + public function trannum() { + return sprintf('%03s-%06s',$this->item_type,$this->id); + } + public function invoice_line() { if ($this->charge_id) return $this->charge->description.' '.$this->period(); @@ -62,69 +175,9 @@ class Model_Invoice_Item extends ORM_OSB { throw HTTP_Exception::factory(501,'Unable to render invoice item :id',array(':id'=>$this->id)); } - // Display a transaction number - public function trannum() { - return sprintf('%03s-%06s',$this->item_type,$this->id); - } - - // Display the period that a transaction applies - public function period() { - if ($this->date_start == $this->date_stop) - return Config::date($this->date_start); - - else - return sprintf('%s -> %s',Config::date($this->date_start),Config::date($this->date_stop)); - } - - // Sum up the tax that applies to this invoice item - public function tax($format=FALSE) { - $result = 0; - - foreach ($this->invoice_item_tax->find_all() as $iit) - $result += $iit->amount; - - // @todo This shouldnt be required. - if (! $result) - $result += round($this->price_base*$this->quantity*.1,2); - - $result = Currency::round($result); - - return $format ? Currency::display($result) : $result; - } - - public function tax_items() { - $result = array(); - - foreach ($this->invoice_item_tax->find_all() as $iit) { - if (! isset($result[$iit->tax_id])) - $result[$iit->tax_id] = 0; - - $result[$iit->tax_id] += $iit->amount; - } - - return $result; - } - - // This total of this item before discounts and taxes - public function subtotal($format=FALSE) { - $result = Currency::round($this->price_base*$this->quantity); - - return $format ? Currency::display($result) : $result; - } - - // The total of all discounts - public function discount() { - return Currency::round($this->discount_amt); - } - - public function total($format=FALSE) { - $result = $this->void ? 0 : Currency::round($this->subtotal()+$this->tax()-$this->discount()); - - return $format ? Currency::display($result) : $result; - } - /** * Name for an invoice item + * @deprecated, use StaticList_ItemType() */ public function name() { switch ($this->item_type) { @@ -186,40 +239,5 @@ class Model_Invoice_Item extends ORM_OSB { return array('Item'=>$this->item_type); } } - - public function save(Validation $validation = NULL) { - // Save the invoice item - parent::save($validation); - - // Need to save the discounts associated with the invoice_item - if ($this->saved()) { - $iito = ORM::factory('Invoice_Item_Tax'); - - if ($this->_sub_items) { - foreach (array('tax') as $i) - foreach ($this->_sub_items[$i] as $io) - if ($io->changed()) - $io->save(); - - // Add TAX details - } else - // @todo tax parameters should come from user session - foreach (Tax::detail(61,NULL,$this->subtotal()) as $tax) { - $iito->clear(); - $iito->invoice_item_id = $this->id; - $iito->tax_id = $tax['id']; - // @todo Rounding here should come from a global config - $iito->amount = round($tax['amount'],2); - - $iito->save(); - - if (! $iito->saved()) - throw new Kohana_Exception('Couldnt save tax for some reason - failed save()?'); - } - } else - throw new Kohana_Exception('Couldnt save invoice_item for some reason?'); - - return $this; - } } ?> diff --git a/modules/payment/classes/Model/Payment.php b/modules/payment/classes/Model/Payment.php index fcf86694..0c2bd155 100644 --- a/modules/payment/classes/Model/Payment.php +++ b/modules/payment/classes/Model/Payment.php @@ -66,7 +66,7 @@ class Model_Payment extends ORM_OSB { public function balance($format=FALSE) { $result = $this->total(); - foreach ($this->items('ALLOC') as $pio) + foreach ($this->subitems('ALLOC') as $pio) $result -= $pio->alloc_amt; return $format ? Currency::display($result) : Currency::round($result); @@ -78,7 +78,7 @@ class Model_Payment extends ORM_OSB { public function credit($format=FALSE) { $result = 0; - foreach ($this->items('CREDIT') as $pio) + foreach ($this->subitems('CREDIT') as $pio) $result += $pio->alloc_amt*-1; return $format ? Currency::display($result) : Currency::round($result); @@ -105,7 +105,7 @@ class Model_Payment extends ORM_OSB { * @param type [ALLOC|CREDIT|ALL] * @see payment_items */ - public function items($type='ALL') { + public function subitems($type='ALL') { if (! $this->_sub_items_sorted) { Sort::MAsort($this->_sub_items,'invoice_id'); $this->_sub_items_sorted = TRUE; @@ -145,7 +145,7 @@ class Model_Payment extends ORM_OSB { */ public function save(Validation $validation = NULL) { // Our items will be clobbered once we save the object, so we need to save it here. - $items = $this->items(); + $items = $this->subitems(); // If a user modified this payment, we'll update the source to their ID. if ($ao = Auth::instance()->get_user()) diff --git a/modules/payment/views/payment/admin/ajaxitemlist.php b/modules/payment/views/payment/admin/ajaxitemlist.php index 68077aac..ac9b8f68 100644 --- a/modules/payment/views/payment/admin/ajaxitemlist.php +++ b/modules/payment/views/payment/admin/ajaxitemlist.php @@ -12,7 +12,7 @@ - items('ALLOC') as $pio) : ?> + subitems('ALLOC') as $pio) : ?> invoice_id),$pio->invoice->id()); ?> invoice->display('date_orig'); ?>