Prevent duplicate form submissions

This commit is contained in:
Hillel Coren 2013-11-28 18:40:13 +02:00
parent 746d5ec773
commit b79d5210fa
11 changed files with 143 additions and 38 deletions

View File

@ -119,6 +119,6 @@ return array(
| |
*/ */
'pretend' => false, 'pretend' => true,
); );

View File

@ -92,8 +92,11 @@ class ClientController extends \BaseController {
$contact->phone = Input::get('phone'); $contact->phone = Input::get('phone');
$client->contacts()->save($contact); $client->contacts()->save($contact);
$url = 'clients/' . $client->id;
processedRequest($url);
Session::flash('message', 'Successfully created client'); Session::flash('message', 'Successfully created client');
return Redirect::to('clients/' . $client->id); return Redirect::to($url);
} }
} }
@ -141,9 +144,23 @@ class ClientController extends \BaseController {
->withInput(Input::except('password')); ->withInput(Input::except('password'));
} else { } else {
$client = Client::find($id); $client = Client::find($id);
$client->name = Input::get('name'); $client->name = Input::get('name');
$client->work_phone = Input::get('work_phone');
$client->address1 = Input::get('address1');
$client->address2 = Input::get('address2');
$client->city = Input::get('city');
$client->state = Input::get('state');
$client->notes = Input::get('notes');
$client->postal_code = Input::get('postal_code');
$client->save(); $client->save();
$contact = $client->contacts[0];
$contact->email = Input::get('email');
$contact->first_name = Input::get('first_name');
$contact->last_name = Input::get('last_name');
$contact->phone = Input::get('phone');
$contact->save();
Session::flash('message', 'Successfully updated client'); Session::flash('message', 'Successfully updated client');
return Redirect::to('clients'); return Redirect::to('clients');
} }

View File

