mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-09-19 16:01:34 +02:00
working on currency support
This commit is contained in:
parent
4ff55936d7
commit
ef24ac2c36
@ -57,4 +57,5 @@ Configure config/database.php and then initialize the database
|
||||
* [briannesbitt/Carbon](https://github.com/briannesbitt/Carbon) - A simple API extension for DateTime with PHP 5.3+
|
||||
* [thomaspark/bootswatch](https://github.com/thomaspark/bootswatch) - Themes for Bootstrap
|
||||
* [mozilla/pdf.js](https://github.com/mozilla/pdf.js) - PDF Reader in JavaScript
|
||||
* [nnnick/Chart.js](https://github.com/nnnick/Chart.js) - Simple HTML5 Charts using the <canvas> tag
|
||||
* [nnnick/Chart.js](https://github.com/nnnick/Chart.js) - Simple HTML5 Charts using the <canvas> tag
|
||||
* [josscrowcroft/accounting.js](https://github.com/josscrowcroft/accounting.js) - A lightweight JavaScript library for number, money and currency formatting
|
@ -92,6 +92,7 @@ class AccountController extends \BaseController {
|
||||
'timezones' => Timezone::orderBy('location')->get(),
|
||||
'dateFormats' => DateFormat::all(),
|
||||
'datetimeFormats' => DatetimeFormat::all(),
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
];
|
||||
|
||||
foreach ($data['gateways'] as $gateway)
|
||||
@ -406,6 +407,7 @@ class AccountController extends \BaseController {
|
||||
$account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null;
|
||||
$account->date_format_id = Input::get('date_format_id') ? Input::get('date_format_id') : null;
|
||||
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
|
||||
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : null;
|
||||
|
||||
$account->invoice_terms = Input::get('invoice_terms');
|
||||
$account->save();
|
||||
|
@ -9,7 +9,7 @@ class ActivityController extends \BaseController {
|
||||
return Datatable::collection(Activity::scope()->where('client_id','=',$clientId)->get())
|
||||
->addColumn('date', function($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); })
|
||||
->addColumn('message', function($model) { return $model->message; })
|
||||
->addColumn('balance', function($model) { return '$' . $model->balance; })
|
||||
->addColumn('balance', function($model) { return Utils::formatMoney($model->balance, $model->account->currency_id); })
|
||||
->orderColumns('date')
|
||||
->make();
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class ClientController extends \BaseController {
|
||||
->where('clients.account_id', '=', Auth::user()->account_id)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('contacts.is_primary', '=', true)
|
||||
->select('clients.public_id','clients.name','contacts.first_name','contacts.last_name','clients.balance','clients.last_login','clients.created_at','clients.work_phone','contacts.email');
|
||||
->select('clients.public_id','clients.name','contacts.first_name','contacts.last_name','clients.balance','clients.last_login','clients.created_at','clients.work_phone','contacts.email','clients.currency_id');
|
||||
|
||||
$filter = Input::get('sSearch');
|
||||
if ($filter)
|
||||
@ -51,7 +51,7 @@ class ClientController extends \BaseController {
|
||||
->addColumn('email', function($model) { return $model->email ? HTML::mailto($model->email, $model->email) : ''; })
|
||||
->addColumn('work_phone', function($model) { return Utils::formatPhoneNumber($model->work_phone); })
|
||||
->addColumn('last_login', function($model) { return Utils::timestampToDateString($model->last_login); })
|
||||
->addColumn('balance', function($model) { return '$' . $model->balance; })
|
||||
->addColumn('balance', function($model) { return Utils::formatMoney($model->balance, $model->currency_id); })
|
||||
->addColumn('dropdown', function($model)
|
||||
{
|
||||
return '<div class="btn-group tr-action" style="visibility:hidden;">
|
||||
@ -139,6 +139,7 @@ class ClientController extends \BaseController {
|
||||
'title' => '- ' . $client->name,
|
||||
'clientSizes' => ClientSize::orderBy('id')->get(),
|
||||
'clientIndustries' => ClientIndustry::orderBy('name')->get(),
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'countries' => Country::orderBy('name')->get());
|
||||
return View::make('clients.edit', $data);
|
||||
}
|
||||
@ -184,6 +185,7 @@ class ClientController extends \BaseController {
|
||||
$client->notes = trim(Input::get('notes'));
|
||||
$client->client_size_id = Input::get('client_size_id') ? Input::get('client_size_id') : null;
|
||||
$client->client_industry_id = Input::get('client_industry_id') ? Input::get('client_industry_id') : null;
|
||||
$client->currency_id = Input::get('currency_id') ? Input::get('currency_id') : null;
|
||||
$client->website = trim(Input::get('website'));
|
||||
|
||||
$client->save();
|
||||
|
@ -23,7 +23,7 @@ class CreditController extends \BaseController {
|
||||
->where('clients.account_id', '=', Auth::user()->account_id)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('credits.deleted_at', '=', null)
|
||||
->select('credits.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'credits.amount', 'credits.credit_date');
|
||||
->select('credits.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'credits.amount', 'credits.credit_date', 'credits.currency_id');
|
||||
|
||||
if ($clientPublicId) {
|
||||
$query->where('clients.public_id', '=', $clientPublicId);
|
||||
@ -45,8 +45,8 @@ class CreditController extends \BaseController {
|
||||
->addColumn('client_name', function($model) { return link_to('clients/' . $model->client_public_id, $model->client_name); });
|
||||
}
|
||||
|
||||
return $table->addColumn('amount', function($model){ return '$' . money_format('%i', $model->amount); })
|
||||
->addColumn('credit_date', function($model) { return Utils::timestampToDateString($model->credit_date); })
|
||||
return $table->addColumn('amount', function($model){ return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('credit_date', function($model) { return Utils::fromSqlDate($model->credit_date); })
|
||||
->addColumn('dropdown', function($model)
|
||||
{
|
||||
return '<div class="btn-group tr-action" style="visibility:hidden;">
|
||||
@ -79,6 +79,7 @@ class CreditController extends \BaseController {
|
||||
'method' => 'POST',
|
||||
'url' => 'credits',
|
||||
'title' => '- New Credit',
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'clients' => Client::scope()->orderBy('name')->get());
|
||||
|
||||
return View::make('credits.edit', $data);
|
||||
@ -93,6 +94,7 @@ class CreditController extends \BaseController {
|
||||
'method' => 'PUT',
|
||||
'url' => 'credits/' . $publicId,
|
||||
'title' => '- Edit Credit',
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'clients' => Client::scope()->orderBy('name')->get());
|
||||
return View::make('credit.edit', $data);
|
||||
}
|
||||
@ -130,6 +132,7 @@ class CreditController extends \BaseController {
|
||||
$credit->client_id = Input::get('client');
|
||||
$credit->credit_date = Utils::toSqlDate(Input::get('credit_date'));
|
||||
$credit->amount = floatval(Input::get('amount'));
|
||||
$credit->currency_id = Input::get('currency_id') ? Input::get('currency_id') : null;
|
||||
$credit->save();
|
||||
|
||||
$message = $publicId ? 'Successfully updated credit' : 'Successfully created credit';
|
||||
|
@ -55,8 +55,8 @@ class InvoiceController extends \BaseController {
|
||||
}
|
||||
|
||||
return $table->addColumn('invoice_date', function($model) { return Utils::fromSqlDate($model->invoice_date); })
|
||||
->addColumn('total', function($model) { return '$' . money_format('%i', $model->amount); })
|
||||
->addColumn('balance', function($model) { return '$' . money_format('%i', $model->balance); })
|
||||
->addColumn('total', function($model) { return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('balance', function($model) { return Utils::formatMoney($model->balance, $model->currency_id); })
|
||||
->addColumn('due_date', function($model) { return Utils::fromSqlDate($model->due_date); })
|
||||
->addColumn('invoice_status_name', function($model) { return $model->invoice_status_name; })
|
||||
->addColumn('dropdown', function($model)
|
||||
@ -94,7 +94,7 @@ class InvoiceController extends \BaseController {
|
||||
|
||||
return $table->addColumn('start_date', function($model) { return Utils::fromSqlDate($model->start_date); })
|
||||
->addColumn('end_date', function($model) { return Utils::fromSqlDate($model->end_date); })
|
||||
->addColumn('total', function($model) { return '$' . money_format('%i', $model->amount); })
|
||||
->addColumn('total', function($model) { return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('dropdown', function($model)
|
||||
{
|
||||
return '<div class="btn-group tr-action" style="visibility:hidden;">
|
||||
@ -352,6 +352,7 @@ class InvoiceController extends \BaseController {
|
||||
'countries' => Country::orderBy('name')->get(),
|
||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'frequencies' => array(
|
||||
1 => 'Weekly',
|
||||
2 => 'Two weeks',
|
||||
|
@ -19,7 +19,7 @@ class PaymentController extends \BaseController
|
||||
->where('payments.account_id', '=', Auth::user()->account_id)
|
||||
->where('payments.deleted_at', '=', null)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->select('payments.public_id', 'payments.transaction_reference', 'clients.name as client_name', 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', 'invoices.public_id as invoice_public_id', 'invoices.invoice_number');
|
||||
->select('payments.public_id', 'payments.transaction_reference', 'clients.name as client_name', 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', 'invoices.public_id as invoice_public_id', 'invoices.invoice_number', 'payments.currency_id');
|
||||
|
||||
if ($clientPublicId) {
|
||||
$query->where('clients.public_id', '=', $clientPublicId);
|
||||
@ -47,7 +47,7 @@ class PaymentController extends \BaseController
|
||||
}
|
||||
|
||||
return $table->addColumn('invoice_number', function($model) { return $model->invoice_public_id ? link_to('invoices/' . $model->invoice_public_id . '/edit', $model->invoice_number) : ''; })
|
||||
->addColumn('amount', function($model) { return '$' . $model->amount; })
|
||||
->addColumn('amount', function($model) { return Utils::formatMoney($model->amount, $model->currency_id); })
|
||||
->addColumn('payment_date', function($model) { return Utils::dateToString($model->payment_date); })
|
||||
->addColumn('dropdown', function($model)
|
||||
{
|
||||
@ -83,6 +83,7 @@ class PaymentController extends \BaseController
|
||||
'method' => 'POST',
|
||||
'url' => 'payments',
|
||||
'title' => '- New Payment',
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'clients' => Client::scope()->orderBy('name')->get());
|
||||
|
||||
return View::make('payments.edit', $data);
|
||||
@ -99,6 +100,7 @@ class PaymentController extends \BaseController
|
||||
'method' => 'PUT',
|
||||
'url' => 'payments/' . $publicId,
|
||||
'title' => '- Edit Payment',
|
||||
'currencies' => Currency::orderBy('name')->get(),
|
||||
'clients' => Client::scope()->orderBy('name')->get());
|
||||
return View::make('payments.edit', $data);
|
||||
}
|
||||
@ -137,6 +139,7 @@ class PaymentController extends \BaseController
|
||||
|
||||
$payment->client_id = Input::get('client');
|
||||
$payment->invoice_id = $invoiceId;
|
||||
$payment->currency_id = Input::get('currency_id') ? Input::get('currency_id') : null;
|
||||
$payment->payment_date = Utils::toSqlDate(Input::get('payment_date'));
|
||||
$payment->amount = floatval(Input::get('amount'));
|
||||
$payment->save();
|
||||
|
@ -9,7 +9,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
{
|
||||
Schema::dropIfExists('themes');
|
||||
Schema::dropIfExists('credits');
|
||||
Schema::dropIfExists('activities');
|
||||
@ -28,6 +28,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
Schema::dropIfExists('client_industries');
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('accounts');
|
||||
Schema::dropIfExists('currencies');
|
||||
Schema::dropIfExists('invoice_statuses');
|
||||
Schema::dropIfExists('countries');
|
||||
Schema::dropIfExists('timezones');
|
||||
@ -81,12 +82,24 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->string('label');
|
||||
});
|
||||
|
||||
Schema::create('currencies', function($t)
|
||||
{
|
||||
$t->increments('id');
|
||||
|
||||
$t->string('name');
|
||||
$t->string('symbol');
|
||||
$t->string('precision');
|
||||
$t->string('thousand_separator');
|
||||
$t->string('decimal_separator');
|
||||
});
|
||||
|
||||
Schema::create('accounts', function($t)
|
||||
{
|
||||
$t->increments('id');
|
||||
$t->unsignedInteger('timezone_id')->nullable();
|
||||
$t->unsignedInteger('date_format_id')->nullable();
|
||||
$t->unsignedInteger('datetime_format_id')->nullable();
|
||||
$t->unsignedInteger('currency_id')->nullable();
|
||||
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
@ -108,6 +121,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->foreign('date_format_id')->references('id')->on('date_formats');
|
||||
$t->foreign('datetime_format_id')->references('id')->on('datetime_formats');
|
||||
$t->foreign('country_id')->references('id')->on('countries');
|
||||
$t->foreign('currency_id')->references('id')->on('currencies');
|
||||
});
|
||||
|
||||
Schema::create('gateways', function($t)
|
||||
@ -186,6 +200,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->increments('id');
|
||||
$t->unsignedInteger('user_id');
|
||||
$t->unsignedInteger('account_id')->index();
|
||||
$t->unsignedInteger('currency_id')->default(1)->nullable();
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
|
||||
@ -198,8 +213,8 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('country_id')->nullable();
|
||||
$t->string('work_phone');
|
||||
$t->text('notes');
|
||||
$t->decimal('balance', 10, 2);
|
||||
$t->decimal('paid_to_date', 10, 2);
|
||||
$t->decimal('balance', 13, 4);
|
||||
$t->decimal('paid_to_date', 13, 4);
|
||||
$t->timestamp('last_login')->nullable();
|
||||
$t->string('website');
|
||||
$t->unsignedInteger('client_industry_id')->nullable();
|
||||
@ -211,7 +226,8 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->foreign('country_id')->references('id')->on('countries');
|
||||
$t->foreign('client_industry_id')->references('id')->on('client_industries');
|
||||
$t->foreign('client_size_id')->references('id')->on('client_sizes');
|
||||
|
||||
$t->foreign('currency_id')->references('id')->on('currencies');
|
||||
|
||||
$t->unsignedInteger('public_id')->index();
|
||||
$t->unique( array('account_id','public_id') );
|
||||
});
|
||||
@ -259,6 +275,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('user_id');
|
||||
$t->unsignedInteger('account_id')->index();
|
||||
$t->unsignedInteger('invoice_status_id')->default(1);
|
||||
$t->unsignedInteger('currency_id')->default(1);
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
|
||||
@ -277,13 +294,14 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('recurring_invoice_id')->index()->nullable();
|
||||
|
||||
|
||||
$t->decimal('amount', 10, 2);
|
||||
$t->decimal('balance', 10, 2);
|
||||
$t->decimal('amount', 13, 4);
|
||||
$t->decimal('balance', 13, 4);
|
||||
|
||||
$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('currency_id')->references('id')->on('currencies');
|
||||
$t->foreign('recurring_invoice_id')->references('id')->on('invoices');
|
||||
|
||||
$t->unsignedInteger('public_id')->index();
|
||||
@ -322,7 +340,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->softDeletes();
|
||||
|
||||
$t->string('name');
|
||||
$t->decimal('rate', 10, 2);
|
||||
$t->decimal('rate', 13, 4);
|
||||
|
||||
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
@ -341,8 +359,8 @@ class ConfideSetupUsersTable extends Migration {
|
||||
|
||||
$t->string('product_key');
|
||||
$t->string('notes');
|
||||
$t->decimal('cost', 10, 2);
|
||||
$t->decimal('qty', 10, 2);
|
||||
$t->decimal('cost', 13, 4);
|
||||
$t->decimal('qty', 13, 4);
|
||||
|
||||
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
@ -364,11 +382,11 @@ class ConfideSetupUsersTable extends Migration {
|
||||
|
||||
$t->string('product_key');
|
||||
$t->string('notes');
|
||||
$t->decimal('cost', 10, 2);
|
||||
$t->decimal('qty', 10, 2);
|
||||
$t->decimal('cost', 13, 4);
|
||||
$t->decimal('qty', 13, 4);
|
||||
|
||||
$t->string('tax_name');
|
||||
$t->decimal('tax_rate', 10, 2);
|
||||
$t->decimal('tax_rate', 13, 4);
|
||||
|
||||
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||
$t->foreign('product_id')->references('id')->on('products');
|
||||
@ -387,11 +405,12 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('contact_id')->nullable();
|
||||
$t->unsignedInteger('invitation_id')->nullable();
|
||||
$t->unsignedInteger('user_id')->nullable();
|
||||
$t->unsignedInteger('currency_id')->default(1);
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
|
||||
$t->boolean('is_deleted');
|
||||
$t->decimal('amount', 10, 2);
|
||||
$t->decimal('amount', 13, 4);
|
||||
$t->date('payment_date');
|
||||
$t->string('transaction_reference');
|
||||
$t->string('payer_id');
|
||||
@ -401,7 +420,8 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
$t->foreign('contact_id')->references('id')->on('contacts');
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
|
||||
$t->foreign('currency_id')->references('id')->on('currencies');
|
||||
|
||||
$t->unsignedInteger('public_id')->index();
|
||||
$t->unique( array('account_id','public_id') );
|
||||
});
|
||||
@ -413,11 +433,12 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('user_id');
|
||||
$t->unsignedInteger('client_id')->index()->nullable();
|
||||
$t->unsignedInteger('contact_id')->nullable();
|
||||
$t->unsignedInteger('currency_id')->default(1);
|
||||
$t->timestamps();
|
||||
$t->softDeletes();
|
||||
|
||||
$t->boolean('is_deleted');
|
||||
$t->decimal('amount', 10, 2);
|
||||
$t->decimal('amount', 13, 4);
|
||||
$t->date('credit_date')->nullable();
|
||||
$t->string('credit_number');
|
||||
|
||||
@ -425,6 +446,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
$t->foreign('contact_id')->references('id')->on('contacts');
|
||||
$t->foreign('user_id')->references('id')->on('users');
|
||||
$t->foreign('currency_id')->references('id')->on('currencies');
|
||||
|
||||
$t->unsignedInteger('public_id')->index();
|
||||
$t->unique( array('account_id','public_id') );
|
||||
@ -443,14 +465,16 @@ class ConfideSetupUsersTable extends Migration {
|
||||
$t->unsignedInteger('invoice_id');
|
||||
$t->unsignedInteger('credit_id');
|
||||
$t->unsignedInteger('invitation_id');
|
||||
|
||||
$t->unsignedInteger('currency_id')->default(1);
|
||||
|
||||
$t->text('message');
|
||||
$t->integer('activity_type_id');
|
||||
$t->decimal('adjustment', 10, 2);
|
||||
$t->decimal('balance', 10, 2);
|
||||
$t->decimal('adjustment', 13, 4);
|
||||
$t->decimal('balance', 13, 4);
|
||||
|
||||
$t->foreign('account_id')->references('id')->on('accounts');
|
||||
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
$t->foreign('currency_id')->references('id')->on('currencies');
|
||||
});
|
||||
}
|
||||
|
||||
@ -479,6 +503,7 @@ class ConfideSetupUsersTable extends Migration {
|
||||
Schema::dropIfExists('client_industries');
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('accounts');
|
||||
Schema::dropIfExists('currencies');
|
||||
Schema::dropIfExists('invoice_statuses');
|
||||
Schema::dropIfExists('countries');
|
||||
Schema::dropIfExists('timezones');
|
||||
|
@ -69,6 +69,10 @@ class ConstantsSeeder extends Seeder
|
||||
ClientSize::create(array('name' => '101 - 500'));
|
||||
ClientSize::create(array('name' => '500+'));
|
||||
|
||||
Currency::create(array('name' => 'US Dollar', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'));
|
||||
Currency::create(array('name' => 'Pound Sterling', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'));
|
||||
|
||||
|
||||
DatetimeFormat::create(array('format' => 'F j, Y, g:i a', 'label' => 'March 10, 2013, 6:15 pm'));
|
||||
DatetimeFormat::create(array('format' => 'D M jS, Y g:ia', 'label' => 'Mon March 10th, 2013, 6:15 pm'));
|
||||
|
||||
|
@ -33,5 +33,6 @@ class UserEventHandler
|
||||
Session::put(SESSION_DATE_FORMAT, $account->date_format ? $account->date_format->format : DEFAULT_DATE_FORMAT);
|
||||
Session::put(SESSION_DATE_PICKER_FORMAT, $account->date_format ? $account->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);
|
||||
Session::put(SESSION_DATETIME_FORMAT, $account->datetime_format ? $account->datetime_format->format : DEFAULT_DATETIME_FORMAT);
|
||||
Session::put(SESSION_CURRENCY, $account->currency_id ? $account->currency_id : DEFAULT_CURRENCY);
|
||||
}
|
||||
}
|
@ -35,6 +35,15 @@ class Utils
|
||||
return $phoneNumber;
|
||||
}
|
||||
|
||||
public static function formatMoney($value, $currencyId)
|
||||
{
|
||||
$currency = Currency::find($currencyId);
|
||||
if (!$currency) {
|
||||
$currency = Currency::find(1);
|
||||
}
|
||||
return $currency->symbol . number_format($value, $currency->precision, $currency->decimal_separator, $currency->thousand_separator);
|
||||
}
|
||||
|
||||
public static function pluralize($string, $count)
|
||||
{
|
||||
$string = str_replace('?', $count, $string);
|
||||
|
@ -26,6 +26,12 @@ class Activity extends Eloquent
|
||||
return $query->whereAccountId(Auth::user()->account_id);
|
||||
}
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo('Account');
|
||||
}
|
||||
|
||||
|
||||
private static function getBlank($entity = false)
|
||||
{
|
||||
$activity = new Activity;
|
||||
@ -75,6 +81,7 @@ class Activity extends Eloquent
|
||||
$activity = Activity::getBlank($invoice);
|
||||
$activity->invoice_id = $invoice->id;
|
||||
$activity->client_id = $invoice->client_id;
|
||||
$activity->currency_id = $invoice->currency_id;
|
||||
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_INVOICE;
|
||||
$activity->message = $message;
|
||||
$activity->save();
|
||||
@ -121,6 +128,7 @@ class Activity extends Eloquent
|
||||
$activity->invoice_id = $payment->invoice_id;
|
||||
}
|
||||
$activity->client_id = $payment->client_id;
|
||||
$activity->currency_id = $payment->currency_id;
|
||||
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_PAYMENT;
|
||||
$activity->save();
|
||||
}
|
||||
@ -132,6 +140,7 @@ class Activity extends Eloquent
|
||||
$activity->message = Auth::user()->getFullName() . ' created credit';
|
||||
$activity->credit_id = $credit->id;
|
||||
$activity->client_id = $credit->client_id;
|
||||
$activity->currency_id = $credit->currency_id;
|
||||
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_CREDIT;
|
||||
$activity->save();
|
||||
}
|
||||
|
7
app/models/Currency.php
Executable file
7
app/models/Currency.php
Executable file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
class Currency extends Eloquent
|
||||
{
|
||||
public $timestamps = false;
|
||||
protected $softDelete = false;
|
||||
}
|
@ -14,6 +14,7 @@ class AccountRepository
|
||||
$contacts = \DB::table('clients')
|
||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->whereRaw("CONCAT(contacts.first_name, contacts.last_name) <> ''")
|
||||
->select(\DB::raw("'Contacts' as type, clients.public_id, CONCAT(contacts.first_name, ' ', contacts.last_name, ': ', clients.name) as name, '' as token"));
|
||||
|
||||
$invoices = \DB::table('clients')
|
||||
|
@ -30,6 +30,7 @@ class ClientRepository
|
||||
$client->notes = trim($data['notes']);
|
||||
$client->client_size_id = $data['client_size_id'] ? $data['client_size_id'] : null;
|
||||
$client->client_industry_id = $data['client_industry_id'] ? $data['client_industry_id'] : null;
|
||||
$client->currency_id = $data['currency_id'] ? $data['currency_id'] : null;
|
||||
$client->website = trim($data['website']);
|
||||
$client->save();
|
||||
|
||||
|
@ -17,7 +17,7 @@ class InvoiceRepository
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'amount', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name');
|
||||
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'amount', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name', 'invoices.currency_id');
|
||||
|
||||
if ($clientPublicId)
|
||||
{
|
||||
@ -45,7 +45,7 @@ class InvoiceRepository
|
||||
->where('invoices.account_id', '=', $accountId)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('invoices.is_recurring', '=', true)
|
||||
->select('clients.public_id as client_public_id', 'clients.name as client_name', 'invoices.public_id', 'amount', 'frequencies.name as frequency', 'start_date', 'end_date');
|
||||
->select('clients.public_id as client_public_id', 'clients.name as client_name', 'invoices.public_id', 'amount', 'frequencies.name as frequency', 'start_date', 'end_date', 'invoices.currency_id');
|
||||
|
||||
if ($clientPublicId)
|
||||
{
|
||||
@ -88,7 +88,8 @@ class InvoiceRepository
|
||||
$invoice->end_date = Utils::toSqlDate($data['end_date']);
|
||||
$invoice->terms = trim($data['terms']);
|
||||
$invoice->po_number = trim($data['po_number']);
|
||||
|
||||
$invoice->currency_id = $data['currency_id'];
|
||||
|
||||
$total = 0;
|
||||
|
||||
foreach ($data['invoice_items'] as $item)
|
||||
|
@ -170,11 +170,13 @@ define('FREQUENCY_SIX_MONTHS', 6);
|
||||
define('FREQUENCY_ANNUALLY', 7);
|
||||
|
||||
define('SESSION_TIMEZONE', 'timezone');
|
||||
define('SESSION_CURRENCY', 'currency');
|
||||
define('SESSION_DATE_FORMAT', 'dateFormat');
|
||||
define('SESSION_DATE_PICKER_FORMAT', 'datePickerFormat');
|
||||
define('SESSION_DATETIME_FORMAT', 'datetimeFormat');
|
||||
|
||||
define('DEFAULT_TIMEZONE', 'US/Eastern');
|
||||
define('DEFAULT_CURRENCY', 1); // US Dollar
|
||||
define('DEFAULT_DATE_FORMAT', 'M j, Y');
|
||||
define('DEFAULT_DATE_PICKER_FORMAT', 'yyyy-mm-dd');
|
||||
define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a');
|
||||
|
@ -39,7 +39,9 @@
|
||||
@endforeach
|
||||
|
||||
|
||||
{{ Former::legend('Date and Time') }}
|
||||
{{ Former::legend('Localization') }}
|
||||
{{ Former::select('currency_id')->addOption('','')->label('Currency')
|
||||
->fromQuery($currencies, 'name', 'id')->select($account->currency_id) }}
|
||||
{{ Former::select('timezone_id')->addOption('','')->label('Timezone')
|
||||
->fromQuery($timezones, 'location', 'id')->select($account->timezone_id) }}
|
||||
{{ Former::select('date_format_id')->addOption('','')->label('Date Format')
|
||||
|
@ -64,6 +64,8 @@
|
||||
</div>
|
||||
|
||||
{{ Former::legend('Additional Info') }}
|
||||
{{ Former::select('currency_id')->addOption('','')->label('Currency')
|
||||
->fromQuery($currencies, 'name', 'id')->select(Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY)) }}
|
||||
{{ Former::select('client_size_id')->addOption('','')->label('Size')
|
||||
->fromQuery($clientSizes, 'name', 'id')->select($client ? $client->client_size_id : '') }}
|
||||
{{ Former::select('client_industry_id')->addOption('','')->label('Industry')
|
||||
|
@ -28,6 +28,8 @@
|
||||
@endif
|
||||
|
||||
{{ Former::select('client')->fromQuery($clients, 'name', 'public_id')->select($client ? $client->public_id : '')->addOption('', '')->addGroupClass('client-select') }}
|
||||
{{ Former::select('currency_id')->addOption('','')->label('Currency')
|
||||
->fromQuery($currencies, 'name', 'id')->select(Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY)) }}
|
||||
{{ Former::text('amount') }}
|
||||
{{ Former::text('credit_date')->data_date_format(DEFAULT_DATE_PICKER_FORMAT) }}
|
||||
|
||||
@ -50,7 +52,8 @@
|
||||
|
||||
var $input = $('select#client');
|
||||
$input.combobox();
|
||||
|
||||
$('#currency_id').combobox();
|
||||
|
||||
$('#credit_date').datepicker({
|
||||
autoclose: true,
|
||||
todayHighlight: true
|
||||
|
@ -36,7 +36,8 @@
|
||||
// dynamic table
|
||||
jQuery('.{{ $class }}').dataTable({
|
||||
// Disable sorting on the first column
|
||||
@if (isset($haeCheckboxes) && $hasCheckboxes)
|
||||
"aaSorting": [],
|
||||
@if (isset($hasCheckboxes) && $hasCheckboxes)
|
||||
"aoColumnDefs" : [ {
|
||||
'bSortable' : false,
|
||||
'aTargets' : [ 0 ]
|
||||
|
@ -33,7 +33,9 @@
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/typeahead.js-bootstrap.css') }}"/>
|
||||
|
||||
<script src="{{ asset('js/script.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ asset('js/accounting.js') }}" type="text/javascript"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/style.css') }}"/>
|
||||
<style type="text/css">
|
||||
|
||||
@if (!Auth::check() || Auth::user()->showGreyBackground())
|
||||
@ -42,191 +44,23 @@
|
||||
}
|
||||
@endif
|
||||
|
||||
/*
|
||||
body > div.container {
|
||||
min-height: 600px;
|
||||
}
|
||||
*/
|
||||
|
||||
label.checkbox,
|
||||
label.control-label {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.panel {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
.form-horizontal {
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
.form-group {
|
||||
width: 50%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
*/
|
||||
|
||||
/* DataTables and BootStrap */
|
||||
.dataTables_wrapper {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
table.table thead > tr > th {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
#DataTables_Table_0_length label {
|
||||
font-weight: normal;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
div.dataTables_paginate.paging_bootstrap {
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
/*
|
||||
table.table tbody tr.odd {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.table tbody tr:hover {
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* table sorting indicators */
|
||||
/*table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; }*/
|
||||
/*
|
||||
table.table thead .sorting,
|
||||
table.table thead .sorting_asc,
|
||||
table.table thead .sorting_desc,
|
||||
table.table thead .sorting_asc_disabled,
|
||||
table.table thead .sorting_desc_disabled {
|
||||
cursor: pointer;
|
||||
*cursor: hand;
|
||||
}
|
||||
|
||||
table.table thead .sorting_asc { background: url('images/sort_asc.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc { background: url('images/sort_desc.png') no-repeat center right; }
|
||||
|
||||
|
||||
table.table thead .sorting_asc_disabled { background: url('images/sort_asc_disabled.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc_disabled { background: url('images/sort_desc_disabled.png') no-repeat center right; }
|
||||
*/
|
||||
|
||||
|
||||
/* Hover nav */
|
||||
.sidebar-nav {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
||||
.dropdown-menu .sub-menu {
|
||||
left: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
visibility: hidden;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.dropdown-menu li:hover .sub-menu {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-tabs .dropdown-menu, .nav-pills .dropdown-menu, .navbar .dropdown-menu {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.navbar .sub-menu:before {
|
||||
border-bottom: 7px solid transparent;
|
||||
border-left: none;
|
||||
border-right: 7px solid rgba(0, 0, 0, 0.2);
|
||||
border-top: 7px solid transparent;
|
||||
left: -7px;
|
||||
top: 10px;
|
||||
}
|
||||
.navbar .sub-menu:after {
|
||||
border-top: 6px solid transparent;
|
||||
border-left: none;
|
||||
border-right: 6px solid #fff;
|
||||
border-bottom: 6px solid transparent;
|
||||
left: 10px;
|
||||
top: 11px;
|
||||
left: -6px;
|
||||
}
|
||||
|
||||
|
||||
.invoice-table {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
table.invoice-table tbody tr:hover {
|
||||
background-color: #FFFFFF !important;
|
||||
}
|
||||
*/
|
||||
|
||||
.invoice-table td {
|
||||
padding: 2px !important;
|
||||
}
|
||||
|
||||
.invoice-table td input,
|
||||
.invoice-table td textarea {
|
||||
border: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.invoice-table th {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}
|
||||
|
||||
.invoice-table td.hide-border,
|
||||
.invoice-table th.hide-border {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
.invoice-table td.td-icon {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.fa-bars {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.closer-row {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Animate col width changes */
|
||||
.row div {
|
||||
-webkit-transition: width 0.5s ease, margin 0.5s ease;
|
||||
-moz-transition: width 0.5s ease, margin 0.5s ease;
|
||||
-o-transition: width 0.5s ease, margin 0.5s ease;
|
||||
transition: width 0.5s ease, margin 0.5s ease;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
var currencies = {{ Currency::remember(120)->get(); }};
|
||||
var currencyMap = {};
|
||||
for (var i=0; i<currencies.length; i++) {
|
||||
var currency = currencies[i];
|
||||
currencyMap[currency.id] = currency;
|
||||
}
|
||||
function formatMoney(value, currency_id, hide_symbol) {
|
||||
if (!currency_id) currency_id = {{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY); }};
|
||||
var currency = currencyMap[currency_id];
|
||||
return accounting.formatMoney(value, hide_symbol ? '' : currency.symbol, currency.precision, currency.thousand_separator, currency.decimal_separator);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
|
@ -66,7 +66,7 @@
|
||||
<div class="col-md-3" id="col_2">
|
||||
{{ Former::text('po_number')->label('PO number')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::text('currency')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
|
||||
{{ Former::select('currency_id')->label('Currency')->fromQuery($currencies, 'name', 'id')->data_bind("value: currency_id") }}
|
||||
|
||||
<div class="form-group" style="margin-bottom: 8px">
|
||||
<label for="recurring" class="control-label col-lg-4 col-sm-4">Taxes</label>
|
||||
@ -241,6 +241,8 @@
|
||||
</div>
|
||||
|
||||
{{ Former::legend('Additional Info') }}
|
||||
{{ Former::select('currency_id')->addOption('','')->label('Currency')->data_bind('value: currency_id')
|
||||
->fromQuery($currencies, 'name', 'id') }}
|
||||
{{ Former::select('client_size_id')->addOption('','')->label('Size')->data_bind('value: client_size_id')
|
||||
->fromQuery($clientSizes, 'name', 'id')->select($client ? $client->client_size_id : '') }}
|
||||
{{ Former::select('client_industry_id')->addOption('','')->label('Industry')->data_bind('value: client_industry_id')
|
||||
@ -329,6 +331,10 @@
|
||||
todayHighlight: true
|
||||
});
|
||||
|
||||
@if ($client && !$invoice)
|
||||
$('input[name=client]').val({{ $client->public_id }});
|
||||
@endif
|
||||
|
||||
var $input = $('select#client');
|
||||
$input.combobox();
|
||||
$('.client_select input.form-control').on('change', function(e) {
|
||||
@ -513,6 +519,7 @@
|
||||
this.client = new ClientModel();
|
||||
self.discount = ko.observable('');
|
||||
self.frequency_id = ko.observable('');
|
||||
self.currency_id = ko.observable({{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }});
|
||||
self.terms = ko.observable('');
|
||||
self.po_number = ko.observable('');
|
||||
self.invoice_date = ko.observable('');
|
||||
@ -636,13 +643,13 @@
|
||||
|
||||
this.subtotal = ko.computed(function() {
|
||||
var total = self.rawSubtotal();
|
||||
return total > 0 ? formatMoney(total) : '';
|
||||
return total > 0 ? formatMoney(total, self.currency_id()) : '';
|
||||
});
|
||||
|
||||
|
||||
this.discounted = ko.computed(function() {
|
||||
var total = self.rawSubtotal() * (self.discount()/100);
|
||||
return formatMoney(total);
|
||||
return formatMoney(total, self.currency_id());
|
||||
});
|
||||
|
||||
this.total = ko.computed(function() {
|
||||
@ -653,7 +660,7 @@
|
||||
total = total * ((100 - discount)/100);
|
||||
}
|
||||
|
||||
return total > 0 ? formatMoney(total) : '';
|
||||
return total > 0 ? formatMoney(total, self.currency_id()) : '';
|
||||
});
|
||||
|
||||
self.onDragged = function(item) {
|
||||
@ -675,6 +682,7 @@
|
||||
self.country_id = ko.observable('');
|
||||
self.client_size_id = ko.observable('');
|
||||
self.client_industry_id = ko.observable('');
|
||||
self.currency_id = ko.observable('');
|
||||
self.website = ko.observable('');
|
||||
self.contacts = ko.observableArray();
|
||||
|
||||
@ -778,7 +786,7 @@
|
||||
this.qty = ko.observable();
|
||||
this.tax = ko.observable();
|
||||
this.actionsVisible = ko.observable(false);
|
||||
|
||||
|
||||
this.prettyQty = ko.computed({
|
||||
read: function () {
|
||||
return this.qty() ? parseFloat(this.qty()) : '';
|
||||
@ -791,6 +799,7 @@
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
if (this.cost()) this.cost(formatMoney(this.cost(), model.currency_id(), true));
|
||||
}
|
||||
|
||||
for (var i=0; i<model.tax_rates().length; i++) {
|
||||
@ -839,7 +848,7 @@
|
||||
|
||||
this.total = ko.computed(function() {
|
||||
var total = self.rawTotal();
|
||||
return total ? formatMoney(total) : '';
|
||||
return total ? formatMoney(total, model.currency_id()) : '';
|
||||
});
|
||||
|
||||
this.hideActions = function() {
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
{{ Former::select('client')->addOption('', '')->addGroupClass('client-select') }}
|
||||
{{ Former::select('invoice')->addOption('', '')->addGroupClass('invoice-select') }}
|
||||
{{ Former::select('currency_id')->addOption('','')->label('Currency')
|
||||
->fromQuery($currencies, 'name', 'id')->select(Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY)) }}
|
||||
{{ Former::text('amount') }}
|
||||
{{ Former::text('payment_date')->data_date_format(DEFAULT_DATE_PICKER_FORMAT) }}
|
||||
|
||||
@ -107,7 +109,7 @@
|
||||
var list = clientId ? (invoiceMap.hasOwnProperty(clientId) ? invoiceMap[clientId] : []) : invoices;
|
||||
for (var i=0; i<list.length; i++) {
|
||||
var invoice = list[i];
|
||||
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + invoice.invoice_date + ' - ' + invoice.client.name, invoice.public_id));
|
||||
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + invoice.client.name, invoice.public_id));
|
||||
}
|
||||
$('select#invoice').combobox('refresh');
|
||||
}).trigger('change');
|
||||
@ -122,6 +124,8 @@
|
||||
});
|
||||
$input.combobox();
|
||||
|
||||
$('#currency_id').combobox();
|
||||
|
||||
$('#payment_date').datepicker({
|
||||
autoclose: true,
|
||||
todayHighlight: true
|
||||
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
body > div.container {
|
||||
min-height: 600px;
|
||||
min-height: 600px;
|
||||
}
|
||||
*/
|
||||
|
||||
label.checkbox,
|
||||
label.control-label {
|
||||
font-weight: normal !important;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.panel {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
@ -23,57 +23,52 @@ div.panel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sides-padded {
|
||||
margin-left: 8px !important;
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
|
||||
/*
|
||||
.form-horizontal {
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
.form-group {
|
||||
width: 50%;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
*/
|
||||
|
||||
/* DataTables and BootStrap */
|
||||
.dataTables_wrapper {
|
||||
padding-top: 16px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
table.table thead > tr > th {
|
||||
border-bottom-width: 0px;
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
#DataTables_Table_0_length label {
|
||||
font-weight: normal;
|
||||
padding-bottom: 10px;
|
||||
font-weight: normal;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
div.dataTables_paginate.paging_bootstrap {
|
||||
margin-top: -30px;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
/*
|
||||
table.table tbody tr.odd {
|
||||
background-color: #f9f9f9;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.table tbody tr:hover {
|
||||
background-color: #f0f0f0 !important;
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* table sorting indicators */
|
||||
/*table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; }*/
|
||||
/*
|
||||
|
||||
table.table thead .sorting,
|
||||
table.table thead .sorting_asc,
|
||||
table.table thead .sorting_desc,
|
||||
@ -83,14 +78,13 @@ table.table thead .sorting_desc_disabled {
|
||||
*cursor: hand;
|
||||
}
|
||||
|
||||
table.table thead .sorting_asc { background: url('images/sort_asc.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc { background: url('images/sort_desc.png') no-repeat center right; }
|
||||
table.table thead .sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; }
|
||||
|
||||
|
||||
table.table thead .sorting_asc_disabled { background: url('images/sort_asc_disabled.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc_disabled { background: url('images/sort_desc_disabled.png') no-repeat center right; }
|
||||
*/
|
||||
|
||||
table.table thead .sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; }
|
||||
table.table thead .sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; }
|
||||
|
||||
|
||||
/* Hover nav */
|
||||
.sidebar-nav {
|
||||
@ -137,44 +131,44 @@ table.table thead .sorting_desc_disabled { background: url('images/sort_desc_dis
|
||||
|
||||
|
||||
.invoice-table {
|
||||
border-style: none !important;
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
table.invoice-table tbody tr:hover {
|
||||
background-color: #FFFFFF !important;
|
||||
background-color: #FFFFFF !important;
|
||||
}
|
||||
*/
|
||||
|
||||
.invoice-table td {
|
||||
padding: 2px !important;
|
||||
padding: 2px !important;
|
||||
}
|
||||
|
||||
.invoice-table td input,
|
||||
.invoice-table td textarea {
|
||||
border: none !important;
|
||||
width: 100%;
|
||||
border: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.invoice-table th {
|
||||
border-top: 1px solid #ddd !important;
|
||||
border-top: 1px solid #ddd !important;
|
||||
}
|
||||
|
||||
.invoice-table td.hide-border,
|
||||
.invoice-table th.hide-border {
|
||||
border-style: none !important;
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
.invoice-table td.td-icon {
|
||||
vertical-align: middle !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.fa-bars {
|
||||
cursor: move !important;
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.closer-row {
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
|
||||
@ -183,5 +177,5 @@ table.invoice-table tbody tr:hover {
|
||||
-webkit-transition: width 0.5s ease, margin 0.5s ease;
|
||||
-moz-transition: width 0.5s ease, margin 0.5s ease;
|
||||
-o-transition: width 0.5s ease, margin 0.5s ease;
|
||||
transition: width 0.5s ease, margin 0.5s ease;
|
||||
}
|
||||
transition: width 0.5s ease, margin 0.5s ease;
|
||||
}
|
||||
|
412
public/js/accounting.js
Executable file
412
public/js/accounting.js
Executable file
@ -0,0 +1,412 @@
|
||||
/*!
|
||||
* accounting.js v0.3.2
|
||||
* Copyright 2011, Joss Crowcroft
|
||||
*
|
||||
* Freely distributable under the MIT license.
|
||||
* Portions of accounting.js are inspired or borrowed from underscore.js
|
||||
*
|
||||
* Full details and documentation:
|
||||
* http://josscrowcroft.github.com/accounting.js/
|
||||
*/
|
||||
|
||||
(function(root, undefined) {
|
||||
|
||||
/* --- Setup --- */
|
||||
|
||||
// Create the local library object, to be exported or referenced globally later
|
||||
var lib = {};
|
||||
|
||||
// Current version
|
||||
lib.version = '0.3.2';
|
||||
|
||||
|
||||
/* --- Exposed settings --- */
|
||||
|
||||
// The library's settings configuration object. Contains default parameters for
|
||||
// currency and number formatting
|
||||
lib.settings = {
|
||||
currency: {
|
||||
symbol : "$", // default currency symbol is '$'
|
||||
format : "%s%v", // controls output: %s = symbol, %v = value (can be object, see docs)
|
||||
decimal : ".", // decimal point separator
|
||||
thousand : ",", // thousands separator
|
||||
precision : 2, // decimal places
|
||||
grouping : 3 // digit grouping (not implemented yet)
|
||||
},
|
||||
number: {
|
||||
precision : 0, // default precision on numbers is 0
|
||||
grouping : 3, // digit grouping (not implemented yet)
|
||||
thousand : ",",
|
||||
decimal : "."
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* --- Internal Helper Methods --- */
|
||||
|
||||
// Store reference to possibly-available ECMAScript 5 methods for later
|
||||
var nativeMap = Array.prototype.map,
|
||||
nativeIsArray = Array.isArray,
|
||||
toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Tests whether supplied parameter is a string
|
||||
* from underscore.js
|
||||
*/
|
||||
function isString(obj) {
|
||||
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether supplied parameter is a string
|
||||
* from underscore.js, delegates to ECMA5's native Array.isArray
|
||||
*/
|
||||
function isArray(obj) {
|
||||
return nativeIsArray ? nativeIsArray(obj) : toString.call(obj) === '[object Array]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether supplied parameter is a true object
|
||||
*/
|
||||
function isObject(obj) {
|
||||
return obj && toString.call(obj) === '[object Object]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends an object with a defaults object, similar to underscore's _.defaults
|
||||
*
|
||||
* Used for abstracting parameter handling from API methods
|
||||
*/
|
||||
function defaults(object, defs) {
|
||||
var key;
|
||||
object = object || {};
|
||||
defs = defs || {};
|
||||
// Iterate over object non-prototype properties:
|
||||
for (key in defs) {
|
||||
if (defs.hasOwnProperty(key)) {
|
||||
// Replace values with defaults only if undefined (allow empty/zero values):
|
||||
if (object[key] == null) object[key] = defs[key];
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of `Array.map()` for iteration loops
|
||||
*
|
||||
* Returns a new Array as a result of calling `iterator` on each array value.
|
||||
* Defers to native Array.map if available
|
||||
*/
|
||||
function map(obj, iterator, context) {
|
||||
var results = [], i, j;
|
||||
|
||||
if (!obj) return results;
|
||||
|
||||
// Use native .map method if it exists:
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
|
||||
// Fallback for native .map:
|
||||
for (i = 0, j = obj.length; i < j; i++ ) {
|
||||
results[i] = iterator.call(context, obj[i], i, obj);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and normalise the value of precision (must be positive integer)
|
||||
*/
|
||||
function checkPrecision(val, base) {
|
||||
val = Math.round(Math.abs(val));
|
||||
return isNaN(val)? base : val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a format string or object and returns format obj for use in rendering
|
||||
*
|
||||
* `format` is either a string with the default (positive) format, or object
|
||||
* containing `pos` (required), `neg` and `zero` values (or a function returning
|
||||
* either a string or object)
|
||||
*
|
||||
* Either string or format.pos must contain "%v" (value) to be valid
|
||||
*/
|
||||
function checkCurrencyFormat(format) {
|
||||
var defaults = lib.settings.currency.format;
|
||||
|
||||
// Allow function as format parameter (should return string or object):
|
||||
if ( typeof format === "function" ) format = format();
|
||||
|
||||
// Format can be a string, in which case `value` ("%v") must be present:
|
||||
if ( isString( format ) && format.match("%v") ) {
|
||||
|
||||
// Create and return positive, negative and zero formats:
|
||||
return {
|
||||
pos : format,
|
||||
neg : format.replace("-", "").replace("%v", "-%v"),
|
||||
zero : format
|
||||
};
|
||||
|
||||
// If no format, or object is missing valid positive value, use defaults:
|
||||
} else if ( !format || !format.pos || !format.pos.match("%v") ) {
|
||||
|
||||
// If defaults is a string, casts it to an object for faster checking next time:
|
||||
return ( !isString( defaults ) ) ? defaults : lib.settings.currency.format = {
|
||||
pos : defaults,
|
||||
neg : defaults.replace("%v", "-%v"),
|
||||
zero : defaults
|
||||
};
|
||||
|
||||
}
|
||||
// Otherwise, assume format was fine:
|
||||
return format;
|
||||
}
|
||||
|
||||
|
||||
/* --- API Methods --- */
|
||||
|
||||
/**
|
||||
* Takes a string/array of strings, removes all formatting/cruft and returns the raw float value
|
||||
* alias: accounting.`parse(string)`
|
||||
*
|
||||
* Decimal must be included in the regular expression to match floats (defaults to
|
||||
* accounting.settings.number.decimal), so if the number uses a non-standard decimal
|
||||
* separator, provide it as the second argument.
|
||||
*
|
||||
* Also matches bracketed negatives (eg. "$ (1.99)" => -1.99)
|
||||
*
|
||||
* Doesn't throw any errors (`NaN`s become 0) but this may change in future
|
||||
*/
|
||||
var unformat = lib.unformat = lib.parse = function(value, decimal) {
|
||||
// Recursively unformat arrays:
|
||||
if (isArray(value)) {
|
||||
return map(value, function(val) {
|
||||
return unformat(val, decimal);
|
||||
});
|
||||
}
|
||||
|
||||
// Fails silently (need decent errors):
|
||||
value = value || 0;
|
||||
|
||||
// Return the value as-is if it's already a number:
|
||||
if (typeof value === "number") return value;
|
||||
|
||||
// Default decimal point comes from settings, but could be set to eg. "," in opts:
|
||||
decimal = decimal || lib.settings.number.decimal;
|
||||
|
||||
// Build regex to strip out everything except digits, decimal point and minus sign:
|
||||
var regex = new RegExp("[^0-9-" + decimal + "]", ["g"]),
|
||||
unformatted = parseFloat(
|
||||
("" + value)
|
||||
.replace(/\((.*)\)/, "-$1") // replace bracketed values with negatives
|
||||
.replace(regex, '') // strip out any cruft
|
||||
.replace(decimal, '.') // make sure decimal point is standard
|
||||
);
|
||||
|
||||
// This will fail silently which may cause trouble, let's wait and see:
|
||||
return !isNaN(unformatted) ? unformatted : 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of toFixed() that treats floats more like decimals
|
||||
*
|
||||
* Fixes binary rounding issues (eg. (0.615).toFixed(2) === "0.61") that present
|
||||
* problems for accounting- and finance-related software.
|
||||
*/
|
||||
var toFixed = lib.toFixed = function(value, precision) {
|
||||
precision = checkPrecision(precision, lib.settings.number.precision);
|
||||
var power = Math.pow(10, precision);
|
||||
|
||||
// Multiply up by precision, round accurately, then divide and use native toFixed():
|
||||
return (Math.round(lib.unformat(value) * power) / power).toFixed(precision);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Format a number, with comma-separated thousands and custom precision/decimal places
|
||||
*
|
||||
* Localise by overriding the precision and thousand / decimal separators
|
||||
* 2nd parameter `precision` can be an object matching `settings.number`
|
||||
*/
|
||||
var formatNumber = lib.formatNumber = function(number, precision, thousand, decimal) {
|
||||
// Resursively format arrays:
|
||||
if (isArray(number)) {
|
||||
return map(number, function(val) {
|
||||
return formatNumber(val, precision, thousand, decimal);
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up number:
|
||||
number = unformat(number);
|
||||
|
||||
// Build options object from second param (if object) or all params, extending defaults:
|
||||
var opts = defaults(
|
||||
(isObject(precision) ? precision : {
|
||||
precision : precision,
|
||||
thousand : thousand,
|
||||
decimal : decimal
|
||||
}),
|
||||
lib.settings.number
|
||||
),
|
||||
|
||||
// Clean up precision
|
||||
usePrecision = checkPrecision(opts.precision),
|
||||
|
||||
// Do some calc:
|
||||
negative = number < 0 ? "-" : "",
|
||||
base = parseInt(toFixed(Math.abs(number || 0), usePrecision), 10) + "",
|
||||
mod = base.length > 3 ? base.length % 3 : 0;
|
||||
|
||||
// Format the number:
|
||||
return negative + (mod ? base.substr(0, mod) + opts.thousand : "") + base.substr(mod).replace(/(\d{3})(?=\d)/g, "$1" + opts.thousand) + (usePrecision ? opts.decimal + toFixed(Math.abs(number), usePrecision).split('.')[1] : "");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Format a number into currency
|
||||
*
|
||||
* Usage: accounting.formatMoney(number, symbol, precision, thousandsSep, decimalSep, format)
|
||||
* defaults: (0, "$", 2, ",", ".", "%s%v")
|
||||
*
|
||||
* Localise by overriding the symbol, precision, thousand / decimal separators and format
|
||||
* Second param can be an object matching `settings.currency` which is the easiest way.
|
||||
*
|
||||
* To do: tidy up the parameters
|
||||
*/
|
||||
var formatMoney = lib.formatMoney = function(number, symbol, precision, thousand, decimal, format) {
|
||||
// Resursively format arrays:
|
||||
if (isArray(number)) {
|
||||
return map(number, function(val){
|
||||
return formatMoney(val, symbol, precision, thousand, decimal, format);
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up number:
|
||||
number = unformat(number);
|
||||
|
||||
// Build options object from second param (if object) or all params, extending defaults:
|
||||
var opts = defaults(
|
||||
(isObject(symbol) ? symbol : {
|
||||
symbol : symbol,
|
||||
precision : precision,
|
||||
thousand : thousand,
|
||||
decimal : decimal,
|
||||
format : format
|
||||
}),
|
||||
lib.settings.currency
|
||||
),
|
||||
|
||||
// Check format (returns object with pos, neg and zero):
|
||||
formats = checkCurrencyFormat(opts.format),
|
||||
|
||||
// Choose which format to use for this value:
|
||||
useFormat = number > 0 ? formats.pos : number < 0 ? formats.neg : formats.zero;
|
||||
|
||||
// Return with currency symbol added:
|
||||
return useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(number), checkPrecision(opts.precision), opts.thousand, opts.decimal));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Format a list of numbers into an accounting column, padding with whitespace
|
||||
* to line up currency symbols, thousand separators and decimals places
|
||||
*
|
||||
* List should be an array of numbers
|
||||
* Second parameter can be an object containing keys that match the params
|
||||
*
|
||||
* Returns array of accouting-formatted number strings of same length
|
||||
*
|
||||
* NB: `white-space:pre` CSS rule is required on the list container to prevent
|
||||
* browsers from collapsing the whitespace in the output strings.
|
||||
*/
|
||||
lib.formatColumn = function(list, symbol, precision, thousand, decimal, format) {
|
||||
if (!list) return [];
|
||||
|
||||
// Build options object from second param (if object) or all params, extending defaults:
|
||||
var opts = defaults(
|
||||
(isObject(symbol) ? symbol : {
|
||||
symbol : symbol,
|
||||
precision : precision,
|
||||
thousand : thousand,
|
||||
decimal : decimal,
|
||||
format : format
|
||||
}),
|
||||
lib.settings.currency
|
||||
),
|
||||
|
||||
// Check format (returns object with pos, neg and zero), only need pos for now:
|
||||
formats = checkCurrencyFormat(opts.format),
|
||||
|
||||
// Whether to pad at start of string or after currency symbol:
|
||||
padAfterSymbol = formats.pos.indexOf("%s") < formats.pos.indexOf("%v") ? true : false,
|
||||
|
||||
// Store value for the length of the longest string in the column:
|
||||
maxLength = 0,
|
||||
|
||||
// Format the list according to options, store the length of the longest string:
|
||||
formatted = map(list, function(val, i) {
|
||||
if (isArray(val)) {
|
||||
// Recursively format columns if list is a multi-dimensional array:
|
||||
return lib.formatColumn(val, opts);
|
||||
} else {
|
||||
// Clean up the value
|
||||
val = unformat(val);
|
||||
|
||||
// Choose which format to use for this value (pos, neg or zero):
|
||||
var useFormat = val > 0 ? formats.pos : val < 0 ? formats.neg : formats.zero,
|
||||
|
||||
// Format this value, push into formatted list and save the length:
|
||||
fVal = useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(val), checkPrecision(opts.precision), opts.thousand, opts.decimal));
|
||||
|
||||
if (fVal.length > maxLength) maxLength = fVal.length;
|
||||
return fVal;
|
||||
}
|
||||
});
|
||||
|
||||
// Pad each number in the list and send back the column of numbers:
|
||||
return map(formatted, function(val, i) {
|
||||
// Only if this is a string (not a nested array, which would have already been padded):
|
||||
if (isString(val) && val.length < maxLength) {
|
||||
// Depending on symbol position, pad after symbol or at index 0:
|
||||
return padAfterSymbol ? val.replace(opts.symbol, opts.symbol+(new Array(maxLength - val.length + 1).join(" "))) : (new Array(maxLength - val.length + 1).join(" ")) + val;
|
||||
}
|
||||
return val;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* --- Module Definition --- */
|
||||
|
||||
// Export accounting for CommonJS. If being loaded as an AMD module, define it as such.
|
||||
// Otherwise, just add `accounting` to the global object
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = lib;
|
||||
}
|
||||
exports.accounting = lib;
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
// Return the library as an AMD module:
|
||||
define([], function() {
|
||||
return lib;
|
||||
});
|
||||
} else {
|
||||
// Use accounting.noConflict to restore `accounting` back to its original value.
|
||||
// Returns a reference to the library's `accounting` object;
|
||||
// e.g. `var numbers = accounting.noConflict();`
|
||||
lib.noConflict = (function(oldAccounting) {
|
||||
return function() {
|
||||
// Reset the value of the root's `accounting` variable:
|
||||
root.accounting = oldAccounting;
|
||||
// Delete the noConflict method:
|
||||
lib.noConflict = undefined;
|
||||
// Return reference to the library to re-assign it:
|
||||
return lib;
|
||||
};
|
||||
})(root.accounting);
|
||||
|
||||
// Declare `fx` on the root (global/window) object:
|
||||
root['accounting'] = lib;
|
||||
}
|
||||
|
||||
// Root will be `window` in browser or `global` on the server:
|
||||
}(this));
|
@ -1,4 +1,5 @@
|
||||
function generatePDF(invoice) {
|
||||
var currencyId = invoice.currency_id;
|
||||
var invoiceNumber = invoice.invoice_number;
|
||||
var issuedOn = invoice.invoice_date ? invoice.invoice_date : '';
|
||||
var amount = '$0.00';
|
||||
@ -128,7 +129,7 @@ function generatePDF(invoice) {
|
||||
|
||||
for (var i=0; i<invoice.invoice_items.length; i++) {
|
||||
var item = invoice.invoice_items[i];
|
||||
var cost = formatNumber(item.cost);
|
||||
var cost = formatMoney(item.cost, currencyId, true);
|
||||
var qty = item.qty ? parseFloat(item.qty) + '' : '';
|
||||
var notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
@ -151,7 +152,7 @@ function generatePDF(invoice) {
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
}
|
||||
lineTotal = formatNumber(lineTotal);
|
||||
lineTotal = formatMoney(lineTotal, currencyId, true);
|
||||
|
||||
var costX = unitCostRight - (doc.getStringUnitWidth(cost) * doc.internal.getFontSize());
|
||||
var qtyX = qtyRight - (doc.getStringUnitWidth(qty) * doc.internal.getFontSize());
|
||||
@ -181,7 +182,7 @@ function generatePDF(invoice) {
|
||||
|
||||
x += 16;
|
||||
doc.text(footerLeft, x, 'Subtotal');
|
||||
var total = formatNumber(total);
|
||||
var total = formatMoney(total, currencyId, true);
|
||||
var totalX = headerRight - (doc.getStringUnitWidth(total) * doc.internal.getFontSize());
|
||||
doc.text(totalX, x, total);
|
||||
|
||||
@ -189,7 +190,7 @@ function generatePDF(invoice) {
|
||||
|
||||
x += 16;
|
||||
doc.text(footerLeft, x, 'Discount');
|
||||
var discount = formatNumber(total * (invoice.discount/100));
|
||||
var discount = formatMoney(total * (invoice.discount/100), currencyId, true);
|
||||
total -= discount;
|
||||
var discountX = headerRight - (doc.getStringUnitWidth(discount) * doc.internal.getFontSize());
|
||||
doc.text(discountX, x, discount);
|
||||
@ -197,7 +198,7 @@ function generatePDF(invoice) {
|
||||
|
||||
x += 16;
|
||||
doc.text(footerLeft, x, 'Paid to Date');
|
||||
var paid = formatNumber(0);
|
||||
var paid = formatMoney(0, currencyId, true);
|
||||
var paidX = headerRight - (doc.getStringUnitWidth(paid) * doc.internal.getFontSize());
|
||||
doc.text(paidX, x, paid);
|
||||
|
||||
@ -206,7 +207,7 @@ function generatePDF(invoice) {
|
||||
doc.setFontType("bold");
|
||||
doc.text(footerLeft, x, 'Total');
|
||||
|
||||
var total = formatMoney(total);
|
||||
var total = formatMoney(total, currencyId);
|
||||
var totalX = headerRight - (doc.getStringUnitWidth(total) * doc.internal.getFontSize());
|
||||
doc.text(totalX, x, total);
|
||||
|
||||
@ -345,26 +346,6 @@ function getQuarter(offset) {
|
||||
return 'Q' + quarter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function formatMoney(num) {
|
||||
num = parseFloat(num);
|
||||
if (!num) return '$0.00';
|
||||
return '$' + formatNumber(num);
|
||||
}
|
||||
|
||||
|
||||
function formatNumber(num) {
|
||||
num = parseFloat(num);
|
||||
if (!num) num = 0;
|
||||
var p = num.toFixed(2).split(".");
|
||||
return p[0].split("").reverse().reduce(function(acc, num, i, orig) {
|
||||
return num + (i && !(i % 3) ? "," : "") + acc;
|
||||
}, "") + "." + p[1];
|
||||
}
|
||||
|
||||
|
||||
/* Set the defaults for DataTables initialisation */
|
||||
$.extend( true, $.fn.dataTable.defaults, {
|
||||
"sDom": "t<'row-fluid'<'span6'i><'span6'p>>",
|
||||
|
Loading…
Reference in New Issue
Block a user