<?php
/*
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace GetOptionKit;

use ArrayIterator;
use IteratorAggregate;
use Countable;
use Exception;
use LogicException;
use GetOptionKit\Exception\OptionConflictException;

class OptionCollection
    implements IteratorAggregate, Countable
{
    public $data = array();

    /**
     * @var Option[string]
     *
     * read-only property
     */
    public $longOptions = array();

    /**
     * @var Option[string]
     *
     * read-only property
     */
    public $shortOptions = array();

    /**
     * @var Option[]
     *
     * read-only property
     */
    public $options = array();

    public function __construct()
    {
        $this->data = array();
    }

    public function __clone()
    {
        foreach ($this->data as $k => $v) {
            $this->data[ $k ] = clone $v;
        }
        foreach ($this->longOptions as $k => $v) {
            $this->longOptions[ $k ] = clone $v;
        }
        foreach ($this->shortOptions as $k => $v) {
            $this->shortOptions[ $k ] = clone $v;
        }
        foreach ($this->options as $k => $v) {
            $this->options[ $k ] = clone $v;
        }
    }

    /**
     * add( [spec string], [desc string] ).
     *
     * add( [option object] )
     */
    public function add()
    {
        $num = func_num_args();
        $args = func_get_args();
        $first = $args[0];

        if ($first instanceof Option) {

            $this->addOption($first);

        } else if (is_string($first)) {

            $specString = $args[0];
            $desc = isset($args[1]) ? $args[1] : null;
            $key = isset($args[2]) ? $args[2] : null;

            // parse spec string
            $spec = new Option($specString);
            if ($desc) {
                $spec->desc($desc);
            }
            if ($key) {
                $spec->key = $key;
            }
            $this->addOption($spec);
            return $spec;

        } else {

            throw new LogicException('Unknown Spec Type');

        }
    }

    /**
     * Add option object.
     *
     * @param object $spec the option object.
     */
    public function addOption(Option $spec)
    {
        $this->data[$spec->getId()] = $spec;
        if ($spec->long) {
            if (isset($this->longOptions[$spec->long])) {
                throw new OptionConflictException('Option conflict: --'.$spec->long.' is already defined.');
            }
            $this->longOptions[$spec->long] = $spec;
        }
        if ($spec->short) {
            if (isset($this->shortOptions[$spec->short])) {
                throw new OptionConflictException('Option conflict: -'.$spec->short.' is already defined.');
            }
            $this->shortOptions[$spec->short] = $spec;
        }
        $this->options[] = $spec;
        if (!$spec->long && !$spec->short) {
            throw new Exception('Neither long option name nor short name is not given.');
        }
    }

    public function getLongOption($name)
    {
        return isset($this->longOptions[ $name ]) ? $this->longOptions[ $name ] : null;
    }

    public function getShortOption($name)
    {
        return isset($this->shortOptions[ $name ]) ? $this->shortOptions[ $name ] : null;
    }

    /* Get spec by spec id */
    public function get($id)
    {
        if (isset($this->data[$id])) {
            return $this->data[$id];
        } else if (isset($this->longOptions[$id])) {
            return $this->longOptions[$id];
        } else if (isset($this->shortOptions[$id])) {
            return $this->shortOptions[$id];
        }
    }

    public function find($name)
    {
        foreach ($this->options as $option) {
            if ($option->short === $name || $option->long === $name) {
                return $option;
            }
        }
    }

    public function size()
    {
        return count($this->data);
    }

    public function all()
    {
        return $this->data;
    }

    public function toArray()
    {
        $array = array();
        foreach ($this->data as $k => $spec) {
            $item = array();
            if ($spec->long) {
                $item['long'] = $spec->long;
            }
            if ($spec->short) {
                $item['short'] = $spec->short;
            }
            $item['desc'] = $spec->desc;
            $array[] = $item;
        }

        return $array;
    }

    public function keys()
    {
        return array_merge(array_keys($this->longOptions), array_keys($this->shortOptions));
    }

    public function count()
    {
        return count($this->data);
    }

    public function getIterator()
    {
        return new ArrayIterator($this->data);
    }
}


<?php
/*
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace GetOptionKit;

use Exception;
use GetOptionKit\Exception\InvalidOptionException;
use GetOptionKit\Exception\RequireValueException;

class OptionParser
{
    public $specs;
    public $longOptions;
    public $shortOptions;

    public function __construct(OptionCollection $specs)
    {
        $this->specs = $specs;
    }

    public function setSpecs(OptionCollection $specs)
    {
        $this->specs = $specs;
    }

    /**
     * consume option value from current argument or from the next argument
     *
     * @return boolean next token consumed?
     */
    protected function consumeOptionToken(Option $spec, $arg, $next, & $success = false)
    {
        // Check options doesn't require next token before
        // all options that require values.
        if ($spec->isFlag()) {

            if ($spec->isIncremental()) {
                $spec->increaseValue();
            } else {
                $spec->setValue(true);
            }
            return 0;

        } else if ($spec->isRequired()) {

            if ($next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) {
                $spec->setValue($next->arg);
                return 1;
            } else {
                throw new RequireValueException("Option '{$arg->getOptionName()}' requires a value.");
            }

        } else if ($spec->isMultiple()) {

            if ($next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) {
                $this->pushOptionValue($spec, $arg, $next);
                return 1;
            }

        } else if ($spec->isOptional() && $next && !$next->isEmpty() && !$next->anyOfOptions($this->specs)) {

            $spec->setValue($next->arg);
            return 1;

        }
        return 0;
    }

    /*
     * push value to multipl value option
     */
    protected function pushOptionValue(Option $spec, $arg, $next)
    {
        if ($next && !$next->anyOfOptions($this->specs)) {
            $spec->pushValue($next->arg);
        }
    }

    /**
     * preprocess the argv array
     *
     * - split option and option value
     * - separate arguments after "--"
     */
    protected function preprocessingArguments(array $argv)
    {
        // preprocessing arguments
        $newArgv = array();
        $extra = array();
        $afterDash = false;
        foreach ($argv as $arg) {
            if ($arg === '--') {
                $afterDash = true;
                continue;
            }
            if ($afterDash) {
                $extra[] = $arg;
                continue;
            }

            $a = new Argument($arg);
            if ($a->anyOfOptions($this->specs) && $a->containsOptionValue()) {
                list($opt, $val) = $a->splitAsOption();
                array_push($newArgv, $opt, $val);
            } else {
                $newArgv[] = $arg;
            }
        }
        return array($newArgv, $extra);
    }

    protected function fillDefaultValues(OptionCollection $opts, OptionResult $result)
    {
        // register option result from options with default value
        foreach ($opts as $opt) {
            if ($opt->value === null && $opt->defaultValue !== null) {
                $opt->setValue($opt->getDefaultValue());
                $result->set($opt->getId(), $opt);
            }
        }
    }

    /**
     * @param array $argv
     *
     * @return OptionResult|Option[]
     *
     * @throws Exception\RequireValueException
     * @throws Exception\InvalidOptionException
     * @throws \Exception
     */
    public function parse(array $argv)
    {
        $result = new OptionResult();

        list($argv, $extra) = $this->preprocessingArguments($argv);

        $len = count($argv);

        // some people might still pass only the option names here.
        $first = new Argument($argv[0]);
        if ($first->isOption()) {
            throw new Exception('parse(argv) expects the first argument to be the program name.');
        }

        for ($i = 1; $i < $len; ++$i) {
            $arg = new Argument($argv[$i]);

            // if looks like not an option, push it to argument list.
            // TODO: we might want to support argument with preceding dash (?)
            if (!$arg->isOption()) {
                $result->addArgument($arg);
                continue;
            }

            // if the option is with extra flags,
            //   split the string, and insert into the argv array
            if ($arg->withExtraFlagOptions()) {
                $extra = $arg->extractExtraFlagOptions();
                array_splice($argv, $i + 1, 0, $extra);
                $argv[$i] = $arg->arg; // update argument to current argv list.
                $len = count($argv);   // update argv list length
            }

            $next = null;
            if ($i + 1 < count($argv)) {
                $next = new Argument($argv[$i + 1]);
            }

            $spec = $this->specs->get($arg->getOptionName());
            if (!$spec) {
                throw new InvalidOptionException('Invalid option: '.$arg);
            }

            // This if expr might be unnecessary, becase we have default mode - flag
            // if ($spec->isRequired() || $spec->isMultiple() || $spec->isOptional() || $spec->isFlag()) {
            $i += $this->consumeOptionToken($spec, $arg, $next);
            $result->set($spec->getId(), $spec);
        }

        $this->fillDefaultValues($this->specs, $result);

        return $result;
    }
}


