orca-services/nova-api-php

View on GitHub
src/Method/NovaCreateServicesMethod.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace OrcaServices\NovaApi\Method;

use DomainException;
use DOMDocument;
use DOMElement;
use Exception;
use OrcaServices\NovaApi\Parameter\NovaCreateServicesParameter;
use OrcaServices\NovaApi\Parser\NovaApiErrorParser;
use OrcaServices\NovaApi\Parser\NovaMessageParser;
use OrcaServices\NovaApi\Result\NovaCreateServicesResult;
use OrcaServices\NovaApi\Result\NovaServiceItem;
use OrcaServices\NovaApi\Soap\NovaApiSoapAction;
use OrcaServices\NovaApi\Xml\XmlDocument;

/**
 * SOAP method.
 */
final class NovaCreateServicesMethod implements NovaMethod
{
    /**
     * @var NovaApiSoapAction
     */
    private $novaSoapAction;

    /**
     * @var NovaApiErrorParser
     */
    private $novaErrorParser;

    /**
     * @var NovaMessageParser
     */
    private $novaMessageParser;

    /**
     * NovaSearchPartnerMethod constructor.
     *
     * @param NovaApiSoapAction $novaSoapAction The novaSoapAction
     * @param NovaApiErrorParser $novaErrorParser The novaErrorParser
     * @param NovaMessageParser $novaMessageParser The message parser
     */
    public function __construct(
        NovaApiSoapAction $novaSoapAction,
        NovaApiErrorParser $novaErrorParser,
        NovaMessageParser $novaMessageParser
    ) {
        $this->novaSoapAction = $novaSoapAction;
        $this->novaErrorParser = $novaErrorParser;
        $this->novaMessageParser = $novaMessageParser;
    }

    /**
     * 2. Klang.
     *
     * The next step is to accept the requested OFFER.
     * This subroutine checks the OFFER and whether the requested PERFORMANCE (e.g. for dynamic quotas)
     * is actually available. This turns the OFFER into a real PERFORMANCE, the sale of which is guaranteed by NOVA.
     * If desired, NOVA OFFER needs further information (e.g. about the customer) for processing.
     *
     * Service: https://confluence-ext.sbb.ch/display/NOVAUG/offeriereLeistungen
     *
     * @param NovaCreateServicesParameter $parameter The parameters
     *
     * @throws Exception if an error occurs
     *
     * @return NovaCreateServicesResult the result data
     */
    public function createService(NovaCreateServicesParameter $parameter): NovaCreateServicesResult
    {
        // The SOAP endpoint url
        $url = $this->novaSoapAction->getNovaSalesServiceUrl();

        // The SOAP action (http header)
        $soapAction = $this->novaSoapAction->getSoapAction('vertrieb', 'offeriereLeistungen');

        // The SOAP content (http body)
        $body = $this->createRequestBody($parameter);

        try {
            $xmlContent = $this->novaSoapAction->invokeSoapRequest($url, $soapAction, $body);
            $xml = XmlDocument::createFromXmlString($xmlContent);

            return $this->createResult($xml);
        } catch (Exception $exception) {
            throw $this->novaErrorParser->createGeneralException($exception);
        }
    }

