Passed
Push — master ( 49de82...9f1fc0 )
by James
35:45 queued 23:44
created

AccountServiceTrait::updateMetaData()   C

Complexity

Conditions 12
Paths 24

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 26
rs 6.9666
cc 12
nc 24
nop 2

How to fix   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
 * AccountServiceTrait.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\TransactionCurrencyFactory;
30
use FireflyIII\Factory\TransactionGroupFactory;
31
use FireflyIII\Models\Account;
32
use FireflyIII\Models\AccountType;
33
use FireflyIII\Models\Note;
34
use FireflyIII\Models\Transaction;
35
use FireflyIII\Models\TransactionCurrency;
36
use FireflyIII\Models\TransactionGroup;
37
use FireflyIII\Models\TransactionJournal;
38
use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService;
39
use Log;
40
use Validator;
41
42
/**
43
 * Trait AccountServiceTrait
44
 *
45
 */
46
trait AccountServiceTrait
47
{
48
    /**
49
     * @param null|string $iban
50
     *
51
     * @return null|string
52
     */
53
    public function filterIban(?string $iban): ?string
54
    {
55
        if (null === $iban) {
56
            return null;
57
        }
58
        $data      = ['iban' => $iban];
59
        $rules     = ['iban' => 'required|iban'];
60
        $validator = Validator::make($data, $rules);
61
        if ($validator->fails()) {
62
            Log::info(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban));
63
64
            return null;
65
        }
66
67
68
        return $iban;
69
    }
70
71
    /**
72
     * Update meta data for account. Depends on type which fields are valid.
73
     *
74
     * TODO this method treats expense accounts and liabilities the same way (tries to save interest)
75
     *
76
     * @param Account $account
77
     * @param array   $data
78
     *
79
     */
80
    public function updateMetaData(Account $account, array $data): void
81
    {
82
        $fields = $this->validFields;
83
84
        if ($account->accountType->type === AccountType::ASSET) {
85
            $fields = $this->validAssetFields;
86
        }
87
        if ($account->accountType->type === AccountType::ASSET && isset($data['account_role']) && 'ccAsset' === $data['account_role']) {
88
            $fields = $this->validCCFields;
89
        }
90
        /** @var AccountMetaFactory $factory */
91
        $factory = app(AccountMetaFactory::class);
92
        foreach ($fields as $field) {
93
            // if the field is set but NULL, skip it.
94
            // if the field is set but "", update it.
95
            if (isset($data[$field]) && null !== $data[$field]) {
96
97
                // convert boolean value:
98
                if (is_bool($data[$field]) && false === $data[$field]) {
99
                    $data[$field] = 0;
100
                }
101
                if (is_bool($data[$field]) && true === $data[$field]) {
102
                    $data[$field] = 1;
103
                }
104
105
                $factory->crud($account, $field, (string)$data[$field]);
106
            }
107
        }
108
    }
109
110
    /**
111
     * @param Account $account
112
     * @param string  $note
113
     *
114
     * @codeCoverageIgnore
115
     * @return bool
116
     */
117
    public function updateNote(Account $account, string $note): bool
118
    {
119
        if ('' === $note) {
120
            $dbNote = $account->notes()->first();
121
            if (null !== $dbNote) {
122
                try {
123
                    $dbNote->delete();
124
                } catch (Exception $e) {
125
                    Log::debug($e->getMessage());
126
                }
127
            }
128
129
            return true;
130
        }
131
        $dbNote = $account->notes()->first();
132
        if (null === $dbNote) {
133
            $dbNote = new Note;
134
            $dbNote->noteable()->associate($account);
135
        }
136
        $dbNote->text = trim($note);
137
        $dbNote->save();
138
139
        return true;
140
    }
141
142
    /**
143
     * Verify if array contains valid data to possibly store or update the opening balance.
144
     *
145
     * @param array $data
146
     *
147
     * @return bool
148
     */
149
    public function validOBData(array $data): bool
