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

751 lines
24 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
<script type="text/javascript" src="{{ asset('js/pdf_viewer.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/compatibility.js') }}"></script>
@stop
2013-11-26 13:45:07 +01:00
@section('content')
2013-12-07 19:45:00 +01:00
2013-11-26 13:45:07 +01:00
<p>&nbsp;</p>
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-10 23:10:43 +01:00
'product_key' => 'max:14',
2013-11-26 22:45:10 +01:00
)); }}
<div class="row" style="min-height:195px" onkeypress="formEnterClick(event)">
2013-12-24 07:22:16 +01:00
<div class="col-md-5" id="col_1">
{{ Former::select('client')->addOption('', '')->fromQuery($clients, 'name', 'public_id')->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">
<a href="#" data-bind="click: showClientForm, text: showClientText"></a>
</div>
</div>
<div data-bind="with: client">
<div class="form-group" data-bind="visible: contacts().length > 1, 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'}">
<span data-bind="text: fullName"/>
</label>
</div>
</div>
</div>
2013-12-15 13:55:50 +01:00
{{ Former::textarea('terms')->data_bind("value: wrapped_terms, valueUpdate: 'afterkeydown'") }}
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()">
{{ Former::text('invoice_number')->label('Invoice #')->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") }}
{{ Former::text('invoice_date')->data_bind("value: invoice_date, valueUpdate: 'afterkeydown'") }}
{{ Former::text('due_date')->data_bind("value: due_date, valueUpdate: 'afterkeydown'") }}
2013-12-10 23:10:43 +01:00
{{-- Former::text('invoice_date')->label('Invoice Date')->data_date_format('yyyy-mm-dd') --}}
</div>
<div data-bind="visible: is_recurring">
{{ Former::select('frequency_id')->label('How often')->options($frequencies)->data_bind("value: frequency_id") }}
{{ Former::text('start_date')->data_bind("value: start_date, valueUpdate: 'afterkeydown'") }}
{{ Former::text('end_date')->data_bind("value: end_date, valueUpdate: 'afterkeydown'") }}
2013-12-10 23:10:43 +01:00
</div>
2013-12-24 07:22:16 +01:00
<div data-bind="visible: invoice_status_id() < CONSTS.INVOICE_STATUS_SENT">
2013-12-24 22:27:36 +01:00
{{ Former::checkbox('recurring')->text('Enable | <a href="#" rel="tooltip" data-toggle="tooltip" title="Recurring invoices are automatically sent. Use :MONTH, :QUARTER or :YEAR for dynamic dates. Basic math works as well. ie, :MONTH-1.">Learn more</a>')->data_bind("checked: is_recurring")
2013-12-24 07:22:16 +01:00
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::timestampToDateString($invoice->last_sent_date) : '') }}
</div>
@if ($invoice && $invoice->recurring_invoice_id)
<div style="padding-top: 6px">
Created by a {{ link_to('/invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') }}
</div>
@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
<div class="col-md-3" id="col_2">
{{ Former::text('po_number')->label('PO&nbsp;number')->data_bind("value: po_number, valueUpdate: 'afterkeydown'") }}
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
2013-12-24 22:27:36 +01:00
{{ Former::text('currency')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
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
{{ Former::hidden('data')->data_bind("value: ko.toJSON(model)") }}
<table class="table invoice-table" style="margin-bottom: 0px !important">
2013-11-26 22:45:10 +01:00
<thead>
<tr>
<th class="hide-border"></th>
<th>Item</th>
<th>Description</th>
<th>Unit Cost</th>
<th>Quantity</th>
2013-11-26 22:45:10 +01:00
<th>Line&nbsp;Total</th>
<th class="hide-border"></th>
</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">
2013-12-08 14:32:49 +01:00
<td style="width:20px;" class="hide-border td-icon">
<i 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>
2013-12-01 13:22:08 +01:00
<td style="width:120px">
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key')->onkeyup('onChange()')
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
2013-11-26 22:45:10 +01:00
</td>
<td style="width:300px">
2013-12-15 13:55:50 +01:00
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control word-wrap" onchange="refreshPDF()"></textarea>
2013-11-26 22:45:10 +01:00
</td>
<td style="width:100px">
2013-12-01 13:22:08 +01:00
<input onkeyup="onChange()" data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
2013-11-26 22:45:10 +01:00
</td>
<td style="width:80px">
2013-12-01 13:22:08 +01:00
<input onkeyup="onChange()" data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
2013-11-26 22:45:10 +01:00
</td>
<!--
<td style="width:100px">
<input data-bind="value: tax, valueUpdate: 'afterkeydown'"/>
</td>
-->
2013-12-07 19:45:00 +01:00
<td style="width:100px;text-align: right;padding-top:9px !important">
2013-11-26 22:45:10 +01:00
<span data-bind="text: total"></span>
</td>
2013-12-08 14:32:49 +01:00
<td style="width:20px; cursor:pointer" class="hide-border td-icon">
&nbsp;<i 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>
<tfoot>
2013-12-01 21:58:25 +01:00
<tr>
<td class="hide-border"></td>
<td colspan="2"/>
2013-11-26 22:45:10 +01:00
<td colspan="2">Subtotal</td>
<td style="text-align: right"><span data-bind="text: subtotal"/></td>
</tr>
2013-12-01 21:58:25 +01:00
<tr>
<td class="hide-border"></td>
<td colspan="2" class="hide-border"/>
<td colspan="2">Paid to Date</td>
<td style="text-align: right"></td>
</tr>
2013-11-26 22:45:10 +01:00
<tr data-bind="visible: discount() > 0">
2013-12-01 21:58:25 +01:00
<td class="hide-border"></td>
<td colspan="2" class="hide-border"/>
2013-11-26 22:45:10 +01:00
<td colspan="2">Discount</td>
<td style="text-align: right"><span data-bind="text: discounted"/></td>
</tr>
<tr>
<td class="hide-border"></td>
2013-12-01 21:58:25 +01:00
<td colspan="2" class="hide-border"/>
2013-12-11 21:33:44 +01:00
<td colspan="2"><b>Balance Due</b></td>
2013-11-26 22:45:10 +01:00
<td style="text-align: right"><span data-bind="text: total"/></td>
</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') }}
@if ($invoice)
2013-12-05 21:25:20 +01:00
{{ Former::text('id') }}
2013-12-07 19:45:00 +01:00
@endif
</div>
2013-12-11 21:33:44 +01:00
{{ Button::normal('Download PDF', array('onclick' => 'onDownloadClick()')) }}
2013-12-07 19:45:00 +01:00
@if ($invoice)
2013-12-11 21:33:44 +01:00
{{ DropdownButton::primary('Save Invoice',
2013-12-03 18:32:33 +01:00
Navigation::links(
array(
2013-12-11 21:33:44 +01:00
array('Save Invoice', "javascript:onSaveClick()"),
array('Clone Invoice', "javascript:onCloneClick()"),
2013-12-03 18:32:33 +01:00
array(Navigation::DIVIDER),
array('Archive Invoice', "javascript:onArchiveClick()"),
array('Delete Invoice', "javascript:onDeleteClick()"),
)
)
, array('id'=>'actionDropDown','style'=>'text-align:left'))->split(); }}
@else
2013-12-11 21:33:44 +01:00
{{ Button::primary_submit('Save Invoice') }}
2013-12-03 18:32:33 +01:00
@endif
2013-12-10 23:10:43 +01:00
{{ Button::primary('Send Email', array('id' => 'email_button', 'onclick' => 'onEmailClick()')) }}
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> -->
2013-12-11 21:33:44 +01:00
<iframe id="theIFrame" frameborder="1" width="100%" height="500"></iframe>
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
2013-11-26 13:45:07 +01:00
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" 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>
<h4 class="modal-title" id="myModalLabel">New Client</h4>
</div>
2013-12-05 21:25:20 +01:00
<div class="row" data-bind="with: client" style="padding-left:16px;padding-right:16px" onkeypress="modalEnterClick(event)">
<div class="col-md-6">
2013-12-05 21:25:20 +01:00
{{ Former::legend('Organization') }}
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown'") }}
2013-12-15 13:55:50 +01:00
{{ 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'") }}
{{ Former::text('address2')->label('Apt/Floor')->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'") }}
2013-12-24 22:27:36 +01:00
{{ Former::select('country_id')->addOption('','')->label('Country')->addGroupClass('country_select')
->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") }}
2013-12-05 21:25:20 +01:00
</div>
<div class="col-md-6">
{{ 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'") }}
{{ 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') }}
2013-12-24 22:27:36 +01:00
{{ Former::select('client_size_id')->addOption('','')->label('Size')->data_bind('value: client_size_id')
->fromQuery($clientSizes, 'name', 'id')->select($client ? $client->client_size_id : '') }}
2013-12-24 22:27:36 +01:00
{{ Former::select('client_industry_id')->addOption('','')->label('Industry')->data_bind('value: client_industry_id')
->fromQuery($clientIndustries, 'name', 'id')->select($client ? $client->client_industry_id : '') }}
{{ Former::textarea('notes') }}
2013-12-05 21:25:20 +01:00
</div>
</div>
2013-11-26 13:45:07 +01:00
<div class="modal-footer">
2013-12-05 21:25:20 +01:00
<span class="error-block" id="nameError" style="display:none;float:left">Please provide a value for the name field.</span><span>&nbsp;</span>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" data-bind="click: clientFormComplete">Done</button>
2013-11-26 13:45:07 +01:00
</div>
2013-12-05 21:25:20 +01:00
2013-11-26 13:45:07 +01:00
</div>
</div>
</div>
{{ Former::close() }}
<script type="text/javascript">
2013-11-26 13:45:07 +01:00
$(function() {
2013-12-11 21:33:44 +01:00
$('form').change(refreshPDF);
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
$('#invoice_date').datepicker({
2013-11-26 13:45:07 +01:00
autoclose: true,
todayHighlight: true
});
2013-12-10 18:18:35 +01:00
$('#due_date, #start_date, #end_date').datepicker({
2013-11-28 17:40:13 +01:00
autoclose: true,
todayHighlight: true
});
2013-11-26 13:45:07 +01:00
var $input = $('select#client');
$input.combobox();
2013-12-08 14:32:49 +01:00
$('.client_select input.form-control').on('change', function(e) {
2013-12-11 21:33:44 +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 {
2013-12-17 14:14:47 +01:00
model.client.public_id(0); // TODO_FIX
2013-12-08 14:32:49 +01:00
}
}).trigger('change');
2013-11-26 13:45:07 +01:00
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);
model.client.country_id(countryId);
});
2013-11-26 13:45:07 +01:00
@if ($client)
2013-12-08 20:08:17 +01:00
$('#invoice_number').focus();
2013-11-26 13:45:07 +01:00
@else
2013-12-07 19:45:00 +01:00
//$('[name="client_combobox"]').focus();
2013-11-26 13:45:07 +01:00
@endif
$('#myModal').on('hidden.bs.modal', function () {
2013-12-17 14:14:47 +01:00
if (model.clientBackup) {
console.log("Loading backup");
//console.log(model.clientBackup);
model.loadClient(model.clientBackup);
refreshPDF();
}
2013-11-26 13:45:07 +01:00
})
2013-12-17 14:14:47 +01:00
2013-11-26 13:45:07 +01:00
$('#myModal').on('shown.bs.modal', function () {
2013-12-17 14:14:47 +01:00
$('#name').focus();
2013-11-26 13:45:07 +01:00
})
2013-12-03 18:32:33 +01:00
$('#actionDropDown > 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');
applyComboboxListeners();
refreshPDF();
2013-12-17 14:14:47 +01:00
});
2013-11-26 13:45:07 +01:00
function applyComboboxListeners() {
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);
model.notes(product.notes);
model.cost(product.cost);
model.qty(product.qty);
break;
}
}
});
}
2013-11-26 13:45:07 +01:00
function createInvoiceModel() {
var invoice = ko.toJS(model);
@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;
}
function refreshPDF() {
2013-12-07 19:45:00 +01:00
setTimeout(function() {
_refreshPDF();
}, 100);
}
2013-12-08 14:32:49 +01:00
2013-12-07 19:45:00 +01:00
function _refreshPDF() {
console.log("refreshPDF");
2013-11-26 13:45:07 +01:00
var invoice = createInvoiceModel();
2013-12-11 21:33:44 +01:00
var doc = generatePDF(invoice);
/*
2013-12-08 14:32:49 +01:00
var string = doc.output('dataurlstring');
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);
2013-12-11 21:33:44 +01:00
var canvas = document.getElementById('theCanvas');
2013-12-08 14:32:49 +01:00
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({canvasContext: context, viewport: viewport});
});
2013-12-08 20:08:17 +01:00
});
2013-12-11 21:33:44 +01:00
*/
2013-12-08 14:32:49 +01:00
2013-12-11 21:33:44 +01:00
var string = doc.output('datauristring');
$('#theIFrame').attr('src', string);
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() {
if (confirm('Are you sure you want to email this invoice?')) {
2013-12-03 18:32:33 +01:00
$('#action').val('email');
2013-11-26 13:45:07 +01:00
$('.main_form').submit();
}
}
2013-12-11 21:33:44 +01:00
function onSaveClick() {
$('.main_form').submit();
}
function onCloneClick() {
$('#action').val('clone');
$('.main_form').submit();
}
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
}
2013-11-26 13:45:07 +01:00
event.preventDefault();
$('.main_form').submit();
return false;
2013-11-26 13:45:07 +01:00
}
}
2013-12-05 21:25:20 +01:00
function modalEnterClick(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;
}
}
function InvoiceModel() {
var self = this;
this.client = new ClientModel();
self.discount = ko.observable('');
self.frequency_id = ko.observable('');
2013-12-15 13:55:50 +01:00
self.terms = ko.observable('');
self.po_number = ko.observable('');
self.invoice_date = ko.observable('');
2013-12-15 13:55:50 +01:00
self.invoice_number = ko.observable('');
self.due_date = ko.observable('');
self.start_date = ko.observable('');
self.end_date = ko.observable('');
self.is_recurring = ko.observable(false);
self.invoice_status_id = ko.observable(0);
self.invoice_items = ko.observableArray();
self.mapping = {
'invoice_items': {
create: function(options) {
return new ItemModel(options.data);
}
}
}
2013-12-17 14:14:47 +01:00
self.loadClient = function(client) {
//console.log(client);
ko.mapping.fromJS(client, model.client.mapping, model.client);
}
2013-12-15 13:55:50 +01:00
self.wrapped_terms = ko.computed({
read: function() {
return this.terms();
},
write: function(value) {
value = wordWrapText(value, 250);
self.terms(value);
$('#terms').height(value.split('\n').length * 22);
},
owner: this
});
self.showClientText = ko.computed(function() {
return self.client.public_id() ? 'Edit client details' : 'Create new client';
});
self.showClientForm = function() {
2013-12-17 14:14:47 +01:00
self.clientBackup = ko.mapping.toJS(self.client);
console.log(self.clientBackup);
if (self.client.public_id() == 0) {
$('#myModal input').val('');
$('#myModal #country_id').val('');
}
$('#nameError').css( "display", "none" );
$('#myModal').modal('show');
}
self.clientFormComplete = function() {
var name = $('#name').val();
if (!name) {
if (!name) $('#nameError').css( "display", "inline" );
return;
}
$('select#client').combobox('setSelected');
if (self.client.public_id() == 0) {
self.client.public_id(-1);
}
$('.client_select input.form-control').val(name);
$('.client_select .combobox-container').addClass('combobox-selected');
$('#nameError').css( "display", "none" );
$('.client_select input.form-control').focus();
refreshPDF();
2013-12-17 14:14:47 +01:00
model.clientBackup = false;
$('#myModal').modal('hide');
}
2013-12-03 18:32:33 +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();
}
self.addItem = function() {
self.invoice_items.push(new ItemModel());
applyComboboxListeners();
2013-11-26 13:45:07 +01:00
}
this.rawSubtotal = ko.computed(function() {
var total = 0;
for(var p = 0; p < self.invoice_items().length; ++p)
2013-11-26 13:45:07 +01:00
{
total += self.invoice_items()[p].rawTotal();
2013-11-26 13:45:07 +01:00
}
return total;
});
this.subtotal = ko.computed(function() {
var total = self.rawSubtotal();
return total > 0 ? formatMoney(total) : '';
});
this.discounted = ko.computed(function() {
var total = self.rawSubtotal() * (self.discount()/100);
return formatMoney(total);
});
this.total = ko.computed(function() {
var total = self.rawSubtotal();
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);
}
return total > 0 ? formatMoney(total) : '';
});
self.onDragged = function(item) {
refreshPDF();
}
}
function ClientModel(data) {
var self = this;
self.public_id = ko.observable(0);
self.name = ko.observable('');
self.work_phone = ko.observable('');
self.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('');
self.client_size_id = ko.observable('');
self.client_industry_id = ko.observable('');
2013-12-15 13:55:50 +01:00
self.website = ko.observable('');
self.contacts = ko.observableArray();
self.mapping = {
'contacts': {
create: function(options) {
return new ContactModel(options.data);
}
}
}
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();
console.log('num: ' + self.contacts().length);
if (self.contacts().length == 0) {
contact.send_invoice(true);
}
self.contacts.push(contact);
return false;
}
self.removeContact = function() {
self.contacts.remove(this);
}
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);
if (data) {
ko.mapping.fromJS(data, {}, this);
}
self.fullName = ko.computed(function() {
return self.first_name() + ' ' + self.last_name();
});
}
function ItemModel(data) {
var self = this;
2013-11-26 13:45:07 +01:00
this.product_key = ko.observable('');
this.notes = ko.observable('');
this.cost = ko.observable();
this.qty = ko.observable();
this.tax = ko.observable();
this.actionsVisible = ko.observable(false);
if (data) {
ko.mapping.fromJS(data, {}, this);
}
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);
onChange();
},
owner: this
});
2013-11-26 13:45:07 +01:00
this.rawTotal = ko.computed(function() {
var cost = parseFloat(self.cost());
2013-12-08 14:32:49 +01:00
var qty = parseFloat(self.qty());
2013-11-26 13:45:07 +01:00
var tax = parseFloat(self.tax());
var value = cost * qty;
if (self.tax() > 0) {
//value = value * ((100 - this.tax())/100);
}
return value ? value : '';
});
this.total = ko.computed(function() {
var total = self.rawTotal();
return total ? formatMoney(total) : '';
});
this.hideActions = function() {
this.actionsVisible(false);
}
this.showActions = function() {
this.actionsVisible(true);
}
2013-12-01 13:22:08 +01:00
this.isEmpty = function() {
return !self.product_key() && !self.notes() && !self.cost() && !self.qty() && !self.tax();
}
}
function onChange()
{
var hasEmpty = false;
for(var i=0; i<model.invoice_items().length; i++) {
var item = model.invoice_items()[i];
2013-12-01 13:22:08 +01:00
if (item.isEmpty()) {
hasEmpty = true;
}
}
if (!hasEmpty) {
model.addItem();
}
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
}
var products = {{ $products }};
2013-12-07 19:45:00 +01:00
var clients = {{ $clients }};
var clientMap = {};
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;
}
window.model = new InvoiceModel();
@if ($invoice)
var invoice = {{ $invoice }};
ko.mapping.fromJS(invoice, model.mapping, model);
if (!model.discount()) model.discount('');
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;
}
@else
2013-12-15 13:55:50 +01:00
model.invoice_number('{{ $invoiceNumber }}');
model.terms(wordWrapText('{{ $account->invoice_terms }}', 250));
@endif
model.invoice_items.push(new ItemModel());
2013-11-26 13:45:07 +01:00
ko.applyBindings(model);
2013-11-26 13:45:07 +01:00
</script>
@stop