<?php
/*
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace GetOptionKit;

use Exception;
use LogicException;
use GetOptionKit\Exception\InvalidOptionException;
use GetOptionKit\Exception\RequireValueException;

/**
 * A common command line argument format:.
 *
 *      app.php
 *         [--app-options]
 *
 *      [subcommand
 *          --subcommand-options]
 *      [subcommand
 *          --subcommand-options]
 *      [subcommand
 *          --subcommand-options]
 *
 *      [arguments]
 *
 * ContinuousOptionParser is for the process flow:
 *
 * init app options,
 * parse app options
 *
 *
 *
 * while not end
 *   if stop at command
 *     shift command
 *     parse command options
 *   else if stop at arguments
 *     shift arguments
 *     execute current command with the arguments.
 *
 *  Example code:
 *
 *
 *      // subcommand stack
 *      $subcommands = array('subcommand1','subcommand2','subcommand3');
 *
 *      // different command has its own options
 *      $subcommand_specs = array(
 *          'subcommand1' => $cmdspecs,
 *          'subcommand2' => $cmdspecs,
 *          'subcommand3' => $cmdspecs,
 *      );
 *
 *      // for saved options
 *      $subcommand_options = array();
 *
 *      // command arguments
 *      $arguments = array();
 *
 *      $argv = explode(' ','-v -d -c subcommand1 -a -b -c subcommand2 -c subcommand3 arg1 arg2 arg3');
 *
 *      // parse application options first
 *      $parser = new ContinuousOptionParser( $appspecs );
 *      $app_options = $parser->parse( $argv );
 *      while (! $parser->isEnd()) {
 *          if( $parser->getCurrentArgument() == $subcommands[0] ) {
 *              $parser->advance();
 *              $subcommand = array_shift( $subcommands );
 *              $parser->setSpecs( $subcommand_specs[$subcommand] );
 *              $subcommand_options[ $subcommand ] = $parser->continueParse();
 *          } else {
 *              $arguments[] = $parser->advance();
 *          }
 *      }
 **/
class ContinuousOptionParser extends OptionParser
{
    public $index;
    public $length;
    public $argv;

    /* for the constructor , the option specs is application options */
    public function __construct(OptionCollection $specs)
    {
        parent::__construct($specs);
        $this->index = 1;
    }

    /**
     * @codeCoverageIgnore
     */
    public function startFrom($index)
    {
        $this->index = $index;
    }

    public function isEnd()
    {
        # echo "!! {$this->index} >= {$this->length}\n";
        return $this->index >= $this->length;
    }

    /**
     * Return the current argument and advance to the next position.
     *
     * @return string
     */
    public function advance()
    {
        if ($this->index >= $this->length) {
            throw new LogicException("Argument index out of bounds.");
        }
        return $this->argv[$this->index++];
    }

    /**
     * Return the current argument that the index pointed to.
     *
     * @return string
     */
    public function getCurrentArgument()
    {
        return $this->argv[$this->index];
    }

    public function continueParse()
    {
        return $this->parse($this->argv);
    }

    protected function fillDefaultValues(OptionCollection $opts, OptionResult $result)
    {
        // register option result from options with default value
        foreach ($opts as $opt) {
            if ($opt->value === null && $opt->defaultValue !== null) {
                $opt->setValue($opt->getDefaultValue());
                $result->set($opt->getId(), $opt);
            }
        }
    }

    public function parse(array $argv)
    {
        // create new Result object.
        $result = new OptionResult();
        list($this->argv, $extra) = $this->preprocessingArguments($argv);
        $this->length = count($this->argv);

        // from last parse index
        for (; $this->index < $this->length; ++$this->index) {
            $arg = new Argument($this->argv[$this->index]);

            /* let the application decide for: command or arguments */
            if (!$arg->isOption()) {
                # echo "stop at {$this->index}\n";
                $this->fillDefaultValues($this->specs, $result);
                return $result;
            }

            // if the option is with extra flags,
            //   split it out, and insert into the argv array
            //
            //   like -abc
            if ($arg->withExtraFlagOptions()) {
                $extra = $arg->extractExtraFlagOptions();
                array_splice($this->argv, $this->index + 1, 0, $extra);
                $this->argv[$this->index] = $arg->arg; // update argument to current argv list.
                $this->length = count($this->argv);   // update argv list length
            }

            $next = null;
            if ($this->index + 1 < count($this->argv)) {
                $next = new Argument($this->argv[$this->index + 1]);
            }

            $spec = $this->specs->get($arg->getOptionName());
            if (!$spec) {
                throw new InvalidOptionException('Invalid option: '.$arg);
            }

            // This if block is unnecessary
            // if ($spec->isRequired() || $spec->isMultiple() || $spec->isOptional() || $spec->isFlag()) {
            $this->index += $this->consumeOptionToken($spec, $arg, $next);
            $result->set($spec->getId(), $spec);
        }

        $this->fillDefaultValues($this->specs, $result);

        return $result;
    }
}


<?php
/**
 * This file is part of the GetOptionKit package.
 *
 * (c) Yo-An Lin <cornelius.howl@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace GetOptionKit\OptionPrinter;

use GetOptionKit\OptionCollection;
use GetOptionKit\Option;

class ConsoleOptionPrinter implements OptionPrinter
{
    public $screenWidth = 78;

    /**
     * Render readable spec.
     */
    public function renderOption(Option $opt)
    {
        $c1 = '';
        if ($opt->short && $opt->long) {
            $c1 = sprintf('-%s, --%s', $opt->short, $opt->long);
        } else if ($opt->short) {
            $c1 = sprintf('-%s', $opt->short);
        } else if ($opt->long) {
            $c1 = sprintf('--%s', $opt->long);
        }
        $c1 .= $opt->renderValueHint();

        return $c1;
    }

    /**
     * render option descriptions.
     *
     * @return string output
     */
    public function render(OptionCollection $options)
    {
        # echo "* Available options:\n";
        $lines = array();
        foreach ($options as $option) {
            $c1 = $this->renderOption($option);
            $lines[] = "\t".$c1;
            $lines[] = wordwrap("\t\t".$option->desc, $this->screenWidth, "\n\t\t");  # wrap text
            $lines[] = '';
        }

        return implode("\n", $lines);
    }
}


<?php

namespace PlentymarketsAdapter\RequestGenerator\Payment;

use PlentymarketsAdapter\PlentymarketsAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Currency\Currency;
use SystemConnector\TransferObject\Payment\Payment;
use SystemConnector\TransferObject\PaymentMethod\PaymentMethod;

class PaymentRequestGenerator implements PaymentRequestGeneratorInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

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

    /**
     * {@inheritdoc}
     */
    public function generate(Payment $payment)
    {
        $paymentMethodIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $payment->getPaymentMethodIdentifier(),
            'objectType' => PaymentMethod::TYPE,
            'adapterName' => PlentymarketsAdapter::NAME,
        ]);

        if (null === $paymentMethodIdentity) {
            throw new NotFoundException('payment method not mapped');
        }

        $currencyIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $payment->getCurrencyIdentifier(),
            'objectType' => Currency::TYPE,
            'adapterName' => PlentymarketsAdapter::NAME,
        ]);

        if (null === $currencyIdentity) {
            throw new NotFoundException('currency not mapped');
        }

        $paymentParams = [
            'amount' => $payment->getAmount(),
            'exchangeRatio' => 1,
            'mopId' => $paymentMethodIdentity->getAdapterIdentifier(),
            'currency' => $currencyIdentity->getAdapterIdentifier(),
            'type' => 'credit',
            'transactionType' => 2,
            'status' => 2,
        ];

        $paymentParams['properties'] = [
            [
                'typeId' => 23,
                'value' => 4,
            ],
            [
                'typeId' => 11,
                'value' => $payment->getAccountHolder(),
            ],
            [
                'typeId' => 1,
                'value' => $payment->getTransactionReference(),
            ],
            [
                'typeId' => 3,
                'value' => $payment->getTransactionReference(),
            ],
        ];

        return $paymentParams;
    }
}


<?php

namespace PlentyConnector\Components\PayPal\Plentymarkets;

use PlentyConnector\Components\PayPal\PaymentData\PayPalInstallmentPaymentData;
use PlentymarketsAdapter\RequestGenerator\Payment\PaymentRequestGeneratorInterface;
use SystemConnector\TransferObject\Payment\Payment;

class PayPalInstallmentRequestGenerator implements PaymentRequestGeneratorInterface
{
    /**
     * @var PaymentRequestGeneratorInterface
     */
    private $parentRequestGenerator;

    public function __construct(PaymentRequestGeneratorInterface $parentRequestGenerator)
    {
        $this->parentRequestGenerator = $parentRequestGenerator;
    }

    /**
     * {@inheritdoc}
     */
    public function generate(Payment $payment)
    {
        $paymentParams = $this->parentRequestGenerator->generate($payment);
        $data = $payment->getPaymentData();

        if (!($data instanceof PayPalInstallmentPaymentData)) {
            return $paymentParams;
        }

        $paymentParams['properties'][] = [
            'typeId' => 22,
            'value' => json_encode([
                'currency' => $data->getCurrency(),
                'financingCosts' => $data->getFinancingCosts(),
                'totalCostsIncludeFinancing' => $data->getTotalCostsIncludeFinancing(),
            ]),
        ];

        return $paymentParams;
    }
}


<?php

namespace PlentyConnector\Components\PayPal\Plentymarkets;

use PlentyConnector\Components\PayPal\PaymentData\PayPalPlusInvoicePaymentData;
use PlentymarketsAdapter\RequestGenerator\Payment\PaymentRequestGeneratorInterface;
use SystemConnector\TransferObject\Payment\Payment;

