NovaCreateOffersMethod   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 259
Duplicated Lines 0 %

Test Coverage

Coverage 85.96%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 115
c 1
b 0
f 0
dl 0
loc 259
ccs 98
cts 114
cp 0.8596
rs 10
wmc 14

4 Methods

Rating   Name   Duplication   Size   Complexity  
B createRequestBody() 0 97 7
A createOffers() 0 18 2
A createResult() 0 76 4
A __construct() 0 8 1
1
<?php
2
3
namespace OrcaServices\NovaApi\Method;
4
5
use Cake\Chronos\Chronos;
6
use DomainException;
7
use DOMDocument;
8
use DOMElement;
9
use Exception;
10
use InvalidArgumentException;
11
use OrcaServices\NovaApi\Parameter\NovaCreateOffersParameter;
12
use OrcaServices\NovaApi\Parser\NovaApiErrorParser;
13
use OrcaServices\NovaApi\Parser\NovaMessageParser;
14
use OrcaServices\NovaApi\Result\NovaCreateOffersResult;
15
use OrcaServices\NovaApi\Result\NovaOffer;
16
use OrcaServices\NovaApi\Soap\NovaApiSoapAction;
17
use OrcaServices\NovaApi\Type\GenderType;
18
use OrcaServices\NovaApi\Xml\XmlDocument;
19
20
/**
21
 * SOAP method.
22
 */
