<?php

namespace PlentyConnector\Components\PayPal\Shopware;

use Doctrine\DBAL\Connection;
use Exception;
use PlentyConnector\Components\PayPal\PaymentData\PayPalUnifiedInstallmentPaymentData;
use ShopwareAdapter\ResponseParser\Payment\PaymentResponseParserInterface;
use SystemConnector\TransferObject\Payment\Payment;

class PayPalUnifiedInstallmentPaymentResponseParser implements PaymentResponseParserInterface
{
    /**
     * @var PaymentResponseParserInterface
     */
    private $parentResponseParser;

    /**
     * @var Connection
     */
    private $connection;

    public function __construct(
        PaymentResponseParserInterface $parentResponseParser,
        Connection $connection
    ) {
        $this->parentResponseParser = $parentResponseParser;
        $this->connection = $connection;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $element)
    {
        $payments = $this->parentResponseParser->parse($element);

        foreach ($payments as $payment) {
            if (!($payment instanceof Payment)) {
                continue;
            }

            $this->addPayPalUnifiedInstallment($payment, $element);
        }

        return $payments;
    }

    /**
     * @param $paymentId
     *
     * @return array|bool
     */
    private function getPayPalUnifiedInstallmentData($paymentId)
    {
        try {
            $query = 'SELECT * FROM swag_payment_paypal_unified_financing_information WHERE payment_id = ?';

            return $this->connection->fetchAssoc($query, [$paymentId]);
        } catch (Exception $exception) {
            return false;
        }
    }

    /**
     * @param Payment $payment
     * @param array   $element
     */
    private function addPayPalUnifiedInstallment(Payment $payment, array $element)
    {
        $data = $this->getPayPalUnifiedInstallmentData($element['paymentInstances'][0]['id']);

        if (empty($data)) {
            return;
        }

        $paymentData = new PayPalUnifiedInstallmentPaymentData();
        $paymentData->setFeeAmount((float) $data['fee_amount']);
        $paymentData->setTotalCost((float) $data['total_cost']);
        $paymentData->setMonthlyPayment((float) $data['monthly_payment']);
        $paymentData->setTerm((int) $data['term']);

        $payment->setPaymentData($paymentData);
    }
}


<?php

namespace ShopwareAdapter\RequestGenerator\Product\Variation;

use ShopwareAdapter\DataProvider\CustomerGroup\CustomerGroupDataProviderInterface;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\CustomerGroup\CustomerGroup;
use SystemConnector\TransferObject\Media\Media;
use SystemConnector\TransferObject\Product\Barcode\Barcode;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\TransferObject\Product\Variation\Variation;
use SystemConnector\TransferObject\Shop\Shop;
use SystemConnector\TransferObject\Unit\Unit;

