Working on recurring invoices

This commit is contained in:
Hillel Coren 2013-12-11 00:10:43 +02:00
parent 0611004e77
commit f97fa8991f
16 changed files with 594 additions and 96 deletions

View File

@ -22,9 +22,9 @@ class SendRecurringInvoices extends Command {
{
$this->info(date('Y-m-d') . ' Running SendRecurringInvoices...');
$today = date('Y-m-d');
$today = new DateTime();
$invoices = Invoice::with('account', 'invoice_items')->whereRaw('start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))->get();
$invoices = RecurringInvoice::with('account', 'invoice_items')->whereRaw('start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))->get();
$this->info(count($invoices) . ' recurring invoice(s) found');
foreach ($invoices as $recurInvoice)
@ -38,7 +38,7 @@ class SendRecurringInvoices extends Command {
$invoice = Invoice::createNew($recurInvoice);
$invoice->client_id = $recurInvoice->client_id;
$invoice->parent_id = $recurInvoice->id;
$invoice->recurring_invoice_id = $recurInvoice->id;
$invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber();
$invoice->total = $recurInvoice->total;
$invoice->invoice_date = new DateTime();

View File

@ -99,7 +99,9 @@ class ClientController extends \BaseController {
$data = array(
'client' => $client,
'title' => '- ' . $client->name);
'title' => '- ' . $client->name,
'hasRecurringInvoices' => RecurringInvoice::whereClientId($client->id)->count() > 0
);
return View::make('clients.show', $data);
}

View File

@ -279,15 +279,15 @@ class InvoiceController extends \BaseController {
return View::make('invoices.edit', $data);
}
private static function getViewModel()
public static function getViewModel($isRecurring = false)
{
return [
'isRecurring' => $isRecurring,
'account' => Auth::user()->account,
'products' => Product::scope()->get(array('product_key','notes','cost','qty')),
'countries' => Country::orderBy('name')->get(),
'clients' => Client::scope()->orderBy('name')->get(),
'frequencies' => array(
0 => '',
1 => 'Weekly',
2 => 'Two weeks',
3 => 'Four weeks',
@ -366,21 +366,17 @@ class InvoiceController extends \BaseController {
if ($publicId) {
$invoice = Invoice::scope($publicId)->firstOrFail();
$invoice->invoice_items()->forceDelete();
} else {
} else {
$invoice = Invoice::createNew();
}
$invoice->client_id = $client->id;
$invoice->invoice_number = trim(Input::get('invoice_number'));
$invoice->discount = 0;
$invoice->invoice_number = trim(Input::get('invoice_number'));
$invoice->invoice_date = Utils::toSqlDate(Input::get('invoice_date'));
$invoice->due_date = Utils::toSqlDate(Input::get('due_date', null));
$invoice->due_date = Utils::toSqlDate(Input::get('due_date', null));
$invoice->notes = Input::get('notes');
$invoice->how_often = Input::get('how_often');
$invoice->start_date = Utils::toSqlDate(Input::get('start_date', null));
$invoice->end_date = Utils::toSqlDate(Input::get('end_date', null));
$client->invoices()->save($invoice);
$items = json_decode(Input::get('items'));

View File

@ -0,0 +1,280 @@
<?php
class RecurringInvoiceController extends \BaseController {
public function index()
{
return View::make('list', array(
'entityType'=>ENTITY_RECURRING_INVOICE,
'title' => '- Invoices',
'columns'=>['checkbox', 'Client', 'Total', 'How Often', 'Start Date', 'End Date', 'Action']
));
}
public function getDatatable($clientPublicId = null)
{
$query = DB::table('recurring_invoices')
->join('clients', 'clients.id', '=', 'recurring_invoices.client_id')
->join('frequencies', 'recurring_invoices.frequency_id', '=', 'frequencies.id')
->where('recurring_invoices.account_id', '=', Auth::user()->account_id)
->where('recurring_invoices.deleted_at', '=', null)
->select('clients.public_id as client_public_id', 'clients.name as client_name', 'recurring_invoices.public_id', 'total', 'frequencies.name as frequency', 'start_date', 'end_date');
if ($clientPublicId) {
$query->where('clients.public_id', '=', $clientPublicId);
}
$table = Datatable::query($query);
if (!$clientPublicId) {
$table->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; });
}
//$table->addColumn('invoice_number', function($model) { return link_to('invoices/' . $model->public_id . '/edit', $model->invoice_number); });
if (!$clientPublicId) {
$table->addColumn('client', function($model) { return link_to('clients/' . $model->client_public_id, $model->client_name); });
}
return $table->addColumn('total', function($model) { return '$' . money_format('%i', $model->total); })
->addColumn('frequency', function($model) { return $model->frequency; })
->addColumn('start_date', function($model) { return Utils::fromSqlDate($model->start_date); })
->addColumn('end_date', function($model) { return Utils::fromSqlDate($model->end_date); })
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="visibility:hidden;">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Select <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('invoices/'.$model->public_id.'/edit') . '">Edit Recurring Invoice</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('invoices/'.$model->public_id.'/archive') . '">Archive Recurring Invoice</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id . ')">Delete Recurring Invoice</a></li>
</ul>
</div>';
})
->orderColumns('client','total','how_often','start_date','end_date')
->make();
}
public function edit($publicId)
{
$invoice = RecurringInvoice::scope($publicId)->with('account.country', 'client', 'invoice_items')->firstOrFail();
Utils::trackViewed($invoice->getName() . ' - ' . $invoice->client->name, ENTITY_RECURRING_INVOICE);
$data = array(
'account' => $invoice->account,
'invoice' => $invoice,
'method' => 'PUT',
'url' => 'recurring_invoices/' . $publicId,
'title' => '- ' . $invoice->getName(),
'client' => $invoice->client);
$data = array_merge($data, InvoiceController::getViewModel(true));
return View::make('invoices.edit', $data);
}
public function create($clientPublicId = 0)
{
$client = null;
$invoiceNumber = Auth::user()->account->getNextInvoiceNumber();
$account = Account::with('country')->findOrFail(Auth::user()->account_id);
if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail();
}
$data = array(
'account' => $account,
'invoice' => null,
'invoiceNumber' => $invoiceNumber,
'method' => 'POST',
'url' => 'recurring_invoices',
'title' => '- New Recurring Invoice',
'client' => $client,
'items' => json_decode(Input::old('items')));
$data = array_merge($data, InvoiceController::getViewModel(true));
return View::make('invoices.edit', $data);
}
public function store()
{
return RecurringInvoiceController::save();
}
private function save($publicId = null)
{
$action = Input::get('action');
if ($action == 'archive' || $action == 'delete')
{
return RecurringInvoiceController::bulk();
}
$rules = array(
'client' => 'required',
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('invoices/create')
->withInput()
->withErrors($validator);
} else {
$clientPublicId = Input::get('client');
if ($clientPublicId == "-1")
{
$client = Client::createNew();
$client->name = trim(Input::get('name'));
$client->work_phone = trim(Input::get('work_phone'));
$client->address1 = trim(Input::get('address1'));
$client->address2 = trim(Input::get('address2'));
$client->city = trim(Input::get('city'));
$client->state = trim(Input::get('state'));
$client->postal_code = trim(Input::get('postal_code'));
if (Input::get('country_id')) {
$client->country_id = Input::get('country_id');
}
$client->save();
$clientId = $client->id;
$contact = Contact::createNew();
$contact->is_primary = true;
$contact->first_name = trim(Input::get('first_name'));
$contact->last_name = trim(Input::get('last_name'));
$contact->phone = trim(Input::get('phone'));
$contact->email = trim(Input::get('email'));
$client->contacts()->save($contact);
}
else
{
$client = Client::scope($clientPublicId)->with('contacts')->firstOrFail();
$contact = $client->contacts()->first();
}
if ($publicId) {
$invoice = RecurringInvoice::scope($publicId)->firstOrFail();
$invoice->invoice_items()->forceDelete();
} else {
$invoice = RecurringInvoice::createNew();
}
$invoice->client_id = $client->id;
$invoice->discount = 0;
$invoice->frequency_id = Input::get('frequency');
$invoice->start_date = Utils::toSqlDate(Input::get('start_date', null));
$invoice->end_date = Utils::toSqlDate(Input::get('end_date', null));
$invoice->notes = Input::get('notes');
$client->invoices()->save($invoice);
$items = json_decode(Input::get('items'));
$total = 0;
foreach ($items as $item)
{
if (!isset($item->cost)) {
$item->cost = 0;
}
if (!isset($item->qty)) {
$item->qty = 0;
}
$total += floatval($item->qty) * floatval($item->cost);
}
$invoice->total = $total;
$invoice->save();
foreach ($items as $item)
{
if (!$item->cost && !$item->qty && !$item->product_key && !$item->notes)
{
continue;
}
if ($item->product_key)
{
$product = Product::findProductByKey(trim($item->product_key));
if (!$product)
{
$product = Product::createNew();
$product->product_key = trim($item->product_key);
}
/*
$product->notes = $item->notes;
$product->cost = $item->cost;
$product->qty = $item->qty;
*/
$product->save();
}
$invoiceItem = RecurringInvoiceItem::createNew();
$invoiceItem->product_id = isset($product) ? $product->id : null;
$invoiceItem->product_key = trim($item->product_key);
$invoiceItem->notes = trim($item->notes);
$invoiceItem->cost = floatval($item->cost);
$invoiceItem->qty = floatval($item->qty);
$invoice->invoice_items()->save($invoiceItem);
}
Session::flash('message', 'Successfully saved recurring invoice');
$url = 'recurring_invoices/' . $invoice->public_id . '/edit';
return Redirect::to($url);
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return Response
*/
public function show($publicId)
{
return Redirect::to('recurring_invoices/'.$publicId.'/edit');
}
/**
* Update the specified resource in storage.
*
* @param int $id
* @return Response
*/
public function update($publicId)
{
return RecurringInvoiceController::save($publicId);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function bulk()
{
$action = Input::get('action');
$ids = Input::get('id') ? Input::get('id') : Input::get('ids');
$invoices = RecurringInvoice::scope($ids)->get();
foreach ($invoices as $invoice) {
if ($action == 'archive') {
$invoice->delete();
} else if ($action == 'delete') {
$invoice->forceDelete();
}
}
$message = Utils::pluralize('Successfully '.$action.'d ? recurring invoice', count($ids));
Session::flash('message', $message);
return Redirect::to('invoices');
}
}

View File

@ -17,10 +17,12 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('account_gateways');
Schema::dropIfExists('gateways');
Schema::dropIfExists('payments');
Schema::dropIfExists('recurring_invoice_items');
Schema::dropIfExists('invoice_items');
Schema::dropIfExists('products');
Schema::dropIfExists('contacts');
Schema::dropIfExists('invoices');
Schema::dropIfExists('recurring_invoices');
Schema::dropIfExists('password_reminders');
Schema::dropIfExists('clients');
Schema::dropIfExists('users');
@ -28,6 +30,7 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('invoice_statuses');
Schema::dropIfExists('countries');
Schema::dropIfExists('timezones');
Schema::dropIfExists('frequencies');
Schema::create('countries', function($table)
{
@ -200,6 +203,38 @@ class ConfideSetupUsersTable extends Migration {
$t->string('name');
});
Schema::create('frequencies', function($t)
{
$t->increments('id');
$t->string('name');
});
Schema::create('recurring_invoices', function($t)
{
$t->increments('id');
$t->unsignedInteger('client_id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('account_id');
$t->timestamps();
$t->softDeletes();
$t->float('discount');
$t->text('notes');
$t->decimal('total', 10, 2);
$t->unsignedInteger('frequency_id');
$t->date('start_date')->nullable();
$t->date('end_date')->nullable();
$t->date('last_sent_date')->nullable();
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('user_id')->references('id')->on('users');
$t->foreign('frequency_id')->references('id')->on('frequencies');
$t->unsignedInteger('public_id');
$t->unique( array('account_id','public_id') );
});
Schema::create('invoices', function($t)
{
@ -220,17 +255,13 @@ class ConfideSetupUsersTable extends Migration {
$t->decimal('total', 10, 2);
$t->decimal('balance', 10, 2);
$t->integer('how_often');
$t->date('start_date')->nullable();
$t->date('end_date')->nullable();
$t->date('last_sent_date')->nullable();
$t->unsignedInteger('parent_id')->nullable();
$t->unsignedInteger('recurring_invoice_id')->nullable();
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('user_id')->references('id')->on('users');
$t->foreign('invoice_status_id')->references('id')->on('invoice_statuses');
$t->foreign('parent_id')->references('id')->on('invoices');
$t->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices');
$t->unsignedInteger('public_id');
$t->unique( array('account_id','public_id') );
@ -302,6 +333,29 @@ class ConfideSetupUsersTable extends Migration {
$t->unique( array('account_id','public_id') );
});
Schema::create('recurring_invoice_items', function($t)
{
$t->increments('id');
$t->unsignedInteger('account_id');
$t->unsignedInteger('user_id');
$t->unsignedInteger('recurring_invoice_id');
$t->unsignedInteger('product_id')->nullable();
$t->timestamps();
$t->softDeletes();
$t->string('product_key');
$t->string('notes');
$t->decimal('cost', 10, 2);
$t->decimal('qty', 10, 2);
$t->foreign('recurring_invoice_id')->references('id')->on('recurring_invoices')->onDelete('cascade');
$t->foreign('product_id')->references('id')->on('products');
$t->foreign('user_id')->references('id')->on('users');
$t->unsignedInteger('public_id');
$t->unique( array('account_id','public_id') );
});
Schema::create('payments', function($t)
{
$t->increments('id');
@ -363,6 +417,7 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('contact_id');
$t->unsignedInteger('payment_id');
$t->unsignedInteger('invoice_id');
$t->unsignedInteger('recurring_invoice_id');
$t->unsignedInteger('credit_id');
$t->unsignedInteger('invitation_id');
@ -390,16 +445,19 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('account_gateways');
Schema::dropIfExists('gateways');
Schema::dropIfExists('payments');
Schema::dropIfExists('recurring_invoice_items');
Schema::dropIfExists('invoice_items');
Schema::dropIfExists('products');
Schema::dropIfExists('contacts');
Schema::dropIfExists('invoices');
Schema::dropIfExists('recurring_invoices');
Schema::dropIfExists('password_reminders');
Schema::dropIfExists('clients');
Schema::dropIfExists('users');
Schema::dropIfExists('accounts');
Schema::dropIfExists('invoice_statuses');
Schema::dropIfExists('countries');
Schema::dropIfExists('timezones');
Schema::dropIfExists('timezones');
Schema::dropIfExists('frequencies');
}
}

View File

@ -50,6 +50,14 @@ class ConstantsSeeder extends Seeder
InvoiceStatus::create(array('name' => 'Partial'));
InvoiceStatus::create(array('name' => 'Paid'));
Frequency::create(array('name' => 'Weekly'));
Frequency::create(array('name' => 'Two weeks'));
Frequency::create(array('name' => 'Four weeks'));
Frequency::create(array('name' => 'Monthly'));
Frequency::create(array('name' => 'Three months'));
Frequency::create(array('name' => 'Six months'));
Frequency::create(array('name' => 'Annually'));
$gateways = [
array('name'=>'Authorize.Net AIM', 'provider'=>'AuthorizeNet_AIM'),
array('name'=>'Authorize.Net SIM', 'provider'=>'AuthorizeNet_SIM'),

View File

@ -207,4 +207,9 @@ class Utils
$year = intval(date('Y'));
return $year + $offset;
}
public static function getEntityName($entityType)
{
return ucwords(str_replace('_', ' ', $entityType));
}
}

View File

@ -15,6 +15,10 @@ define("ACTIVITY_TYPE_DELETE_PAYMENT", 11);
define("ACTIVITY_TYPE_CREATE_CREDIT", 12);
define("ACTIVITY_TYPE_ARCHIVE_CREDIT", 13);
define("ACTIVITY_TYPE_DELETE_CREDIT", 14);
define("ACTIVITY_TYPE_CREATE_RECURRING_INVOICE", 15);
define("ACTIVITY_TYPE_ARCHIVE_RECURRING_INVOICE", 16);
define("ACTIVITY_TYPE_DELETE_RECURRING_INVOICE", 17);
class Activity extends Eloquent
{
@ -94,6 +98,26 @@ class Activity extends Eloquent
$activity->save();
}
public static function createRecurringInvoice($invoice)
{
$activity = Activity::getBlank();
$activity->recurring_invoice_id = $invoice->id;
$activity->client_id = $invoice->client_id;
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_RECURRING_INVOICE;
$activity->message = Auth::user()->getFullName() . ' created recurring invoice ' . link_to('recurring_invoices/'.$invoice->public_id, $invoice->getName());
$activity->save();
}
public static function archiveRecurringInvoice($invoice)
{
$activity = Activity::getBlank();
$activity->recurring_invoice_id = $invoice->id;
$activity->client_id = $invoice->client_id;
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_INVOICE;
$activity->message = Auth::user()->getFullName() . ' archived recurring invoice ' . $invoice->getName();
$activity->save();
}
public static function createPayment($payment)
{
if (Auth::check())

6
app/models/Frequency.php Executable file
View File

@ -0,0 +1,6 @@
<?php
class Frequency extends Eloquent
{
public $timestamps = false;
}

View File

@ -33,55 +33,6 @@ class Invoice extends EntityModel
{
return ENTITY_INVOICE;
}
public function isRecurring()
{
return $this->how_often || $this->start_date || $this->end_date;
}
public function shouldSendToday()
{
$dayOfWeekToday = date('w');
$dayOfWeekStart = date('w', strtotime($this->start_date));
$dayOfMonthToday = date('j');
$dayOfMonthStart = date('j', strtotime($this->start_date));
if (!$this->last_sent_date) {
$daysSinceLastSent = 0;
$monthsSinceLastSent = 0;
} else {
$date1 = new DateTime($this->last_sent_date);
$date2 = new DateTime();
$diff = $date2->diff($date1);
$daysSinceLastSent = $diff->format("%a");
$monthsSinceLastSent = ($diff->format('%y') * 12) + $diff->format('%m');
if ($daysSinceLastSent == 0) {
return false;
}
}
switch ($this->how_often)
{
case FREQUENCY_WEEKLY:
return $dayOfWeekStart == $dayOfWeekToday;
case FREQUENCY_TWO_WEEKS:
return $dayOfWeekStart == $dayOfWeekToday && (!$daysSinceLastSent || $daysSinceLastSent == 14);
case FREQUENCY_FOUR_WEEKS:
return $dayOfWeekStart == $dayOfWeekToday && (!$daysSinceLastSent || $daysSinceLastSent == 28);
case FREQUENCY_MONTHLY:
return $dayOfMonthStart == $dayOfMonthToday || $daysSinceLastSent > 31;
case FREQUENCY_THREE_MONTHS:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 3)) || $daysSinceLastSent > (3 * 31);
case FREQUENCY_SIX_MONTHS:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 6)) || $daysSinceLastSent > (6 * 31);
case FREQUENCY_ANNUALLY:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 12)) || $daysSinceLastSent > (12 *31);
}
return false;
}
}
Invoice::created(function($invoice)

88
app/models/RecurringInvoice.php Executable file
View File

@ -0,0 +1,88 @@
<?php
class RecurringInvoice extends EntityModel
{
protected $hidden = array('id', 'account_id', 'client_id', 'created_at', 'updated_at', 'deleted_at');
public function account()
{
return $this->belongsTo('Account');
}
public function client()
{
return $this->belongsTo('Client');
}
public function invoice_items()
{
return $this->hasMany('RecurringInvoiceItem');
}
public function freqency()
{
return $this->belongsTo('Frequency');
}
public function getName()
{
return $this->start_date;
}
public function getEntityType()
{
return ENTITY_RECURRING_INVOICE;
}
public function shouldSendToday()
{
$dayOfWeekToday = date('w');
$dayOfWeekStart = date('w', strtotime($this->start_date));
$dayOfMonthToday = date('j');
$dayOfMonthStart = date('j', strtotime($this->start_date));
if (!$this->last_sent_date) {
$daysSinceLastSent = 0;
$monthsSinceLastSent = 0;
} else {
$date1 = new DateTime($this->last_sent_date);
$date2 = new DateTime();
$diff = $date2->diff($date1);
$daysSinceLastSent = $diff->format("%a");
$monthsSinceLastSent = ($diff->format('%y') * 12) + $diff->format('%m');
if ($daysSinceLastSent == 0) {
return false;
}
}
switch ($this->frequency_id)
{
case FREQUENCY_WEEKLY:
return $dayOfWeekStart == $dayOfWeekToday;
case FREQUENCY_TWO_WEEKS:
return $dayOfWeekStart == $dayOfWeekToday && (!$daysSinceLastSent || $daysSinceLastSent == 14);
case FREQUENCY_FOUR_WEEKS:
return $dayOfWeekStart == $dayOfWeekToday && (!$daysSinceLastSent || $daysSinceLastSent == 28);
case FREQUENCY_MONTHLY:
return $dayOfMonthStart == $dayOfMonthToday || $daysSinceLastSent > 31;
case FREQUENCY_THREE_MONTHS:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 3)) || $daysSinceLastSent > (3 * 31);
case FREQUENCY_SIX_MONTHS:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 6)) || $daysSinceLastSent > (6 * 31);
case FREQUENCY_ANNUALLY:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 12)) || $daysSinceLastSent > (12 *31);
default:
echo "Error: invalid frequency_id - ".$this->frequency_id; exit; //TODO_FIX
break;
}
return false;
}
}
RecurringInvoice::created(function($invoice)
{
Activity::createInvoice($invoice, true);
});

View File

@ -0,0 +1,6 @@
<?php
class RecurringInvoiceItem extends EntityModel
{
}

View File

@ -14,6 +14,10 @@
//dd(DB::getQueryLog());
//dd(Client::getPrivateId(1));
//dd(new DateTime());
Route::get('/send_emails', function() {
Artisan::call('ninja:send-invoices');
});
Route::get('/', 'HomeController@showWelcome');
Route::post('get_started', 'AccountController@getStarted');
@ -61,6 +65,9 @@ Route::group(array('before' => 'auth'), function()
Route::get('invoices/create/{client_id?}', 'InvoiceController@create');
Route::post('invoices/bulk', 'InvoiceController@bulk');
Route::resource('recurring_invoices', 'RecurringInvoiceController');
Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'RecurringInvoiceController@getDatatable'));
Route::get('payments/{id}/edit', function() { return View::make('header'); });
Route::resource('payments', 'PaymentController');
Route::get('payments/create/{client_id?}', 'PaymentController@create');
@ -93,12 +100,17 @@ HTML::macro('menu_link', function($type) {
$types = $type.'s';
$Type = ucfirst($type);
$Types = ucfirst($types);
$class = ( Request::is($types) || Request::is($types.'/*')) ? ' active' : '';
return '<li class="dropdown '.$class.'">
$class = ( Request::is($types) || Request::is('*'.$type.'*')) ? ' active' : '';
$str= '<li class="dropdown '.$class.'">
<a href="'.URL::to($types).'" class="dropdown-toggle">'.$Types.'</a>
<ul class="dropdown-menu" id="menu1">
<!-- <li><a href="'.URL::to($types).'">List '.$Types.'</a></li> -->
<li><a href="'.URL::to($types.'/create').'">New '.$Type.'</a></li>
<li><a href="'.URL::to($types).'">View '.$Types.'</a></li>';
if ($Type == 'Invoice') {
$str .= '<li><a href="'.URL::to('recurring_invoices').'">View Recurring Invoices</a></li>';
}
return $str . '<li><a href="'.URL::to($types.'/create').'">New '.$Type.'</a></li>
</ul>
</li>';
});
@ -116,6 +128,7 @@ define("ENV_PRODUCTION", "production");
define("RECENTLY_VIEWED", "RECENTLY_VIEWED");
define("ENTITY_CLIENT", "client");
define("ENTITY_INVOICE", "invoice");
define("ENTITY_RECURRING_INVOICE", "recurring_invoice");
define("ENTITY_PAYMENT", "payment");
define("ENTITY_CREDIT", "credit");

