1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Added support for all Omnipay providers

This commit is contained in:
Hillel Coren 2013-11-28 14:15:34 +02:00
parent 426e7838c9
commit 746d5ec773
14 changed files with 238 additions and 160 deletions

0
LICENSE Executable file → Normal file
View File

2
README.md Executable file → Normal file
View File

@ -6,8 +6,6 @@
Most online invoicing sites are expensive. They shouldn't be. The aim of this project is to provide a free, open-source alternative. Additionally, the hope is this codebase will serve as a sample site for Laravel as well as other JavaScript technologies. Most online invoicing sites are expensive. They shouldn't be. The aim of this project is to provide a free, open-source alternative. Additionally, the hope is this codebase will serve as a sample site for Laravel as well as other JavaScript technologies.
If you'd like to get involved please send an email to hillelcoren at gmail.
### Features ### Features
* Core application built using Laravel 4 * Core application built using Laravel 4
* Invoice PDF generation directly in the browser * Invoice PDF generation directly in the browser

View File

@ -51,14 +51,31 @@ class AccountController extends \BaseController {
else if ($section == ACCOUNT_SETTINGS) else if ($section == ACCOUNT_SETTINGS)
{ {
$account = Account::with('account_gateways')->find(Auth::user()->account_id); $account = Account::with('account_gateways')->find(Auth::user()->account_id);
$gateways = Gateway::all(); $accountGateway = null;
foreach ($gateways as $gateway) if (count($account->account_gateways) > 0)
{ {
$gateway->fields = Omnipay::create($gateway->provider)->getDefaultParameters(); $accountGateway = $account->account_gateways[0];
} }
return View::make('accounts.settings', array('account' => $account, 'gateways' => $gateways)); $data = [
'account' => $account,
'accountGateway' => $accountGateway,
'config' => json_decode($accountGateway->config),
'gateways' => Gateway::all()
];
foreach ($data['gateways'] as $gateway)
{
$gateway->fields = Omnipay::create($gateway->provider)->getDefaultParameters();
if ($accountGateway && $accountGateway->gateway_id == $gateway->id)
{
$accountGateway->fields = $gateway->fields;
}
}
return View::make('accounts.settings', $data);
} }
else if ($section == ACCOUNT_IMPORT) else if ($section == ACCOUNT_IMPORT)
{ {
@ -313,23 +330,18 @@ class AccountController extends \BaseController {
return View::make('accounts.import_map', $data); return View::make('accounts.import_map', $data);
} }
private function saveSetting() private function saveSettings()
{ {
$gateways = Gateway::all();
$rules = array(); $rules = array();
foreach ($gateways as $gateway) if ($gatewayId = Input::get('gateway_id'))
{ {
if (!Input::get('gateway_'.$gateway->id)) $gateway = Gateway::find($gatewayId);
{
continue;
}
$fields = Omnipay::create($gateway->provider)->getDefaultParameters(); $fields = Omnipay::create($gateway->provider)->getDefaultParameters();
foreach ($fields as $field => $details) foreach ($fields as $field => $details)
{ {
if (in_array($field, array('username','password','signature'))) if (!in_array($field, ['testMode', 'developerMode', 'headerImageUrl', 'solutionType', 'landingPage']))
{ {
$rules[$gateway->id.'_'.$field] = 'required'; $rules[$gateway->id.'_'.$field] = 'required';
} }
@ -347,25 +359,24 @@ class AccountController extends \BaseController {
else else
{ {
$account = Account::find(Auth::user()->account_id); $account = Account::find(Auth::user()->account_id);
/* Payment Gateways */
$account->account_gateways()->forceDelete(); $account->account_gateways()->forceDelete();
foreach ($gateways as $gateway)
if ($gatewayId)
{ {
if (!Input::get('gateway_'.$gateway->id)) $accountGateway = new AccountGateway;
$accountGateway->gateway_id = $gatewayId;
$config = new stdClass;
foreach ($fields as $field => $details)
{ {
continue; $config->$field = Input::get($gateway->id.'_'.$field);
} }
$accountGateway = new AccountGateway; $accountGateway->config = json_encode($config);
$accountGateway->gateway_id = $gateway->id;
$accountGateway->username = Input::get($gateway->id.'_username');
$accountGateway->password = Input::get($gateway->id.'_password');
$accountGateway->signature = Input::get($gateway->id.'_signature');
$account->account_gateways()->save($accountGateway); $account->account_gateways()->save($accountGateway);
} }
Session::flash('message', 'Successfully updated account'); Session::flash('message', 'Successfully updated settings');
return Redirect::to('account/settings'); return Redirect::to('account/settings');
} }
} }
@ -411,7 +422,7 @@ class AccountController extends \BaseController {
Image::make($path)->resize(150, 100, true, false)->save('logo/' . $account->key . '.jpg'); Image::make($path)->resize(150, 100, true, false)->save('logo/' . $account->key . '.jpg');
} }
Session::flash('message', 'Successfully updated account'); Session::flash('message', 'Successfully updated details');
return Redirect::to('account/details'); return Redirect::to('account/details');
} }
} }

View File

@ -18,7 +18,7 @@ class InvoiceController extends \BaseController {
return Datatable::collection(Invoice::with('client','invoice_items')->where('account_id','=',Auth::user()->account_id)->get()) return Datatable::collection(Invoice::with('client','invoice_items')->where('account_id','=',Auth::user()->account_id)->get())
->addColumn('number', function($model) ->addColumn('number', function($model)
{ {
return link_to('invoices/' . $model->id . '/edit', $model->number); return link_to('invoices/' . $model->id . '/edit', $model->invoice_number);
}) })
->addColumn('client', function($model) ->addColumn('client', function($model)
{ {
@ -47,24 +47,26 @@ class InvoiceController extends \BaseController {
return View::make('invoices.view')->with('invoice', $invoice); return View::make('invoices.view')->with('invoice', $invoice);
} }
private function createGateway($config) private function createGateway($accountGateway)
{ {
$gateway = Omnipay::create($accountGateway->gateway->provider);
$config = json_decode($accountGateway->config);
/* /*
$gateway = Omnipay::create($config->gateway->provider);
$gateway->setUsername($config->username);
$gateway->setPassword($config->password);
$gateway->setSignature($config->signature);
*/
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('hillelcoren_api1.gmail.com');
$gateway->setPassword('1385044249');
$gateway->setSignature('AFcWxV21C7fd0v3bYYYRCpSSRl31AWJs8aLiB19haXzcAicPwo6qC7Hm');
$gateway->setTestMode(true);
$gateway->setSolutionType ("Sole"); $gateway->setSolutionType ("Sole");
$gateway->setLandingPage("Billing"); $gateway->setLandingPage("Billing");
//$gateway->headerImageUrl = ""; */
foreach ($config as $key => $val)
{
if (!$val)
{
continue;
}
$function = "set" . ucfirst($key);
$gateway->$function($val);
}
return $gateway; return $gateway;
} }
@ -72,8 +74,8 @@ class InvoiceController extends \BaseController {
private function getPaymentDetails($invoice) private function getPaymentDetails($invoice)
{ {
$data = array( $data = array(
'firstName' => 'Bobby', 'firstName' => '',
'lastName' => 'Tables', 'lastName' => '',
); );
$card = new CreditCard($data); $card = new CreditCard($data);
@ -90,20 +92,26 @@ 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('invoice_key', '=', $invoiceKey)->firstOrFail();
//$config = $invoice->client->account->account_gateways[0]; $accountGateway = $invoice->client->account->account_gateways[0];
$gateway = InvoiceController::createGateway($accountGateway);
$gateway = InvoiceController::createGateway(false);
try try
{ {
$details = InvoiceController::getPaymentDetails($invoice); $details = InvoiceController::getPaymentDetails($invoice);
$response = $gateway->purchase($details)->send(); $response = $gateway->purchase($details)->send();
$ref = $response->getTransactionReference();
if (!$ref)
{
var_dump($response);
exit('Sorry, there was an error processing your payment. Please try again later.');
}
$payment = new Payment; $payment = new Payment;
$payment->invoice_id = $invoice->id; $payment->invoice_id = $invoice->id;
$payment->account_id = $invoice->account_id; $payment->account_id = $invoice->account_id;
$payment->contact_id = 0; $payment->contact_id = 0; // TODO_FIX
$payment->transaction_reference = $response->getTransactionReference(); $payment->transaction_reference = $ref;
$payment->save(); $payment->save();
if ($response->isSuccessful()) if ($response->isSuccessful())
@ -116,7 +124,7 @@ class InvoiceController extends \BaseController {
} }
else else
{ {
exit($response->getMessage());
} }
} }
catch (\Exception $e) catch (\Exception $e)
@ -133,7 +141,9 @@ class InvoiceController extends \BaseController {
$token = Request::query('token'); $token = Request::query('token');
$payment = Payment::with('invoice.invoice_items')->where('transaction_reference','=',$token)->firstOrFail(); $payment = Payment::with('invoice.invoice_items')->where('transaction_reference','=',$token)->firstOrFail();
$gateway = InvoiceController::createGateway(false); $invoice = Invoice::with('client.account.account_gateways.gateway')->where('id', '=', $payment->invoice_id)->firstOrFail();
$accountGateway = $invoice->client->account->account_gateways[0];
$gateway = InvoiceController::createGateway($accountGateway);
try try
{ {
@ -180,6 +190,7 @@ class InvoiceController extends \BaseController {
'url' => 'invoices', 'url' => 'invoices',
'title' => 'New', 'title' => 'New',
'client' => $client, 'client' => $client,
'items' => json_decode(Input::old('items')),
'account' => Auth::user()->account, 'account' => Auth::user()->account,
'products' => Product::getProducts()->get(), 'products' => Product::getProducts()->get(),
'clients' => Client::where('account_id','=',Auth::user()->account_id)->get()); 'clients' => Client::where('account_id','=',Auth::user()->account_id)->get());
@ -199,13 +210,15 @@ class InvoiceController extends \BaseController {
private function save($id = null) private function save($id = null)
{ {
$rules = array( $rules = array(
'number' => 'required',
'client' => 'required', 'client' => 'required',
'invoice_number' => 'required',
'invoice_date' => 'required'
); );
$validator = Validator::make(Input::all(), $rules); $validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) { if ($validator->fails()) {
return Redirect::to('invoices/create') return Redirect::to('invoices/create')
->withInput()
->withErrors($validator); ->withErrors($validator);
} else { } else {
@ -238,19 +251,24 @@ class InvoiceController extends \BaseController {
$invoice->account_id = Auth::user()->account_id; $invoice->account_id = Auth::user()->account_id;
} }
$date = DateTime::createFromFormat('m/d/Y', Input::get('issued_on')); $date = DateTime::createFromFormat('m/d/Y', Input::get('invoice_date'));
$invoice->client_id = $clientId; $invoice->client_id = $clientId;
$invoice->number = Input::get('number'); $invoice->invoice_number = Input::get('invoice_number');
$invoice->discount = 0; $invoice->discount = 0;
$invoice->issued_on = $date->format('Y-m-d'); $invoice->invoice_date = $date->format('Y-m-d');
$invoice->save(); $invoice->save();
$items = json_decode(Input::get('items')); $items = json_decode(Input::get('items'));
foreach ($items as $item) { foreach ($items as $item) {
if (!isset($item->cost) || !isset($item->qty)) { if (!isset($item->cost))
continue; {
$item->cost = 0;
}
if (!isset($item->qty))
{
$item->qty = 0;
} }
$product = Product::findProduct($item->product_key); $product = Product::findProduct($item->product_key);
@ -260,10 +278,12 @@ 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->product_key = $item->product_key;
}
$product->notes = $item->notes; $product->notes = $item->notes;
$product->cost = $item->cost; $product->cost = $item->cost;
$product->qty = $item->qty;
$product->save(); $product->save();
}
$invoiceItem = new InvoiceItem; $invoiceItem = new InvoiceItem;
$invoiceItem->product_id = $product->id; $invoiceItem->product_id = $product->id;

View File

@ -51,6 +51,7 @@ class ConfideSetupUsersTable extends Migration {
$t->string('name'); $t->string('name');
$t->string('provider'); $t->string('provider');
$t->boolean('visible')->default(true);
}); });
Schema::create('account_gateways', function($t) Schema::create('account_gateways', function($t)
@ -61,9 +62,7 @@ class ConfideSetupUsersTable extends Migration {
$t->timestamps(); $t->timestamps();
$t->softDeletes(); $t->softDeletes();
$t->string('username'); $t->text('config');
$t->string('password');
$t->string('signature');
}); });
Schema::create('users', function($t) Schema::create('users', function($t)
@ -140,9 +139,9 @@ class ConfideSetupUsersTable extends Migration {
$t->softDeletes(); $t->softDeletes();
$t->string('invoice_key')->unique(); $t->string('invoice_key')->unique();
$t->string('number'); $t->string('invoice_number');
$t->float('discount'); $t->float('discount');
$t->date('issued_on'); $t->date('invoice_date');
//$t->foreign('account_id')->references('id')->on('accounts'); //$t->foreign('account_id')->references('id')->on('accounts');
}); });
@ -173,6 +172,7 @@ class ConfideSetupUsersTable extends Migration {
$t->string('product_key'); $t->string('product_key');
$t->string('notes'); $t->string('notes');
$t->decimal('cost', 8, 2); $t->decimal('cost', 8, 2);
$t->integer('qty');
//$t->foreign('account_id')->references('id')->on('accounts'); //$t->foreign('account_id')->references('id')->on('accounts');
}); });

View File

@ -7,15 +7,43 @@ class ConstantsSeeder extends Seeder
{ {
DB::table('gateways')->delete(); DB::table('gateways')->delete();
Gateway::create(array( $gateways = [
'name' => 'PayPal Express', array('name'=>'Authorize.Net AIM', 'provider'=>'AuthorizeNet_AIM'),
'provider' => 'PayPal_Express' array('name'=>'Authorize.Net SIM', 'provider'=>'AuthorizeNet_SIM'),
)); array('name'=>'Buckaroo', 'provider'=>'Buckaroo'),
array('name'=>'Buckaroo Ideal', 'provider'=>'Buckaroo_Ideal'),
array('name'=>'Buckaroo PayPal', 'provider'=>'Buckaroo_PayPal'),
array('name'=>'CardSave', 'provider'=>'CardSave'),
array('name'=>'Eway Rapid', 'provider'=>'Eway_Rapid'),
array('name'=>'FirstData Connect', 'provider'=>'FirstData_Connect'),
array('name'=>'GoCardless', 'provider'=>'GoCardless'),
array('name'=>'Migs ThreeParty', 'provider'=>'Migs_ThreeParty'),
array('name'=>'Migs TwoParty', 'provider'=>'Migs_TwoParty'),
array('name'=>'Mollie', 'provider'=>'Mollie'),
array('name'=>'MultiSafepay', 'provider'=>'MultiSafepay'),
array('name'=>'Netaxept', 'provider'=>'Netaxept'),
array('name'=>'NetBanx', 'provider'=>'NetBanx'),
array('name'=>'PayFast', 'provider'=>'PayFast'),
array('name'=>'Payflow Pro', 'provider'=>'Payflow_Pro'),
array('name'=>'PaymentExpress PxPay', 'provider'=>'PaymentExpress_PxPay'),
array('name'=>'PaymentExpress PxPost', 'provider'=>'PaymentExpress_PxPost'),
array('name'=>'PayPal Express', 'provider'=>'PayPal_Express'),
array('name'=>'PayPal Pro', 'provider'=>'PayPal_Pro'),
array('name'=>'Pin', 'provider'=>'Pin'),
array('name'=>'SagePay Direct', 'provider'=>'SagePay_Direct'),
array('name'=>'SagePay Server', 'provider'=>'SagePay_Server'),
array('name'=>'SecurePay DirectPost', 'provider'=>'SecurePay_DirectPost'),
array('name'=>'Stripe', 'provider'=>'Stripe'),
array('name'=>'TargetPay Direct eBanking', 'provider'=>'TargetPay_Directebanking'),
array('name'=>'TargetPay Ideal', 'provider'=>'TargetPay_Ideal'),
array('name'=>'TargetPay Mr Cash', 'provider'=>'TargetPay_Mrcash'),
array('name'=>'TwoCheckout', 'provider'=>'TwoCheckout'),
array('name'=>'WorldPay', 'provider'=>'WorldPay'),
];
/* foreach ($gateways as $gateway)
ActivityType::create(array( {
'name' => 'Created invoice' Gateway::create($gateway);
)); }
*/
} }
} }

View File

@ -9,6 +9,8 @@ class DatabaseSeeder extends Seeder {
*/ */
public function run() public function run()
{ {
$this->command->info('Running DatabaseSeeder');
Eloquent::unguard(); Eloquent::unguard();
$this->call('UserTableSeeder'); $this->call('UserTableSeeder');

View File

@ -80,7 +80,7 @@ class Activity extends Eloquent
{ {
$activity = new Activity; $activity = new Activity;
$activity->contact_id = $payment->contact_id; $activity->contact_id = $payment->contact_id;
$activity->message = $contact->getFullName() . ' created payment ' . $payment->transaction_reference; //$activity->message = $contact->getFullName() . ' created payment ' . $payment->transaction_reference;
} }
$activity->payment_id = $payment->id; $activity->payment_id = $payment->id;
@ -107,7 +107,7 @@ class Activity extends Eloquent
$activity->invoice_id = $invoice->id; $activity->invoice_id = $invoice->id;
$activity->client_id = $invoice->client_id; $activity->client_id = $invoice->client_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();
} }
} }

View File

@ -11,6 +11,9 @@
| |
*/ */
//dd(Omnipay::getFactory()->find());
Route::get('/', 'HomeController@showWelcome'); Route::get('/', 'HomeController@showWelcome');
Route::post('get_started', 'AccountController@getStarted'); Route::post('get_started', 'AccountController@getStarted');
@ -97,6 +100,11 @@ function toArray($data)
return json_decode(json_encode((array) $data), true); return json_decode(json_encode((array) $data), true);
} }
function toSpaceCase($camelStr)
{
return preg_replace('/([a-z])([A-Z])/s','$1 $2', $camelStr);
}
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