@ -37,14 +37,14 @@ class InvoiceController extends \BaseController {
} }
public function view($invoiceKey) public function view($key)
{ {
$invoice = Invoice::with('invoice_items', 'client.account.account_gateways')->where('invoice_key', '=', $invoiceKey)->firstOrFail(); $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.account.account_gateways')->where('key', '=', $key)->firstOrFail();
$contact = null; $contact = null;
Activity::viewInvoice($invoice, $contact); Activity::viewInvoice($invitation);
return View::make('invoices.view')->with('invoice', $invoice); return View::make('invoices.view')->with('invoice', $invitation->invoice);
} }
private function createGateway($accountGateway) private function createGateway($accountGateway)
@ -91,7 +91,7 @@ class InvoiceController extends \BaseController {
public function show_payment($invoiceKey) public function show_payment($invoiceKey)
{ {
$invoice = Invoice::with('invoice_items', 'client.account.account_gateways.gateway')->where('invoice_key', '=', $invoiceKey)->firstOrFail(); $invoice = Invoice::with('invoice_items', 'client.account.account_gateways.gateway')->where('key', '=', $invoiceKey)->firstOrFail();
$accountGateway = $invoice->client->account->account_gateways[0]; $accountGateway = $invoice->client->account->account_gateways[0];
$gateway = InvoiceController::createGateway($accountGateway); $gateway = InvoiceController::createGateway($accountGateway);
@ -159,7 +159,7 @@ class InvoiceController extends \BaseController {
$payment->save(); $payment->save();
Session::flash('message', 'Successfully applied payment'); Session::flash('message', 'Successfully applied payment');
return Redirect::to('view/' . $payment->invoice->invoice_key); return Redirect::to('view/' . $payment->invoice->key);
} }
else else
{ {
@ -247,16 +247,14 @@ class InvoiceController extends \BaseController {
$invoice->invoice_items()->forceDelete(); $invoice->invoice_items()->forceDelete();
} else { } else {
$invoice = new Invoice; $invoice = new Invoice;
$invoice->invoice_key = str_random(20);
$invoice->account_id = Auth::user()->account_id; $invoice->account_id = Auth::user()->account_id;
} }
$date = DateTime::createFromFormat('m/d/Y', Input::get('invoice_date'));
$invoice->client_id = $clientId; $invoice->client_id = $clientId;
$invoice->invoice_number = Input::get('invoice_number'); $invoice->invoice_number = Input::get('invoice_number');
$invoice->discount = 0; $invoice->discount = 0;
$invoice->invoice_date = $date->format('Y-m-d'); $invoice->invoice_date = toSqlDate(Input::get('invoice_date'));
$invoice->due_date = toSqlDate(Input::get('due_date'));
$invoice->save(); $invoice->save();
$items = json_decode(Input::get('items')); $items = json_decode(Input::get('items'));
@ -277,7 +275,7 @@ class InvoiceController extends \BaseController {
{ {
$product = new Product; $product = new Product;
$product->account_id = Auth::user()->account_id; $product->account_id = Auth::user()->account_id;
$product->product_key = $item->product_key; $product->key = $item->product_key;
} }
$product->notes = $item->notes; $product->notes = $item->notes;
@ -298,20 +296,30 @@ class InvoiceController extends \BaseController {
if (Input::get('send_email_checkBox')) if (Input::get('send_email_checkBox'))
{ {
$data = array('link' => URL::to('view') . '/' . $invoice->invoice_key); $data = array('link' => URL::to('view') . '/' . $invoice->invoice_key);
/*
Mail::send(array('html'=>'emails.invoice_html','text'=>'emails.invoice_text'), $data, function($message) use ($contact) Mail::send(array('html'=>'emails.invoice_html','text'=>'emails.invoice_text'), $data, function($message) use ($contact)
{ {
$message->from('hillelcoren@gmail.com', 'Hillel Coren'); $message->from('hillelcoren@gmail.com', 'Hillel Coren');
$message->to($contact->email); $message->to($contact->email);
}); });
*/
$invitation = new Invitation;
$invitation->invoice_id = $invoice->id;
$invitation->user_id = Auth::user()->id;
$invitation->contact_id = $contact->id;
$invitation->key = str_random(20);
$invitation->save();
Activity::emailInvoice($invoice, $contact);
Session::flash('message', 'Successfully emailed invoice'); Session::flash('message', 'Successfully emailed invoice');
} else { } else {
Session::flash('message', 'Successfully saved invoice'); Session::flash('message', 'Successfully saved invoice');
} }
return Redirect::to('invoices/' . $invoice->id . '/edit'); $url = 'invoices/' . $invoice->id . '/edit';
processedRequest($url);
return Redirect::to($url);
} }
} }

View File

@ -10,6 +10,7 @@ class ConfideSetupUsersTable extends Migration {
*/ */
public function up() public function up()
{ {
Schema::dropIfExists('invitations');
Schema::dropIfExists('activities'); Schema::dropIfExists('activities');
Schema::dropIfExists('account_gateways'); Schema::dropIfExists('account_gateways');
Schema::dropIfExists('gateways'); Schema::dropIfExists('gateways');
@ -138,14 +139,26 @@ class ConfideSetupUsersTable extends Migration {
$t->timestamps(); $t->timestamps();
$t->softDeletes(); $t->softDeletes();
$t->string('invoice_key')->unique();
$t->string('invoice_number'); $t->string('invoice_number');
$t->float('discount'); $t->float('discount');
$t->date('invoice_date'); $t->date('invoice_date');
$t->date('due_date');
//$t->foreign('account_id')->references('id')->on('accounts'); //$t->foreign('account_id')->references('id')->on('accounts');
}); });
Schema::create('invitations', function($t)
{
$t->increments('id');
$t->integer('user_id');
$t->integer('contact_id');
$t->integer('invoice_id');
$t->string('key')->unique();
$t->timestamps();
$t->softDeletes();
});
Schema::create('invoice_items', function($t) Schema::create('invoice_items', function($t)
{ {
$t->increments('id'); $t->increments('id');
@ -169,7 +182,7 @@ class ConfideSetupUsersTable extends Migration {
$t->timestamps(); $t->timestamps();
$t->softDeletes(); $t->softDeletes();
$t->string('product_key'); $t->string('key');
$t->string('notes'); $t->string('notes');
$t->decimal('cost', 8, 2); $t->decimal('cost', 8, 2);
$t->integer('qty'); $t->integer('qty');
@ -203,6 +216,7 @@ class ConfideSetupUsersTable extends Migration {
$t->integer('contact_id'); $t->integer('contact_id');
$t->integer('invoice_id'); $t->integer('invoice_id');
$t->integer('payment_id'); $t->integer('payment_id');
$t->integer('invitation_id');
$t->timestamps(); $t->timestamps();
$t->integer('activity_type_id'); $t->integer('activity_type_id');
@ -217,6 +231,7 @@ class ConfideSetupUsersTable extends Migration {
*/ */
public function down() public function down()
{ {
Schema::dropIfExists('invitations');
Schema::dropIfExists('activities'); Schema::dropIfExists('activities');
Schema::dropIfExists('account_gateways'); Schema::dropIfExists('account_gateways');
Schema::dropIfExists('gateways'); Schema::dropIfExists('gateways');

View File

@ -73,7 +73,19 @@ Route::filter('guest', function()
Route::filter('csrf', function() Route::filter('csrf', function()
{ {
if (Session::token() != Input::get('_token')) if (!Input::get('_token'))
{
return;
}
$tokenInput = Input::get('_token');
$tokenSession = Session::token();
if ($url = Session::get($tokenInput))
{
return Redirect::to($url);
}
else if ($tokenSession != $tokenInput)
{ {
throw new Illuminate\Session\TokenMismatchException; throw new Illuminate\Session\TokenMismatchException;
} }

View File

@ -59,13 +59,13 @@ class Activity extends Eloquent
$activity->save(); $activity->save();
} }
public static function emailInvoice($invoice, $contact) public static function emailInvoice($invitation)
{ {
$activity = Activity::getBlank(); $activity = Activity::getBlank();
$activity->invoice_id = $invoice->id; $activity->invoice_id = $invitation->invoice_id;
$activity->client_id = $invoice->client_id; $activity->contact_id = $invitation->contact_id;
$activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE; $activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE;
$activity->message = Auth::user()->getFullName() . ' emailed invoice ' . $invoice->number . ' to ' . $contact->getFullName(); //$activity->message = Auth::user()->getFullName() . ' emailed invoice ' . $invitation->invoice->number . ' to ' . $contact->getFullName();
$activity->save(); $activity->save();
} }
@ -100,12 +100,12 @@ class Activity extends Eloquent
$activity->save(); $activity->save();
} }
public static function viewInvoice($invoice, $contact) public static function viewInvoice($invitation)
{ {
$activity = new Activity; $activity = new Activity;
//$activity->contact_id = $contact->id; $activity->invitation_id = $invitation->invitation_id;
$activity->invoice_id = $invoice->id; $activity->contact_id = $invitation->contact_id;
$activity->client_id = $invoice->client_id; $activity->invoice_id = $invitation->invoice_id;
$activity->activity_type_id = ACTIVITY_TYPE_VIEW_INVOICE; $activity->activity_type_id = ACTIVITY_TYPE_VIEW_INVOICE;
//$activity->message = $contact->getFullName() . ' viewed invoice ' . $invoice->number; //$activity->message = $contact->getFullName() . ' viewed invoice ' . $invoice->number;
$activity->save(); $activity->save();

16
app/models/Invitation.php Normal file
View File

@ -0,0 +1,16 @@
<?php
class Invitation extends Eloquent
{
protected $softDelete = true;
public function invoice()
{
return $this->belongsTo('Invoice');
}
}
Invitation::created(function($invitation)
{
Activity::emailInvoice($invitation);
});

View File

@ -11,12 +11,12 @@ class Product extends Eloquent
public static function findProduct($key) public static function findProduct($key)
{ {
return Product::getProducts()->where('product_key','=',$key)->first(); return Product::getProducts()->where('key','=',$key)->first();
} }
public static function getProductKeys($products) public static function getProductKeys($products)
{ {
$products = array_pluck($products, 'product_key'); $products = array_pluck($products, 'key');
$products = array_combine($products, $products); $products = array_combine($products, $products);
return $products; return $products;

View File

@ -24,13 +24,13 @@ Route::get('complete', 'InvoiceController@do_payment');
Route::filter('auth', function() Route::filter('auth', function()
{ {
if (!Auth::check()) if (!Auth::check())
{ {
return Redirect::to('/'); return Redirect::to('/');
} }
}); });
Route::group(array('before' => 'auth'), function() Route::group(array('before' => array('auth', 'csrf')), function()
{ {
Route::get('account/{section?}', 'AccountController@showSection'); Route::get('account/{section?}', 'AccountController@showSection');
Route::post('account/{section?}', 'AccountController@doSection'); Route::post('account/{section?}', 'AccountController@doSection');
@ -39,7 +39,7 @@ Route::group(array('before' => 'auth'), function()
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
Route::resource('invoices', 'InvoiceController'); Route::resource('invoices', 'InvoiceController');
Route::get('api/invoices', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('api/invoices', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
Route::get('invoices/create/{client_id}', 'InvoiceController@create'); Route::get('invoices/create/{client_id}', 'InvoiceController@create');
Route::get('payments', 'PaymentController@index'); Route::get('payments', 'PaymentController@index');
@ -105,6 +105,32 @@ function toSpaceCase($camelStr)
return preg_replace('/([a-z])([A-Z])/s','$1 $2', $camelStr); return preg_replace('/([a-z])([A-Z])/s','$1 $2', $camelStr);
} }
function toSqlDate($date)
{
if (!$date)
{
return '';
}
return DateTime::createFromFormat('m/d/Y', $date);
}
function fromSqlDate($date)
{
if (!$date || $date == '0000-00-00')
{
return '';
}
return DateTime::createFromFormat('Y-m-d', $date)->format('m/d/Y');
}
function processedRequest($url)
{
Session::put(Input::get('_token'), $url);
Session::put('_token', md5(microtime()));
}
define("ENV_DEVELOPMENT", "local"); define("ENV_DEVELOPMENT", "local");
define("ENV_STAGING", "staging"); define("ENV_STAGING", "staging");
define("ENV_PRODUCTION", "production"); define("ENV_PRODUCTION", "production");

View File

@ -14,7 +14,7 @@
{{ Former::populate($client); }} {{ Former::populate($client); }}
@endif @endif
{{ Former::open($url)->addClass('col-md-9 col-md-offset-1')->method($method)->rules(array( {{ Former::open($url)->addClass('col-md-9 col-md-offset-1 main_form')->method($method)->rules(array(
'name' => 'required', 'name' => 'required',
'email' => 'email' 'email' => 'email'
)); }} )); }}
@ -38,7 +38,7 @@
{{ Former::text('state') }} {{ Former::text('state') }}
{{ Former::text('postal_code') }} {{ Former::text('postal_code') }}
{{ Former::actions()->lg_primary_submit('Save') }} {{ Former::actions( Button::lg_primary_submit('Save') ) }}
{{ Former::close() }} {{ Former::close() }}
@stop @stop

View File

@ -13,7 +13,8 @@
@if ($invoice) @if ($invoice)
{{ Former::populate($invoice); }} {{ Former::populate($invoice); }}
{{ Former::populateField('invoice_date', DateTime::createFromFormat('Y-m-d', $invoice->invoice_date)->format('m/d/Y')); }} {{ Former::populateField('invoice_date', fromSqlDate($invoice->invoice_date)); }}
{{ Former::populateField('due_date', fromSqlDate($invoice->due_date)); }}
@else @else
{{ Former::populateField('invoice_date', date('m/d/Y')) }} {{ Former::populateField('invoice_date', date('m/d/Y')) }}
@endif @endif
@ -26,6 +27,7 @@
<div class="col-md-5"> <div class="col-md-5">
{{ Former::text('invoice_number')->label('Invoice #') }} {{ Former::text('invoice_number')->label('Invoice #') }}
{{ Former::text('invoice_date') }} {{ Former::text('invoice_date') }}
{{ Former::text('due_date') }}
{{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}} {{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}}
</div> </div>
</div> </div>
@ -53,7 +55,7 @@
<i data-bind="visible: actionsVisible" class="fa fa-sort"></i> <i data-bind="visible: actionsVisible" class="fa fa-sort"></i>
</td> </td>
<td style="width:120px"> <td style="width:120px">
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'product_key') {{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }} ->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
</td> </td>
<td style="width:300px"> <td style="width:300px">
@ -106,7 +108,7 @@
<p>&nbsp;</p> <p>&nbsp;</p>
<div class="form-actions"> <div class="form-actions">
{{ Button::primary('Download PDF', array('onclick' => 'onDownloadClick()')) }} {{ Button::primary('Download PDF', array('onclick' => 'onDownloadClick()')) }}
{{ Button::primary_submit('Save Invoice') }} {{ Button::primary_submit('Save Invoice', array('onclick' => 'onSaveClick()')) }}
{{ Button::primary('Send Email', array('onclick' => 'onEmailClick()')) }} {{ Button::primary('Send Email', array('onclick' => 'onEmailClick()')) }}
</div> </div>
<p>&nbsp;</p> <p>&nbsp;</p>
@ -174,6 +176,11 @@
refreshPDF(); refreshPDF();
}); });
$('#due_date').datepicker({
autoclose: true,
todayHighlight: true
});
var $input = $('select#client'); var $input = $('select#client');
$input.combobox(); $input.combobox();
$('.combobox-container input.form-control').attr('name', 'client_combobox').on('change', function(e) { $('.combobox-container input.form-control').attr('name', 'client_combobox').on('change', function(e) {
@ -212,7 +219,7 @@
var key = $(this).val(); var key = $(this).val();
for (var i=0; i<products.length; i++) { for (var i=0; i<products.length; i++) {
var product = products[i]; var product = products[i];
if (product.product_key == key) { if (product.key == key) {
var model = ko.dataFor(this); var model = ko.dataFor(this);
console.log(model); console.log(model);
model.notes(product.notes); model.notes(product.notes);
@ -277,6 +284,10 @@
} }
} }
function onSaveClick() {
$('.main_form').submit();
}
function newClient() { function newClient() {
var name = $('#client_name').val(); var name = $('#client_name').val();
var email = $('#client_email').val(); var email = $('#client_email').val();