array(), 'service'=>array() ); protected $_has_many = array( 'tax'=>array('model'=>'Invoice_Item_Tax','far_key'=>'id') ); protected $_display_filters = array( 'date_orig'=>array( array('Config::date',array(':value')), ), 'date_start'=>array( array('Config::date',array(':value')), ), 'date_stop'=>array( array('Config::date',array(':value')), ), ); // Items belonging to an invoice item protected $_sub_items_load = array( 'tax'=>FALSE, ); // The total of all discounts public function discount() { return Currency::round($this->discount_amt); } /** * The line that will be printed on an invoice * * @todo This method includes some database format validation routines, which can be removed when the database * is completly transformed. */ public function invoice_line() { $ii = NULL; // Our module is responsible for rending the invoice line $ii = ($this->module_id AND method_exists($this->module(),'invoice_item')) ? $this->module()->invoice_item($this->item_type) : StaticList_ItemType::get($this->item_type); switch ($this->item_type) { // Service Charges case 0: case 7: return ((! $this->service_id OR $this->product_id OR $this->charge_id OR $this->product_name OR ! $this->recurring_schedule OR ! $this->date_start OR ! $this->date_stop) ? '+ ' : '').$ii.' '.$this->period(); case 1: // @todo return $ii; case 2: case 3: case 4: case 5: case 6: return ((! $this->service_id OR $this->product_id OR $this->charge_id OR $this->product_name OR $this->recurring_schedule OR ! $this->date_start OR ! $this->date_stop) ? '+ ' : '').$ii; case 124: case 125: case 126: case 127: // @todo return $ii; // @todo DB records to fix. default: if ($this->charge_id) return '*'.($ii ? $ii : $this->charge->description).' '.$this->period(); else throw HTTP_Exception::factory(501,'Unable to render invoice item :id',array(':id'=>$this->id)); } } /** * Return an instance of the Model of this charge */ public function module() { $x = ORM::factory('Module',$this->module_id); if (! $x->loaded()) throw new Kohana_Exception('Module :module doesnt exist?',array(':module'=>$this->module_id)); return ORM::factory('Module',$this->module_id)->instance($this->module_ref); } // Display the period that a transaction applies public function period() { return ($this->date_start == $this->date_stop) ? Config::date($this->date_start) : sprintf('%s -> %s',Config::date($this->date_start),Config::date($this->date_stop)); } 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) : $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 */ public function title() { if ($this->service_id AND $this->module_id AND method_exists($this->module(),'invoice_title')) return $this->module_ref ? sprintf('%s: %s',$this->module()->invoice_title(),$this->service->name()) : $this->service->name(); elseif ($x=$this->module() AND ($x instanceof Model_Charge) AND $x->product_id) return $x->product->title().' '.$this->service->name(); elseif ($this->product_id) return sprintf('* %s: %s',$this->product->invoice_title(),$this->service->name()); else return 'Unknown Item'; } 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_detail_items() { switch ($this->item_type) { case 0: return $this->service->details('invoice_detail_items'); case 4: return array('Charge'=>_('Service Connection Fee')); case 5: return $this->charge->details('invoice_detail_items'); default: return array('Item'=>$this->item_type); } } } ?>