diff --git a/app/Console/Commands/UpgradeDatabase.php b/app/Console/Commands/UpgradeDatabase.php index dcaebf9050..6399df4adf 100644 --- a/app/Console/Commands/UpgradeDatabase.php +++ b/app/Console/Commands/UpgradeDatabase.php @@ -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. diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 83098c0fb6..633b8804b8 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -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' + ) + ); } /** diff --git a/app/Models/Account.php b/app/Models/Account.php index 268f843e61..cfef7e34af 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -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. * diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index a091f92f3e..52d32d47f5 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -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 */ diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index c173941f10..f51c398f0b 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -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; + } diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 25ad689f2e..6fa0850bd2 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -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 diff --git a/config/twigbridge.php b/config/twigbridge.php index dc51e30651..1733496d56 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -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' => [ diff --git a/public/js/ff/accounts/edit.js b/public/js/ff/accounts/edit.js index 675a5fe730..cb275d4ab6 100644 --- a/public/js/ff/accounts/edit.js +++ b/public/js/ff/accounts/edit.js @@ -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); +} diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index e01e7f98ee..5123ee9751 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -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 %} @@ -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 %} + + {# JS currency list for update thing #} + + {% endblock %} diff --git a/resources/views/form/non-selectable-balance.twig b/resources/views/form/non-selectable-balance.twig new file mode 100644 index 0000000000..1391873842 --- /dev/null +++ b/resources/views/form/non-selectable-balance.twig @@ -0,0 +1,11 @@ +
+ + +
+
+ {{ selectedCurrency.symbol }} + {{ Form.input('number', name, value, options) }} +
+ {% include 'form/feedback' %} +
+