class PayPalPlusInvoiceRequestGenerator implements PaymentRequestGeneratorInterface
{
    /**
     * @var PaymentRequestGeneratorInterface
     */
    private $parentRequestGenerator;

    public function __construct(PaymentRequestGeneratorInterface $parentRequestGenerator)
    {
        $this->parentRequestGenerator = $parentRequestGenerator;
    }

    /**
     * {@inheritdoc}
     */
    public function generate(Payment $payment)
    {
        $paymentParams = $this->parentRequestGenerator->generate($payment);
        $data = $payment->getPaymentData();

        if (!($data instanceof PayPalPlusInvoicePaymentData)) {
            return $paymentParams;
        }

        $paymentParams['properties'][] = [
            'typeId' => 22,
            'value' => json_encode([
                'accountHolder' => $data->getAccountHolderName(),
                'bankName' => $data->getBankName(),
                'bic' => $data->getBankIdentifierCode(),
                'iban' => $data->getInternationalBankAccountNumber(),
                'paymentDue' => $data->getPaymentDueDate()->format(DATE_W3C),
                'referenceNumber' => $data->getReferenceNumber(),
            ]),
        ];

        return $paymentParams;
    }
}


<?php

namespace PlentymarketsAdapter\ReadApi\Item;

use PlentymarketsAdapter\ReadApi\ApiAbstract;

class Variation extends ApiAbstract
{
    /**
     * @var array
     */
    private static $includes = [
        'variationClients',
        'variationSalesPrices',
        'variationCategories',
        'variationDefaultCategory',
        'unit',
        'variationAttributeValues',
        'variationBarcodes',
        'images',
        'stock',
        'variationProperties',
        'properties',
    ];

    /**
     * @param array $criteria
     *
     * @return array
     */
    public function findBy(array $criteria)
    {
        $params = array_merge($criteria, [
            'with' => implode(',', self::$includes),
        ]);

        return iterator_to_array($this->client->getIterator('items/variations', $params));
    }
}


<?php

use PlentyConnector\PlentyConnector;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use Ramsey\Uuid\Uuid;
use SystemConnector\BacklogService\Middleware\BacklogCommandHandlerMiddleware;
use SystemConnector\ConfigService\ConfigServiceInterface;
use SystemConnector\ConnectorInterface;
use SystemConnector\IdentityService\IdentityService;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\MappingService\MappingServiceInterface;
use SystemConnector\MappingService\Struct\Mapping;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\TransferObject\TransferObjectInterface;

class Shopware_Controllers_Backend_PlentyConnector extends Shopware_Controllers_Backend_ExtJs
{
    public function initAcl()
    {
        // Credentials
        $this->addAclPermission('testApiCredentials', PlentyConnector::PERMISSION_READ, 'Insufficient Permissions');

        // Settings
        $this->addAclPermission('getSettingsList', PlentyConnector::PERMISSION_READ, 'Insufficient Permissions');
        $this->addAclPermission('saveSettings', PlentyConnector::PERMISSION_WRITE, 'Insufficient Permissions');

        // Mapping
        $this->addAclPermission('getMappingInformation', PlentyConnector::PERMISSION_READ, 'Insufficient Permissions');
        $this->addAclPermission('updateIdentities', PlentyConnector::PERMISSION_WRITE, 'Insufficient Permissions');

        // Sync one product
        $this->addAclPermission('syncItem', PlentyConnector::PERMISSION_WRITE, 'Insufficient Permissions');
    }

    public function testApiCredentialsAction()
    {
        /**
         * @var ClientInterface $client
         */
        $client = $this->container->get('plentymarkets_adapter.client');

        $params = [
            'username' => $this->Request()->get('rest_username'),
            'password' => $this->Request()->get('rest_password'),
        ];

        $options = [
            'base_uri' => $this->Request()->get('rest_url'),
        ];

        $success = false;

        try {
            $login = $client->request('POST', 'login', $params, null, null, $options);

            if (isset($login['accessToken'])) {
                $success = true;
            }
        } catch (Exception $exception) {
            // fail silently
        }

        $this->View()->assign([
            'success' => $success,
        ]);
    }

    public function saveSettingsAction()
    {
        /**
         * @var ConfigServiceInterface $configService
         */
        $configService = $this->container->get('plenty_connector.config_service');

        foreach ($this->cleanParameters($this->Request()->getParams()) as $key => $value) {
            $configService->set($key, $value);
        }

        $this->View()->assign([
            'success' => true,
            'data' => $this->Request()->getParams(),
        ]);
    }

    public function getSettingsListAction()
    {
        /**
         * @var ConfigServiceInterface $configService
         */
        $configService = $this->container->get('plenty_connector.config_service');

        $this->View()->assign([
            'success' => true,
            'data' => $configService->getAll(),
        ]);
    }

    public function getMappingInformationAction()
    {
        /**
         * @var MappingServiceInterface $mappingService
         */
        $mappingService = $this->container->get('plenty_connector.mapping_service');

        try {
            $mappingInformation = $mappingService->getMappingInformation();
        } catch (Exception $exception) {
            $this->View()->assign([
                'success' => false,
                'message' => $exception->getMessage(),
            ]);

            return;
        }

        $transferObjectMapping = function (TransferObjectInterface $object) {
            if (method_exists($object, 'getName')) {
                $name = $object->getName();
            } else {
                $name = $object->getIdentifier();
            }

            return [
                'identifier' => $object->getIdentifier(),
                'type' => $object->getType(),
                'name' => $name,
            ];
        };

        $this->View()->assign([
            'success' => true,
            'data' => array_map(function (Mapping $mapping) use ($transferObjectMapping) {
                return [
                    'originAdapterName' => $mapping->getOriginAdapterName(),
                    'destinationAdapterName' => $mapping->getDestinationAdapterName(),
                    'originTransferObjects' => array_map($transferObjectMapping, $mapping->getOriginTransferObjects()),
                    'destinationTransferObjects' => array_map($transferObjectMapping, $mapping->getDestinationTransferObjects()),
                    'objectType' => $mapping->getObjectType(),
                ];
            }, $mappingInformation),
        ]);
    }

    public function updateIdentitiesAction()
    {
        $updates = json_decode($this->request->getRawBody(), true);

        if (array_key_exists('identifier', $updates)) {
            $updates = [$updates];
        }

        /**
         * @var IdentityService $identityService
         */
        $identityService = $this->container->get('plenty_connector.identity_service');

        try {
            foreach ($updates as $key => $update) {
                $remove = $update['remove'];

                $objectType = $update['objectType'];
                $destinationAdapterName = $update['adapterName'];
                $destinationIdentifier = $update['identifier'];
                $originIdentifier = $update['originIdentifier'];

                $oldDestinationIdentity = $identityService->findOneBy([
                    'objectIdentifier' => $destinationIdentifier,
                    'objectType' => $objectType,
                    'adapterName' => $destinationAdapterName,
                ]);

                if (null === $oldDestinationIdentity) {
                    $this->View()->assign([
                        'success' => false,
                        'message' => 'reload mapping',
                    ]);

                    return;
                }

                $newIdentifier = $remove ? Uuid::uuid4()->toString() : $originIdentifier;

                $identityService->update(
                    $oldDestinationIdentity,
                    [
                        'objectIdentifier' => $newIdentifier,
                    ]
                );

                $updates[$key]['identifier'] = $newIdentifier;
                if ($remove) {
                    $updates[$key]['originAdapterName'] = null;
                    $updates[$key]['originIdentifier'] = null;
                    $updates[$key]['remove'] = false;
                }
            }

            $this->View()->assign([
                'success' => true,
                'data' => $updates,
            ]);
        } catch (Exception $exception) {
            $this->View()->assign([
                'success' => false,
                'message' => $exception->getMessage(),
            ]);
        } catch (Throwable $exception) {
            $this->View()->assign([
                'success' => false,
                'message' => $exception->getMessage(),
            ]);
        }
    }

    public function getOrderOriginsAction()
    {
        /**
         * @var ClientInterface $client
         */
        $client = $this->container->get('plentymarkets_adapter.client');

        $data = [];

        try {
            foreach ($client->request('GET', 'orders/referrers') as $origin) {
                $data[] = [
                    'id' => $origin['id'],
                    'name' => $origin['name'],
                ];
            }
        } catch (Exception $exception) {
            // fail silently
        }

        $this->View()->assign([
            'success' => true,
            'data' => $data,
        ]);
    }

    public function getItemWarehousesAction()
    {
        /**
         * @var ClientInterface $client
         */
        $client = $this->container->get('plentymarkets_adapter.client');

        $data = [
            [
                'id' => 0,
                'name' => $this->getTranslation(
                    'plentyconnector/view/settings/additional/item_warehouse/virtualWarehouse'
                ),
            ],
        ];

        try {
            foreach ($client->request('GET', 'stockmanagement/warehouses') as $origin) {
                $data[] = [
                    'id' => $origin['id'],
                    'name' => $origin['name'],
                ];
            }
        } catch (Exception $exception) {
            // fail silently
        }

        $this->View()->assign([
            'success' => true,
            'data' => $data,
        ]);
    }

