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 FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\LimitRepetition;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Schema;
|
||||
|
||||
/**
|
||||
@ -63,8 +68,13 @@ class UpgradeDatabase extends Command
|
||||
$this->setTransactionIdentifier();
|
||||
$this->migrateRepetitions();
|
||||
$this->repairPiggyBanks();
|
||||
$this->updateAccountCurrencies();
|
||||
$this->info('Firefly III database is up to date.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate budget repetitions to new format.
|
||||
*/
|
||||
private function migrateRepetitions()
|
||||
{
|
||||
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
|
||||
* which has 0 as an identifier and give it the same identifier.
|
||||
|
@ -147,14 +147,14 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function edit(Request $request, Account $account)
|
||||
{
|
||||
|
||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
||||
$roles = [];
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
|
||||
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
|
||||
$allCurrencies = $repository->get();
|
||||
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
|
||||
$roles = [];
|
||||
foreach (config('firefly.accountRoles') as $role) {
|
||||
$roles[$role] = strval(trans('firefly.account_role_' . $role));
|
||||
}
|
||||
@ -173,6 +173,7 @@ class AccountController extends Controller
|
||||
$openingBalanceAmount = $account->getOpeningBalanceAmount() === '0' ? '' : $openingBalanceAmount;
|
||||
$openingBalanceDate = $account->getOpeningBalanceDate();
|
||||
$openingBalanceDate = $openingBalanceDate->year === 1900 ? null : $openingBalanceDate->format('Y-m-d');
|
||||
$currency = $repository->find(intval($account->getMeta('currency_id')));
|
||||
|
||||
$preFilled = [
|
||||
'accountNumber' => $account->getMeta('accountNumber'),
|
||||
@ -183,13 +184,18 @@ class AccountController extends Controller
|
||||
'openingBalanceDate' => $openingBalanceDate,
|
||||
'openingBalance' => $openingBalanceAmount,
|
||||
'virtualBalance' => $account->virtual_balance,
|
||||
'currency_id' => $account->getMeta('currency_id'),
|
||||
'currency_id' => $currency->id,
|
||||
|
||||
];
|
||||
$request->session()->flash('preFilled', $preFilled);
|
||||
$request->session()->flash('gaEventCategory', 'accounts');
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -530,12 +530,10 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
}
|
||||
// opening balance data? update it!
|
||||
if (!is_null($openingBalance->id)) {
|
||||
$date = $data['openingBalanceDate'];
|
||||
$amount = $data['openingBalance'];
|
||||
|
||||
Log::debug('Opening balance journal found, update journal.');
|
||||
|
||||
$this->updateOpeningBalanceJournal($account, $openingBalance, $date, $amount);
|
||||
$this->updateOpeningBalanceJournal($account, $openingBalance, $data);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -589,15 +587,19 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param TransactionJournal $journal
|
||||
* @param Carbon $date
|
||||
* @param float $amount
|
||||
* @param array $data
|
||||
*
|
||||
* @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:
|
||||
$journal->date = $date;
|
||||
$journal->date = $date;
|
||||
$journal->transaction_currency_id = $currencyId;
|
||||
$journal->save();
|
||||
// update transactions:
|
||||
/** @var Transaction $transaction */
|
||||
|
@ -137,4 +137,12 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 $name
|
||||
|
@ -159,7 +159,7 @@ return [
|
||||
'ExpandedForm' => [
|
||||
'is_safe' => [
|
||||
'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' => [
|
||||
|
@ -6,7 +6,7 @@
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/** global: Modernizr */
|
||||
/** global: Modernizr, currencies */
|
||||
|
||||
$(document).ready(function () {
|
||||
"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') }}
|
||||
{% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %}
|
||||
{# Not really mandatory but OK #}
|
||||
{{ ExpandedForm.select('currency_id', currencies) }}
|
||||
{{ ExpandedForm.select('currency_id', currencySelectList) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@ -36,10 +36,12 @@
|
||||
{{ ExpandedForm.text('accountNumber') }}
|
||||
|
||||
{% 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.select('accountRole', roles) }}
|
||||
{{ ExpandedForm.balance('virtualBalance',null) }}
|
||||
{{ ExpandedForm.nonSelectableBalance('virtualBalance',null, {'currency' : currency }) }}
|
||||
|
||||
{% endif %}
|
||||
{{ ExpandedForm.checkbox('active','1') }}
|
||||
@ -80,6 +82,15 @@
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="js/lib/modernizr-custom.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>
|
||||
{% 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