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
|
|||
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 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: