Completed
Pull Request — master (#13)
by Yann
03:10
created

Decoder::addRelatedPartiesToTransactionDetails()   C

Complexity

Conditions 11
Paths 23

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 14.525

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 41
ccs 18
cts 26
cp 0.6923
rs 5.2653
cc 11
eloc 27
nc 23
nop 2
crap 14.525

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
namespace Genkgo\Camt\Camt053;
3
4
use DateTimeImmutable;
5
use DOMDocument;
6
use Genkgo\Camt\DecoderInterface;
7
use Genkgo\Camt\Exception\InvalidMessageException;
8
use Genkgo\Camt\Iban;
9
use Genkgo\Camt\Util\StringToUnits;
10
use Money\Currency;
11
use Money\Money;
12
use SimpleXMLElement;
13
14
class Decoder implements DecoderInterface
15
{
16
    /**
17
     * @var SimpleXMLElement[]
18
     */
19
    private $document;
20
21
    /**
22
     * Path to the schema definition
23
     * @var string
24
     */
25
    protected $schemeDefinitionPath;
26
27
    /**
28
     * @param $schemeDefinitionPath
29
     */
30 10
    public function __construct($schemeDefinitionPath)
31
    {
32 10
        $this->schemeDefinitionPath = $schemeDefinitionPath;
33 10
    }
34
35
    /**
36
     * @param DOMDocument $document
37
     * @throws InvalidMessageException
38
     */
39 10
    private function validate(DOMDocument $document)
40
    {
41 10
        libxml_use_internal_errors(true);
42 10
        $valid = $document->schemaValidate(dirname(dirname(__DIR__)).$this->schemeDefinitionPath);
43 10
        $errors = libxml_get_errors();
44 10
        libxml_clear_errors();
45
46 10
        if (!$valid) {
47 1
            $messages = [];
48 1
            foreach ($errors as $error) {
49 1
                $messages[] = $error->message;
50
            }
51
52 1
            $errorMessage = implode("\n", $messages);
53 1
            throw new InvalidMessageException("Provided XML is not valid according to the XSD:\n{$errorMessage}");
54
        }
55 9
    }
56
57
    /**
58
     * @param DOMDocument $document
59
     * @return Message
60
     * @throws InvalidMessageException
61
     */
62 10
    public function decode(DOMDocument $document)
63
    {
64 10
        $this->validate($document);
65 9
        $this->document = simplexml_import_dom($document);
0 ignored issues
show
Documentation Bug introduced by
It seems like simplexml_import_dom($document) of type object<SimpleXMLElement> is incompatible with the declared type array<integer,object<SimpleXMLElement>> of property $document.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
66
67 9
        $message = new Message();
68 9
        $this->addGroupHeaderToMessage($message);
69 9
        $this->addStatementsToMessage($message);
70
71 9
        return $message;
72
    }
73
74
    /**
75
     * @param SimpleXMLElement $statementXml
76
     * @param Statement $statement
77
     */
78 9
    private function addBalancesToStatement(SimpleXMLElement $statementXml, Statement $statement)
79
    {
80 9
        $balancesXml = $statementXml->Bal;
0 ignored issues
show
Bug introduced by
The property Bal does not seem to exist in SimpleXMLElement.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
81 9
        foreach ($balancesXml as $balanceXml) {
82 9
            $amount = StringToUnits::convert((string) $balanceXml->Amt);
83 9
            $currency = (string)$balanceXml->Amt['Ccy'];
84 9
            $date = (string)$balanceXml->Dt->Dt;
85
86 9
            if ((string) $balanceXml->CdtDbtInd === 'DBIT') {
87
                $amount = $amount * -1;
88
            }
89
90 9
            if ((string) $balanceXml->Tp->CdOrPrtry->Cd === 'OPBD') {
91 9
                $balance = Balance::opening(
92 9
                    new Money(
93
                        $amount,
94 9
                        new Currency($currency)
95
                    ),
96 9
                    new DateTimeImmutable($date)
97
                );
98
            } else {
99 9
                $balance = Balance::closing(
100 9
                    new Money(
101
                        $amount,
102 9
                        new Currency($currency)
103
                    ),
104 9
                    new DateTimeImmutable($date)
105
                );
106
            }
107
108 9
            $statement->addBalance($balance);
109
        }
110 9
    }
111
112
    /**
113
     * @param SimpleXMLElement $statementXml
114
     * @param Statement $statement
115
     */
116 9
    private function addEntriesToStatement(SimpleXMLElement $statementXml, Statement $statement)
117
    {
118 9
        $index = 0;
119 9
        $entriesXml = $statementXml->Ntry;
0 ignored issues
show
Bug introduced by
The property Ntry does not seem to exist in SimpleXMLElement.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
120 9
        foreach ($entriesXml as $entryXml) {
121 9
            $amount = StringToUnits::convert((string) $entryXml->Amt);
122 9
            $currency = (string)$entryXml->Amt['Ccy'];
123 9
            $bookingDate = (string)$entryXml->BookgDt->Dt;
124 9
            $valueDate = (string)$entryXml->ValDt->Dt;
125
126 9
            if ((string) $entryXml->CdtDbtInd === 'DBIT') {
127 1
                $amount = $amount * -1;
128
            }
129
130 9
            $entry = new Entry(
131
                $statement,
132
                $index,
133 9
                new Money($amount, new Currency($currency)),
134 9
                new DateTimeImmutable($bookingDate),
135 9
                new DateTimeImmutable($valueDate)
136
            );
137
138 9
            if (isset($entryXml->RvslInd) && (string) $entryXml->RvslInd === 'true') {
139
                $entry->setReversalIndicator(true);
140
            }
141
142 9
            if (isset($entryXml->NtryRef) && (string) $entryXml->NtryRef) {
143
                $entry->setReference((string) $entryXml->NtryRef);
144
            }
145
146 9
            if (isset($entryXml->AcctSvcrRef) && (string) $entryXml->AcctSvcrRef) {
147 2
                $entry->setAccountServicerReference((string) $entryXml->AcctSvcrRef);
148
            }
149
150 9
            if (isset($entryXml->NtryDtls->Btch->PmtInfId) && (string) $entryXml->NtryDtls->Btch->PmtInfId) {
151
                $entry->setBatchPaymentId((string) $entryXml->NtryDtls->Btch->PmtInfId);
152
            }
153
154 9
            $this->addTransactionDetailsToEntry($entryXml, $entry);
155
156 9
            $statement->addEntry($entry);
157 9
            $index++;
158
        }
159 9
    }
160
161
    /**
162
     * @param SimpleXMLElement $entryXml
163
     * @param Entry $entry
164
     */
165 9
    private function addTransactionDetailsToEntry(SimpleXMLElement $entryXml, Entry $entry)
166
    {
167 9
        $detailsXml = $entryXml->NtryDtls->TxDtls;
168 9
        if ($detailsXml) {
169 9
            foreach ($detailsXml as $detailXml) {
170 9
                $detail = new EntryTransactionDetail();
171 9
                $this->addReferencesToTransactionDetails($detailXml, $detail);
172 9
                $this->addRelatedPartiesToTransactionDetails($detailXml, $detail);
173 9
                $this->addRemittanceInformationToTransactionDetails($detailXml, $detail);
174 9
                $this->addReturnInformationToTransactionDetails($detailXml, $detail);
175 9
                $this->addAdditionalTransactionInformation($detailXml, $detail);
176 9
                $entry->addTransactionDetail($detail);
177
            }
178
        }
179 9
    }
180
181
    /**
182
     * @param SimpleXMLElement $detailXml
183
     * @param EntryTransactionDetail $detail
184
     */
185 9
    private function addRelatedPartiesToTransactionDetails(SimpleXMLElement $detailXml, EntryTransactionDetail $detail)
186
    {
187 9
        if (isset($detailXml->RltdPties)) {
188 9
            foreach ($detailXml->RltdPties as $relatedPartyXml) {
189 9
                if (isset($relatedPartyXml->Cdtr)) {
190 9
                    $relatedPartyTypeXml = $relatedPartyXml->Cdtr;
191 9
                    $relatedPartyTypeAccountXml = $relatedPartyXml->CdtrAcct;
192 9
                    $relatedPartyType = $creditor = new Creditor((string) $relatedPartyTypeXml->Nm);
0 ignored issues
show
Unused Code introduced by
$creditor is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
193
                } elseif (isset($relatedPartyXml->Dbtr)) {
194
                    $relatedPartyTypeXml = $relatedPartyXml->Dbtr;
195
                    $relatedPartyTypeAccountXml = $relatedPartyXml->DbtrAcct;
196
                    $relatedPartyType = $creditor = new Debtor((string) $relatedPartyTypeXml->Nm);
0 ignored issues
show
Unused Code introduced by
$creditor is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
197
                } else {
198
                    continue;
199
                }
200
201 9
                if (isset($relatedPartyTypeXml->PstlAdr)) {
202 7
                    $address = new Address();
203 7
                    if (isset($relatedPartyTypeXml->PstlAdr->Ctry)) {
204 7
                        $address = $address->setCountry((string) $relatedPartyTypeXml->PstlAdr->Ctry);
205
                    }
206 7
                    if (isset($relatedPartyTypeXml->PstlAdr->AdrLine)) {
207
                        foreach ($relatedPartyTypeXml->PstlAdr->AdrLine as $line) {
208
                            $address = $address->addAddressLine((string)$line);
209
                        }
210
                    }
211
212 7
                    $relatedPartyType->setAddress($address);
213
                }
214
215 9
                if (isset($relatedPartyTypeAccountXml->Id->IBAN) && $ibanCode = (string) $relatedPartyTypeAccountXml->Id->IBAN) {
216 9
                    $account = new Account(new Iban($ibanCode));
217
                } else {
218
                    $account = null;
219
                }
220
221 9
                $relatedParty = new RelatedParty($relatedPartyType, $account);
222 9
                $detail->addRelatedParty($relatedParty);
223
            }
224
        }
225 9
    }
226
227
    /**
228
     * @param SimpleXMLElement $detailXml
229
     * @param EntryTransactionDetail $detail
230
     */
231 9
    private function addReferencesToTransactionDetails(SimpleXMLElement $detailXml, EntryTransactionDetail $detail)
232
    {
233 9
        if (isset($detailXml->Refs->EndToEndId)) {
234 7
            $endToEndId = (string)$detailXml->Refs->EndToEndId;
235 7
            if (isset($detailXml->Refs->MndtId)) {
236
                $mandateId = (string)$detailXml->Refs->MndtId;
237
            } else {
238 7
                $mandateId = null;
239
            }
240 7
            $detail->addReference(new Reference($endToEndId, $mandateId));
241
        }
242 9
    }
243
244
    /**
245
     * @param SimpleXMLElement $detailXml
246
     * @param EntryTransactionDetail $detail
247
     */
248 9
    private function addRemittanceInformationToTransactionDetails(SimpleXMLElement $detailXml, EntryTransactionDetail $detail)
249
    {
250 9
        if (isset($detailXml->RmtInf)) {
251 9
            if (isset($detailXml->RmtInf->Ustrd)) {
252 7
                $remittanceInformation = RemittanceInformation::fromUnstructured(
253 7
                    (string)$detailXml->RmtInf->Ustrd
254
                );
255 7
                $detail->setRemittanceInformation($remittanceInformation);
256
            } elseif (isset($detailXml->RmtInf->Strd)) {
257 2
                if (isset($detailXml->RmtInf->Strd->CdtrRefInf) && isset($detailXml->RmtInf->Strd->CdtrRefInf->Ref)) {
258 2
                    $creditorReferenceInformation = CreditorReferenceInformation::fromUnstructured(
259 2
                        (string)$detailXml->RmtInf->Strd->CdtrRefInf->Ref
260
                    );
261 2
                    $remittanceInformation = new RemittanceInformation();
262 2
                    $remittanceInformation->setCreditorReferenceInformation($creditorReferenceInformation);
263 2
                    $detail->setRemittanceInformation($remittanceInformation);
264
                }
265
            }
266
        }
267 9
    }
268
269
    /**
270
     * @param SimpleXMLElement $detailXml
271
     * @param EntryTransactionDetail $detail
272
     */
273 9
    private function addReturnInformationToTransactionDetails(SimpleXMLElement $detailXml, EntryTransactionDetail $detail)
274
    {
275 9
        if (isset($detailXml->RtrInf)) {
276
            if (isset($detailXml->RtrInf->Rsn->Cd)) {
277
                $remittanceInformation = ReturnInformation::fromUnstructured(
278
                    (string)$detailXml->RtrInf->Rsn->Cd,
279
                    (string)$detailXml->RtrInf->AddtlInf
280
                );
281
                $detail->setReturnInformation($remittanceInformation);
282
            }
283
        }
284 9
    }
285
286
    /**
287
     * @param Message $message
288
     */
289 9
    private function addGroupHeaderToMessage(Message $message)
290
    {
291 9
        $groupHeaderXml = $this->document->BkToCstmrStmt->GrpHdr;
292 9
        $groupHeader = new GroupHeader(
293 9
            (string)$groupHeaderXml->MsgId,
294 9
            new DateTimeImmutable((string)$groupHeaderXml->CreDtTm)
295
        );
296
297 9
        $message->setGroupHeader($groupHeader);
298 9
    }
299
300
    /**
301
     * @param Message $message
302
     */
303 9
    private function addStatementsToMessage(Message $message)
304
    {
305 9
        $statements = [];
306
307 9
        $statementsXml = $this->document->BkToCstmrStmt->Stmt;
308 9
        foreach ($statementsXml as $statementXml) {
309 9
            $statement = new Statement(
310 9
                (string) $statementXml->Id,
311 9
                new DateTimeImmutable((string)$statementXml->CreDtTm),
312 9
                new Account(new Iban((string)$statementXml->Acct->Id->IBAN))
313
            );
314
315 9
            $this->addBalancesToStatement($statementXml, $statement);
316 9
            $this->addEntriesToStatement($statementXml, $statement);
317
318 9
            $statements[] = $statement;
319
        }
320
321 9
        $message->setStatements($statements);
322 9
    }
323
324
    /**
325
     * @param SimpleXMLElement $detailXml
326
     * @param EntryTransactionDetail $detail
327
     */
328 9
    private function addAdditionalTransactionInformation(SimpleXMLElement $detailXml, EntryTransactionDetail $detail)
329
    {
330 9
        if (isset($detailXml->AddtlTxInf)) {
331
            $additionalInformation = new AdditionalTransactionInformation(
332
                (string) $detailXml->AddtlTxInf
333
            );
334
            $detail->setAdditionalTransactionInformation($additionalInformation);
335
        }
336 9
    }
337
}
338