JournalServiceTrait::getAccount()   F
last analyzed

Complexity

Conditions 17
Paths 324

Size

Total Lines 98
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 98
rs 2.8333
c 0
b 0
f 0
cc 17
nc 324
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * JournalServiceTrait.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
22
declare(strict_types=1);
23
24
namespace FireflyIII\Services\Internal\Support;
25
26
use Exception;
27
use FireflyIII\Exceptions\FireflyException;
28
use FireflyIII\Factory\AccountMetaFactory;
29
use FireflyIII\Factory\TagFactory;
30
use FireflyIII\Models\Account;
31
use FireflyIII\Models\AccountType;
32
use FireflyIII\Models\Note;
33
use FireflyIII\Models\TransactionJournal;
34
use FireflyIII\Models\TransactionType;
35
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
36
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
37
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
38
use FireflyIII\Support\NullArrayObject;
39
use Log;
40
41
/**
42
 * Trait JournalServiceTrait
43
 *
44
 */
45
trait JournalServiceTrait
46
{
47
    /** @var AccountRepositoryInterface */
48
    private $accountRepository;
49
    /** @var BudgetRepositoryInterface */
50
    private $budgetRepository;
51
    /** @var CategoryRepositoryInterface */
52
    private $categoryRepository;
53
    /** @var TagFactory */
54
    private $tagFactory;
55
56
57
    /**
58
     * @param string|null $amount
59
     *
60
     * @return string
61
     * @codeCoverageIgnore
62
     */
63
    protected function getForeignAmount(?string $amount): ?string
64
    {
65
        if (null === $amount) {
66
            Log::debug('No foreign amount info in array. Return NULL');
67
68
            return null;
69
        }
70
        if ('' === $amount) {
71
            Log::debug('Foreign amount is empty string, return NULL.');
72
73
            return null;
74
        }
75
        if (0 === bccomp('0', $amount)) {
76
            Log::debug('Foreign amount is 0.0, return NULL.');
77
78
            return null;
79
        }
80
        Log::debug(sprintf('Foreign amount is %s', $amount));
81
82
        return $amount;
83
    }
84
85
    /**
86
     * @param string $transactionType
87
     * @param string $direction
88
     * @param array  $data
89
     *
90
     * @return Account
91
     * @codeCoverageIgnore
92
     */
93
    protected function getAccount(string $transactionType, string $direction, array $data): Account
94
    {
95
        // some debug logging:
96
        Log::debug(sprintf('Now in getAccount(%s)', $direction), $data);
97
98
        // final result:
99
        $result = null;
100
101
        // expected type of source account, in order of preference
102
        /** @var array $array */
103
        $array         = config('firefly.expected_source_types');
104
        $expectedTypes = $array[$direction];
105
        unset($array);
106
107
        // and now try to find it, based on the type of transaction.
108
        $message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s';
109
        Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType])));
110
111
        // first attempt, find by ID.
112
        if (null !== $data['id']) {
113
            $search = $this->accountRepository->findNull($data['id']);
114
            if (null !== $search && in_array($search->accountType->type, $expectedTypes[$transactionType], true)) {
115
                Log::debug(
116
                    sprintf('Found "account_id" object  for %s: #%d, "%s" of type %s', $direction, $search->id, $search->name, $search->accountType->type)
117
                );
118
                $result = $search;
119
            }
120
        }
121
122
        // second attempt, find by name.
123
        if (null === $result && null !== $data['name']) {
124
            Log::debug('Found nothing by account ID.');
125
            // find by preferred type.
126
            $source = $this->accountRepository->findByName($data['name'], [$expectedTypes[$transactionType][0]]);
127
            // or any expected type.
128
            $source = $source ?? $this->accountRepository->findByName($data['name'], $expectedTypes[$transactionType]);
129
130
            if (null !== $source) {
131
                Log::debug(sprintf('Found "account_name" object for %s: #%d, %s', $direction, $source->id, $source->name));
132
133
                $result = $source;
134
            }
135
        }
136
137
        // third attempt, find by IBAN
138
        if (null === $result && null !== $data['iban']) {
139
            Log::debug('Found nothing by account name.');
140
            // find by preferred type.
141
            $source = $this->accountRepository->findByIbanNull($data['iban'], [$expectedTypes[$transactionType][0]]);
142
            // or any expected type.
143
            $source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $expectedTypes[$transactionType]);
144
145
            if (null !== $source) {
146
                Log::debug(sprintf('Found "account_iban" object for %s: #%d, %s', $direction, $source->id, $source->name));
147
148
                $result = $source;
149
            }
150
        }
151
152
        // return cash account.
153
        if (null === $result && null === $data['name']
154
            && in_array(AccountType::CASH, $expectedTypes[$transactionType], true)) {
155
            $result = $this->accountRepository->getCashAccount();
156
        }
157
158
        // return new account.