    public function syncItemAction()
    {
        $itemId = (int) $this->Request()->get('item_id');

        if (empty($itemId)) {
            $this->View()->assign([
                'success' => false,
                'message' => $this->getTranslation('plentyconnector/controller/actions/item_import/missing_item_id'),
            ]);

            return;
        }

        try {
            /**
             * @var IdentityServiceInterface $identityService
             */
            $identityService = $this->container->get('plenty_connector.identity_service');

            /**
             * @var ConnectorInterface $connector
             */
            $connector = $this->container->get('plenty_connector.connector');

            /**
             * @var ClientInterface $client
             */
            $client = $this->container->get('plentymarkets_adapter.client');

            try {
                $client->request('GET', 'items/' . $itemId);
            } catch (Exception $exception) {
                $this->View()->assign([
                    'success' => false,
                    'message' => $this->getTranslation('plentyconnector/controller/actions/item_import/item_not_found'),
                ]);

                return;
            }

            $productIdentity = $identityService->findOneOrCreate(
                (string) $itemId,
                PlentymarketsAdapter::NAME,
                Product::TYPE
            );

            BacklogCommandHandlerMiddleware::$active = false;

            $connector->handle(QueryType::ONE, Product::TYPE, $productIdentity->getObjectIdentifier());

            $this->View()->assign([
                'success' => true,
                'message' => '',
            ]);
        } catch (Exception $exception) {
            $this->View()->assign([
                'success' => false,
                'message' => $exception->getMessage(),
            ]);
        } catch (Throwable $exception) {
            $this->View()->assign([
                'success' => false,
                'message' => $exception->getMessage(),
            ]);
        }
    }

    private function cleanParameters(array $params)
    {
        $result = [];

        $blacklist = [
            'action',
            'controller',
            'module',
            '_dc',
        ];

        foreach ($params as $key => $value) {
            if (in_array($key, $blacklist, true)) {
                continue;
            }

            $result[$key] = $value;
        }

        return $result;
    }

    /**
     * @param string $snippet
     *
     * @return string
     */
    private function getTranslation($snippet)
    {
        /**
         * @var Shopware_Components_Snippet_Manager $snippetManager
         */
        $snippetManager = $this->container->get('snippets');
        $namespace = 'backend/plentyconnector/main';

        return $snippetManager->getNamespace($namespace)->get($snippet);
    }
}


<?php

namespace ShopwareAdapter\DataPersister\Attribute;

use Assert\Assertion;
use Shopware\Bundle\AttributeBundle\Service\CrudService;
use Shopware\Bundle\AttributeBundle\Service\DataPersister as ShopwareDataPersister;
use Shopware\Bundle\AttributeBundle\Service\TypeMapping;
use Shopware\Components\Model\ModelManager;
use Shopware\Models\Article\Detail;
use Shopware\Models\Article\Supplier;
use Shopware\Models\Category\Category;
use Shopware\Models\Media\Media;
use Shopware\Models\Order\Order;
use SystemConnector\ValueObject\Attribute\Attribute;

class AttributeDataPersister implements AttributeDataPersisterInterface
{
    /**
     * @var CrudService
     */
    private $attributeService;

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

    /**
     * @var ShopwareDataPersister
     */
    private $shopwareDataPersister;

    /**
     * @var string
     */
    private $prefix = 'plenty_connector_';

    public function __construct(
        CrudService $attributeService,
        ModelManager $entityManager,
        ShopwareDataPersister $shopwareDataPersister
    ) {
        $this->attributeService = $attributeService;
        $this->entityManager = $entityManager;
        $this->shopwareDataPersister = $shopwareDataPersister;
    }

    /**
     * {@inheritdoc}
     */
    public function saveCategoryAttributes(Category $category, array $attributes = [])
    {
        $this->saveAttributes($category->getId(), 's_categories_attributes', $attributes);
    }

    /**
     * {@inheritdoc}
     */
    public function saveManufacturerAttributes(Supplier $supplier, array $attributes = [])
    {
        $this->saveAttributes($supplier->getId(), 's_articles_supplier_attributes', $attributes);
    }

    /**
     * {@inheritdoc}
     */
    public function saveOrderAttributes(Order $order, array $attributes = [])
    {
        $this->saveAttributes($order->getId(), 's_order_attributes', $attributes);
    }

    /**
     * {@inheritdoc}
     */
    public function saveMediaAttributes(Media $media, array $attributes = [])
    {
        $this->saveAttributes($media->getId(), 's_media_attributes', $attributes);
    }

    /**
     * {@inheritdoc}
     */
    public function saveProductDetailAttributes(Detail $detail, array $attributes = [])
    {
        $this->saveAttributes($detail->getId(), 's_articles_attributes', $attributes);
    }

    /**
     * @param int         $identifier
     * @param string      $table
     * @param Attribute[] $attributes
     */
    private function saveAttributes($identifier, $table, array $attributes = [])
    {
        Assertion::integer($identifier);
        Assertion::allIsInstanceOf($attributes, Attribute::class);
        Assertion::notBlank($table);

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

        array_walk($attributes, function (Attribute $attribute) use ($table) {
            $this->prepareAttribute($attribute, $table);
        });

        $this->shopwareDataPersister->persist(
            $this->getAttributesAsArray($attributes),
            $table,
            $identifier
        );
    }

    /**
     * @param Attribute $attribute
     *
     * @return string
     */
    private function getAttributeKey(Attribute $attribute)
    {
        $key = iconv('UTF-8', 'ASCII//TRANSLIT', $attribute->getKey());

        $attribute_key = strtolower(preg_replace('/[A-Z]/', '_\\0', lcfirst($key)));

        return $this->prefix . $attribute_key;
    }

    /**
     * @param Attribute $attribute
     * @param string    $table
     */
    private function prepareAttribute(Attribute $attribute, $table)
    {
        $key = $this->getAttributeKey($attribute);

        $attributeConfig = $this->attributeService->get(
            $table,
            $key
        );

        if (null === $attributeConfig) {
            $this->attributeService->update(
                $table,
                $key,
                TypeMapping::TYPE_TEXT,
                [
                    'label' => 'PlentyConnector ' . $attribute->getKey(),
                    'displayInBackend' => true,
                    'translatable' => true,
                    'custom' => true,
                ]
            );

            $this->entityManager->generateAttributeModels([$table]);
        }
    }

    /**
     * @param Attribute[] $attributes
     *
     * @return array
     */
    private function getAttributesAsArray(array $attributes = [])
    {
        $result = [];

        foreach ($attributes as $attribute) {
            $key = $this->getAttributeKey($attribute);

            $result[$key] = $attribute->getValue();
        }

        return $result;
    }
}


<?php

namespace ShopwareAdapter\ResponseParser\Order;

use Assert\Assertion;
use DateTimeImmutable;
use Doctrine\ORM\EntityRepository;
use Psr\Log\LoggerInterface;
use Shopware\Models\Tax\Tax;
use ShopwareAdapter\DataProvider\Currency\CurrencyDataProviderInterface;
use ShopwareAdapter\ResponseParser\Address\AddressResponseParserInterface;
use ShopwareAdapter\ResponseParser\Customer\CustomerResponseParserInterface;
use ShopwareAdapter\ResponseParser\GetAttributeTrait;
use ShopwareAdapter\ResponseParser\OrderItem\OrderItemResponseParserInterface;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Currency\Currency;
use SystemConnector\TransferObject\Order\Comment\Comment;
use SystemConnector\TransferObject\Order\Order;
use SystemConnector\TransferObject\Order\OrderItem\OrderItem;
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;
use SystemConnector\TransferObject\VatRate\VatRate;

class OrderResponseParser implements OrderResponseParserInterface
{
    use GetAttributeTrait;

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

    /**
     * @var OrderItemResponseParserInterface
     */
    private $orderItemResponseParser;

    /**
     * @var AddressResponseParserInterface
     */
    private $orderAddressParser;

    /**
     * @var CustomerResponseParserInterface
     */
    private $customerParser;

    /**
     * @var CurrencyDataProviderInterface
     */
    private $currencyDataProvider;

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

    /**
     * @var EntityRepository
     */
    private $taxRepository;

