mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Make sure that accounts and their opening balance values are the same currency.
This commit is contained in:
parent
89ee9c058a
commit
953c38563b
@ -15,15 +15,20 @@ namespace FireflyIII\Console\Commands;
|
|||||||
|
|
||||||
|
|
||||||
use DB;
|
use DB;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountMeta;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\LimitRepetition;
|
use FireflyIII\Models\LimitRepetition;
|
||||||
use FireflyIII\Models\PiggyBankEvent;
|
use FireflyIII\Models\PiggyBankEvent;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Log;
|
use Log;
|
||||||
|
use Preferences;
|
||||||
use Schema;
|
use Schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,8 +68,13 @@ class UpgradeDatabase extends Command
|
|||||||
$this->setTransactionIdentifier();
|
$this->setTransactionIdentifier();
|
||||||
$this->migrateRepetitions();
|
$this->migrateRepetitions();
|
||||||
$this->repairPiggyBanks();
|
$this->repairPiggyBanks();
|
||||||
|
$this->updateAccountCurrencies();
|
||||||
|
$this->info('Firefly III database is up to date.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate budget repetitions to new format.
|
||||||
|
*/
|
||||||
private function migrateRepetitions()
|
private function migrateRepetitions()
|
||||||
{
|
{
|
||||||
if (!Schema::hasTable('budget_limits')) {
|
if (!Schema::hasTable('budget_limits')) {
|
||||||
@ -153,6 +163,57 @@ class UpgradeDatabase extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function updateAccountCurrencies()
|
||||||
|
{
|
||||||
|
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||||
|
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
|
||||||
|
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
// get users preference, fall back to system pref.
|
||||||
|
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||||
|
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||||
|
$accountCurrency = intval($account->getMeta('currency_id'));
|
||||||
|
$openingBalance = $account->getOpeningBalance();
|
||||||
|
$openingBalanceCurrency = intval($openingBalance->transaction_currency_id);
|
||||||
|
|
||||||
|
// both 0? set to default currency:
|
||||||
|
if ($accountCurrency === 0 && $openingBalanceCurrency === 0) {
|
||||||
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opening balance 0, account not zero? just continue:
|
||||||
|
if ($accountCurrency > 0 && $openingBalanceCurrency === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// account is set to 0, opening balance is not?
|
||||||
|
if ($accountCurrency === 0 && $openingBalanceCurrency > 0) {
|
||||||
|
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $openingBalanceCurrency]);
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both are equal, just continue:
|
||||||
|
if ($accountCurrency === $openingBalanceCurrency) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// do not match:
|
||||||
|
if ($accountCurrency !== $openingBalanceCurrency) {
|
||||||
|
// update opening balance:
|
||||||
|
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||||
|
$openingBalance->save();
|
||||||
|
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
|
||||||
* which has 0 as an identifier and give it the same identifier.
|
* which has 0 as an identifier and give it the same identifier.
|
||||||
|
@ -147,13 +147,13 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function edit(Request $request, Account $account)
|
public function edit(Request $request, Account $account)
|
||||||
{
|
{
|
||||||
|
/** @var CurrencyRepositoryInterface $repository */
|
||||||
|
$repository = app(CurrencyRepositoryInterface::class);
|
||||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||||
/** @var CurrencyRepositoryInterface $repository */
|
$allCurrencies = $repository->get();
|
||||||
$repository = app(CurrencyRepositoryInterface::class);
|
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
|
||||||
$roles = [];
|
$roles = [];
|
||||||
foreach (config('firefly.accountRoles') as $role) {
|
foreach (config('firefly.accountRoles') as $role) {
|
||||||
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
||||||
@ -173,6 +173,7 @@ class AccountController extends Controller
|
|||||||
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
|
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
|
||||||
$openingBalanceDate = $account->getOpeningBalanceDate();
|
$openingBalanceDate = $account->getOpeningBalanceDate();
|
||||||
$openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
|
$openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
|
||||||
|
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||||
|
|
||||||
$preFilled = [
|
$preFilled = [
|
||||||
'accountNumber' => $account->getMeta('accountNumber'),
|
'accountNumber' => $account->getMeta('accountNumber'),
|
||||||
@ -183,13 +184,18 @@ class AccountController extends Controller
|
|||||||
'openingBalanceDate' => $openingBalanceDate,
|
'openingBalanceDate' => $openingBalanceDate,
|
||||||
'openingBalance' => $openingBalanceAmount,
|
'openingBalance' => $openingBalanceAmount,
|
||||||
'virtualBalance' => $account->virtual_balance,
|
'virtualBalance' => $account->virtual_balance,
|
||||||
'currency_id' => $account->getMeta('currency_id'),
|
'currency_id' => $currency->id,
|
||||||
|
|
||||||
];
|
];
|
||||||
$request->session()->flash('preFilled', $preFilled);
|
$request->session()->flash('preFilled', $preFilled);
|
||||||
$request->session()->flash('gaEventCategory', 'accounts');
|
$request->session()->flash('gaEventCategory', 'accounts');
|
||||||
$request->session()->flash('gaEventAction', 'edit-' . $what);
|
$request->session()->flash('gaEventAction', 'edit-' . $what);
|
||||||
|
|
||||||
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'));
|
return view(
|
||||||
|
'accounts.edit', compact(
|
||||||
|
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,6 +211,26 @@ class Account extends Model
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the opening balance
|
||||||
|
*
|
||||||
|
* @return TransactionJournal
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function getOpeningBalance(): TransactionJournal
|
||||||
|
{
|
||||||
|
$journal = TransactionJournal::sortCorrectly()
|
||||||
|
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||||
|
->where('transactions.account_id', $this->id)
|
||||||
|
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||||
|
->first(['transaction_journals.*']);
|
||||||
|
if (is_null($journal)) {
|
||||||
|
return new TransactionJournal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $journal;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of the opening balance for this account.
|
* Returns the amount of the opening balance for this account.
|
||||||
*
|
*
|
||||||
|
@ -530,12 +530,10 @@ class AccountRepository implements AccountRepositoryInterface
|
|||||||
}
|
}
|
||||||
// opening balance data? update it!
|
// opening balance data? update it!
|
||||||
if (!is_null($openingBalance->id)) {
|
if (!is_null($openingBalance->id)) {
|
||||||
$date = $data['openingBalanceDate'];
|
|
||||||
$amount = $data['openingBalance'];
|
|
||||||
|
|
||||||
Log::debug('Opening balance journal found, update journal.');
|
Log::debug('Opening balance journal found, update journal.');
|
||||||
|
|
||||||
$this->updateOpeningBalanceJournal($account, $openingBalance, $date, $amount);
|
$this->updateOpeningBalanceJournal($account, $openingBalance, $data);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -589,15 +587,19 @@ class AccountRepository implements AccountRepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* @param Account $account
|
* @param Account $account
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
* @param Carbon $date
|
* @param array $data
|
||||||
* @param float $amount
|
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function updateOpeningBalanceJournal(Account $account, TransactionJournal $journal, Carbon $date, float $amount): bool
|
protected function updateOpeningBalanceJournal(Account $account, TransactionJournal $journal, array $data): bool
|
||||||
{
|
{
|
||||||
|
$date = $data['openingBalanceDate'];
|
||||||
|
$amount = $data['openingBalance'];
|
||||||
|
$currencyId = intval($data['currency_id']);
|
||||||
|
|
||||||
// update date:
|
// update date:
|
||||||
$journal->date = $date;
|
$journal->date = $date;
|
||||||
|
$journal->transaction_currency_id = $currencyId;
|
||||||
$journal->save();
|
$journal->save();
|
||||||
// update transactions:
|
// update transactions:
|
||||||
/** @var Transaction $transaction */
|
/** @var Transaction $transaction */
|
||||||
|
@ -137,4 +137,12 @@ interface AccountRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function store(array $data): Account;
|
public function store(array $data): Account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Account $account
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return Account
|
||||||
|
*/
|
||||||
|
public function update(Account $account, array $data): Account;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -261,6 +261,36 @@ class ExpandedForm
|
|||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param null $value
|
||||||
|
* @param array $options
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function nonSelectableBalance(string $name, $value = null, array $options = []): string
|
||||||
|
{
|
||||||
|
|
||||||
|
$label = $this->label($name, $options);
|
||||||
|
$options = $this->expandOptionArray($name, $label, $options);
|
||||||
|
$classes = $this->getHolderClasses($name);
|
||||||
|
$value = $this->fillFieldValue($name, $value);
|
||||||
|
$options['step'] = 'any';
|
||||||
|
$selectedCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
|
||||||
|
unset($options['currency']);
|
||||||
|
unset($options['placeholder']);
|
||||||
|
|
||||||
|
// make sure value is formatted nicely:
|
||||||
|
if (!is_null($value) && $value !== '') {
|
||||||
|
$value = round($value, $selectedCurrency->decimal_places);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$html = view('form.non-selectable-balance', compact('selectedCurrency', 'classes', 'name', 'label', 'value', 'options'))->render();
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $type
|
* @param $type
|
||||||
* @param $name
|
* @param $name
|
||||||
|
@ -159,7 +159,7 @@ return [
|
|||||||
'ExpandedForm' => [
|
'ExpandedForm' => [
|
||||||
'is_safe' => [
|
'is_safe' => [
|
||||||
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',
|
'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location',
|
||||||
'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall', 'password',
|
'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall', 'password','nonSelectableBalance'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'Form' => [
|
'Form' => [
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* See the LICENSE file for details.
|
* See the LICENSE file for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** global: Modernizr */
|
/** global: Modernizr, currencies */
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
@ -17,4 +17,14 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on change currency drop down list:
|
||||||
|
$('#ffInput_currency_id').change(updateCurrencyItems);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateCurrencyItems() {
|
||||||
|
var value = $('#ffInput_currency_id').val();
|
||||||
|
var symbol = currencies[value];
|
||||||
|
$('.non-selectable-currency-symbol').text(symbol);
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
{{ ExpandedForm.text('name') }}
|
{{ ExpandedForm.text('name') }}
|
||||||
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
|
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
|
||||||
{# Not really mandatory but OK #}
|
{# Not really mandatory but OK #}
|
||||||
{{ ExpandedForm.select('currency_id', currencies) }}
|
{{ ExpandedForm.select('currency_id', currencySelectList) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,10 +36,12 @@
|
|||||||
{{ ExpandedForm.text('accountNumber') }}
|
{{ ExpandedForm.text('accountNumber') }}
|
||||||
|
|
||||||
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
|
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
|
||||||
{{ ExpandedForm.balance('openingBalance',null, {'currency' : openingBalance ? openingBalance.transactionCurrency : null}) }}
|
|
||||||
|
{# get opening balance entry for this thing! #}
|
||||||
|
{{ ExpandedForm.nonSelectableBalance('openingBalance',null, {'currency' : currency }) }}
|
||||||
{{ ExpandedForm.date('openingBalanceDate') }}
|
{{ ExpandedForm.date('openingBalanceDate') }}
|
||||||
{{ ExpandedForm.select('accountRole', roles) }}
|
{{ ExpandedForm.select('accountRole', roles) }}
|
||||||
{{ ExpandedForm.balance('virtualBalance',null) }}
|
{{ ExpandedForm.nonSelectableBalance('virtualBalance',null, {'currency' : currency }) }}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ ExpandedForm.checkbox('active','1') }}
|
{{ ExpandedForm.checkbox('active','1') }}
|
||||||
@ -80,6 +82,15 @@
|
|||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script type="text/javascript" src="js/lib/modernizr-custom.js"></script>
|
<script type="text/javascript" src="js/lib/modernizr-custom.js"></script>
|
||||||
<script type="text/javascript" src="js/lib/jquery-ui.min.js"></script>
|
<script type="text/javascript" src="js/lib/jquery-ui.min.js"></script>
|
||||||
|
|
||||||
|
{# JS currency list for update thing #}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var currencies = [];
|
||||||
|
{% for currency in allCurrencies %}
|
||||||
|
currencies[{{ currency.id }}] = "{{ currency.symbol }}";
|
||||||
|
{% endfor %}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript" src="js/ff/accounts/edit.js"></script>
|
<script type="text/javascript" src="js/ff/accounts/edit.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
11
resources/views/form/non-selectable-balance.twig
Normal file
11
resources/views/form/non-selectable-balance.twig
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="{{ classes }}" id="{{ name }}_holder">
|
||||||
|
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon non-selectable-currency-symbol">{{ selectedCurrency.symbol }}</span>
|
||||||
|
{{ Form.input('number', name, value, options) }}
|
||||||
|
</div>
|
||||||
|
{% include 'form/feedback' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user