1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-21 08:51:34 +02:00
invoiceninja/resources/views/invoices/edit.blade.php

1917 lines
70 KiB
PHP
Raw Normal View History

2013-11-26 13:45:07 +01:00
@extends('header')
2013-12-08 14:32:49 +01:00
@section('head')
@parent
2014-01-05 15:03:29 +01:00
<script src="{{ asset('js/pdf_viewer.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/compatibility.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/pdfmake.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script>
2015-04-27 14:28:40 +02:00
2013-12-08 14:32:49 +01:00
@stop
2013-11-26 13:45:07 +01:00
@section('content')
2013-12-07 19:45:00 +01:00
2014-05-20 23:40:09 +02:00
@if ($invoice && $invoice->id)
2014-02-17 00:09:34 +01:00
<ol class="breadcrumb">
@if ($isRecurring)
<li>{!! link_to('invoices', trans('texts.recurring_invoice')) !!}</li>
@else
<li>{!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}</li>
<li class='active'>{{ $invoice->invoice_number }}</li>
@endif
2014-02-17 00:09:34 +01:00
</ol>
@endif
2014-01-01 16:23:32 +01:00
2015-03-27 06:02:19 +01:00
{!! Former::open($url)->method($method)->addClass('warn-on-exit')->rules(array(
2013-12-08 14:32:49 +01:00
'client' => 'required',
2015-06-14 10:55:02 +02:00
'product_key' => 'max:255'
2015-03-27 06:02:19 +01:00
)) !!}
2013-12-31 20:49:54 +01:00
2015-04-20 16:34:23 +02:00
2014-03-17 19:05:01 +01:00
<input type="submit" style="display:none" name="submitButton" id="submitButton">
2014-01-01 16:23:32 +01:00
<div data-bind="with: invoice">
2015-04-20 16:34:23 +02:00
<div class="panel panel-default">
2015-04-22 23:40:21 +02:00
<div class="panel-body" style="padding-bottom: 0px;">
2015-04-20 16:34:23 +02:00
<div class="row" style="min-height:195px" onkeypress="formEnterClick(event)">
2014-02-16 23:03:19 +01:00
<div class="col-md-4" id="col_1">
2013-12-31 00:19:17 +01:00
2014-05-20 23:40:09 +02:00
@if ($invoice && $invoice->id)
2013-12-31 00:19:17 +01:00
<div class="form-group">
<label for="client" class="control-label col-lg-4 col-sm-4">Client</label>
2015-05-08 10:21:29 +02:00
<div class="col-lg-8 col-sm-8">
<h4><div data-bind="text: getClientDisplayName(ko.toJS(client()))"></div></h4>
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ trans('texts.edit_client') }}</a> |
2015-06-10 10:34:20 +02:00
{!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!}
2013-12-31 00:19:17 +01:00
</div>
</div>
<div style="display:none">
@endif
{!! Former::select('client')->addOption('', '')->data_bind("dropdown: client")->addClass('client-input')->addGroupClass('client_select closer-row') !!}
<div class="form-group" style="margin-bottom: 8px">
2013-12-15 13:55:50 +01:00
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
2015-08-11 16:38:36 +02:00
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, html: $root.clientLinkText"></a>
2015-05-08 10:21:29 +02:00
<span data-bind="visible: $root.invoice().client().public_id() > 0">|
<a data-bind="attr: {href: '{{ url('/clients') }}/' + $root.invoice().client().public_id()}" target="_blank">{{ trans('texts.view_client') }}</a>
</span>
</div>
</div>
2014-05-20 23:40:09 +02:00
@if ($invoice && $invoice->id)
2013-12-31 00:19:17 +01:00
</div>
@endif
<div data-bind="with: client">
2015-04-16 19:12:56 +02:00
<div style="display:none" class="form-group" data-bind="visible: contacts().length > 0 &amp;&amp; (contacts()[0].email() || contacts()[0].first_name()), foreach: contacts">
<div class="col-lg-8 col-lg-offset-4">
2015-07-21 20:51:56 +02:00
<label class="checkbox" data-bind="attr: {for: $index() + '_check'}" onclick="refreshPDF(true)">
<input type="checkbox" value="1" data-bind="checked: send_invoice, attr: {id: $index() + '_check'}">
2014-01-22 10:11:33 +01:00
<span data-bind="html: email.display"/>
</label>
</div>
</div>
</div>
2013-12-11 21:33:44 +01:00
2013-11-26 22:45:10 +01:00
</div>
2013-12-11 21:33:44 +01:00
<div class="col-md-4" id="col_2">
<div data-bind="visible: !is_recurring()">
2015-03-27 06:02:19 +01:00
{!! Former::text('invoice_date')->data_bind("datePicker: invoice_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_date"))
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!}
2015-09-07 11:07:55 +02:00
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_due_date"))
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!}
2015-04-16 19:12:56 +02:00
2015-06-03 19:55:48 +02:00
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()')
->rel('tooltip')->data_toggle('tooltip')->data_placement('bottom')->title(trans('texts.partial_value')) !!}
2013-12-10 23:10:43 +01:00
</div>
2014-05-17 21:29:57 +02:00
@if ($entityType == ENTITY_INVOICE)
<div data-bind="visible: is_recurring" style="display: none">
{!! Former::select('frequency_id')->options($frequencies)->data_bind("value: frequency_id")
->appendIcon('question-sign')->addGroupClass('frequency_id') !!}
2015-03-27 06:02:19 +01:00
{!! Former::text('start_date')->data_bind("datePicker: start_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('start_date') !!}
2015-03-27 06:02:19 +01:00
{!! Former::text('end_date')->data_bind("datePicker: end_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('end_date') !!}
2014-05-17 21:29:57 +02:00
</div>
2015-08-03 21:08:07 +02:00
@if ($invoice && $invoice->recurring_invoice)
2014-05-20 23:40:09 +02:00
<div class="pull-right" style="padding-top: 6px">
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
2014-05-20 23:40:09 +02:00
</div>
2015-08-30 14:08:15 +02:00
@elseif ($invoice && isset($lastSent) && $lastSent)
2015-09-20 23:05:02 +02:00
<div class="pull-right" style="padding-top: 6px">
{!! trans('texts.last_sent_on', ['date' => link_to('/invoices/'.$lastSent->public_id, Utils::dateToString($invoice->last_sent_date))]) !!}
2015-05-05 11:48:23 +02:00
</div>
@endif
2013-12-24 07:22:16 +01:00
@endif
2013-12-11 21:33:44 +01:00
2013-11-26 22:45:10 +01:00
</div>
2013-12-24 07:22:16 +01:00
2014-02-16 23:03:19 +01:00
<div class="col-md-4" id="col_2">
2015-08-14 14:04:33 +02:00
<span data-bind="visible: !is_recurring()">
{!! Former::text('invoice_number')
->label(trans("texts.{$entityType}_number_short"))
->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
</span>
<span data-bind="visible: is_recurring()" style="display: none">
{!! Former::checkbox('auto_bill')
->label(trans('texts.auto_bill'))
->text(trans('texts.enable'))
->data_bind("checked: auto_bill, valueUpdate: 'afterkeydown'") !!}
2015-08-14 14:04:33 +02:00
</span>
2015-03-27 06:02:19 +01:00
{!! Former::text('po_number')->label(trans('texts.po_number_short'))->data_bind("value: po_number, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'")
2014-12-03 23:05:38 +01:00
->addGroupClass('discount-group')->type('number')->min('0')->step('any')->append(
Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0')
->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw()
2015-03-27 06:02:19 +01:00
) !!}
2013-12-27 10:10:32 +01:00
<div class="form-group" style="margin-bottom: 8px">
2015-05-10 10:45:03 +02:00
<label for="taxes" class="control-label col-lg-4 col-sm-4">{{ trans('texts.taxes') }}</label>
<div class="col-lg-8 col-sm-8" style="padding-top: 10px">
2014-03-27 13:25:31 +01:00
<a href="#" data-bind="click: $root.showTaxesForm"><i class="glyphicon glyphicon-list-alt"></i> {{ trans('texts.manage_rates') }}</a>
2013-12-27 10:10:32 +01:00
</div>
</div>
2013-12-24 07:22:16 +01:00
</div>
2013-11-26 22:45:10 +01:00
</div>
2013-11-26 13:45:07 +01:00
2013-11-26 22:45:10 +01:00
<p>&nbsp;</p>
2013-11-26 13:45:07 +01:00
2014-07-16 23:04:56 +02:00
<div class="table-responsive">
<table class="table invoice-table">
2014-05-22 20:29:29 +02:00
<thead>
<tr>
<th style="min-width:32px;" class="hide-border"></th>
2015-06-10 10:34:20 +02:00
<th style="min-width:160px" data-bind="text: productLabel"></th>
<th style="width:100%">{{ $invoiceLabels['description'] }}</th>
2015-06-10 10:34:20 +02:00
<th style="min-width:120px" data-bind="text: costLabel"></th>
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}" data-bind="text: qtyLabel"></th>
2014-05-22 20:29:29 +02:00
<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>
</tr>
</thead>
<tbody data-bind="sortable: { data: invoice_items, afterMove: onDragged }">
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
<td class="hide-border td-icon">
2015-08-14 14:04:33 +02:00
<i style="display:none" data-bind="visible: actionsVisible() &amp;&amp;
$index() < ($parent.invoice_items().length - 1) &amp;&amp;
$parent.invoice_items().length > 1" class="fa fa-sort"></i>
2014-05-22 20:29:29 +02:00
</td>
2015-08-14 14:04:33 +02:00
<td>
2015-04-22 23:40:21 +02:00
{!! Former::text('product_key')->useDatalist($products->toArray(), 'product_key')->onkeyup('onItemChange()')
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') !!}
2014-05-22 20:29:29 +02:00
</td>
<td>
2015-05-09 20:25:16 +02:00
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: vertical" class="form-control word-wrap"></textarea>
2014-05-22 20:29:29 +02:00
</td>
<td>
<input onkeyup="onItemChange()" data-bind="value: prettyCost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control"//>
</td>
2014-07-20 12:38:42 +02:00
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
2014-05-22 20:29:29 +02:00
<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">
<select class="form-control" style="width:100%" data-bind="value: tax, options: $root.tax_rates, optionsText: 'displayName'"></select>
</td>
<td style="text-align:right;padding-top:9px !important">
<div class="line-total" data-bind="text: totals.total"></div>
</td>
2015-09-20 23:05:02 +02:00
<td style="cursor:pointer" class="hide-border td-icon">
<i style="display:none;padding-left:4px" data-bind="click: $parent.removeItem, visible: actionsVisible() &amp;&amp;
2015-08-14 14:04:33 +02:00
$index() < ($parent.invoice_items().length - 1) &amp;&amp;
$parent.invoice_items().length > 1" class="fa fa-minus-circle redlink" title="Remove item"/>
2014-05-22 20:29:29 +02:00
</td>
</tr>
2013-11-26 22:45:10 +01:00
</tbody>
2014-07-20 12:38:42 +02:00
2013-12-31 20:49:54 +01:00
<tfoot>
<tr>
2014-05-22 20:29:29 +02:00
<td class="hide-border"/>
2015-04-20 16:34:23 +02:00
<td class="hide-border" colspan="2" rowspan="6" style="vertical-align:top">
2014-05-22 20:29:29 +02:00
<br/>
2015-02-28 22:42:47 +01:00
<div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"><a href="#notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.note_to_client') }}</a></li>
<li role="presentation"><a href="#terms" aria-controls="terms" role="tab" data-toggle="tab">{{ trans('texts.invoice_terms') }}</a></li>
<li role="presentation"><a href="#footer" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.invoice_footer') }}</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="notes" style="padding-bottom:44px">
2015-03-27 06:02:19 +01:00
{!! Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'")
->label(null)->style('resize: none; min-width: 450px;')->rows(3) !!}
2015-02-28 22:42:47 +01:00
</div>
<div role="tabpanel" class="tab-pane" id="terms">
2015-09-07 11:07:55 +02:00
{!! Former::textarea('terms')->data_bind("value:wrapped_terms, placeholder: terms_placeholder, valueUpdate: 'afterkeydown'")
2015-03-10 13:39:25 +01:00
->label(false)->style('resize: none; min-width: 450px')->rows(3)
2015-09-07 11:07:55 +02:00
->help('<div class="checkbox">
<label>
<input type="checkbox" style="width: 24px" data-bind="checked: set_default_terms"/>'.trans('texts.save_as_default_terms').'
</label>
<div class="pull-right"><a href="#" onclick="return resetTerms()">' . trans("texts.reset_terms") . '</a></div>
</div>') !!}
2015-02-28 22:42:47 +01:00
</div>
<div role="tabpanel" class="tab-pane" id="footer">
2015-09-07 11:07:55 +02:00
{!! Former::textarea('invoice_footer')->data_bind("value:wrapped_footer, placeholder: footer_placeholder, valueUpdate: 'afterkeydown'")
2015-03-10 13:39:25 +01:00
->label(false)->style('resize: none; min-width: 450px')->rows(3)
2015-09-07 11:07:55 +02:00
->help('<div class="checkbox">
<label>
<input type="checkbox" style="width: 24px" data-bind="checked: set_default_footer"/>'.trans('texts.save_as_default_footer').'
</label>
<div class="pull-right"><a href="#" onclick="return resetFooter()">' . trans("texts.reset_footer") . '</a></div>
</div>') !!}
2015-02-28 22:42:47 +01:00
</div>
</div>
</div>
2014-05-22 20:29:29 +02:00
</td>
2015-02-28 22:42:47 +01:00
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
2014-07-20 12:38:42 +02:00
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ trans('texts.subtotal') }}</td>
2014-01-01 16:23:32 +01:00
<td style="text-align: right"><span data-bind="text: totals.subtotal"/></td>
2014-05-22 20:29:29 +02:00
</tr>
2014-07-20 12:38:42 +02:00
2014-12-03 23:05:38 +01:00
<tr style="display:none" data-bind="visible: discount() != 0">
2014-05-22 20:29:29 +02:00
<td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
2014-07-20 12:38:42 +02:00
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ trans('texts.discount') }}</td>
2014-01-01 16:23:32 +01:00
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
2014-05-22 20:29:29 +02:00
</tr>
2014-07-20 12:38:42 +02:00
@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>
2014-07-23 17:02:56 +02:00
<td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
2014-07-20 12:38:42 +02:00
</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>
2014-07-23 17:02:56 +02:00
<td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
2014-07-20 12:38:42 +02:00
</tr>
@endif
<tr style="display:none" data-bind="visible: $root.invoice_item_taxes.show &amp;&amp; totals.hasItemTaxes">
<td class="hide-border" colspan="4"/>
@if (!$account->hide_quantity)
<td>{{ trans('texts.tax') }}</td>
@endif
<td style="min-width:120px"><span data-bind="html: totals.itemTaxRates"/></td>
<td style="text-align: right"><span data-bind="html: totals.itemTaxAmounts"/></td>
</tr>
2014-05-22 20:29:29 +02:00
<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"/>
2014-07-20 12:38:42 +02:00
@if (!$account->hide_quantity)
<td>{{ trans('texts.tax') }}</td>
@endif
2014-02-03 11:34:58 +01:00
<td style="min-width:120px"><select class="form-control" style="width:100%" data-bind="value: tax, options: $root.tax_rates, optionsText: 'displayName'"></select></td>
2014-03-10 12:04:15 +01:00
<td style="text-align: right"><span data-bind="text: totals.taxAmount"/></td>
2014-05-22 20:29:29 +02:00
</tr>
2014-07-20 12:38:42 +02:00
@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>
2014-07-23 17:02:56 +02:00
<td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
2014-07-20 12:38:42 +02:00
</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>
2014-07-23 17:02:56 +02:00
<td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
2014-07-20 12:38:42 +02:00
</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
2014-05-22 20:29:29 +02:00
<tr>
<td class="hide-border" colspan="3"/>
2015-04-20 16:34:23 +02:00
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td class="hide-border" colspan="{{ $account->hide_quantity ? 1 : 2 }}"><b>{{ trans($entityType == ENTITY_INVOICE ? 'texts.balance_due' : 'texts.total') }}</b></td>
<td class="hide-border" style="text-align: right"><span data-bind="text: totals.total"></span></td>
2014-05-22 20:29:29 +02:00
</tr>
2014-07-20 12:38:42 +02:00
2014-05-22 20:29:29 +02:00
</tfoot>
2014-07-20 12:38:42 +02:00
2013-11-26 22:45:10 +01:00
</table>
2014-07-16 23:04:56 +02:00
</div>
2015-04-20 16:34:23 +02:00
</div>
</div>
2013-11-26 22:45:10 +01:00
<p>&nbsp;</p>
<div class="form-actions">
2013-12-03 18:32:33 +01:00
2013-12-07 19:45:00 +01:00
<div style="display:none">
2015-03-27 06:02:19 +01:00
{!! Former::populateField('entityType', $entityType) !!}
{!! Former::text('entityType') !!}
{!! Former::text('action') !!}
2015-06-03 19:55:48 +02:00
{!! Former::text('data')->data_bind("value: ko.mapping.toJSON(model)") !!}
2015-04-22 23:40:21 +02:00
{!! Former::text('pdfupload') !!}
2014-05-20 23:40:09 +02:00
@if ($invoice && $invoice->id)
2015-03-27 06:02:19 +01:00
{!! Former::populateField('id', $invoice->public_id) !!}
{!! Former::text('id') !!}
2013-12-07 19:45:00 +01:00
@endif
</div>
2013-12-11 21:33:44 +01:00
2015-07-24 16:13:17 +02:00
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
2015-07-21 20:51:56 +02:00
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else
2015-07-21 20:51:56 +02:00
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
@endif
2014-02-21 06:26:40 +01:00
2015-03-27 06:02:19 +01:00
{!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!}
2014-11-23 22:47:10 +01:00
@if (!$invoice || (!$invoice->trashed() && !$invoice->client->trashed()))
2014-05-20 23:40:09 +02:00
2015-06-04 22:53:58 +02:00
{!! Button::success(trans("texts.save_{$entityType}"))->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))->appendIcon(Icon::create('floppy-disk')) !!}
2015-08-20 17:09:04 +02:00
{!! Button::info(trans("texts.email_{$entityType}"))->withAttributes(array('id' => 'emailButton', 'onclick' => 'onEmailClick()'))->appendIcon(Icon::create('send')) !!}
2014-01-15 15:01:24 +01:00
2015-06-04 22:53:58 +02:00
@if ($invoice && $invoice->id)
{!! DropdownButton::normal(trans('texts.more_actions'))
->withContents($actions)
->dropup() !!}
@endif
2015-01-11 12:56:58 +01:00
@elseif ($invoice && $invoice->trashed() && !$invoice->is_deleted == '1')
2015-03-27 07:06:14 +01:00
{!! Button::success(trans('texts.restore'))->withAttributes(['onclick' => 'submitAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!}
2014-01-15 15:01:24 +01:00
@endif
2013-11-26 13:45:07 +01:00
</div>
2013-11-26 22:45:10 +01:00
<p>&nbsp;</p>
2014-10-12 20:11:41 +02:00
@include('invoices.pdf', ['account' => Auth::user()->account])
2013-11-26 13:45:07 +01:00
2014-04-25 15:04:57 +02:00
@if (!Auth::user()->account->isPro())
2014-04-23 14:47:44 +02:00
<div style="font-size:larger">
2015-06-10 22:55:23 +02:00
{!! trans('texts.pro_plan.remove_logo', ['link'=>'<a href="#" onclick="showProPlan(\'remove_logo\')">'.trans('texts.pro_plan.remove_logo_link').'</a>']) !!}
2014-04-23 14:47:44 +02:00
</div>
2014-04-03 22:01:03 +02:00
@endif
2013-11-26 13:45:07 +01:00
2013-12-27 10:10:32 +01:00
<div class="modal fade" id="clientModal" tabindex="-1" role="dialog" aria-labelledby="clientModalLabel" aria-hidden="true">
2015-04-20 16:34:23 +02:00
<div class="modal-dialog" data-bind="css: {'large-dialog': $root.showMore}">
<div class="modal-content" style="background-color: #f8f8f8">
2013-11-26 13:45:07 +01:00
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2014-03-27 13:25:31 +01:00
<h4 class="modal-title" id="clientModalLabel">{{ trans('texts.client') }}</h4>
2013-11-26 13:45:07 +01:00
</div>
2013-12-05 21:25:20 +01:00
2015-04-20 16:34:23 +02:00
<div class="container" style="width: 100%; padding-bottom: 0px !important">
<div class="panel panel-default">
<div class="panel-body">
<div class="row" data-bind="with: client" onkeypress="clientModalEnterClick(event)">
<div style="margin-left:0px;margin-right:0px" data-bind="css: {'col-md-6': $root.showMore}">
{!! Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }")->label('client_name') !!}
<span data-bind="visible: $root.showMore">
{!! Former::text('id_number')->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('vat_number')->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!}
@if (Auth::user()->isPro())
@if ($account->custom_client_label1)
{!! Former::text('custom_value1')->label($account->custom_client_label1)
->data_bind("value: custom_value1, valueUpdate: 'afterkeydown'") !!}
@endif
@if ($account->custom_client_label2)
{!! Former::text('custom_value2')->label($account->custom_client_label2)
->data_bind("value: custom_value2, valueUpdate: 'afterkeydown'") !!}
@endif
@endif
&nbsp;
{!! Former::text('address1')->data_bind("value: address1, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('address2')->data_bind("value: address2, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('state')->data_bind("value: state, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") !!}
{!! Former::select('country_id')->addOption('','')->addGroupClass('country_select')
->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") !!}
</span>
</div>
<div style="margin-left:0px;margin-right:0px" data-bind="css: {'col-md-6': $root.showMore}">
<div data-bind='template: { foreach: contacts,
beforeRemove: hideContact,
afterAdd: showContact }'>
{!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('email')->data_bind('value: email, valueUpdate: \'afterkeydown\', attr: {id:\'email\'+$index()}') !!}
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!}
<div class="form-group">
<div class="col-lg-8 col-lg-offset-4">
<span class="redlink bold" data-bind="visible: $parent.contacts().length > 1">
{!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!}
</span>
<span data-bind="visible: $index() === ($parent.contacts().length - 1)" class="pull-right greenlink bold">
{!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!}
</span>
</div>
</div>
</div>
2013-12-05 21:25:20 +01:00
2015-04-20 16:34:23 +02:00
<span data-bind="visible: $root.showMore">
&nbsp;
</span>
{!! Former::select('currency_id')->addOption('','')
->placeholder($account->currency ? $account->currency->name : '')
->data_bind('value: currency_id')
2015-04-20 16:34:23 +02:00
->fromQuery($currencies, 'name', 'id') !!}
<span data-bind="visible: $root.showMore">
{!! Former::select('language_id')->addOption('','')
->placeholder($account->language ? $account->language->name : '')
->data_bind('value: language_id')
->fromQuery($languages, 'name', 'id') !!}
2015-04-28 22:13:52 +02:00
{!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms')
2015-07-07 22:08:16 +02:00
->fromQuery($paymentTerms, 'name', 'num_days')
->help(trans('texts.payment_terms_help')) !!}
2015-04-20 16:34:23 +02:00
{!! Former::select('size_id')->addOption('','')->data_bind('value: size_id')
->fromQuery($sizes, 'name', 'id') !!}
{!! Former::select('industry_id')->addOption('','')->data_bind('value: industry_id')
->fromQuery($industries, 'name', 'id') !!}
{!! Former::textarea('private_notes')->data_bind('value: private_notes') !!}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer" style="margin-top: 0px; padding-top:0px;">
2015-04-30 19:54:19 +02:00
<span class="error-block" id="emailError" style="display:none;float:left;font-weight:bold">{{ trans('texts.provide_name_or_email') }}</span><span>&nbsp;</span>
2015-04-20 16:34:23 +02:00
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
<button type="button" class="btn btn-default" data-bind="click: $root.showMoreFields, text: $root.showMore() ? '{{ trans('texts.less_fields') }}' : '{{ trans('texts.more_fields') }}'"></button>
<button id="clientDoneButton" type="button" class="btn btn-primary" data-bind="click: $root.clientFormComplete">{{ trans('texts.done') }}</button>
</div>
</div>
</div>
</div>
2013-12-27 10:10:32 +01:00
<div class="modal fade" id="taxModal" tabindex="-1" role="dialog" aria-labelledby="taxModalLabel" aria-hidden="true">
2014-07-16 23:04:56 +02:00
<div class="modal-dialog">
2013-12-27 10:10:32 +01:00
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2014-03-27 13:25:31 +01:00
<h4 class="modal-title" id="taxModalLabel">{{ trans('texts.tax_rates') }}</h4>
2013-11-26 13:45:07 +01:00
</div>
2013-12-27 10:10:32 +01:00
<div style="background-color: #fff" onkeypress="taxModalEnterClick(event)">
2013-12-27 10:10:32 +01:00
<table class="table invoice-table sides-padded" style="margin-bottom: 0px !important">
<thead>
<tr>
<th class="hide-border"></th>
2014-03-27 13:25:31 +01:00
<th class="hide-border">{{ trans('texts.name') }}</th>
<th class="hide-border">{{ trans('texts.rate') }}</th>
2013-12-27 10:10:32 +01:00
<th class="hide-border"></th>
</tr>
</thead>
2014-01-01 16:23:32 +01:00
<tbody data-bind="foreach: $root.tax_rates.filtered">
2013-12-27 10:10:32 +01:00
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }">
<td style="width:30px" class="hide-border"></td>
2013-12-27 10:10:32 +01:00
<td style="width:60px">
2015-07-21 20:51:56 +02:00
<input onkeyup="onTaxRateChange()" data-bind="value: name, valueUpdate: 'afterkeydown'" class="form-control" onchange="refreshPDF(true)"//>
2013-12-27 10:10:32 +01:00
</td>
<td style="width:60px">
2015-07-21 20:51:56 +02:00
<input onkeyup="onTaxRateChange()" data-bind="value: prettyRate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF(true)"//>
2013-12-27 10:10:32 +01:00
</td>
<td style="width:30px; cursor:pointer" class="hide-border td-icon">
&nbsp;<i style="width:12px;" data-bind="click: $root.removeTaxRate, visible: actionsVisible() &amp;&amp; !isEmpty()" class="fa fa-minus-circle redlink" title="Remove item"/>
2013-12-27 10:10:32 +01:00
</td>
</tr>
</tbody>
</table>
&nbsp;
2014-07-16 23:04:56 +02:00
2015-03-27 06:02:19 +01:00
{!! Former::checkbox('invoice_taxes')->text(trans('texts.enable_invoice_tax'))
->label(trans('texts.settings'))->data_bind('checked: $root.invoice_taxes, enable: $root.tax_rates().length > 1') !!}
{!! Former::checkbox('invoice_item_taxes')->text(trans('texts.enable_line_item_tax'))
->label('&nbsp;')->data_bind('checked: $root.invoice_item_taxes, enable: $root.tax_rates().length > 1') !!}
2015-09-07 11:07:55 +02:00
{!! Former::checkbox('show_item_taxes')->text(trans('texts.show_line_item_tax'))
->label('&nbsp;')->data_bind('checked: $root.show_item_taxes, enable: $root.tax_rates().length > 1') !!}
2013-12-31 20:49:54 +01:00
2014-01-06 07:48:11 +01:00
<br/>
2013-12-27 10:10:32 +01:00
</div>
<div class="modal-footer" style="margin-top: 0px">
2013-12-29 12:28:44 +01:00
<!-- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> -->
2014-03-27 13:25:31 +01:00
<button type="button" class="btn btn-primary" data-bind="click: $root.taxFormComplete">{{ trans('texts.done') }}</button>
2013-12-27 10:10:32 +01:00
</div>
2013-12-05 21:25:20 +01:00
2013-11-26 13:45:07 +01:00
</div>
</div>
</div>
2014-01-15 15:01:24 +01:00
<div class="modal fade" id="recurringModal" tabindex="-1" role="dialog" aria-labelledby="recurringModalLabel" aria-hidden="true">
<div class="modal-dialog" style="min-width:150px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2014-03-27 13:25:31 +01:00
<h4 class="modal-title" id="recurringModalLabel">{{ trans('texts.recurring_invoices') }}</h4>
2014-01-15 15:01:24 +01:00
</div>
<div style="background-color: #fff; padding-left: 16px; padding-right: 16px">
2015-03-31 11:38:24 +02:00
&nbsp; {!! isset($recurringHelp) ? $recurringHelp : '' !!} &nbsp;
2014-01-15 15:01:24 +01:00
</div>
<div class="modal-footer" style="margin-top: 0px">
2014-04-10 21:29:01 +02:00
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
2014-01-15 15:01:24 +01:00
</div>
</div>
</div>
</div>
2015-03-27 06:02:19 +01:00
{!! Former::close() !!}
2014-01-15 15:01:24 +01:00
2015-04-20 16:34:23 +02:00
</div>
2013-11-26 13:45:07 +01:00
<script type="text/javascript">
2014-01-15 15:01:24 +01:00
function showLearnMore() {
$('#recurringModal').modal('show');
}
2013-11-26 13:45:07 +01:00
$(function() {
2014-03-17 19:05:01 +01:00
$('#country_id').combobox().on('change', function(e) {
2015-09-07 11:07:55 +02:00
var countryId = $('input[name=country_id]').val();
var country = _.findWhere(countries, {id: countryId});
if (country) {
model.invoice().client().country = country;
model.invoice().client().country_id(countryId);
} else {
2014-03-17 19:05:01 +01:00
model.invoice().client().country = false;
model.invoice().client().country_id(0);
}
});
2015-06-03 19:55:48 +02:00
$('[rel=tooltip]').tooltip({'trigger':'manual'});
2013-12-05 21:25:20 +01:00
$('#invoice_date, #due_date, #start_date, #end_date, #last_sent_date').datepicker();
2013-11-28 17:40:13 +01:00
2013-12-29 18:40:11 +01:00
@if ($client && !$invoice)
$('input[name=client]').val({{ $client->public_id }});
@endif
2014-04-02 21:03:01 +02:00
/*
if (clients.length == 0) {
$('.client_select input.form-control').prop('disabled', true);
}
*/
2013-11-26 13:45:07 +01:00
var $input = $('select#client');
2013-12-31 17:38:49 +01:00
$input.combobox().on('change', function(e) {
2014-01-01 18:34:37 +01:00
var clientId = parseInt($('input[name=client]').val(), 10);
if (clientId > 0) {
model.loadClient(clientMap[clientId]);
// we enable searching by contact but the selection must be the client
$('.client-input').val(getClientDisplayName(clientMap[clientId]));
} else {
2014-01-02 09:27:48 +01:00
model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
2014-03-17 19:05:01 +01:00
model.invoice().client().country = false;
2015-07-24 16:13:17 +02:00
}
refreshPDF(true);
2014-07-07 08:43:16 +02:00
});
// If no clients exists show the client form when clicking on the client select input
if (clients.length === 0) {
$('.client_select input.form-control').on('click', function() {
model.showClientForm();
});
}
2013-11-26 13:45:07 +01:00
2015-04-16 19:12:56 +02:00
$('#invoice_footer, #terms, #public_notes, #invoice_number, #invoice_date, #due_date, #po_number, #discount, #currency_id, #invoice_design_id, #recurring, #is_amount_discount, #partial').change(function() {
2015-07-24 16:13:17 +02:00
setTimeout(function() {
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2014-05-04 11:11:08 +02:00
}, 1);
2013-12-30 21:17:45 +01:00
});
2013-12-24 22:27:36 +01:00
$('.frequency_id .input-group-addon').click(function() {
showLearnMore();
});
var fields = ['invoice_date', 'due_date', 'start_date', 'end_date', 'last_sent_date'];
for (var i=0; i<fields.length; i++) {
var field = fields[i];
(function (_field) {
$('.' + _field + ' .input-group-addon').click(function() {
toggleDatePicker(_field);
});
})(field);
}
2014-07-23 17:02:56 +02:00
@if ($client || $invoice || count($clients) == 0)
2013-12-08 20:08:17 +01:00
$('#invoice_number').focus();
2013-11-26 13:45:07 +01:00
@else
2013-12-31 00:19:17 +01:00
$('.client_select input.form-control').focus();
2013-11-26 13:45:07 +01:00
@endif
2013-12-29 00:33:48 +01:00
$('#clientModal').on('shown.bs.modal', function () {
2014-01-06 07:48:11 +01:00
$('#name').focus();
2013-12-29 00:33:48 +01:00
}).on('hidden.bs.modal', function () {
2013-12-17 14:14:47 +01:00
if (model.clientBackup) {
model.loadClient(model.clientBackup);
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2013-12-17 14:14:47 +01:00
}
2013-11-26 13:45:07 +01:00
})
2013-12-17 14:14:47 +01:00
2013-12-27 10:10:32 +01:00
$('#taxModal').on('shown.bs.modal', function () {
$('#taxModal input:first').focus();
2013-12-29 00:33:48 +01:00
}).on('hidden.bs.modal', function () {
2014-01-02 00:12:33 +01:00
// if the user changed the tax rates we need to trigger the
// change event on the selects for the model to get updated
$('table.invoice-table select').trigger('change');
2013-12-29 00:33:48 +01:00
})
2013-12-27 10:10:32 +01:00
2014-01-02 18:16:00 +01:00
$('#relatedActions > button:first').click(function() {
2014-01-15 15:01:24 +01:00
onPaymentClick();
2014-01-02 18:16:00 +01:00
});
2013-12-10 23:10:43 +01:00
$('label.radio').addClass('radio-inline');
2014-01-06 19:03:00 +01:00
applyComboboxListeners();
@if ($client)
$input.trigger('change');
@else
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2014-01-06 19:03:00 +01:00
@endif
2014-01-02 00:12:33 +01:00
var client = model.invoice().client();
setComboboxValue($('.client_select'),
client.public_id(),
2015-02-28 22:42:47 +01:00
client.name.display());
2015-07-12 21:43:45 +02:00
@if (isset($tasks) && $tasks)
NINJA.formIsChanged = true;
@endif
2013-12-17 14:14:47 +01:00
});
2013-11-26 13:45:07 +01:00
function applyComboboxListeners() {
2015-05-09 20:25:16 +02:00
var selectorStr = '.invoice-table input, .invoice-table select, .invoice-table textarea';
2014-04-14 13:33:43 +02:00
$(selectorStr).off('blur').on('blur', function() {
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2013-12-31 10:43:39 +01:00
});
2015-05-09 20:25:16 +02:00
$('textarea').on('keyup focus', function(e) {
while($(this).outerHeight() < this.scrollHeight + parseFloat($(this).css("borderTopWidth")) + parseFloat($(this).css("borderBottomWidth"))) {
$(this).height($(this).height()+1);
};
});
2014-04-23 23:48:09 +02:00
@if (Auth::user()->account->fill_products)
2015-05-09 20:25:16 +02:00
$('.datalist').on('input', function() {
2014-04-23 23:48:09 +02:00
var key = $(this).val();
for (var i=0; i<products.length; i++) {
var product = products[i];
if (product.product_key == key) {
var model = ko.dataFor(this);
2015-08-11 16:38:36 +02:00
if (!model.notes()) {
model.notes(product.notes);
}
if (!model.cost()) {
model.cost(accounting.toFixed(product.cost,2));
}
if (!model.qty()) {
model.qty(1);
}
2014-04-23 23:48:09 +02:00
break;
}
}
2015-02-18 14:46:46 +01:00
onItemChange();
refreshPDF();
2014-04-23 23:48:09 +02:00
});
@endif
}
2013-11-26 13:45:07 +01:00
function createInvoiceModel() {
2015-09-07 11:07:55 +02:00
var invoice = ko.toJS(model).invoice;
2014-04-14 13:33:43 +02:00
invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }};
2014-05-20 23:40:09 +02:00
invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }};
2014-12-03 23:05:38 +01:00
invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true});
2015-09-07 11:07:55 +02:00
invoice.account.show_item_taxes = $('#show_item_taxes').is(':checked');
2014-04-13 15:34:58 +02:00
if (invoice.is_recurring) {
invoice.invoice_number = '0000';
}
2015-04-27 14:28:40 +02:00
@if (!$invoice)
if (!invoice.terms) {
2015-06-04 22:53:58 +02:00
invoice.terms = wordWrapText('{!! str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_terms)) !!}', 300);
2015-04-27 14:28:40 +02:00
}
if (!invoice.invoice_footer) {
2015-06-04 22:53:58 +02:00
invoice.invoice_footer = wordWrapText('{!! str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_footer)) !!}', 600);
2015-04-27 14:28:40 +02:00
}
@endif
2015-02-28 22:42:47 +01:00
@if (file_exists($account->getLogoPath()))
invoice.image = "{{ HTML::image_data($account->getLogoPath()) }}";
2014-03-18 22:25:54 +01:00
invoice.imageWidth = {{ $account->getLogoWidth() }};
invoice.imageHeight = {{ $account->getLogoHeight() }};
@endif
2014-02-21 06:26:40 +01:00
2015-06-10 10:34:20 +02:00
invoiceLabels.item = invoice.has_tasks ? invoiceLabels.date : invoiceLabels.item_orig;
invoiceLabels.quantity = invoice.has_tasks ? invoiceLabels.hours : invoiceLabels.quantity_orig;
invoiceLabels.unit_cost = invoice.has_tasks ? invoiceLabels.rate : invoiceLabels.unit_cost_orig;
2015-04-13 14:49:40 +02:00
return invoice;
2013-11-26 13:45:07 +01:00
}
2015-07-21 20:51:56 +02:00
function getPDFString(cb, force) {
2015-07-24 16:13:17 +02:00
var invoice = createInvoiceModel();
2014-10-12 20:11:41 +02:00
var design = getDesignJavascript();
if (!design) return;
2015-07-21 20:51:56 +02:00
generatePDF(invoice, design, force, cb);
2014-03-10 12:04:15 +01:00
}
2014-04-14 13:33:43 +02:00
2014-10-12 20:11:41 +02:00
function getDesignJavascript() {
var id = $('#invoice_design_id').val();
if (id == '-1') {
showMoreDesigns();
2014-10-25 19:32:28 +02:00
model.invoice().invoice_design_id(1);
return invoiceDesigns[0].javascript;
} else {
2015-07-21 20:51:56 +02:00
var design = _.find(invoiceDesigns, function(design){ return design.id == id});
return design ? design.javascript : '';
}
2013-12-07 19:45:00 +01:00
}
2013-11-26 13:45:07 +01:00
2015-09-07 11:07:55 +02:00
function resetTerms() {
2015-09-17 21:01:06 +02:00
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
model.invoice().terms(model.invoice().default_terms());
refreshPDF();
}
2015-09-07 11:07:55 +02:00
return false;
}
function resetFooter() {
2015-09-17 21:01:06 +02:00
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
model.invoice().invoice_footer(model.invoice().default_footer());
refreshPDF();
}
2015-09-07 11:07:55 +02:00
return false;
}
2013-11-26 13:45:07 +01:00
function onDownloadClick() {
2015-06-01 15:06:18 +02:00
trackEvent('/activity', '/download_pdf');
2013-11-26 13:45:07 +01:00
var invoice = createInvoiceModel();
var design = getDesignJavascript();
if (!design) return;
var doc = generatePDF(invoice, design, true);
2015-05-09 20:25:16 +02:00
var type = invoice.is_quote ? '{{ trans('texts.'.ENTITY_QUOTE) }}' : '{{ trans('texts.'.ENTITY_INVOICE) }}';
doc.save(type +'-' + $('#invoice_number').val() + '.pdf');
2013-11-26 13:45:07 +01:00
}
function onEmailClick() {
2015-07-07 22:08:16 +02:00
if (!NINJA.isRegistered) {
2015-09-17 21:01:06 +02:00
alert("{!! trans('texts.registration_required') !!}");
2015-07-07 22:08:16 +02:00
return;
}
if (confirm('{!! trans("texts.confirm_email_$entityType") !!}' + '\n\n' + getSendToEmails())) {
2015-04-22 23:40:21 +02:00
preparePdfData('email');
2013-11-26 13:45:07 +01:00
}
}
2013-12-11 21:33:44 +01:00
function onSaveClick() {
if (model.invoice().is_recurring()) {
if (confirm("{!! trans("texts.confirm_recurring_email_$entityType") !!}" + '\n\n' + getSendToEmails() + '\n' + "{!! trans("texts.confirm_recurring_timing") !!}")) {
submitAction('');
}
} else {
2015-04-22 23:40:21 +02:00
preparePdfData('');
}
2014-07-30 09:08:01 +02:00
}
2015-07-07 22:08:16 +02:00
function getSendToEmails() {
var client = model.invoice().client();
var parts = [];
for (var i=0; i<client.contacts().length; i++) {
var contact = client.contacts()[i];
if (contact.send_invoice()) {
parts.push(contact.displayName());
}
}
return parts.join('\n');
}
2015-04-22 23:40:21 +02:00
function preparePdfData(action) {
var invoice = createInvoiceModel();
var design = getDesignJavascript();
if (!design) return;
doc = generatePDF(invoice, design, true);
doc.getDataUrl( function(pdfString){
2015-09-17 21:01:06 +02:00
$('#pdfupload').val(pdfString);
submitAction(action);
});
2015-04-22 23:40:21 +02:00
}
2014-07-30 09:08:01 +02:00
function submitAction(value) {
if (!isSaveValid()) {
model.showClientForm();
return;
}
2015-06-03 19:55:48 +02:00
onPartialChange(true);
2014-07-30 09:08:01 +02:00
$('#action').val(value);
$('#submitButton').click();
2013-12-11 21:33:44 +01:00
}
2014-03-09 22:19:56 +01:00
function isSaveValid() {
var isValid = false;
2014-07-30 09:08:01 +02:00
for (var i=0; i<model.invoice().client().contacts().length; i++) {
var contact = model.invoice().client().contacts()[i];
2015-04-16 19:12:56 +02:00
if (isValidEmailAddress(contact.email()) || contact.first_name()) {
2014-03-09 22:19:56 +01:00
isValid = true;
} else {
isValid = false;
break;
}
}
return isValid;
}
function isEmailValid() {
var isValid = false;
var sendTo = false;
var client = self.invoice().client();
for (var i=0; i<client.contacts().length; i++) {
var contact = client.contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
if (contact.send_invoice() || client.contacts().length == 1) {
sendTo = true;
}
} else {
isValid = false;
break;
}
}
return (isValid && sendTo)
}
2014-12-07 09:47:58 +01:00
function onMarkClick() {
submitAction('mark');
}
2013-12-11 21:33:44 +01:00
function onCloneClick() {
2014-07-30 09:08:01 +02:00
submitAction('clone');
2013-12-11 21:33:44 +01:00
}
2014-05-20 23:40:09 +02:00
function onConvertClick() {
2014-07-30 09:08:01 +02:00
submitAction('convert');
2014-05-20 23:40:09 +02:00
}
2014-01-02 14:21:15 +01:00
@if ($client && $invoice)
function onPaymentClick() {
window.location = '{{ URL::to('payments/create/' . $client->public_id . '/' . $invoice->public_id ) }}';
}
function onCreditClick() {
window.location = '{{ URL::to('credits/create/' . $client->public_id . '/' . $invoice->public_id ) }}';
}
@endif
2013-12-03 18:32:33 +01:00
function onArchiveClick() {
2014-07-30 09:08:01 +02:00
submitAction('archive');
2013-12-03 18:32:33 +01:00
}
function onDeleteClick() {
2015-06-03 19:55:48 +02:00
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
2014-07-30 09:08:01 +02:00
submitAction('delete');
2013-12-03 18:32:33 +01:00
}
}
function formEnterClick(event) {
if (event.keyCode === 13){
if (event.target.type == 'textarea') {
return;
2013-12-11 21:33:44 +01:00
}
event.preventDefault();
2014-01-02 00:12:33 +01:00
2014-07-30 09:08:01 +02:00
submitAction('');
return false;
2013-11-26 13:45:07 +01:00
}
}
2013-12-05 21:25:20 +01:00
2013-12-27 10:10:32 +01:00
function clientModalEnterClick(event) {
2013-11-26 13:45:07 +01:00
if (event.keyCode === 13){
event.preventDefault();
model.clientFormComplete();
2013-11-26 13:45:07 +01:00
return false;
}
}
2013-12-27 10:10:32 +01:00
function taxModalEnterClick(event) {
if (event.keyCode === 13){
event.preventDefault();
model.taxFormComplete();
return false;
}
}
2014-01-01 18:34:37 +01:00
function ViewModel(data) {
2014-01-01 16:23:32 +01:00
var self = this;
2015-04-20 16:34:23 +02:00
self.showMore = ko.observable(false);
2014-01-01 18:34:37 +01:00
//self.invoice = data ? false : new InvoiceModel();
self.invoice = ko.observable(data ? false : new InvoiceModel());
2014-01-01 16:23:32 +01:00
self.tax_rates = ko.observableArray();
self.loadClient = function(client) {
2014-01-01 18:34:37 +01:00
ko.mapping.fromJS(client, model.invoice().client().mapping, model.invoice().client);
2015-04-21 22:09:45 +02:00
@if (!$invoice)
self.setDueDate();
@endif
2014-01-08 00:59:06 +01:00
}
2015-04-20 16:34:23 +02:00
self.showMoreFields = function() {
self.showMore(!self.showMore());
}
2014-01-08 00:59:06 +01:00
self.setDueDate = function() {
2015-04-21 22:09:45 +02:00
@if ($entityType == ENTITY_INVOICE)
2015-04-28 22:13:52 +02:00
var paymentTerms = parseInt(self.invoice().client().payment_terms());
if (paymentTerms && paymentTerms != 0 && !self.invoice().due_date())
2014-01-08 00:59:06 +01:00
{
2015-04-28 22:13:52 +02:00
if (paymentTerms == -1) paymentTerms = 0;
2014-01-08 00:59:06 +01:00
var dueDate = $('#invoice_date').datepicker('getDate');
dueDate.setDate(dueDate.getDate() + paymentTerms);
self.invoice().due_date(dueDate);
2014-04-29 15:45:00 +02:00
// We're using the datepicker to handle the date formatting
self.invoice().due_date($('#due_date').val());
2014-01-08 00:59:06 +01:00
}
2015-04-21 22:09:45 +02:00
@endif
2014-01-01 16:23:32 +01:00
}
self.invoice_taxes = ko.observable({{ Auth::user()->account->invoice_taxes ? 'true' : 'false' }});
self.invoice_item_taxes = ko.observable({{ Auth::user()->account->invoice_item_taxes ? 'true' : 'false' }});
2015-09-07 11:07:55 +02:00
self.show_item_taxes = ko.observable({{ Auth::user()->account->show_item_taxes ? 'true' : 'false' }});
2014-01-01 18:34:37 +01:00
2014-01-01 16:23:32 +01:00
self.mapping = {
'invoice': {
create: function(options) {
return new InvoiceModel(options.data);
}
},
'tax_rates': {
create: function(options) {
return new TaxRateModel(options.data);
}
},
2014-01-01 18:34:37 +01:00
}
if (data) {
ko.mapping.fromJS(data, self.mapping, self);
2014-01-01 16:23:32 +01:00
}
self.invoice_taxes.show = ko.computed(function() {
if (self.tax_rates().length > 2 && self.invoice_taxes()) {
return true;
}
2014-01-01 18:34:37 +01:00
if (self.invoice().tax_rate() > 0) {
2014-01-01 16:23:32 +01:00
return true;
}
return false;
});
self.invoice_item_taxes.show = ko.computed(function() {
if (self.tax_rates().length > 2 && self.invoice_item_taxes()) {
return true;
}
2014-01-01 18:34:37 +01:00
for (var i=0; i<self.invoice().invoice_items().length; i++) {
var item = self.invoice().invoice_items()[i];
2014-01-01 16:23:32 +01:00
if (item.tax_rate() > 0) {
return true;
}
}
return false;
});
self.tax_rates.filtered = ko.computed(function() {
2014-01-02 00:12:33 +01:00
var i = 0;
for (i; i<self.tax_rates().length; i++) {
var taxRate = self.tax_rates()[i];
if (taxRate.isEmpty()) {
break;
}
}
2014-01-01 16:23:32 +01:00
2014-01-02 00:12:33 +01:00
var rates = self.tax_rates().concat();
rates.splice(i, 1);
return rates;
});
2014-01-01 16:23:32 +01:00
self.removeTaxRate = function(taxRate) {
self.tax_rates.remove(taxRate);
//refreshPDF();
}
self.addTaxRate = function(data) {
var itemModel = new TaxRateModel(data);
self.tax_rates.push(itemModel);
applyComboboxListeners();
2014-01-02 00:12:33 +01:00
}
2014-01-01 16:23:32 +01:00
2014-01-02 00:12:33 +01:00
/*
2014-01-01 16:23:32 +01:00
self.getBlankTaxRate = function() {
for (var i=0; i<self.tax_rates().length; i++) {
var taxRate = self.tax_rates()[i];
if (!taxRate.name()) {
return taxRate;
}
}
}
2014-01-02 00:12:33 +01:00
*/
2014-01-01 16:23:32 +01:00
self.getTaxRate = function(name, rate) {
for (var i=0; i<self.tax_rates().length; i++) {
var taxRate = self.tax_rates()[i];
if (taxRate.name() == name && taxRate.rate() == parseFloat(rate)) {
return taxRate;
}
}
2014-01-02 00:12:33 +01:00
2014-01-01 16:23:32 +01:00
var taxRate = new TaxRateModel();
taxRate.name(name);
taxRate.rate(parseFloat(rate));
if (name) {
taxRate.is_deleted(true);
self.tax_rates.push(taxRate);
}
2014-01-01 16:23:32 +01:00
return taxRate;
}
self.showTaxesForm = function() {
self.taxBackup = ko.mapping.toJS(self.tax_rates);
$('#taxModal').modal('show');
}
self.taxFormComplete = function() {
model.taxBackup = false;
2015-09-07 11:07:55 +02:00
$('#taxModal').modal('hide');
refreshPDF();
2014-01-01 16:23:32 +01:00
}
self.showClientForm = function() {
2015-06-01 15:06:18 +02:00
trackEvent('/activity', '/view_client_form');
2014-01-01 18:34:37 +01:00
self.clientBackup = ko.mapping.toJS(self.invoice().client);
2014-01-01 16:23:32 +01:00
$('#emailError').css( "display", "none" );
$('#clientModal').modal('show');
}
self.clientFormComplete = function() {
2015-06-01 15:06:18 +02:00
trackEvent('/activity', '/save_client_form');
2014-01-02 09:27:48 +01:00
2015-04-16 19:12:56 +02:00
var email = $('#email0').val();
var firstName = $('#first_name').val();
var lastName = $('#last_name').val();
var name = $('#name').val();
if (name) {
//
} else if (firstName || lastName) {
name = firstName + ' ' + lastName;
} else {
name = email;
}
2014-01-02 09:27:48 +01:00
var isValid = true;
$("input[name='email']").each(function(item, value) {
2014-01-02 09:27:48 +01:00
var email = $(value).val();
2015-04-16 19:12:56 +02:00
if (!name && (!email || !isValidEmailAddress(email))) {
2014-01-02 09:27:48 +01:00
isValid = false;
}
});
if (!isValid) {
$('#emailError').css( "display", "inline" );
return;
}
2014-01-01 18:34:37 +01:00
if (self.invoice().client().public_id() == 0) {
self.invoice().client().public_id(-1);
2014-01-01 16:23:32 +01:00
}
2014-01-08 00:59:06 +01:00
model.setDueDate();
2014-01-02 00:12:33 +01:00
setComboboxValue($('.client_select'), -1, name);
//$('.client_select select').combobox('setSelected');
//$('.client_select input.form-control').val(name);
//$('.client_select .combobox-container').addClass('combobox-selected');
2014-01-01 16:23:32 +01:00
$('#emailError').css( "display", "none" );
2014-01-08 21:09:47 +01:00
//$('.client_select input.form-control').focus();
2014-01-01 16:23:32 +01:00
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2014-01-01 16:23:32 +01:00
model.clientBackup = false;
2014-07-23 17:02:56 +02:00
$('#clientModal').modal('hide');
2014-01-01 16:23:32 +01:00
}
2014-01-02 00:12:33 +01:00
self.clientLinkText = ko.computed(function() {
2014-02-01 21:01:32 +01:00
if (self.invoice().client().public_id())
{
2015-05-08 10:21:29 +02:00
return "{{ trans('texts.edit_client') }}";
2014-02-01 21:01:32 +01:00
}
else
{
2014-04-13 15:34:58 +02:00
if (clients.length > {{ Auth::user()->getMaxNumClients() }})
2014-02-01 21:01:32 +01:00
{
return '';
}
else
{
2014-03-27 13:25:31 +01:00
return "{{ trans('texts.create_new_client') }}";
2014-02-01 21:01:32 +01:00
}
}
});
2014-01-01 16:23:32 +01:00
}
2014-01-01 00:50:13 +01:00
function InvoiceModel(data) {
var self = this;
2014-01-01 18:34:37 +01:00
this.client = ko.observable(data ? false : new ClientModel());
2015-03-30 21:45:10 +02:00
self.account = {!! $account !!};
self.id = ko.observable('');
self.discount = ko.observable('');
2014-12-03 23:05:38 +01:00
self.is_amount_discount = ko.observable(0);
self.frequency_id = ko.observable(4); // default to monthly
2015-02-28 22:42:47 +01:00
self.terms = ko.observable('');
2015-09-07 11:07:55 +02:00
self.default_terms = ko.observable("{{ str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_terms)) }}");
self.terms_placeholder = ko.observable({{ !$invoice && $account->invoice_terms ? 'true' : 'false' }} ? self.default_terms() : '');
2015-02-28 22:42:47 +01:00
self.set_default_terms = ko.observable(false);
self.invoice_footer = ko.observable('');
2015-09-07 11:07:55 +02:00
self.default_footer = ko.observable("{{ str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_footer)) }}");
self.footer_placeholder = ko.observable({{ !$invoice && $account->invoice_footer ? 'true' : 'false' }} ? self.default_footer() : '');
2015-02-28 22:42:47 +01:00
self.set_default_footer = ko.observable(false);
2013-12-30 21:17:45 +01:00
self.public_notes = ko.observable('');
self.po_number = ko.observable('');
2014-01-01 16:23:32 +01:00
self.invoice_date = ko.observable('{{ Utils::today() }}');
self.invoice_number = ko.observable('{{ isset($invoiceNumber) ? $invoiceNumber : '' }}');
self.due_date = ko.observable('');
2014-01-01 16:23:32 +01:00
self.start_date = ko.observable('{{ Utils::today() }}');
self.end_date = ko.observable('');
self.last_sent_date = ko.observable('');
2014-01-01 16:23:32 +01:00
self.tax_name = ko.observable();
self.tax_rate = ko.observable();
self.is_recurring = ko.observable({{ $isRecurring ? 'true' : 'false' }});
self.auto_bill = ko.observable();
self.invoice_status_id = ko.observable(0);
self.invoice_items = ko.observableArray();
2014-01-08 00:59:06 +01:00
self.amount = ko.observable(0);
self.balance = ko.observable(0);
2015-07-21 20:51:56 +02:00
self.invoice_design_id = ko.observable({{ $account->invoice_design_id }});
2015-04-16 19:12:56 +02:00
self.partial = ko.observable(0);
2015-06-10 10:34:20 +02:00
self.has_tasks = ko.observable(false);
2013-12-31 20:49:54 +01:00
2014-07-20 12:38:42 +02:00
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 = {
2014-01-01 18:34:37 +01:00
'client': {
create: function(options) {
2015-04-21 22:09:45 +02:00
return new ClientModel(options.data);
2014-01-01 18:34:37 +01:00
}
},
'invoice_items': {
create: function(options) {
return new ItemModel(options.data);
}
2014-01-01 16:23:32 +01:00
},
'tax': {
create: function(options) {
return new TaxRateModel(options.data);
}
},
2013-12-17 14:14:47 +01:00
}
2014-01-01 18:34:37 +01:00
self.addItem = function() {
var itemModel = new ItemModel();
2014-07-20 12:38:42 +02:00
@if ($account->hide_quantity)
itemModel.qty(1);
@endif
2014-01-01 18:34:37 +01:00
self.invoice_items.push(itemModel);
applyComboboxListeners();
2015-05-27 18:52:10 +02:00
return itemModel;
2014-01-01 18:34:37 +01:00
}
2015-04-16 19:12:56 +02:00
if (data) {
2014-01-01 18:34:37 +01:00
ko.mapping.fromJS(data, self.mapping, self);
} else {
self.addItem();
}
2015-06-10 10:34:20 +02:00
self.productLabel = ko.computed(function() {
return self.has_tasks() ? invoiceLabels['date'] : invoiceLabels['item'];
}, this);
self.qtyLabel = ko.computed(function() {
return self.has_tasks() ? invoiceLabels['hours'] : invoiceLabels['quantity'];
}, this);
self.costLabel = ko.computed(function() {
return self.has_tasks() ? invoiceLabels['rate'] : invoiceLabels['unit_cost'];
}, this);
2014-01-01 16:23:32 +01:00
self._tax = ko.observable();
this.tax = ko.computed({
read: function () {
return self._tax();
},
write: function(value) {
if (value) {
self._tax(value);
self.tax_name(value.name());
self.tax_rate(value.rate());
} else {
self._tax(false);
self.tax_name('');
self.tax_rate(0);
}
}
})
2013-12-15 13:55:50 +01:00
self.wrapped_terms = ko.computed({
read: function() {
return this.terms();
},
write: function(value) {
2014-03-09 22:19:56 +01:00
value = wordWrapText(value, 300);
2013-12-15 13:55:50 +01:00
self.terms(value);
2013-12-30 21:17:45 +01:00
},
owner: this
});
2013-12-31 20:49:54 +01:00
2015-02-28 22:42:47 +01:00
self.wrapped_notes = ko.computed({
read: function() {
return this.public_notes();
},
write: function(value) {
value = wordWrapText(value, 300);
self.public_notes(value);
},
owner: this
});
self.wrapped_footer = ko.computed({
read: function() {
return this.invoice_footer();
},
write: function(value) {
value = wordWrapText(value, 600);
self.invoice_footer(value);
},
owner: this
});
2014-01-01 00:50:13 +01:00
2013-11-26 13:45:07 +01:00
self.removeItem = function(item) {
self.invoice_items.remove(item);
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2013-11-26 13:45:07 +01:00
}
2014-12-03 23:05:38 +01:00
self.totals = ko.observable();
2013-12-31 20:49:54 +01:00
2014-12-03 23:05:38 +01:00
self.totals.rawSubtotal = ko.computed(function() {
2013-11-26 13:45:07 +01:00
var total = 0;
2014-01-01 18:34:37 +01:00
for(var p=0; p < self.invoice_items().length; ++p) {
var item = self.invoice_items()[p];
total += item.totals.rawTotal();
2013-11-26 13:45:07 +01:00
}
return total;
});
2014-12-03 23:05:38 +01:00
self.totals.subtotal = ko.computed(function() {
2014-01-01 16:23:32 +01:00
var total = self.totals.rawSubtotal();
2014-01-15 15:01:24 +01:00
return total > 0 ? formatMoney(total, self.client().currency_id()) : '';
2013-11-26 13:45:07 +01:00
});
2014-12-03 23:05:38 +01:00
self.totals.rawDiscounted = ko.computed(function() {
if (parseInt(self.is_amount_discount())) {
return roundToTwo(self.discount());
} else {
return roundToTwo(self.totals.rawSubtotal() * (self.discount()/100));
}
2013-12-31 20:49:54 +01:00
});
2013-11-26 13:45:07 +01:00
2014-12-03 23:05:38 +01:00
self.totals.discounted = ko.computed(function() {
2014-01-15 15:01:24 +01:00
return formatMoney(self.totals.rawDiscounted(), self.client().currency_id());
2013-11-26 13:45:07 +01:00
});
2014-01-01 16:23:32 +01:00
self.totals.taxAmount = ko.computed(function() {
2015-06-10 10:34:20 +02:00
var total = self.totals.rawSubtotal();
var discount = self.totals.rawDiscounted();
total -= discount;
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;
}
2013-12-31 20:49:54 +01:00
2014-01-01 16:23:32 +01:00
var taxRate = parseFloat(self.tax_rate());
2013-12-31 20:49:54 +01:00
if (taxRate > 0) {
2014-06-01 15:35:19 +02:00
var tax = roundToTwo(total * (taxRate/100));
2014-01-15 15:01:24 +01:00
return formatMoney(tax, self.client().currency_id());
2013-12-31 20:49:54 +01:00
} else {
2015-06-10 10:34:20 +02:00
return formatMoney(0, self.client().currency_id());
2013-12-31 20:49:54 +01:00
}
});
self.totals.itemTaxes = ko.computed(function() {
var taxes = {};
var total = self.totals.rawSubtotal();
for(var i=0; i<self.invoice_items().length; i++) {
var item = self.invoice_items()[i];
var lineTotal = item.totals.rawTotal();
if (self.discount()) {
if (parseInt(self.is_amount_discount())) {
lineTotal -= roundToTwo((lineTotal/total) * self.discount());
} else {
lineTotal -= roundToTwo(lineTotal * (self.discount()/100));
}
}
var taxAmount = roundToTwo(lineTotal * item.tax_rate() / 100);
if (taxAmount) {
var key = item.tax_name() + item.tax_rate();
if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount;
} else {
taxes[key] = {name:item.tax_name(), rate:item.tax_rate(), amount:taxAmount};
}
}
}
return taxes;
});
self.totals.hasItemTaxes = ko.computed(function() {
var count = 0;
var taxes = self.totals.itemTaxes();
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
count++;
}
}
return count > 0;
});
self.totals.itemTaxRates = ko.computed(function() {
var taxes = self.totals.itemTaxes();
var parts = [];
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
parts.push(taxes[key].name + ' ' + (taxes[key].rate*1) + '%');
}
}
return parts.join('<br/>');
});
self.totals.itemTaxAmounts = ko.computed(function() {
var taxes = self.totals.itemTaxes();
var parts = [];
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
parts.push(formatMoney(taxes[key].amount, self.client().currency_id()));
}
}
return parts.join('<br/>');
});
2015-04-16 19:12:56 +02:00
self.totals.rawPaidToDate = ko.computed(function() {
2014-06-30 13:42:25 +02:00
return accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2);
2014-01-08 00:59:06 +01:00
});
2015-04-16 19:12:56 +02:00
self.totals.paidToDate = ko.computed(function() {
2014-01-08 00:59:06 +01:00
var total = self.totals.rawPaidToDate();
2014-12-07 09:47:58 +01:00
return formatMoney(total, self.client().currency_id());
2014-01-08 00:59:06 +01:00
});
2013-12-31 20:49:54 +01:00
2015-04-16 19:12:56 +02:00
self.totals.rawTotal = ko.computed(function() {
2015-02-18 13:59:25 +01:00
var total = accounting.toFixed(self.totals.rawSubtotal(),2);
var discount = self.totals.rawDiscounted();
total -= discount;
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) {
2015-02-18 13:59:25 +01:00
total = NINJA.parseFloat(total) + roundToTwo((total * (taxRate/100)));
}
var taxes = self.totals.itemTaxes();
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
}
}
2015-02-18 13:59:25 +01:00
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;
}
2014-01-08 00:59:06 +01:00
2015-04-16 19:12:56 +02:00
return total;
2015-02-18 13:59:25 +01:00
});
2013-11-26 13:45:07 +01:00
2015-04-16 19:12:56 +02:00
self.totals.total = ko.computed(function() {
2015-05-05 11:48:23 +02:00
return formatMoney(self.partial() ? self.partial() : self.totals.rawTotal(), self.client().currency_id());
2015-04-16 19:12:56 +02:00
});
2015-02-18 13:59:25 +01:00
self.onDragged = function(item) {
2015-07-21 20:51:56 +02:00
refreshPDF(true);
2015-02-18 13:59:25 +01:00
}
2013-11-26 13:45:07 +01:00
}
function ClientModel(data) {
var self = this;
self.public_id = ko.observable(0);
self.name = ko.observable('');
2015-02-18 13:59:25 +01:00
self.id_number = ko.observable('');
self.vat_number = ko.observable('');
self.work_phone = ko.observable('');
2014-04-18 10:57:31 +02:00
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
2013-12-30 21:17:45 +01:00
self.private_notes = ko.observable('');
self.address1 = ko.observable('');
self.address2 = ko.observable('');
self.city = ko.observable('');
self.state = ko.observable('');
self.postal_code = ko.observable('');
self.country_id = ko.observable('');
2014-01-06 19:03:00 +01:00
self.size_id = ko.observable('');
self.industry_id = ko.observable('');
2013-12-29 18:40:11 +01:00
self.currency_id = ko.observable('');
self.language_id = ko.observable('');
2013-12-15 13:55:50 +01:00
self.website = ko.observable('');
2014-01-01 16:23:32 +01:00
self.payment_terms = ko.observable(0);
self.contacts = ko.observableArray();
self.mapping = {
'contacts': {
create: function(options) {
2015-04-21 22:09:45 +02:00
var model = new ContactModel(options.data);
model.send_invoice(options.data.send_invoice == '1');
return model;
}
}
}
self.showContact = function(elem) { if (elem.nodeType === 1) $(elem).hide().slideDown() }
self.hideContact = function(elem) { if (elem.nodeType === 1) $(elem).slideUp(function() { $(elem).remove(); }) }
self.addContact = function() {
2015-04-21 22:09:45 +02:00
var contact = new ContactModel();
2014-04-23 22:30:54 +02:00
contact.send_invoice(true);
self.contacts.push(contact);
return false;
}
self.removeContact = function() {
self.contacts.remove(this);
}
2014-01-02 00:12:33 +01:00
self.name.display = ko.computed(function() {
if (self.name()) {
return self.name();
}
2013-12-30 21:17:45 +01:00
if (self.contacts().length == 0) return;
2014-01-02 00:12:33 +01:00
var contact = self.contacts()[0];
2013-12-30 21:17:45 +01:00
if (contact.first_name() || contact.last_name()) {
2014-01-02 00:12:33 +01:00
return contact.first_name() + ' ' + contact.last_name();
2013-12-30 21:17:45 +01:00
} else {
2014-01-02 00:12:33 +01:00
return contact.email();
2013-12-30 21:17:45 +01:00
}
2014-01-02 00:12:33 +01:00
});
2014-01-02 09:27:48 +01:00
self.name.placeholder = ko.computed(function() {
if (self.contacts().length == 0) return '';
var contact = self.contacts()[0];
if (contact.first_name() || contact.last_name()) {
return contact.first_name() + ' ' + contact.last_name();
} else {
return contact.email();
}
});
2013-12-30 21:17:45 +01:00
if (data) {
ko.mapping.fromJS(data, {}, this);
} else {
self.addContact();
}
}
function ContactModel(data) {
2013-11-26 13:45:07 +01:00
var self = this;
self.public_id = ko.observable('');
self.first_name = ko.observable('');
self.last_name = ko.observable('');
self.email = ko.observable('');
self.phone = ko.observable('');
self.send_invoice = ko.observable(false);
2014-04-23 09:20:01 +02:00
self.invitation_link = ko.observable('');
2015-07-07 22:08:16 +02:00
if (data) {
ko.mapping.fromJS(data, {}, this);
}
self.displayName = ko.computed(function() {
var str = '';
if (self.first_name() || self.last_name()) {
str += self.first_name() + ' ' + self.last_name() + '\n';
}
if (self.email()) {
str += self.email() + '\n';
}
return str;
});
2014-01-09 20:00:08 +01:00
self.email.display = ko.computed(function() {
var str = '';
if (self.first_name() || self.last_name()) {
2014-01-22 10:11:33 +01:00
str += self.first_name() + ' ' + self.last_name() + '<br/>';
2014-01-09 20:00:08 +01:00
}
2015-04-16 19:12:56 +02:00
if (self.email()) {
str += self.email() + '<br/>';
}
2014-04-23 09:20:01 +02:00
2014-04-23 15:02:24 +02:00
@if (Utils::isConfirmed())
2014-04-23 09:20:01 +02:00
if (self.invitation_link()) {
2015-04-16 19:12:56 +02:00
str += '<a href="' + self.invitation_link() + '" target="_blank">{{ trans('texts.view_as_recipient') }}</a>';
2014-04-23 09:20:01 +02:00
}
2014-04-23 15:02:24 +02:00
@endif
2014-04-23 09:20:01 +02:00
return str;
2014-01-02 09:27:48 +01:00
});
}
2013-12-27 10:10:32 +01:00
function TaxRateModel(data) {
var self = this;
2013-12-29 00:33:48 +01:00
self.public_id = ko.observable('');
2014-01-01 16:23:32 +01:00
self.rate = ko.observable(0);
2013-12-29 00:33:48 +01:00
self.name = ko.observable('');
2013-12-29 12:28:44 +01:00
self.is_deleted = ko.observable(false);
2014-01-02 00:12:33 +01:00
self.is_blank = ko.observable(false);
2013-12-29 00:33:48 +01:00
self.actionsVisible = ko.observable(false);
2013-12-27 10:10:32 +01:00
2013-12-29 00:33:48 +01:00
if (data) {
ko.mapping.fromJS(data, {}, this);
}
2013-12-29 12:28:44 +01:00
this.prettyRate = ko.computed({
read: function () {
2015-09-20 23:05:02 +02:00
return this.rate() ? roundToTwo(this.rate()) : '';
2013-12-29 12:28:44 +01:00
},
write: function (value) {
this.rate(value);
},
owner: this
});
2014-01-01 16:23:32 +01:00
self.displayName = ko.computed({
read: function () {
var name = self.name() ? self.name() : '';
var rate = self.rate() ? parseFloat(self.rate()) + '%' : '';
return name + ' ' + rate;
2014-01-01 16:23:32 +01:00
},
write: function (value) {
// do nothing
},
owner: this
2013-12-29 00:33:48 +01:00
});
self.hideActions = function() {
self.actionsVisible(false);
2013-12-27 10:10:32 +01:00
}
2013-12-29 00:33:48 +01:00
self.showActions = function() {
self.actionsVisible(true);
2013-12-27 10:10:32 +01:00
}
2013-12-29 00:33:48 +01:00
self.isEmpty = function() {
2013-12-27 10:10:32 +01:00
return !self.rate() && !self.name();
}
}
function ItemModel(data) {
var self = this;
2015-05-27 18:52:10 +02:00
self.product_key = ko.observable('');
self.notes = ko.observable('');
self.cost = ko.observable(0);
self.qty = ko.observable(0);
2014-01-01 16:23:32 +01:00
self.tax_name = ko.observable('');
self.tax_rate = ko.observable(0);
2015-05-27 18:52:10 +02:00
self.task_public_id = ko.observable('');
self.actionsVisible = ko.observable(false);
2013-12-29 18:40:11 +01:00
2014-01-01 16:23:32 +01:00
self._tax = ko.observable();
this.tax = ko.computed({
read: function () {
return self._tax();
},
write: function(value) {
self._tax(value);
self.tax_name(value.name());
self.tax_rate(value.rate());
}
})
2013-12-29 12:28:44 +01:00
this.prettyQty = ko.computed({
read: function () {
2014-02-04 08:07:16 +01:00
return NINJA.parseFloat(this.qty()) ? NINJA.parseFloat(this.qty()) : '';
2013-12-29 12:28:44 +01:00
},
write: function (value) {
this.qty(value);
},
owner: this
});
2013-12-31 00:19:17 +01:00
this.prettyCost = ko.computed({
read: function () {
return this.cost() ? this.cost() : '';
},
write: function (value) {
this.cost(value);
},
owner: this
});
2014-01-01 16:23:32 +01:00
self.mapping = {
'tax': {
create: function(options) {
return new TaxRateModel(options.data);
}
}
}
if (data) {
2014-01-01 16:23:32 +01:00
ko.mapping.fromJS(data, self.mapping, this);
2013-12-29 00:33:48 +01:00
}
2013-12-15 13:55:50 +01:00
self.wrapped_notes = ko.computed({
read: function() {
return this.notes();
},
write: function(value) {
2014-03-09 22:19:56 +01:00
value = wordWrapText(value, 235);
2013-12-15 13:55:50 +01:00
self.notes(value);
2013-12-27 10:10:32 +01:00
onItemChange();
2013-12-15 13:55:50 +01:00
},
owner: this
});
2014-01-01 16:23:32 +01:00
this.totals = ko.observable();
this.totals.rawTotal = ko.computed(function() {
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
var value = cost * qty;
return value ? roundToTwo(value) : 0;
});
2013-11-26 13:45:07 +01:00
2014-01-01 16:23:32 +01:00
this.totals.total = ko.computed(function() {
var total = self.totals.rawTotal();
2014-01-15 15:01:24 +01:00
if (window.hasOwnProperty('model') && model.invoice && model.invoice() && model.invoice().client()) {
return total ? formatMoney(total, model.invoice().client().currency_id()) : '';
2014-01-08 21:09:47 +01:00
} else {
return total ? formatMoney(total, 1) : '';
}
2014-06-01 15:35:19 +02:00
});
2013-11-26 13:45:07 +01:00
2014-06-01 15:35:19 +02:00
this.hideActions = function() {
this.actionsVisible(false);
}
2013-11-26 13:45:07 +01:00
2014-06-01 15:35:19 +02:00
this.showActions = function() {
this.actionsVisible(true);
}
2013-12-01 13:22:08 +01:00
2014-06-01 15:35:19 +02:00
this.isEmpty = function() {
2014-07-20 12:38:42 +02:00
return !self.product_key() && !self.notes() && !self.cost() && (!self.qty() || {{ $account->hide_quantity ? 'true' : 'false' }});
2014-06-01 15:35:19 +02:00
}
2013-12-29 00:33:48 +01:00
2014-06-01 15:35:19 +02:00
this.onSelect = function(){
}
2013-12-01 13:22:08 +01:00
}
2013-12-27 10:10:32 +01:00
function onItemChange()
2013-12-01 13:22:08 +01:00
{
var hasEmpty = false;
2014-01-01 18:34:37 +01:00
for(var i=0; i<model.invoice().invoice_items().length; i++) {
var item = model.invoice().invoice_items()[i];
2013-12-01 13:22:08 +01:00
if (item.isEmpty()) {
hasEmpty = true;
}
}
2013-12-27 10:10:32 +01:00
2013-12-01 13:22:08 +01:00
if (!hasEmpty) {
2014-01-01 18:34:37 +01:00
model.invoice().addItem();
2013-12-01 13:22:08 +01:00
}
2013-12-15 13:55:50 +01:00
2015-05-09 20:25:16 +02:00
/*
2013-12-15 13:55:50 +01:00
$('.word-wrap').each(function(index, input) {
2014-03-03 19:26:45 +01:00
$(input).height($(input).val().split('\n').length * 20);
2013-12-15 13:55:50 +01:00
});
2015-05-09 20:25:16 +02:00
*/
2013-11-26 13:45:07 +01:00
}
2013-12-27 10:10:32 +01:00
function onTaxRateChange()
{
2014-01-01 16:23:32 +01:00
var emptyCount = 0;
2014-01-02 00:12:33 +01:00
2013-12-27 10:10:32 +01:00
for(var i=0; i<model.tax_rates().length; i++) {
var taxRate = model.tax_rates()[i];
if (taxRate.isEmpty()) {
2014-01-01 16:23:32 +01:00
emptyCount++;
2013-12-27 10:10:32 +01:00
}
}
2014-01-02 00:12:33 +01:00
for(var i=0; i<2-emptyCount; i++) {
2013-12-27 10:10:32 +01:00
model.addTaxRate();
}
}
2015-06-03 19:55:48 +02:00
function onPartialChange(silent)
2015-04-16 19:12:56 +02:00
{
2015-04-16 21:57:12 +02:00
var val = NINJA.parseFloat($('#partial').val());
2015-06-03 19:55:48 +02:00
var oldVal = val;
2015-04-16 21:57:12 +02:00
val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0);
2015-06-03 19:55:48 +02:00
model.invoice().partial(val || '');
if (!silent && val != oldVal) {
$('#partial').tooltip('show');
setTimeout(function() {
$('#partial').tooltip('hide');
}, 5000);
}
2015-04-16 19:12:56 +02:00
}
function onRecurringEnabled()
2015-03-02 15:17:33 +01:00
{
if ($('#recurring').prop('checked')) {
2015-08-20 17:09:04 +02:00
$('#emailButton').attr('disabled', true);
2015-04-16 19:12:56 +02:00
model.invoice().partial('');
2015-03-02 15:17:33 +01:00
} else {
2015-08-20 17:09:04 +02:00
$('#emailButton').removeAttr('disabled');
2015-03-02 15:17:33 +01:00
}
}
2015-03-31 11:38:24 +02:00
var products = {!! $products !!};
var clients = {!! $clients !!};
2015-09-07 11:07:55 +02:00
var countries = {!! $countries !!};
2013-12-07 19:45:00 +01:00
var clientMap = {};
2013-12-30 21:17:45 +01:00
var $clientSelect = $('select#client');
2015-03-30 21:45:10 +02:00
var invoiceDesigns = {!! $invoiceDesigns !!};
2014-10-12 20:11:41 +02:00
2013-12-07 19:45:00 +01:00
for (var i=0; i<clients.length; i++) {
var client = clients[i];
var clientName = getClientDisplayName(client);
for (var j=0; j<client.contacts.length; j++) {
2013-12-15 13:55:50 +01:00
var contact = client.contacts[j];
var contactName = getContactDisplayName(contact);
if (contact.is_primary) {
contact.send_invoice = true;
}
if (clientName != contactName) {
$clientSelect.append(new Option(contactName, client.public_id));
}
}
2013-12-07 19:45:00 +01:00
clientMap[client.public_id] = client;
$clientSelect.append(new Option(clientName, client.public_id));
2013-12-07 19:45:00 +01:00
}
2014-01-01 18:34:37 +01:00
@if ($data)
2015-04-21 22:09:45 +02:00
window.model = new ViewModel({!! $data !!});
2014-01-01 18:34:37 +01:00
@else
window.model = new ViewModel();
2014-01-01 16:23:32 +01:00
model.addTaxRate();
@foreach ($taxRates as $taxRate)
2015-04-16 19:12:56 +02:00
model.addTaxRate({!! $taxRate !!});
@endforeach
2014-01-01 16:23:32 +01:00
@if ($invoice)
2015-03-31 11:38:24 +02:00
var invoice = {!! $invoice !!};
2014-01-01 18:34:37 +01:00
ko.mapping.fromJS(invoice, model.invoice().mapping, model.invoice);
2015-04-16 19:12:56 +02:00
var invitationContactIds = {!! json_encode($invitationContactIds) !!};
2014-01-01 16:23:32 +01:00
var client = clientMap[invoice.client.public_id];
2014-07-30 09:08:01 +02:00
if (client) { // in case it's deleted
for (var i=0; i<client.contacts.length; i++) {
var contact = client.contacts[i];
contact.send_invoice = invitationContactIds.indexOf(contact.public_id) >= 0;
}
}
2014-01-02 00:12:33 +01:00
model.invoice().addItem();
//model.addTaxRate();
2014-09-30 12:24:53 +02:00
@else
// TODO: Add the first tax rate for new invoices by adding a new db field to the tax codes types to set the default
//if(model.invoice_taxes() && model.tax_rates().length > 2) {
// var tax = model.tax_rates()[1];
// model.invoice().tax(tax);
//}
2014-07-20 12:38:42 +02:00
model.invoice().custom_taxes1({{ $account->custom_invoice_taxes1 ? 'true' : 'false' }});
model.invoice().custom_taxes2({{ $account->custom_invoice_taxes2 ? 'true' : 'false' }});
2014-01-01 16:23:32 +01:00
@endif
2015-07-12 21:43:45 +02:00
@if (isset($tasks) && $tasks)
// move the blank invoice line item to the end
var blank = model.invoice().invoice_items.pop();
var tasks = {!! $tasks !!};
2015-07-24 16:13:17 +02:00
2015-07-12 21:43:45 +02:00
for (var i=0; i<tasks.length; i++) {
var task = tasks[i];
var item = model.invoice().addItem();
item.notes(task.description);
item.product_key(task.startTime);
item.qty(task.duration);
item.task_public_id(task.publicId);
}
model.invoice().invoice_items.push(blank);
model.invoice().has_tasks(true);
@endif
2013-12-29 00:33:48 +01:00
@endif
2014-01-01 18:34:37 +01:00
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));
for (var i=0; i<model.invoice().invoice_items().length; i++) {
var item = model.invoice().invoice_items()[i];
item.tax(model.getTaxRate(item.tax_name(), item.tax_rate()));
2014-12-03 23:05:38 +01:00
item.cost(NINJA.parseFloat(item.cost()) != 0 ? roundToTwo(item.cost(), true) : '');
2014-01-01 18:34:37 +01:00
}
2014-01-02 00:12:33 +01:00
onTaxRateChange();
2014-01-01 18:34:37 +01:00
2014-07-20 12:38:42 +02:00
// display blank instead of '0'
2014-12-03 23:05:38 +01:00
if (!NINJA.parseFloat(model.invoice().discount())) model.invoice().discount('');
2015-04-16 19:12:56 +02:00
if (!NINJA.parseFloat(model.invoice().partial())) model.invoice().partial('');
2014-07-20 12:38:42 +02:00
if (!model.invoice().custom_value1()) model.invoice().custom_value1('');
if (!model.invoice().custom_value2()) model.invoice().custom_value2('');
2014-01-01 18:34:37 +01:00
ko.applyBindings(model);
2014-03-03 19:26:45 +01:00
onItemChange();
2014-02-03 12:15:28 +01:00
2013-11-26 13:45:07 +01:00
</script>
2015-09-02 12:59:03 +02:00
@stop