Passed
Push — master ( 37b02e...ebbbe1 )
by James
08:59
created

app/Import/Object/ImportJournal.php (3 issues)

Severity
1
<?php
2
/**
3
 * ImportJournal.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 Carbon\Carbon;
26
use FireflyIII\Exceptions\FireflyException;
27
use FireflyIII\Import\Converter\Amount;
28
use FireflyIII\Import\Converter\ConverterInterface;
29
use FireflyIII\Import\MapperPreProcess\PreProcessorInterface;
30
use FireflyIII\User;
31
use InvalidArgumentException;
32
use Log;
33
use Steam;
34
35
/**
36
 * Class ImportJournal.
37
 */
38
class ImportJournal
39
{
40
41
    /** @var ImportAccount */
42
    public $asset;
43
    /** @var ImportBill */
44
    public $bill;
45
    /** @var ImportBudget */
46
    public $budget;
47
    /** @var ImportCategory */
48
    public $category;
49
    /** @var ImportCurrency */
50
    public $currency;
51
    /** @var string */
52
    public $description = '';
53
    /** @var ImportCurrency */
54
    public $foreignCurrency;
55
    /** @var string */
56
    public $hash;
57
    /** @var array */
58
    public $metaDates = [];
59
    /** @var array */
60
    public $metaFields = [];
61
    /** @var string */
62
    public $notes = '';
63
    /** @var ImportAccount */
64
    public $opposing;
65
    /** @var array */
66
    public $tags = [];
67
    /** @var array */
68
    private $amount;
69
    /** @var array */
70
    private $amountCredit;
71
    /** @var array */
72
    private $amountDebit;
73
    /** @var string */
74
    private $convertedAmount = null;
75
    /** @var string */
76
    private $date = '';
77
    /** @var string */
78
    private $externalId = '';
79
    /** @var array */
80
    private $foreignAmount;
81
    /** @var array */
82
    private $modifiers = [];
83
    /** @var User */
84
    private $user;
85
86
    /**
87
     * ImportEntry constructor.
88
     */
89
    public function __construct()
90
    {
91
        $this->asset           = new ImportAccount;
92
        $this->opposing        = new ImportAccount;
93
        $this->bill            = new ImportBill;
94
        $this->category        = new ImportCategory;
95
        $this->budget          = new ImportBudget;
96
        $this->currency        = new ImportCurrency;
97
        $this->foreignCurrency = new ImportCurrency;
98
    }
99
100
    /**
101
     * @param array $modifier
102
     */
103
    public function addToModifier(array $modifier)
104
    {
105
        $this->modifiers[] = $modifier;
106
    }
107
108
    /**
109
     * @return string
110
     *
111
     * @throws FireflyException
112
     */
113
    public function getAmount(): string
114
    {
115
        Log::debug('Now in getAmount()');
116
        Log::debug(sprintf('amount is %s', var_export($this->amount, true)));
117
        Log::debug(sprintf('debit amount is %s', var_export($this->amountDebit, true)));
118
        Log::debug(sprintf('credit amount is %s', var_export($this->amountCredit, true)));
119
120
        if (null === $this->convertedAmount) {
121
            $this->calculateAmount();
122
        }
123
        Log::debug(sprintf('convertedAmount is: "%s"', $this->convertedAmount));
124
        if (0 === bccomp($this->convertedAmount, '0')) {
125
            throw new FireflyException('Amount is zero.');
126
        }
127
128
        return $this->convertedAmount;
129
    }
130
131
    /**
132
     * @param string $format
133
     *
134
     * @return Carbon
135
     */
136
    public function getDate(string $format): Carbon
137
    {
138
        $date = new Carbon;
139
        try {
140
            $date = Carbon::createFromFormat($format, $this->date);
141
        } catch (InvalidArgumentException $e) {
142
            // don't care, just log.
143
            Log::error(sprintf('Import journal cannot parse date "%s" from value "%s" so will return current date instead.', $format, $this->date));
144
        }
145
146
        return $date;
147
    }
148
149
    /**
150
     * @return string
151
     */
152
    public function getDescription(): string
153
    {
154
        if ('' === $this->description) {
155
            return '(no description)';
156
        }
157
158
        return $this->description;
159
    }
160
161
    /**
162
     * @return string|null
163
     */
164
    public function getForeignAmount(): ?string
165
    {
166
        Log::debug('Now in getForeignAmount()');
167
        Log::debug(sprintf('foreign amount is %s', var_export($this->foreignAmount, true)));
168
169
        // no foreign amount? return null
170
        if (null === $this->foreignAmount) {
171
            Log::debug('Return NULL for foreign amount');
172
173
            return null;
174
        }
175
        // converter is default amount converter: no special stuff
176
        $converter = app(Amount::class);
177
        $amount    = $converter->convert($this->foreignAmount['value']);
178
        Log::debug(sprintf('First attempt to convert foreign gives "%s"', $amount));
179
        // modify
180
        foreach ($this->modifiers as $modifier) {
181
            $class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
182
            /** @var ConverterInterface $converter */
183
            $converter = app($class);
184
            Log::debug(sprintf('Now launching converter %s', $class));
185
            if ($converter->convert($modifier['value']) === -1) {
186
                $amount = Steam::negative($amount);
187
            }
188
            Log::debug(sprintf('Foreign amount after conversion is  %s', $amount));
189
        }
190
191
        Log::debug(sprintf('After modifiers the result is: "%s"', $amount));
192
193
194
        Log::debug(sprintf('converted foreign amount is: "%s"', $amount));
195
        if (0 === bccomp($amount, '0')) {
196
            return null;
197
        }
198
199
        return $amount;
200
    }
201
202
    /**
203
     * Get date field or NULL
204
     *
205
     * @param string $field
206
     *
207
     * @return Carbon|null
208
     */
209
    public function getMetaDate(string $field): ?Carbon
210
    {
211
        if (isset($this->metaDates[$field])) {
212
            return new Carbon($this->metaDates[$field]);
213
        }
214
215
        return null;
216
    }
217
218
    /**
219
     * Get string field or NULL
220
     *
221
     * @param string $field
222
     *
223
     * @return string|null
224
     */
225
    public function getMetaString(string $field): ?string
226
    {
227
        if (isset($this->metaFields[$field]) && strlen($this->metaFields[$field]) > 0) {
228
            return strval($this->metaFields[$field]);
229
        }
230
231
        return null;
232
    }
233
234
235
    /**
236
     * @param string $hash
237
     */
238
    public function setHash(string $hash)
239
    {
240
        $this->hash = $hash;
241
    }
242
243
    /**
244
     * @param User $user
245
     */
246
    public function setUser(User $user)
247
    {
248
        $this->user = $user;
249
250
        // set user for related objects:
251
        $this->asset->setUser($user);
252
        $this->opposing->setUser($user);
253
        $this->budget->setUser($user);
254
        $this->category->setUser($user);
255
        $this->bill->setUser($user);
256
    }
257
258
    /**
259
     * @param array $array
260
     *
261
     * @throws FireflyException
262
     */
263
    public function setValue(array $array)
264
    {
265
        $array['mapped'] = $array['mapped'] ?? null;
266
        $array['value']  = $array['value'] ?? null;
267
        switch ($array['role']) {
268
            default:
269
                throw new FireflyException(sprintf('ImportJournal cannot handle "%s" with value "%s".', $array['role'], $array['value']));
270
            case 'account-id':
271
                $this->asset->setAccountId($array);
272
                break;
273
            case 'sepa-cc':
274
            case 'sepa-ct-op':
275
            case 'sepa-ct-id':
276
            case 'sepa-db':
277
            case 'sepa-country':
278
            case 'sepa-ep':
279
            case 'sepa-ci':
280
                $value = trim(strval($array['value']));
281
                if (strlen($value) > 0) {
282
                    $this->metaFields[$array['role']] = $value;
283
                }
284
                break;
285
            case 'amount':
286
                $this->amount = $array;
287
                break;
288
            case 'amount_foreign':
289
                $this->foreignAmount = $array;
290
                break;
291
            case 'foreign-currency-code':
292
                $this->foreignCurrency->setCode($array);
293
                break;
294
            case 'amount_debit':
295
                $this->amountDebit = $array;
296
                break;
297
            case 'amount_credit':
298
                $this->amountCredit = $array;
299
                break;
300
            case 'account-iban':
301
                $this->asset->setAccountIban($array);
302
                break;
303
            case 'account-name':
304
                $this->asset->setAccountName($array);
305
                break;
306
            case 'account-number':
307
                $this->asset->setAccountNumber($array);
308
                break;
309
            case 'bill-id':
310
                $this->bill->setId($array);
311
                break;
312
            case 'bill-name':
313
                $this->bill->setName($array);
314
                break;
315
            case 'budget-id':
316
                $this->budget->setId($array);
317
                break;
318
            case 'budget-name':
319
                $this->budget->setName($array);
320
                break;
321
            case 'category-id':
322
                $this->category->setId($array);
323
                break;
324
            case 'category-name':
325
                $this->category->setName($array);
326
                break;
327
            case 'currency-code':
328
                $this->currency->setCode($array);
329
                break;
330
            case 'currency-id':
331
                $this->currency->setId($array);
332
                break;
333
            case 'currency-name':
334
                $this->currency->setName($array);
335
                break;
336
            case 'currency-symbol':
337
                $this->currency->setSymbol($array);
338
                break;
339
            case 'date-transaction':
340
                $this->date = $array['value'];
341
                break;
342
            case 'description':
343
                $this->description .= $array['value'];
344
                break;
345
            case 'note':
346
                $this->notes .= ' ' . $array['value'];
347
                $this->notes = trim($this->notes);
348
                break;
349
            case 'external-id':
350
                $this->externalId = $array['value'];
351
                break;
352
            case 'internal-reference':
353
                $this->metaFields['internal_reference'] = $array['value'];
354
                break;
355
            case '_ignore':
356
                break;
357
            case 'ing-debit-credit':
358
            case 'rabo-debit-credit':
359
                $this->addToModifier($array);
360
                break;
361
            case 'opposing-iban':
362
                $this->opposing->setAccountIban($array);
363
                break;
364
            case 'opposing-name':
365
                $this->opposing->setAccountName($array);
366
                break;
367
            case 'opposing-number':
368
                $this->opposing->setAccountNumber($array);
369
                break;
370
            case 'opposing-id':
371
                $this->opposing->setAccountId($array);
372
                break;
373
            case 'opposing-bic':
374
                $this->opposing->setAccountBic($array);
375
                break;
376
            case 'tags-comma':
377
            case 'tags-space':
378
                $this->setTags($array);
379
                break;
380
            case 'date-interest':
381
                $this->metaDates['interest_date'] = $array['value'];
382
                break;
383
            case 'date-book':
384
                $this->metaDates['book_date'] = $array['value'];
385
                break;
386
            case 'date-process':
387
                $this->metaDates['process_date'] = $array['value'];
388
                break;
389
            case 'date-due':
390
                $this->metaDates['due_date'] = $array['value'];
391
                break;
392
            case 'date-payment':
393
                $this->metaDates['payment_date'] = $array['value'];
394
                break;
395
            case 'date-invoice':
396
                $this->metaDates['invoice_date'] = $array['value'];
397
                break;
398
        }
399
    }
400
401
    /**
402
     * If convertedAmount is NULL, this method will try to calculate the correct amount.
403
     * It starts with amount, but can be overruled by debit and credit amounts.
404
     *
405
     * @throws FireflyException
406
     */
407
    private function calculateAmount()
408
    {
409
        // first check if the amount is set:
410
        Log::debug('convertedAmount is NULL');
411
412
        $info = $this->selectAmountInput();
413
414
        if (0 === count($info)) {
415
            throw new FireflyException('No amount information for this row.');
416
        }
417
        $class = $info['class'] ?? '';
418
        if (0 === strlen($class)) {
419
            throw new FireflyException('No amount information (conversion class) for this row.');
420
        }
421
422
        Log::debug(sprintf('Converter class is %s', $info['class']));
423
        /** @var ConverterInterface $amountConverter */
424
        $amountConverter       = app($info['class']);
425
        $this->convertedAmount = $amountConverter->convert($info['value']);
426
        Log::debug(sprintf('First attempt to convert gives "%s"', $this->convertedAmount));
427
        // modify
428
        foreach ($this->modifiers as $modifier) {
429
            $class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
430
            /** @var ConverterInterface $converter */
431
            $converter = app($class);
432
            Log::debug(sprintf('Now launching converter %s', $class));
433
            if ($converter->convert($modifier['value']) === -1) {
434
                $this->convertedAmount = Steam::negative($this->convertedAmount);
435
            }
436
            Log::debug(sprintf('convertedAmount after conversion is  %s', $this->convertedAmount));
437
        }
438
439
        Log::debug(sprintf('After modifiers the result is: "%s"', $this->convertedAmount));
440
    }
441
442
    /**
443
     * This methods decides which input to use for the amount calculation.
444
     *
445
     * @return array
446
     */
447
    private function selectAmountInput()
448
    {
449
        $info           = [];
450
        $converterClass = '';
451
        if (!is_null($this->amount)) {
0 ignored issues
show
The condition is_null($this->amount) is always false.
Loading history...
452
            Log::debug('Amount value is not NULL, assume this is the correct value.');
453
            $converterClass = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $this->amount['role'])));
