1
0
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:
Hillel Coren 2017-11-17 13:56:10 +02:00
parent dbf136ba41
commit 57757cdb00
8 changed files with 264 additions and 127 deletions

View File

@ -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
*/

View File

@ -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'];

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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