GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 49de82...9f1fc0 )
by James
35:45 queued 23:44
created

AccountRepository::getInactiveAccountsByType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 16
rs 9.9666
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * AccountRepository.php
4
 * Copyright (c) 2019 [email protected]
5
 *
6
 * This file is part of Firefly III (https://github.com/firefly-iii).
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License as
10
 * published by the Free Software Foundation, either version 3 of the
11
 * License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Repositories\Account;
24
25
use Carbon\Carbon;
26
use FireflyIII\Exceptions\FireflyException;
27
use FireflyIII\Factory\AccountFactory;
28
use FireflyIII\Models\Account;
29
use FireflyIII\Models\AccountMeta;
30
use FireflyIII\Models\AccountType;
31
use FireflyIII\Models\TransactionCurrency;
32
use FireflyIII\Models\TransactionGroup;
33
use FireflyIII\Models\TransactionJournal;
34
use FireflyIII\Models\TransactionType;
35
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
36
use FireflyIII\Services\Internal\Update\AccountUpdateService;
37
use FireflyIII\User;
38
use Illuminate\Database\Eloquent\Relations\HasMany;
39
use Illuminate\Support\Collection;
40
use Log;
41
42
/**
43
 * Class AccountRepository.
44
 *
45
 */
46
class AccountRepository implements AccountRepositoryInterface
47
{
48
49
    /** @var User */
50
    private $user;
51
52
    /**
53
     * Constructor.
54
     */
55
    public function __construct()
56
    {
57
        if ('testing' === config('app.env')) {
58
            Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
59
        }
60
    }
61
62
    /**
63
     * @param array $types
64
     *
65
     * @return int
66
     */
67
    public function count(array $types): int
68
    {
69
        return $this->user->accounts()->accountTypeIn($types)->count();
70
    }
71
72
    /**
73
     * Moved here from account CRUD.
74
     *
75
     * @param Account      $account
76
     * @param Account|null $moveTo
77
     *
78
     * @return bool
79
     *
80
81
     */
82
    public function destroy(Account $account, ?Account $moveTo): bool
83
    {
84
        /** @var AccountDestroyService $service */
85
        $service = app(AccountDestroyService::class);
86
        $service->destroy($account, $moveTo);
87
88
        return true;
89
    }
90
91
    /**
92
     * Find account with same name OR same IBAN or both, but not the same type or ID.
93
     *
94
     * @param Collection $accounts
95
     *
96
     * @return Collection
97
     */
98
    public function expandWithDoubles(Collection $accounts): Collection
99
    {
100
        $result = new Collection;
101
        /** @var Account $account */
102
        foreach ($accounts as $account) {
103
            $byName = $this->user->accounts()->where('name', $account->name)
104
                                 ->where('id', '!=', $account->id)->first();
105
            if (null !== $byName) {
106
                $result->push($account);
107
                $result->push($byName);
108
                continue;
109
            }
110
            if (null !== $account->iban) {
111
                $byIban = $this->user->accounts()->where('iban', $account->iban)
112
                                     ->where('id', '!=', $account->id)->first();
113
                if (null !== $byIban) {
114
                    $result->push($account);
115
                    $result->push($byIban);
116
                    continue;
117
                }
118
            }
119
            $result->push($account);
120
        }
121
122
        return $result;
123
124
    }
125
126
    /**
127
     * @param string $number
128
     * @param array  $types
129
     *
130
     * @return Account|null
131
     */
132
    public function findByAccountNumber(string $number, array $types): ?Account
133
    {
134
        $query = $this->user->accounts()
135
                            ->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
136
                            ->where('account_meta.name', 'account_number')
137
                            ->where('account_meta.data', json_encode($number));
138
139
        if (count($types) > 0) {
140
            $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
141
            $query->whereIn('account_types.type', $types);
142
        }
143
144
        /** @var Collection $accounts */
145
        $accounts = $query->get(['accounts.*']);
146
        if ($accounts->count() > 0) {
147
            return $accounts->first();
148
        }
149
150
        return null;
151
    }
152
153
    /**
154
     * @param string $iban
155
     * @param array  $types
156
     *
157
     * @return Account|null
158
     */
159
    public function findByIbanNull(string $iban, array $types): ?Account
160
    {
161
        $query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
162
163
        if (count($types) > 0) {
164
            $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
165
            $query->whereIn('account_types.type', $types);
166
        }
167
168
        // TODO a loop like this is no longer necessary
169
170
        $accounts = $query->get(['accounts.*']);
171
        /** @var Account $account */
172
        foreach ($accounts as $account) {
173
            if ($account->iban === $iban) {
174
                return $account;
175
            }
176
        }
177
178
        return null;
179
    }
180
181
    /**
182
     * @param string $name
183
     * @param array  $types
184
     *
185
     * @return Account|null
186
     */
187
    public function findByName(string $name, array $types): ?Account
188
    {
189
        $query = $this->user->accounts();
190
191
        if (count($types) > 0) {
192
            $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
193
            $query->whereIn('account_types.type', $types);
194
        }
195
        Log::debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]);
196
197
        $accounts = $query->get(['accounts.*']);
198
199
        // TODO no longer need to loop like this
200
201
        /** @var Account $account */
202
        foreach ($accounts as $account) {
203
            if ($account->name === $name) {
204
                Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id));
205
206
                return $account;
207
            }
