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.

TransactionGroupRepository   A
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 428
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 42
eloc 177
c 2
b 0
f 0
dl 0
loc 428
rs 9.0399

19 Methods

Rating   Name   Duplication   Size   Complexity  
A update() 0 6 1
A getMetaFields() 0 15 2
A getTags() 0 9 1
A destroy() 0 5 1
A __construct() 0 4 2
A store() 0 14 3
A getPiggyEvents() 0 33 4
A expandJournal() 0 20 2
A getNoteText() 0 12 2
A getAttachments() 0 21 2
A setUser() 0 3 1
A getFormattedForeignAmount() 0 19 4
A getTagObjects() 0 6 1
A getMetaDateFields() 0 15 2
A find() 0 3 1
A expandGroup() 0 10 2
A expandTransaction() 0 16 3
A getFormattedAmount() 0 17 3
A getLinks() 0 46 5

How to fix   Complexity   

Complex Class

Complex classes like TransactionGroupRepository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TransactionGroupRepository, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TransactionGroupRepository.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\Repositories\TransactionGroup;
25
26
27
use Carbon\Carbon;
28
use DB;
29
use Exception;
30
use FireflyIII\Exceptions\DuplicateTransactionException;
31
use FireflyIII\Exceptions\FireflyException;
32
use FireflyIII\Factory\TransactionGroupFactory;
33
use FireflyIII\Models\AccountMeta;
34
use FireflyIII\Models\Attachment;
35
use FireflyIII\Models\Note;
36
use FireflyIII\Models\PiggyBankEvent;
37
use FireflyIII\Models\Transaction;
38
use FireflyIII\Models\TransactionCurrency;
39
use FireflyIII\Models\TransactionGroup;
40
use FireflyIII\Models\TransactionJournal;
41
use FireflyIII\Models\TransactionJournalLink;
42
use FireflyIII\Models\TransactionType;
43
use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService;
44
use FireflyIII\Services\Internal\Update\GroupUpdateService;
45
use FireflyIII\Support\NullArrayObject;
46
use FireflyIII\User;
47
use Illuminate\Database\Eloquent\Builder;
48
use Illuminate\Support\Collection;
49
use Log;
50
51
/**
52
 * Class TransactionGroupRepository
53
 */