150
    {
151
        $data['opening_balance'] = (string)($data['opening_balance'] ?? '');
152
        if ('' !== $data['opening_balance'] && 0 === bccomp($data['opening_balance'], '0')) {
153
            $data['opening_balance'] = '';
154
        }
155
        if ('' !== $data['opening_balance'] && isset($data['opening_balance'], $data['opening_balance_date'])) {
156
            Log::debug('Array has valid opening balance data.');
157
158
            return true;
159
        }
160
        Log::debug('Array does not have valid opening balance data.');
161
162
        return false;
163
    }
164
165
    /**
166
     * @param Account $account
167
     * @param array   $data
168
     *
169
     * @return TransactionGroup|null
170
     */
171
    protected function createOBGroup(Account $account, array $data): ?TransactionGroup
172
    {
173
        Log::debug('Now going to create an OB group.');
174
        $language   = app('preferences')->getForUser($account->user, 'language', 'en_US')->data;
175
        $sourceId   = null;
176
        $sourceName = null;
177
        $destId     = null;
178
        $destName   = null;
179
        $amount     = $data['opening_balance'];
180
        if (1 === bccomp($amount, '0')) {
181
            Log::debug(sprintf('Amount is %s, which is positive. Source is a new IB account, destination is #%d', $amount, $account->id));
182
            // amount is positive.
183
            $sourceName = trans('firefly.initial_balance_description', ['account' => $account->name], $language);
184
            $destId     = $account->id;
185
        }
186
        if (-1 === bccomp($amount, '0')) {
187
            Log::debug(sprintf('Amount is %s, which is negative. Destination is a new IB account, source is #%d', $amount, $account->id));
188
            // amount is not positive
189
            $destName = trans('firefly.initial_balance_account', ['account' => $account->name], $language);
190
            $sourceId = $account->id;
191
        }
192
        if (0 === bccomp($amount, '0')) {
193
            Log::debug('Amount is zero, so will not make an OB group.');
194
195
            return null;
196
        }
197
        $amount     = app('steam')->positive($amount);
198
        $submission = [
199
            'group_title'  => null,
200
            'user'         => $account->user_id,
201
            'transactions' => [
202
                [
203
                    'type'             => 'Opening balance',
204
                    'date'             => $data['opening_balance_date'],
205
                    'source_id'        => $sourceId,
206
                    'source_name'      => $sourceName,
207
                    'destination_id'   => $destId,
208
                    'destination_name' => $destName,
209
                    'user'             => $account->user_id,
210
                    'currency_id'      => $data['currency_id'],
211
                    'order'            => 0,
212
                    'amount'           => $amount,
213
                    'foreign_amount'   => null,
214
                    'description'      => trans('firefly.initial_balance_description', ['account' => $account->name]),
215
                    'budget_id'        => null,
216
                    'budget_name'      => null,
217
                    'category_id'      => null,
218
                    'category_name'    => null,
219
                    'piggy_bank_id'    => null,
220
                    'piggy_bank_name'  => null,
221
                    'reconciled'       => false,
222
                    'notes'            => null,
223
                    'tags'             => [],
224
                ],
225
            ],
226
        ];
227
        Log::debug('Going for submission', $submission);
228
        $group = null;
0 ignored issues
show
Unused Code introduced by James Cole
The assignment to $group is dead and can be removed.
Loading history...
229
        /** @var TransactionGroupFactory $factory */
230
        $factory = app(TransactionGroupFactory::class);
231
        $factory->setUser($account->user);
232
233
        try {
234
            $group = $factory->create($submission);
235
            // @codeCoverageIgnoreStart
236
        } catch (FireflyException $e) {
237
            Log::error($e->getMessage());
238
            Log::error($e->getTraceAsString());
239
        }
240
241
        // @codeCoverageIgnoreEnd
242
243
        return $group;
244
    }
245
246
    /**
247
     * Delete TransactionGroup with opening balance in it.
248
     *
249
     * @param Account $account
250
     */