@ -4,48 +4,57 @@
@parent @parent
{{ Former::open()->addClass('col-md-9 col-md-offset-1') }} {{ Former::open()->addClass('col-md-9 col-md-offset-1') }}
{{ Former::legend('Payment Gateways') }} {{ Former::legend('Payment Gateway') }}
@if ($accountGateway)
{{ Former::populateField('gateway_id', $accountGateway->gateway_id) }}
@foreach ($accountGateway->fields as $field => $junk)
{{ Former::populateField($accountGateway->gateway_id.'_'.$field, $config->$field) }}
@endforeach
@endif
{{ Former::select('gateway_id')->label('Provider')->addOption('', '')->fromQuery($gateways, 'name', 'id')->onchange('setFieldsShown()'); }}
@foreach ($gateways as $gateway) @foreach ($gateways as $gateway)
{{ Former::checkbox('gateway_'.$gateway->id)->label('')->text($gateway->provider)
->check($account->isGatewayConfigured($gateway->id)) }}
<div id="gateway_{{ $gateway->id }}_div" style="display: none"> <div id="gateway_{{ $gateway->id }}_div" style="display: none">
@foreach ($gateway->fields as $field => $details) @foreach ($gateway->fields as $field => $details)
@if ($config = $account->getGatewayConfig($gateway->id))
{{ Former::populateField($gateway->id.'_'.$field, $config[$field]) }} @if ($field == 'solutionType' || $field == 'landingPage')
@endif {{-- do nothing --}}
@if (in_array($field, array('username','password','signature'))) @elseif ($field == 'testMode' || $field == 'developerMode')
{{ Former::text($gateway->id.'_'.$field)->label($field) }} {{ Former::checkbox($gateway->id.'_'.$field)->label(toSpaceCase($field))->text('Enable') }}
@else
{{ Former::text($gateway->id.'_'.$field)->label(toSpaceCase($field)) }}
@endif @endif
@endforeach @endforeach
</div> </div>
@endforeach @endforeach
{{ Former::actions( Button::lg_primary_submit('Save') ) }} {{ Former::actions( Button::lg_primary_submit('Save') ) }}
{{ Former::close() }} {{ Former::close() }}
<script type="text/javascript"> <script type="text/javascript">
$(function() { var gateways = {{ $gateways }};
function setFieldsShown() {
function setFieldsShown(id) { var val = $('#gateway_id').val();
if ($('#gateway_' + id).is(':checked')) { for (var i=0; i<gateways.length; i++) {
$('#gateway_' + id + '_div').show(); var gateway = gateways[i];
if (val == gateway.id) {
$('#gateway_' + gateway.id + '_div').show();
} else { } else {
$('#gateway_' + id + '_div').hide(); $('#gateway_' + gateway.id + '_div').hide();
}
} }
} }
@foreach ($gateways as $gateway) $(function() {
$('#gateway_{{ $gateway->id }}').click(function() { setFieldsShown();
setFieldsShown('{{ $gateway->id }}');
});
setFieldsShown('{{ $gateway->id }}');
@endforeach
}); });
</script> </script>

