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 ( 37b02e...ebbbe1 )
by James
08:59
created

app/Import/Object/ImportAccount.php (1 issue)

Languages
Labels
Severity
1
<?php
2
/**
3
 * ImportAccount.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\Import\Object;
24
25
use FireflyIII\Exceptions\FireflyException;
26
use FireflyIII\Models\Account;
27
use FireflyIII\Models\AccountType;
28
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
29
use FireflyIII\User;
30
use Log;
31
32
/**
33
 * Class ImportAccount.
34
 */
35
class ImportAccount
36
{
37
    /** @var Account */
38
    private $account;
39
    /** @var array */
40
    private $accountBic = [];
41
    /** @var array */
42
    private $accountIban = [];
43
    /** @var array */
44
    private $accountId = [];
45
    /** @var array */
46
    private $accountName = [];
47
    /** @var array */
48
    private $accountNumber = [];
49
    /** @var int */
50
    private $defaultAccountId = 0;
51
    /** @var string */
52
    private $expectedType = '';
53
    /**
54
     * This value is used to indicate the other account ID (the opposing transaction's account),
55
     * if it is know. If so, this particular import account may never return an Account with this ID.
56
     * If it would, this would result in a transaction from-to the same account.
57
     *
58
     * @var int
59
     */
60
    private $forbiddenAccountId = 0;
61
    /** @var AccountRepositoryInterface */
62
    private $repository;
63
    /** @var User */
64
    private $user;
65
66
    /**
67
     * ImportAccount constructor.
68
     */
69
    public function __construct()
70
    {
71
        $this->expectedType = AccountType::ASSET;
72
        $this->repository   = app(AccountRepositoryInterface::class);
73
        Log::debug('Created ImportAccount.');
74
    }
75
76
    /**
77
     * @return Account
78
     *
79
     * @throws FireflyException
80
     */
81
    public function getAccount(): Account
82
    {
83
        if (null === $this->account) {
84
            $this->store();
85
        }
86
87
        return $this->account;
88
    }
89
90
    /**
91
     * @codeCoverageIgnore
92
     *
93
     * @return string
94
     */
95
    public function getExpectedType(): string
96
    {
97
        return $this->expectedType;
98
    }
99
100
    /**
101
     * @codeCoverageIgnore
102
     *
103
     * @param string $expectedType
104
     */
105
    public function setExpectedType(string $expectedType)
106
    {
107
        $this->expectedType = $expectedType;
108
    }
109
110
    /**
111
     * @param array $accountBic
112
     */
113
    public function setAccountBic(array $accountBic): void
114
    {
115
        $this->accountBic = $accountBic;
116
    }
117
118
    /**
119
     * @codeCoverageIgnore
120
     *
121
     * @param array $accountIban
122
     */
123
    public function setAccountIban(array $accountIban)
124
    {
125
        $this->accountIban = $accountIban;
126
    }
127
128
    /**
129
     * @codeCoverageIgnore
130
     *
131
     * @param array $value
132
     */
133
    public function setAccountId(array $value)
134
    {
135
        $this->accountId = $value;
136
    }
137
138
    /**
139
     * @codeCoverageIgnore
140
     *
141
     * @param array $accountName
142
     */
143
    public function setAccountName(array $accountName)
144
    {
145
        $this->accountName = $accountName;
146
    }
147
148
    /**
149
     * @codeCoverageIgnore
150
     *
151
     * @param array $accountNumber
152
     */
153
    public function setAccountNumber(array $accountNumber)
154
    {
155
        $this->accountNumber = $accountNumber;
156
    }
157
158
    /**
159
     * @codeCoverageIgnore
160
     *
161
     * @param int $defaultAccountId
162
     */
163
    public function setDefaultAccountId(int $defaultAccountId)
164
    {
165
        $this->defaultAccountId = $defaultAccountId;
166
    }
167
168
    /**
169
     * @codeCoverageIgnore
170
     *
171
     * @param int $forbiddenAccountId
172
     */
173
    public function setForbiddenAccountId(int $forbiddenAccountId)
174
    {
175
        $this->forbiddenAccountId = $forbiddenAccountId;
176
    }
177
178
    /**
179
     * @codeCoverageIgnore
180
     *
181
     * @param User $user
182
     */
183
    public function setUser(User $user)
184
    {
185
        $this->user = $user;
186
        $this->repository->setUser($user);
187
    }
188
189
    /**
190
     * Find account by IBAN and type.
191
     *
192
     * @param AccountType $type
193
     *
194
     * @return Account|null
195
     */
196
    private function findByIBAN(AccountType $type): ?Account
197
    {
198
        if (3 === count($this->accountIban)) {
199
            $accounts = $this->repository->getAccountsByType([$type->type]);
200
            $iban     = $this->accountIban['value'];
201
            Log::debug(sprintf('Finding account of type %d and IBAN %s', $type->id, $iban));
202
            $filtered = $accounts->filter(
203
                function (Account $account) use ($iban) {
204
                    if ($account->iban === $iban && $account->id !== $this->forbiddenAccountId) {
205
                        Log::debug(
206
                            sprintf(
207
                                'Found unmapped %s account by IBAN (#%d): %s (%s)',
208
                                $this->expectedType, $account->id, $account->name, $account->iban
209
                            )
210
                        );
211
212
                        return $account;
213
                    }
214
215
                    return null;
216
                }
217
            );
218
            if (1 === $filtered->count()) {
219
                return $filtered->first();
220
            }
221
            Log::debug('Found nothing.');
222
        }
223
224
        return null;
225
    }
226
227
    /**
228
     * Find account of type X by its ID.
229
     *
230
     * @param AccountType $type
231
     *
232
     * @return Account|null
233
     */
234
    private function findById(AccountType $type): ?Account
235
    {
236
        if (3 === count($this->accountId)) {
237
            Log::debug(sprintf('Finding account of type %d and ID %d', $type->id, $this->accountId['value']));
238
            /** @var Account $account */
239
            $account = $this->user->accounts()
240
                                  ->where('id', '!=', $this->forbiddenAccountId)
241
                                  ->where('account_type_id', $type->id)
242
                                  ->where('id', $this->accountId['value'])
243
                                  ->first();
244
245
            if (null !== $account) {
246
                Log::debug(sprintf('Found unmapped %s account by ID (#%d): %s', $this->expectedType, $account->id, $account->name));
247
248
                return $account;
249
            }
250
            Log::debug('Found nothing.');
251
        }
252
253
        return null;
254
    }
255
256
    /**
257
     * Find account by account type and name.
258
     *
259
     * @param AccountType $type
260
     *
261
     * @return Account|null
262
     */
263
    private function findByName(AccountType $type): ?Account
264
    {
265
        // Three: find by name (and type):
266
        if (3 === count($this->accountName)) {
267
            $accounts = $this->repository->getAccountsByType([$type->type]);
268
            $name     = $this->accountName['value'];
269
            Log::debug(sprintf('Finding account of type %d and name %s', $type->id, $name));
270
            $filtered = $accounts->filter(
271
                function (Account $account) use ($name) {
272
                    if ($account->name === $name && $account->id !== $this->forbiddenAccountId) {
273
                        Log::debug(sprintf('Found unmapped %s account by name (#%d): %s', $this->expectedType, $account->id, $account->name));
274
275
                        return $account;
276
                    }
277
278
                    return null;
279
                }
280
            );
281
282
            if (1 === $filtered->count()) {
283
                return $filtered->first();
284
            }
285
            Log::debug('Found nothing.');
286
        }
287
288
        return null;
289
    }
290
291
    /**
292
     * Determin account type to find, then use fields in object to try and find it.
293
     *
294
     * @return Account|null
295
     */
296
    private function findExistingObject(): ?Account
297
    {
298
        Log::debug('In findExistingObject() for Account');
299
        /** @var AccountType $accountType */
300
        $accountType = $this->repository->getAccountType($this->expectedType);
301
        $result      = $this->findById($accountType);
302
        if (!is_null($result)) {
303
            return $result;
304
        }
305
306
        $result = $this->findByIBAN($accountType);
307
308
        if (!is_null($result)) {
309
            return $result;
310
        }
311
312
        $result = $this->findByName($accountType);
313
314
        if (!is_null($result)) {
315
            return $result;
316
        }
317
318
        Log::debug('Found NO existing accounts.');
319
320
        return null;
321
    }
322
323
    /**
324
     * @return Account|null
325
     */
326
    private function findMappedObject(): ?Account
327
    {
328
        Log::debug('In findMappedObject() for Account');
329
        $fields = ['accountId', 'accountIban', 'accountNumber', 'accountName'];
330
        foreach ($fields as $field) {
331
            $array = $this->$field;
332
            Log::debug(sprintf('Find mapped account based on field "%s" with value', $field), $array);
333
            // check if a pre-mapped object exists.
334
            $mapped = $this->getMappedObject($array);
335
            if (null !== $mapped) {
336
                Log::debug(sprintf('Found account #%d!', $mapped->id));
337
338
                return $mapped;
339
            }
340
        }
341
        Log::debug('Found no account on mapped data or no map present.');
342
343
        return null;
344
    }
345
346
    /**
347
     * @param array $array
348
     *
349
     * @return Account|null
350
     */
351
    private function getMappedObject(array $array): ?Account
352
    {
353
        Log::debug('In getMappedObject() for Account');
354
        if (0 === count($array)) {
355
            Log::debug('Array is empty, nothing will come of this.');
356
357
            return null;
358
        }
359
360
        if (array_key_exists('mapped', $array) && null === $array['mapped']) {
361
            Log::debug(sprintf('No map present for value "%s". Return NULL.', $array['value']));
362
363
            return null;
364
        }
365
366
        Log::debug('Finding a mapped account based on', $array);
367
368
        $search  = intval($array['mapped'] ?? 0);
369
        $account = $this->repository->findNull($search);
370
371
        if (null === $account) {
372
            Log::error(sprintf('There is no account with id #%d. Invalid mapping will be ignored!', $search));
373
374
            return null;
375
        }
376
        // must be of the same type
377
        // except when mapped is an asset, then it's fair game.
378
        // which only shows that user must map very carefully.
379
        if ($account->accountType->type !== $this->expectedType && AccountType::ASSET !== $account->accountType->type) {
380
            Log::error(
381
                sprintf(
382
                    'Mapped account #%d is of type "%s" but we expect a "%s"-account. Mapping will be ignored.',
383
                    $account->id,
384
                    $account->accountType->type,
385
                    $this->expectedType
386
                )
387
            );
388
389
            return null;
390
        }
391
392
        Log::debug(sprintf('Found account! #%d ("%s"). Return it', $account->id, $account->name));
393
394
        return $account;
395
    }
396
397
    /**
398
     * @return bool
399
     *
400
     * @throws FireflyException
401
     */
402
    private function store(): bool
403
    {
404
        if (is_null($this->user)) {
405
            throw new FireflyException('ImportAccount cannot continue without user.');
406
        }
407
        if ((is_null($this->defaultAccountId) || 0 === intval($this->defaultAccountId)) && AccountType::ASSET === $this->expectedType) {
408
            throw new FireflyException('ImportAccount cannot continue without a default account to fall back on.');
409
        }
410
        // 1: find mapped object:
411
        $mapped = $this->findMappedObject();
412
        if (null !== $mapped) {
413
            $this->account = $mapped;
414
415
            return true;
416
        }
417
        // 2: find existing by given values:
418
        $found = $this->findExistingObject();
419
        if (null !== $found) {
420
            $this->account = $found;
421
422
            return true;
423
        }
424
425
        // 3: if found nothing, retry the search with an asset account:
426
        Log::debug('Will try to find an asset account just in case.');
427
        $oldExpectedType    = $this->expectedType;
428
        $this->expectedType = AccountType::ASSET;
429
        $found              = $this->findExistingObject();
430
        if (null !== $found) {
431
            Log::debug('Found asset account!');
432
            $this->account = $found;
433
434
            return true;
435
        }
436
        $this->expectedType = $oldExpectedType;
437
438
        // 4: if search for an asset account, fall back to given "default account" (mandatory)
439
        if (AccountType::ASSET === $this->expectedType) {
440
            $this->account = $this->repository->findNull($this->defaultAccountId);
0 ignored issues
show
It seems like $this->defaultAccountId can also be of type null; however, parameter $accountId of FireflyIII\Repositories\...ryInterface::findNull() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

440
            $this->account = $this->repository->findNull(/** @scrutinizer ignore-type */ $this->defaultAccountId);
Loading history...
441
            Log::debug(sprintf('Fall back to default account #%d "%s"', $this->account->id, $this->account->name));
442
443
            return true;
444
        }
445
446
        // 5: then maybe, create one:
447
        Log::debug(sprintf('Found no account of type %s so must create one ourselves.', $this->expectedType));
448
449
        $data = [
450
            'accountType'     => config('firefly.shortNamesByFullName.' . $this->expectedType),
451
            'name'            => $this->accountName['value'] ?? '(no name)',
452
            'iban'            => $this->accountIban['value'] ?? null,
453
            'active'          => true,
454
            'virtualBalance'  => '0',
455
            'account_type_id' => null,
456
            'BIC'             => $this->accountBic['value'] ?? null,
457
        ];
458
459
        $this->account = $this->repository->store($data);
460
        Log::debug(sprintf('Successfully stored new account #%d: %s', $this->account->id, $this->account->name));
461
462
        return true;
463
    }
464
}
465