mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Added custom invoice fields
This commit is contained in:
parent
4620a06a9b
commit
4b177e160d
@ -4,6 +4,6 @@ return array(
|
||||
|
||||
//'TAG_MANAGER_KEY' => '',
|
||||
//'ANALYTICS_KEY' => '',
|
||||
'NINJA_PROD' => true,
|
||||
//'NINJA_PROD' => true,
|
||||
|
||||
);
|
||||
|
@ -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();
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
||||
);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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> </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> </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
|
||||
|
@ -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> </p>
|
||||
|
||||
{{ Former::legend('invoice_design') }}
|
||||
{{ Former::text('primary_color') }}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
************************************************/
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
************************************************/
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user