View File

@ -88,16 +88,6 @@
} }
*/ */
a.ul {
text-decoration: underline;
cursor: pointer;
}
a.ul:hover {
text-decoration: none;
}
/* DataTables and BootStrap */ /* DataTables and BootStrap */
.dataTables_wrapper { .dataTables_wrapper {
padding-top: 16px; padding-top: 16px;

View File

@ -5,33 +5,35 @@
<p>&nbsp;</p> <p>&nbsp;</p>
{{ Former::open($url)->method($method)->addClass('main_form')->rules(array( {{ Former::open($url)->method($method)->addClass('main_form')->rules(array(
'number' => 'required', 'invoice_number' => 'required',
'invoice_date' => 'required'
)); }} )); }}
<!-- <h3>{{ $title }} Invoice</h3> --> <!-- <h3>{{ $title }} Invoice</h3> -->
@if ($invoice) @if ($invoice)
{{ Former::populate($invoice); }} {{ Former::populate($invoice); }}
{{ Former::populateField('issued_on', DateTime::createFromFormat('Y-m-d', $invoice->issued_on)->format('m/d/Y')); }} {{ Former::populateField('invoice_date', DateTime::createFromFormat('Y-m-d', $invoice->invoice_date)->format('m/d/Y')); }}
@else @else
{{ Former::populateField('issued_on', date('m/d/Y')) }} {{ Former::populateField('invoice_date', date('m/d/Y')) }}
@endif @endif
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
{{ Former::select('client')->addOption('', '')->fromQuery($clients, 'name', 'id')->select($client ? $client->id : '') {{ Former::select('client')->addOption('', '')->fromQuery($clients, 'name', 'id')->select($client ? $client->id : '')
->help('<a class="ul" data-toggle="modal" data-target="#myModal">Create new client</a>'); }} ->help('<a data-toggle="modal" data-target="#myModal">Create new client</a>'); }}
</div> </div>
<div class="col-md-5"> <div class="col-md-5">
{{ Former::text('number')->label('Invoice #') }} {{ Former::text('invoice_number')->label('Invoice #') }}
{{ Former::text('issued_on')->label('Invoice Date') }} {{ Former::text('invoice_date') }}
{{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}} {{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}}
</div> </div>
</div> </div>
<p>&nbsp;</p> <p>&nbsp;</p>
<input type="text" name="items" data-bind="value: ko.toJSON(items)" style="display:none"/> {{ Former::hidden('items')->data_bind("value: ko.toJSON(items)") }}
<table class="table invoice-table" style="margin-bottom: 0px !important"> <table class="table invoice-table" style="margin-bottom: 0px !important">
<thead> <thead>
<tr> <tr>
@ -46,12 +48,13 @@
</thead> </thead>
<tbody data-bind="sortable: { data: items, afterMove: onDragged }"> <tbody data-bind="sortable: { data: items, afterMove: onDragged }">
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row"> <tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
<td style="width:50px;" class="hide-border"> <td style="width:20px;" class="hide-border">
<i data-bind="click: $parent.addItem, visible: actionsVisible" class="fa fa-plus-circle" style="cursor:pointer" title="Add item"></i>&nbsp; <!-- <i data-bind="click: $parent.addItem, visible: actionsVisible" class="fa fa-plus-circle" style="cursor:pointer" title="Add item"></i>&nbsp; -->
<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')->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }} {{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'product_key')
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
</td> </td>
<td style="width:300px"> <td style="width:300px">
<textarea data-bind="value: notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" class="form-control" onchange="refreshPDF()"></textarea> <textarea data-bind="value: notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" class="form-control" onchange="refreshPDF()"></textarea>
@ -87,8 +90,11 @@
<td style="text-align: right"><span data-bind="text: discounted"/></td> <td style="text-align: right"><span data-bind="text: discounted"/></td>
</tr> </tr>
<tr> <tr>
<td colspan="3" class="hide-border"/> <td class="hide-border"></td>
<td colspan="2">Invoice Total</td> <td colspan="2" class="hide-border">
<a href="#" onclick="model.addItem()">Add line item</a>
</td>
<td colspan="2"><b>Invoice Total</b></td>
<td style="text-align: right"><span data-bind="text: total"/></td> <td style="text-align: right"><span data-bind="text: total"/></td>
</tr> </tr>
</tfoot> </tfoot>
@ -118,14 +124,14 @@
<h4 class="modal-title" id="myModalLabel">New Client</h4> <h4 class="modal-title" id="myModalLabel">New Client</h4>
</div> </div>
<div class="modal-body" style="min-height:80px"> <div class="modal-body" style="min-height:80px">
<div class="form-group required"> <div class="form-group">
<label for="name" class="control-label col-lg-2 col-sm-4">Name<sup>*</sup></label> <label for="name" class="control-label col-lg-2 col-sm-4">Name<sup>*</sup></label>
<div class="col-lg-10 col-sm-8"> <div class="col-lg-10 col-sm-8">
<input class="form-control" id="client_name" type="text" name="client_name" onkeypress="nameKeyPress(event)"> <input class="form-control" id="client_name" type="text" name="client_name" onkeypress="nameKeyPress(event)">
<span class="help-block" id="nameHelp" style="display: none">Please provide a value</span><span>&nbsp;</span> <span class="help-block" id="nameHelp" style="display: none">Please provide a value</span><span>&nbsp;</span>
</div> </div>
</div> </div>
<div class="form-group required"> <div class="form-group">
<label for="email" class="control-label col-lg-2 col-sm-4">Email<sup>*</sup></label> <label for="email" class="control-label col-lg-2 col-sm-4">Email<sup>*</sup></label>
<div class="col-lg-10 col-sm-8"> <div class="col-lg-10 col-sm-8">
<input class="form-control" id="client_email" type="text" name="client_email" onkeypress="nameKeyPress(event)"> <input class="form-control" id="client_email" type="text" name="client_email" onkeypress="nameKeyPress(event)">
@ -161,7 +167,7 @@
$(function() { $(function() {
$('#issued_on').datepicker({ $('#invoice_date').datepicker({
autoclose: true, autoclose: true,
todayHighlight: true todayHighlight: true
}).on('changeDate', function(e) { }).on('changeDate', function(e) {
@ -175,7 +181,7 @@
}); });
@if ($client) @if ($client)
$('input#number').focus(); $('input#invoice_number').focus();
@else @else
$('[name="client_combobox"]').focus(); $('[name="client_combobox"]').focus();
@endif @endif
@ -190,7 +196,7 @@
$('#client_name').focus(); $('#client_name').focus();
}) })
$('#number').change(refreshPDF); $('#invoice_number').change(refreshPDF);
applyComboboxListeners(); applyComboboxListeners();
refreshPDF(); refreshPDF();
@ -211,6 +217,7 @@
console.log(model); console.log(model);
model.notes(product.notes); model.notes(product.notes);
model.cost(product.cost); model.cost(product.cost);
model.qty(product.qty);
break; break;
} }
} }
@ -224,8 +231,8 @@
function createInvoiceModel() { function createInvoiceModel() {
var invoice = { var invoice = {
number: $('#number').val(), invoice_number: $('#invoice_number').val(),
issued_on: $('#issued_on').val(), invoice_date: $('#invoice_date').val(),
client: { client: {
name: $('[name="client_combobox"]').val() name: $('[name="client_combobox"]').val()
}, },
@ -285,7 +292,7 @@
$('#emailHelp').css( "display", "none" ); $('#emailHelp').css( "display", "none" );
//$('#client_name').val(''); //$('#client_name').val('');
$('#myModal').modal('hide'); $('#myModal').modal('hide');
$('input#number').focus(); $('input#invoice_number').focus();
refreshPDF(); refreshPDF();
} }
@ -304,13 +311,22 @@
self.discount = ko.observable(); self.discount = ko.observable();
self.items = ko.observableArray(); self.items = ko.observableArray();
@if ($invoice) @if (isset($items) && $items)
@foreach ($items as $item)
var item = new ItemModel();
item.product_key("{{ $item->product_key }}");
item.notes('{{ str_replace("\n", "\\n", $item->notes) }}');
item.cost("{{ isset($item->cost) ? $item->cost : '' }}");
item.qty("{{ isset($item->qty) ? $item->qty : '' }}");
self.items.push(item);
@endforeach
@elseif ($invoice)
self.discount({{ $invoice->discount }}); self.discount({{ $invoice->discount }});
@if (count($invoice->invoice_items) > 0) @if (count($invoice->invoice_items) > 0)
@foreach ($invoice->invoice_items as $item) @foreach ($invoice->invoice_items as $item)
var item = new ItemModel(); var item = new ItemModel();
item.product_key("{{ $item->product_key }}"); item.product_key("{{ $item->product_key }}");
item.notes('{{ str_replace("\n", "\\notes", $item->notes) }}'); item.notes('{{ str_replace("\n", "\\n", $item->notes) }}');
item.cost("{{ $item->cost }}"); item.cost("{{ $item->cost }}");
item.qty("{{ $item->qty }}"); item.qty("{{ $item->qty }}");
self.items.push(item); self.items.push(item);
@ -320,7 +336,6 @@
@endif @endif
@else @else
var model1 = new ItemModel(); var model1 = new ItemModel();
var model2 = new ItemModel();
/* /*
model1.item('TEST'); model1.item('TEST');
model1.notes('Some test text'); model1.notes('Some test text');
@ -328,7 +343,6 @@
model1.qty(1); model1.qty(1);
*/ */
self.items.push(model1); self.items.push(model1);
self.items.push(model2);
@endif @endif
self.removeItem = function(item) { self.removeItem = function(item) {

View File

@ -2,7 +2,7 @@
@section('content') @section('content')
@if (true || $invoice->client->account->isGatewayConfigured()) @if ($invoice->client->account->isGatewayConfigured())
{{ Button::primary_link(URL::to('payment/' . $invoice->invoice_key), 'Pay Now', array('class' => 'btn-lg pull-right')) }} {{ Button::primary_link(URL::to('payment/' . $invoice->invoice_key), 'Pay Now', array('class' => 'btn-lg pull-right')) }}
<div class="clearfix"></div><p>&nbsp;</p> <div class="clearfix"></div><p>&nbsp;</p>
@endif @endif

View File

@ -1,8 +1,8 @@
function generatePDF(invoice) { function generatePDF(invoice) {
var clientName = invoice.client.name; var clientName = invoice.client.name;
var invoiceNumber = invoice.number; var invoiceNumber = invoice.invoice_number;
var issuedOn = invoice.issued_on; var issuedOn = invoice.invoice_date;
var amount = '$0.00'; var amount = '$0.00';
var marginLeft = 90; var marginLeft = 90;
@ -28,7 +28,6 @@ function generatePDF(invoice) {
doc.addImage(invoice.image, 'JPEG', 30, 30, invoice.imageWidth, invoice.imageHeight); doc.addImage(invoice.image, 'JPEG', 30, 30, invoice.imageWidth, invoice.imageHeight);
} }
doc.setDrawColor(200,200,200); doc.setDrawColor(200,200,200);
doc.setFillColor(230,230,230); doc.setFillColor(230,230,230);
doc.rect(headerLeft - 6, headerTop + rowHeight + 4, headerRight - headerLeft + 12, rowHeight + 2, 'FD'); doc.rect(headerLeft - 6, headerTop + rowHeight + 4, headerRight - headerLeft + 12, rowHeight + 2, 'FD');
@ -46,7 +45,6 @@ function generatePDF(invoice) {
doc.setFontType("bold"); doc.setFontType("bold");
doc.text(headerLeft, headerTop + (2 * rowHeight), 'Amount Due'); doc.text(headerLeft, headerTop + (2 * rowHeight), 'Amount Due');
doc.setDrawColor(200,200,200); doc.setDrawColor(200,200,200);
doc.setFillColor(230,230,230); doc.setFillColor(230,230,230);
doc.rect(tableLeft - 6, tableTop - 12, headerRight - tableLeft + 12, rowHeight + 2, 'FD'); doc.rect(tableLeft - 6, tableTop - 12, headerRight - tableLeft + 12, rowHeight + 2, 'FD');
@ -113,7 +111,7 @@ function formatNumber(num) {
} }
function formatMoney(num) { function formatMoney(num) {
num = parseInt(num); num = parseFloat(num);
if (!num) return '$0.00'; if (!num) return '$0.00';
var p = num.toFixed(2).split("."); var p = num.toFixed(2).split(".");
return "$" + p[0].split("").reverse().reduce(function(acc, num, i, orig) { return "$" + p[0].split("").reverse().reduce(function(acc, num, i, orig) {