    /**
     * Create SOAP body XML content.
     *
     * @param NovaCreateServicesParameter $parameter The parameters
     *
     * @return string The xml content
     */
    private function createRequestBody(NovaCreateServicesParameter $parameter): string
    {
        $dom = new DOMDocument('1.0', 'utf-8');
        $dom->formatOutput = true;

        $dom->appendChild($dom->createComment(' powered by Barakuda '));

        $envelope = $dom->createElement('soapenv:Envelope');
        $dom->appendChild($envelope);
        $envelope->setAttribute('xmlns:soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');

        $soapHeader = $dom->createElement('soapenv:Header');
        $envelope->appendChild($soapHeader);

        $body = $dom->createElement('soapenv:Body');
        $envelope->appendChild($body);

        $method = $dom->createElement('offeriereLeistungen');
        $body->appendChild($method);

        $this->novaSoapAction->appendMethodNamespaces($method);

        $methodRequest = $dom->createElement('ns18:offertenRequest');
        $method->appendChild($methodRequest);

        $methodRequest->setAttribute('ns18:transaktionsVerhalten', 'ROLLBACK_ON_ERROR');
        $methodRequest->setAttribute('ns18:fachlogLevel', 'OFF');

        $this->novaSoapAction->appendDomClientIdentifier($dom, $methodRequest, $parameter, 'ns18:');
        $this->novaSoapAction->appendDomCorrelationContext($dom, $methodRequest, $parameter, 'ns18:');

        $serviceRequest = $dom->createElement('ns18:leistungsRequest');
        $methodRequest->appendChild($serviceRequest);

        $serviceRequest->setAttribute('ns18:angebotsId', $parameter->novaOfferId);
        $serviceRequest->setAttribute('ns18:externeLeistungsReferenzId', '');
        $serviceRequest->setAttribute('ns18:externeReisendenReferenzId', '');

        //
        // Key/Value pairs
        //

        // Address
        if ($parameter->country && $parameter->postalCode) {
            $addressSaleParameter = $dom->createElement('ns18:verkaufsParameter');
            $serviceRequest->appendChild($addressSaleParameter);
            $addressSaleParameter->setAttribute('ns18:code', 'ADRESSE');

            $addressValue = $addressSaleParameter->appendChild($dom->createElement('wert'));

            $distributionBase = $dom->createElement('vertriebsbase:adresse');
            $addressValue->appendChild($distributionBase);
            $distributionBase->setAttribute('base:land', $parameter->country);
            $distributionBase->setAttribute('base:ort', $parameter->postalCode);
        }

        if ($parameter->lastName && $parameter->firstName) {
            $customerSaleParameter = $dom->createElement('ns18:verkaufsParameter');
            $serviceRequest->appendChild($customerSaleParameter);
            $customerSaleParameter->setAttribute('ns18:code', 'KUNDENNAME');

            $customerValue = $customerSaleParameter->appendChild($dom->createElement('wert'));

            $distributionName = $dom->createElement('vertriebsbase:nameVorname');
            $customerValue->appendChild($distributionName);
            $distributionName->setAttribute('base:name', $parameter->lastName);
            $distributionName->setAttribute('base:vorname', $parameter->firstName);
        }

        // TKID
        if ($parameter->tkId) {
            $travellerParameter = $dom->createElement('ns18:verkaufsParameter');
            $serviceRequest->appendChild($travellerParameter);

            $travellerParameter->setAttribute('ns18:code', 'REISENDER');

            $travellerValue = $dom->createElement('ns18:wert');
            $travellerParameter->appendChild($travellerValue);

            $distributionBaseTkid = $dom->createElement('vertriebsbase:tkid', $parameter->tkId);
            $travellerValue->appendChild($distributionBaseTkid);
        }

        return (string)$dom->saveXML();
    }

    /**
     * Create result object.
     *
     * @param XmlDocument $xml The xml document
     *
     * @throws DomainException
     *
     * @return NovaCreateServicesResult The mapped result
     */
    private function createResult(XmlDocument $xml): NovaCreateServicesResult
    {
        $result = new NovaCreateServicesResult();

        $xml = $xml->withoutNamespaces();

        // Find and append all messages
        foreach ($this->novaMessageParser->findNovaMessages($xml) as $message) {
            $result->addMessage($message);
        }

        // Root node
        $serviceResponseNode = $xml->queryFirstNode('/Envelope/Body/offeriereLeistungenResponse');
        $serviceNodes = $xml->queryNodes('offertenResponse/leistung', $serviceResponseNode);

        /** @var DOMElement $serviceNode */
        foreach ($serviceNodes as $serviceNode) {
            $serviceItem = new NovaServiceItem();

            $serviceId = $serviceNode->getAttribute('leistungsId');

            if (empty($serviceId)) {
                throw new DomainException('SBB-NOVA service ID not found');
            }

            $serviceItem->serviceId = $serviceId;
            $serviceItem->serviceReference = $serviceNode->getAttribute('leistungsReferenz');
            $serviceItem->serviceStatus = $serviceNode->getAttribute('leistungsStatus');
            $serviceItem->productNumber = $serviceNode->getAttribute('produktNummer');
            $serviceItem->tkId = $xml->findNodeValue('//tkid', $serviceNode);
            $serviceItem->price = $xml->getAttributeValue('verkaufsPreis/geldBetrag/@betrag', $serviceNode);
            $serviceItem->currency = $xml->getAttributeValue('verkaufsPreis/geldBetrag/@waehrung', $serviceNode);
            $serviceItem->vatAmount = $xml->getAttributeValue('verkaufsPreis/mwstAnteil/@betrag', $serviceNode);
            $serviceItem->vatPercent = $xml->getAttributeValue('verkaufsPreis/mwstAnteil/@mwstSatz', $serviceNode);

            $result->services[] = $serviceItem;
        }

        return $result;
    }
}