Added custom invoice fields

This commit is contained in:
Hillel Coren 2014-07-20 13:38:42 +03:00
parent 4620a06a9b
commit 4b177e160d
16 changed files with 391 additions and 79 deletions

View File

@ -4,6 +4,6 @@ return array(
//'TAG_MANAGER_KEY' => '',
//'ANALYTICS_KEY' => '',
'NINJA_PROD' => true,
//'NINJA_PROD' => true,
);

View File

@ -273,12 +273,16 @@ class AccountController extends \BaseController {
if (Auth::user()->account->isPro())
{
$account = Auth::user()->account;
$account->custom_label1 = Input::get('custom_label1');
$account->custom_value1 = Input::get('custom_value1');
$account->custom_label2 = Input::get('custom_label2');
$account->custom_value2 = Input::get('custom_value2');
$account->custom_client_label1 = Input::get('custom_client_label1');
$account->custom_client_label2 = Input::get('custom_client_label2');
$account->custom_label1 = trim(Input::get('custom_label1'));
$account->custom_value1 = trim(Input::get('custom_value1'));
$account->custom_label2 = trim(Input::get('custom_label2'));
$account->custom_value2 = trim(Input::get('custom_value2'));
$account->custom_client_label1 = trim(Input::get('custom_client_label1'));
$account->custom_client_label2 = trim(Input::get('custom_client_label2'));
$account->custom_invoice_label1 = trim(Input::get('custom_invoice_label1'));
$account->custom_invoice_label2 = trim(Input::get('custom_invoice_label2'));
$account->custom_invoice_taxes1 = Input::get('custom_invoice_taxes1') ? true : false;
$account->custom_invoice_taxes2 = Input::get('custom_invoice_taxes2') ? true : false;
$account->save();
Session::flash('message', trans('texts.updated_settings'));
@ -292,6 +296,8 @@ class AccountController extends \BaseController {
if (Auth::user()->account->isPro())
{
$account = Auth::user()->account;
$account->hide_quantity = Input::get('hide_quantity') ? true : false;
$account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false;
$account->primary_color = Input::get('primary_color');// ? Input::get('primary_color') : null;
$account->secondary_color = Input::get('secondary_color');// ? Input::get('secondary_color') : null;
$account->save();

View File

@ -244,8 +244,8 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('country_id')->nullable();
$t->string('work_phone');
$t->text('private_notes');
$t->decimal('balance', 13, 4);
$t->decimal('paid_to_date', 13, 4);
$t->decimal('balance', 13, 2);
$t->decimal('paid_to_date', 13, 2);
$t->timestamp('last_login')->nullable();
$t->string('website');
$t->unsignedInteger('industry_id')->nullable();
@ -326,10 +326,10 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('recurring_invoice_id')->index()->nullable();
$t->string('tax_name');
$t->decimal('tax_rate', 13, 4);
$t->decimal('tax_rate', 13, 2);
$t->decimal('amount', 13, 4);
$t->decimal('balance', 13, 4);
$t->decimal('amount', 13, 2);
$t->decimal('balance', 13, 2);
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
@ -375,7 +375,7 @@ class ConfideSetupUsersTable extends Migration {
$t->softDeletes();
$t->string('name');
$t->decimal('rate', 13, 4);
$t->decimal('rate', 13, 2);
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');;
@ -394,8 +394,8 @@ class ConfideSetupUsersTable extends Migration {
$t->string('product_key');
$t->text('notes');
$t->decimal('cost', 13, 4);
$t->decimal('qty', 13, 4);
$t->decimal('cost', 13, 2);
$t->decimal('qty', 13, 2);
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');;
@ -417,11 +417,11 @@ class ConfideSetupUsersTable extends Migration {
$t->string('product_key');
$t->text('notes');
$t->decimal('cost', 13, 4);
$t->decimal('qty', 13, 4);
$t->decimal('cost', 13, 2);
$t->decimal('qty', 13, 2);
$t->string('tax_name');
$t->decimal('tax_rate', 13, 4);
$t->decimal('tax_rate', 13, 2);
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
$t->foreign('product_id')->references('id')->on('products');
@ -446,7 +446,7 @@ class ConfideSetupUsersTable extends Migration {
$t->softDeletes();
$t->boolean('is_deleted');
$t->decimal('amount', 13, 4);
$t->decimal('amount', 13, 2);
$t->date('payment_date');
$t->string('transaction_reference');
$t->string('payer_id');
@ -473,8 +473,8 @@ class ConfideSetupUsersTable extends Migration {
$t->softDeletes();
$t->boolean('is_deleted');
$t->decimal('amount', 13, 4);
$t->decimal('balance', 13, 4);
$t->decimal('amount', 13, 2);
$t->decimal('balance', 13, 2);
$t->date('credit_date')->nullable();
$t->string('credit_number');
$t->text('private_notes');
@ -504,8 +504,8 @@ class ConfideSetupUsersTable extends Migration {
$t->text('message');
$t->text('json_backup');
$t->integer('activity_type_id');
$t->decimal('adjustment', 13, 4);
$t->decimal('balance', 13, 4);
$t->decimal('adjustment', 13, 2);
$t->decimal('balance', 13, 2);
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');

View File

@ -0,0 +1,66 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class SupportHidingQuantity extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function($table)
{
$table->boolean('hide_quantity');
$table->boolean('hide_paid_to_date');
$table->string('custom_invoice_label1');
$table->string('custom_invoice_label2');
$table->boolean('custom_invoice_taxes1');
$table->boolean('custom_invoice_taxes2');
});
Schema::table('invoices', function($table)
{
$table->decimal('custom_value1', 13, 2);
$table->decimal('custom_value2', 13, 2);
$table->boolean('custom_taxes1');
$table->boolean('custom_taxes2');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function($table)
{
$table->dropColumn('hide_quantity');
$table->dropColumn('hide_paid_to_date');
$table->dropColumn('custom_invoice_label1');
$table->dropColumn('custom_invoice_label2');
$table->dropColumn('custom_invoice_taxes1');
$table->dropColumn('custom_invoice_taxes2');
});
Schema::table('invoices', function($table)
{
$table->dropColumn('custom_value1');
$table->dropColumn('custom_value2');
$table->dropColumn('custom_taxes1');
$table->dropColumn('custom_taxes2');
});
}
}

View File

@ -69,13 +69,17 @@ App::after(function($request, $response)
Route::filter('auth', function()
{
if (Auth::guest()) {
if(Utils::isNinja()) {
return Redirect::guest('/');
} else {
return Redirect::guest('/login');
}
if (Auth::guest())
{
if (Utils::isNinja() || Account::count() == 0)
{
return Redirect::guest('/');
}
else
{
return Redirect::guest('/login');
}
}
});

View File

@ -45,7 +45,7 @@ return array(
'quantity' => 'Menge',
'line_total' => 'Summe',
'subtotal' => 'Zwischensumme',
'paid_to_date' => 'Zahlungsdatum',
'paid_to_date' => 'Bereits gezahlt',
'balance_due' => 'Rechnungsbetrag',
'invoice_design_id' => 'Design',
'terms' => 'Bedingungen',

View File

@ -397,6 +397,14 @@ return array(
'session_expired' => 'Your session has expired.',
'invoice_fields' => 'Invoice Fields',
'invoice_options' => 'Invoice Options',
'hide_quantity' => 'Hide quantity',
'hide_quantity_help' => 'All line items will have a quantity of one',
'hide_paid_to_date' => 'Hide paid to date',
'hide_paid_to_date_help' => 'Hide until a payment is made',
'charge_taxes' => 'Charge taxes',
);

View File

@ -82,7 +82,11 @@ class Invoice extends EntityModel
'account',
'invoice_design_id',
'is_pro',
'is_quote']);
'is_quote',
'custom_value1',
'custom_value2',
'custom_taxes1',
'custom_taxes2']);
$this->client->setVisible([
'name',
@ -117,7 +121,9 @@ class Invoice extends EntityModel
'custom_client_label1',
'custom_client_label2',
'primary_color',
'secondary_color']);
'secondary_color',
'hide_quantity',
'hide_paid_to_date']);
foreach ($this->invoice_items as $invoiceItem)
{

View File

@ -237,7 +237,7 @@ class InvoiceRepository
foreach ($data['invoice_items'] as $item)
{
if (!$item->cost && !$item->qty && !$item->product_key && !$item->notes)
if (!$item->cost && !$item->product_key && !$item->notes)
{
continue;
}
@ -261,9 +261,30 @@ class InvoiceRepository
$total *= (100 - $invoice->discount) / 100;
}
$invoice->custom_value1 = $data['custom_value1'];
$invoice->custom_value2 = $data['custom_value2'];
$invoice->custom_taxes1 = $data['custom_taxes1'] ? true : false;
$invoice->custom_taxes2 = $data['custom_taxes2'] ? true : false;
// custom fields charged taxes
if ($invoice->custom_value1 && $invoice->custom_taxes1) {
$total += $invoice->custom_value1;
}
if ($invoice->custom_value2 && $invoice->custom_taxes2) {
$total += $invoice->custom_value2;
}
$total += $total * $invoice->tax_rate / 100;
$total = round($total, 2);
// custom fields not charged taxes
if ($invoice->custom_value1 && !$invoice->custom_taxes1) {
$total += $invoice->custom_value1;
}
if ($invoice->custom_value2 && !$invoice->custom_taxes2) {
$total += $invoice->custom_value2;
}
if ($publicId)
{
$invoice->balance = $total - ($invoice->amount - $invoice->balance);
@ -280,7 +301,7 @@ class InvoiceRepository
foreach ($data['invoice_items'] as $item)
{
if (!$item->cost && !$item->qty && !$item->product_key && !$item->notes)
if (!$item->cost && !$item->product_key && !$item->notes)
{
continue;
}

View File

@ -6,6 +6,20 @@
{{ Former::open()->addClass('col-md-8 col-md-offset-2 warn-on-exit') }}
{{ Former::populate($account) }}
{{ Former::populateField('custom_invoice_taxes1', intval($account->custom_invoice_taxes1)) }}
{{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }}
{{ Former::legend('invoice_fields') }}
{{ Former::text('custom_invoice_label1')->label(trans('texts.field_label'))
->append(Former::checkbox('custom_invoice_taxes1')->raw() . ' ' . trans('texts.charge_taxes')) }}
{{ Former::text('custom_invoice_label2')->label(trans('texts.field_label'))
->append(Former::checkbox('custom_invoice_taxes2')->raw() . ' ' . trans('texts.charge_taxes')) }}
<p>&nbsp;</p>
{{ Former::legend('client_fields') }}
{{ Former::text('custom_client_label1')->label(trans('texts.field_label')) }}
{{ Former::text('custom_client_label2')->label(trans('texts.field_label')) }}
<p>&nbsp;</p>
{{ Former::legend('company_fields') }}
{{ Former::text('custom_label1')->label(trans('texts.field_label')) }}
@ -14,10 +28,6 @@
{{ Former::text('custom_label2')->label(trans('texts.field_label')) }}
{{ Former::text('custom_value2')->label(trans('texts.field_value')) }}
{{ Former::legend('client_fields') }}
{{ Former::text('custom_client_label1')->label(trans('texts.field_label')) }}
{{ Former::text('custom_client_label2')->label(trans('texts.field_label')) }}
@if (Auth::user()->isPro())
{{ Former::actions( Button::lg_success_submit(trans('texts.save'))->append_with_icon('floppy-disk') ) }}
@else

View File

@ -6,6 +6,13 @@
{{ Former::open()->addClass('col-md-8 col-md-offset-2 warn-on-exit') }}
{{ Former::populate($account) }}
{{ Former::populateField('hide_quantity', intval($account->hide_quantity)) }}
{{ Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) }}
{{ Former::legend('invoice_options') }}
{{ Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) }}
{{ Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) }}
<p>&nbsp;</p>
{{ Former::legend('invoice_design') }}
{{ Former::text('primary_color') }}

View File

@ -119,7 +119,7 @@
<th style="min-width:160px">{{ trans('texts.item') }}</th>
<th style="width:100%">{{ trans('texts.description') }}</th>
<th style="min-width:120px">{{ trans('texts.unit_cost') }}</th>
<th style="min-width:120px">{{ trans('texts.quantity') }}</th>
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}">{{ trans('texts.quantity') }}</th>
<th style="min-width:120px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">{{ trans('texts.tax') }}</th>
<th style="min-width:120px;">{{ trans('texts.line_total') }}</th>
<th style="min-width:32px;" class="hide-border"></th>
@ -140,7 +140,7 @@
<td>
<input onkeyup="onItemChange()" data-bind="value: prettyCost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control"//>
</td>
<td>
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
<input onkeyup="onItemChange()" data-bind="value: prettyQty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control"//>
</td>
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
@ -154,10 +154,12 @@
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="hide-border"/>
<td colspan="2" rowspan="5">
<td colspan="2" rowspan="6" style="vertical-align:top">
<br/>
{{ Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'")
->label(false)->placeholder(trans('texts.note_to_client'))->style('resize: none') }}
@ -169,35 +171,82 @@
</label>
</td>
<td style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.subtotal') }}</td>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ trans('texts.subtotal') }}</td>
<td style="text-align: right"><span data-bind="text: totals.subtotal"/></td>
</tr>
<tr style="display:none" data-bind="visible: discount() > 0">
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.discount') }}</td>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ trans('texts.discount') }}</td>
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
</tr>
@if (($account->custom_invoice_label1 || ($invoice && floatval($invoice->custom_value1)) != 0) && $account->custom_invoice_taxes1)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if (($account->custom_invoice_label2 || ($invoice && floatval($invoice->custom_value2)) != 0) && $account->custom_invoice_taxes2)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td>{{ trans('texts.tax') }}</td>
@if (!$account->hide_quantity)
<td>{{ trans('texts.tax') }}</td>
@endif
<td style="min-width:120px"><select class="form-control" style="width:100%" data-bind="value: tax, options: $root.tax_rates, optionsText: 'displayName'"></select></td>
<td style="text-align: right"><span data-bind="text: totals.taxAmount"/></td>
</tr>
@if (($account->custom_invoice_label1 || ($invoice && floatval($invoice->custom_value1)) != 0) && !$account->custom_invoice_taxes1)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if (($account->custom_invoice_label2 || ($invoice && floatval($invoice->custom_value2)) != 0) && !$account->custom_invoice_taxes2)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr>
@endif
@if (!$account->hide_paid_to_date)
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ trans('texts.paid_to_date') }}</td>
<td style="text-align: right" data-bind="text: totals.paidToDate"></td>
</tr>
@endif
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2">{{ trans('texts.paid_to_date') }}</td>
<td style="text-align: right" data-bind="text: totals.paidToDate"></td>
</tr>
<tr>
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="2"><b>{{ trans($entityType == ENTITY_INVOICE ? 'texts.balance_due' : 'texts.total') }}</b></td>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}"><b>{{ trans($entityType == ENTITY_INVOICE ? 'texts.balance_due' : 'texts.total') }}</b></td>
<td style="text-align: right"><span data-bind="text: totals.total"/></td>
</tr>
</tfoot>
</table>
</div>
@ -1017,6 +1066,11 @@
self.balance = ko.observable(0);
self.invoice_design_id = ko.observable({{ $account->invoice_design_id }});
self.custom_value1 = ko.observable(0);
self.custom_value2 = ko.observable(0);
self.custom_taxes1 = ko.observable(false);
self.custom_taxes2 = ko.observable(false);
self.mapping = {
'client': {
create: function(options) {
@ -1037,6 +1091,9 @@
self.addItem = function() {
var itemModel = new ItemModel();
@if ($account->hide_quantity)
itemModel.qty(1);
@endif
self.invoice_items.push(itemModel);
applyComboboxListeners();
}
@ -1044,6 +1101,8 @@
if (data) {
ko.mapping.fromJS(data, self.mapping, self);
self.is_recurring(parseInt(data.is_recurring));
self.is_recurring(parseInt(data.is_recurring) == 1);
self.is_recurring(parseInt(data.is_recurring) == 1);
} else {
self.addItem();
}
@ -1125,12 +1184,24 @@
});
self.totals.taxAmount = ko.computed(function() {
var total = self.totals.rawSubtotal();
var total = self.totals.rawSubtotal();
var discount = parseFloat(self.discount());
if (discount > 0) {
total = roundToTwo(total * ((100 - discount)/100));
}
var discount = parseFloat(self.discount());
if (discount > 0) {
total = roundToTwo(total * ((100 - discount)/100));
}
var customValue1 = roundToTwo(self.custom_value1());
var customValue2 = roundToTwo(self.custom_value2());
var customTaxes1 = self.custom_taxes1() == 1;
var customTaxes2 = self.custom_taxes2() == 1;
if (customValue1 && customTaxes1) {
total = NINJA.parseFloat(total) + customValue1;
}
if (customValue2 && customTaxes2) {
total = NINJA.parseFloat(total) + customValue2;
}
var taxRate = parseFloat(self.tax_rate());
if (taxRate > 0) {
@ -1158,11 +1229,30 @@
total = roundToTwo(total * ((100 - discount)/100));
}
var customValue1 = roundToTwo(self.custom_value1());
var customValue2 = roundToTwo(self.custom_value2());
var customTaxes1 = self.custom_taxes1() == 1;
var customTaxes2 = self.custom_taxes2() == 1;
if (customValue1 && customTaxes1) {
total = NINJA.parseFloat(total) + customValue1;
}
if (customValue2 && customTaxes2) {
total = NINJA.parseFloat(total) + customValue2;
}
var taxRate = parseFloat(self.tax_rate());
if (taxRate > 0) {
total = NINJA.parseFloat(total) + roundToTwo((total * (taxRate/100)));
}
if (customValue1 && !customTaxes1) {
total = NINJA.parseFloat(total) + customValue1;
}
if (customValue2 && !customTaxes2) {
total = NINJA.parseFloat(total) + customValue2;
}
var paid = self.totals.rawPaidToDate();
if (paid > 0) {
total -= paid;
@ -1428,7 +1518,7 @@
}
this.isEmpty = function() {
return !self.product_key() && !self.notes() && !self.cost() && !self.qty();
return !self.product_key() && !self.notes() && !self.cost() && (!self.qty() || {{ $account->hide_quantity ? 'true' : 'false' }});
}
this.onSelect = function(){
@ -1508,6 +1598,9 @@
}
model.invoice().addItem();
//model.addTaxRate();
@else
model.invoice().custom_taxes1({{ $account->custom_invoice_taxes1 ? 'true' : 'false' }});
model.invoice().custom_taxes2({{ $account->custom_invoice_taxes2 ? 'true' : 'false' }});
@endif
// Add the first tax rate for new invoices
//if(model.invoice_taxes() && model.tax_rates().length > 0) {
@ -1524,7 +1617,10 @@
}
onTaxRateChange();
// display blank instead of '0'
if (!model.invoice().discount()) model.invoice().discount('');
if (!model.invoice().custom_value1()) model.invoice().custom_value1('');
if (!model.invoice().custom_value2()) model.invoice().custom_value2('');
ko.applyBindings(model);
onItemChange();

View File

@ -2269,6 +2269,12 @@ border-bottom-width: 1px;
.dashboard .table-striped>tbody>tr>td:first-child { padding-left: 15px; }
.dashboard .table-striped>thead>tr>th:first-child { padding-left: 15px; }
.invoice-table tfoot input {
text-align: right;
}
/***********************************************
New/edit invoice page
************************************************/

View File

@ -47256,14 +47256,32 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
return;
}
//var taxTitle = 'Tax ' + getInvoiceTaxRate(invoice) + '%';
var data = [
{'subtotal': formatMoney(invoice.subtotal_amount, invoice.client.currency_id)},
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false},
{'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false},
{'paid_to_date': formatMoney(invoice.amount - invoice.balance, invoice.client.currency_id)}
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false}
];
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
}
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
}
data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false});
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
}
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
}
var paid = invoice.amount - invoice.balance;
if (invoice.account.hide_paid_to_date === '0' || paid) {
data.push({'paid_to_date': formatMoney(paid, invoice.client.currency_id)});
}
return displayGrid(doc, invoice, data, 300, y, layout, true, 550, rightAlignTitleX) + 10;
}
@ -47410,11 +47428,18 @@ function calculateAmounts(invoice) {
invoice.subtotal_amount = total;
if (invoice.discount > 0) {
var discount = roundToTwo(total * (invoice.discount/100));
total -= discount;
}
// custom fields with taxes
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) {
total += roundToTwo(invoice.custom_value1);
}
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) {
total += roundToTwo(invoice.custom_value2);
}
var tax = 0;
if (invoice.tax && parseFloat(invoice.tax.rate)) {
tax = parseFloat(invoice.tax.rate);
@ -47427,9 +47452,17 @@ function calculateAmounts(invoice) {
total = parseFloat(total) + parseFloat(tax);
}
// custom fields w/o with taxes
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) {
total += roundToTwo(invoice.custom_value1);
}
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) {
total += roundToTwo(invoice.custom_value2);
}
invoice.balance_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance));
invoice.tax_amount = tax;
invoice.discount_amount = discount;
invoice.tax_amount = tax;
invoice.has_taxes = hasTaxes;
return invoice;
@ -47455,7 +47488,9 @@ function displayInvoiceHeader(doc, invoice, layout) {
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item);
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description);
doc.text(costX, layout.tableTop, invoiceLabels.unit_cost);
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity);
if (invoice.account.hide_quantity === '0') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity);
}
doc.text(totalX, layout.tableTop, invoiceLabels.line_total);
if (invoice.has_taxes)
@ -47472,6 +47507,7 @@ function displayInvoiceItems(doc, invoice, layout) {
var shownItem = false;
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
var tableTop = layout.tableTop;
var hideQuantity = invoice.account.hide_quantity === '1';
doc.setFontSize(8);
for (var i=0; i<invoice.invoice_items.length; i++) {
@ -47507,7 +47543,7 @@ function displayInvoiceItems(doc, invoice, layout) {
}
// show at most one blank line
if (shownItem && (!cost || cost == '0.00') && !qty && !notes && !productKey) {
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
continue;
}
shownItem = true;
@ -47581,7 +47617,9 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.text(layout.descriptionLeft, y+2, notes);
doc.text(costX, y+2, cost);
doc.text(qtyX, y+2, qty);
if (!hideQuantity) {
doc.text(qtyX, y+2, qty);
}
doc.text(totalX, y+2, lineTotal);
if (tax) {

View File

@ -494,6 +494,12 @@ border-bottom-width: 1px;
.dashboard .table-striped>tbody>tr>td:first-child { padding-left: 15px; }
.dashboard .table-striped>thead>tr>th:first-child { padding-left: 15px; }
.invoice-table tfoot input {
text-align: right;
}
/***********************************************
New/edit invoice page
************************************************/

View File

@ -1331,14 +1331,32 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
return;
}
//var taxTitle = 'Tax ' + getInvoiceTaxRate(invoice) + '%';
var data = [
{'subtotal': formatMoney(invoice.subtotal_amount, invoice.client.currency_id)},
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false},
{'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false},
{'paid_to_date': formatMoney(invoice.amount - invoice.balance, invoice.client.currency_id)}
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false}
];
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
}
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
}
data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false});
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
}
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
}
var paid = invoice.amount - invoice.balance;
if (invoice.account.hide_paid_to_date === '0' || paid) {
data.push({'paid_to_date': formatMoney(paid, invoice.client.currency_id)});
}
return displayGrid(doc, invoice, data, 300, y, layout, true, 550, rightAlignTitleX) + 10;
}
@ -1485,11 +1503,18 @@ function calculateAmounts(invoice) {
invoice.subtotal_amount = total;
if (invoice.discount > 0) {
var discount = roundToTwo(total * (invoice.discount/100));
total -= discount;
}
// custom fields with taxes
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) {
total += roundToTwo(invoice.custom_value1);
}
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) {
total += roundToTwo(invoice.custom_value2);
}
var tax = 0;
if (invoice.tax && parseFloat(invoice.tax.rate)) {
tax = parseFloat(invoice.tax.rate);
@ -1502,9 +1527,17 @@ function calculateAmounts(invoice) {
total = parseFloat(total) + parseFloat(tax);
}
// custom fields w/o with taxes
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) {
total += roundToTwo(invoice.custom_value1);
}
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) {
total += roundToTwo(invoice.custom_value2);
}
invoice.balance_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance));
invoice.tax_amount = tax;
invoice.discount_amount = discount;
invoice.tax_amount = tax;
invoice.has_taxes = hasTaxes;
return invoice;
@ -1530,7 +1563,9 @@ function displayInvoiceHeader(doc, invoice, layout) {
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item);
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description);
doc.text(costX, layout.tableTop, invoiceLabels.unit_cost);
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity);
if (invoice.account.hide_quantity === '0') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity);
}
doc.text(totalX, layout.tableTop, invoiceLabels.line_total);
if (invoice.has_taxes)
@ -1547,6 +1582,7 @@ function displayInvoiceItems(doc, invoice, layout) {
var shownItem = false;
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
var tableTop = layout.tableTop;
var hideQuantity = invoice.account.hide_quantity === '1';
doc.setFontSize(8);
for (var i=0; i<invoice.invoice_items.length; i++) {
@ -1582,7 +1618,7 @@ function displayInvoiceItems(doc, invoice, layout) {
}
// show at most one blank line
if (shownItem && (!cost || cost == '0.00') && !qty && !notes && !productKey) {
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
continue;
}
shownItem = true;
@ -1656,7 +1692,9 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.text(layout.descriptionLeft, y+2, notes);
doc.text(costX, y+2, cost);
doc.text(qtyX, y+2, qty);
if (!hideQuantity) {
doc.text(qtyX, y+2, qty);
}
doc.text(totalX, y+2, lineTotal);
if (tax) {