208
        }
209
        Log::debug(sprintf('There is no account with name "%s" of types', $name), $types);
210
211
        return null;
212
    }
213
214
    /**
215
     * @param int $accountId
216
     *
217
     * @return Account|null
218
     */
219
    public function findNull(int $accountId): ?Account
220
    {
221
        return $this->user->accounts()->find($accountId);
222
    }
223
224
    /**
225
     * @param Account $account
226
     *
227
     * @return TransactionCurrency|null
228
     */
229
    public function getAccountCurrency(Account $account): ?TransactionCurrency
230
    {
231
        $currencyId = (int)$this->getMetaValue($account, 'currency_id');
232
        if ($currencyId > 0) {
233
            return TransactionCurrency::find($currencyId);
234
        }
235
236
        return null;
237
    }
238
239
    /**
240
     * @param Account $account
241
     *
242
     * @return string
243
     */
244
    public function getAccountType(Account $account): string
245
    {
246
        return $account->accountType->type;
247
    }
248
249
    /**
250
     * Return account type or null if not found.
251
     *
252
     * @param string $type
253
     *
254
     * @return AccountType|null
255
     */
256
    public function getAccountTypeByType(string $type): ?AccountType
257
    {
258
        return AccountType::whereType($type)->first();
259
    }
260
261
    /**
262
     * @param array $accountIds
263
     *
264
     * @return Collection
265
     */
266
    public function getAccountsById(array $accountIds): Collection
267
    {
268
        /** @var Collection $result */
269
        $query = $this->user->accounts();
270
271
        if (count($accountIds) > 0) {
272
            $query->whereIn('accounts.id', $accountIds);
273
        }
274
        $query->orderBy('accounts.active', 'DESC');
275
        $query->orderBy('accounts.name', 'ASC');
276
277
        $result = $query->get(['accounts.*']);
278
279
        return $result;
280
    }
281
282
    /**
283
     * @param array $types
284
     *
285
     * @return Collection
286
     */
287
    public function getAccountsByType(array $types): Collection
288
    {
289
        /** @var Collection $result */
290
        $query = $this->user->accounts();
291
        if (count($types) > 0) {
292
            $query->accountTypeIn($types);
293
        }
294
        $query->orderBy('accounts.active', 'DESC');
295
        $query->orderBy('accounts.name', 'ASC');
296
        $result = $query->get(['accounts.*']);
297
298
299
        return $result;
300
    }
301
302
    /**
303
     * @param array $types
304
     *
305
     * @return Collection
306
     */
307
    public function getActiveAccountsByType(array $types): Collection
308
    {
309
        /** @var Collection $result */
310
        $query = $this->user->accounts()->with(
311
            ['accountmeta' => function (HasMany $query) {
312
                $query->where('name', 'account_role');
313
            }]
314
        );
315
        if (count($types) > 0) {
316
            $query->accountTypeIn($types);
317
        }
318
        $query->where('active', 1);
319
        $query->orderBy('accounts.account_type_id', 'ASC');
320
        $query->orderBy('accounts.name', 'ASC');
321
322
        return $query->get(['accounts.*']);
323
    }