View File

@ -89,6 +89,15 @@
<div class="tab-pane" id="invoices">
@if ($hasRecurringInvoices)
{{ Datatable::table()
->addColumn('Total', 'How Often', 'Start Date', 'End Date')
->setUrl(url('api/recurring_invoices/' . $client->public_id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->render('datatable') }}
@endif
{{ Datatable::table()
->addColumn('Invoice Number', 'Total', 'Amount Due', 'Invoice Date', 'Due Date', 'Status')
->setUrl(url('api/invoices/' . $client->public_id))

View File

@ -13,9 +13,7 @@
{{ Former::open($url)->method($method)->addClass('main_form')->rules(array(
'client' => 'required',
'invoice_number' => 'required',
'invoice_date' => 'required',
'product_key' => 'max:14',
'product_key' => 'max:14',
)); }}
<!-- <h3>{{ $title }} Invoice</h3> -->
@ -26,10 +24,11 @@
{{ Former::populateField('invoice_date', Utils::fromSqlDate($invoice->invoice_date)); }}
{{ Former::populateField('due_date', Utils::fromSqlDate($invoice->due_date)); }}
{{ Former::populateField('start_date', Utils::fromSqlDate($invoice->start_date)); }}
{{ Former::populateField('end_date', Utils::fromSqlDate($invoice->end_date)); }}
{{ Former::populateField('end_date', Utils::fromSqlDate($invoice->end_date)); }}
@else
{{ Former::populateField('invoice_number', $invoiceNumber) }}
{{ Former::populateField('invoice_date', date('m/d/Y')) }}
{{ Former::populateField('frequency', FREQUENCY_MONTHLY) }}
@endif
<div class="row" style="min-height:195px">
@ -39,19 +38,25 @@
{{ Former::textarea('notes') }}
</div>
<div class="col-md-5" id="col_2">
{{ Former::text('invoice_number')->label('Invoice #') }}
{{-- Former::text('invoice_date')->label('Invoice Date')->data_date_format('yyyy-mm-dd') --}}
{{ Former::text('invoice_date') }}
{{ Former::text('due_date')->help('<a id="showRecurring" style="cursor:pointer" onclick="toggleRecurring(true)">Enable recurring</a>') }}
{{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}}
<div id="recurring_checkbox">
{{ Former::checkbox('recurring')->text('Enable automatic invoicing | <a href="#">Learn more</a>')->onchange('toggleRecurring()')
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::timestampToDateString($invoice->last_sent_date) : '') }}
</div>
<div id="recurring_off">
{{ Former::text('invoice_number')->label('Invoice #') }}
{{ Former::text('invoice_date') }}
{{ Former::text('due_date') }}
{{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}}
{{-- Former::text('invoice_date')->label('Invoice Date')->data_date_format('yyyy-mm-dd') --}}
</div>
<div id="recurring_on" style="display:none">
{{ Former::select('frequency')->label('How often')->options($frequencies)->onchange('updateRecurringStats()') }}
{{ Former::text('start_date')->onchange('updateRecurringStats()') }}
{{ Former::text('end_date')->onchange('updateRecurringStats()') }}
</div>
</div>
<div class="col-md-3" id="col_3" style="display:none">
{{ Former::select('how_often')->options($frequencies) }}
{{ Former::text('start_date') }}
{{ Former::text('end_date')->help('<a id="hideRecurring" style="cursor:pointer;display:none" onclick="toggleRecurring(false)">Disable recurring</a>') }}
<div class="col-md-3" id="col_3" style="display:none">
</div>
</div>
@ -156,7 +161,7 @@
@endif
{{ Button::primary_submit('Save Invoice') }}
{{ Button::primary('Send Email', array('onclick' => 'onEmailClick()')) }}
{{ Button::primary('Send Email', array('id' => 'email_button', 'onclick' => 'onEmailClick()')) }}
</div>
<p>&nbsp;</p>
@ -294,9 +299,19 @@
});
@if ($invoice && $invoice->isRecurring())
toggleRecurring(true);
@endif;
$('label.radio').addClass('radio-inline');
@if ($isRecurring)
$('#recurring').prop('checked', true);
@if ($invoice)
$('#recurring_checkbox').hide();
@endif
@elseif (isset($invoice->recurring_invoice_id) && $invoice->recurring_invoice_id)
$('#recurring_checkbox > div > div').html('Created by a {{ link_to('/recurring_invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') }}').css('padding-top','6px');
@endif
toggleRecurring();
applyComboboxListeners();
refreshPDF();
@ -636,8 +651,25 @@
}
}
function toggleRecurring(show)
function toggleRecurring()
{
var enabled = $('#recurring').is(':checked');
if (enabled) {
$('#recurring_on').show();
$('#recurring_off').hide();
$('#email_button').prop('disabled', true);
@if (!$isRecurring)
$('.main_form').prop('action', '{{ URL::to('/recurring_invoices') }}');
@endif
} else {
$('#recurring_on').hide();
$('#recurring_off').show();
$('#email_button').prop('disabled', false);
$('.main_form').prop('action', '{{ $url }}');
}
/*
$('#col_1').toggleClass('col-md-6 col-md-5');
$('#col_2').toggleClass('col-md-5 col-md-3');
@ -654,7 +686,27 @@
if (!show) {
$('#how_often, #start_date, #end_date').val('')
}
}
*/
};
function updateRecurringStats()
{
/*
var howOften = $('#how_often').val();
var startDate = $('#start_date').val();
var endDate = $('#end_date').val();
console.log("%s %s %s", howOften, startDate, endDate);
var str = "Send ";
if (!endDate) {
str += " an unlimited number of ";
} else {
str += "";
}
str += " emails";
$('#stats').html(str);
*/
}
var products = {{ $products }};
var clients = {{ $clients }};

View File

@ -18,7 +18,7 @@
, array('id'=>'archive'))->split(); }}
{{ Button::primary_link(URL::to($entityType . 's/create'), 'New ' . ucwords($entityType), array('class' => 'pull-right')) }}
{{ Button::primary_link(URL::to($entityType . 's/create'), 'New ' . Utils::getEntityName($entityType), array('class' => 'pull-right')) }}
{{ Datatable::table()
->addColumn($columns)