config('REMIND_DUE'); foreach (ORM::factory('Invoice')->list_due(time()+86400*$days) as $io) { // @todo Use another option to supress reminders // 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')) OR ($io->account->invoice_delivery != 1)) 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(URL::link('user','invoice/view/'.$io->id),'http'), 'SITE_NAME'=>Company::instance()->name(), ); // @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: case 2: case 3: $days = ORM::factory('Invoice')->config('REMIND_OVERDUE_'.$notice); 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) { // @todo Use another option to supress reminders // 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')) OR ($io->account->invoice_delivery != 1)) 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'=>Company::instance()->email(), 'FIRST_NAME'=>$io->account->first_name, 'INV_NUM'=>$io->refnum(), 'INV_URL'=>URL::site(URL::link('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'=>Company::instance()->name(), ); // @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) - multiple services colon separated */ public function action_services() { // Used to only process X invoices in a row. $max = ($x=Kohana::$config->load('debug')->invoice) ? $x : ORM::factory('Invoice')->config('GEN_INV_MAX'); // Our service next billing dates that need to be updated if this is successful. $snd = array(); // Our charges that need to be updated if this is successful. $chgs = array(); // If we are invoicing a specific service $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()->as_array(); 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 AND (++$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; } $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; // Service Billing $iio->discount_amt = NULL; // @todo $iio->price_base = $so->price(); $iio->recurring_schedule = $so->recur_schedule; $iio->date_start = $pdata['start_time']; $iio->date_stop = $pdata['end_time']; // 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 // We'll mark any charges as temporarily processed, although they should be set to status=1 later. $co->status=2; $co->save(); array_push($chgs,$co->id); } } // Save our invoice. if ($io AND ! $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. // @todo Catch any update errors foreach ($snd as $sid=>$date) { $so = ORM::factory('Service',$sid); $so->date_next_invoice = $date; $so->save(); } // Update any processed charges as such // @todo Catch any update errors foreach ($chgs as $cid) { $co = ORM::factory('Charge',$cid); $co->status=1; $co->save(); } $this->response->body(_('Services Invoiced: ').join('|',array_keys($snd))); } public function action_send() { // Used to only process X invoices in a row. $max = ORM::factory('Invoice')->config('EMAIL_INV_MAX'); $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 or we dont email invoices we'll skip to the next one. if (($io->remind($key) AND (is_null($x) OR $x != 'again')) OR ($io->account->invoice_delivery != 1)) 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'=>Company::instance()->email(), 'FIRST_NAME'=>$io->account->first_name, 'HTML_INVOICE'=>$io->html(), 'INV_NUM'=>$io->refnum(), 'INV_URL'=>URL::site(URL::link('user','invoice/view/'.$io->id),'http'), 'INV_URL_DOWNLOAD'=>URL::site(URL::link('user',sprintf('invoice/download/%s?token=%s',$io->id,$token)),'http'), 'SITE_NAME'=>Company::instance()->name(), ); // @todo Record email log id if possible. if ($et->send()) { $io->print_status = 1; $io->set_remind($key,time(),($x=='again' ? TRUE : FALSE)); 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->title()))) { $iio->product_name = NULL; $iio->save(); } else { print_r(array("DIFF",'id'=>$iio->id,'pn'=>serialize($iio->product_name),'ppn'=>serialize($iio->product->title()),'pid'=>$iio->product_id,'test'=>strcasecmp($iio->product_name,$iio->product->title()))); } } } $this->response->body($output); } } ?>