324
325
    /**
326
     * @return Account
327
     *
328
     * @throws FireflyException
329
     */
330
    public function getCashAccount(): Account
331
    {
332
        /** @var AccountType $type */
333
        $type = AccountType::where('type', AccountType::CASH)->first();
334
        /** @var AccountFactory $factory */
335
        $factory = app(AccountFactory::class);
336
        $factory->setUser($this->user);
337
338
        return $factory->findOrCreate('Cash account', $type->type);
339
    }
340
341
    /**
342
     * Return meta value for account. Null if not found.
343
     *
344
     * @param Account $account
345
     * @param string  $field
346
     *
347
     * @return null|string
348
     */
349
    public function getMetaValue(Account $account, string $field): ?string
350
    {
351
        /** @var AccountMeta $meta */
352
        $meta = $account->accountMeta()->where('name', $field)->first();
353
        if (null === $meta) {
354
            return null;
355
        }
356
357
        return (string)$meta->data;
358
    }
359
360
    /**
361
     * Get note text or null.
362
     *
363
     * @param Account $account
364
     *
365
     * @return null|string
366
     */
367
    public function getNoteText(Account $account): ?string
368
    {
369
        $note = $account->notes()->first();
370
371
        if (null === $note) {
372
            return null;
373
        }
374
375
        return $note->text;
376
    }
377
378
    /**
379
     * @param Account $account
380
     *
381
     * @return TransactionJournal|null
382
     */
383
    public function getOpeningBalance(Account $account): ?TransactionJournal
384
    {
385
        return TransactionJournal
386
            ::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
387
            ->where('transactions.account_id', $account->id)
388
            ->transactionTypes([TransactionType::OPENING_BALANCE])
389
            ->first(['transaction_journals.*']);
390
    }
391
392
    /**
393
     * Returns the amount of the opening balance for this account.
394
     *
395
     * @param Account $account
396
     *
397
     * @return string
398
     */
399
    public function getOpeningBalanceAmount(Account $account): ?string
400
    {
401
402
        $journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
403
                                     ->where('transactions.account_id', $account->id)
404
                                     ->transactionTypes([TransactionType::OPENING_BALANCE])
405
                                     ->first(['transaction_journals.*']);
406
        if (null === $journal) {
407
            return null;
408
        }
409
        $transaction = $journal->transactions()->where('account_id', $account->id)->first();
410
        if (null === $transaction) {
411
            return null;
412
        }
413
414
        return (string)$transaction->amount;
415
    }
416
417
    /**
418
     * Return date of opening balance as string or null.
419
     *
420
     * @param Account $account
421
     *
422
     * @return null|string
423
     */
424
    public function getOpeningBalanceDate(Account $account): ?string
425
    {
426
        $journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
427
                                     ->where('transactions.account_id', $account->id)
428
                                     ->transactionTypes([TransactionType::OPENING_BALANCE])
429
                                     ->first(['transaction_journals.*']);
430
        if (null === $journal) {
431
            return null;
432
        }
433
434
        return $journal->date->format('Y-m-d');
435
    }
436
437
    /**
438
     * @param Account $account
439
     *
440
     * @return TransactionGroup|null
441
     */
442
    public function getOpeningBalanceGroup(Account $account): ?TransactionGroup
443
    {
444
        $journal = $this->getOpeningBalance($account);
445
        $group   = null;
446
        if (null !== $journal) {
447
            $group = $journal->transactionGroup;
448
        }
449
450
        return $group;
451
    }
452
453
    /**
454
     * @param Account $account
455
     *
456
     * @return Collection
457
     */
458
    public function getPiggyBanks(Account $account): Collection
459
    {
460
        return $account->piggyBanks()->get();
461
    }
462
463
    /**
464
     * @param Account $account
465
     *
466
     * @return Account|null
467
     *
468
     * @throws FireflyException
469
     */
470
    public function getReconciliation(Account $account): ?Account
