This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Deon George 29c1913f47 Theme work with focusbusiness and baseadmin
Improvements to NAVBAR, updates to StaticList methods, other minor items
Enable product category rendering and other minor improvements
Added ADSL-large category price plan
2013-05-02 20:49:30 +10:00

341 lines
11 KiB
PHP

<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides OSB invoice task capabilities.
*
* @package Invoice
* @category Controllers
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Task_Invoice extends Controller_Task {
/**
* Email a list of invoice balances
*
* This function is typically used to list the overdue invoices to the admins
* @param string mode The callback method to use as the data list eg: overdue
*/
public function action_list() {
$mode = $this->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();
$key = 'remind_due';
$days = ORM::factory('Invoice')->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_type = $so->product->price_type;
$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);
}
}
?>