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.
Completed
Push — master ( bae40e...a4cb2c )
by James
19:36 queued 09:47
created

AccountRepository::latestJournal()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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