request->param('id'); $i = ORM::factory('invoice'); $tm = 'list_'.$mode; if (! method_exists($i,$tm)) throw new Kohana_Exception('Unknown Task List command :command',array(':command'=>$mode)); $total = $numinv = 0; $duelist = View::factory('invoice/task/'.$tm.'_head'); foreach ($i->$tm() as $t) { $duelist .= View::factory('invoice/task/'.$tm.'_body') ->set('io',$t); $numinv++; $total += $t->due(); } $duelist .= View::factory('invoice/task/'.$tm.'_foot'); // Send our email $et = Email_Template::instance('task_invoice_list_overdue'); // @todo Update this to be dynamic $et->to = array('account'=>array(1,68)); $et->variables = array( 'TABLE'=>$duelist, 'NUM_INV'=>$numinv, 'TOTAL'=>$total, ); $et->send(); $output = sprintf('List (%s) sent to: %s',$mode,implode(',',array_keys($et->to))); $this->response->body($output); } /** * Email a customers a reminder of their upcoming invoices that are due. */ public function action_remind_due() { $action = array(); // @todo This should go in a config somewhere $days = 5; $key = 'remind_due'; foreach (ORM::factory('invoice')->list_due(time()+86400*$days) as $io) { // If we have already sent a reminder, we'll skip to the next one. if ($io->remind($key) AND (is_null($x=$this->request->param('id')) OR $x != 'again')) continue; // Send our email $et = Email_Template::instance('task_invoice_'.$key); $et->to = array('account'=>array($io->account_id)); $et->variables = array( 'DUE'=>$io->due(TRUE), 'DUE_DATE'=>$io->display('due_date'), 'FIRST_NAME'=>$io->account->first_name, 'INV_NUM'=>$io->refnum(), 'INV_URL'=>URL::site('user/invoice/view/'.$io->id,'http'), 'SITE_NAME'=>Config::sitename(), ); // @todo Record email log id if possible. if ($et->send()) { $io->set_remind($key,time()); array_push($action,(string)$io); } } $this->response->body(_('Due Reminders Sent: ').join('|',$action)); } /** * Email a customers when their invoices are now overdue. */ public function action_remind_overdue() { $action = array(); $notice = $this->request->param('id'); $x = NULL; if (preg_match('/:/',$notice)) list($notice,$x) = explode(':',$notice); switch ($notice) { case 1: // @todo This should go in a config somewhere $days = 2; break; case 2: // @todo This should go in a config somewhere $days = 7; break; case 3: // @todo This should go in a config somewhere $days = 13; break; default: $this->response->body(_('Unknown Remind Period: ').$notice); return; } $key = 'remind_overdue_'.$notice; foreach (ORM::factory('invoice')->list_overdue_billing(time()-86400*$days,FALSE) as $io) { // If we have already sent a reminder, we'll skip to the next one. if ($io->remind($key) AND (is_null($x) OR $x != 'again')) continue; // Send our email $et = Email_Template::instance('task_invoice_'.$key); $et->to = array('account'=>array($io->account_id)); $et->variables = array( 'DUE'=>$io->due(TRUE), 'DUE_DATE'=>$io->display('due_date'), 'EMAIL'=>'accounts@graytech.net.au', // @todo This should come from a config. 'FIRST_NAME'=>$io->account->first_name, 'INV_NUM'=>$io->refnum(), 'INV_URL'=>URL::site('user/invoice/view/'.$io->id,'http'), 'LATE_FEE'=>'5.50', // @todo This should come from a config file. 'PAYMENTS_TABLE'=>$io->account->payment->list_recent_table(), 'SITE_NAME'=>Config::sitename(), ); // @todo Record email log id if possible. if ($et->send()) { $io->set_remind($key,time()); array_push($action,(string)$io); } } $this->response->body(_('Overdue Reminders Sent: ').join('|',$action)); } /** * Generate our services invoices, based on the service next invoice date * * @param int ID Service ID to generate invoice for (optional) */ public function action_services() { // Used to only process X invoices in a row. // @todo This should be a configuration item. $max = 10; $action = array(); $snd = array(); // Our service next billing dates that need to be updated if this is successful. $sid = is_null($this->request->param('id')) ? NULL : explode(':',$this->request->param('id')); // Sort our service by account_id, then we can generate 1 invoice. $svs = ORM::factory('service')->list_invoicesoon(); Sort::MAsort($svs,'account_id,date_next_invoice'); $aid = $due = $io = NULL; $max_count = 0; foreach ($svs as $so) { // If we are generating an invoice for a service, skip to that service. if (! is_null($sid) AND ! in_array($so->id,$sid)) continue; // Close off invoice, and start a new one. if (is_null($io) OR (! is_null($aid) AND $aid != $so->account_id) OR (! is_null($due) AND $due != $io->min_due($so->date_next_invoice))) { // Close this invoice. if (is_object($io)) { // Save our invoice. if (! $io->save()) throw new Kohana_Exception('Failed to save invoice :invoice for service :service',array(':invoice'=>$io->id,':service'=>$so->id)); } // If we have issued the max number of invoices this round, finish. if (++$max_count > $max) break; // Start a new invoice. $io = ORM::factory('invoice'); $io->due_date = $due = $io->min_due($so->date_next_invoice); $io->account_id = $aid = $so->account_id; $io->status = TRUE; } $ppa = $so->product->get_price_array(); // @todo Need to check our recurr_weekday configuration for items that need to be pro-rated and items that are billed on absolute dates. $pdata = Period::details($so->recur_schedule,$so->product->price_recurr_weekday,$so->date_next_invoice,TRUE); $iio = $io->add_item(); $iio->service_id = $so->id; $iio->product_id = $so->product_id; $iio->quantity = $pdata['prorata']; $iio->item_type = 0; $iio->discount_amt = null; // @todo $iio->price_type = $so->product->price_type; // @todo Do we need this? // @todo Might be a better way to do this $iio->price_base = $so->price ? $so->price : (isset($ppa[$so->recur_schedule]['price_base']) ? $ppa[$so->recur_schedule]['price_base'] : 0); $iio->recurring_schedule = $so->recur_schedule; $iio->date_start = $pdata['start_time']; // @todo $iio->date_stop = $pdata['end_time']; // @todo // Our service next billing date, if this invoice generation is successful. $snd[$so->id] = $pdata['end_time']+86400; // Check if there are any charges $c = ORM::factory('charge') ->where('service_id','=',$so->id) ->where('status','=',0) ->where('sweep_type','=',6); // @todo This needs to be dynamic, not "6" foreach ($c->find_all() as $co) { $iio = $io->add_item(); $iio->service_id = $co->service_id; $iio->product_id = $co->product_id; $iio->charge_id = $co->id; $iio->quantity = $co->quantity; $iio->item_type = 5; // @todo This probably should not be hard coded as "5". $iio->discount_amt = null; // @todo $iio->price_base = $co->amount; $iio->date_start = $co->date_orig; $iio->date_stop = $co->date_orig; // @todo // @todo Temp $co->status=2; $co->save(); } array_push($action,(string)$so->id); } // Save our invoice. if (! $io->saved() AND ! $io->save()) { print_r($io->items()); throw new Kohana_Exception('Failed to save invoice :invoice for service :service',array(':invoice'=>$io->id,':service'=>$so->id)); } // Update our service next billing dates. foreach ($snd as $sid=>$date) { $so = ORM::factory('service',$sid); $so->date_next_invoice = $date; $so->save(); } $this->response->body(_('Services Invoiced: ').join('|',$action)); } public function action_send() { // Used to only process X invoices in a row. // @todo This should be a configuration item. $max = 2; $action = array(); $iid = $this->request->param('id'); $x = NULL; if (preg_match('/:/',$iid)) list($iid,$x) = explode(':',$iid); // Get our list of invoices to send $i = $iid ? ORM::factory('invoice')->where('id','=',$iid) : ORM::factory('invoice')->list_tosend(); $key = 'send'; $max_count = 0; foreach ($i->find_all() as $io) { // If we have already sent a reminder, we'll skip to the next one. if ($io->remind($key) AND (is_null($x) OR $x != 'again')) continue; // If we have issued the max number of invoices this round, finish. if (++$max_count > $max) break; // Send our email $et = Email_Template::instance('task_invoice_'.$key); $token = ORM::factory('module_method_token') ->method(array('invoice','user_download')) ->account($io->account) ->expire(time()+86400*21) ->uses(3) ->generate(); $et->to = array('account'=>array($io->account_id)); $et->variables = array( 'DUE'=>$io->due(TRUE), 'DUE_DATE'=>$io->display('due_date'), 'EMAIL'=>'accounts@graytech.net.au', // @todo This should come from a config. 'FIRST_NAME'=>$io->account->first_name, 'HTML_INVOICE'=>$io->html(), 'INV_NUM'=>$io->refnum(), 'INV_URL'=>URL::site('user/invoice/view/'.$io->id,'http'), 'INV_URL_DOWNLOAD'=>URL::site(sprintf('user/invoice/download/%s?token=%s',$io->id,$token),'http'), 'SITE_NAME'=>Config::sitename(), ); // @todo Record email log id if possible. if ($et->send()) { $io->print_status = 1; $io->set_remind($key,time()); array_push($action,(string)$io); } } $this->response->body(_('Invoices Sent: ').join('|',$action)); } /** END **/ public function action_audit_invoice_items() { $output = ''; foreach (ORM::factory('invoice_item')->find_all() as $iio) { if ($iio->product_name AND $iio->product_id) { if (md5(strtoupper($iio->product_name)) == md5(strtoupper($iio->product->name()))) { $iio->product_name = null; $iio->save(); } else { print_r(array("DIFF",'id'=>$iio->id,'pn'=>serialize($iio->product_name),'ppn'=>serialize($iio->product->name()),'pid'=>$iio->product_id,'test'=>strcasecmp($iio->product_name,$iio->product->name()))); } } } $this->response->body($output); } } ?>