454
            $info           = $this->amount;
455
        }
456
        if (!is_null($this->amountDebit)) {
0 ignored issues
show
The condition is_null($this->amountDebit) is always false.
Loading history...
457
            Log::debug('Amount DEBIT value is not NULL, assume this is the correct value (overrules Amount).');
458
            $converterClass = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $this->amountDebit['role'])));
459
            $info           = $this->amountDebit;
460
        }
461
        if (!is_null($this->amountCredit)) {
0 ignored issues
show
The condition is_null($this->amountCredit) is always false.
Loading history...
462
            Log::debug('Amount CREDIT value is not NULL, assume this is the correct value (overrules Amount and AmountDebit).');
463
            $converterClass = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $this->amountCredit['role'])));
464
            $info           = $this->amountCredit;
465
        }
466
        $info['class'] = $converterClass;
467
468
        return $info;
469
    }
470
471
    /**
472
     * @param array $array
473
     */
474
    private function setTags(array $array): void
475
    {
476
        $preProcessorClass = config(sprintf('csv.import_roles.%s.pre-process-mapper', $array['role']));
477
        /** @var PreProcessorInterface $preProcessor */
478
        $preProcessor = app(sprintf('\FireflyIII\Import\MapperPreProcess\%s', $preProcessorClass));
479
        $tags         = $preProcessor->run($array['value']);
480
        $this->tags   = array_merge($this->tags, $tags);
481
    }
482
}
483