SepaDoc::addPaymentInstructionInfo()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 70
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 70
rs 8.2012
c 0
b 0
f 0
cc 7
nc 6
nop 1

How to fix   Long Method   

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 SKien\Sepa;
3
4
/**
5
 * Main class to create a Sepa-Document.
6
 *
7
 * @package Sepa
8
 * @author Stefanius <[email protected]>
9
 * @copyright MIT License - see the LICENSE file for details
10
 */
11
class SepaDoc extends \DOMDocument
12
{
13
    use SepaHelper;
14
15
    /** @var string  unique id  */
16
    protected string $strID = '';
17
    /** @var string type of sepa document  */
18
    protected string $type = '';
19
    /** @var string sepa version to use  */
20
    protected string $strSepaVersion = '';
21
    /** @var \DOMElement XML Base-Element       */
22
    protected ?\DOMElement $xmlBase = null;
23
    /** @var int overall count of transactions  */
24
    protected int $iTxCount = 0;
25
    /** @var \DOMElement DOM element containing overall count of transactions   */
26
    protected ?\DOMElement $xmlTxCount = null;
27
    /** @var float controlsum (sum of all PII's) */
28
    protected float $dblCtrlSum = 0.0;
29
    /** @var \DOMElement DOM element containing controlsum      */
30
    protected ?\DOMElement $xmlCtrlSum = null;
31
    /** @var int count of invalid transactions*/
32
    protected int $iInvalidTxCount = 0;
33
34
    /**
35
     * Creating a SEPA document.
36
     * A single SEPA document can only hold one type of transactions: <ul>
37
     * <li> Credit Transfer Transaction (Sepa::CCT) </li>
38
     * <li> Direct Debit Transaction (Sepa::CDD) </li></ul>
39
     * The Sepa version in which the file is to be created depends primarily on the
40
     * requirements of the bank to which the created file is to be submitted. It is
41
     * recommended to use the latest version supported by the institute.
42
     * @param string $type  type of transaction
43
     * @param string $strSepaVersion    Sepa version to use (Sepa::V26, Sepa::V29, Sepa::V30)
44
     */
45
    public function __construct(string $type, string $strSepaVersion = Sepa::V30)
46
    {
47
        // invalid type causes E_USER_ERROR
48
        $this->isValidType($type);
49
        $aTypeBase = [Sepa::CCT => 'CstmrCdtTrfInitn', Sepa::CDD => 'CstmrDrctDbtInitn'];
50
51
        $strPain = Sepa::getPainVersion($type, $strSepaVersion);
52
        $strBase = $aTypeBase[$type];
53
54
        parent::__construct("1.0", "UTF-8");
55
56
        $this->type = $type;
57
        $this->strSepaVersion = $strSepaVersion;
58
59
        $this->formatOutput = true;
60
        $this->preserveWhiteSpace = false; // 'formatOutput' only works if 'preserveWhiteSpace' set to false
61
62
        $xmlRoot = $this->createElement("Document");
63
        $xmlRoot->setAttribute("xmlns", "urn:iso:std:iso:20022:tech:xsd:" . $strPain);
64
        $xmlRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
65
        $xmlRoot->setAttribute("xsi:schemaLocation", "urn:iso:std:iso:20022:tech:xsd:" . $strPain . " " . $strPain . ".xsd");
66
        $this->appendChild($xmlRoot);
67
68
        $this->xmlBase = $this->createElement($strBase);
69
        $xmlRoot->appendChild($this->xmlBase);
70
    }
71
72
    /**
73
     * Creating group header and required elements.
74
     * If no `strID` is spassed, a unique identifier is internaly generated. This id
75
     * can be used to assign it to the data elements that were used to generate the
76
     * transactions contained in this file.
77
     * This ID is used by the receiving bank institute to avoid double processing.
78
     * @param string $strName   name (initiator of the transactions)
79
     * @param string $strID     unique id for the document (if null, id will be generated)
80
     * @return string           the (possibly created) id for the document
81
     */
82
    public function createGroupHeader(string $strName, string $strID = null) : string
83
    {
84
        if ($this->xmlBase != null) {
85
            $xmlGrpHdr = $this->createElement("GrpHdr");
86
            $this->xmlBase->appendChild($xmlGrpHdr);
87
88
            $this->strID = $strID ?? self::createUID();
89
90
            $this->addChild($xmlGrpHdr, 'MsgId', $this->strID);
91
            $this->addChild($xmlGrpHdr, 'CreDtTm', date(DATE_ATOM)); // str_replace(' ', 'T', date('Y-m-d h:i:s')));
92
            $this->xmlTxCount = $this->addChild($xmlGrpHdr, 'NbOfTxs', 0);
93
            $this->xmlCtrlSum = $this->addChild($xmlGrpHdr, 'CtrlSum', sprintf("%01.2f", 0.0));
94
95
            $xmlNode = $this->addChild($xmlGrpHdr, 'InitgPty');
96
            $this->addChild($xmlNode, 'Nm', self::validString($strName, Sepa::MAX70));
97
            // SEPA spec recommends not to support 'InitgPty' -> 'Id'
98
        }
99
100
        return $this->strID;
101
    }
102
103
    /**
104
     * Add payment instruction info (PII) to the document.
105
     * PII is the base element to add transactions to the SEPA document.
106
     * > One SEPA document may contains multiple PII.
107
     * @see SepaPmtInf
108
     * @param SepaPmtInf $oPmtInf
109
     * @return int Sepa::OK or error code from SepaPmtInf::validate()
110
     */
111
    public function addPaymentInstructionInfo(SepaPmtInf $oPmtInf) : int
112
    {
113
        $iErr = -1;
114
        if ($this->xmlBase === null || $this->xmlTxCount === null || $this->xmlCtrlSum === null) {
115
            trigger_error('call createGroupHeader() before add PII', E_USER_ERROR);
116
        } else {
117
            $iErr = $oPmtInf->validate();
118
            if ($iErr == Sepa::OK) {
119
                $this->xmlBase->appendChild($oPmtInf);
120
121
                $this->addChild($oPmtInf, 'PmtInfId', $this->strID);
122
                $this->addChild($oPmtInf, 'PmtMtd', $this->type);
123
                $oPmtInf->setTxCountNode($this->addChild($oPmtInf, 'NbOfTxs', 0));
124
                $oPmtInf->setCtrlSumNode($this->addChild($oPmtInf, 'CtrlSum', sprintf("%01.2f", 0.0)));
125
126
                // Payment Type Information
127
                $xmlPmtTpInf = $this->addChild($oPmtInf, 'PmtTpInf');
128
                $xmlNode = $this->addChild($xmlPmtTpInf, 'SvcLvl');
129
                $this->addChild($xmlNode, 'Cd', 'SEPA');
130
131
                if (($strCategoryPurpose = $oPmtInf->getCategoryPurpose()) != '') {
132
                    $xmlNode = $this->addChild($xmlPmtTpInf, 'CtgyPurp');
133
                    $this->addChild($xmlNode, 'Cd', $strCategoryPurpose);
134
                }
135
136
                if ($this->type == Sepa::CDD) {
137
                    // only for directdebit
138
                    $xmlNode = $this->addChild($xmlPmtTpInf, 'LclInstrm');
139
                    $this->addChild($xmlNode, 'Cd', 'CORE');
140
                    $this->addChild($xmlPmtTpInf, 'SeqTp', $oPmtInf->getSeqType());
141
                    $this->addChild($oPmtInf, 'ReqdColltnDt', $oPmtInf->getCollExecDate(Sepa::CDD));
142
143
                    // Creditor Information
144
                    $xmlNode = $this->addChild($oPmtInf, 'Cdtr');
145
                    $this->addChild($xmlNode, 'Nm', $oPmtInf->getName());
146
147
                    $xmlNode = $this->addChild($oPmtInf, 'CdtrAcct');
148
                    $xmlNode = $this->addChild($xmlNode, 'Id');
149
                    $this->addChild($xmlNode, 'IBAN', $oPmtInf->getIBAN());
150
151
                    $xmlNode = $this->addChild($oPmtInf, 'CdtrAgt');
152
                    $xmlNode = $this->addChild($xmlNode, 'FinInstnId');
153
                    $this->addChild($xmlNode, 'BIC', $oPmtInf->getBIC());
154
155
                    // Creditor Scheme Identification
156
                    $xmlNode = $this->addChild($oPmtInf, 'CdtrSchmeId');
157
                    $xmlNode = $this->addChild($xmlNode, 'Id');
158
                    $xmlNode = $this->addChild($xmlNode, 'PrvtId');
159
                    $xmlNode = $this->addChild($xmlNode, 'Othr');
160
                    $this->addChild($xmlNode, 'Id', $oPmtInf->getCI());
161
                    $xmlNode = $this->addChild($xmlNode, 'SchmeNm');
162
                    $this->addChild($xmlNode, 'Prtry', 'SEPA');
163
                } else {
164
                    $this->addChild($oPmtInf, 'ReqdExctnDt', $oPmtInf->getCollExecDate(Sepa::CCT));
165
166
                    // Creditor Information
167
                    $xmlNode = $this->addChild($oPmtInf, 'Dbtr');
168
                    $this->addChild($xmlNode, 'Nm', $oPmtInf->getName());
169
170
                    $xmlNode = $this->addChild($oPmtInf, 'DbtrAcct');
171
                    $xmlNode = $this->addChild($xmlNode, 'Id');
172
                    $this->addChild($xmlNode, 'IBAN', $oPmtInf->getIBAN());
173
174
                    $xmlNode = $this->addChild($oPmtInf, 'DbtrAgt');
175
                    $xmlNode = $this->addChild($xmlNode, 'FinInstnId');
176
                    $this->addChild($xmlNode, 'BIC', $oPmtInf->getBIC());
177
                }
178
            }
179
        }
180
        return $iErr;
181
    }
182
183
    /**
184
     * Outputs the generated SEPA document as XML-File through the browser.
185
     * > - To save the XML file direct anywhere on the server, use the `DOMDocument::save()` method.<br>
186
     * > - To save the XML file in a database use the `DOMDocument::saveXML()` method.<br>
187
     *
188
     * The target should always be 'attachment' (<i>indicating it should be downloaded; most browsers
189
     * presenting a 'Save as' dialog, prefilled with the value of the filename parameter</i>). <br>
190
     * For test purposes you can change it to 'inline' to display the XML inside of the browser
191
     * rather than save it to a file.
192
     * @param string $strName       output filename
193
     * @param string $strTarget     target (default: 'attachment')
194
     */
195
    public function output(string $strName, string $strTarget = 'attachment') : void
196
    {
197
        // Set the HTTP header and echo the generated content
198
        header('Content-Type: application/xml');
199
        header('Content-Disposition: ' . $strTarget . '; filename="' . $strName . '"');
200
        header('Cache-Control: private, max-age=0, must-revalidate');
201
        header('Pragma: public');
202
203
        echo $this->saveXML();
204
    }
205
206
    /**
207
     * Calculate overall transaction count and controlsum.
208
     * This method should only be called internal by the `SepaPmtInf`class!
209
     * @param float $dblValue
210
     * @internal
211
     */
212
    public function calc(float $dblValue) : void
213
    {
214
        if ($this->xmlTxCount === null || $this->xmlCtrlSum === null) {
215
            trigger_error('call createGroupHeader() before calc()', E_USER_ERROR);
216
        } else {
217
            $this->iTxCount++;
218
            $this->xmlTxCount->nodeValue = (string)$this->iTxCount;
219
            $this->dblCtrlSum += $dblValue;
220
            $this->xmlCtrlSum->nodeValue = sprintf("%01.2f", $this->dblCtrlSum);
221
        }
222
    }
223
224
    /**
225
     * Increments the count of invalid transactions.
226
     * @internal
227
     */
228
    public function incInvalidCount() : void
229
    {
230
        $this->iInvalidTxCount++;
231
    }
232
233
    /**
234
     * Create child element for given parent.
235
     * @param \DOMElement   $xmlParent  parent for the node. If null, child of current instance is created
236
     * @param string        $strNode    nodename
237
     * @param int|string    $value      nodevalue. If empty, no value will be assigned (to create node only containing child elements)
238
     * @return \DOMElement
239
     */
240
    protected function addChild(\DOMElement $xmlParent, string $strNode, $value = '') : \DOMElement
241
    {
242
        $xmlNode = $this->createElement($strNode);
243
        if (!empty($value)) {
244
            $xmlNode->nodeValue = (string)$value;
245
        }
246
        $xmlParent->appendChild($xmlNode);
247
248
        return $xmlNode;
249
    }
250
251
    /**
252
     * Return the internal generated ID.
253
     * @return string
254
     */
255
    public function getId() : string
256
    {
257
        return $this->strID;
258
    }
259
260
    /**
261
     * Return the type.
262
     * @return string
263
     */
264
    public function getType() : string
265
    {
266
        return $this->type;
267
    }
268
269
    /**
270
     * Count of valid transactions.
271
     * @return int
272
     */
273
    public function getTxCount() : int
274
    {
275
        return $this->iTxCount;
276
    }
277
278
    /**
279
     * Total value of valid transactions.
280
     * @return float
281
     */
282
    public function getCtrlSum() : float
283
    {
284
        return $this->dblCtrlSum;
285
    }
286
287
    /**
288
     * Get the count of invalid transactions passed.
289
     * @return int
290
     */
291
    public function getInvalidCount() : int
292
    {
293
        return $this->iInvalidTxCount;
294
    }
295
}
296