251
    protected function deleteOBGroup(Account $account): void
252
    {
253
        Log::debug(sprintf('deleteOB() for account #%d', $account->id));
254
        $openingBalanceGroup = $this->getOBGroup($account);
255
256
        // opening balance data? update it!
257
        if (null !== $openingBalanceGroup) {
258
            Log::debug('Opening balance journal found, delete journal.');
259
            /** @var TransactionGroupDestroyService $service */
260
            $service = app(TransactionGroupDestroyService::class);
261
            $service->destroy($openingBalanceGroup);
262
        }
263
    }
264
265
    /**
266
     * @param int    $currencyId
267
     * @param string $currencyCode
268
     *
269
     * @return TransactionCurrency
270
     */
271
    protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency
272
    {
273
        // find currency, or use default currency instead.
274
        /** @var TransactionCurrencyFactory $factory */
275
        $factory = app(TransactionCurrencyFactory::class);
276
        /** @var TransactionCurrency $currency */
277
        $currency = $factory->find($currencyId, $currencyCode);
278
279
        if (null === $currency) {
280
            // use default currency:
281
            $currency = app('amount')->getDefaultCurrencyByUser($this->user);
282
        }
283
        $currency->enabled = true;
284
        $currency->save();
285
286
        return $currency;
287
    }
288
289
    /**
290
     * Returns the opening balance group, or NULL if it does not exist.
291
     *
292
     * @param Account $account
293
     *
294
     * @return TransactionGroup|null
295
     */
296
    protected function getOBGroup(Account $account): ?TransactionGroup
297
    {
298
        return $this->accountRepository->getOpeningBalanceGroup($account);
299
    }
300
301
    /**
302
     * Update or create the opening balance group. Assumes valid data in $data.
303
     *
304
     * Returns null if this fails.
305
     *
306
     * @param Account $account
307
     * @param array   $data
308
     *
309
     * @return TransactionGroup|null
310
     * @codeCoverageIgnore
311
     */
312
    protected function updateOBGroup(Account $account, array $data): ?TransactionGroup
313
    {
314
        $obGroup = $this->getOBGroup($account);
315
        if (null === $obGroup) {
316
            return $this->createOBGroup($account, $data);
317
        }
318
        /** @var TransactionJournal $journal */
319
        $journal                          = $obGroup->transactionJournals()->first();
320
        $journal->date                    = $data['opening_balance_date'] ?? $journal->date;
321
        $journal->transaction_currency_id = $data['currency_id'];
322
323
        /** @var Transaction $obTransaction */
324
        $obTransaction = $journal->transactions()->where('account_id', '!=', $account->id)->first();
325
        /** @var Transaction $accountTransaction */
326
        $accountTransaction = $journal->transactions()->where('account_id', $account->id)->first();
327
328
        // if amount is negative:
329
        if (1 === bccomp('0', $data['opening_balance'])) {
330
            // account transaction loses money:
331
            $accountTransaction->amount                  = app('steam')->negative($data['opening_balance']);
332
            $accountTransaction->transaction_currency_id = $data['currency_id'];
333
334
            // OB account transaction gains money
335
            $obTransaction->amount                  = app('steam')->positive($data['opening_balance']);
336
            $obTransaction->transaction_currency_id = $data['currency_id'];
337
        }
338
        if (-1 === bccomp('0', $data['opening_balance'])) {
339
            // account gains money:
340
            $accountTransaction->amount                  = app('steam')->positive($data['opening_balance']);
341
            $accountTransaction->transaction_currency_id = $data['currency_id'];
342
343
            // OB account loses money:
344
            $obTransaction->amount                  = app('steam')->negative($data['opening_balance']);
345
            $obTransaction->transaction_currency_id = $data['currency_id'];
346
        }
347
        // save both
348
        $accountTransaction->save();
349
        $obTransaction->save();
350
        $journal->save();
351
        $obGroup->refresh();
352
353
        return $obGroup;
354
    }
355
}
356