1 | <?php |
||||
2 | declare(strict_types=1); |
||||
3 | /** |
||||
4 | * UpgradeDatabase.php |
||||
5 | * Copyright (c) 2017 [email protected] |
||||
6 | * |
||||
7 | * This file is part of Firefly III. |
||||
8 | * |
||||
9 | * Firefly III is free software: you can redistribute it and/or modify |
||||
10 | * it under the terms of the GNU General Public License as published by |
||||
11 | * the Free Software Foundation, either version 3 of the License, or |
||||
12 | * (at your option) any later version. |
||||
13 | * |
||||
14 | * Firefly III is distributed in the hope that it will be useful, |
||||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
17 | * GNU General Public License for more details. |
||||
18 | * |
||||
19 | * You should have received a copy of the GNU General Public License |
||||
20 | * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. |
||||
21 | */ |
||||
22 | |||||
23 | namespace FireflyIII\Console\Commands; |
||||
24 | |||||
25 | use DB; |
||||
26 | use Exception; |
||||
27 | use FireflyIII\Models\Account; |
||||
28 | use FireflyIII\Models\AccountMeta; |
||||
29 | use FireflyIII\Models\AccountType; |
||||
30 | use FireflyIII\Models\Attachment; |
||||
31 | use FireflyIII\Models\Note; |
||||
32 | use FireflyIII\Models\Transaction; |
||||
33 | use FireflyIII\Models\TransactionCurrency; |
||||
34 | use FireflyIII\Models\TransactionJournal; |
||||
35 | use FireflyIII\Models\TransactionJournalMeta; |
||||
36 | use FireflyIII\Models\TransactionType; |
||||
37 | use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; |
||||
38 | use Illuminate\Console\Command; |
||||
39 | use Illuminate\Database\QueryException; |
||||
40 | use Illuminate\Support\Collection; |
||||
41 | use Log; |
||||
42 | use Preferences; |
||||
43 | use Schema; |
||||
44 | |||||
45 | /** |
||||
46 | * Class UpgradeDatabase. |
||||
47 | * |
||||
48 | * Upgrade user database. |
||||
49 | * |
||||
50 | * |
||||
51 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things. |
||||
52 | */ |
||||
53 | class UpgradeDatabase extends Command |
||||
54 | { |
||||
55 | /** |
||||
56 | * The console command description. |
||||
57 | * |
||||
58 | * @var string |
||||
59 | */ |
||||
60 | protected $description = 'Will run various commands to update database records.'; |
||||
61 | /** |
||||
62 | * The name and signature of the console command. |
||||
63 | * |
||||
64 | * @var string |
||||
65 | */ |
||||
66 | protected $signature = 'firefly:upgrade-database'; |
||||
67 | |||||
68 | /** |
||||
69 | * Execute the console command. |
||||
70 | */ |
||||
71 | public function handle() |
||||
72 | { |
||||
73 | $this->setTransactionIdentifier(); |
||||
74 | $this->updateAccountCurrencies(); |
||||
75 | $this->createNewTypes(); |
||||
76 | $this->line('Updating currency information..'); |
||||
77 | $this->updateTransferCurrencies(); |
||||
78 | $this->updateOtherCurrencies(); |
||||
79 | $this->line('Done updating currency information..'); |
||||
80 | $this->migrateNotes(); |
||||
81 | $this->migrateAttachmentData(); |
||||
82 | $this->info('Firefly III database is up to date.'); |
||||
83 | |||||
84 | return; |
||||
85 | } |
||||
86 | |||||
87 | /** |
||||
88 | * This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they are easier |
||||
89 | * to easier to match to their counterpart. When a journal is split, it has two or three transactions: -3, -4 and -5 for example. |
||||
90 | * |
||||
91 | * In the database this is reflected as 6 transactions: -3/+3, -4/+4, -5/+5. |
||||
92 | * |
||||
93 | * When either of these are the same amount, FF3 can't keep them apart: +3/-3, +3/-3, +3/-3. This happens more often than you would |
||||
94 | * think. So each set gets a number (1,2,3) to keep them apart. |
||||
95 | */ |
||||
96 | public function setTransactionIdentifier(): void |
||||
97 | { |
||||
98 | // if table does not exist, return false |
||||
99 | if (!Schema::hasTable('transaction_journals')) { |
||||
100 | return; |
||||
101 | } |
||||
102 | $subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') |
||||
103 | ->whereNull('transaction_journals.deleted_at') |
||||
104 | ->whereNull('transactions.deleted_at') |
||||
105 | ->groupBy(['transaction_journals.id']) |
||||
106 | ->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]); |
||||
107 | $result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived')) |
||||
108 | ->mergeBindings($subQuery->getQuery()) |
||||
109 | ->where('t_count', '>', 2) |
||||
110 | ->select(['id', 't_count']); |
||||
111 | $journalIds = array_unique($result->pluck('id')->toArray()); |
||||
112 | |||||
113 | foreach ($journalIds as $journalId) { |
||||
114 | $this->updateJournalidentifiers((int)$journalId); |
||||
115 | } |
||||
116 | |||||
117 | return; |
||||
118 | } |
||||
119 | |||||
120 | /** |
||||
121 | * Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account. |
||||
122 | * |
||||
123 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's seven but it can't really be helped. |
||||
124 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |
||||
125 | */ |
||||
126 | public function updateAccountCurrencies(): void |
||||
127 | { |
||||
128 | $accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') |
||||
129 | ->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']); |
||||
130 | |||||
131 | $accounts->each( |
||||
132 | function (Account $account) { |
||||
133 | // get users preference, fall back to system pref. |
||||
134 | $defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data; |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
135 | $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); |
||||
136 | $accountCurrency = (int)$account->getMeta('currency_id'); |
||||
137 | $openingBalance = $account->getOpeningBalance(); |
||||
138 | $obCurrency = (int)$openingBalance->transaction_currency_id; |
||||
0 ignored issues
–
show
|
|||||
139 | |||||
140 | // both 0? set to default currency: |
||||
141 | if (0 === $accountCurrency && 0 === $obCurrency) { |
||||
142 | AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete(); |
||||
143 | AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]); |
||||
144 | $this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode)); |
||||
145 | |||||
146 | return true; |
||||
147 | } |
||||
148 | |||||
149 | // account is set to 0, opening balance is not? |
||||
150 | if (0 === $accountCurrency && $obCurrency > 0) { |
||||
151 | AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]); |
||||
152 | $this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode)); |
||||
153 | |||||
154 | return true; |
||||
155 | } |
||||
156 | |||||
157 | // do not match and opening balance id is not null. |
||||
158 | if ($accountCurrency !== $obCurrency && $openingBalance->id > 0) { |
||||
159 | // update opening balance: |
||||
160 | $openingBalance->transaction_currency_id = $accountCurrency; |
||||
161 | $openingBalance->save(); |
||||
162 | $this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name)); |
||||
163 | |||||
164 | return true; |
||||
165 | } |
||||
166 | |||||
167 | return true; |
||||
168 | } |
||||
169 | ); |
||||
170 | |||||
171 | return; |
||||
172 | } |
||||
173 | |||||
174 | /** |
||||
175 | * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for |
||||
176 | * the accounts they are linked to. |
||||
177 | * |
||||
178 | * Both source and destination must match the respective currency preference of the related asset account. |
||||
179 | * So FF3 must verify all transactions. |
||||
180 | * |
||||
181 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |
||||
182 | */ |
||||
183 | public function updateOtherCurrencies(): void |
||||
184 | { |
||||
185 | /** @var CurrencyRepositoryInterface $repository */ |
||||
186 | $repository = app(CurrencyRepositoryInterface::class); |
||||
187 | $set = TransactionJournal |
||||
188 | ::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') |
||||
189 | ->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE]) |
||||
190 | ->get(['transaction_journals.*']); |
||||
191 | |||||
192 | $set->each( |
||||
193 | function (TransactionJournal $journal) use ($repository) { |
||||
194 | // get the transaction with the asset account in it: |
||||
195 | /** @var Transaction $transaction */ |
||||
196 | $transaction = $journal->transactions() |
||||
197 | ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') |
||||
198 | ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') |
||||
199 | ->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->first(['transactions.*']); |
||||
200 | if (null === $transaction) { |
||||
201 | return; |
||||
202 | } |
||||
203 | /** @var Account $account */ |
||||
204 | $account = $transaction->account; |
||||
205 | $currency = $repository->find((int)$account->getMeta('currency_id')); |
||||
206 | $transactions = $journal->transactions()->get(); |
||||
207 | $transactions->each( |
||||
208 | function (Transaction $transaction) use ($currency) { |
||||
209 | if (null === $transaction->transaction_currency_id) { |
||||
210 | $transaction->transaction_currency_id = $currency->id; |
||||
211 | $transaction->save(); |
||||
212 | } |
||||
213 | |||||
214 | // when mismatch in transaction: |
||||
215 | if (!((int)$transaction->transaction_currency_id === (int)$currency->id)) { |
||||
216 | $transaction->foreign_currency_id = (int)$transaction->transaction_currency_id; |
||||
217 | $transaction->foreign_amount = $transaction->amount; |
||||
218 | $transaction->transaction_currency_id = $currency->id; |
||||
219 | $transaction->save(); |
||||
220 | } |
||||
221 | } |
||||
222 | ); |
||||
223 | // also update the journal, of course: |
||||
224 | $journal->transaction_currency_id = $currency->id; |
||||
0 ignored issues
–
show
|
|||||
225 | $journal->save(); |
||||
226 | } |
||||
227 | ); |
||||
228 | |||||
229 | return; |
||||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * This routine verifies that transfers have the correct currency settings for the accounts they are linked to. |
||||
234 | * For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they |
||||
235 | * like it or not. Previous routines MUST have set the currency setting for both accounts for this to work. |
||||
236 | * |
||||
237 | * A transfer always has the |
||||
238 | * |
||||
239 | * Both source and destination must match the respective currency preference. So FF3 must verify ALL |
||||
240 | * transactions. |
||||
241 | */ |
||||
242 | public function updateTransferCurrencies() |
||||
243 | { |
||||
244 | $set = TransactionJournal |
||||
245 | ::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') |
||||
246 | ->where('transaction_types.type', TransactionType::TRANSFER) |
||||
247 | ->get(['transaction_journals.*']); |
||||
248 | |||||
249 | $set->each( |
||||
250 | function (TransactionJournal $transfer) { |
||||
251 | // select all "source" transactions: |
||||
252 | /** @var Collection $transactions */ |
||||
253 | $transactions = $transfer->transactions()->where('amount', '<', 0)->get(); |
||||
254 | $transactions->each( |
||||
255 | function (Transaction $transaction) { |
||||
256 | $this->updateTransactionCurrency($transaction); |
||||
257 | $this->updateJournalCurrency($transaction); |
||||
258 | } |
||||
259 | ); |
||||
260 | } |
||||
261 | ); |
||||
262 | } |
||||
263 | |||||
264 | private function createNewTypes(): void |
||||
265 | { |
||||
266 | // create transaction type "Reconciliation". |
||||
267 | $type = TransactionType::where('type', TransactionType::RECONCILIATION)->first(); |
||||
268 | if (null === $type) { |
||||
269 | TransactionType::create(['type' => TransactionType::RECONCILIATION]); |
||||
270 | } |
||||
271 | $account = AccountType::where('type', AccountType::RECONCILIATION)->first(); |
||||
272 | if (null === $account) { |
||||
273 | AccountType::create(['type' => AccountType::RECONCILIATION]); |
||||
274 | } |
||||
275 | } |
||||
276 | |||||
277 | /** |
||||
278 | * Move the description of each attachment (when not NULL) to the notes or to a new note object |
||||
279 | * for all attachments. |
||||
280 | */ |
||||
281 | private function migrateAttachmentData(): void |
||||
282 | { |
||||
283 | $attachments = Attachment::get(); |
||||
284 | |||||
285 | /** @var Attachment $att */ |
||||
286 | foreach ($attachments as $att) { |
||||
287 | |||||
288 | // move description: |
||||
289 | $description = (string)$att->description; |
||||
290 | if (strlen($description) > 0) { |
||||
291 | // find or create note: |
||||
292 | $note = $att->notes()->first(); |
||||
293 | if (null === $note) { |
||||
294 | $note = new Note; |
||||
295 | $note->noteable()->associate($att); |
||||
296 | } |
||||
297 | $note->text = $description; |
||||
298 | $note->save(); |
||||
299 | |||||
300 | // clear description: |
||||
301 | $att->description = ''; |
||||
0 ignored issues
–
show
|
|||||
302 | $att->save(); |
||||
303 | |||||
304 | Log::debug(sprintf('Migrated attachment #%s description to note #%d', $att->id, $note->id)); |
||||
305 | } |
||||
306 | } |
||||
307 | } |
||||
308 | |||||
309 | /** |
||||
310 | * Move all the journal_meta notes to their note object counter parts. |
||||
311 | */ |
||||
312 | private function migrateNotes(): void |
||||
313 | { |
||||
314 | $set = TransactionJournalMeta::whereName('notes')->get(); |
||||
315 | /** @var TransactionJournalMeta $meta */ |
||||
316 | foreach ($set as $meta) { |
||||
317 | $journal = $meta->transactionJournal; |
||||
318 | $note = $journal->notes()->first(); |
||||
319 | if (null === $note) { |
||||
320 | $note = new Note(); |
||||
321 | $note->noteable()->associate($journal); |
||||
322 | } |
||||
323 | |||||
324 | $note->text = $meta->data; |
||||
325 | $note->save(); |
||||
326 | Log::debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); |
||||
327 | try { |
||||
328 | $meta->delete(); |
||||
329 | } catch (Exception $e) { |
||||
330 | Log::error(sprintf('Could not delete old meta entry #%d: %s', $meta->id, $e->getMessage())); |
||||
331 | } |
||||
332 | } |
||||
333 | } |
||||
334 | |||||
335 | /** |
||||
336 | * This method makes sure that the transaction journal uses the currency given in the transaction. |
||||
337 | * |
||||
338 | * @param Transaction $transaction |
||||
339 | */ |
||||
340 | private function updateJournalCurrency(Transaction $transaction): void |
||||
341 | { |
||||
342 | /** @var CurrencyRepositoryInterface $repository */ |
||||
343 | $repository = app(CurrencyRepositoryInterface::class); |
||||
344 | $currency = $repository->find((int)$transaction->account->getMeta('currency_id')); |
||||
345 | $journal = $transaction->transactionJournal; |
||||
346 | |||||
347 | if (!((int)$currency->id === (int)$journal->transaction_currency_id)) { |
||||
0 ignored issues
–
show
|
|||||
348 | $this->line( |
||||
349 | sprintf( |
||||
350 | 'Transfer #%d ("%s") has been updated to use %s instead of %s.', |
||||
351 | $journal->id, |
||||
352 | $journal->description, |
||||
353 | $currency->code, |
||||
354 | $journal->transactionCurrency->code |
||||
355 | ) |
||||
356 | ); |
||||
357 | $journal->transaction_currency_id = $currency->id; |
||||
358 | $journal->save(); |
||||
359 | } |
||||
360 | |||||
361 | return; |
||||
362 | } |
||||
363 | |||||
364 | /** |
||||
365 | * grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one |
||||
366 | * which has 0 as an identifier and give it the same identifier. |
||||
367 | * |
||||
368 | * @param int $journalId |
||||
369 | */ |
||||
370 | private function updateJournalidentifiers(int $journalId): void |
||||
371 | { |
||||
372 | $identifier = 0; |
||||
373 | $processed = []; |
||||
374 | $transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get(); |
||||
375 | /** @var Transaction $transaction */ |
||||
376 | foreach ($transactions as $transaction) { |
||||
377 | // find opposing: |
||||
378 | $amount = bcmul((string)$transaction->amount, '-1'); |
||||
379 | |||||
380 | try { |
||||
381 | /** @var Transaction $opposing */ |
||||
382 | $opposing = Transaction::where('transaction_journal_id', $journalId) |
||||
383 | ->where('amount', $amount)->where('identifier', '=', 0) |
||||
384 | ->whereNotIn('id', $processed) |
||||
385 | ->first(); |
||||
386 | } catch (QueryException $e) { |
||||
387 | Log::error($e->getMessage()); |
||||
388 | $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); |
||||
389 | $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); |
||||
390 | $this->error('Please run "php artisan migrate" to add this field to the table.'); |
||||
391 | $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); |
||||
392 | |||||
393 | return; |
||||
394 | } |
||||
395 | if (null !== $opposing) { |
||||
396 | // give both a new identifier: |
||||
397 | $transaction->identifier = $identifier; |
||||
398 | $opposing->identifier = $identifier; |
||||
399 | $transaction->save(); |
||||
400 | $opposing->save(); |
||||
401 | $processed[] = $transaction->id; |
||||
402 | $processed[] = $opposing->id; |
||||
403 | } |
||||
404 | ++$identifier; |
||||
405 | } |
||||
406 | |||||
407 | return; |
||||
408 | } |
||||
409 | |||||
410 | /** |
||||
411 | * This method makes sure that the tranaction uses the same currency as the source account does. |
||||
412 | * If not, the currency is updated to include a reference to its original currency as the "foreign" currency. |
||||
413 | * |
||||
414 | * The transaction that is sent to this function MUST be the source transaction (amount negative). |
||||
415 | * |
||||
416 | * Method is long and complex bit I'm taking it for granted. |
||||
417 | * |
||||
418 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |
||||
419 | * @SuppressWarnings(PHPMD.NPathComplexity) |
||||
420 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||||
421 | * |
||||
422 | * @param Transaction $transaction |
||||
423 | */ |
||||
424 | private function updateTransactionCurrency(Transaction $transaction): void |
||||
425 | { |
||||
426 | /** @var CurrencyRepositoryInterface $repository */ |
||||
427 | $repository = app(CurrencyRepositoryInterface::class); |
||||
428 | $currency = $repository->find((int)$transaction->account->getMeta('currency_id')); |
||||
429 | |||||
430 | // has no currency ID? Must have, so fill in using account preference: |
||||
431 | if (null === $transaction->transaction_currency_id) { |
||||
432 | $transaction->transaction_currency_id = (int)$currency->id; |
||||
433 | Log::debug(sprintf('Transaction #%d has no currency setting, now set to %s', $transaction->id, $currency->code)); |
||||
434 | $transaction->save(); |
||||
435 | } |
||||
436 | |||||
437 | // does not match the source account (see above)? Can be fixed |
||||
438 | // when mismatch in transaction and NO foreign amount is set: |
||||
439 | if (!((int)$transaction->transaction_currency_id === (int)$currency->id) && null === $transaction->foreign_amount) { |
||||
440 | Log::debug( |
||||
441 | sprintf( |
||||
442 | 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', |
||||
443 | $transaction->id, |
||||
444 | $transaction->transaction_currency_id, |
||||
445 | $currency->id, |
||||
446 | $transaction->amount |
||||
447 | ) |
||||
448 | ); |
||||
449 | $transaction->transaction_currency_id = (int)$currency->id; |
||||
450 | $transaction->save(); |
||||
451 | } |
||||
452 | |||||
453 | // grab opposing transaction: |
||||
454 | /** @var TransactionJournal $journal */ |
||||
455 | $journal = $transaction->transactionJournal; |
||||
456 | /** @var Transaction $opposing */ |
||||
457 | $opposing = $journal->transactions()->where('amount', '>', 0)->where('identifier', $transaction->identifier)->first(); |
||||
458 | $opposingCurrency = $repository->find((int)$opposing->account->getMeta('currency_id')); |
||||
459 | |||||
460 | if (null === $opposingCurrency->id) { |
||||
461 | Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $opposing->account->id, $opposing->account->name)); |
||||
462 | |||||
463 | return; |
||||
464 | } |
||||
465 | |||||
466 | // if the destination account currency is the same, both foreign_amount and foreign_currency_id must be NULL for both transactions: |
||||
467 | if ((int)$opposingCurrency->id === (int)$currency->id) { |
||||
468 | // update both transactions to match: |
||||
469 | $transaction->foreign_amount = null; |
||||
470 | $transaction->foreign_currency_id = null; |
||||
471 | $opposing->foreign_amount = null; |
||||
472 | $opposing->foreign_currency_id = null; |
||||
473 | $opposing->transaction_currency_id = $currency->id; |
||||
474 | $transaction->save(); |
||||
475 | $opposing->save(); |
||||
476 | Log::debug(sprintf('Cleaned up transaction #%d and #%d', $transaction->id, $opposing->id)); |
||||
477 | |||||
478 | return; |
||||
479 | } |
||||
480 | // if destination account currency is different, both transactions must have this currency as foreign currency id. |
||||
481 | if (!((int)$opposingCurrency->id === (int)$currency->id)) { |
||||
482 | $transaction->foreign_currency_id = $opposingCurrency->id; |
||||
483 | $opposing->foreign_currency_id = $opposingCurrency->id; |
||||
484 | $transaction->save(); |
||||
485 | $opposing->save(); |
||||
486 | Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $transaction->id, $opposing->id)); |
||||
487 | } |
||||
488 | |||||
489 | // if foreign amount of one is null and the other is not, use this to restore: |
||||
490 | if (null === $transaction->foreign_amount && null !== $opposing->foreign_amount) { |
||||
491 | $transaction->foreign_amount = bcmul((string)$opposing->foreign_amount, '-1'); |
||||
492 | $transaction->save(); |
||||
493 | Log::debug(sprintf('Restored foreign amount of transaction (1) #%d to %s', $transaction->id, $transaction->foreign_amount)); |
||||
494 | } |
||||
495 | |||||
496 | // if foreign amount of one is null and the other is not, use this to restore (other way around) |
||||
497 | if (null === $opposing->foreign_amount && null !== $transaction->foreign_amount) { |
||||
498 | $opposing->foreign_amount = bcmul((string)$transaction->foreign_amount, '-1'); |
||||
499 | $opposing->save(); |
||||
500 | Log::debug(sprintf('Restored foreign amount of transaction (2) #%d to %s', $opposing->id, $opposing->foreign_amount)); |
||||
501 | } |
||||
502 | |||||
503 | // when both are zero, try to grab it from journal: |
||||
504 | if (null === $opposing->foreign_amount && null === $transaction->foreign_amount) { |
||||
505 | $foreignAmount = $journal->getMeta('foreign_amount'); |
||||
506 | if (null === $foreignAmount) { |
||||
507 | Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $transaction->transaction_journal_id)); |
||||
508 | $transaction->foreign_amount = bcmul((string)$transaction->amount, '-1'); |
||||
509 | $opposing->foreign_amount = bcmul((string)$opposing->amount, '-1'); |
||||
510 | $transaction->save(); |
||||
511 | $opposing->save(); |
||||
512 | |||||
513 | return; |
||||
514 | } |
||||
515 | $foreignPositive = app('steam')->positive((string)$foreignAmount); |
||||
0 ignored issues
–
show
The method
positive() does not exist on FireflyIII\Support\Facades\Steam .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
516 | Log::debug( |
||||
517 | sprintf( |
||||
518 | 'Journal #%d has missing foreign currency info, try to restore from meta-data ("%s").', |
||||
519 | $transaction->transaction_journal_id, |
||||
0 ignored issues
–
show
|
|||||
520 | $foreignAmount |
||||
521 | ) |
||||
522 | ); |
||||
523 | $transaction->foreign_amount = bcmul($foreignPositive, '-1'); |
||||
524 | $opposing->foreign_amount = $foreignPositive; |
||||
525 | $transaction->save(); |
||||
526 | $opposing->save(); |
||||
527 | } |
||||
528 | |||||
529 | return; |
||||
530 | } |
||||
531 | |||||
532 | } |
||||
533 |