159
        if (null === $result) {
160
            $data['name'] = $data['name'] ?? '(no name)';
161
            // final attempt, create it.
162
            $preferredType = $expectedTypes[$transactionType][0];
163
            if (AccountType::ASSET === $preferredType) {
164
                throw new FireflyException('TransactionFactory: Cannot create asset account with these values', $data);
0 ignored issues
show
Bug introduced by James Cole
$data of type array is incompatible with the type integer expected by parameter $code of FireflyIII\Exceptions\Fi...xception::__construct(). ( Ignorable by Annotation )

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

164
                throw new FireflyException('TransactionFactory: Cannot create asset account with these values', /** @scrutinizer ignore-type */ $data);
Loading history...
165
            }
166
167
            $result = $this->accountRepository->store(
168
                [
169
                    'account_type_id' => null,
170
                    'account_type'    => $preferredType,
171
                    'name'            => $data['name'],
172
                    'active'          => true,
173
                    'iban'            => $data['iban'],
174
                ]
175
            );
176
            // store BIC
177
            if (null !== $data['bic']) {
178
                /** @var AccountMetaFactory $metaFactory */
179
                $metaFactory = app(AccountMetaFactory::class);
180
                $metaFactory->create(['account_id' => $result->id, 'name' => 'BIC', 'data' => $data['bic']]);
181
            }
182
            // store account number
183
            if (null !== $data['number']) {
184
                /** @var AccountMetaFactory $metaFactory */
185
                $metaFactory = app(AccountMetaFactory::class);
186
                $metaFactory->create(['account_id' => $result->id, 'name' => 'account_number', 'data' => $data['bic']]);
187
            }
188
        }
189
190
        return $result;
191
    }
192
193
    /**
194
     * @param string $amount
195
     *
196
     * @return string
197
     * @throws FireflyException
198
     * @codeCoverageIgnore
199
     */
200
    protected function getAmount(string $amount): string
201
    {
202
        if ('' === $amount) {
203
            throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount));
204
        }
205
        if (0 === bccomp('0', $amount)) {
206
            throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount));
207
        }
208
209
        return $amount;
210
    }
211
212
    /**
213
     * @param TransactionJournal $journal
214
     * @param NullArrayObject    $data
215
     *
216
     * @codeCoverageIgnore
217
     */
218
    protected function storeBudget(TransactionJournal $journal, NullArrayObject $data): void
219
    {
220
        if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) {
221
            $journal->budgets()->sync([]);
222
223
            return;
224
        }
225
        $budget = $this->budgetRepository->findBudget($data['budget_id'], $data['budget_name']);
226
        if (null !== $budget) {
227
            Log::debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id));
228
            $journal->budgets()->sync([$budget->id]);
229
230
            return;
231
        }
232
        // if the budget is NULL, sync empty.
233
        $journal->budgets()->sync([]);
234
    }
235
236
    /**
237
     * @param TransactionJournal $journal
238
     * @param NullArrayObject    $data
239
     *
240
     * @codeCoverageIgnore
241
     */
242
    protected function storeCategory(TransactionJournal $journal, NullArrayObject $data): void
243
    {
244
        $category = $this->categoryRepository->findCategory($data['category_id'], $data['category_name']);
245
        if (null !== $category) {
246
            Log::debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id));
247
            $journal->categories()->sync([$category->id]);
248
249
            return;
250
        }
251
        // if the category is NULL, sync empty.
252
        $journal->categories()->sync([]);
253
    }
254
255
    /**
256
     * @param TransactionJournal $journal
257
     * @param string             $notes
258
     *
259
     * @codeCoverageIgnore
260
     */
261
    protected function storeNotes(TransactionJournal $journal, ?string $notes): void
262
    {
263
        $notes = (string)$notes;
264
        $note  = $journal->notes()->first();
265
        if ('' !== $notes) {
266
            if (null === $note) {
267
                $note = new Note;
268
                $note->noteable()->associate($journal);
269
            }
270
            $note->text = $notes;
271
            $note->save();
272
            Log::debug(sprintf('Stored notes for journal #%d', $journal->id));
273
274
            return;
275
        }
276
        if ('' === $notes && null !== $note) {
277
            // try to delete existing notes.
278
            try {
279
                $note->delete();
280
                // @codeCoverageIgnoreStart
281
            } catch (Exception $e) {
282
                Log::debug(sprintf('Could not delete journal notes: %s', $e->getMessage()));
283
            }
284
            // @codeCoverageIgnoreEnd
285
        }
286
    }
287
288
    /**
289
     * Link tags to journal.
290
     *
291
     * @param TransactionJournal $journal
292
     * @param array              $tags
293
     *
294
     * @codeCoverageIgnore
295
     */
296
    protected function storeTags(TransactionJournal $journal, ?array $tags): void
297
    {
298
299
        $this->tagFactory->setUser($journal->user);
300
        $set = [];
301
        if (!is_array($tags)) {
0 ignored issues
show
introduced by James Cole
The condition is_array($tags) is always true.
Loading history...
302
            return;
303
        }
304
        foreach ($tags as $string) {
305
            $string = (string)$string;
306
            if ('' !== $string) {
307
                $tag = $this->tagFactory->findOrCreate($string);
308
                if (null !== $tag) {
309
                    $set[] = $tag->id;
310
                }
311
            }
312
        }
313
        $journal->tags()->sync($set);
314
    }
315
316
317
}
318