class VariationRequestGenerator implements VariationRequestGeneratorInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var CustomerGroupDataProviderInterface
     */
    private $customerGroupDataProvider;

    public function __construct(
        IdentityServiceInterface $identityService,
        CustomerGroupDataProviderInterface $customerGroupDataProvider
    ) {
        $this->identityService = $identityService;
        $this->customerGroupDataProvider = $customerGroupDataProvider;
    }

    /**
     * {@inheritdoc}
     */
    public function generate(Variation $variation)
    {
        $unitIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $variation->getUnitIdentifier(),
            'objectType' => Unit::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        if (null === $unitIdentity) {
            throw new NotFoundException('Missing unit mapping - ' . $variation->getNumber());
        }

        $productIdentitiy = $this->identityService->findOneBy([
            'objectIdentifier' => $variation->getProductIdentifier(),
            'objectType' => Product::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        if (null === $productIdentitiy) {
            throw new NotFoundException('Missing product for variation - ' . $variation->getProductIdentifier());
        }

        $shopwareVariation = [
            'articleId' => $productIdentitiy->getAdapterIdentifier(),
            'number' => $variation->getNumber(),
            'position' => $variation->getPosition(),
            'unitId' => $unitIdentity->getAdapterIdentifier(),
            'active' => $variation->getActive(),
            'kind' => $variation->isMain() ? 1 : 2,
            'isMain' => $variation->isMain(),
            'lastStock' => $variation->hasStockLimitation(),
            'standard' => $variation->isMain(),
            'shippingtime' => $variation->getShippingTime(),
            'prices' => $this->getPrices($variation),
            'supplierNumber' => $variation->getModel(),
            'purchasePrice' => $variation->getPurchasePrice(),
            'weight' => $variation->getWeight(),
            'len' => $variation->getLength(),
            'height' => $variation->getHeight(),
            'width' => $variation->getWidth(),
            'images' => $this->getImages($variation),
            'purchaseUnit' => $variation->getContent(),
            'referenceUnit' => $variation->getReferenceAmount(),
            'minPurchase' => $variation->getMinimumOrderQuantity(),
            'purchaseSteps' => $variation->getIntervalOrderQuantity(),
            'maxPurchase' => $variation->getMaximumOrderQuantity(),
            '__options_prices' => ['replace' => true],
            '__options_images' => ['replace' => true],
        ];

        $releaseData = $variation->getReleaseDate();
        if (null !== $releaseData) {
            $shopwareVariation['releaseDate'] = $releaseData->format(DATE_W3C);
        }

        $configuratorOptions = $this->getConfiguratorOptions($variation);
        if (!empty($configuratorOptions)) {
            $shopwareVariation['configuratorOptions'] = $configuratorOptions;
        }

        /**
         * @var Barcode[] $barcodes
         */
        $barcodes = array_filter($variation->getBarcodes(), function (Barcode $barcode) {
            return $barcode->getType() === Barcode::TYPE_GTIN13;
        });

        if (!empty($barcodes)) {
            $barcode = array_shift($barcodes);

            $shopwareVariation['ean'] = $barcode->getCode();
        }

        return $shopwareVariation;
    }

    /**
     * @param Variation $variation
     *
     * @return array
     */
    private function getPrices(Variation $variation)
    {
        $prices = [];

        foreach ($variation->getPrices() as $price) {
            if (null === $price->getCustomerGroupIdentifier()) {
                $customerGroupKey = 'EK';
            } else {
                $customerGroupIdentity = $this->identityService->findOneBy([
                    'objectIdentifier' => $price->getCustomerGroupIdentifier(),
                    'objectType' => CustomerGroup::TYPE,
                    'adapterName' => ShopwareAdapter::NAME,
                ]);

                if (null === $customerGroupIdentity) {
                    continue;
                }

                $customerGroupKey = $this->customerGroupDataProvider->getCustomerGroupKeyByShopwareIdentifier(
                    $customerGroupIdentity->getAdapterIdentifier()
                );

                if (null === $customerGroupKey) {
                    continue;
                }
            }

            $prices[] = [
                'customerGroupKey' => $customerGroupKey,
                'price' => $price->getPrice(),
                'pseudoPrice' => $price->getPseudoPrice(),
                'from' => $price->getFromAmount(),
                'to' => $price->getToAmount(),
            ];
        }

        return $prices;
    }

    /**
     * @param Variation $variation
     *
     * @return array
     */
    private function getImages(Variation $variation)
    {
        $images = [];
        foreach ($variation->getImages() as $image) {
            $shopIdentifiers = array_filter($image->getShopIdentifiers(), function ($shop) {
                $identity = $this->identityService->findOneBy([
                    'objectIdentifier' => (string) $shop,
                    'objectType' => Shop::TYPE,
                    'adapterName' => ShopwareAdapter::NAME,
                ]);

                return $identity !== null;
            });

            if (empty($shopIdentifiers)) {
                continue;
            }

            $imageIdentity = $this->identityService->findOneBy([
                'objectIdentifier' => $image->getMediaIdentifier(),
                'objectType' => Media::TYPE,
                'adapterName' => ShopwareAdapter::NAME,
            ]);

            if (null === $imageIdentity) {
                continue;
            }

            $images[] = [
                'mediaId' => $imageIdentity->getAdapterIdentifier(),
                'position' => $image->getPosition(),
            ];
        }

        return $images;
    }

    /**
     * @param Variation $variation
     *
     * @return array
     */
    private function getConfiguratorOptions(Variation $variation)
    {
        $configuratorOptions = [];
        foreach ($variation->getProperties() as $property) {
            foreach ($property->getValues() as $value) {
                $configuratorOptions[] = [
                    'groupId' => null,
                    'group' => $property->getName(),
                    'optionId' => null,
                    'option' => $value->getValue(),
                ];
            }
        }

        return $configuratorOptions;
    }
}


<?php

namespace PlentyConnector\Components\Bundle\PlentymarketsAdapter\ResponseParser;

use DateTimeImmutable;
use PlentyConnector\Components\Bundle\TransferObject\Bundle;
use PlentyConnector\Components\Bundle\TransferObject\BundleProduct\BundleProduct;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\Helper\VariationHelperInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Product\Price\PriceResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\TransferObject\VatRate\VatRate;
use SystemConnector\ValueObject\Translation\Translation;

class BundleResponseParser implements BundleResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var PriceResponseParserInterface
     */
    private $priceResponseParser;

    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var VariationHelperInterface
     */
    private $variationHelper;

    public function __construct(
        IdentityServiceInterface $identityService,
        PriceResponseParserInterface $priceResponseParser,
        VariationHelperInterface $variationHelper,
        ClientInterface $client,
        LoggerInterface $logger
    ) {
        $this->identityService = $identityService;
        $this->priceResponseParser = $priceResponseParser;
        $this->client = $client;
        $this->logger = $logger;
        $this->variationHelper = $variationHelper;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $product)
    {
        $bundleVariations = array_filter($product['variations'], function (array $variation) {
            return $variation['bundleType'] === 'bundle';
        });

        if (empty($bundleVariations)) {
            return [];
        }

        $bundles = [];

        foreach ($bundleVariations as $bundle) {
            if (empty($this->variationHelper->getShopIdentifiers($bundle))) {
                continue;
            }

            $bundles[] = $this->parseBundle($bundle, $product);
        }

        return array_filter($bundles);
    }

    /**
     * @param array $variation
     *
     * @return string
     */
    private function getVatRateIdentifier(array $variation)
    {
        $vatRateIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => $variation['vatId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => VatRate::TYPE,
        ]);

        if (null === $vatRateIdentity) {
            throw new NotFoundException('missing mapping for vat rate');
        }

        return $vatRateIdentity->getObjectIdentifier();
    }

    /**
     * @param array $variation
     * @param array $product
     *
     * @return null|Bundle
     */
    private function parseBundle(array $variation, array $product)
    {
        $identity = $this->identityService->findOneOrCreate(
            (string) $variation['id'],
            PlentymarketsAdapter::NAME,
            Bundle::TYPE
        );

        $productIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $product['id'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Product::TYPE,
        ]);

        if (null === $productIdentity) {
            $this->logger->notice('product not found', ['bundle' => $variation]);

            return null;
        }

        $bundle = new Bundle();
        $bundle->setIdentifier($identity->getObjectIdentifier());
        $bundle->setName($product['texts'][0]['name1']);
        $bundle->setNumber($variation['number']);
        $bundle->setPosition($variation['position']);
        $bundle->setStockLimitation((bool) $variation['stockLimitation']);
        $bundle->setPrices($this->priceResponseParser->parse($variation));
        $bundle->setVatRateIdentifier($this->getVatRateIdentifier($variation));
        $bundle->setProductIdentifier($productIdentity->getObjectIdentifier());
        $bundle->setAvailableFrom($this->getAvailableFrom($variation));
        $bundle->setAvailableTo($this->getAvailableTo($variation));
        $bundle->setBundleProducts($this->getBundleProducts($variation));
        $bundle->setTranslations($this->getTranslations($product));

        return $bundle;
    }

    /**
     * @param array $mainVariation
     *
     * @return null|DateTimeImmutable
     */
    private function getAvailableFrom(array $mainVariation)
    {
        if (!empty($mainVariation['availableUntil'])) {
            return new DateTimeImmutable('now');
        }

        return null;
    }

    /**
     * @param array $mainVariation
     *
     * @return null|DateTimeImmutable
     */
    private function getAvailableTo(array $mainVariation)
    {
        if (!empty($mainVariation['availableUntil'])) {
            return new DateTimeImmutable($mainVariation['availableUntil']);
        }

        return null;
    }

    /**
     * @param array $product
     *
     * @return Translation[]
     */
    private function getTranslations(array $product)
    {
        $translations = [];

        foreach ($product['texts'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'name',
                'value' => $text['name1'],
            ]);
        }

        return $translations;
    }

    /**
     * @param array $elements
     */
    private function addProductNumberToResponse(array &$elements)
    {
        $ids = implode(',', array_column($elements, 'componentVariationId'));
        $variations = $this->client->request('GET', 'items/variations', ['id' => $ids]);

        foreach ($elements as &$element) {
            $matchedVariations = array_filter($variations, function (array $variation) use ($element) {
                return (int) $element['componentVariationId'] === (int) $variation['id'];
            });

            if (empty($matchedVariations)) {
                continue;
            }

            $variation = array_shift($matchedVariations);

            $element['number'] = $variation['number'];
        }
    }

    /**
     * @param array $variation
     *
     * @return BundleProduct[]
     */
    private function getBundleProducts(array $variation)
    {
        $url = 'items/' . $variation['itemId'] . '/variations/' . $variation['id'] . '/variation_bundles';
        $elements = $this->client->request('GET', $url);

        $this->addProductNumberToResponse($elements);

        $result = [];
        foreach ($elements as $element) {
            $bundleProduct = new BundleProduct();
            $bundleProduct->setAmount((float) $element['componentQuantity']);
            $bundleProduct->setNumber($element['number']);

            $result[] = $bundleProduct;
        }

        return $result;
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Order;

use DateTimeImmutable;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Customer\Customer as CustomerApi;
use Psr\Log\LoggerInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\IdentityService\Struct\Identity;
use SystemConnector\TransferObject\Country\Country;
use SystemConnector\TransferObject\Currency\Currency;
use SystemConnector\TransferObject\CustomerGroup\CustomerGroup;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Order\Address\Address;
use SystemConnector\TransferObject\Order\Comment\Comment;
use SystemConnector\TransferObject\Order\Customer\Customer;
use SystemConnector\TransferObject\Order\Order;
use SystemConnector\TransferObject\Order\OrderItem\OrderItem;
use SystemConnector\TransferObject\Order\Package\Package;
use SystemConnector\TransferObject\OrderStatus\OrderStatus;
use SystemConnector\TransferObject\PaymentMethod\PaymentMethod;
use SystemConnector\TransferObject\PaymentStatus\PaymentStatus;
use SystemConnector\TransferObject\ShippingProfile\ShippingProfile;
use SystemConnector\TransferObject\Shop\Shop;

class OrderResponseParser implements OrderResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var CustomerApi
     */
    private $customerApi;

    /**
     * @var ClientInterface
     */
    private $client;

    public function __construct(
        IdentityServiceInterface $identityService,
        LoggerInterface $logger,
        CustomerApi $customerApi,
        ClientInterface $client
    ) {
        $this->identityService = $identityService;
        $this->logger = $logger;
        $this->customerApi = $customerApi;
        $this->client = $client;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $entry)
    {
        $identity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['id'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Order::TYPE,
        ]);

        if (!$identity) {
            return [];
        }

        $shopIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['plentyId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Shop::TYPE,
        ]);

        if (null === $shopIdentity) {
            $this->logger->notice('unknown shop');

            return [];
        }

        $isMappedShopIdentity = $this->identityService->isMappedIdentity(
            $shopIdentity->getObjectIdentifier(),
            $shopIdentity->getObjectType(),
            $shopIdentity->getAdapterName()
        );

        if (!$isMappedShopIdentity) {
            return [];
        }

        $orderNumber = $this->getOrdernumber($entry);
        if (null === $orderNumber) {
            return [];
        }

        $shippingProfileIdentity = $this->getShippingProfileIdentity($entry);
        if (null === $shippingProfileIdentity) {
            $this->logger->notice('no shipping profile found', ['entry' => $entry]);

            return [];
        }

        $currencyIdentity = $this->getCurrencyIdentity($entry);
        if (null === $currencyIdentity) {
            $this->logger->notice('no currency found', ['entry' => $entry]);

            return [];
        }

        $paymentMethodIdentity = $this->getPaymentMethodIdentity($entry);
        if (null === $paymentMethodIdentity) {
            $this->logger->notice('no payment method found', ['entry' => $entry]);

            return [];
        }

        $paymentStatusIdentity = $this->getPaymentStatusIdentity($entry);
        if (null === $paymentStatusIdentity) {
            $this->logger->notice('no payment status found', ['entry' => $entry]);

            return [];
        }

        $oderStatusIdentity = $this->getOrderStatusIdentity($entry);
        if (null === $oderStatusIdentity) {
            $this->logger->notice('no order status found', ['entry' => $entry]);

            return [];
        }

        $entry['customerData'] = $this->getCustomerData($entry);
        if (empty($entry['customerData'])) {
            $this->logger->notice('no customer found', ['entry' => $entry]);

            return [];
        }

        $entry['billingAddressData'] = $this->getBillingAddressData($entry);
        if (empty($entry['billingAddressData'])) {
            $this->logger->notice('no billing address found', ['entry' => $entry]);

            return [];
        }

        $entry['shippingAddressData'] = $this->getShippingAddressData($entry);
        if (empty($entry['shippingAddressData'])) {
            $this->logger->notice('no shipping address found', ['entry' => $entry]);

            return [];
        }

        $customer = $this->getCustomer($entry);
        if (null === $customer) {
            $this->logger->notice('no customer found', ['entry' => $entry]);

            return [];
        }

        $order = new Order();
        $order->setIdentifier($identity->getObjectIdentifier());
        $order->setOrderNumber($orderNumber);
        $order->setOrderTime($this->getOrderTime($entry));
        $order->setCustomer($customer);
        $order->setOrderItems($this->getOrderItems($entry));
        $order->setShopIdentifier($shopIdentity->getObjectIdentifier());
        $order->setCurrencyIdentifier($currencyIdentity->getObjectIdentifier());
        $order->setOrderStatusIdentifier($oderStatusIdentity->getObjectIdentifier());
        $order->setPaymentStatusIdentifier($paymentStatusIdentity->getObjectIdentifier());
        $order->setPaymentMethodIdentifier($paymentMethodIdentity->getObjectIdentifier());
        $order->setShippingProfileIdentifier($shippingProfileIdentity->getObjectIdentifier());
        $order->setComments($this->getComments($entry));
        $order->setPackages($this->getPackages($entry));
        $order->setAttributes([]);

        $billingAddress = $this->getBillingAddress($entry);
        if (null !== $billingAddress) {
            $order->setBillingAddress($billingAddress);
        }

        $shippingAddress = $this->getShippingAddress($entry);
        if (null !== $shippingAddress) {
            $order->setShippingAddress($shippingAddress);
        }

        return [$order];
    }

    /**
     * @param array $entry
     *
     * @return array
     */
    private function getBillingAddressData(array $entry)
    {
        $billingAddress = array_filter($entry['addresses'], function (array $address) {
            return $address['pivot']['typeId'] === 1;
        });

        if (empty($billingAddress)) {
            return [];
        }

        return array_shift($billingAddress);
    }

    /**
     * @param array $entry
     *
     * @return array
     */
    private function getShippingAddressData(array $entry)
    {
        $shippingAddress = array_filter($entry['addresses'], function (array $address) {
            return $address['pivot']['typeId'] === 2;
        });

        if (empty($shippingAddress)) {
            return [];
        }

        return array_shift($shippingAddress);
    }

    /**
     * @param array $entry
     *
     * @return array
     */
    private function getCustomerData(array $entry)
    {
        $relations = array_filter($entry['relations'], function (array $relation) {
            return $relation['referenceType'] === 'contact';
        });

        if (empty($relations)) {
            return [];
        }

        $relation = array_shift($relations);

        return $this->customerApi->find($relation['referenceId']);
    }

    /**
     * @param array $entry
     *
     * @return null|string
     */
    private function getOrdernumber(array $entry)
    {
        $property = array_filter($entry['properties'], function (array $property) {
            return $property['typeId'] === 7;
        });

        if (!empty($property)) {
            $property = array_shift($property);

            return $property['value'];
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getLanguageIdentity(array $entry)
    {
        $property = array_filter($entry['properties'], function (array $property) {
            return $property['typeId'] === 6;
        });

        if (!empty($property)) {
            $property = array_shift($property);

            return $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $property['value'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return Comment[]
     */
    private function getComments(array $entry)
    {
        $result = [];
        foreach ($entry['comments'] as $data) {
            $comment = new Comment();
            $comment->setComment((string) $data['text']);
            $comment->setType($data['text'] ? Comment::TYPE_CUSTOMER : Comment::TYPE_INTERNAL);

            $result[] = $comment;
        }

        return $result;
    }

    /**
     * @param array $entry
     *
     * @return null|Customer
     */
    private function getCustomer(array $entry)
    {
        $languageIdentity = $this->getLanguageIdentity($entry);

        if (null === $languageIdentity) {
            $this->logger->info('language of customer not found', [
                'entry' => $entry,
            ]);

            return null;
        }

        $cutomerGroupIdentity = $this->getCustomerGroupIdentity($entry['customerData']);

        if (null === $cutomerGroupIdentity) {
            $this->logger->info('customer group not found', [
                'entry' => $entry,
            ]);

            return null;
        }

        $shopIdentity = $this->getShopIdentity($entry['customerData']);

        if (null === $shopIdentity) {
            $this->logger->info('customer shop not found', [
                'entry' => $entry,
            ]);

            return null;
        }

        $customer = new Customer();
        $customer->setType(Customer::TYPE_NORMAL);
        $customer->setNumber($entry['customerData']['number']);
        $customer->setEmail($this->getMail($entry));
        $customer->setLanguageIdentifier($languageIdentity->getObjectIdentifier());
        $customer->setCustomerGroupIdentifier($cutomerGroupIdentity->getObjectIdentifier());
        $customer->setGender($entry['customerData']['gender'] === 'male' ? Customer::GENDER_MALE : Customer::GENDER_FEMALE);
        $customer->setFirstname($entry['customerData']['firstName']);
        $customer->setLastname($entry['customerData']['lastName']);
        $customer->setPhoneNumber($this->getPhoneNumber($entry));
        $customer->setMobilePhoneNumber($this->getMobilePhoneNumber($entry));
        $customer->setShopIdentifier($shopIdentity->getObjectIdentifier());

        return $customer;
    }

    /**
     * @param array $plentyCustomer
     *
     * @return Identity
     */
    private function getShopIdentity(array $plentyCustomer)
    {
        return $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $plentyCustomer['plentyId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Shop::TYPE,
        ]);
    }

    /**
     * @param array $plentyCustomer
     *
     * @return Identity
     */
    private function getCustomerGroupIdentity(array $plentyCustomer)
    {
        return $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $plentyCustomer['classId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => CustomerGroup::TYPE,
        ]);
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getShippingProfileIdentity(array $entry)
    {
        foreach ($entry['properties'] as $orderProperty) {
            if ($orderProperty['typeId'] !== 2) {
                continue;
            }

            if (!empty($orderProperty['value'])) {
                return $this->identityService->findOneBy([
                    'adapterIdentifier' => (string) $orderProperty['value'],
                    'adapterName' => PlentymarketsAdapter::NAME,
                    'objectType' => ShippingProfile::TYPE,
                ]);
            }
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getCurrencyIdentity(array $entry)
    {
        if (empty($entry['amounts'])) {
            return null;
        }

        $amount = array_shift($entry['amounts']);

        return $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $amount['currency'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Currency::TYPE,
        ]);
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getPaymentMethodIdentity(array $entry)
    {
        $property = array_filter($entry['properties'], function (array $property) {
            return $property['typeId'] === 3;
        });

        if (!empty($property)) {
            $property = array_shift($property);

            return $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $property['value'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => PaymentMethod::TYPE,
            ]);
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getPaymentStatusIdentity(array $entry)
    {
        $property = array_filter($entry['properties'], function (array $property) {
            return $property['typeId'] === 4;
        });

        if (!empty($property)) {
            //$property = array_shift($property);

            return $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $entry['statusId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => PaymentStatus::TYPE,
            ]);
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|Identity
     */
    private function getOrderStatusIdentity(array $entry)
    {
        return $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['statusId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => OrderStatus::TYPE,
        ]);
    }

    /**
     * @param array $entry
     *
     * @return DateTimeImmutable
     */
    private function getOrderTime(array $entry)
    {
        $date = array_filter($entry['dates'], function (array $property) {
            return $property['typeId'] === 2;
        });

        if (!empty($date)) {
            $date = array_shift($date);

            return DateTimeImmutable::createFromFormat(DATE_W3C, $date['date']);
        }

        return new DateTimeImmutable();
    }

    /**
     * @param array $entry
     *
     * @return null|Address
     */
    private function getBillingAddress(array $entry)
    {
        if (empty($entry['billingAddressData'])) {
            return null;
        }

        $countryIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['billingAddressData']['countryId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Country::TYPE,
        ]);

        if (null === $countryIdentity) {
            $this->logger->error('country not found');

            return null;
        }

        $street = $entry['billingAddressData']['address1'] . ' ' . $entry['billingAddressData']['address2'] . ' ' . $entry['billingAddressData']['address3'];

        $address = new Address();
        $address->setGender(Customer::GENDER_MALE);
        $address->setFirstname($entry['billingAddressData']['name2']);
        $address->setLastname($entry['billingAddressData']['name3']);
        $address->setStreet(trim($street));
        $address->setPostalCode($entry['billingAddressData']['postalCode']);
        $address->setCity($entry['billingAddressData']['town']);
        $address->setCountryIdentifier($countryIdentity->getObjectIdentifier());
        $address->setPhoneNumber($this->getPhoneNumber($entry));
        $address->setMobilePhoneNumber($this->getMobilePhoneNumber($entry));

        return $address;
    }

    /**
     * @param array $entry
     *
     * @return null|Address
     */
    private function getShippingAddress(array $entry)
    {
        if (empty($entry['shippingAddressData'])) {
            return null;
        }

        $countryIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['shippingAddressData']['countryId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => Country::TYPE,
        ]);

        if (null === $countryIdentity) {
            $this->logger->error('country not found');

            return null;
        }

        $street = $entry['shippingAddressData']['address1'] . ' ' . $entry['shippingAddressData']['address2'] . ' ' . $entry['shippingAddressData']['address3'];

        $address = new Address();
        $address->setGender(Customer::GENDER_MALE);
        $address->setFirstname($entry['shippingAddressData']['name2']);
        $address->setLastname($entry['shippingAddressData']['name3']);
        $address->setStreet(trim($street));
        $address->setPostalCode($entry['shippingAddressData']['postalCode']);
        $address->setCity($entry['shippingAddressData']['town']);
        $address->setCountryIdentifier($countryIdentity->getObjectIdentifier());
        $address->setPhoneNumber($this->getPhoneNumber($entry));
        $address->setMobilePhoneNumber($this->getMobilePhoneNumber($entry));

        return $address;
    }

    /**
     * @param array $entry
     *
     * @return null|string
     */
    private function getPhoneNumber(array $entry)
    {
        $options = array_filter($entry['customerData']['options'], function (array $option) {
            return $option['typeId'] === 1 && $option['subTypeId'] === 4;
        });

        if (!empty($options)) {
            $option = array_shift($options);

            return $option['value'];
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|string
     */
    private function getMobilePhoneNumber(array $entry)
    {
        $options = array_filter($entry['customerData']['options'], function (array $option) {
            return $option['typeId'] === 1 && $option['subTypeId'] === 2;
        });

        if (!empty($options)) {
            $option = array_shift($options);

            return $option['value'];
        }

        return null;
    }

    /**
     * @param array $entry
     *
     * @return null|string
     */
    private function getMail(array $entry)
    {
        if (empty($entry['billingAddressData']['options'])) {
            return null;
        }

        $options = array_filter($entry['billingAddressData']['options'], function (array $option) {
            return $option['typeId'] === 5;
        });

        if (empty($options)) {
            return null;
        }

        $option = array_shift($options);

        return $option['value'];
    }

    /**
     * @param array $entry
     *
     * @return Package[]
     */
    private function getPackages(array $entry)
    {
        $numbers = $this->client->request('GET', 'orders/' . $entry['id'] . '/packagenumbers');

        $shippingDate = array_filter($entry['dates'], function (array $date) {
            return $date['typeId'] === 8;
        });

        $result = [];
        foreach ($numbers as $number) {
            $package = new Package();
            $package->setShippingCode((string) $number);
            $package->setShippingProvider();

            if (!empty($shippingDate)) {
                $shippingDate = array_shift($shippingDate);

                $package->setShippingTime(DateTimeImmutable::createFromFormat(
                    DATE_ATOM,
                    $shippingDate['date']
                ));
            }

            $result[] = $package;
        }

        return $result;
    }

    /**
     * @param array $entry
     *
     * @return OrderItem[]
     */
    private function getOrderItems(array $entry)
    {
        $result = [];

        foreach ($entry['orderItems'] as $item) {
            $number = $this->getNumberFromVariation($item['itemVariationId']);

            $orderItem = new OrderItem();
            $orderItem->setQuantity((float) $item['quantity']);
            $orderItem->setName($item['orderItemName']);
            $orderItem->setNumber($number);
            $orderItem->setPrice($this->getOrderItemPrice($item));
            $orderItem->setAttributes([]);

            $result[] = $orderItem;
        }

        return $result;
    }

    /**
     * @param array $item
     *
     * @return float
     */
    private function getOrderItemPrice(array $item)
    {
        $price = 0.0;

        foreach ($item['amounts'] as $amount) {
            $price += $amount['priceOriginalGross'];
        }

        return $price;
    }

    /**
     * @param int $variationId
     *
     * @return string
     */
    private function getNumberFromVariation($variationId)
    {
        static $variations;

        if (!isset($variations[$variationId])) {
            $response = $this->client->request('GET', 'items/variations', ['id' => $variationId]);

            if (empty($response)) {
                $variations[$variationId] = null;

                return $variations[$variationId];
            }

            $variation = array_shift($response);

            $variations[$variationId] = $variation['number'];
        }

        return $variations[$variationId];
    }
}


<?php

namespace PlentymarketsAdapter\ReadApi\Category;

use DateTimeImmutable;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\Helper\LanguageHelperInterface;
use PlentymarketsAdapter\ReadApi\ApiAbstract;

class Category extends ApiAbstract
{
    /**
     * @var LanguageHelperInterface
     */
    private $languageHelper;

    public function __construct(
        ClientInterface $client,
        LanguageHelperInterface $languageHelper
    ) {
        parent::__construct($client);

        $this->languageHelper = $languageHelper;
    }

    /**
     * @param int $categoryId
     *
     * @return array
     */
    public function findOne($categoryId)
    {
        return $this->client->request('GET', 'categories/' . $categoryId, [
            'with' => 'details,clients',
            'type' => 'item',
            'lang' => $this->languageHelper->getLanguagesQueryString(),
        ]);
    }

    /**
     * @return array
     */
    public function findAll()
    {
        $elements = iterator_to_array($this->client->getIterator('categories', [
            'with' => 'details,clients',
            'type' => 'item',
            'lang' => $this->languageHelper->getLanguagesQueryString(),
        ]));

        $this->sortCategories($elements);

        return $elements;
    }

    /**
     * @param DateTimeImmutable $startTimestamp
     *
     * @return array
     */
    public function findChanged(DateTimeImmutable $startTimestamp)
    {
        $elements = iterator_to_array($this->client->getIterator('categories', [
            'with' => 'details,clients',
            'type' => 'item',
            'updatedAt' => $startTimestamp->format(DATE_W3C),
            'lang' => $this->languageHelper->getLanguagesQueryString(),
        ]));

        $this->sortCategories($elements);

        return $elements;
    }

    /**
     * @param array $categories
     */
    private function sortCategories(array &$categories)
    {
        usort($categories, function ($a, $b) {
            if (!isset($a['level'], $b['level'])) {
                return 0;
            }

            if ((int) $a['level'] === (int) $b['level']) {
                return 0;
            }

            if ((int) $a['level'] < (int) $b['level']) {
                return -1;
            }

            return 1;
        });
    }
}


<?php

namespace PlentymarketsAdapter\Client\Iterator;

use Assert\Assertion;
use Closure;
use Countable;
use Iterator as BaseIterator;
use PlentymarketsAdapter\Client\Client;

class Iterator implements BaseIterator, Countable
{
    /**
     * @var Client
     */
    private $client;

    /**
     * @var int
     */
    private $index = 0;

    /**
     * @var int
     */
    private $limit = 50;

    /**
     * @var int
     */
    private $offset = 0;

    /**
     * @var array
     */
    private $data = [];

    /**
     * @var array
     */
    private $criteria;

    /**
     * @var string
     */
    private $path;

    /**
     * @var Closure
     */
    private $prepareFunction;

    /**
     * @var bool
     */
    private $isLastPage = false;

    /**
     * @param string       $path
     * @param Client       $client
     * @param array        $criteria
     * @param null|Closure $prepareFunction
     */
    public function __construct($path, Client $client, array $criteria = [], Closure $prepareFunction = null)
    {
        Assertion::string($path);

        $this->client = $client;
        $this->criteria = $criteria;
        $this->path = $path;
        $this->prepareFunction = $prepareFunction;
    }

    /**
     * {@inheritdoc}
     */
    public function current()
    {
        $element = $this->data[$this->index];

        unset($this->data[$this->index]);

        return $element;
    }

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        ++$this->index;

        if (!$this->isLastPage && !$this->valid()) {
            $this->offset += $this->limit;

            $this->loadPage($this->criteria, $this->limit, $this->offset);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function valid()
    {
        return array_key_exists($this->index, $this->data);
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return $this->index;
    }

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        $this->loadPage($this->criteria, $this->limit);

        $this->offset = 0;
        $this->index = 0;
    }

    /**
     * {@inheritdoc}
     */
    public function count()
    {
        return $this->client->getTotal($this->path, $this->criteria);
    }

    /**
     * @param array $criteria
     * @param int   $limit
     * @param int   $offset
     */
    private function loadPage(array $criteria = [], $limit = 0, $offset = 0)
    {
        $result = $this->client->request('GET', $this->path, $criteria, $limit, $offset);

        if (null !== $this->prepareFunction) {
            $result = call_user_func($this->prepareFunction, $result);
        }

        if (count($result) !== $this->limit) {
            $this->isLastPage = true;
        }

        foreach ($result as $key => $item) {
            $this->data[$this->index + $key] = $item;
        }
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\MediaCategory;

use Exception;
use PlentymarketsAdapter\Helper\MediaCategoryHelperInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\MediaCategory\MediaCategoryResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\Console\OutputHandler\OutputHandlerInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\MediaCategory\MediaCategory;

class FetchAllMediaCategoriesQueryHandler implements QueryHandlerInterface
{
    /**
     * @var MediaCategoryHelperInterface
     */
    private $mediaCategoryHelper;

    /**
     * @var MediaCategoryResponseParserInterface
     */
    private $responseParser;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var OutputHandlerInterface
     */
    private $outputHandler;

    public function __construct(
        MediaCategoryHelperInterface $mediaCategoryHelper,
        MediaCategoryResponseParserInterface $responseParser,
        LoggerInterface $logger,
        OutputHandlerInterface $outputHandler
    ) {
        $this->mediaCategoryHelper = $mediaCategoryHelper;
        $this->responseParser = $responseParser;
        $this->logger = $logger;
        $this->outputHandler = $outputHandler;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === MediaCategory::TYPE &&
            $query->getQueryType() === QueryType::ALL;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $elements = $this->mediaCategoryHelper->getCategories();

        $this->outputHandler->startProgressBar(count($elements));

        foreach ($elements as $element) {
            try {
                $result = $this->responseParser->parse($element);
            } catch (Exception $exception) {
                $this->logger->error($exception->getMessage());

                $result = null;
            }

            if (null !== $result) {
                yield $result;
            }

            $this->outputHandler->advanceProgressBar();
        }

        $this->outputHandler->finishProgressBar();
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\ShippingProfile;

use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\ShippingProfile\ShippingProfileResponseParserInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\ShippingProfile\ShippingProfile;

class FetchAllShippingProfilesQueryHandler implements QueryHandlerInterface
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var ShippingProfileResponseParserInterface
     */
    private $responseParser;

    public function __construct(
        ClientInterface $client,
        ShippingProfileResponseParserInterface $responseParser
    ) {
        $this->client = $client;
        $this->responseParser = $responseParser;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === ShippingProfile::TYPE &&
            $query->getQueryType() === QueryType::ALL;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $elements = $this->client->request('GET', 'orders/shipping/presets');

        foreach ($elements as $element) {
            $result = $this->responseParser->parse($element);

            if (null === $result) {
                continue;
            }

            yield $result;
        }
    }
}


<?php

namespace ShopwareAdapter\ServiceBus\CommandHandler\Variation;

use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Shopware\Components\Api\Manager;
use Shopware\Components\Api\Resource\Variant;
use Shopware\Models\Article\Detail;
use ShopwareAdapter\DataPersister\Attribute\AttributeDataPersisterInterface;
use ShopwareAdapter\RequestGenerator\Product\Variation\VariationRequestGeneratorInterface;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\IdentityService\Struct\Identity;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\TransferObject\Product\Variation\Variation;

class HandleVariationCommandHandler implements CommandHandlerInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var VariationRequestGeneratorInterface
     */
    private $variationRequestGenerator;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var AttributeDataPersisterInterface
     */
    private $attributeDataPersister;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        IdentityServiceInterface $identityService,
        VariationRequestGeneratorInterface $variationRequestGenerator,
        EntityManagerInterface $entityManager,
        AttributeDataPersisterInterface $attributeDataPersister,
        LoggerInterface $logger
    ) {
        $this->identityService = $identityService;
        $this->variationRequestGenerator = $variationRequestGenerator;
        $this->entityManager = $entityManager;
        $this->attributeDataPersister = $attributeDataPersister;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(CommandInterface $command)
    {
        return $command instanceof TransferObjectCommand &&
            $command->getAdapterName() === ShopwareAdapter::NAME &&
            $command->getObjectType() === Variation::TYPE &&
            $command->getCommandType() === CommandType::HANDLE;
    }

    /**
     * {@inheritdoc}
     *
     * @param TransferObjectCommand $command
     */
    public function handle(CommandInterface $command)
    {
        /**
         * @var Variation $variation
         */
        $variation = $command->getPayload();

        $productIdentitiy = $this->identityService->findOneBy([
            'objectIdentifier' => $variation->getProductIdentifier(),
            'objectType' => Product::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        if (null === $productIdentitiy) {
            return false;
        }

        $variationIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $variation->getIdentifier(),
            'objectType' => Variation::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        $variationResource = $this->getVariationResource();
        $variationParams = $this->variationRequestGenerator->generate($variation);
        $variationRepository = $this->entityManager->getRepository(Detail::class);

        if (null === $variationIdentity) {
            /**
             * @var null|Detail $variationModel
             */
            $variationModel = $variationRepository->findOneBy(['number' => $variation->getNumber()]);

            if (null === $variationModel) {
                $variationModel = $variationResource->create($variationParams);
            } else {
                $variationModel = $variationResource->update(
                    $variationModel->getId(),
                    $variationParams
                );
            }

            $this->identityService->insert(
                $variation->getIdentifier(),
                Variation::TYPE,
                (string) $variationModel->getId(),
                ShopwareAdapter::NAME
            );
        } else {
            /**
             * @var null|Detail $variationModel
             */
            $variationModel = $variationRepository->find($variationIdentity->getAdapterIdentifier());

            if (null === $variationModel) {
                $variationModel = $variationRepository->findOneBy(['number' => $variation->getNumber()]);
            }

            if (null === $variationModel) {
                $variationModel = $variationResource->create($variationParams);
            } else {
                $variationModel = $variationResource->update(
                    $variationModel->getId(),
                    $variationParams
                );
            }

            $this->identityService->update(
                $variationIdentity,
                [
                    'adapterIdentifier' => (string) $variationModel->getId(),
                ]
            );
        }

        $this->correctProductAssignment($variationModel, $productIdentitiy);
        $this->correctMainDetailAssignment($variationModel, $variation);

        $this->attributeDataPersister->saveProductDetailAttributes(
            $variationModel,
            $variation->getAttributes()
        );

        return true;
    }

    /**
     * @param Detail    $variationModel
     * @param Variation $variation
     */
    private function correctMainDetailAssignment(Detail $variationModel, Variation $variation)
    {
        if (!$variation->isMain()) {
            return;
        }

        $this->entityManager->getConnection()->update(
            's_articles',
            [
                'main_detail_id' => $variationModel->getId(),
                'active' => $variation->getActive(),
                'changetime' => (new DateTime('now'))->format('Y-m-d H:i:s'),
            ],
            ['id' => $variationModel->getArticle()->getId()]
        );
    }

    /**
     * @return Variant
     */
    private function getVariationResource()
    {
        // without this reset the entitymanager sometimes the album is not found correctly.
        Shopware()->Container()->reset('models');

        return Manager::getResource('Variant');
    }

    /**
     * migrating variation from one product to the correct connector handeled product
     *
     * @param null|Detail $variationModel
     * @param Identity    $productIdentitiy
     */
    private function correctProductAssignment($variationModel, $productIdentitiy)
    {
        if (null === $variationModel) {
            return;
        }

        if ((int) $productIdentitiy->getAdapterIdentifier() === $variationModel->getArticle()->getId()) {
            return;
        }

        $this->entityManager->getConnection()->update(
            's_articles_details',
            ['articleID' => $productIdentitiy->getAdapterIdentifier()],
            ['id' => $variationModel->getId()]
        );

        $this->logger->notice('migrated variation from existing product to connector handeled product.', [
            'variation' => $variationModel->getNumber(),
            'old shopware product id' => $variationModel->getArticle()->getId(),
            'new shopware product id' => $productIdentitiy->getAdapterIdentifier(),
        ]);
    }
}


<?php

namespace SystemConnector\BacklogService\Middleware;

use League\Tactician\Middleware;
use Psr\Log\LoggerInterface;
use SystemConnector\BacklogService\BacklogServiceInterface;
use SystemConnector\BacklogService\Command\HandleBacklogElementCommand;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\TransferObject\TransferObjectInterface;

class BacklogCommandHandlerMiddleware implements Middleware
{
    /**
     * flag to enable or disable the whole backlog functionality
     *
     * @var bool
     */
    public static $active = true;

    /**
     * @var BacklogServiceInterface
     */
    private $backlogService;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(BacklogServiceInterface $backlogService, LoggerInterface $logger)
    {
        $this->backlogService = $backlogService;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function execute($command, callable $next)
    {
        if (!self::$active) {
            return $next($command);
        }

        if ($command instanceof HandleBacklogElementCommand) {
            $command = $command->getPayload();

            return $next($command);
        }

        if ($command instanceof CommandInterface) {
            $this->backlogService->enqueue($command);

            $this->logCommandEnqueued($command);

            return true;
        }

        return $next($command);
    }

    /**
     * @param CommandInterface $command
     */
    private function logCommandEnqueued(CommandInterface $command)
    {
        $context = [];

        if ($command instanceof TransferObjectCommand) {
            $context['adapterName'] = $command->getAdapterName();
            $context['objectType'] = $command->getObjectType();
            $context['commandType'] = $command->getCommandType();

            if ($command->getPayload() instanceof TransferObjectInterface) {
                $context['transferObject'] = $command->getPayload()->getIdentifier();
            } else {
                $context['transferObject'] = $command->getPayload();
            }
        }

        $this->logger->debug('Command enqueued', $context);
    }
}


<?php

namespace SystemConnector\TransferObject\Payment;

use SystemConnector\TransferObject\AbstractTransferObject;
use SystemConnector\TransferObject\AttributableInterface;
use SystemConnector\TransferObject\Payment\PaymentData\PaymentDataInterface;
use SystemConnector\ValueObject\Attribute\Attribute;

class Payment extends AbstractTransferObject implements AttributableInterface
{
    const TYPE = 'Payment';

    /**
     * @var string
     */
    private $identifier;

    /**
     * @var string
     */
    private $orderIdentifer;

    /**
     * @var float
     */
    private $amount = 0.0;

    /**
     * @var string
     */
    private $shopIdentifier = '';

    /**
     * @var string
     */
    private $currencyIdentifier = '';

    /**
     * @var string
     */
    private $paymentMethodIdentifier = '';

    /**
     * @var string
     */
    private $transactionReference = '';

    /**
     * @var string
     */
    private $accountHolder = '';

    /**
     * @var null|PaymentDataInterface
     */
    private $paymentData;

    /**
     * @var Attribute[]
     */
    private $attributes = [];

    /**
     * return the unique type of the object.
     *
     * @return string
     */
    public function getType()
    {
        return self::TYPE;
    }

    /**
     * {@inheritdoc}
     */
    public function getIdentifier()
    {
        return $this->identifier;
    }

    /**
     * {@inheritdoc}
     */
    public function setIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * @return string
     */
    public function getOrderIdentifer()
    {
        return $this->orderIdentifer;
    }

    /**
     * @param string $orderIdentifer
     */
    public function setOrderIdentifer($orderIdentifer)
    {
        $this->orderIdentifer = $orderIdentifer;
    }

    /**
     * @return float
     */
    public function getAmount()
    {
        return $this->amount;
    }

    /**
     * @param float $amount
     */
    public function setAmount($amount)
    {
        $this->amount = $amount;
    }

    /**
     * @return string
     */
    public function getShopIdentifier()
    {
        return $this->shopIdentifier;
    }

    /**
     * @param string $shopIdentifier
     */
    public function setShopIdentifier($shopIdentifier)
    {
        $this->shopIdentifier = $shopIdentifier;
    }

    /**
     * @return string
     */
    public function getCurrencyIdentifier()
    {
        return $this->currencyIdentifier;
    }

    /**
     * @param string $currencyIdentifier
     */
    public function setCurrencyIdentifier($currencyIdentifier)
    {
        $this->currencyIdentifier = $currencyIdentifier;
    }

    /**
     * @return string
     */
    public function getPaymentMethodIdentifier()
    {
        return $this->paymentMethodIdentifier;
    }

    /**
     * @param string $paymentMethodIdentifier
     */
    public function setPaymentMethodIdentifier($paymentMethodIdentifier)
    {
        $this->paymentMethodIdentifier = $paymentMethodIdentifier;
    }

    /**
     * @return string
     */
    public function getTransactionReference()
    {
        return $this->transactionReference;
    }

    /**
     * @param string $transactionReference
     */
    public function setTransactionReference($transactionReference)
    {
        $this->transactionReference = $transactionReference;
    }

    /**
     * @return string
     */
    public function getAccountHolder()
    {
        return $this->accountHolder;
    }

    /**
     * @param string $accountHolder
     */
    public function setAccountHolder($accountHolder = '')
    {
        $this->accountHolder = $accountHolder;
    }

    /**
     * @return null|PaymentDataInterface
     */
    public function getPaymentData()
    {
        return $this->paymentData;
    }

    /**
     * @param null|PaymentDataInterface $paymentData
     */
    public function setPaymentData(PaymentDataInterface $paymentData = null)
    {
        $this->paymentData = $paymentData;
    }

    /**
     * @return Attribute[]
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * @param Attribute[] $attributes
     */
    public function setAttributes(array $attributes)
    {
        $this->attributes = $attributes;
    }

    /**
     * {@inheritdoc}
     */
    public function getClassProperties()
    {
        return [
            'identifier' => $this->getIdentifier(),
            'orderIdentifier' => $this->getOrderIdentifer(),
            'amount' => $this->getAmount(),
            'shopIdentifier' => $this->getShopIdentifier(),
            'currencyIdentifier' => $this->getCurrencyIdentifier(),
            'paymentMethodIdentifier' => $this->getPaymentMethodIdentifier(),
            'transactionReference' => $this->getTransactionReference(),
            'accountHolder' => $this->getAccountHolder(),
            'paymentData' => $this->getPaymentData(),
            'attributes' => $this->getAttributes(),
        ];
    }
}


<?php

namespace ShopwareAdapter\ResponseParser;

use DateTimeInterface;
use SystemConnector\ValueObject\Attribute\Attribute;

trait GetAttributeTrait
{
    /**
     * @param array $attributeData
     *
     * @return Attribute[]
     */
    private function getAttributes(array $attributeData)
    {
        $attributes = [];

        foreach ($attributeData as $key => $value) {
            if (empty($value)) {
                continue;
            }

            if ($value instanceof DateTimeInterface) {
                $strValue = $value->format('Y-m-d H:i:s');
            } else {
                $strValue = (string) $value;
            }

            $attribute = new Attribute();
            $attribute->setKey((string) $key);
            $attribute->setValue($strValue);

            $attributes[] = $attribute;
        }

        return $attributes;
    }
}


<?php

namespace PlentyConnector\Components\Klarna\Shopware;

use Doctrine\DBAL\Connection;
use Exception;
use PlentyConnector\Components\Klarna\PaymentData\KlarnaPaymentData;
use Shopware\Components\Plugin\CachedConfigReader;
use ShopwareAdapter\ResponseParser\Payment\PaymentResponseParserInterface;
use SystemConnector\TransferObject\Payment\Payment;

class KlarnaPaymentResponseParser implements PaymentResponseParserInterface
{
    /**
     * ISO3166_AT
     *
     * @var int
     */
    const AT = 15;

    /**
     * ISO3166_DK
     *
     * @var int
     */
    const DK = 59;

    /**
     * ISO3166_FI
     *
     * @var int
     */
    const FI = 73;

    /**
     * ISO3166_DE
     *
     * @var int
     */
    const DE = 81;

    /**
     * ISO3166_NL
     *
     * @var int
     */
    const NL = 154;

    /**
     * ISO3166_NO
     *
     * @var int
     */
    const NO = 164;

    /**
     * ISO3166_SE
     *
     * @var int
     */
    const SE = 209;

    /**
     * @var PaymentResponseParserInterface
     */
    private $parentResponseParser;

    /**
     * @var Connection
     */
    private $connection;

    /**
     * @var CachedConfigReader
     */
    private $configReader;

    public function __construct(
        PaymentResponseParserInterface $parentResponseParser,
        Connection $connection,
        CachedConfigReader $configReader
    ) {
        $this->parentResponseParser = $parentResponseParser;
        $this->connection = $connection;
        $this->configReader = $configReader;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $element)
    {
        $payments = $this->parentResponseParser->parse($element);

        foreach ($payments as $payment) {
            if (!($payment instanceof Payment)) {
                continue;
            }

            $this->addKlarnaPaymentData($payment, $element);
        }

        return $payments;
    }

    /**
     * @param Payment $payment
     * @param array   $element
     */
    private function addKlarnaPaymentData(Payment $payment, array $element)
    {
        if ('payment_klarna_kpm' !== $element['payment']['action']) {
            return;
        }

        $klarnaConfig = $this->configReader->getByPluginName('SwagPaymentKlarnaKpm');
        $klarnaShopId = $klarnaConfig['merchantId'];

        $paymentData = new KlarnaPaymentData();
        $paymentData->setPclassId('-1');

        if ('klarna_account' === $element['payment']['name']) {
            $paymentData->setPclassId($this->getKlarnaPclassId($klarnaShopId, $element['billing']['country']['iso']));
        }

        $paymentData->setShopId($klarnaShopId);
        $paymentData->setTransactionId($element['transactionId']);

        $payment->setPaymentData($paymentData);
    }

    /**
     * @param string $klarnaShopId
     * @param string $countryIso
     *
     * @return string
     */
    private function getKlarnaPclassId($klarnaShopId, $countryIso)
    {
        try {
            $query = 'SELECT id FROM s_klarna_pclasses
                      WHERE eid = :klarnaShopId
                      AND country = :country';

            return $this->connection->fetchColumn($query, [
                'klarnaShopId' => $klarnaShopId,
                'country' => $this->getKlarnaCountryId($countryIso), ]
            );
        } catch (Exception $exception) {
            return '';
        }
    }

    /**
     * @param string $country
     *
     * @return int
     */
    private function getKlarnaCountryId($country)
    {
        switch ($country) {
            case 'DE':
                return self::DE;
            case 'AT':
                return self::AT;
            case 'DK':
                return self::DK;
            case 'FI':
                return self::FI;
            case 'NL':
                return self::NL;
            case 'NO':
                return self::NO;
            case 'SE':
                return self::SE;
            default:
                return 0;
        }
    }
}


<?php

namespace SystemConnector\ServiceBus\CommandHandlerMiddleware;

use League\Tactician\Middleware;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandHandlerMiddleware\Exception\NotFoundException;
use Traversable;

class CommandHandlerMiddleware implements Middleware
{
    /**
     * @var CommandHandlerInterface[]|Traversable
     */
    private $handlers;

    /**
     * @param CommandHandlerInterface[]|Traversable $handlers
     */
    public function __construct(Traversable $handlers)
    {
        $this->handlers = iterator_to_array($handlers);
    }

    /**
     * {@inheritdoc}
     */
    public function execute($command, callable $next)
    {
        if (null === $this->handlers) {
            return $next($command);
        }

        if (!($command instanceof CommandInterface)) {
            return $next($command);
        }

        $handlers = array_filter($this->handlers, function (CommandHandlerInterface $handler) use ($command) {
            return $handler->supports($command);
        });

        if (0 === count($handlers)) {
            throw NotFoundException::fromCommand($command);
        }

        foreach ($handlers as $handler) {
            $handler->handle($command);
        }

        return $next($command);
    }
}


<?php

namespace ShopwareAdapter\DataProvider\Order;

use Doctrine\DBAL\Connection;
use Shopware\Components\Api\Resource\Order as OrderResource;
use Shopware\Models\Order\Status;

class OrderDataProvider implements OrderDataProviderInterface
{
    /**
     * @var Connection
     */
    private $connection;

    /**
     * @var OrderResource
     */
    private $orderResource;

    public function __construct(Connection $connection, OrderResource $orderResource)
    {
        $this->connection = $connection;
        $this->orderResource = $orderResource;
    }

    /**
     * {@inheritdoc}
     */
    public function getOpenOrders()
    {
        $filter = [
            [
                'property' => 'status',
                'expression' => '=',
                'value' => Status::ORDER_STATE_OPEN,
            ],
        ];

        $orders = $this->orderResource->getList(0, null, $filter);

        return $orders['data'];
    }

    /**
     * {@inheritdoc}
     */
    public function getOrderDetails($identifier)
    {
        $order = $this->orderResource->getOne($identifier);

        $order['shopId'] = $this->getCorrectSubShopIdentifier($identifier);

        return $this->removeOrphanedShopArray($order);
    }

    /**
     * @param int $orderIdentifier
     *
     * @return int
     */
    private function getCorrectSubShopIdentifier($orderIdentifier)
    {
        return $this->connection->fetchColumn('SELECT language FROM s_order WHERE id = ?', [$orderIdentifier]);
    }

    /**
     * @param array $order
     *
     * @return array
     */
    private function removeOrphanedShopArray(array $order)
    {
        unset($order['shop']);

        return $order;
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Stock;

use Exception;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\Client\Iterator\Iterator;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Product\Stock\StockResponseParserInterface;
use PlentymarketsAdapter\ServiceBus\ChangedDateTimeTrait;
use Psr\Log\LoggerInterface;
use SystemConnector\Console\OutputHandler\OutputHandlerInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Product\Stock\Stock;

class FetchChangedStocksQueryHandler implements QueryHandlerInterface
{
    use ChangedDateTimeTrait;

    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var StockResponseParserInterface
     */
    private $responseParser;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var OutputHandlerInterface
     */
    private $outputHandler;

    public function __construct(
        ClientInterface $client,
        StockResponseParserInterface $responseParser,
        LoggerInterface $logger,
        OutputHandlerInterface $outputHandler
    ) {
        $this->client = $client;
        $this->responseParser = $responseParser;
        $this->logger = $logger;
        $this->outputHandler = $outputHandler;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === Stock::TYPE &&
            $query->getQueryType() === QueryType::CHANGED;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $lastCangedTime = $this->getChangedDateTime();
        $currentDateTime = $this->getCurrentDateTime();

        $stocks = $this->client->getIterator('stockmanagement/stock', [
            'updatedAtFrom' => $lastCangedTime->format(DATE_W3C),
            'updatedAtTo' => $currentDateTime->format(DATE_W3C),
            'columns' => ['variationId'],
        ]);

        $this->outputHandler->startProgressBar(count($stocks));

        foreach ($this->getAffectedVariations($stocks) as $variationIdentifierGroup) {
            if (empty($variationIdentifierGroup)) {
                continue;
            }

            $elements = $this->client->getIterator('items/variations', [
                'with' => 'stock',
                'id' => implode(',', $variationIdentifierGroup),
            ]);

            foreach ($elements as $element) {
                $stock = null;

                try {
                    $stock = $this->responseParser->parse($element);
                } catch (Exception $exception) {
                    $this->logger->error($exception->getMessage());
                }

                if ($stock !== null) {
                    yield $stock;
                }

                $this->outputHandler->advanceProgressBar();
            }
        }

        $this->outputHandler->finishProgressBar();
        $this->setChangedDateTime($currentDateTime);
    }

    private function getAffectedVariations(Iterator $stocks)
    {
        $stockBacklog = [];

        foreach ($stocks as $stock) {
            if (isset($stockBacklog[$stock['variationId']])) {
                continue;
            }

            $stockBacklog[$stock['variationId']] = $stock['variationId'];

            if (count($stockBacklog) % 50 === 0) {
                yield $stockBacklog;

                $stockBacklog = [];
            }
        }

        yield $stockBacklog;
    }
}


<?php

namespace ShopwareAdapter\ServiceBus\CommandHandler\Variation;

use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Shopware\Models\Article\Detail;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\IdentityService\Struct\Identity;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\Product\Variation\Variation;

class RemoveVariationCommandHandler implements CommandHandlerInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        IdentityServiceInterface $identityService,
        EntityManagerInterface $entityManager,
        LoggerInterface $logger
    ) {
        $this->identityService = $identityService;
        $this->entityManager = $entityManager;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(CommandInterface $command)
    {
        return $command instanceof TransferObjectCommand &&
            $command->getAdapterName() === ShopwareAdapter::NAME &&
            $command->getObjectType() === Variation::TYPE &&
            $command->getCommandType() === CommandType::REMOVE;
    }

    /**
     * {@inheritdoc}
     *
     * @param TransferObjectCommand $command
     */
    public function handle(CommandInterface $command)
    {
        $identifier = $command->getPayload();

        $identity = $this->identityService->findOneBy([
            'objectIdentifier' => (string) $identifier,
            'objectType' => Variation::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        if (null === $identity) {
            $this->logger->notice('no matching identity found', ['command' => $command]);

            return false;
        }

        $repository = $this->entityManager->getRepository(Detail::class);
        $variation = $repository->find($identity->getAdapterIdentifier());

        if (null !== $variation) {
            $this->entityManager->remove($variation);
            $this->entityManager->flush();
        } else {
            $this->logger->notice('identity removed but the object was not found', ['command' => $command]);
        }

        $identities = $this->identityService->findBy([
            'objectIdentifier' => $identifier,
        ]);

        array_walk($identities, function (Identity $identity) {
            $this->identityService->remove($identity);
        });

        return true;
    }
}


<?php

namespace SystemConnector\MappingService;

use Assert\Assertion;
use Psr\Log\LoggerInterface;
use SystemConnector\DefinitionProvider\DefinitionProviderInterface;
use SystemConnector\DefinitionProvider\Struct\Definition;
use SystemConnector\MappingService\Struct\Mapping;
use SystemConnector\ServiceBus\QueryFactory\QueryFactoryInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\ServiceBus\ServiceBusInterface;
use SystemConnector\TransferObject\TransferObjectInterface;
use SystemConnector\ValidatorService\ValidatorServiceInterface;

class MappingService implements MappingServiceInterface
{
    /**
     * @var QueryFactoryInterface
     */
    private $queryFactory;

    /**
     * @var ServiceBusInterface
     */
    private $serviceBus;

    /**
     * @var ValidatorServiceInterface
     */
    private $validator;

    /**
     * @var DefinitionProviderInterface
     */
    private $definitionProvider;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        QueryFactoryInterface $queryFactory,
        ServiceBusInterface $serviceBus,
        ValidatorServiceInterface $validator,
        DefinitionProviderInterface $definitionProvider,
        LoggerInterface $logger
    ) {
        $this->queryFactory = $queryFactory;
        $this->serviceBus = $serviceBus;
        $this->validator = $validator;
        $this->definitionProvider = $definitionProvider;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function getMappingInformation($objectType = null)
    {
        Assertion::nullOrString($objectType);

        $definitions = $this->definitionProvider->getMappingDefinitions($objectType);

        if (empty($definitions)) {
            $this->logger->notice('No mappingdefinition found');
        }

        $result = [];

        foreach ($definitions as $definition) {
            $mapping = new Mapping();
            $mapping->setOriginAdapterName($definition->getOriginAdapterName());
            $mapping->setOriginTransferObjects($this->query($definition, $definition->getOriginAdapterName()));
            $mapping->setDestinationAdapterName($definition->getDestinationAdapterName());
            $mapping->setDestinationTransferObjects($this->query($definition, $definition->getDestinationAdapterName()));
            $mapping->setObjectType($definition->getObjectType());

            $this->validator->validate($mapping);

            $result[] = $mapping;
        }

        return $result;
    }

    /**
     * @param Definition $definition
     * @param string     $adapterName
     *
     * @return TransferObjectInterface[]
     */
    private function query(Definition $definition, $adapterName)
    {
        $originQuery = $this->queryFactory->create(
            $adapterName,
            $definition->getObjectType(),
            QueryType::ALL
        );

        $objects = $this->serviceBus->handle($originQuery);

        if (empty($objects)) {
            $objects = [];
        }

        $result = [];
        foreach ($objects as $object) {
            if ($object->getType() !== $definition->getObjectType()) {
                continue;
            }

            $result[] = $object;
        }

        return $result;
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Order;

use Exception;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Order\Order as OrderApi;
use PlentymarketsAdapter\ResponseParser\Order\OrderResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\Console\OutputHandler\OutputHandlerInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Order\Order;

class FetchAllOrdersQueryHandler implements QueryHandlerInterface
{
    /**
     * @var OrderApi
     */
    private $api;

    /**
     * @var OrderResponseParserInterface
     */
    private $responseParser;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var OutputHandlerInterface
     */
    private $outputHandler;

    public function __construct(
        OrderApi $api,
        OrderResponseParserInterface $responseParser,
        LoggerInterface $logger,
        OutputHandlerInterface $outputHandler
    ) {
        $this->api = $api;
        $this->responseParser = $responseParser;
        $this->logger = $logger;
        $this->outputHandler = $outputHandler;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === Order::TYPE &&
            $query->getQueryType() === QueryType::ALL;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $elements = $this->api->findAll();

        $this->outputHandler->startProgressBar(count($elements));

        foreach ($elements as $element) {
            try {
                $result = $this->responseParser->parse($element);
            } catch (Exception $exception) {
                $this->logger->error($exception->getMessage());

                $result = null;
            }

            if (empty($result)) {
                $result = [];
            }

            $result = array_filter($result);

            foreach ($result as $parsedElement) {
                yield $parsedElement;
            }

            $this->outputHandler->advanceProgressBar();
        }

        $this->outputHandler->finishProgressBar();
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Category;

use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\Helper\LanguageHelperInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Category\CategoryResponseParserInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Category\Category;

class FetchCategoryQueryHandler implements QueryHandlerInterface
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var CategoryResponseParserInterface
     */
    private $categoryResponseParser;

    /**
     * @var LanguageHelperInterface
     */
    private $languageHelper;

    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    public function __construct(
        ClientInterface $client,
        CategoryResponseParserInterface $categoryResponseParser,
        LanguageHelperInterface $languageHelper,
        IdentityServiceInterface $identityService
    ) {
        $this->client = $client;
        $this->categoryResponseParser = $categoryResponseParser;
        $this->languageHelper = $languageHelper;
        $this->identityService = $identityService;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === Category::TYPE &&
            $query->getQueryType() === QueryType::ONE;
    }

    /**
     * {@inheritdoc}
     *
     * @param FetchTransferObjectQuery $query
     */
    public function handle(QueryInterface $query)
    {
        $identity = $this->identityService->findOneBy([
            'objectIdentifier' => $query->getObjectIdentifier(),
            'objectType' => Category::TYPE,
            'adapterName' => PlentymarketsAdapter::NAME,
        ]);

        if (null === $identity) {
            return [];
        }

        $elements = $this->client->request('GET', 'categories/' . $identity->getAdapterIdentifier(), [
            'with' => 'details,clients',
            'type' => 'item',
            'lang' => $this->languageHelper->getLanguagesQueryString(),
        ]);

        if (empty($elements)) {
            return [];
        }

        $element = array_shift($elements);

        $parsedElements = $this->categoryResponseParser->parse($element);

        return array_filter($parsedElements);
    }
}


<?php

namespace SystemConnector\Logger;

use Exception;
use League\Tactician\Middleware;

class LoggerMiddleware implements Middleware
{
    /**
     * @var ClassNameFormatter
     */
    private $formatter;

    /**
     * @param ClassNameFormatter $formatter
     */
    public function __construct(ClassNameFormatter $formatter)
    {
        $this->formatter = $formatter;
    }

    /**
     * {@inheritdoc}
     */
    public function execute($command, callable $next)
    {
        $this->formatter->logCommandReceived($command);

        $returnValue = false;

        try {
            $returnValue = $next($command);

            $this->formatter->logCommandProcessed($command, $returnValue);

            return $returnValue;
        } catch (Exception $exception) {
            $this->formatter->logCommandFailed($command, $exception);
        }

        return $returnValue;
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Variation;

use Exception;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Item;
use PlentymarketsAdapter\ResponseParser\Product\ProductResponseParserInterface;
use PlentymarketsAdapter\ServiceBus\ChangedDateTimeTrait;
use Psr\Log\LoggerInterface;
use SystemConnector\Console\OutputHandler\OutputHandlerInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Product\Variation\Variation;

class FetchChangedVariationsQueryHandler implements QueryHandlerInterface
{
    use ChangedDateTimeTrait;

    /**
     * @var Item
     */
    private $api;

    /**
     * @var ProductResponseParserInterface
     */
    private $responseParser;

    /**
     * @var OutputHandlerInterface
     */
    private $outputHandler;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        Item $api,
        ProductResponseParserInterface $responseParser,
        OutputHandlerInterface $outputHandler,
        LoggerInterface $logger
    ) {
        $this->api = $api;
        $this->responseParser = $responseParser;
        $this->outputHandler = $outputHandler;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === Variation::TYPE &&
            $query->getQueryType() === QueryType::CHANGED;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $lastCangedTime = $this->getChangedDateTime();
        $currentDateTime = $this->getCurrentDateTime();

        $elements = $this->api->findChangedVariations($lastCangedTime, $currentDateTime);

        $this->outputHandler->startProgressBar(count($elements));

        foreach ($elements as $element) {
            try {
                $result = $this->responseParser->parse($element);
            } catch (Exception $exception) {
                $this->logger->error($exception->getMessage());

                $result = null;
            }

            if (empty($result)) {
                $result = [];
            }

            $result = array_filter($result);

            foreach ($result as $parsedElement) {
                yield $parsedElement;
            }

            $this->outputHandler->advanceProgressBar();
        }

        $this->outputHandler->finishProgressBar();
        $this->setChangedDateTime($currentDateTime);
    }
}


<?php

namespace SystemConnector\ServiceBus\QueryHandlerMiddleware\Exception;

use Exception;
use SystemConnector\ServiceBus\Query\QueryInterface;

class NotFoundException extends Exception
{
    /**
     * @param QueryInterface $query
     *
     * @return self
     */
    public static function fromQuery(QueryInterface $query)
    {
        $name = substr(strrchr(get_class($query), '\\'), 1);

        $message = 'No matching query handler found: ' . $name;

        return new self($message);
    }
}