mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-09-19 16:01:34 +02:00
Support converting payment currency
This commit is contained in:
parent
dbf136ba41
commit
57757cdb00
@ -23,7 +23,10 @@ class Payment extends EntityModel
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'transaction_reference',
|
||||
'private_notes',
|
||||
'exchange_rate',
|
||||
'exchange_currency_id',
|
||||
];
|
||||
|
||||
public static $statusClasses = [
|
||||
@ -303,6 +306,14 @@ class Payment extends EntityModel
|
||||
return $this->getCompletedAmount() > 0 && ($this->isCompleted() || $this->isPartiallyRefunded());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isExchanged()
|
||||
{
|
||||
return $this->exchange_currency_id || $this->exchange_rate != 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null|\stdClass|string
|
||||
*/
|
||||
|
@ -187,12 +187,7 @@ class PaymentRepository extends BaseRepository
|
||||
$payment->payment_date = date('Y-m-d');
|
||||
}
|
||||
|
||||
if (isset($input['transaction_reference'])) {
|
||||
$payment->transaction_reference = trim($input['transaction_reference']);
|
||||
}
|
||||
if (isset($input['private_notes'])) {
|
||||
$payment->private_notes = trim($input['private_notes']);
|
||||
}
|
||||
$payment->fill(request()->all());
|
||||
|
||||
if (! $publicId) {
|
||||
$clientId = $input['client_id'];
|
||||
|
@ -16,6 +16,16 @@ class AddSubdomainToLookups extends Migration
|
||||
Schema::table('lookup_accounts', function ($table) {
|
||||
$table->string('subdomain')->nullable()->unique();
|
||||
});
|
||||
|
||||
Schema::table('payments', function ($table) {
|
||||
$table->decimal('exchange_rate', 13, 4)->default(1);
|
||||
$table->unsignedInteger('exchange_currency_id')->nullable(false);
|
||||
});
|
||||
|
||||
Schema::table('expenses', function ($table) {
|
||||
$table->decimal('exchange_rate', 13, 4)->default(1)->change();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,5 +38,10 @@ class AddSubdomainToLookups extends Migration
|
||||
Schema::table('lookup_accounts', function ($table) {
|
||||
$table->dropColumn('subdomain');
|
||||
});
|
||||
|
||||
Schema::table('payments', function ($table) {
|
||||
$table->dropColumn('exchange_rate');
|
||||
$table->dropColumn('exchange_currency_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -515,101 +515,6 @@ function getClientDisplayName(client)
|
||||
return '';
|
||||
}
|
||||
|
||||
function populateInvoiceComboboxes(clientId, invoiceId) {
|
||||
var clientMap = {};
|
||||
var invoiceMap = {};
|
||||
var invoicesForClientMap = {};
|
||||
var $clientSelect = $('select#client');
|
||||
|
||||
for (var i=0; i<invoices.length; i++) {
|
||||
var invoice = invoices[i];
|
||||
var client = invoice.client;
|
||||
|
||||
if (!invoicesForClientMap.hasOwnProperty(client.public_id)) {
|
||||
invoicesForClientMap[client.public_id] = [];
|
||||
}
|
||||
|
||||
invoicesForClientMap[client.public_id].push(invoice);
|
||||
invoiceMap[invoice.public_id] = invoice;
|
||||
}
|
||||
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
clientMap[client.public_id] = client;
|
||||
}
|
||||
|
||||
$clientSelect.append(new Option('', ''));
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
var clientName = getClientDisplayName(client);
|
||||
if (!clientName) {
|
||||
continue;
|
||||
}
|
||||
$clientSelect.append(new Option(clientName, client.public_id));
|
||||
}
|
||||
|
||||
if (clientId) {
|
||||
$clientSelect.val(clientId);
|
||||
}
|
||||
|
||||
$clientSelect.combobox({highlighter: comboboxHighlighter});
|
||||
$clientSelect.on('change', function(e) {
|
||||
var clientId = $('input[name=client]').val();
|
||||
var invoiceId = $('input[name=invoice]').val();
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
if (invoice && invoice.client.public_id == clientId) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
setComboboxValue($('.invoice-select'), '', '');
|
||||
$invoiceCombobox = $('select#invoice');
|
||||
$invoiceCombobox.find('option').remove().end().combobox('refresh');
|
||||
$invoiceCombobox.append(new Option('', ''));
|
||||
var list = clientId ? (invoicesForClientMap.hasOwnProperty(clientId) ? invoicesForClientMap[clientId] : []) : invoices;
|
||||
for (var i=0; i<list.length; i++) {
|
||||
var invoice = list[i];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
if (!client || !getClientDisplayName(client)) continue; // client is deleted/archived
|
||||
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + invoice.invoice_status.name + ' - ' +
|
||||
getClientDisplayName(client) + ' - ' + formatMoneyInvoice(invoice.amount, invoice) + ' | ' +
|
||||
formatMoneyInvoice(invoice.balance, invoice), invoice.public_id));
|
||||
}
|
||||
$('select#invoice').combobox('refresh');
|
||||
});
|
||||
|
||||
var $invoiceSelect = $('select#invoice').on('change', function(e) {
|
||||
$clientCombobox = $('select#client');
|
||||
var invoiceId = $('input[name=invoice]').val();
|
||||
if (invoiceId) {
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
invoice.client = client;
|
||||
setComboboxValue($('.client-select'), client.public_id, getClientDisplayName(client));
|
||||
if (!parseFloat($('#amount').val())) {
|
||||
$('#amount').val(parseFloat(invoice.balance).toFixed(2));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$invoiceSelect.combobox({highlighter: comboboxHighlighter});
|
||||
|
||||
if (invoiceId) {
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
invoice.client = client;
|
||||
setComboboxValue($('.invoice-select'), invoice.public_id, (invoice.invoice_number + ' - ' +
|
||||
invoice.invoice_status.name + ' - ' + getClientDisplayName(client) + ' - ' +
|
||||
formatMoneyInvoice(invoice.amount, invoice) + ' | ' + formatMoneyInvoice(invoice.balance, invoice)));
|
||||
$invoiceSelect.trigger('change');
|
||||
} else if (clientId) {
|
||||
var client = clientMap[clientId];
|
||||
setComboboxValue($('.client-select'), client.public_id, getClientDisplayName(client));
|
||||
$clientSelect.trigger('change');
|
||||
} else {
|
||||
$clientSelect.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var CONSTS = {};
|
||||
CONSTS.INVOICE_STATUS_DRAFT = 1;
|
||||
|
@ -471,7 +471,10 @@
|
||||
return roundToTwo(self.amount() * self.exchange_rate()).toFixed(2);
|
||||
},
|
||||
write: function(value) {
|
||||
self.amount(roundToTwo(value / self.exchange_rate()));
|
||||
// When changing the converted amount we're updating
|
||||
// the exchange rate rather than change the amount
|
||||
self.exchange_rate(NINJA.parseFloat(value) / self.amount());
|
||||
//self.amount(roundToTwo(value / self.exchange_rate()));
|
||||
}
|
||||
}, self);
|
||||
|
||||
|
@ -50,7 +50,7 @@
|
||||
@else
|
||||
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
|
||||
{!! Former::select('invoice')->addOption('', '')->addGroupClass('invoice-select') !!}
|
||||
{!! Former::text('amount') !!}
|
||||
{!! Former::text('amount')->append('<span data-bind="html: paymentCurrencyCode"></span>') !!}
|
||||
|
||||
@if (isset($paymentTypeId) && $paymentTypeId)
|
||||
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
|
||||
@ -71,6 +71,31 @@
|
||||
{!! Former::text('transaction_reference') !!}
|
||||
{!! Former::textarea('private_notes') !!}
|
||||
|
||||
|
||||
@if (!$payment || ($payment && ! $payment->isExchanged()))
|
||||
{!! Former::checkbox('convert_currency')
|
||||
->text(trans('texts.convert_currency'))
|
||||
->data_bind('checked: convert_currency')
|
||||
->label(' ')
|
||||
->value(1) !!}
|
||||
@endif
|
||||
|
||||
<div style="display:none" data-bind="visible: enableExchangeRate">
|
||||
<br/>
|
||||
{!! Former::select('exchange_currency_id')->addOption('','')
|
||||
->label(trans('texts.currency'))
|
||||
->data_placeholder(Utils::getFromCache($account->getCurrencyId(), 'currencies')->name)
|
||||
->data_bind('combobox: exchange_currency_id, disable: true')
|
||||
->fromQuery($currencies, 'name', 'id') !!}
|
||||
{!! Former::text('exchange_rate')
|
||||
->data_bind("value: exchange_rate, enable: enableExchangeRate, valueUpdate: 'afterkeydown'") !!}
|
||||
{!! Former::text('')
|
||||
->label(trans('texts.amount'))
|
||||
->data_bind("value: convertedAmount, enable: enableExchangeRate")
|
||||
->append('<span data-bind="html: exchangeCurrencyCode"></span>') !!}
|
||||
</div>
|
||||
|
||||
|
||||
@if (!$payment)
|
||||
{!! Former::checkbox('email_receipt')
|
||||
->onchange('onEmailReceiptChange()')
|
||||
@ -110,6 +135,27 @@
|
||||
var invoices = {!! $invoices !!};
|
||||
var clients = {!! $clients !!};
|
||||
|
||||
var clientMap = {};
|
||||
var invoiceMap = {};
|
||||
var invoicesForClientMap = {};
|
||||
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
clientMap[client.public_id] = client;
|
||||
}
|
||||
|
||||
for (var i=0; i<invoices.length; i++) {
|
||||
var invoice = invoices[i];
|
||||
var client = invoice.client;
|
||||
|
||||
if (!invoicesForClientMap.hasOwnProperty(client.public_id)) {
|
||||
invoicesForClientMap[client.public_id] = [];
|
||||
}
|
||||
|
||||
invoicesForClientMap[client.public_id].push(invoice);
|
||||
invoiceMap[invoice.public_id] = invoice;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
@if ($payment)
|
||||
@ -141,6 +187,20 @@
|
||||
$('#email_receipt').prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
@if (Input::old('data'))
|
||||
// this means we failed so we'll reload the previous state
|
||||
window.model = new ViewModel({!! $data !!});
|
||||
@else
|
||||
// otherwise create blank model
|
||||
window.model = new ViewModel({!! $payment !!});
|
||||
@endif
|
||||
ko.applyBindings(model);
|
||||
|
||||
$('#amount').change(function() {
|
||||
var amount = $('#amount').val();
|
||||
model.amount(NINJA.parseFloat(amount));
|
||||
})
|
||||
});
|
||||
|
||||
function onFormSubmit(event) {
|
||||
@ -170,6 +230,154 @@
|
||||
localStorage.setItem('last:send_email_receipt', checked ? true : '');
|
||||
}
|
||||
|
||||
|
||||
var ViewModel = function(data) {
|
||||
var self = this;
|
||||
|
||||
self.client_id = ko.observable();
|
||||
self.exchange_currency_id = ko.observable();
|
||||
self.amount = ko.observable();
|
||||
self.exchange_rate = ko.observable(1);
|
||||
self.convert_currency = ko.observable({{ ($payment && $payment->isExchanged()) ? 'true' : 'false' }});
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, self.mapping, this);
|
||||
self.exchange_rate(roundSignificant(self.exchange_rate()));
|
||||
}
|
||||
|
||||
self.account_currency_id = ko.observable({{ $account->getCurrencyId() }});
|
||||
|
||||
self.convertedAmount = ko.computed({
|
||||
read: function () {
|
||||
return roundToTwo(self.amount() * self.exchange_rate()).toFixed(2);
|
||||
},
|
||||
write: function(value) {
|
||||
var amount = NINJA.parseFloat(value) / self.amount();
|
||||
self.exchange_rate(roundSignificant(amount));
|
||||
}
|
||||
}, self);
|
||||
|
||||
|
||||
self.payment_currency_id = ko.computed(function() {
|
||||
//return
|
||||
});
|
||||
|
||||
self.getCurrency = function(currencyId) {
|
||||
return currencyMap[currencyId || self.account_currency_id()];
|
||||
};
|
||||
|
||||
self.exchangeCurrencyCode = ko.computed(function() {
|
||||
return self.getCurrency(self.exchange_currency_id()).code;
|
||||
});
|
||||
|
||||
self.paymentCurrencyCode = ko.computed(function() {
|
||||
var client = clientMap[self.client_id()];
|
||||
if (client && client.currency_id) {
|
||||
var currencyId = client.currency_id;
|
||||
} else {
|
||||
var currencyId = self.account_currency_id();
|
||||
}
|
||||
return self.getCurrency(currencyId).code;
|
||||
});
|
||||
|
||||
self.enableExchangeRate = ko.computed(function() {
|
||||
if (self.convert_currency()) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
var expenseCurrencyId = self.expense_currency_id() || self.account_currency_id();
|
||||
var invoiceCurrencyId = self.invoice_currency_id() || self.account_currency_id();
|
||||
return expenseCurrencyId != invoiceCurrencyId
|
||||
|| invoiceCurrencyId != self.account_currency_id()
|
||||
|| expenseCurrencyId != self.account_currency_id();
|
||||
*/
|
||||
})
|
||||
};
|
||||
|
||||
function populateInvoiceComboboxes(clientId, invoiceId) {
|
||||
var $clientSelect = $('select#client');
|
||||
$clientSelect.append(new Option('', ''));
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
var clientName = getClientDisplayName(client);
|
||||
if (!clientName) {
|
||||
continue;
|
||||
}
|
||||
$clientSelect.append(new Option(clientName, client.public_id));
|
||||
}
|
||||
|
||||
if (clientId) {
|
||||
$clientSelect.val(clientId);
|
||||
}
|
||||
|
||||
$clientSelect.combobox({highlighter: comboboxHighlighter});
|
||||
$clientSelect.on('change', function(e) {
|
||||
var clientId = $('input[name=client]').val();
|
||||
var invoiceId = $('input[name=invoice]').val();
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
if (invoice && invoice.client.public_id == clientId) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
setComboboxValue($('.invoice-select'), '', '');
|
||||
$invoiceCombobox = $('select#invoice');
|
||||
$invoiceCombobox.find('option').remove().end().combobox('refresh');
|
||||
$invoiceCombobox.append(new Option('', ''));
|
||||
var list = clientId ? (invoicesForClientMap.hasOwnProperty(clientId) ? invoicesForClientMap[clientId] : []) : invoices;
|
||||
for (var i=0; i<list.length; i++) {
|
||||
var invoice = list[i];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
if (!client || !getClientDisplayName(client)) continue; // client is deleted/archived
|
||||
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + invoice.invoice_status.name + ' - ' +
|
||||
getClientDisplayName(client) + ' - ' + formatMoneyInvoice(invoice.amount, invoice) + ' | ' +
|
||||
formatMoneyInvoice(invoice.balance, invoice), invoice.public_id));
|
||||
}
|
||||
$('select#invoice').combobox('refresh');
|
||||
|
||||
if (window.model) {
|
||||
model.client_id(clientId);
|
||||
}
|
||||
});
|
||||
|
||||
var $invoiceSelect = $('select#invoice').on('change', function(e) {
|
||||
$clientCombobox = $('select#client');
|
||||
var invoiceId = $('input[name=invoice]').val();
|
||||
if (invoiceId) {
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
invoice.client = client;
|
||||
setComboboxValue($('.client-select'), client.public_id, getClientDisplayName(client));
|
||||
if (!parseFloat($('#amount').val())) {
|
||||
var amount = parseFloat(invoice.balance);
|
||||
$('#amount').val(amount.toFixed(2));
|
||||
model.amount(amount);
|
||||
}
|
||||
}
|
||||
if (window.model) {
|
||||
model.client_id(client ? client.public_id : 0);
|
||||
}
|
||||
});
|
||||
|
||||
$invoiceSelect.combobox({highlighter: comboboxHighlighter});
|
||||
|
||||
if (invoiceId) {
|
||||
var invoice = invoiceMap[invoiceId];
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
invoice.client = client;
|
||||
setComboboxValue($('.invoice-select'), invoice.public_id, (invoice.invoice_number + ' - ' +
|
||||
invoice.invoice_status.name + ' - ' + getClientDisplayName(client) + ' - ' +
|
||||
formatMoneyInvoice(invoice.amount, invoice) + ' | ' + formatMoneyInvoice(invoice.balance, invoice)));
|
||||
$invoiceSelect.trigger('change');
|
||||
} else if (clientId) {
|
||||
var client = clientMap[clientId];
|
||||
setComboboxValue($('.client-select'), client.public_id, getClientDisplayName(client));
|
||||
$clientSelect.trigger('change');
|
||||
} else {
|
||||
$clientSelect.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
||||
|
Loading…
Reference in New Issue
Block a user