    public function __construct(
        IdentityServiceInterface $identityService,
        OrderItemResponseParserInterface $orderItemResponseParser,
        AddressResponseParserInterface $orderAddressParser,
        CustomerResponseParserInterface $customerParser,
        CurrencyDataProviderInterface $currencyDataProvider,
        LoggerInterface $logger,
        EntityRepository $taxRepository
    ) {
        $this->identityService = $identityService;
        $this->orderItemResponseParser = $orderItemResponseParser;
        $this->orderAddressParser = $orderAddressParser;
        $this->customerParser = $customerParser;
        $this->currencyDataProvider = $currencyDataProvider;
        $this->logger = $logger;
        $this->taxRepository = $taxRepository;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $entry)
    {
        if (!$this->isValidOrder($entry)) {
            return [];
        }

        $taxFree = $entry['taxFree'] || $entry['net'];

        $orderItems = array_filter(
            array_map(
                function (array $orderItem) use ($taxFree) {
                    return $this->orderItemResponseParser->parse($orderItem, $taxFree);
                },
                $entry['details']
            )
        );

        $orderItems[] = $this->getShippingCosts($entry);

        $billingAddress = $this->orderAddressParser->parse($entry['billing']);
        $shippingAddress = $this->orderAddressParser->parse($entry['shipping']);

        if (null === $billingAddress || null === $shippingAddress) {
            $this->logger->warning('could not parse address,  order: ' . $entry['number']);

            return [];
        }

        if (null === $entry['customer']) {
            $this->logger->warning('could not find customer,  order: ' . $entry['number']);

            return [];
        }

        $customer = $this->customerParser->parse($entry['customer']);

        if (null === $customer) {
            $this->logger->warning('could not parse customer,  order: ' . $entry['number']);

            return [];
        }

        $customer->setMobilePhoneNumber($billingAddress->getMobilePhoneNumber());
        $customer->setPhoneNumber($billingAddress->getPhoneNumber());

        $orderStatusIdentifier = $this->getConnectorIdentifier($entry['orderStatusId'], OrderStatus::TYPE);
        $paymentStatusIdentifier = $this->getConnectorIdentifier($entry['paymentStatusId'], PaymentStatus::TYPE);
        $paymentMethodIdentifier = $this->getConnectorIdentifier($entry['paymentId'], PaymentMethod::TYPE);
        $shippingProfileIdentifier = $this->getConnectorIdentifier($entry['dispatchId'], ShippingProfile::TYPE);
        $shopIdentifier = $this->getConnectorIdentifier($entry['languageSubShop']['id'], Shop::TYPE);

        $shopwareCurrencyIdentifier = $this->currencyDataProvider->getCurrencyIdentifierByCode($entry['currency']);
        $currencyIdentifier = $this->getConnectorIdentifier($shopwareCurrencyIdentifier, Currency::TYPE);

        $orderIdentity = $this->identityService->findOneOrCreate(
            (string) $entry['id'],
            ShopwareAdapter::NAME,
            Order::TYPE
        );

        $isMappedOrderIdentity = $this->identityService->isMappedIdentity(
            $orderIdentity->getObjectIdentifier(),
            $orderIdentity->getObjectType(),
            $orderIdentity->getAdapterName()
        );

        if ($isMappedOrderIdentity) {
            return [];
        }

        $order = new Order();
        $order->setIdentifier($orderIdentity->getObjectIdentifier());
        $order->setOrderNumber($entry['number']);
        $order->setOrderItems($orderItems);
        $order->setAttributes($this->getAttributes($entry['attribute']));
        $order->setBillingAddress($billingAddress);
        $order->setShippingAddress($shippingAddress);
        $order->setComments($this->getComments($entry));
        $order->setCustomer($customer);
        $order->setOrderTime(DateTimeImmutable::createFromMutable($entry['orderTime']));
        $order->setOrderStatusIdentifier($orderStatusIdentifier);
        $order->setPaymentStatusIdentifier($paymentStatusIdentifier);
        $order->setPaymentMethodIdentifier($paymentMethodIdentifier);
        $order->setShippingProfileIdentifier($shippingProfileIdentifier);
        $order->setCurrencyIdentifier($currencyIdentifier);
        $order->setShopIdentifier($shopIdentifier);

        return [$order];
    }

    /**
     * @param array $entry
     *
     * @return bool
     */
    private function isValidOrder(array $entry)
    {
        $shopIdentity = $this->identityService->findOneOrThrow(
            (string) $entry['languageSubShop']['id'],
            ShopwareAdapter::NAME,
            Shop::TYPE
        );

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

        if (!$isMappedIdentity) {
            return false;
        }

        if (empty($entry['billing'])) {
            $this->logger->warning('empty order billing address - order: ' . $entry['number']);

            return false;
        }

        if (empty($entry['details'])) {
            $this->logger->warning('empty order positions - order: ' . $entry['number']);

            return false;
        }

        if (empty($entry['shipping'])) {
            $this->logger->warning('empty order shipping address - order: ' . $entry['number']);

            return false;
        }

        $shippingProfileIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $entry['dispatchId'],
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => ShippingProfile::TYPE,
        ]);

        if (null === $shippingProfileIdentity) {
            $this->logger->warning('no shipping profile was selected for order: ' . $entry['number']);

            return false;
        }

        return true;
    }

    /**
     * @param array $entry
     *
     * @return Comment[]
     */
    private function getComments($entry)
    {
        $comments = [];

        if ($entry['internalComment']) {
            $comment = new Comment();
            $comment->setType(Comment::TYPE_INTERNAL);
            $comment->setComment($entry['internalComment']);
            $comments[] = $comment;
        }

        if ($entry['customerComment']) {
            $comment = new Comment();
            $comment->setType(Comment::TYPE_CUSTOMER);
            $comment->setComment($entry['customerComment']);
            $comments[] = $comment;
        }

        return $comments;
    }

    /**
     * @param int    $entry
     * @param string $type
     *
     * @return string
     */
    private function getConnectorIdentifier($entry, $type)
    {
        Assertion::integerish($entry);

        return $this->identityService->findOneOrThrow(
            (string) $entry,
            ShopwareAdapter::NAME,
            $type
        )->getObjectIdentifier();
    }

    /**
     * @param array $entry
     *
     * @return string
     */
    private function getShippingCostsVatRateIdentifier(array $entry)
    {
        $taxRateId = $this->getMaxTaxRateFromOrderItems($entry);

        /**
         * @var Tax $taxModel
         */
        $taxModel = $this->taxRepository->findOneBy(['tax' => $taxRateId]);

        $taxRateId = $taxModel->getId();
        if (isset($entry['dispatch']['taxCalculation']) && $entry['dispatch']['taxCalculation'] > 0) {
            $taxRateId = $entry['dispatch']['taxCalculation'];
        }

        $identity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $taxRateId,
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => VatRate::TYPE,
        ]);

        if (null === $identity) {
            throw new NotFoundException('missing tax rate mapping - ' . $taxRateId);
        }

        return $identity->getObjectIdentifier();
    }

    /**
     * @param array $entry
     *
     * @return OrderItem
     */
    private function getShippingCosts(array $entry)
    {
        $shippingCosts = $this->getShippingAmount($entry);
        $vatRateIdentifier = $this->getShippingCostsVatRateIdentifier($entry);

        $orderItem = new OrderItem();
        $orderItem->setType(OrderItem::TYPE_SHIPPING_COSTS);
        $orderItem->setQuantity(1.0);
        $orderItem->setName('ShippingCosts');
        $orderItem->setNumber('ShippingCosts');
        $orderItem->setPrice($shippingCosts);
        $orderItem->setVatRateIdentifier($vatRateIdentifier);

        return $orderItem;
    }

    /**
     * @param array $entry
     *
     * @return float
     */
    private function getMaxTaxRateFromOrderItems(array $entry)
    {
        return max(array_column($entry['details'], 'taxRate'));
    }

    /**
     * @param array $entry
     *
     * @return float
     */
    private function getShippingAmount(array $entry)
    {
        $isShippingBruttoAndNettoSame = 1 === $entry['taxFree'] && $entry['taxFree'] == $entry['net'];
        if ($isShippingBruttoAndNettoSame) {
            return $entry['invoiceShippingNet'] + $entry['invoiceShippingNet'] * $this->getMaxTaxRateFromOrderItems($entry) / 100;
        }

        return (float) $entry['invoiceShipping'];
    }
}


<?php

namespace SystemConnector\ServiceBus\ValidatorMiddleware;

use League\Tactician\Middleware;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ValidatorService\ValidatorServiceInterface;

class ValidatorMiddleware implements Middleware
{
    /**
     * @var ValidatorServiceInterface
     */
    private $validator;

    public function __construct(ValidatorServiceInterface $validator)
    {
        $this->validator = $validator;
    }

    /**
     * @param mixed    $command
     * @param callable $next
     *
     * @return mixed
     */
    public function execute($command, callable $next)
    {
        if (!($command instanceof TransferObjectCommand)) {
            return $next($command);
        }

        $object = $command->getPayload();

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

        return $next($command);
    }
}


<?php

namespace PlentymarketsAdapter\ReadApi\Order;

use PlentymarketsAdapter\Client\Iterator\Iterator;
use PlentymarketsAdapter\ReadApi\ApiAbstract;

class Order extends ApiAbstract
{
    /**
     * @var array
     */
    private static $includes = [
        'addresses',
        'relations',
        'addresses',
        'comments',
    ];

    /**
     * @param int $id
     *
     * @return array
     */
    public function find($id)
    {
        $criteria = [
            'with' => self::$includes,
        ];

        return $this->client->request('GET', 'orders/' . $id, $criteria);
    }

    /**
     * @param array $criteria
     *
     * @return Iterator
     */
    public function findAll(array $criteria = [])
    {
        $criteria = array_merge($criteria, [
            'with' => self::$includes,
        ]);

        return $this->client->getIterator('orders', $criteria);
    }

    /**
     * @param array $criteria
     *
     * @return Iterator
     */
    public function findBy(array $criteria = [])
    {
        $criteria = array_merge($criteria, [
            'with' => self::$includes,
        ]);

        return $this->client->getIterator('orders', $criteria);
    }

    /**
     * @param array $criteria
     *
     * @return array
     */
    public function findOneBy(array $criteria = [])
    {
        $result = iterator_to_array($this->findBy($criteria));

        if (!empty($result)) {
            $result = array_shift($result);
        }

        return $result;
    }
}


<?php