54
class TransactionGroupRepository implements TransactionGroupRepositoryInterface
55
{
56
    /** @var User */
57
    private $user;
58
59
    /**
60
     * Constructor.
61
     */
62
    public function __construct()
63
    {
64
        if ('testing' === config('app.env')) {
65
            app('log')->warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
66
        }
67
    }
68
69
    /**
70
     * @param TransactionGroup $group
71
     */
72
    public function destroy(TransactionGroup $group): void
73
    {
74
        /** @var TransactionGroupDestroyService $service */
75
        $service = new TransactionGroupDestroyService;
76
        $service->destroy($group);
77
    }
78
79
    /**
80
     * @inheritDoc
81
     */
82
    public function expandGroup(TransactionGroup $group): array
83
    {
84
        $result                         = $group->toArray();
85
        $result['transaction_journals'] = [];
86
        /** @var TransactionJournal $journal */
87
        foreach ($group->transactionJournals as $journal) {
88
            $result['transaction_journals'][] = $this->expandJournal($journal);
89
        }
90
91
        return $result;
92
    }
93
94
    /**
95
     * Find a transaction group by its ID.
96
     *
97
     * @param int $groupId
98
     *
99
     * @return TransactionGroup|null
100
     */
101
    public function find(int $groupId): ?TransactionGroup
102
    {
103
        return $this->user->transactionGroups()->where('id', $groupId)->first();
104
    }
105
106
    /**
107
     * Return all attachments for all journals in the group.
108
     *
109
     * @param TransactionGroup $group
110
     *
111
     * @return array
112
     */
113
    public function getAttachments(TransactionGroup $group): array
114
    {
115
        $journals = $group->transactionJournals->pluck('id')->toArray();
116
        $set      = Attachment::whereIn('attachable_id', $journals)
117
                              ->where('attachable_type', TransactionJournal::class)
118
                              ->where('uploaded', 1)
119
                              ->whereNull('deleted_at')->get();
120
121
        $result = [];
122
        /** @var Attachment $attachment */
123
        foreach ($set as $attachment) {
124
            $journalId                = (int)$attachment->attachable_id;
125
            $result[$journalId]       = $result[$journalId] ?? [];
126
            $current                  = $attachment->toArray();
127
            $current['file_exists']   = true;
128
            $current['journal_title'] = $attachment->attachable->description;
129
            $result[$journalId][]     = $current;
130
131
        }
132
133
        return $result;
134
    }
135
136
    /**
137
     * Return all journal links for all journals in the group.
138
     *
139
     * @param TransactionGroup $group
140
     *
141
     * @return array
142
     */
143
    public function getLinks(TransactionGroup $group): array
144
    {
145
        $return   = [];
146
        $journals = $group->transactionJournals->pluck('id')->toArray();
147
        $set      = TransactionJournalLink
148
            ::where(
149
                static function (Builder $q) use ($journals) {
150
                    $q->whereIn('source_id', $journals);
151
                    $q->orWhereIn('destination_id', $journals);
152
                }
153
            )
154
            ->with(['source', 'destination', 'source.transactions'])
155
            ->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id')
156
            ->get(['journal_links.*', 'link_types.inward', 'link_types.outward']);
157
        /** @var TransactionJournalLink $entry */
158
        foreach ($set as $entry) {
159
            $journalId          = in_array($entry->source_id, $journals, true) ? $entry->source_id : $entry->destination_id;
160
            $return[$journalId] = $return[$journalId] ?? [];
161
162
            if ($journalId === $entry->source_id) {
163
                $amount               = $this->getFormattedAmount($entry->destination);
164
                $foreignAmount        = $this->getFormattedForeignAmount($entry->destination);
165
                $return[$journalId][] = [
166
                    'id'             => $entry->id,
167
                    'link'           => $entry->outward,
168
                    'group'          => $entry->destination->transaction_group_id,
169
                    'description'    => $entry->destination->description,
170
                    'amount'         => $amount,
171
                    'foreign_amount' => $foreignAmount,
172
                ];
173
            }
174
            if ($journalId === $entry->destination_id) {
175
                $amount               = $this->getFormattedAmount($entry->source);
176
                $foreignAmount        = $this->getFormattedForeignAmount($entry->source);
177
                $return[$journalId][] = [
178
                    'id'             => $entry->id,
179
                    'link'           => $entry->inward,
180
                    'group'          => $entry->source->transaction_group_id,
181
                    'description'    => $entry->source->description,
182
                    'amount'         => $amount,
183
                    'foreign_amount' => $foreignAmount,
184
                ];
185
            }
186
        }
187
188
        return $return;
189
    }
190
191
    /**
192
     * Return object with all found meta field things as Carbon objects.
193
     *
194
     * @param int   $journalId
195
     * @param array $fields
196
     *
197
     * @return NullArrayObject
198
     * @throws Exception
199
     */
200
    public function getMetaDateFields(int $journalId, array $fields): NullArrayObject
201
    {
202
        $query  = DB
203
            ::table('journal_meta')
204
            ->where('transaction_journal_id', $journalId)
205
            ->whereIn('name', $fields)
206
            ->whereNull('deleted_at')
207
            ->get(['name', 'data']);
208
        $return = [];
209
210
        foreach ($query as $row) {
211
            $return[$row->name] = new Carbon(json_decode($row->data));
212
        }
213
214
        return new NullArrayObject($return);
215
    }
216
217
    /**
218
     * Return object with all found meta field things.
219
     *
220
     * @param int   $journalId
221
     * @param array $fields
222
     *
223
     * @return NullArrayObject
224
     */
225
    public function getMetaFields(int $journalId, array $fields): NullArrayObject
226
    {
227
        $query  = DB
228
            ::table('journal_meta')
229
            ->where('transaction_journal_id', $journalId)
230
            ->whereIn('name', $fields)
231
            ->whereNull('deleted_at')
232
            ->get(['name', 'data']);
233
        $return = [];
234
235
        foreach ($query as $row) {
236
            $return[$row->name] = json_decode($row->data);
237
        }
238
239
        return new NullArrayObject($return);
240
    }
241
242
    /**
243
     * Get the note text for a journal (by ID).
244
     *
245
     * @param int $journalId
246
     *
247
     * @return string|null
248
     */
249
    public function getNoteText(int $journalId): ?string
250
    {
251
        /** @var Note $note */
252
        $note = Note
253
            ::where('noteable_id', $journalId)
254
            ->where('noteable_type', TransactionJournal::class)
255
            ->first();
256
        if (null === $note) {
257
            return null;
258
        }
259
260
        return $note->text;
261
    }
262
263
    /**
264
     * Return all piggy bank events for all journals in the group.
265
     *
266
     * @param TransactionGroup $group
267
     *
268
     * @return array
269
     */
270
    public function getPiggyEvents(TransactionGroup $group): array
271
    {
272
        $return   = [];
273
        $journals = $group->transactionJournals->pluck('id')->toArray();
274
        $data     = PiggyBankEvent
275
            ::whereIn('transaction_journal_id', $journals)
276
            ->with('piggyBank', 'piggyBank.account')
277
            ->get(['piggy_bank_events.*']);
278
        /** @var PiggyBankEvent $row */
279
        foreach ($data as $row) {
280
            // get currency preference.
281
            $currencyPreference = AccountMeta
282
                ::where('account_id', $row->piggyBank->account_id)
283
                ->where('name', 'currency_id')
284
                ->first();
285
            if (null !== $currencyPreference) {
286
                $currency = TransactionCurrency::where('id', $currencyPreference->data)->first();
287
            }
288
            if (null === $currencyPreference) {
289
                $currencyCode = app('preferences')->getForUser($this->user, 'currencyPreference', 'EUR')->data;
290
                $currency     = TransactionCurrency::where('code', $currencyCode)->first();
291
            }
292
            $journalId          = (int)$row->transaction_journal_id;
293
            $return[$journalId] = $return[$journalId] ?? [];
294
295
            $return[$journalId][] = [
296
                'piggy'    => $row->piggyBank->name,
297
                'piggy_id' => $row->piggy_bank_id,
298
                'amount'   => app('amount')->formatAnything($currency, $row->amount),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $currency does not seem to be defined for all execution paths leading up to this point.
Loading history...
299
            ];
300
        }
301
302
        return $return;
303
    }
304
305
    /**
306
     * Get the tags for a journal (by ID).
307
     *
308
     * @param int $journalId
309
     *
310
     * @return array
311
     */
312
    public function getTags(int $journalId): array
313
    {
314
        $result = DB
315
            ::table('tag_transaction_journal')
316
            ->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id')
317
            ->where('tag_transaction_journal.transaction_journal_id', $journalId)
318
            ->get(['tags.tag']);
319
320
        return $result->pluck('tag')->toArray();
321
    }
322
323
    /**
324
     * @param User $user
325
     */
326
    public function setUser(User $user): void
327
    {
328
        $this->user = $user;
329
    }
330
331
    /**
332
     * @param array $data
333
     *
334
     * @return TransactionGroup
335
     * @throws DuplicateTransactionException
336
     * @throws FireflyException
337
     */
338
    public function store(array $data): TransactionGroup
339
    {
340
        /** @var TransactionGroupFactory $factory */
341
        $factory = app(TransactionGroupFactory::class);
342
        $factory->setUser($this->user);
343
        try {
344
            return $factory->create($data);
345
        } catch (DuplicateTransactionException $e) {
346
            Log::warning('Group repository caught group factory with a duplicate exception!');
347
            throw new DuplicateTransactionException($e->getMessage());
348
        } catch(FireflyException $e) {
349
            Log::error($e->getMessage());
350
            Log::error($e->getTraceAsString());
351
            throw new FireflyException($e->getMessage());
352
        }
353
354
355
    }
356
357
    /**
358
     * @param TransactionGroup $transactionGroup
359
     * @param array            $data
360
     *
361
     * @return TransactionGroup
362
     *
363
     * @throws FireflyException
364
     */
365
    public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup
366
    {
367
        /** @var GroupUpdateService $service */
368
        $service = app(GroupUpdateService::class);
369
370
        return $service->update($transactionGroup, $data);
371
    }
372
373
    /**
374
     * @param TransactionJournal $journal
375
     *
376
     * @return array
377
     */
378
    private function expandJournal(TransactionJournal $journal): array
379
    {
380
        $array                      = $journal->toArray();
381
        $array['transactions']      = [];
382
        $array['meta']              = $journal->transactionJournalMeta->toArray();
383
        $array['tags']              = $journal->tags->toArray();
384
        $array['categories']        = $journal->categories->toArray();
385
        $array['budgets']           = $journal->budgets->toArray();
386
        $array['notes']             = $journal->notes->toArray();
387
        $array['locations']         = []; // todo
388
        $array['attachments']       = $journal->attachments->toArray();
389
        $array['links']             = []; // todo
390
        $array['piggy_bank_events'] = $journal->piggyBankEvents->toArray();
391
392
        /** @var Transaction $transaction */
393
        foreach ($journal->transactions as $transaction) {
394
            $array['transactions'][] = $this->expandTransaction($transaction);
395
        }
396
397
        return $array;
398
    }
399
400
    /**
401
     * @param Transaction $transaction
402
     *
403
     * @return array
404
     */
405
    private function expandTransaction(Transaction $transaction): array
406
    {
407
        $array = $transaction->toArray();
408
        $array['account'] = $transaction->account->toArray();
409
        $array['budgets'] = [];
410
        $array['categories'] = [];
411
412
        foreach ($transaction->categories as $category) {
413
            $array['categories'][] = $category->toArray();
414
        }
415
416
        foreach ($transaction->budgets as $budget) {
417
            $array['budgets'][] = $budget->toArray();
418
        }
419
420
        return $array;
421
    }
422
423
    /**
424
     * @param TransactionJournal $journal
425
     *
426
     * @return string
427
     */
428
    private function getFormattedAmount(TransactionJournal $journal): string
429
    {
430
        /** @var Transaction $transaction */
431
        $transaction = $journal->transactions->first();
432
        $currency    = $transaction->transactionCurrency;
433
        $type        = $journal->transactionType->type;
434
        $amount      = app('steam')->positive($transaction->amount);
435
        $return      = '';
436
        if (TransactionType::WITHDRAWAL === $type) {
437
            $return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
438
        }
439
        if (TransactionType::WITHDRAWAL !== $type) {
440
            $return = app('amount')->formatAnything($currency, $amount);
441
442
        }
443
444
        return $return;
445
    }
446
447
    /**
448
     * @param TransactionJournal $journal
449
     *
450
     * @return string
451
     */
452
    private function getFormattedForeignAmount(TransactionJournal $journal): string
453
    {
454
        /** @var Transaction $transaction */
455
        $transaction = $journal->transactions->first();
456
        if (null === $transaction->foreign_amount) {
457
            return '';
458
        }
459
        $currency = $transaction->foreignCurrency;
460
        $type     = $journal->transactionType->type;
461
        $amount   = app('steam')->positive($transaction->foreign_amount);
462
        $return   = '';
463
        if (TransactionType::WITHDRAWAL === $type) {
464
            $return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
465
        }
466
        if (TransactionType::WITHDRAWAL !== $type) {
467
            $return = app('amount')->formatAnything($currency, $amount);
468
        }
469
470
        return $return;
471
    }
472
473
    /**
474
     * @inheritDoc
475
     */
476
    public function getTagObjects(int $journalId): Collection
477
    {
478
        /** @var TransactionJournal $journal */
479
        $journal = $this->user->transactionJournals()->find($journalId);
480
481
        return $journal->tags()->get();
482
    }
483
}
484