1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-15 23:52:33 +01:00
invoiceninja/resources/views/invoices/knockout.blade.php

1150 lines
38 KiB
PHP
Raw Normal View History

2015-10-22 20:48:12 +02:00
<script type="text/javascript">
function ViewModel(data) {
var self = this;
self.showMore = ko.observable(false);
//self.invoice = data ? false : new InvoiceModel();
self.invoice = ko.observable(data ? false : new InvoiceModel());
self.expense_currency_id = ko.observable();
2017-05-08 21:10:39 +02:00
self.products = {!! $products !!};
2015-10-22 20:48:12 +02:00
self.loadClient = function(client) {
ko.mapping.fromJS(client, model.invoice().client().mapping, model.invoice().client);
2015-10-29 15:51:57 +01:00
@if (!$invoice->id)
2015-10-22 20:48:12 +02:00
self.setDueDate();
// copy default note from the client to the invoice
if (client.public_notes) {
model.invoice().public_notes(client.public_notes);
}
2015-10-22 20:48:12 +02:00
@endif
}
self.showMoreFields = function() {
self.showMore(!self.showMore());
}
self.setDueDate = function() {
var paymentTerms = parseInt(self.invoice().client().payment_terms());
if (paymentTerms && paymentTerms != 0 && !self.invoice().due_date()) {
if (paymentTerms == -1) paymentTerms = 0;
var dueDate = $('#invoice_date').datepicker('getDate');
dueDate.setDate(dueDate.getDate() + paymentTerms);
dueDate = moment(dueDate).format("{{ $account->getMomentDateFormat() }}");
$('#due_date').attr('placeholder', dueDate);
} else {
$('#due_date').attr('placeholder', "{{ $invoice->id ? ' ' : $account->present()->dueDatePlaceholder() }}");
}
2015-10-22 20:48:12 +02:00
}
2017-04-03 21:48:47 +02:00
self.clearBlankContacts = function() {
var client = self.invoice().client();
var contacts = client.contacts();
$(contacts).each(function(index, contact) {
if (index > 0 && contact.isBlank()) {
client.contacts.remove(contact);
}
});
}
2015-10-22 20:48:12 +02: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' }});
self.mapping = {
'invoice': {
create: function(options) {
return new InvoiceModel(options.data);
}
},
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
if (data) {
ko.mapping.fromJS(data, self.mapping, self);
}
self.invoice_taxes.show = ko.computed(function() {
2016-03-31 11:29:01 +02:00
if (self.invoice().tax_name1() || self.invoice().tax_name2()) {
2015-10-22 20:48:12 +02:00
return true;
2016-01-10 11:25:05 +01:00
}
2016-03-31 11:29:01 +02:00
return self.invoice_taxes() && {{ count($taxRateOptions) ? 'true' : 'false' }};
2015-10-22 20:48:12 +02:00
});
self.invoice_item_taxes.show = ko.computed(function() {
if (self.invoice_item_taxes() && {{ count($taxRateOptions) ? 'true' : 'false' }}) {
2015-10-22 20:48:12 +02:00
return true;
}
for (var i=0; i<self.invoice().invoice_items().length; i++) {
var item = self.invoice().invoice_items()[i];
2016-03-31 11:29:01 +02:00
if (item.tax_name1() || item.tax_name2()) {
2015-10-22 20:48:12 +02:00
return true;
}
}
return false;
});
self.showClientForm = function() {
trackEvent('/activity', '/view_client_form');
self.clientBackup = ko.mapping.toJS(self.invoice().client);
2015-11-02 23:05:28 +01:00
$('#emailError').css( "display", "none" );
$('#clientModal').modal('show');
2015-10-22 20:48:12 +02:00
}
self.clientFormComplete = function() {
trackEvent('/activity', '/save_client_form');
2015-11-09 12:46:53 +01:00
var email = $("[name='client[contacts][0][email]']").val();
var firstName = $("[name='client[contacts][0][first_name]']").val();
var lastName = $("[name='client[contacts][0][last_name]']").val();
2015-12-29 11:23:50 +01:00
var name = $("[name='client[name]']").val();
2015-10-22 20:48:12 +02:00
if (name) {
//
} else if (firstName || lastName) {
name = firstName + ' ' + lastName;
} else {
name = email;
}
2017-02-21 14:27:04 +01:00
var isValid = name ? true : false;
2016-12-25 09:54:01 +01:00
var contacts = self.invoice().client().contacts();
$(contacts).each(function(item, value) {
2017-02-21 14:27:04 +01:00
if (value.isValid()) {
isValid = true;
2015-10-22 20:48:12 +02:00
}
});
if (!isValid) {
$('#emailError').css( "display", "inline" );
return;
}
if (self.invoice().client().public_id() == 0) {
self.invoice().client().public_id(-1);
self.invoice().client().invoice_number_counter = 1;
self.invoice().client().quote_number_counter = 1;
}
model.setDueDate();
2017-04-03 21:48:47 +02:00
model.clearBlankContacts();
2015-10-22 20:48:12 +02:00
setComboboxValue($('.client_select'), -1, name);
var client = $.parseJSON(ko.toJSON(self.invoice().client()));
setInvoiceNumber(client);
//$('.client_select select').combobox('setSelected');
//$('.client_select input.form-control').val(name);
//$('.client_select .combobox-container').addClass('combobox-selected');
$('#emailError').css( "display", "none" );
refreshPDF(true);
model.clientBackup = false;
$('#clientModal').modal('hide');
}
self.clientLinkText = ko.computed(function() {
if (self.invoice().client().public_id())
{
return "{{ trans('texts.edit_client') }}";
}
else
{
if (clients.length > {{ Auth::user()->getMaxNumClients() }})
{
return '';
}
else
{
return "{{ trans('texts.create_new_client') }}";
}
}
});
2017-07-27 09:08:36 +02:00
self.hasTasksCached = false;
self.hasTasks = ko.computed(function() {
2017-06-20 12:49:49 +02:00
if (self.hasTasksCached) {
return true;
}
invoice = self.invoice();
2017-07-27 09:08:36 +02:00
for (var i=0; i<invoice.invoice_items_with_tasks().length; ++i) {
var item = invoice.invoice_items_with_tasks()[i];
2017-07-27 09:08:36 +02:00
if (! item.isEmpty()) {
2017-06-20 12:49:49 +02:00
self.hasTasksCached = true;
return true;
}
}
return false;
});
self.hasItemsCached = false;
self.forceShowItems = ko.observable(false);
self.hasItems = ko.computed(function() {
if (self.forceShowItems()) {
return true;
}
if (self.hasItemsCached) {
return true;
}
invoice = self.invoice();
for (var i=0; i<invoice.invoice_items_without_tasks().length; ++i) {
var item = invoice.invoice_items_without_tasks()[i];
if (! item.isEmpty()) {
self.hasItemsCached = true;
return true;
}
}
return false;
});
2015-10-22 20:48:12 +02:00
}
function InvoiceModel(data) {
2017-01-03 20:48:40 +01:00
if (data) {
var clientModel = false;
} else {
var clientModel = new ClientModel();
2017-01-04 09:11:32 +01:00
clientModel.id_number("{{ $account->getNextNumber() }}");
2017-01-03 20:48:40 +01:00
}
2015-10-23 13:55:18 +02:00
var self = this;
2017-01-03 20:48:40 +01:00
this.client = ko.observable(clientModel);
this.is_public = ko.observable(0);
2017-05-08 21:10:39 +02:00
self.account = {!! $account !!};
2015-10-22 20:48:12 +02:00
self.id = ko.observable('');
self.discount = ko.observable('');
self.is_amount_discount = ko.observable(0);
2017-06-26 16:19:44 +02:00
self.frequency_id = ko.observable({{ FREQUENCY_MONTHLY }});
2015-10-22 20:48:12 +02:00
self.terms = ko.observable('');
2015-10-29 15:42:05 +01:00
self.default_terms = ko.observable(account.{{ $entityType }}_terms);
self.terms_placeholder = ko.observable({{ (!$invoice->id || $invoice->is_recurring) && $account->{"{$entityType}_terms"} ? "account.{$entityType}_terms" : false}});
2015-10-22 20:48:12 +02:00
self.set_default_terms = ko.observable(false);
self.invoice_footer = ko.observable('');
self.default_footer = ko.observable(account.invoice_footer);
self.footer_placeholder = ko.observable({{ (!$invoice->id || $invoice->is_recurring) && $account->invoice_footer ? 'account.invoice_footer' : false}});
2015-10-22 20:48:12 +02:00
self.set_default_footer = ko.observable(false);
2015-10-23 13:55:18 +02:00
self.public_notes = ko.observable('');
self.private_notes = ko.observable('');
2015-10-22 20:48:12 +02:00
self.po_number = ko.observable('');
2015-10-23 13:55:18 +02:00
self.invoice_date = ko.observable('');
self.invoice_number = ko.observable('');
2015-10-22 20:48:12 +02:00
self.due_date = ko.observable('');
self.recurring_due_date = ko.observable('');
2015-10-23 13:55:18 +02:00
self.start_date = ko.observable('');
self.start_date_orig = ko.observable('');
2015-10-22 20:48:12 +02:00
self.end_date = ko.observable('');
self.last_sent_date = ko.observable('');
2016-03-31 11:29:01 +02:00
self.tax_name1 = ko.observable();
self.tax_rate1 = ko.observable();
2017-01-02 12:38:58 +01:00
self.tax_rate1IsInclusive = ko.observable(0);
2016-03-31 11:29:01 +02:00
self.tax_name2 = ko.observable();
self.tax_rate2 = ko.observable();
2017-01-02 12:38:58 +01:00
self.tax_rate2IsInclusive = ko.observable(0);
2015-10-28 20:22:07 +01:00
self.is_recurring = ko.observable(0);
self.is_quote = ko.observable({{ $entityType == ENTITY_QUOTE ? '1' : '0' }});
2016-05-09 22:29:02 +02:00
self.auto_bill = ko.observable(0);
self.client_enable_auto_bill = ko.observable(false);
2015-10-22 20:48:12 +02:00
self.invoice_status_id = ko.observable(0);
2016-03-23 03:23:45 +01:00
self.documents = ko.observableArray();
self.expenses = ko.observableArray();
2015-10-22 20:48:12 +02:00
self.amount = ko.observable(0);
self.balance = ko.observable(0);
2015-10-23 13:55:18 +02:00
self.invoice_design_id = ko.observable(1);
self.partial = ko.observable(0);
2015-11-02 23:05:28 +01:00
self.has_tasks = ko.observable();
2016-01-10 11:25:05 +01:00
self.has_expenses = ko.observable();
2017-10-25 23:35:38 +02:00
self.partial_due_date = ko.observable('');
2015-10-22 20:48:12 +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.custom_text_value1 = ko.observable();
self.custom_text_value2 = ko.observable();
2017-07-27 09:08:36 +02:00
self.invoice_items_with_tasks = ko.observableArray();
self.invoice_items_without_tasks = ko.observableArray();
self.invoice_items = ko.computed({
read: function () {
return self.invoice_items_with_tasks().concat(self.invoice_items_without_tasks());
},
write: function(data) {
self.invoice_items_with_tasks.removeAll();
self.invoice_items_without_tasks.removeAll();
for (var i=0; i<data.length; i++) {
var item = new ItemModel(data[i]);
if (item.isTask()) {
self.invoice_items_with_tasks.push(item);
} else {
self.invoice_items_without_tasks.push(item);
}
}
},
owner: this
})
2015-10-22 20:48:12 +02:00
self.mapping = {
'client': {
create: function(options) {
return new ClientModel(options.data);
}
},
2016-03-23 03:23:45 +01:00
'documents': {
create: function(options) {
return new DocumentModel(options.data);
}
},
'expenses': {
create: function(options) {
return new ExpenseModel(options.data);
}
},
2015-10-22 20:48:12 +02:00
}
2017-07-27 09:08:36 +02:00
self.addItem = function(isTask) {
2016-09-14 09:39:08 +02:00
if (self.invoice_items().length >= {{ MAX_INVOICE_ITEMS }}) {
return false;
}
2015-10-22 20:48:12 +02:00
var itemModel = new ItemModel();
2017-07-27 09:08:36 +02:00
if (isTask) {
itemModel.invoice_item_type_id({{ INVOICE_ITEM_TYPE_TASK }});
self.invoice_items_with_tasks.push(itemModel);
} else {
self.invoice_items_without_tasks.push(itemModel);
}
2015-12-29 08:52:02 +01:00
applyComboboxListeners();
2015-10-22 20:48:12 +02:00
return itemModel;
}
2016-03-23 03:23:45 +01:00
self.addDocument = function() {
var documentModel = new DocumentModel();
self.documents.push(documentModel);
return documentModel;
}
self.removeDocument = function(doc) {
2017-06-05 16:29:22 +02:00
var public_id = doc && doc.public_id ? doc.public_id() : doc;
if (! public_id) {
return;
}
2016-03-23 03:23:45 +01:00
self.documents.remove(function(document) {
return document.public_id() == public_id;
});
}
2015-10-22 20:48:12 +02:00
if (data) {
2015-12-29 08:52:02 +01:00
ko.mapping.fromJS(data, self.mapping, self);
2015-10-22 20:48:12 +02:00
} else {
self.addItem();
}
2016-03-31 11:29:01 +02:00
this.tax1 = ko.computed({
read: function () {
2017-01-02 12:38:58 +01:00
return self.tax_rate1IsInclusive() + ' ' + self.tax_rate1() + ' ' + self.tax_name1();
2016-03-31 11:29:01 +02:00
},
write: function(value) {
2018-02-19 08:33:43 +01:00
value = value || '';
2017-01-02 12:38:58 +01:00
var parts = value.split(' ');
self.tax_rate1IsInclusive(parts.shift());
self.tax_rate1(parts.shift());
self.tax_name1(parts.join(' '));
2016-03-31 11:29:01 +02:00
}
})
this.tax2 = ko.computed({
2015-10-22 20:48:12 +02:00
read: function () {
2017-01-02 12:38:58 +01:00
return self.tax_rate2IsInclusive() + ' ' + self.tax_rate2() + ' ' + self.tax_name2();
2015-10-22 20:48:12 +02:00
},
write: function(value) {
2018-02-19 08:33:43 +01:00
value = value || '';
2017-01-02 12:38:58 +01:00
var parts = value.split(' ');
self.tax_rate2IsInclusive(parts.shift());
self.tax_rate2(parts.shift());
self.tax_name2(parts.join(' '));
2015-10-22 20:48:12 +02:00
}
})
2015-10-22 20:48:12 +02:00
self.removeItem = function(item) {
2017-07-27 09:08:36 +02:00
if (item.isTask()) {
self.invoice_items_with_tasks.remove(item);
} else {
self.invoice_items_without_tasks.remove(item);
}
2015-10-22 20:48:12 +02:00
refreshPDF(true);
}
self.formatMoney = function(amount) {
2018-03-27 14:12:10 +02:00
/*
var client = $.parseJSON(ko.toJSON(self.client()));
return formatMoneyAccount(amount, self.account, client);
2018-03-27 14:12:10 +02:00
*/
var currencyId = (self.client().currency_id() || account.currency_id) || {{ DEFAULT_CURRENCY }};
var countryId = (self.client().country_id() || account.country_id) || {{ DEFAULT_COUNTRY }};
var decorator = parseInt(account.show_currency_code) ? 'code' : 'symbol';
return formatMoney(amount, currencyId, countryId, decorator);
}
2015-10-22 20:48:12 +02:00
self.totals = ko.observable();
self.totals.rawSubtotal = ko.computed(function() {
var total = 0;
for(var p=0; p < self.invoice_items().length; ++p) {
var item = self.invoice_items()[p];
total += item.totals.rawTotal();
2016-12-11 20:45:40 +01:00
total = roundToTwo(total);
2015-10-22 20:48:12 +02:00
}
return total;
});
self.totals.subtotal = ko.computed(function() {
var total = self.totals.rawSubtotal();
return self.formatMoney(total);
2015-10-22 20:48:12 +02:00
});
self.totals.rawDiscounted = ko.computed(function() {
if (parseInt(self.is_amount_discount())) {
return roundToTwo(self.discount());
} else {
2017-12-30 20:22:03 +01:00
return roundToTwo(self.totals.rawSubtotal() * roundToTwo(self.discount()) / 100);
2015-10-22 20:48:12 +02:00
}
});
self.totals.discounted = ko.computed(function() {
return self.formatMoney(self.totals.rawDiscounted());
2015-10-22 20:48:12 +02:00
});
self.totals.taxAmount = ko.computed(function() {
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;
}
2016-03-31 11:29:01 +02:00
var taxRate1 = parseFloat(self.tax_rate1());
2017-12-03 12:56:10 +01:00
@if ($account->inclusive_taxes)
2018-01-23 11:16:48 +01:00
var tax1 = roundToTwo(total - (total / (1 + (taxRate1 / 100))));
2017-12-03 12:56:10 +01:00
@else
var tax1 = roundToTwo(total * (taxRate1/100));
@endif
2016-03-31 11:29:01 +02:00
var taxRate2 = parseFloat(self.tax_rate2());
2017-12-03 12:56:10 +01:00
@if ($account->inclusive_taxes)
2018-01-23 11:16:48 +01:00
var tax2 = roundToTwo(total - (total / (1 + (taxRate2 / 100))));
2017-12-03 12:56:10 +01:00
@else
var tax2 = roundToTwo(total * (taxRate2/100));
@endif
2016-03-31 11:29:01 +02:00
return self.formatMoney(tax1 + tax2);
2015-10-22 20:48:12 +02: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())) {
2017-12-30 20:22:03 +01:00
lineTotal -= roundToTwo((lineTotal/total) * roundToTwo(self.discount()));
2015-10-22 20:48:12 +02:00
} else {
2017-12-30 20:22:03 +01:00
lineTotal -= roundToTwo(lineTotal * roundToTwo(self.discount()) / 100);
2015-10-22 20:48:12 +02:00
}
}
2017-12-03 12:56:10 +01:00
@if ($account->inclusive_taxes)
2018-01-23 11:16:48 +01:00
var taxAmount = roundToTwo(lineTotal - (lineTotal / (1 + (item.tax_rate1() / 100))))
2017-12-03 12:56:10 +01:00
@else
var taxAmount = roundToTwo(lineTotal * item.tax_rate1() / 100);
@endif
2015-10-22 20:48:12 +02:00
if (taxAmount) {
2016-03-31 11:29:01 +02:00
var key = item.tax_name1() + item.tax_rate1();
2015-10-22 20:48:12 +02:00
if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount;
} else {
2016-03-31 11:29:01 +02:00
taxes[key] = {name:item.tax_name1(), rate:item.tax_rate1(), amount:taxAmount};
}
}
2017-12-03 12:56:10 +01:00
@if ($account->inclusive_taxes)
2018-01-23 11:16:48 +01:00
var taxAmount = roundToTwo(lineTotal - (lineTotal / (1 + (item.tax_rate2() / 100))))
2017-12-03 12:56:10 +01:00
@else
var taxAmount = roundToTwo(lineTotal * item.tax_rate2() / 100);
@endif
2016-03-31 11:29:01 +02:00
if (taxAmount) {
var key = item.tax_name2() + item.tax_rate2();
if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount;
} else {
taxes[key] = {name:item.tax_name2(), rate:item.tax_rate2(), amount:taxAmount};
2015-10-22 20:48:12 +02:00
}
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
}
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;
});
2016-01-10 11:25:05 +01:00
self.totals.itemTaxRates = ko.computed(function() {
2015-10-22 20:48:12 +02:00
var taxes = self.totals.itemTaxes();
2016-01-10 11:25:05 +01:00
var parts = [];
2015-10-22 20:48:12 +02:00
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
parts.push(taxes[key].name + ' ' + (taxes[key].rate*1) + '%');
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
}
return parts.join('<br/>');
});
self.totals.itemTaxAmounts = ko.computed(function() {
var taxes = self.totals.itemTaxes();
2016-01-10 11:25:05 +01:00
var parts = [];
2015-10-22 20:48:12 +02:00
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
parts.push(self.formatMoney(taxes[key].amount));
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
}
return parts.join('<br/>');
});
self.totals.rawPaidToDate = ko.computed(function() {
2016-04-26 11:41:34 +02:00
return roundToTwo(accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2));
2015-10-22 20:48:12 +02:00
});
self.totals.paidToDate = ko.computed(function() {
var total = self.totals.rawPaidToDate();
return self.formatMoney(total);
2015-10-22 20:48:12 +02:00
});
self.totals.rawTotal = ko.computed(function() {
2016-01-10 11:25:05 +01:00
var total = accounting.toFixed(self.totals.rawSubtotal(),2);
2015-10-22 20:48:12 +02:00
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;
}
2017-12-03 12:56:10 +01:00
@if (! $account->inclusive_taxes)
var taxAmount1 = roundToTwo(total * parseFloat(self.tax_rate1()) / 100);
var taxAmount2 = roundToTwo(total * parseFloat(self.tax_rate2()) / 100);
2017-04-29 21:09:10 +02:00
2017-12-03 12:56:10 +01:00
total = NINJA.parseFloat(total) + taxAmount1 + taxAmount2;
total = roundToTwo(total);
2015-10-22 20:48:12 +02:00
2017-12-03 12:56:10 +01:00
var taxes = self.totals.itemTaxes();
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
total = roundToTwo(total);
}
2015-10-22 20:48:12 +02:00
}
2017-12-03 12:56:10 +01:00
@endif
2015-10-22 20:48:12 +02:00
if (customValue1 && !customTaxes1) {
total = NINJA.parseFloat(total) + customValue1;
}
if (customValue2 && !customTaxes2) {
total = NINJA.parseFloat(total) + customValue2;
}
2017-09-01 12:56:10 +02:00
total -= self.totals.rawPaidToDate();
2015-10-22 20:48:12 +02:00
return total;
});
self.totals.total = ko.computed(function() {
2016-03-18 13:34:46 +01:00
return self.formatMoney(self.totals.rawTotal());
});
self.totals.partial = ko.computed(function() {
return self.formatMoney(self.partial());
2016-01-10 11:25:05 +01:00
});
2015-10-22 20:48:12 +02:00
self.onDragged = function(item) {
refreshPDF(true);
}
2015-11-11 13:17:58 +01:00
self.showResetTerms = function() {
return self.default_terms() && self.terms() != self.default_terms();
2015-11-11 13:17:58 +01:00
}
self.showResetFooter = function() {
return self.default_footer() && self.invoice_footer() != self.default_footer();
2015-11-11 13:17:58 +01:00
}
2017-01-02 12:38:58 +01:00
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax = function(taxRate) {
2017-01-02 12:38:58 +01:00
for (var i=0; i<self.invoice_items().length; i++) {
var item = self.invoice_items()[i];
2017-09-07 21:14:11 +02:00
item.applyInclusiveTax(taxRate);
2017-01-02 12:38:58 +01:00
}
}
self.onTax1Change = function(obj, event) {
if ( ! event.originalEvent) {
return;
}
var taxKey = $(event.currentTarget).val();
var taxRate = parseFloat(self.tax_rate1());
if (taxKey.substr(0, 1) != 1) {
return;
}
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax(taxRate);
2017-01-02 12:38:58 +01:00
}
self.onTax2Change = function(obj, event) {
if ( ! event.originalEvent) {
return;
}
var taxKey = $(event.currentTarget).val();
var taxRate = parseFloat(self.tax_rate2());
if (taxKey.substr(0, 1) != 1) {
return;
}
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax(taxRate);
2017-01-02 12:38:58 +01:00
}
2017-08-15 17:36:17 +02:00
self.isAmountDiscountChanged = function(obj, event) {
2017-08-15 17:39:22 +02:00
if (! event.originalEvent) {
2017-08-15 17:36:17 +02:00
return;
}
if (! isStorageSupported()) {
return;
}
var isAmountDiscount = $('#is_amount_discount').val();
localStorage.setItem('last:is_amount_discount', isAmountDiscount);
}
2017-10-25 23:35:38 +02:00
self.isPartialSet = ko.computed(function() {
return self.partial() && self.partial() <= model.invoice().totals.rawTotal()
});
self.showPartialDueDate = ko.computed(function() {
if (self.is_quote()) {
return false;
}
return self.isPartialSet();
});
2015-10-22 20:48:12 +02:00
}
function ClientModel(data) {
var self = this;
self.public_id = ko.observable(0);
self.name = ko.observable('');
self.id_number = ko.observable('');
self.vat_number = ko.observable('');
self.work_phone = ko.observable('');
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
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('');
self.size_id = ko.observable('');
self.industry_id = ko.observable('');
self.currency_id = ko.observable('');
self.language_id = ko.observable('');
self.website = ko.observable('');
self.payment_terms = ko.observable(0);
self.contacts = ko.observableArray();
self.mapping = {
'contacts': {
create: function(options) {
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() {
var contact = new ContactModel();
contact.send_invoice(true);
self.contacts.push(contact);
return false;
}
self.removeContact = function() {
2016-01-10 11:25:05 +01:00
self.contacts.remove(this);
2015-10-22 20:48:12 +02:00
}
self.name.display = ko.computed(function() {
if (self.name()) {
return self.name();
}
if (self.contacts().length == 0) return;
2016-01-10 11:25:05 +01:00
var contact = self.contacts()[0];
2015-10-22 20:48:12 +02:00
if (contact.first_name() || contact.last_name()) {
2017-12-31 16:01:16 +01:00
return (contact.first_name() || '') + ' ' + (contact.last_name() || '');
2015-10-22 20:48:12 +02:00
} else {
return contact.email();
}
2016-01-10 11:25:05 +01:00
});
2015-10-22 20:48:12 +02: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()) {
2017-12-31 16:01:16 +01:00
return (contact.first_name() || '') + ' ' + (contact.last_name() || '');
2015-10-22 20:48:12 +02:00
} else {
return contact.email();
}
2016-01-10 11:25:05 +01:00
});
2015-10-22 20:48:12 +02:00
if (data) {
ko.mapping.fromJS(data, {}, this);
} else {
self.addContact();
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
}
function ContactModel(data) {
var self = this;
self.public_id = ko.observable('');
self.first_name = ko.observable('');
self.last_name = ko.observable('');
self.email = ko.observable('');
2016-01-10 11:25:05 +01:00
self.phone = ko.observable('');
2015-10-22 20:48:12 +02:00
self.send_invoice = ko.observable(false);
self.invitation_link = ko.observable('');
self.invitation_status = ko.observable('');
self.invitation_opened = ko.observable(false);
2015-10-22 20:48:12 +02:00
self.invitation_viewed = ko.observable(false);
self.email_error = ko.observable('');
2016-11-04 14:34:15 +01:00
self.invitation_signature_svg = ko.observable('');
self.invitation_signature_date = ko.observable('');
2017-04-16 13:31:14 +02:00
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
2015-10-22 20:48:12 +02:00
if (data) {
ko.mapping.fromJS(data, {}, this);
}
2017-04-03 21:48:47 +02:00
self.isBlank = ko.computed(function() {
return ! self.first_name() && ! self.last_name() && ! self.email() && ! self.phone();
});
2015-10-22 20:48:12 +02:00
self.displayName = ko.computed(function() {
var str = '';
if (self.first_name() || self.last_name()) {
2017-02-05 08:53:49 +01:00
str += (self.first_name() || '') + ' ' + (self.last_name() || '') + ' ';
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
if (self.email()) {
2017-02-06 10:41:16 +01:00
if (str) {
str += '&lt;' + self.email() + '&gt;';
} else {
str += self.email();
}
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
2017-02-05 08:53:49 +01:00
return str + '<br/>';
2015-10-22 20:48:12 +02:00
});
self.email.display = ko.computed(function() {
var str = '';
2016-02-16 16:30:09 +01:00
2015-10-22 20:48:12 +02:00
if (self.first_name() || self.last_name()) {
2016-02-16 16:30:09 +01:00
str += (self.first_name() || '') + ' ' + (self.last_name() || '') + '<br/>';
2016-01-10 11:25:05 +01:00
}
2015-10-22 20:48:12 +02:00
if (self.email()) {
2016-01-10 11:25:05 +01:00
str += self.email() + '<br/>';
2015-10-22 20:48:12 +02:00
}
return str;
});
self.view_as_recipient = ko.computed(function() {
var str = '';
@if (Utils::isConfirmed())
if (self.invitation_link()) {
2017-03-26 12:19:07 +02:00
// clicking adds 'silent=true' however it's removed when copying the link
2017-02-17 08:48:44 +01:00
str += '<a href="' + self.invitation_link() + '" onclick="window.open(\'' + self.invitation_link()
2018-03-26 21:31:45 +02:00
+ '?silent=true\', \'_blank\');return false;">{{ trans('texts.view_in_portal') }}</a>';
2015-10-22 20:48:12 +02:00
}
@endif
return str;
2016-01-10 11:25:05 +01:00
});
self.info_color = ko.computed(function() {
if (self.invitation_viewed()) {
return '#57D172';
} else if (self.invitation_opened()) {
return '#FFCC00';
} else {
return '#B1B5BA';
}
});
2016-12-25 09:54:01 +01:00
self.isValid = function() {
var email = (self.email() || '').trim();
var emailValid = isValidEmailAddress(email);
// if the email is set it must be valid
if (email && ! emailValid) {
return false;
} else {
return self.first_name() || email;
}
}
2015-10-22 20:48:12 +02:00
}
function ItemModel(data) {
2016-01-10 11:25:05 +01:00
var self = this;
2015-10-22 20:48:12 +02:00
self.product_key = ko.observable('');
self.notes = ko.observable('');
self.cost = ko.observable(0);
2018-02-01 08:13:45 +01:00
self.qty = ko.observable({{ $account->hasInvoiceField('product', 'product.quantity') ? 0 : 1 }});
2017-12-21 15:30:18 +01:00
self.discount = ko.observable();
2016-02-28 12:59:52 +01:00
self.custom_value1 = ko.observable('');
self.custom_value2 = ko.observable('');
2016-03-31 11:29:01 +02:00
self.tax_name1 = ko.observable('');
self.tax_rate1 = ko.observable(0);
2017-01-02 12:38:58 +01:00
self.tax_rate1IsInclusive = ko.observable(0);
2016-03-31 11:29:01 +02:00
self.tax_name2 = ko.observable('');
self.tax_rate2 = ko.observable(0);
2017-01-02 12:38:58 +01:00
self.tax_rate2IsInclusive = ko.observable(0);
2015-10-22 20:48:12 +02:00
self.task_public_id = ko.observable('');
2016-01-10 11:25:05 +01:00
self.expense_public_id = ko.observable('');
2017-03-26 09:54:04 +02:00
self.invoice_item_type_id = ko.observable({{ INVOICE_ITEM_TYPE_STANDARD }});
2015-10-22 20:48:12 +02:00
self.actionsVisible = ko.observable(false);
2017-07-27 09:08:36 +02:00
self.isTask = ko.computed(function() {
return self.invoice_item_type_id() == {{ INVOICE_ITEM_TYPE_TASK }};
});
2016-03-31 11:29:01 +02:00
this.tax1 = ko.computed({
read: function () {
2017-01-02 12:38:58 +01:00
return self.tax_rate1IsInclusive() + ' ' + self.tax_rate1() + ' ' + self.tax_name1();
2016-03-31 11:29:01 +02:00
},
write: function(value) {
2018-02-19 08:33:43 +01:00
value = value || '';
2017-01-02 12:38:58 +01:00
var parts = value.split(' ');
self.tax_rate1IsInclusive(parts.shift());
self.tax_rate1(parts.shift());
self.tax_name1(parts.join(' '));
2016-03-31 11:29:01 +02:00
}
})
this.tax2 = ko.computed({
2015-10-22 20:48:12 +02:00
read: function () {
2017-01-02 12:38:58 +01:00
return self.tax_rate2IsInclusive() + ' ' + self.tax_rate2() + ' ' + self.tax_name2();
2015-10-22 20:48:12 +02:00
},
write: function(value) {
2018-02-19 08:33:43 +01:00
value = value || '';
2017-01-02 12:38:58 +01:00
var parts = value.split(' ');
self.tax_rate2IsInclusive(parts.shift());
self.tax_rate2(parts.shift());
self.tax_name2(parts.join(' '));
2015-10-22 20:48:12 +02:00
}
})
this.prettyQty = ko.computed({
read: function () {
2017-08-18 12:26:14 +02:00
return NINJA.parseFloat(this.qty()) ? NINJA.parseFloat(this.qty()) : '';
2015-10-22 20:48:12 +02:00
},
write: function (value) {
this.qty(value);
},
owner: this
2016-01-10 11:25:05 +01:00
});
2015-10-22 20:48:12 +02:00
this.prettyCost = ko.computed({
read: function () {
2017-08-18 12:26:14 +02:00
return this.cost() ? this.cost() : '';
2015-10-22 20:48:12 +02:00
},
write: function (value) {
this.cost(value);
},
owner: this
2016-01-10 11:25:05 +01:00
});
2015-10-22 20:48:12 +02:00
2017-11-03 09:19:03 +01:00
self.loadData = function(data) {
2016-03-27 17:06:02 +02:00
ko.mapping.fromJS(data, {}, this);
2017-11-06 11:50:19 +01:00
this.cost(roundSignificant(this.cost(), true));
2017-08-18 12:26:14 +02:00
this.qty(roundSignificant(this.qty()));
2015-10-22 20:48:12 +02:00
}
2017-11-03 09:19:03 +01:00
if (data) {
self.loadData(data);
}
2015-10-22 20:48:12 +02:00
this.totals = ko.observable();
this.totals.rawTotal = ko.computed(function() {
2017-11-01 12:50:31 +01:00
var value = roundSignificant(NINJA.parseFloat(self.cost()) * NINJA.parseFloat(self.qty()));
2017-12-21 18:56:07 +01:00
if (self.discount()) {
2017-12-30 20:22:03 +01:00
var discount = roundToTwo(NINJA.parseFloat(self.discount()));
2017-12-21 18:56:07 +01:00
if (parseInt(model.invoice().is_amount_discount())) {
value -= discount;
} else {
2018-03-19 08:28:32 +01:00
value -= roundToTwo(value * discount / 100);
2017-12-21 18:56:07 +01:00
}
}
return value ? roundSignificant(value) : 0;
2015-10-22 20:48:12 +02:00
});
this.totals.total = ko.computed(function() {
var total = self.totals.rawTotal();
2016-02-16 22:56:59 +01:00
return window.hasOwnProperty('model') && total ? model.invoice().formatMoney(total) : '';
2015-10-22 20:48:12 +02:00
});
this.hideActions = function() {
this.actionsVisible(false);
}
this.showActions = function() {
this.actionsVisible(true);
}
this.isEmpty = function() {
2018-02-01 08:13:45 +01:00
return !self.product_key() && !self.notes() && !self.cost();
2015-10-22 20:48:12 +02:00
}
this.onSelect = function() {}
2017-01-02 12:38:58 +01:00
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax = function(taxRate) {
2017-01-02 12:38:58 +01:00
if ( ! taxRate) {
return;
}
var cost = self.cost() / (100 + taxRate) * 100;
self.cost(roundToTwo(cost));
}
self.onTax1Change = function (obj, event) {
if (event.originalEvent) {
var taxKey = $(event.currentTarget).val();
var taxRate = parseFloat(self.tax_rate1());
if (taxKey.substr(0, 1) == 1) {
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax(taxRate);
2017-01-02 12:38:58 +01:00
}
}
}
self.onTax2Change = function (obj, event) {
if (event.originalEvent) {
var taxKey = $(event.currentTarget).val();
var taxRate = parseFloat(self.tax_rate2());
if (taxKey.substr(0, 1) == 1) {
2017-09-07 21:14:11 +02:00
self.applyInclusiveTax(taxRate);
2017-01-02 12:38:58 +01:00
}
}
}
2015-10-22 20:48:12 +02:00
}
2016-03-23 03:23:45 +01:00
function DocumentModel(data) {
var self = this;
self.public_id = ko.observable(0);
self.size = ko.observable(0);
self.name = ko.observable('');
self.type = ko.observable('');
self.url = ko.observable('');
2016-03-23 03:23:45 +01:00
self.update = function(data){
ko.mapping.fromJS(data, {}, this);
}
2016-03-23 03:23:45 +01:00
if (data) {
self.update(data);
}
2016-03-23 03:23:45 +01:00
}
2016-07-07 10:03:43 +02:00
function CategoryModel(data) {
var self = this;
self.name = ko.observable('')
self.update = function(data){
ko.mapping.fromJS(data, {}, this);
}
if (data) {
self.update(data);
}
}
var ExpenseModel = function(data) {
var self = this;
self.mapping = {
'documents': {
create: function(options) {
return new DocumentModel(options.data);
}
2016-07-07 10:03:43 +02:00
},
'expense_category': {
create: function(options) {
return new CategoryModel(options.data);
}
}
}
self.description = ko.observable('');
self.qty = ko.observable(0);
self.public_id = ko.observable(0);
self.amount = ko.observable();
self.converted_amount = ko.observable();
if (data) {
ko.mapping.fromJS(data, self.mapping, this);
}
};
2015-10-22 20:48:12 +02:00
/* Custom binding for product key typeahead */
2016-07-05 20:49:47 +02:00
ko.bindingHandlers.productTypeahead = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var $element = $(element);
var allBindings = allBindingsAccessor();
$element.typeahead({
highlight: true,
minLength: 0,
},
{
name: 'data',
display: allBindings.key,
2016-04-11 12:30:23 +02:00
limit: 50,
2017-02-14 14:49:47 +01:00
templates: {
2017-05-08 11:23:51 +02:00
suggestion: function(item) { return '<div title="'
+ _.escape(item.product_key) + ': '
2017-05-08 11:23:51 +02:00
+ item.cost + "\n"
+ item.notes.substring(0, 60) + '">'
+ _.escape(item.product_key) + '</div>' }
2017-02-14 14:49:47 +01:00
},
source: searchData(allBindings.items, allBindings.key)
}).on('typeahead:select', function(element, datum, name) {
@if (Auth::user()->account->fill_products)
var model = ko.dataFor(this);
if (model.expense_public_id()) {
return;
}
if (datum.notes && (! model.notes() || ! model.isTask())) {
model.notes(datum.notes);
}
2017-10-17 13:38:02 +02:00
if (parseFloat(datum.cost)) {
if (! NINJA.parseFloat(model.cost()) || ! model.isTask()) {
2017-12-21 09:05:33 +01:00
var cost = datum.cost;
2017-12-21 09:28:44 +01:00
// optionally handle curency conversion
@if ($account->convert_products)
var client = window.model.invoice().client();
if (client) {
var clientCurrencyId = client.currency_id();
2017-12-21 09:05:33 +01:00
var accountCurrencyId = {{ $account->getCurrencyId() }};
2017-12-21 09:28:44 +01:00
if (clientCurrencyId && clientCurrencyId != accountCurrencyId) {
cost = fx.convert(cost, {
from: currencyMap[accountCurrencyId].code,
to: currencyMap[clientCurrencyId].code,
});
var rate = fx.convert(1, {
from: currencyMap[accountCurrencyId].code,
to: currencyMap[clientCurrencyId].code,
});
2018-04-04 15:24:59 +02:00
if ((account.custom_fields.invoice_text1 || '').toLowerCase() == "{{ strtolower(trans('texts.exchange_rate')) }}") {
2017-12-21 09:28:44 +01:00
window.model.invoice().custom_text_value1(roundToFour(rate, true));
2018-04-04 15:24:59 +02:00
} else if ((account.custom_fields.invoice_text1 || '').toLowerCase() == "{{ strtolower(trans('texts.exchange_rate')) }}") {
2017-12-21 09:28:44 +01:00
window.model.invoice().custom_text_value2(roundToFour(rate, true));
}
}
2017-12-21 09:05:33 +01:00
}
2017-12-21 09:28:44 +01:00
@endif
model.cost(roundToTwo(cost, true));
}
}
if (! model.qty() && ! model.isTask()) {
model.qty(1);
}
@if ($account->invoice_item_taxes)
if (datum.tax_name1) {
2017-01-02 12:38:58 +01:00
var $select = $(this).parentsUntil('tbody').find('select').first();
$select.val('0 ' + datum.tax_rate1 + ' ' + datum.tax_name1).trigger('change');
}
if (datum.tax_name2) {
var $select = $(this).parentsUntil('tbody').find('select').last();
$select.val('0 ' + datum.tax_rate2 + ' ' + datum.tax_name2).trigger('change');
}
@endif
2018-04-04 15:24:59 +02:00
@if (Auth::user()->isPro() && $account->customLabel('product1'))
2017-02-23 15:33:02 +01:00
if (datum.custom_value1) {
model.custom_value1(datum.custom_value1);
}
@endif
2018-04-04 15:24:59 +02:00
@if (Auth::user()->isPro() && $account->customLabel('product2'))
2017-02-23 15:33:02 +01:00
if (datum.custom_value2) {
model.custom_value2(datum.custom_value2);
}
@endif
@endif
onItemChange();
}).on('typeahead:change', function(element, datum, name) {
var value = valueAccessor();
value(datum);
onItemChange();
refreshPDF(true);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).typeahead('val', value);
}
}
};
function checkInvoiceNumber() {
var url = '{{ url('check_invoice_number') }}/{{ $invoice->id ? $invoice->public_id : '' }}?invoice_number=' + encodeURIComponent($('#invoice_number').val());
$.get(url, function(data) {
var isValid = data == '{{ RESULT_SUCCESS }}' ? true : false;
if (isValid) {
$('.invoice-number')
.removeClass('has-error')
.find('span')
.hide();
} else {
if ($('.invoice-number').hasClass('has-error')) {
return;
}
$('.invoice-number')
.addClass('has-error')
.find('div')
.append('<span class="help-block">{{ trans('validation.unique', ['attribute' => trans('texts.invoice_number')]) }}</span>');
}
});
}
</script>