mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support converting payment currency
This commit is contained in:
parent
dbf136ba41
commit
57757cdb00
@ -23,7 +23,10 @@ class Payment extends EntityModel
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'transaction_reference',
|
||||||
'private_notes',
|
'private_notes',
|
||||||
|
'exchange_rate',
|
||||||
|
'exchange_currency_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $statusClasses = [
|
public static $statusClasses = [
|
||||||
@ -303,6 +306,14 @@ class Payment extends EntityModel
|
|||||||
return $this->getCompletedAmount() > 0 && ($this->isCompleted() || $this->isPartiallyRefunded());
|
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
|
* @return mixed|null|\stdClass|string
|
||||||
*/
|
*/
|
||||||
|
@ -187,12 +187,7 @@ class PaymentRepository extends BaseRepository
|
|||||||
$payment->payment_date = date('Y-m-d');
|
$payment->payment_date = date('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($input['transaction_reference'])) {
|
$payment->fill(request()->all());
|
||||||
$payment->transaction_reference = trim($input['transaction_reference']);
|
|
||||||
}
|
|
||||||
if (isset($input['private_notes'])) {
|
|
||||||
$payment->private_notes = trim($input['private_notes']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $publicId) {
|
if (! $publicId) {
|
||||||
$clientId = $input['client_id'];
|
$clientId = $input['client_id'];
|
||||||
|
@ -16,6 +16,16 @@ class AddSubdomainToLookups extends Migration
|
|||||||
Schema::table('lookup_accounts', function ($table) {
|
Schema::table('lookup_accounts', function ($table) {
|
||||||
$table->string('subdomain')->nullable()->unique();
|
$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) {
|
Schema::table('lookup_accounts', function ($table) {
|
||||||
$table->dropColumn('subdomain');
|
$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 '';
|
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 = {};
|
var CONSTS = {};
|
||||||
CONSTS.INVOICE_STATUS_DRAFT = 1;
|
CONSTS.INVOICE_STATUS_DRAFT = 1;
|
||||||
|
@ -471,7 +471,10 @@
|
|||||||
return roundToTwo(self.amount() * self.exchange_rate()).toFixed(2);
|
return roundToTwo(self.amount() * self.exchange_rate()).toFixed(2);
|
||||||
},
|
},
|
||||||
write: function(value) {
|
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);
|
}, self);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
@else
|
@else
|
||||||
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
|
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
|
||||||
{!! Former::select('invoice')->addOption('', '')->addGroupClass('invoice-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)
|
@if (isset($paymentTypeId) && $paymentTypeId)
|
||||||
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
|
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
|
||||||
@ -71,6 +71,31 @@
|
|||||||
{!! Former::text('transaction_reference') !!}
|
{!! Former::text('transaction_reference') !!}
|
||||||
{!! Former::textarea('private_notes') !!}
|
{!! 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)
|
@if (!$payment)
|
||||||
{!! Former::checkbox('email_receipt')
|
{!! Former::checkbox('email_receipt')
|
||||||
->onchange('onEmailReceiptChange()')
|
->onchange('onEmailReceiptChange()')
|
||||||
@ -110,6 +135,27 @@
|
|||||||
var invoices = {!! $invoices !!};
|
var invoices = {!! $invoices !!};
|
||||||
var clients = {!! $clients !!};
|
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() {
|
$(function() {
|
||||||
|
|
||||||
@if ($payment)
|
@if ($payment)
|
||||||
@ -141,6 +187,20 @@
|
|||||||
$('#email_receipt').prop('checked', true);
|
$('#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) {
|
function onFormSubmit(event) {
|
||||||
@ -170,6 +230,154 @@
|
|||||||
localStorage.setItem('last:send_email_receipt', checked ? true : '');
|
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>
|
</script>
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
Loading…
x
Reference in New Issue
Block a user