namespace SystemConnector\ServiceBus\Command;

use Assert\Assertion;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\TransferObjectInterface;

class TransferObjectCommand implements CommandInterface
{
    /**
     * @var string
     */
    private $adapterName;

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

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

    /**
     * @var int
     */
    private $priority;

    /**
     * @var string|TransferObjectInterface
     */
    private $payload;

    /**
     * @param string                         $adapterName
     * @param string                         $objectType
     * @param string                         $commandType
     * @param int                            $priority
     * @param string|TransferObjectInterface $payload
     */
    public function __construct($adapterName, $objectType, $commandType, $priority, $payload)
    {
        Assertion::string($adapterName);
        Assertion::string($objectType);
        Assertion::inArray($commandType, CommandType::getAllTypes());
        Assertion::integer($priority);

        if ($commandType === CommandType::HANDLE) {
            Assertion::isInstanceOf($payload, TransferObjectInterface::class);
        }

        if ($commandType === CommandType::REMOVE) {
            Assertion::uuid($payload);
        }

        $this->adapterName = $adapterName;
        $this->objectType = $objectType;
        $this->commandType = $commandType;
        $this->priority = $priority;
        $this->payload = $payload;
    }

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

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

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

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

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

    /**
     * {@inheritdoc}
     */
    public function toArray()
    {
        return [
            'adapterName' => $this->adapterName,
            'objectType' => $this->objectType,
            'commandType' => $this->commandType,
            'priority' => $this->priority,
            'payload' => $this->payload,
        ];
    }
}


<?php

namespace PlentyConnector\Components\Bundle\ShopwareAdapter\CommandHandler;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use PlentyConnector\Components\Bundle\Helper\BundleHelper;
use PlentyConnector\Components\Bundle\TransferObject\Bundle;
use Psr\Log\LoggerInterface;
use Shopware\Models\Article\Detail as DetailModel;
use Shopware\Models\Customer\Group as GroupModel;
use ShopwareAdapter\ShopwareAdapter;
use SwagBundle\Models\Article;
use SwagBundle\Models\Bundle as BundleModel;
use SwagBundle\Models\Price as PriceModel;
use SwagBundle\Models\Repository as BundleRepository;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\CustomerGroup\CustomerGroup;
use SystemConnector\TransferObject\Product\Price\Price;

class HandleBundleCommandHandler implements CommandHandlerInterface
{
    /**
     * @var bool
     */
    private $active;

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

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

    /**
     * @var BundleHelper
     */
    private $bundleHelper;

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

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

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

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

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

        $this->bundleHelper->registerBundleModels();

        /**
         * @var BundleRepository $repository
         */
        $repository = $this->entityManager->getRepository(BundleModel::class);

        if (null === $identity) {
            $existingBundle = $repository->findOneBy(['number' => $bundle->getNumber()]);

            if (null !== $existingBundle) {
                $identity = $this->identityService->insert(
                    $bundle->getIdentifier(),
                    Bundle::TYPE,
                    (string) $existingBundle->getId(),
                    ShopwareAdapter::NAME
                );
            }
        } else {
            $existingBundle = $repository->find($identity->getAdapterIdentifier());

            if (null === $existingBundle) {
                $this->identityService->remove($identity);

                $identity = null;
            }
        }

        if (null === $identity) {
            $bundleModel = new BundleModel();

            $bundleModel->setDisplayGlobal(true);
            $bundleModel->setSells(0);
            $bundleModel->setCreated();
            $bundleModel->setType(1);
            $bundleModel->setDiscountType('abs');
            $bundleModel->setQuantity(0);
            $bundleModel->setShowName(false);
        } else {
            /**
             * @var BundleModel $bundleModel
             */
            $bundleModel = $repository->find($identity->getAdapterIdentifier());

            foreach ($bundleModel->getPrices() as $price) {
                $this->entityManager->remove($price);
            }

            foreach ($bundleModel->getArticles() as $article) {
                $this->entityManager->remove($article);
            }

            $this->entityManager->flush();
        }

        try {
            $mainVariant = $this->getMainVariant($bundle);
        } catch (Exception $exception) {
            $this->logger->error($exception->getMessage());

            return false;
        }

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

        $mainArticle = $mainVariant->getArticle();

        $this->active = $mainArticle->getActive();

        $bundleModel->setName($bundle->getName());
        $bundleModel->setValidFrom($bundle->getAvailableFrom());
        $bundleModel->setValidTo($bundle->getAvailableTo());
        $bundleModel->setLimited($bundle->hasStockLimitation());
        $bundleModel->setQuantity($this->getBundleStock($bundle->getNumber()));
        $bundleModel->setNumber($bundle->getNumber());
        $bundleModel->setArticle($mainArticle);
        $bundleModel->setCustomerGroups($this->getCustomerGroups($bundle));
        $bundleModel->setPrices($this->getPrices($bundle, $bundleModel));
        $bundleModel->setPosition($bundle->getPosition());
        $bundleModel->setArticles($this->getArticles($bundle, $bundleModel, $mainVariant));
        $bundleModel->setActive($this->active);

        $this->entityManager->persist($bundleModel);
        $this->entityManager->flush();
        $this->entityManager->clear();

        return true;
    }

    /**
     * @param Price $price
     *
     * @return null|GroupModel
     */
    private function getCustomerGroupFromPrice(Price $price)
    {
        $repository = $this->entityManager->getRepository(GroupModel::class);

        if (null === $price->getCustomerGroupIdentifier()) {
            return $repository->findOneBy(['key' => 'EK']);
        }

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

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

        return $repository->find($identity->getAdapterIdentifier());
    }

    /**
     * @param Bundle $bundle
     *
     * @return ArrayCollection
     */
    private function getCustomerGroups(Bundle $bundle)
    {
        $result = [];
        foreach ($bundle->getPrices() as $price) {
            $group = $this->getCustomerGroupFromPrice($price);

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

            $result[$group->getKey()] = $group;
        }

        return new ArrayCollection($result);
    }

    /**
     * @param Bundle      $bundle
     * @param BundleModel $bundleModel
     *
     * @return ArrayCollection
     */
    private function getPrices(Bundle $bundle, BundleModel $bundleModel)
    {
        $prices = [];
        foreach ($bundle->getPrices() as $price) {
            $group = $this->getCustomerGroupFromPrice($price);

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

            $netPrice = $price->getPrice() * (100 / ($bundleModel->getArticle()->getTax()->getTax() + 100));

            $priceModel = new PriceModel();
            $priceModel->setBundle($bundleModel);
            $priceModel->setCustomerGroup($group);
            $priceModel->setPrice($netPrice);
            $this->entityManager->persist($priceModel);
            $prices[] = $priceModel;
        }

        return new ArrayCollection($prices);
    }

    /**
     * @param Bundle      $bundle
     * @param BundleModel $bundleModel
     * @param DetailModel $mainVariant
     *
     * @return ArrayCollection
     */
    private function getArticles(Bundle $bundle, BundleModel $bundleModel, DetailModel $mainVariant)
    {
        $repository = $this->entityManager->getRepository(DetailModel::class);

        $result = [];
        foreach ($bundle->getBundleProducts() as $bundleProduct) {
            if ($mainVariant->getNumber() === $bundleProduct->getNumber()) {
                continue;
            }

            $detail = $repository->findOneBy(['number' => $bundleProduct->getNumber()]);

            if (null === $detail) {
                $this->logger->error('bundle product not found => number: ' . $bundleProduct->getNumber());
                $this->active = false;

                continue;
            }

            $product = new Article();
            $product->setQuantity($bundleProduct->getAmount());
            $product->setArticleDetail($detail);
            $product->setPosition($bundleProduct->getPosition());
            $product->setBundle($bundleModel);

            $this->entityManager->persist($product);
            $result[] = $product;
        }

        return new ArrayCollection($result);
    }

    /**
     * @param Bundle $bundle
     *
     * @throws NotFoundException
     *
     * @return null|object
     */
    private function getMainVariant(Bundle $bundle)
    {
        $repository = $this->entityManager->getRepository(DetailModel::class);

        foreach ($bundle->getBundleProducts() as $bundleProduct) {
            $detail = $repository->findOneBy(['number' => $bundleProduct->getNumber()]);

            if (null === $detail) {
                throw new NotFoundException('bundle main product not found');
                continue;
            }

            return $detail;
        }

        return null;
    }

    /**
     * @param string $bundleNumber
     *
     * @return int
     */
    private function getBundleStock($bundleNumber)
    {
        $repository = $this->entityManager->getRepository(DetailModel::class);
        $detail = $repository->findOneBy(['number' => $bundleNumber]);

        if (null === $detail) {
            return 0;
        }

        return $detail->getInStock();
    }
}


<?php

namespace ShopwareAdapter\ServiceBus\CommandHandler\MediaCategory;

use Doctrine\ORM\EntityManagerInterface;
use Shopware\Models\Media\Album;
use Shopware\Models\Media\Settings;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\MediaCategory\MediaCategory;