471
    {
472
        if (AccountType::ASSET !== $account->accountType->type) {
473
            throw new FireflyException(sprintf('%s is not an asset account.', $account->name));
474
        }
475
        $name = $account->name . ' reconciliation';
476
        /** @var AccountType $type */
477
        $type     = AccountType::where('type', AccountType::RECONCILIATION)->first();
478
        $accounts = $this->user->accounts()->where('account_type_id', $type->id)->get();
479
480
        // TODO no longer need to loop like this
481
482
        /** @var Account $current */
483
        foreach ($accounts as $current) {
484
            if ($current->name === $name) {
485
                return $current;
486
            }
487
        }
488
        /** @var AccountFactory $factory */
489
        $factory = app(AccountFactory::class);
490
        $factory->setUser($account->user);
491
        $account = $factory->findOrCreate($name, $type->type);
492
493
        return $account;
494
    }
495
496
    /**
497
     * @param Account $account
498
     *
499
     * @return bool
500
     */
501
    public function isLiability(Account $account): bool
502
    {
503
        return in_array($account->accountType->type, [AccountType::CREDITCARD, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], true);
504
    }
505
506
    /**
507
     * Returns the date of the very first transaction in this account.
508
     *
509
     * @param Account $account
510
     *
511
     * @return TransactionJournal|null
512
     */
513
    public function oldestJournal(Account $account): ?TransactionJournal
514
    {
515
        $first = $account->transactions()
516
                         ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
517
                         ->orderBy('transaction_journals.date', 'ASC')
518
                         ->orderBy('transaction_journals.order', 'DESC')
519
                         ->where('transaction_journals.user_id', $this->user->id)
520
                         ->orderBy('transaction_journals.id', 'ASC')
521
                         ->first(['transaction_journals.id']);
522
        if (null !== $first) {
523
            return TransactionJournal::find((int)$first->id);
524
        }
525
526
        return null;
527
    }
528
529
    /**
530
     * Returns the date of the very first transaction in this account.
531
     *
532
     * @param Account $account
533
     *
534
     * @return Carbon|null
535
     */
536
    public function oldestJournalDate(Account $account): ?Carbon
537
    {
538
        $result  = null;
539
        $journal = $this->oldestJournal($account);
540
        if (null !== $journal) {
541
            $result = $journal->date;
542
        }
543
544
        return $result;
545
    }
546
547
    /**
548
     * @param string $query
549
     * @param array  $types
550
     *
551
     * @return Collection
552
     */
553
    public function searchAccount(string $query, array $types): Collection
554
    {
555
        $dbQuery = $this->user->accounts()
556
                              ->where('active', 1)
557
                              ->with(['accountType']);
558
        if ('' !== $query) {
559
            $search = sprintf('%%%s%%', $query);
560
            $dbQuery->where('name', 'LIKE', $search);
561
        }
562
        if (count($types) > 0) {
563
            $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
564
            $dbQuery->whereIn('account_types.type', $types);
565
        }
566
567
        return $dbQuery->get(['accounts.*']);
568
    }
569
570
    /**
571
     * @param User $user
572
     */
573
    public function setUser(User $user): void
574
    {
575
        $this->user = $user;
576
    }
577
578
    /**
579
     * @param array $data
580
     *
581
     * @return Account
582
     * @throws FireflyException
583
     */
584
    public function store(array $data): Account
585
    {
586
        /** @var AccountFactory $factory */
587
        $factory = app(AccountFactory::class);
588
        $factory->setUser($this->user);
589
590
        return $factory->create($data);
591
    }
592
593
    /**
594
     * @param Account $account
595
     * @param array   $data
596
     *
597
     * @return Account
598
     * @throws FireflyException
599
     */
600
    public function update(Account $account, array $data): Account
601
    {
602
        /** @var AccountUpdateService $service */
603
        $service = app(AccountUpdateService::class);
604
        $account = $service->update($account, $data);
605
606
        return $account;
607
    }
608
609
    /**
610
     * @param array $types
611
     *
612
     * @return Collection
613
     */
614
    public function getInactiveAccountsByType(array $types): Collection
615
    {
616
        /** @var Collection $result */
617
        $query = $this->user->accounts()->with(
618
            ['accountmeta' => function (HasMany $query) {
619
                $query->where('name', 'account_role');
620
            }]
621
        );
622
        if (count($types) > 0) {
623
            $query->accountTypeIn($types);
624
        }
625
        $query->where('active', 0);
626
        $query->orderBy('accounts.account_type_id', 'ASC');
627
        $query->orderBy('accounts.name', 'ASC');
628
629
        return $query->get(['accounts.*']);
630
    }
631
}
632