23
final class NovaCreateOffersMethod implements NovaMethod
24
{
25
    /**
26
     * @var NovaApiSoapAction
27
     */
28
    private $novaSoapAction;
29
30
    /**
31
     * @var NovaApiErrorParser
32
     */
33
    private $novaErrorParser;
34
35
    /**
36
     * @var NovaMessageParser
37
     */
38
    private $novaMessageParser;
39
40
    /**
41
     * NovaSearchPartnerMethod constructor.
42
     *
43
     * @param NovaApiSoapAction $novaSoapAction The novaSoapAction
44
     * @param NovaApiErrorParser $novaErrorParser The novaErrorParser
45
     * @param NovaMessageParser $novaMessageParser The message parser
46
     */
47 13
    public function __construct(
48
        NovaApiSoapAction $novaSoapAction,
49
        NovaApiErrorParser $novaErrorParser,
50
        NovaMessageParser $novaMessageParser
51
    ) {
52 13
        $this->novaSoapAction = $novaSoapAction;
53 13
        $this->novaErrorParser = $novaErrorParser;
54 13
        $this->novaMessageParser = $novaMessageParser;
55 13
    }
56
57
    /**
58
     * Create offers.
59
     *
60
     * https://confluence-ext.sbb.ch/display/NOVAUG/erstelleAngebote
61
     *
62
     * @param NovaCreateOffersParameter $parameter The parameter
63
     *
64
     * @throws Exception If an error occurs
65
     *
66
     * @return NovaCreateOffersResult List of offers
67
     */
68 2
    public function createOffers(NovaCreateOffersParameter $parameter): NovaCreateOffersResult
69
    {
70
        // The SOAP endpoint url
71 2
        $url = $this->novaSoapAction->getNovaSalesServiceUrl();
72
73
        // The SOAP action (http header)
74 2
        $soapAction = $this->novaSoapAction->getSoapAction('vertrieb', 'erstelleAngebote');
75
76
        // The SOAP content (http body)
77 2
        $body = $this->createRequestBody($parameter);
78
79
        try {
80 2
            $xmlContent = $this->novaSoapAction->invokeSoapRequest($url, $soapAction, $body);
81 2
            $xml = XmlDocument::createFromXmlString($xmlContent);
82
83 2
            return $this->createResult($xml);
84
        } catch (Exception $exception) {
85
            throw $this->novaErrorParser->createGeneralException($exception);
86
        }
87
    }
88
89
    /**
90
     * Create SOAP body XML content.
91
     *
92
     * @param NovaCreateOffersParameter $parameter The parameters
93
     *
94
     * @throws InvalidArgumentException
95
     *
96
     * @return string The xml content
97
     */
98 2
    private function createRequestBody(NovaCreateOffersParameter $parameter): string
99
    {
100 2
        $dom = new DOMDocument('1.0', 'utf-8');
101 2
        $dom->formatOutput = true;
102
103 2
        $dom->appendChild($dom->createComment(' powered by Barakuda '));
104
105 2
        $envelope = $dom->createElement('soapenv:Envelope');
106 2
        $dom->appendChild($envelope);
107 2
        $envelope->setAttribute('xmlns:soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
108
109 2
        $soapHeader = $dom->createElement('soapenv:Header');
110 2
        $envelope->appendChild($soapHeader);
111
112 2
        $body = $dom->createElement('soapenv:Body');
113 2
        $envelope->appendChild($body);
114
115 2
        $method = $dom->createElement('ns18:erstelleAngebote');
116 2
        $body->appendChild($method);
117
118 2
        $this->novaSoapAction->appendMethodNamespaces($method);
119
120 2
        $methodRequest = $dom->createElement('ns18:angebotsRequest');
121 2
        $method->appendChild($methodRequest);
122
123 2
        $methodRequest->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
124 2
        $methodRequest->setAttribute('xsi:type', 'ns18:ZonenplanBasierterAngebotsRequest');
125
126
        // Ticket valid from
127 2
        $methodRequest->setAttribute('ns18:gueltigAbDatum', $parameter->validFrom->format('Y-m-d'));
128
129 2
        $methodRequest->setAttribute('ns18:kundenSegmenteGruppieren', 'false');
130 2
        $methodRequest->setAttribute('ns18:fachlogLevel', 'OFF');
131
132 2
        $this->novaSoapAction->appendDomClientIdentifier($dom, $methodRequest, $parameter, 'ns18:');
133 2
        $this->novaSoapAction->appendDomCorrelationContext($dom, $methodRequest, $parameter, 'ns18:');
134
135 2
        $methodRequest->appendChild($dom->createElement('ns18:traegerMedium', 'SWISSPASS'));
136
137 2
        $traveller = $dom->createElement('ns18:reisender');
138 2
        $methodRequest->appendChild($traveller);
139
140 2
        $traveller->setAttribute('ns18:externeReisendenReferenzId', '1');
141
142 2
        $tkId = $parameter->tkId;
143 2
        if (!empty($tkId)) {
144
            // with tkid
145 2
            $withTkid = $dom->createElement('ns18:mitTkid');
146 2
            $traveller->appendChild($withTkid);
147
148 2
            $withTkid->appendChild($dom->createElement('ns18:tkid', $parameter->tkId));
149 2
            $withTkid->appendChild($dom->createElement('ns18:ermaessigungsKarteCode', 'KEINE_ERMAESSIGUNGSKARTE'));
150
        } else {
151
            // without tkid, search with personal information
152
            $withoutTkid = $dom->createElement('ns18:ohneTkid');
153
            $traveller->appendChild($withoutTkid);
154
155
            $withoutTkid->appendChild($dom->createElement('ns18:reisendenTyp', 'PERSON'));
156
157
            $genderTypeId = $parameter->genderTypeId;
158
            if ($genderTypeId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $genderTypeId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
159
                $genderValue = $genderTypeId === GenderType::MEN ? 'MAENNLICH' : 'WEIBLICH';
160
                $withoutTkid->appendChild($dom->createElement('ns18:geschlecht', $genderValue));
161
            }
162
163
            $dateOfBirth = $parameter->dateOfBirth;
164
            if ($dateOfBirth !== null) {
165
                $dateOfBirthValue = $dateOfBirth->format('Y-m-d');
166
                $withoutTkid->appendChild($dom->createElement('ns18:geburtsTag', $dateOfBirthValue));
167
            }
168
169
            $withoutTkid->appendChild($dom->createElement('ns18:ermaessigungsKarteCode', 'KEINE_ERMAESSIGUNGSKARTE'));
170
        }
171
172 2
        $offerFilter = $dom->createElement('ns18:angebotsFilter');
173 2
        $methodRequest->appendChild($offerFilter);
174
175 2
        $offerFilter->setAttribute('xsi:type', 'vertriebsbase:ProduktNummerFilter');
176 2
        $offerFilter->appendChild(
177 2
            $dom->createElement('vertriebsbase:produktNummer', (string)$parameter->novaProductNumber)
178
        );
179
180 2
        if (empty($parameter->travelClass)) {
181
            throw new InvalidArgumentException('Travel class is required');
182
        }
183
184 2
        $travelClassValue = $parameter->travelClass === 1 ? 'KLASSE_1' : 'KLASSE_2';
185 2
        $methodRequest->appendChild($dom->createElement('ns18:klasse', $travelClassValue));
186
187 2
        $zoneRequest = $dom->createElement('ns18:zonenRequest');
188 2
        $methodRequest->appendChild($zoneRequest);
189
190 2
        $zoneRequest->setAttribute('ns18:tarifOwner', $parameter->tariffOwner);
191
192 2
        $zoneRequest->appendChild($dom->createElement('ns18:alleZonen', 'true'));
193
194 2
        return (string)$dom->saveXML();
195
    }
196
197
    /**
198
     * Create result object.
199
     *
200
     * @param XmlDocument $xml The xml document
201
     *
202
     * @throws DomainException
203
     *
204
     * @return NovaCreateOffersResult The mapped result
205
     */
206 2
    private function createResult(XmlDocument $xml): NovaCreateOffersResult
207
    {
208 2
        $result = new NovaCreateOffersResult();
209
210 2
        $xml = $xml->withoutNamespaces();
211
212
        // Find and append all messages
213 2
        foreach ($this->novaMessageParser->findNovaMessages($xml) as $message) {
214 2
            $result->addMessage($message);
215
        }
216
217
        // Root node
218 2
        $responseNode = $xml->queryFirstNode('/Envelope/Body/erstelleAngeboteResponse');
219 2
        $offerNodes = $xml->queryNodes('angebotsResponse/angebote/angebot', $responseNode);
220
221
        /** @var DOMElement $offerNode */
222 2
        foreach ($offerNodes as $offerNode) {
223 2
            $offer = new NovaOffer();
224
225 2
            $offerId = $offerNode->getAttribute('angebotsId');
226
227 2
            if (empty($offerId)) {
228
                throw new DomainException('SBB-NOVA offer ID not found');
229
            }
230
231 2
            $offer->novaOfferId = $offerId;
232
233 2
            $titles = [];
234
235
            // All Zones
236 2
            $titles[] = $xml->getAttributeValue(
237 2
                'nutzungsInfo/tarifStufe/tarifStufenText/@defaultWert',
238 2
                $offerNode
239
            );
240
241
            // Adults
242 2
            $titles[] = $xml->getAttributeValue(
243 2
                'produktEinflussFaktoren/kundenSegment/bezeichnung/@defaultWert',
244 2
                $offerNode
245
            );
246
247
            // Months
248 2
            $titles[] = $xml->getAttributeValue(
249 2
                'produktEinflussFaktoren/geltungsDauer/nutzungsGeltungsDauer/einheit/bezeichnung/@defaultWert',
250 2
                $offerNode
251
            );
252 2
            $title = trim(implode(', ', array_filter($titles)));
253
254 2
            $offer->title = $title;
255
256 2
            $price = $xml->getAttributeValue('verkaufsPreis/geldBetrag/@betrag', $offerNode);
257 2
            $offer->price = $price;
258
259 2
            $currency = $xml->getAttributeValue('verkaufsPreis/geldBetrag/@waehrung', $offerNode);
260
261 2
            $offer->currency = $currency;
262
263 2
            $productNumber = $offerNode->getAttribute('produktNummer');
264 2
            $offer->productNumber = $productNumber;
265
266 2
            $validFrom = $xml->getAttributeValue('nutzungsInfo/nutzungsZeitraum/ausweisbarerZeitraum/@von', $offerNode);
267 2
            $offer->validFrom = (new Chronos($validFrom))->setTime(0, 0);
268
269 2
            $validTo = $xml->getAttributeValue('nutzungsInfo/nutzungsZeitraum/ausweisbarerZeitraum/@bis', $offerNode);
270 2
            $offer->validTo = (new Chronos($validTo))->setTime(23, 59, 59);
271
272 2
            $carrierMedium = $xml->getAttributeValue('produktEinflussFaktoren/@traegerMedium', $offerNode);
273 2
            $offer->carrierMedium = $carrierMedium;
274
275 2
            $travelClass = $xml->getAttributeValue('produktEinflussFaktoren/@klasse', $offerNode);
276 2
            $offer->travelClass = $travelClass;
277
278 2
            $result->offers[] = $offer;
279
        }
280
281 2
        return $result;
282
    }
283
}
284