class HandleMediaCategoryCommandHandler implements CommandHandlerInterface
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

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

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

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

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

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

        $albumRepository = $this->entityManager->getRepository(Album::class);
        $settingsRepository = $this->entityManager->getRepository(Settings::class);

        $parent = $albumRepository->findOneBy([
            'name' => 'PlentyConnector',
        ]);

        /**
         * @var null|Settings $parentSettings
         */
        $parentSettings = $settingsRepository->findOneBy([
            'albumId' => Album::ALBUM_ARTICLE,
        ]);

        if (null === $parent) {
            $parent = new Album();
            $parent->setName('PlentyConnector');
            $parent->setPosition(10);

            $settings = new Settings();
            $settings->setAlbum($parent);
            $settings->setCreateThumbnails($parentSettings->getCreateThumbnails());
            $settings->setThumbnailSize($parentSettings->getThumbnailSize());
            $settings->setIcon('sprite-pictures');
            $settings->setThumbnailHighDpi($parentSettings->isThumbnailHighDpi());
            $settings->setThumbnailQuality($parentSettings->getThumbnailQuality());
            $settings->setThumbnailHighDpiQuality($parentSettings->getThumbnailHighDpiQuality());

            $parent->setSettings($settings);

            $this->entityManager->persist($settings);
            $this->entityManager->persist($parent);

            $this->entityManager->flush();
        }

        if (null === $identity) {
            $this->createNewAlbum($mediaCategory, $parent, $parentSettings);

            return true;
        }

        /**
         * @var null|Album $album
         */
        $album = $albumRepository->find($identity->getAdapterIdentifier());

        if (null !== $album) {
            $album->setName($mediaCategory->getName());

            $this->entityManager->persist($album);
            $this->entityManager->flush();
        } else {
            $this->createNewAlbum($mediaCategory, $parent, $parentSettings);
        }

        return true;
    }

    /**
     * @param MediaCategory $mediaCategory
     * @param Album         $parent
     * @param Settings      $parentSettings
     */
    private function createNewAlbum(MediaCategory $mediaCategory, Album $parent, Settings $parentSettings)
    {
        $connection = $this->entityManager->getConnection();

        $query = 'SELECT max(position) FROM s_media_album WHERE parentId = ?';
        $position = $connection->fetchColumn($query, [$parent->getId()]);

        $album = new Album();
        $album->setParent($parent);
        $album->setName($mediaCategory->getName());
        $album->setPosition((int) $position + 1);

        $settings = new Settings();
        $settings->setAlbum($album);
        $settings->setCreateThumbnails($parentSettings->getCreateThumbnails());
        $settings->setThumbnailSize($parentSettings->getThumbnailSize());
        $settings->setIcon('sprite-inbox');
        $settings->setThumbnailHighDpi($parentSettings->isThumbnailHighDpi());
        $settings->setThumbnailQuality($parentSettings->getThumbnailQuality());
        $settings->setThumbnailHighDpiQuality($parentSettings->getThumbnailHighDpiQuality());

        $album->setSettings($settings);

        $this->entityManager->persist($album);
        $this->entityManager->flush();
        $this->entityManager->clear();

        $this->identityService->insert(
            $mediaCategory->getIdentifier(),
            MediaCategory::TYPE,
            (string) $album->getId(),
            ShopwareAdapter::NAME
        );
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Order;

use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Order\Order as OrderApi;
use PlentymarketsAdapter\ResponseParser\Order\OrderResponseParserInterface;
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\Order\Order;

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

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

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

    public function __construct(
        OrderApi $api,
        IdentityServiceInterface $identityService,
        OrderResponseParserInterface $responseParser
    ) {
        $this->api = $api;
        $this->identityService = $identityService;
        $this->responseParser = $responseParser;
    }

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

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

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

        $orderData = $this->api->find($identity->getAdapterIdentifier());

        $order = $this->responseParser->parse($orderData);

        return array_filter($order);
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Category;

use Exception;
use PlentymarketsAdapter\Helper\MediaCategoryHelper;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Media\MediaResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\ConfigService\ConfigServiceInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\IdentityService\Struct\Identity;
use SystemConnector\TransferObject\Category\Category;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Shop\Shop;
use SystemConnector\ValueObject\Attribute\Attribute;
use SystemConnector\ValueObject\Translation\Translation;

class CategoryResponseParser implements CategoryResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var ConfigServiceInterface
     */
    private $configService;

    /**
     * @var MediaResponseParserInterface
     */
    private $mediaResponseParser;

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

    public function __construct(
        IdentityServiceInterface $identityService,
        ConfigServiceInterface $configService,
        MediaResponseParserInterface $mediaResponseParser,
        LoggerInterface $logger
    ) {
        $this->identityService = $identityService;
        $this->configService = $configService;
        $this->mediaResponseParser = $mediaResponseParser;
        $this->logger = $logger;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $entry)
    {
        if (empty($entry['details'])) {
            $this->logger->warning('category without details');

            return [];
        }

        if ($entry['right'] !== 'all') {
            $this->logger->warning('unsupported category rights');

            return [];
        }

        $categoryIdentity = $this->identityService->findOneOrCreate(
            (string) $entry['id'],
            PlentymarketsAdapter::NAME,
            Category::TYPE
        );

        if (null !== $entry['parentCategoryId']) {
            $parentIdentity = $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $entry['parentCategoryId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Category::TYPE,
            ]);

            if (null === $parentIdentity) {
                $this->logger->warning('parent category was not found', ['category' => $categoryIdentity->getObjectIdentifier()]);

                return [];
            }

            $parentCategoryIdentifier = $parentIdentity->getObjectIdentifier();
        } else {
            $parentCategoryIdentifier = null;
        }

        if (empty($entry['clients'])) {
            return [];
        }

        $shopIdentifiers = [];
        foreach ($entry['clients'] as $client) {
            if (empty($client['plentyId'])) {
                continue;
            }

            $identity = $this->getShopIdentity($client['plentyId']);

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

            $shopIdentifiers[] = $identity->getObjectIdentifier();
        }

        foreach ($entry['details'] as $key => $detail) {
            $isDefaultPlentyId = $key === 0;
            $isPlentyIdEnabled = in_array($entry['details'][$key]['plentyId'], array_column($entry['clients'], 'plentyId'));
            if (!$isPlentyIdEnabled && !$isDefaultPlentyId) {
                unset($entry['details'][$key]);

                continue;
            }
        }

        $validDetails = array_values(array_filter($entry['details'], function (array $detail) {
            if (empty($detail['plentyId'])) {
                return false;
            }

            $identity = $this->getShopIdentity($detail['plentyId']);

            return !(null === $identity);
        }));

        if (empty($validDetails)) {
            $validDetails = $entry['details'];
        }

        $result = [];

        $category = new Category();
        $category->setIdentifier($categoryIdentity->getObjectIdentifier());
        $category->setParentIdentifier($parentCategoryIdentifier);
        $category->setShopIdentifiers($shopIdentifiers);
        $category->setImageIdentifiers($this->getImages($validDetails['0'], $result));
        $category->setName($validDetails['0']['name']);
        $category->setActive(true);
        $category->setPosition((int) $validDetails['0']['position']);
        $category->setDescription($validDetails['0']['shortDescription']);
        $category->setLongDescription($validDetails['0']['description']);
        $category->setMetaTitle($validDetails['0']['metaTitle']);
        $category->setMetaDescription($validDetails['0']['metaDescription']);
        $category->setMetaKeywords($validDetails['0']['metaKeywords']);
        $category->setMetaRobots($this->getMetaRobots($validDetails['0']['metaRobots']));
        $category->setTranslations($this->getTranslations($validDetails, $result));
        $category->setAttributes($this->getAttributes($validDetails));

        return array_merge($result, [$category]);
    }

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

        if (null === $identity) {
            $this->logger->notice('shop not found', ['shop' => $plentyId]);

            return null;
        }

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

        if (!$isMappedIdentity) {
            return null;
        }

        return $identity;
    }

    /**
     * @param array $detail
     * @param array $result
     *
     * @return array
     */
    private function getImages(array $detail, &$result)
    {
        $imageIdentifiers = [];

        $images = [];

        if (!empty($detail['imagePath'])) {
            $images[] = $detail['imagePath'];
        }

        if (!empty($detail['image2Path'])) {
            $images[] = $detail['image2Path'];
        }

        foreach ($images as $image) {
            try {
                $media = $this->mediaResponseParser->parse([
                    'mediaCategory' => MediaCategoryHelper::CATEGORY,
                    'id' => sha1(json_encode($image)),
                    'link' => $this->getBaseUrl() . 'documents/' . $image,
                    'name' => $detail['name'],
                    'alternateName' => $detail['name'],
                ]);

                $result[$media->getIdentifier()] = $media;

                $imageIdentifiers[] = $media->getIdentifier();
            } catch (Exception $exception) {
                $this->logger->notice('error while processing category image', ['name' => $detail['name']]);
            }
        }

        return $imageIdentifiers;
    }

    /**
     * @return string
     */
    private function getBaseUrl()
    {
        $parts = parse_url($this->configService->get('rest_url'));

        return sprintf('%s://%s/', $parts['scheme'], $parts['host']);
    }

    /**
     * @param string $metaRobots
     *
     * @return string
     */
    private function getMetaRobots($metaRobots)
    {
        $robotsMap = [
            'ALL' => 'INDEX, FOLLOW',
            'INDEX' => 'INDEX, FOLLOW',
            'NOFOLLOW' => 'INDEX, NOFOLLOW',
            'NOINDEX' => 'NOINDEX, FOLLOW',
            'NOINDEX, NOFOLLOW' => 'NOINDEX, NOFOLLOW',
        ];

        if (array_key_exists(strtoupper($metaRobots), $robotsMap)) {
            return $robotsMap[$metaRobots];
        }

        return 'INDEX, FOLLOW';
    }

    /**
     * @param array $details
     * @param array $result
     *
     * @return Translation[]
     */
    private function getTranslations(array $details, &$result)
    {
        $translations = [];

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

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

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'imageIdentifiers',
                'value' => $this->getImages($detail, $result),
            ]);

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

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'description',
                'value' => $detail['shortDescription'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'longDescription',
                'value' => $detail['description'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaTitle',
                'value' => $detail['metaTitle'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaDescription',
                'value' => $detail['metaDescription'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaKeywords',
                'value' => $detail['metaKeywords'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaRobots',
                'value' => $this->getMetaRobots($detail['metaRobots']),
            ]);

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

        return $translations;
    }

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

        $attributes[] = $this->getSecondCategoryDescriptionAsAttribute($details);

        return $attributes;
    }

    /**
     * @param array $categoryDetails
     *
     * @return Attribute
     */
    private function getSecondCategoryDescriptionAsAttribute($categoryDetails)
    {
        $translations = [];

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

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

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

        $attribute = new Attribute();
        $attribute->setKey('secondCategoryDescription');
        $attribute->setValue((string) $categoryDetails[0]['description2']);
        $attribute->setTranslations($translations);

        return $attribute;
    }
}


