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

1457 lines
47 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>
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-02-17 00:09:34 +01:00
@if ($invoice)
<ol class="breadcrumb">
<li>{{ link_to('invoices', 'Invoices') }}</li>
<li class='active'>{{ $invoice->invoice_number }}</li>
</ol>
@endif
2014-01-01 16:23:32 +01:00
2013-11-26 22:45:10 +01:00
{{ Former::open($url)->method($method)->addClass('main_form')->rules(array(
2013-12-08 14:32:49 +01:00
'client' => 'required',
2013-12-30 21:17:45 +01:00
'email' => 'required',
2014-01-22 10:11:33 +01:00
'product_key' => 'max:20',
2014-01-01 16:23:32 +01:00
)); }}
2013-12-31 20:49:54 +01:00
2014-01-01 16:23:32 +01:00
<div data-bind="with: invoice">
<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-02-02 15:33:08 +01:00
@if ($invoice)
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>
<div class="col-lg-8 col-sm-8" style="padding-top: 7px">
2014-02-16 21:32:25 +01:00
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ $client->getDisplayName() }}</a>
2013-12-31 00:19:17 +01:00
</div>
</div>
<div style="display:none">
@endif
2014-01-01 18:34:37 +01:00
{{ Former::select('client')->addOption('', '')->data_bind("dropdown: client")->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">
2014-02-16 21:32:25 +01:00
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, text: $root.clientLinkText"></a>
</div>
</div>
2014-02-02 15:33:08 +01:00
@if ($invoice)
2013-12-31 00:19:17 +01:00
</div>
@endif
<div data-bind="with: client">
2014-02-17 19:38:48 +01:00
<div style="display:none" class="form-group" data-bind="visible: contacts().length > 0 &amp;&amp; contacts()[0].email(), foreach: contacts">
<div class="col-lg-8 col-lg-offset-4">
<label for="test" class="checkbox" data-bind="attr: {for: $index() + '_check'}">
<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()">
2014-02-17 17:25:38 +01:00
{{ Former::text('invoice_date')->data_bind("datePicker: invoice_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar"></i>') }}
{{ Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar"></i>') }}
2013-12-10 23:10:43 +01:00
</div>
2014-01-09 00:22:56 +01:00
<div data-bind="visible: is_recurring" style="display: none">
{{ Former::select('frequency_id')->label('How often')->options($frequencies)->data_bind("value: frequency_id") }}
2014-02-17 17:25:38 +01:00
{{ Former::text('start_date')->data_bind("datePicker: start_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar"></i>') }}
{{ Former::text('end_date')->data_bind("datePicker: end_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar"></i>') }}
2013-12-10 23:10:43 +01:00
</div>
2013-12-24 07:22:16 +01:00
@if ($invoice && $invoice->recurring_invoice_id)
2013-12-30 21:17:45 +01:00
<div class="pull-right" style="padding-top: 6px">
2013-12-24 07:22:16 +01:00
Created by a {{ link_to('/invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') }}
</div>
2013-12-30 21:17:45 +01:00
@else
<div data-bind="visible: invoice_status_id() < CONSTS.INVOICE_STATUS_SENT">
2014-02-17 17:25:38 +01:00
{{ Former::checkbox('recurring')->text('Enable &nbsp;&nbsp; <a href="#" onclick="showLearnMore()"><i class="glyphicon glyphicon-question-sign"></i> Learn more</a>')->data_bind("checked: is_recurring")
2013-12-30 21:17:45 +01:00
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::dateToString($invoice->last_sent_date) : '') }}
</div>
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">
2014-01-15 15:01:24 +01:00
{{ Former::text('invoice_number')->label('Invoice #')->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") }}
2014-01-14 12:52:56 +01:00
{{ Former::text('po_number')->label('PO #')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
2014-01-22 10:11:33 +01:00
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'")->append('%') }}
2014-01-15 15:01:24 +01:00
{{-- Former::select('currency_id')->label('Currency')->addOption('', '')->fromQuery($currencies, 'name', 'id')->data_bind("value: currency_id") --}}
2013-12-27 10:10:32 +01:00
<div class="form-group" style="margin-bottom: 8px">
<label for="recurring" class="control-label col-lg-4 col-sm-4">Taxes</label>
<div class="col-lg-8 col-sm-8" style="padding-top: 7px">
2014-02-17 17:25:38 +01:00
<a href="#" data-bind="click: $root.showTaxesForm"><i class="glyphicon glyphicon-list-alt"></i> 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-01-01 16:23:32 +01:00
{{ Former::hidden('data')->data_bind("value: ko.mapping.toJSON(model)") }}
<table class="table invoice-table" style="margin-bottom: 0px !important">
2013-11-26 22:45:10 +01:00
<thead>
<tr>
2014-02-17 19:26:24 +01:00
<th style="min-width:32px;" class="hide-border"></th>
<th style="min-width:160px">Item</th>
<th style="width:100%">Description</th>
<th style="min-width:120px">Unit Cost</th>
<th style="min-width:120px">Quantity</th>
2014-02-17 19:27:22 +01:00
<th style="min-width:120px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">Tax</th>
2014-02-17 19:26:24 +01:00
<th style="min-width:120px;">Line&nbsp;Total</th>
<th style="min-width:32px;" class="hide-border"></th>
2013-11-26 22:45:10 +01:00
</tr>
</thead>
<tbody data-bind="sortable: { data: invoice_items, afterMove: onDragged }">
2013-11-26 22:45:10 +01:00
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }" class="sortable-row">
2014-02-17 19:26:24 +01:00
<td class="hide-border td-icon">
2014-02-03 11:34:58 +01:00
<i style="display:none" data-bind="visible: actionsVisible() &amp;&amp; $parent.invoice_items().length > 1" class="fa fa-sort"></i>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td>
2013-12-27 10:10:32 +01:00
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')->onkeyup('onItemChange()')
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td>
2013-12-31 10:43:39 +01:00
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control word-wrap"></textarea>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td>
2013-12-31 10:43:39 +01:00
<input onkeyup="onItemChange()" data-bind="value: prettyCost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control"//>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td>
2013-12-31 10:43:39 +01:00
<input onkeyup="onItemChange()" data-bind="value: prettyQty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control"//>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td style="display:none;vertical-align:middle" data-bind="visible: $root.invoice_item_taxes.show">
2014-01-01 16:23:32 +01:00
<select class="form-control" style="width:100%" data-bind="value: tax, options: $root.tax_rates, optionsText: 'displayName'"></select>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td style="text-align:right;padding-top:9px !important">
2014-02-17 17:25:38 +01:00
<div class="line-total" data-bind="text: totals.total"></div>
2013-11-26 22:45:10 +01:00
</td>
2014-02-17 19:26:24 +01:00
<td style="cursor:pointer" class="hide-border td-icon">
2014-02-03 11:56:55 +01:00
&nbsp;<i style="display:none" data-bind="click: $parent.removeItem, visible: actionsVisible() &amp;&amp; $parent.invoice_items().length > 1" class="fa fa-minus-circle" title="Remove item"/>
2013-11-26 22:45:10 +01:00
</td>
</tr>
</tbody>
2013-12-31 20:49:54 +01:00
<tfoot>
<tr>
2013-12-29 00:33:48 +01:00
<td class="hide-border"/>
2014-01-02 18:16:00 +01:00
<td colspan="2" rowspan="5">
<br/>
{{ Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'")
->label(false)->placeholder('Note to client')->style('width: 520px; resize: none') }}
{{ Former::textarea('terms')->data_bind("value: wrapped_terms, valueUpdate: 'afterkeydown'")
2014-01-08 21:09:47 +01:00
->label(false)->placeholder('Invoice terms')->style('width: 520px; resize: none')
->addGroupClass('less-space-bottom') }}
2014-01-09 22:38:18 +01:00
<label class="checkbox" style="width: 200px">
2014-01-08 21:09:47 +01:00
<input type="checkbox" style="width: 24px" data-bind="checked: set_default_terms"/>Save as default terms
</label>
2014-01-02 18:16:00 +01:00
</td>
2014-02-17 19:37:04 +01:00
<td style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
2013-11-26 22:45:10 +01:00
<td colspan="2">Subtotal</td>
2014-01-01 16:23:32 +01:00
<td style="text-align: right"><span data-bind="text: totals.subtotal"/></td>
2013-11-26 22:45:10 +01:00
</tr>
2014-02-03 11:34:58 +01:00
<tr style="display:none" data-bind="visible: discount() > 0">
2013-12-31 20:49:54 +01:00
<td class="hide-border" colspan="3"/>
2014-02-17 19:37:04 +01:00
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
2013-11-26 22:45:10 +01:00
<td colspan="2">Discount</td>
2014-01-01 16:23:32 +01:00
<td style="text-align: right"><span data-bind="text: totals.discounted"/></td>
2013-11-26 22:45:10 +01:00
</tr>
2014-02-03 11:34:58 +01:00
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
2013-12-31 20:49:54 +01:00
<td class="hide-border" colspan="3"/>
2014-02-17 19:37:04 +01:00
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
2013-12-31 20:49:54 +01:00
<td style="vertical-align: middle">Tax</td>
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-01-01 16:23:32 +01:00
<td style="vertical-align: middle; text-align: right"><span data-bind="text: totals.taxAmount"/></td>
2013-12-31 20:49:54 +01:00
</tr>
2013-12-30 21:17:45 +01:00
<tr>
2013-12-31 20:49:54 +01:00
<td class="hide-border" colspan="3"/>
2014-02-17 19:37:04 +01:00
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
2013-12-30 21:17:45 +01:00
<td colspan="2">Paid to Date</td>
2014-01-08 00:59:06 +01:00
<td style="text-align: right" data-bind="text: totals.paidToDate"></td>
2013-12-30 21:17:45 +01:00
</tr>
2013-11-26 22:45:10 +01:00
<tr>
2013-12-31 20:49:54 +01:00
<td class="hide-border" colspan="3"/>
2014-02-17 19:37:04 +01:00
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
2013-12-11 21:33:44 +01:00
<td colspan="2"><b>Balance Due</b></td>
2014-01-01 16:23:32 +01:00
<td style="text-align: right"><span data-bind="text: totals.total"/></td>
2013-11-26 22:45:10 +01:00
</tr>
</tfoot>
</table>
2013-11-26 13:45:07 +01:00
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">
{{ Former::text('action') }}
2014-01-01 16:23:32 +01:00
@if ($invoice)
2014-01-12 19:55:33 +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
2014-02-19 20:59:46 +01:00
<div style="display:none">
{{ Former::select('invoice_design_id')->label('Design')->style('display:inline;width:120px')->raw()
->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") }}
2014-02-19 20:59:46 +01:00
</div>
2014-01-15 15:01:24 +01:00
{{ Button::normal('Download PDF', array('onclick' => 'onDownloadClick()')) }}
2014-02-18 22:56:18 +01:00
2014-02-19 14:28:29 +01:00
@if (!$invoice || (!$invoice->trashed() && !$invoice->client->trashed()))
2014-02-18 22:56:18 +01:00
@if ($invoice)
<div id="primaryActions" style="text-align:left" data-bind="css: $root.enable.save" class="btn-group">
<button class="btn-primary btn" type="button" data-bind="css: $root.enable.save">Save Invoice</button>
<button class="btn-primary btn dropdown-toggle" type="button" data-toggle="dropdown" data-bind="css: $root.enable.save">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="javascript:onSaveClick()" id="saveButton">Save Invoice</a></li>
<li><a href="javascript:onCloneClick()">Clone Invoice</a></li>
<li class="divider"></li>
<li><a href="javascript:onArchiveClick()">Archive Invoice</a></li>
<li><a href="javascript:onDeleteClick()">Delete Invoice</a></li>
</ul>
</div>
{{-- DropdownButton::normal('Download PDF',
Navigation::links(
array(
array('Download PDF', "javascript:onDownloadClick()"),
array(Navigation::DIVIDER),
array('Create Payment', "javascript:onPaymentClick()"),
array('Create Credit', "javascript:onCreditClick()"),
)
)
, array('id'=>'relatedActions', 'style'=>'text-align:left'))->split(); --}}
{{-- DropdownButton::primary('Save Invoice',
Navigation::links(
array(
array('Save Invoice', "javascript:onSaveClick()"),
array('Clone Invoice', "javascript:onCloneClick()"),
array(Navigation::DIVIDER),
array('Archive Invoice', "javascript:onArchiveClick()"),
array('Delete Invoice', "javascript:onDeleteClick()"),
)
)
, array('id'=>'primaryActions', 'style'=>'text-align:left', 'data-bind'=>'css: $root.enable.save'))->split(); --}}
@else
{{ Button::primary_submit('Save Invoice', array('data-bind'=>'css: $root.enable.save', 'id' => 'saveButton')) }}
@endif
2014-01-16 22:12:46 +01:00
2014-02-18 22:56:18 +01:00
{{ Button::primary('Email Invoice', array('id' => 'email_button', 'onclick' => 'onEmailClick()', 'data-bind' => 'css: $root.enable.email')) }}
2014-01-15 15:01:24 +01:00
2014-02-18 22:56:18 +01:00
@if ($invoice)
{{ Button::success('Enter Payment', array('onclick' => 'onPaymentClick()')) }}
@endif
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>
<!-- <textarea rows="20" cols="120" id="pdfText" onkeyup="runCode()"></textarea> -->
<!-- <iframe frameborder="1" width="100%" height="600" style="display:block;margin: 0 auto"></iframe> -->
2014-02-18 22:56:18 +01:00
<iframe id="theFrame" style="display:none" frameborder="1" width="100%" height="1180"></iframe>
2013-12-11 21:33:44 +01:00
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
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">
2013-12-05 21:25:20 +01:00
<div class="modal-dialog" style="min-width:1000px">
2013-11-26 13:45:07 +01:00
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2013-12-31 00:19:17 +01:00
<h4 class="modal-title" id="clientModalLabel">Client</h4>
2013-11-26 13:45:07 +01:00
</div>
2013-12-05 21:25:20 +01:00
2013-12-27 10:10:32 +01:00
<div class="container" style="width: 100%">
2014-01-08 21:09:47 +01:00
<div style="background-color: #EEEEEE" class="row" data-bind="with: client" onkeypress="clientModalEnterClick(event)">
2013-12-27 10:10:32 +01:00
<div class="col-md-6" style="margin-left:0px;margin-right:0px" >
2013-12-05 21:25:20 +01:00
2014-01-02 09:27:48 +01:00
{{ Former::legend('Organization') }}
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }") }}
{{ Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") }}
{{ Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'")->label('Phone') }}
{{ Former::legend('Address') }}
{{ Former::text('address1')->label('Street')->data_bind("value: address1, valueUpdate: 'afterkeydown'") }}
2014-01-02 18:16:00 +01:00
{{ Former::text('address2')->label('Apt/Suite')->data_bind("value: address2, valueUpdate: 'afterkeydown'") }}
2014-01-02 09:27:48 +01:00
{{ Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") }}
2014-01-02 18:16:00 +01:00
{{ Former::text('state')->label('State/Province')->data_bind("value: state, valueUpdate: 'afterkeydown'") }}
2014-01-02 09:27:48 +01:00
{{ Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") }}
{{ Former::select('country_id')->addOption('','')->label('Country')->addGroupClass('country_select')
->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") }}
</div>
<div class="col-md-6" style="margin-left:0px;margin-right:0px" >
{{ Former::legend('Contacts') }}
<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 data-bind="visible: $parent.contacts().length > 1">
{{ link_to('#', 'Remove contact', array('data-bind'=>'click: $parent.removeContact')) }}
</span>
<span data-bind="visible: $index() === ($parent.contacts().length - 1)" class="pull-right">
{{ link_to('#', 'Add contact', array('data-bind'=>'click: $parent.addContact')) }}
</span>
</div>
</div>
</div>
{{ Former::legend('Additional Info') }}
2014-01-01 16:23:32 +01:00
{{ Former::select('payment_terms')->addOption('','0')->data_bind('value: payment_terms')
2013-12-31 10:43:39 +01:00
->fromQuery($paymentTerms, 'name', 'num_days') }}
2013-12-29 18:40:11 +01:00
{{ Former::select('currency_id')->addOption('','')->label('Currency')->data_bind('value: currency_id')
->fromQuery($currencies, 'name', 'id') }}
2014-01-06 19:03:00 +01:00
{{ Former::select('size_id')->addOption('','')->label('Size')->data_bind('value: size_id')
->fromQuery($sizes, 'name', 'id') }}
{{ Former::select('industry_id')->addOption('','')->label('Industry')->data_bind('value: industry_id')
->fromQuery($industries, 'name', 'id') }}
2013-12-30 21:17:45 +01:00
{{ Former::textarea('private_notes')->data_bind('value: private_notes') }}
2013-12-05 21:25:20 +01:00
</div>
</div>
2013-12-27 10:10:32 +01:00
</div>
2013-12-05 21:25:20 +01:00
2013-12-27 10:10:32 +01:00
<div class="modal-footer" style="margin-top: 0px">
2013-12-31 10:43:39 +01:00
<span class="error-block" id="emailError" style="display:none;float:left;font-weight:bold">Please provide a valid email address.</span><span>&nbsp;</span>
2013-12-05 21:25:20 +01:00
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
2014-02-03 23:56:23 +01:00
<button id="clientDoneButton" type="button" class="btn btn-primary" data-bind="click: $root.clientFormComplete">Done</button>
2013-12-27 10:10:32 +01:00
</div>
</div>
</div>
</div>
<div class="modal fade" id="taxModal" tabindex="-1" role="dialog" aria-labelledby="taxModalLabel" 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>
<h4 class="modal-title" id="taxModalLabel">Tax Rates</h4>
2013-11-26 13:45:07 +01:00
</div>
2013-12-27 10:10:32 +01:00
2014-01-08 21:09:47 +01:00
<div style="background-color: #EEEEEE" 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>
<th class="hide-border">Name</th>
<th class="hide-border">Rate</th>
<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:10px" class="hide-border"></td>
<td style="width:60px">
<input onkeyup="onTaxRateChange()" data-bind="value: name, valueUpdate: 'afterkeydown'" class="form-control" onchange="refreshPDF()"//>
</td>
<td style="width:60px">
2013-12-29 12:28:44 +01:00
<input onkeyup="onTaxRateChange()" data-bind="value: prettyRate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
2013-12-27 10:10:32 +01:00
</td>
<td style="width:10px; cursor:pointer" class="hide-border td-icon">
2014-01-02 00:12:33 +01:00
&nbsp;<i data-bind="click: $root.removeTaxRate, visible: actionsVisible() &amp;&amp; !isEmpty()" class="fa fa-minus-circle" title="Remove item"/>
2013-12-27 10:10:32 +01:00
</td>
</tr>
</tbody>
</table>
&nbsp;
2013-12-31 20:49:54 +01:00
{{ Former::checkbox('invoice_taxes')->text('Enable specifying an <b>invoice tax</b>')
2014-01-01 16:23:32 +01:00
->label('Settings')->data_bind('checked: $root.invoice_taxes, enable: $root.tax_rates().length > 1') }}
2014-01-08 21:09:47 +01:00
{{ Former::checkbox('invoice_item_taxes')->text('Enable specifying <b>line item taxes</b>')
2014-01-01 16:23:32 +01:00
->label('&nbsp;')->data_bind('checked: $root.invoice_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-01-01 16:23:32 +01:00
<button type="button" class="btn btn-primary" data-bind="click: $root.taxFormComplete">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>
<h4 class="modal-title" id="recurringModalLabel">Recurring Invoices</h4>
</div>
<div style="background-color: #EEEEEE; padding-left: 16px; padding-right: 16px">
&nbsp;
<p>Recurring invoices are automatically sent.</p>
<p>Use :MONTH, :QUARTER or :YEAR for dynamic dates. </p>
<p>Basic math works as well. ie, :MONTH-1. </p>
&nbsp;
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
2013-11-26 13:45:07 +01:00
{{ Former::close() }}
2014-01-01 16:23:32 +01:00
</div>
2013-11-26 13:45:07 +01:00
<script type="text/javascript">
2014-01-08 21:09:47 +01:00
function showSignUp() {
$('#signUpModal').modal('show');
}
2014-01-15 15:01:24 +01:00
function showLearnMore() {
$('#recurringModal').modal('show');
}
2013-11-26 13:45:07 +01:00
$(function() {
2013-12-05 21:25:20 +01:00
$('#country_id').combobox();
2013-12-24 22:27:36 +01:00
$('[rel=tooltip]').tooltip();
2013-12-05 21:25:20 +01:00
2014-01-02 14:21:15 +01:00
$('#invoice_date, #due_date, #start_date, #end_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
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);
2013-12-17 14:14:47 +01:00
if (clientId > 0) {
model.loadClient(clientMap[clientId]);
} else {
2014-01-02 09:27:48 +01:00
model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
2013-12-08 14:32:49 +01:00
}
2013-12-30 21:17:45 +01:00
refreshPDF();
2014-01-06 19:03:00 +01:00
}); //.trigger('change');
2013-11-26 13:45:07 +01:00
$('#terms, #public_notes, #invoice_number, #invoice_date, #due_date, #po_number, #discount, #currency_id, #invoice_design_id').change(function() {
2013-12-30 21:17:45 +01:00
refreshPDF();
});
2013-12-24 22:27:36 +01:00
$('.country_select input.form-control').on('change', function(e) {
var countryId = parseInt($('input[name=country_id]').val(), 10);
2014-01-01 18:34:37 +01:00
model.invoice().client().country_id(countryId);
2013-12-24 22:27:36 +01:00
});
2014-01-06 19:03:00 +01:00
2014-01-02 09:27:48 +01:00
@if ($client || $invoice)
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);
refreshPDF();
}
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
});
$('#primaryActions > button:first').click(function() {
2013-12-11 21:33:44 +01:00
onSaveClick();
2013-12-03 18:32:33 +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
refreshPDF();
@endif
2014-01-02 00:12:33 +01:00
var client = model.invoice().client();
setComboboxValue($('.client_select'),
client.public_id(),
client.name.display());
2014-01-06 19:03:00 +01:00
2013-12-17 14:14:47 +01:00
});
2013-11-26 13:45:07 +01:00
function applyComboboxListeners() {
2013-12-31 10:43:39 +01:00
$('.invoice-table input, .invoice-table select, .invoice-table textarea').on('blur', function() {
//if (value != $(this).val()) refreshPDF();
refreshPDF();
});
var value;
$('.datalist').on('focus', function() {
value = $(this).val();
}).on('blur', function() {
if (value != $(this).val()) refreshPDF();
2013-12-03 18:32:33 +01:00
}).on('input', function() {
var key = $(this).val();
for (var i=0; i<products.length; i++) {
var product = products[i];
2013-12-04 17:20:14 +01:00
if (product.product_key == key) {
var model = ko.dataFor(this);
2013-12-29 12:28:44 +01:00
//model.notes(product.notes);
//model.cost(product.cost);
//model.qty(product.qty);
break;
}
}
});
}
2013-11-26 13:45:07 +01:00
function createInvoiceModel() {
2014-01-06 19:03:00 +01:00
var invoice = ko.toJS(model).invoice;
@if (file_exists($account->getLogoPath()))
invoice.image = "{{ HTML::image_data($account->getLogoPath()) }}";
invoice.imageWidth = {{ $account->getLogoWidth() }};
invoice.imageHeight = {{ $account->getLogoHeight() }};
@endif
2013-11-26 13:45:07 +01:00
return invoice;
}
2014-01-01 00:50:13 +01:00
/*
2013-11-26 13:45:07 +01:00
function refreshPDF() {
2013-12-07 19:45:00 +01:00
setTimeout(function() {
_refreshPDF();
}, 100);
}
2014-01-01 00:50:13 +01:00
*/
2013-12-07 19:45:00 +01:00
2014-01-14 12:52:56 +01:00
var isRefreshing = false;
2014-02-18 14:20:12 +01:00
var needsRefresh = false;
2014-01-01 00:50:13 +01:00
function refreshPDF() {
2013-11-26 13:45:07 +01:00
var invoice = createInvoiceModel();
2013-12-11 21:33:44 +01:00
var doc = generatePDF(invoice);
2014-01-14 12:52:56 +01:00
if (!doc) return;
2013-12-11 21:33:44 +01:00
var string = doc.output('datauristring');
2013-12-31 00:19:17 +01:00
2014-01-27 23:40:40 +01:00
if (isFirefox || (isChrome && !isChromium)) {
2013-12-31 00:19:17 +01:00
$('#theFrame').attr('src', string).show();
} else {
2014-01-14 12:52:56 +01:00
if (isRefreshing) {
2014-02-18 14:20:12 +01:00
needsRefresh = true;
2014-01-14 12:52:56 +01:00
return;
}
isRefreshing = true;
2013-12-31 00:19:17 +01:00
var pdfAsArray = convertDataURIToBinary(string);
PDFJS.getDocument(pdfAsArray).then(function getPdfHelloWorld(pdf) {
pdf.getPage(1).then(function getPageHelloWorld(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('theCanvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({canvasContext: context, viewport: viewport});
$('#theCanvas').show();
2014-01-14 12:52:56 +01:00
isRefreshing = false;
2014-02-18 14:20:12 +01:00
if (needsRefresh) {
needsRefresh = false;
refreshPDF();
}
2013-12-31 00:19:17 +01:00
});
});
}
2013-12-07 19:45:00 +01:00
}
2013-11-26 13:45:07 +01:00
function onDownloadClick() {
var invoice = createInvoiceModel();
var doc = generatePDF(invoice);
2013-12-08 20:08:17 +01:00
doc.save('Invoice-' + $('#invoice_number').val() + '.pdf');
2013-11-26 13:45:07 +01:00
}
function onEmailClick() {
2014-02-02 21:16:31 +01:00
@if (Auth::user()->confirmed)
2014-02-02 21:13:28 +01:00
if (confirm('Are you sure you want to email this invoice?')) {
$('#action').val('email');
$('.main_form').submit();
2013-11-26 13:45:07 +01:00
}
2014-02-02 21:16:31 +01:00
@else
$('#action').val('email');
$('.main_form').submit();
@endif
2013-11-26 13:45:07 +01:00
}
2013-12-11 21:33:44 +01:00
function onSaveClick() {
$('.main_form').submit();
}
function onCloneClick() {
$('#action').val('clone');
$('.main_form').submit();
}
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() {
$('#action').val('archive');
$('.main_form').submit();
}
function onDeleteClick() {
if (confirm('Are you sure you want to delete this invoice?')) {
$('#action').val('delete');
$('.main_form').submit();
}
}
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
if (model.enable.save() != 'enabled') {
return;
}
$('.main_form').submit();
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;
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);
2014-01-08 00:59:06 +01:00
self.setDueDate();
}
self.setDueDate = function() {
var paymentTerms = parseInt(self.invoice().client().payment_terms());
if (paymentTerms && !self.invoice().due_date())
{
var dueDate = $('#invoice_date').datepicker('getDate');
dueDate.setDate(dueDate.getDate() + paymentTerms);
self.invoice().due_date(dueDate);
}
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' }});
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));
2014-01-02 00:12:33 +01:00
if (parseFloat(rate) > 0) taxRate.is_deleted(true);
2014-01-01 18:34:37 +01:00
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;
$('#taxModal').modal('hide');
}
self.showClientForm = function() {
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() {
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();
if (!email || !isValidEmailAddress(email)) {
isValid = false;
}
});
if (!isValid) {
$('#emailError').css( "display", "inline" );
return;
}
var email = $('#email0').val();
2014-01-01 16:23:32 +01:00
var firstName = $('#first_name').val();
var lastName = $('#last_name').val();
var name = $('#name').val();
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-01 16:23:32 +01:00
if (name) {
//
} else if (firstName || lastName) {
name = firstName + ' ' + lastName;
} else {
name = email;
}
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
refreshPDF();
model.clientBackup = false;
$('#clientModal').modal('hide');
2014-01-08 21:09:47 +01:00
$('#invoice_number').focus();
2014-01-01 16:23:32 +01:00
}
2014-01-02 00:12:33 +01:00
self.enable = {};
self.enable.save = ko.computed(function() {
var isValid = false;
for (var i=0; i<self.invoice().client().contacts().length; i++) {
var contact = self.invoice().client().contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
} else {
isValid = false;
break;
}
}
return isValid ? "enabled" : "disabled";
});
self.enable.email = ko.computed(function() {
var isValid = false;
var sendTo = false;
2014-01-06 19:03:00 +01:00
var client = self.invoice().client();
for (var i=0; i<client.contacts().length; i++) {
var contact = client.contacts()[i];
2014-01-02 00:12:33 +01:00
if (isValidEmailAddress(contact.email())) {
isValid = true;
2014-01-06 19:03:00 +01:00
if (contact.send_invoice() || client.contacts().length == 1) {
2014-01-02 00:12:33 +01:00
sendTo = true;
}
} else {
isValid = false;
break;
}
}
return isValid && sendTo ? "enabled" : "disabled";
});
self.clientLinkText = ko.computed(function() {
2014-02-01 21:01:32 +01:00
if (self.invoice().client().public_id())
{
return 'Edit client details';
}
else
{
if (clients.length > {{ MAX_NUM_CLIENTS}})
{
return '';
}
else
{
return 'Create new client';
}
}
});
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());
2014-01-06 19:03:00 +01:00
self.account = {{ $account }};
2014-01-01 16:23:32 +01:00
this.id = ko.observable('');
self.discount = ko.observable('');
self.frequency_id = ko.observable('');
2014-01-15 15:01:24 +01:00
//self.currency_id = ko.observable({{ $client && $client->currency_id ? $client->currency_id : Session::get(SESSION_CURRENCY) }});
2014-02-18 22:56:18 +01:00
self.terms = ko.observable(wordWrapText('{{ str_replace(["\r\n","\r","\n"], '\n', $account->invoice_terms) }}', 340));
2014-01-08 21:09:47 +01:00
self.set_default_terms = 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('');
2014-01-01 16:23:32 +01:00
self.tax_name = ko.observable();
self.tax_rate = ko.observable();
self.is_recurring = ko.observable(false);
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);
self.invoice_design_id = ko.observable({{ $account->invoice_design_id }});
2013-12-31 20:49:54 +01:00
self.mapping = {
2014-01-01 18:34:37 +01:00
'client': {
create: function(options) {
return new ClientModel(options.data);
}
},
'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();
self.invoice_items.push(itemModel);
applyComboboxListeners();
}
if (data) {
ko.mapping.fromJS(data, self.mapping, self);
2014-02-03 11:34:58 +01:00
self.is_recurring(parseInt(data.is_recurring));
2014-01-01 18:34:37 +01:00
} else {
self.addItem();
}
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() {
2013-12-30 21:17:45 +01:00
$('#terms').height(this.terms().split('\n').length * 36);
2013-12-15 13:55:50 +01:00
return this.terms();
},
write: function(value) {
2013-12-30 21:17:45 +01:00
value = wordWrapText(value, 340);
2013-12-15 13:55:50 +01:00
self.terms(value);
2013-12-30 21:17:45 +01:00
$('#terms').height(value.split('\n').length * 36);
},
owner: this
});
2013-12-31 20:49:54 +01:00
2013-12-30 21:17:45 +01:00
self.wrapped_notes = ko.computed({
read: function() {
$('#public_notes').height(this.public_notes().split('\n').length * 36);
return this.public_notes();
},
write: function(value) {
value = wordWrapText(value, 340);
self.public_notes(value);
$('#public_notes').height(value.split('\n').length * 36);
2013-12-15 13:55:50 +01:00
},
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);
2013-11-26 13:45:07 +01:00
refreshPDF();
}
2014-01-01 16:23:32 +01:00
this.totals = ko.observable();
2013-12-31 20:49:54 +01:00
2014-01-01 16:23:32 +01:00
this.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-01-01 16:23:32 +01:00
this.totals.subtotal = ko.computed(function() {
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-01-01 16:23:32 +01:00
this.totals.rawDiscounted = ko.computed(function() {
return self.totals.rawSubtotal() * (self.discount()/100);
2013-12-31 20:49:54 +01:00
});
2013-11-26 13:45:07 +01:00
2014-01-01 16:23:32 +01:00
this.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() {
var total = self.totals.rawSubtotal();
2013-12-31 20:49:54 +01:00
var discount = parseFloat(self.discount());
if (discount > 0) {
total = total * ((100 - discount)/100);
}
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) {
var tax = 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 {
return formatMoney(0);
}
});
2014-01-08 00:59:06 +01:00
this.totals.rawPaidToDate = ko.computed(function() {
return self.amount() - self.balance();
});
this.totals.paidToDate = ko.computed(function() {
var total = self.totals.rawPaidToDate();
2014-01-15 15:01:24 +01:00
return total > 0 ? formatMoney(total, self.client().currency_id()) : '';
2014-01-08 00:59:06 +01:00
});
2013-12-31 20:49:54 +01:00
2014-01-01 16:23:32 +01:00
this.totals.total = ko.computed(function() {
var total = self.totals.rawSubtotal();
2013-11-26 13:45:07 +01:00
2013-12-11 21:33:44 +01:00
var discount = parseFloat(self.discount());
2013-11-26 13:45:07 +01:00
if (discount > 0) {
total = total * ((100 - discount)/100);
}
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-02-04 08:07:16 +01:00
total = NINJA.parseFloat(total) + (total * (taxRate/100));
2013-12-31 20:49:54 +01:00
}
2014-01-08 00:59:06 +01:00
var paid = self.totals.rawPaidToDate();
if (paid > 0) {
total -= paid;
}
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
});
self.onDragged = function(item) {
refreshPDF();
2014-01-01 00:50:13 +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('');
self.work_phone = 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('');
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) {
return new ContactModel(options.data);
}
}
}
2014-01-02 00:12:33 +01:00
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() {
var contact = new ContactModel();
if (self.contacts().length == 0) {
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-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
}
return str + self.email();
2014-01-02 09:27:48 +01:00
});
2014-01-09 20:00:08 +01:00
2014-01-02 09:27:48 +01:00
if (data) {
ko.mapping.fromJS(data, {}, this);
}
}
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 () {
return this.rate() ? parseFloat(this.rate()) : '';
},
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() : '';
2014-01-02 00:12:33 +01:00
var rate = self.rate() ? parseFloat(self.rate()) + '% ' : '';
2014-01-01 16:23:32 +01:00
return rate + name;
},
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;
2013-11-26 13:45:07 +01:00
this.product_key = ko.observable('');
this.notes = ko.observable('');
2013-12-31 00:19:17 +01:00
this.cost = ko.observable(0);
this.qty = ko.observable(0);
2014-01-01 16:23:32 +01:00
self.tax_name = ko.observable('');
self.tax_rate = ko.observable(0);
2013-11-26 13:45:07 +01:00
this.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);
2014-01-02 00:12:33 +01:00
//if (this.cost()) this.cost(formatMoney(this.cost(), model ? model.invoice().currency_id() : 1, true));
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) {
value = wordWrapText(value);
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() {
2014-02-04 08:07:16 +01:00
var cost = NINJA.parseFloat(self.cost());
var qty = NINJA.parseFloat(self.qty());
var taxRate = NINJA.parseFloat(self.tax_rate());
2013-12-29 00:33:48 +01:00
var value = cost * qty;
if (taxRate > 0) {
value += value * (taxRate/100);
}
2013-11-26 13:45:07 +01:00
return value ? value : '';
2013-12-29 00:33:48 +01:00
});
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) : '';
}
2013-11-26 13:45:07 +01:00
});
this.hideActions = function() {
this.actionsVisible(false);
}
this.showActions = function() {
this.actionsVisible(true);
}
2013-12-01 13:22:08 +01:00
this.isEmpty = function() {
2013-12-29 00:33:48 +01:00
return !self.product_key() && !self.notes() && !self.cost() && !self.qty();
2013-12-01 13:22:08 +01:00
}
2013-12-29 00:33:48 +01:00
2014-01-01 18:34:37 +01:00
this.onSelect = function(){
2013-12-29 00:33:48 +01:00
}
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
$('.word-wrap').each(function(index, input) {
$(input).height($(input).val().split('\n').length * 22);
});
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();
}
}
var products = {{ $products }};
2013-12-07 19:45:00 +01:00
var clients = {{ $clients }};
var clientMap = {};
2013-12-30 21:17:45 +01:00
var $clientSelect = $('select#client');
2013-12-07 19:45:00 +01:00
for (var i=0; i<clients.length; i++) {
var client = clients[i];
2013-12-15 13:55:50 +01:00
for (var j=0; j<client.contacts.length; j++) {
var contact = client.contacts[j];
contact.send_invoice = contact.is_primary;
}
2013-12-07 19:45:00 +01:00
clientMap[client.public_id] = client;
2013-12-30 21:17:45 +01:00
$clientSelect.append(new Option(getClientDisplayName(client), client.public_id));
2013-12-07 19:45:00 +01:00
}
2014-01-01 18:34:37 +01:00
@if ($data)
2014-01-02 00:12:33 +01: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)
2014-01-02 00:12:33 +01:00
model.addTaxRate({{ $taxRate }});
2014-01-01 18:34:37 +01:00
@endforeach
2014-01-01 16:23:32 +01:00
@if ($invoice)
2014-01-01 18:34:37 +01:00
var invoice = {{ $invoice }};
ko.mapping.fromJS(invoice, model.invoice().mapping, model.invoice);
2014-02-03 12:15:28 +01:00
if (model.invoice().is_recurring() === '0') {
model.invoice().is_recurring(false);
}
2014-01-01 16:23:32 +01:00
var invitationContactIds = {{ json_encode($invitationContactIds) }};
var client = clientMap[invoice.client.public_id];
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-01 18:34:37 +01:00
}
2014-01-02 00:12:33 +01:00
model.invoice().addItem();
//model.addTaxRate();
2014-01-01 16:23:32 +01:00
@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-02-04 08:07:16 +01:00
item.cost(NINJA.parseFloat(item.cost()) > 0 ? formatMoney(item.cost(), model.invoice().client().currency_id(), 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
if (!model.invoice().discount()) model.invoice().discount('');
ko.applyBindings(model);
2013-11-26 13:45:07 +01:00
2014-02-03 12:15:28 +01:00
2013-11-26 13:45:07 +01:00
</script>
@stop