<?php

namespace PlentyConnector\Components\AmazonPay\Plentymarkets;

use PlentyConnector\Components\AmazonPay\PaymentData\AmazonPayPaymentData;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\Order\Order;
use SystemConnector\TransferObject\Payment\Payment;

class HandleAmazonPayPaymentCommandHandler implements CommandHandlerInterface
{
    /**
     * @var CommandHandlerInterface
     */
    private $parentCommandHandler;

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

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

    public function __construct(
        CommandHandlerInterface $parentCommandHandler,
        ClientInterface $client,
        IdentityServiceInterface $identityService
    ) {
        $this->parentCommandHandler = $parentCommandHandler;
        $this->client = $client;
        $this->identityService = $identityService;
    }

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

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

        /**
         * @var AmazonPayPaymentData $data
         */
        $data = $payment->getPaymentData();

        if (!($data instanceof AmazonPayPaymentData)) {
            return $this->parentCommandHandler->handle($command);
        }

        $orderIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $payment->getOrderIdentifer(),
            'objectType' => Order::TYPE,
            'adapterName' => PlentymarketsAdapter::NAME,
        ]);

        if (null === $orderIdentity) {
            throw new NotFoundException('could not find order for amazon payment handling - ' . $payment->getOrderIdentifer());
        }

        $amazonPayDataParams = [
            'key' => $data->getKey(),
            'order_reference_id' => $data->getTransactionId(),
            'order_id' => $orderIdentity->getAdapterIdentifier(),
        ];

        $this->client->request('POST', 'amazon-shopware-connect', $amazonPayDataParams, null, null, ['foreign' => true]);

        return true;
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\MediaCategory;

use PlentymarketsAdapter\Helper\MediaCategoryHelperInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\MediaCategory\MediaCategoryResponseParserInterface;
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\MediaCategory\MediaCategory;

class FetchMediaCategoryHandler implements QueryHandlerInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var MediaCategoryHelperInterface
     */
    private $mediaCategoryHelper;

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

    public function __construct(
        IdentityServiceInterface $identityService,
        MediaCategoryHelperInterface $mediaCategoryHelper,
        MediaCategoryResponseParserInterface $responseParser
    ) {
        $this->identityService = $identityService;
        $this->mediaCategoryHelper = $mediaCategoryHelper;
        $this->responseParser = $responseParser;
    }

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

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        /**
         * @var FetchTransferObjectQuery $query
         */
        $result = [];

        $identity = $this->identityService->findOneBy([
            'objectIdentifier' => $query->getObjectIdentifier(),
            'objectType' => MediaCategory::TYPE,
            'adapterName' => PlentymarketsAdapter::NAME,
        ]);

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

        $caegories = $this->mediaCategoryHelper->getCategories();

        if (array_key_exists($identity->getAdapterIdentifier(), $caegories)) {
            $result[] = $this->responseParser->parse($caegories[$identity->getAdapterIdentifier()]);
        }

        return array_filter($result);
    }
}


<?php

namespace SystemConnector\Logger;

use Exception;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\TransferObject\TransferObjectInterface;

/**
 * Returns log messages only dump the Command & Exception's class names.
 */
class ClassNameFormatter implements ClassNameFormatterInterface
{
    /**
     * @var LoggerInterface
     */
    private $logger;

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

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

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

    /**
     * @param LoggerInterface $logger
     * @param string          $commandReceivedLevel
     * @param string          $commandSucceededLevel
     * @param string          $commandFailedLevel
     */
    public function __construct(
        LoggerInterface $logger,
        $commandReceivedLevel = LogLevel::DEBUG,
        $commandSucceededLevel = LogLevel::DEBUG,
        $commandFailedLevel = LogLevel::ERROR
    ) {
        $this->logger = $logger;
        $this->commandReceivedLevel = $commandReceivedLevel;
        $this->commandSucceededLevel = $commandSucceededLevel;
        $this->commandFailedLevel = $commandFailedLevel;
    }

    /**
     * {@inheritdoc}
     */
    public function logCommandReceived($command)
    {
        $message = $this->getRecievedMessage($command);
        $payload = $this->getPayload($command);

        $this->logger->log($this->commandReceivedLevel, $message, $payload);
    }

    /**
     * {@inheritdoc}
     */
    public function logCommandProcessed($command, $returnValue)
    {
        $message = $this->getProcessedMessage($command);
        $payload = $this->getPayload($command);

        $this->logger->log($this->commandSucceededLevel, $message, $payload);
    }

    /**
     * {@inheritdoc}
     */
    public function logCommandFailed($command, Exception $exception)
    {
        $message = $this->getFailedMessage($command);
        $payload = $this->getPayload($command);

        $payload = array_merge($payload, ['exception' => $exception]);

        $this->logger->log(
            $this->commandFailedLevel,
            $message,
            $payload
        );
    }

    /**
     * @param mixed $command
     *
     * @return string
     */
    private function getType($command)
    {
        if ($command instanceof CommandInterface) {
            return 'Command';
        }

        if ($command instanceof QueryInterface) {
            return 'Query';
        }

        return 'Undefined';
    }

    /**
     * @param mixed $command
     *
     * @return string
     */
    private function getClassName($command)
    {
        return substr(strrchr(get_class($command), '\\'), 1);
    }

    /**
     * @param mixed $command
     *
     * @return array
     */
    private function getPayload($command)
    {
        if (!($command instanceof CommandInterface) && !($command instanceof QueryInterface)) {
            return [];
        }

        $payload = $command->toArray();

        return $this->preparePayload($payload);
    }

    /**
     * @param array $payload
     *
     * @return array
     */
    private function preparePayload(array $payload)
    {
        return array_map(function ($payload) {
            if (!($payload instanceof TransferObjectInterface)) {
                return $payload;
            }

            return $payload->getIdentifier();
        }, $payload);
    }

    /**
     * @param mixed $command
     *
     * @return string
     */
    private function getRecievedMessage($command)
    {
        return $this->getType($command) . ' received: ' . $this->getClassName($command);
    }

    /**
     * @param mixed $command
     *
     * @return string
     */
    private function getProcessedMessage($command)
    {
        return $this->getType($command) . ' processed: ' . $this->getClassName($command);
    }

    /**
     * @param mixed $command
     *
     * @return string
     */
    private function getFailedMessage($command)
    {
        return $this->getType($command) . ' failed: ' . $this->getClassName($command);
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Manufacturer;

use Exception;
use PlentymarketsAdapter\Helper\MediaCategoryHelper;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Media\MediaResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Manufacturer\Manufacturer;

class ManufacturerResponseParser implements ManufacturerResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var MediaResponseParserInterface
     */
    private $mediaResponseParser;

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

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

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

        $manufacturer = new Manufacturer();
        $manufacturer->setIdentifier($identity->getObjectIdentifier());
        $manufacturer->setName($entry['name']);

        if (!empty($entry['url'])) {
            $manufacturer->setLink($entry['url']);
        }

        $result = [];

        if (!empty($entry['logo'])) {
            try {
                $media = $this->mediaResponseParser->parse([
                    'mediaCategory' => MediaCategoryHelper::MANUFACTURER,
                    'id' => $entry['id'],
                    'link' => $entry['logo'],
                    'name' => $entry['name'],
                    'alternateName' => $entry['name'],
                ]);

                $manufacturer->setLogoIdentifier($media->getIdentifier());

                $result[] = $media;
            } catch (Exception $exception) {
                $this->logger->notice('error while processing manufacturer logo', [
                    'name' => $entry['name'],
                    'url' => $entry['logo'],
                ]);
            }
        }

        $result[] = $manufacturer;

        return $result;
    }
}