<commit_before><?php
/*
 * This file is part of the Sulu CMS.
 *
 * (c) MASSIVE ART WebServices GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Bundle\Sales\OrderBundle\Order;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Sulu\Bundle\ContactBundle\Entity\Account;
use Sulu\Bundle\ContactBundle\Entity\AccountRepository;
use Sulu\Bundle\ContactBundle\Entity\Address;
use Sulu\Bundle\ContactBundle\Entity\Contact;
use Sulu\Bundle\ProductBundle\Product\ProductManagerInterface;
use Sulu\Bundle\Sales\CoreBundle\Entity\ItemInterface;
use Sulu\Bundle\Sales\CoreBundle\Item\Exception\ItemNotFoundException;
use Sulu\Bundle\Sales\CoreBundle\Item\ItemManager;
use Sulu\Bundle\Sales\CoreBundle\Pricing\GroupedItemsPriceCalculatorInterface;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderActivityLog;
use Sulu\Bundle\Sales\CoreBundle\Entity\OrderAddress;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderInterface;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderRepository;
use Sulu\Bundle\Sales\OrderBundle\Entity\Order as OrderEntity;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderStatus as OrderStatusEntity;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderStatus;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderType;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\MissingOrderAttributeException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderDependencyNotFoundException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderNotFoundException;
use Sulu\Component\Rest\Exception\EntityNotFoundException;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineConcatenationFieldDescriptor;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineFieldDescriptor;
use Sulu\Bundle\Sales\OrderBundle\Api\Order;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineJoinDescriptor;
use Sulu\Component\Security\Authentication\UserRepositoryInterface;
use DateTime;
use Sulu\Component\Persistence\RelationTrait;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class OrderManager
{
    use RelationTrait;

    protected static $orderEntityName = 'SuluSalesOrderBundle:Order';
    protected static $contactEntityName = 'SuluContactBundle:Contact';
    protected static $addressEntityName = 'SuluContactBundle:Address';
    protected static $orderStatusEntityName = 'SuluSalesOrderBundle:OrderStatus';
    protected static $orderTypeEntityName = 'SuluSalesOrderBundle:OrderType';
    protected static $orderTypeTranslationEntityName = 'SuluSalesOrderBundle:OrderTypeTranslation';
    protected static $orderAddressEntityName = 'SuluSalesCoreBundle:OrderAddress';
    protected static $orderStatusTranslationEntityName = 'SuluSalesOrderBundle:OrderStatusTranslation';
    protected static $itemEntityName = 'SuluSalesCoreBundle:Item';
    protected static $termsOfDeliveryEntityName = 'SuluContactExtensionBundle:TermsOfDelivery';
    protected static $termsOfPaymentEntityName = 'SuluContactExtensionBundle:TermsOfPayment';

    private $currentLocale;

    /**
     * @var ObjectManager
     */
    private $em;

    /**
     * @var ItemManager
     */
    private $itemManager;

    /**
     * @var OrderRepository
     */
    private $orderRepository;

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

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

    /**
     * @var UserRepositoryInterface
     */
    private $userRepository;

    /**
     * Describes the fields, which are handled by this controller
     * @var DoctrineFieldDescriptor[]
     */
    private $fieldDescriptors = array();

    /**
     * @var SessionInterface
     */
    protected $session;

    /**
     * @var GroupedItemsPriceCalculatorInterface
     */
    private $priceCalculator;

    /**
     * @var ProductManagerInterface
     */
    private $productManager;

    /**
     * @var OrderFactoryInterface
     */
    private $orderFactory;


    /**
     * @var AccountRepository
     */
    private $accountRepository;

    /**
     * @param ObjectManager $em
     * @param OrderRepository $orderRepository
     * @param UserRepositoryInterface $userRepository
     * @param AccountRepository $accountRepository
     * @param ItemManager $itemManager
     * @param EntityRepository $orderStatusRepository
     * @param EntityRepository $orderTypeRepository
     * @param SessionInterface $session
     * @param GroupedItemsPriceCalculatorInterface $priceCalculator
     * @param ProductManagerInterface $productManager
     * @param OrderFactoryInterface $orderFactory
     */
    public function __construct(
        ObjectManager $em,
        OrderRepository $orderRepository,
        UserRepositoryInterface $userRepository,
        AccountRepository $accountRepository,
        ItemManager $itemManager,
        EntityRepository $orderStatusRepository,
        EntityRepository $orderTypeRepository,
        SessionInterface $session,
        GroupedItemsPriceCalculatorInterface $priceCalculator,
        ProductManagerInterface $productManager,
        OrderFactoryInterface $orderFactory
    ) {
        $this->orderRepository = $orderRepository;
        $this->userRepository = $userRepository;
        $this->accountRepository = $accountRepository;
        $this->em = $em;
        $this->itemManager = $itemManager;
        $this->orderStatusRepository = $orderStatusRepository;
        $this->orderTypeRepository = $orderTypeRepository;
        $this->session = $session;
        $this->priceCalculator = $priceCalculator;
        $this->productManager = $productManager;
        $this->orderFactory = $orderFactory;
    }

    /**
     * Creates a new Order Entity
     *
     * @param array $data The data array, which will be used for setting the orders data
     * @param string $locale Locale
     * @param int $userId Id of the User, which is is saved as creator / changer
     * @param int|null $id If defined, the Order with the given ID will be updated
     * @param int|null $statusId if defined, the status will be set to the given value
     * @param bool $flush Defines if a flush should be performed
     * @throws Exception\OrderNotFoundException
     * @throws Exception\OrderException
     * @return null|Order|\Sulu\Bundle\Sales\OrderBundle\Entity\Order
     */
    public function save(
        array $data,
        $locale,
        $userId = null,
        $id = null,
        $statusId = null,
        $flush = true
    ) {
        $isNewOrder = !$id;

        if (!$isNewOrder) {
            $order = $this->findByIdAndLocale($id, $locale);

            if (!$order) {
                throw new OrderNotFoundException($id);
            }
        } else {
            $order = $this->orderFactory->createApiEntity($this->orderFactory->createEntity(), $locale);
            $this->checkRequiredData($data, $id === null);
        }

        $user = $userId ? $this->userRepository->findUserById($userId) : null;

        $order->setOrderNumber($this->getProperty($data, 'orderNumber', $order->getOrderNumber()));
        $order->setCurrencyCode($this->getProperty($data, 'currencyCode', $order->getCurrencyCode()));
        $order->setCostCentre($this->getProperty($data, 'costCentre', $order->getCostCentre()));
        $order->setCommission($this->getProperty($data, 'commission', $order->getCommission()));
        $order->setTaxfree($this->getProperty($data, 'taxfree', $order->getTaxfree()));

        // set type of order (if set)
        $this->setOrderType($data, $order);

        $this->setDate(
            $data,
            'desiredDeliveryDate',
            $order->getDesiredDeliveryDate(),
            array($order, 'setDesiredDeliveryDate')
        );
        $this->setDate(
            $data,
            'orderDate',
            $order->getOrderDate(),
            array($order, 'setOrderDate')
        );

        $this->setTermsOfDelivery($data, $order);
        $this->setTermsOfPayment($data, $order);

        $account = $this->setAccount($data, $order);

        // set session - id
        $sessionId = $this->session->getId();
        $order->setSessionId($sessionId);

        // add contact
        $contact = $this->addContactRelation(
            $data,
            'customerContact',
            function ($contact) use ($order) {
                $order->setCustomerContact($contact);
            }
        );

        // add contact
        $this->addContactRelation(
            $data,
            'responsibleContact',
            function ($contact) use ($order) {
                $order->setResponsibleContact($contact);
            }
        );

        // create order (POST)
        if ($order->getId() == null) {
            $order->setCreated(new DateTime());
            $order->setCreator($user);
            $this->em->persist($order->getEntity());

            // set status to created if not defined
            if ($statusId === null) {
                $statusId = OrderStatus::STATUS_CREATED;
            }

            // create OrderAddress
            $deliveryAddress = new OrderAddress();
            $invoiceAddress = new OrderAddress();
            // persist entities
            $this->em->persist($deliveryAddress);
            $this->em->persist($invoiceAddress);
            // assign to order
            $order->setDeliveryAddress($deliveryAddress);
            $order->setInvoiceAddress($invoiceAddress);
        }

        // set order status
        if ($statusId !== null) {
            $this->convertStatus($order, $statusId);
        }

        // if not new and contact is not set, use old contact
        if (!$isNewOrder && !$contact) {
            $contact = $order->getEntity()->getCustomerContact();
        }
        $contactFullName = null;
        if ($contact) {
            $contactFullName = $contact->getFullName();
        }

        if (isset($data['invoiceAddress'])) {
            // set customer name to account if set, otherwise to contact
            $contactFullName = $this->getContactData($data['invoiceAddress'], $contact)['fullName'];

            // set OrderAddress data
            $this->setOrderAddress(
                $order->getEntity()->getInvoiceAddress(),
                $data['invoiceAddress'],
                $contact,
                $account
            );
        }
        if (isset($data['deliveryAddress'])) {
            $this->setOrderAddress(
                $order->getEntity()->getDeliveryAddress(),
                $data['deliveryAddress'],
                $contact,
                $account
            );
        }

        // set customer name
        $customerName = $account !== null ? $account->getName() : $contactFullName;
        if ($customerName) {
            $order->setCustomerName($customerName);
        }

        // handle items
        if (!$this->processItems($data, $order, $locale, $userId)) {
            throw new OrderException('Error while processing items');
        }

        $order->setChanged(new DateTime());
        $order->setChanger($user);

        $this->updateApiEntity($order, $locale);

        if ($flush) {
            $this->em->flush();
        }

        return $order;
    }

    /**
     * @param Order $apiOrder
     */
    public function updateApiEntity(Order $apiOrder, $locale)
    {
        $items = $apiOrder->getItems();

        // perform price calucaltion
        $prices = $supplierItems = null;
        $totalPrice = $this->priceCalculator->calculate($items, $prices, $supplierItems, true);

        if ($supplierItems) {
            $supplierItems = array_values($supplierItems);
            // update media api entities
            $this->createMediaForSupplierItems($supplierItems, $locale);
            // set grouped items
            $apiOrder->setSupplierItems($supplierItems);
        }

        $this->createMediaForItems($items, $locale);

        // check if any price in cart has changed<commit_msg>bugfix: passing correct parameters to price calculation method of order-prices<commit_after><?php
/*
 * This file is part of the Sulu CMS.
 *
 * (c) MASSIVE ART WebServices GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Bundle\Sales\OrderBundle\Order;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Sulu\Bundle\ContactBundle\Entity\Account;
use Sulu\Bundle\ContactBundle\Entity\AccountRepository;
use Sulu\Bundle\ContactBundle\Entity\Address;
use Sulu\Bundle\ContactBundle\Entity\Contact;
use Sulu\Bundle\ProductBundle\Product\ProductManagerInterface;
use Sulu\Bundle\Sales\CoreBundle\Entity\ItemInterface;
use Sulu\Bundle\Sales\CoreBundle\Item\Exception\ItemNotFoundException;
use Sulu\Bundle\Sales\CoreBundle\Item\ItemManager;
use Sulu\Bundle\Sales\CoreBundle\Pricing\GroupedItemsPriceCalculatorInterface;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderActivityLog;
use Sulu\Bundle\Sales\CoreBundle\Entity\OrderAddress;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderInterface;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderRepository;
use Sulu\Bundle\Sales\OrderBundle\Entity\Order as OrderEntity;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderStatus as OrderStatusEntity;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderStatus;
use Sulu\Bundle\Sales\OrderBundle\Entity\OrderType;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\MissingOrderAttributeException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderDependencyNotFoundException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderException;
use Sulu\Bundle\Sales\OrderBundle\Order\Exception\OrderNotFoundException;
use Sulu\Component\Rest\Exception\EntityNotFoundException;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineConcatenationFieldDescriptor;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineFieldDescriptor;
use Sulu\Bundle\Sales\OrderBundle\Api\Order;
use Sulu\Component\Rest\ListBuilder\Doctrine\FieldDescriptor\DoctrineJoinDescriptor;
use Sulu\Component\Security\Authentication\UserRepositoryInterface;
use DateTime;
use Sulu\Component\Persistence\RelationTrait;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class OrderManager
{
    use RelationTrait;

    protected static $orderEntityName = 'SuluSalesOrderBundle:Order';
    protected static $contactEntityName = 'SuluContactBundle:Contact';
    protected static $addressEntityName = 'SuluContactBundle:Address';
    protected static $orderStatusEntityName = 'SuluSalesOrderBundle:OrderStatus';
    protected static $orderTypeEntityName = 'SuluSalesOrderBundle:OrderType';
    protected static $orderTypeTranslationEntityName = 'SuluSalesOrderBundle:OrderTypeTranslation';
    protected static $orderAddressEntityName = 'SuluSalesCoreBundle:OrderAddress';
    protected static $orderStatusTranslationEntityName = 'SuluSalesOrderBundle:OrderStatusTranslation';
    protected static $itemEntityName = 'SuluSalesCoreBundle:Item';
    protected static $termsOfDeliveryEntityName = 'SuluContactExtensionBundle:TermsOfDelivery';
    protected static $termsOfPaymentEntityName = 'SuluContactExtensionBundle:TermsOfPayment';

    private $currentLocale;

    /**
     * @var ObjectManager
     */
    private $em;

    /**
     * @var ItemManager
     */
    private $itemManager;

    /**
     * @var OrderRepository
     */
    private $orderRepository;

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

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

    /**
     * @var UserRepositoryInterface
     */
    private $userRepository;

    /**
     * Describes the fields, which are handled by this controller
     * @var DoctrineFieldDescriptor[]
     */
    private $fieldDescriptors = array();

    /**
     * @var SessionInterface
     */
    protected $session;

    /**
     * @var GroupedItemsPriceCalculatorInterface
     */
    private $priceCalculator;

    /**
     * @var ProductManagerInterface
     */
    private $productManager;

    /**
     * @var OrderFactoryInterface
     */
    private $orderFactory;


    /**
     * @var AccountRepository
     */
    private $accountRepository;

    /**
     * @param ObjectManager $em
     * @param OrderRepository $orderRepository
     * @param UserRepositoryInterface $userRepository
     * @param AccountRepository $accountRepository
     * @param ItemManager $itemManager
     * @param EntityRepository $orderStatusRepository
     * @param EntityRepository $orderTypeRepository
     * @param SessionInterface $session
     * @param GroupedItemsPriceCalculatorInterface $priceCalculator
     * @param ProductManagerInterface $productManager
     * @param OrderFactoryInterface $orderFactory
     */
    public function __construct(
        ObjectManager $em,
        OrderRepository $orderRepository,
        UserRepositoryInterface $userRepository,
        AccountRepository $accountRepository,
        ItemManager $itemManager,
        EntityRepository $orderStatusRepository,
        EntityRepository $orderTypeRepository,
        SessionInterface $session,
        GroupedItemsPriceCalculatorInterface $priceCalculator,
        ProductManagerInterface $productManager,
        OrderFactoryInterface $orderFactory
    ) {
        $this->orderRepository = $orderRepository;
        $this->userRepository = $userRepository;
        $this->accountRepository = $accountRepository;
        $this->em = $em;
        $this->itemManager = $itemManager;
        $this->orderStatusRepository = $orderStatusRepository;
        $this->orderTypeRepository = $orderTypeRepository;
        $this->session = $session;
        $this->priceCalculator = $priceCalculator;
        $this->productManager = $productManager;
        $this->orderFactory = $orderFactory;
    }

    /**
     * Creates a new Order Entity
     *
     * @param array $data The data array, which will be used for setting the orders data
     * @param string $locale Locale
     * @param int $userId Id of the User, which is is saved as creator / changer
     * @param int|null $id If defined, the Order with the given ID will be updated
     * @param int|null $statusId if defined, the status will be set to the given value
     * @param bool $flush Defines if a flush should be performed
     * @throws Exception\OrderNotFoundException
     * @throws Exception\OrderException
     * @return null|Order|\Sulu\Bundle\Sales\OrderBundle\Entity\Order
     */
    public function save(
        array $data,
        $locale,
        $userId = null,
        $id = null,
        $statusId = null,
        $flush = true
    ) {
        $isNewOrder = !$id;

        if (!$isNewOrder) {
            $order = $this->findByIdAndLocale($id, $locale);

            if (!$order) {
                throw new OrderNotFoundException($id);
            }
        } else {
            $order = $this->orderFactory->createApiEntity($this->orderFactory->createEntity(), $locale);
            $this->checkRequiredData($data, $id === null);
        }

        $user = $userId ? $this->userRepository->findUserById($userId) : null;

        $order->setOrderNumber($this->getProperty($data, 'orderNumber', $order->getOrderNumber()));
        $order->setCurrencyCode($this->getProperty($data, 'currencyCode', $order->getCurrencyCode()));
        $order->setCostCentre($this->getProperty($data, 'costCentre', $order->getCostCentre()));
        $order->setCommission($this->getProperty($data, 'commission', $order->getCommission()));
        $order->setTaxfree($this->getProperty($data, 'taxfree', $order->getTaxfree()));

        // set type of order (if set)
        $this->setOrderType($data, $order);

        $this->setDate(
            $data,
            'desiredDeliveryDate',
            $order->getDesiredDeliveryDate(),
            array($order, 'setDesiredDeliveryDate')
        );
        $this->setDate(
            $data,
            'orderDate',
            $order->getOrderDate(),
            array($order, 'setOrderDate')
        );

        $this->setTermsOfDelivery($data, $order);
        $this->setTermsOfPayment($data, $order);

        $account = $this->setAccount($data, $order);

        // set session - id
        $sessionId = $this->session->getId();
        $order->setSessionId($sessionId);

        // add contact
        $contact = $this->addContactRelation(
            $data,
            'customerContact',
            function ($contact) use ($order) {
                $order->setCustomerContact($contact);
            }
        );

        // add contact
        $this->addContactRelation(
            $data,
            'responsibleContact',
            function ($contact) use ($order) {
                $order->setResponsibleContact($contact);
            }
        );

        // create order (POST)
        if ($order->getId() == null) {
            $order->setCreated(new DateTime());
            $order->setCreator($user);
            $this->em->persist($order->getEntity());

            // set status to created if not defined
            if ($statusId === null) {
                $statusId = OrderStatus::STATUS_CREATED;
            }

            // create OrderAddress
            $deliveryAddress = new OrderAddress();
            $invoiceAddress = new OrderAddress();
            // persist entities
            $this->em->persist($deliveryAddress);
            $this->em->persist($invoiceAddress);
            // assign to order
            $order->setDeliveryAddress($deliveryAddress);
            $order->setInvoiceAddress($invoiceAddress);
        }

        // set order status
        if ($statusId !== null) {
            $this->convertStatus($order, $statusId);
        }

        // if not new and contact is not set, use old contact
        if (!$isNewOrder && !$contact) {
            $contact = $order->getEntity()->getCustomerContact();
        }
        $contactFullName = null;
        if ($contact) {
            $contactFullName = $contact->getFullName();
        }

        if (isset($data['invoiceAddress'])) {
            // set customer name to account if set, otherwise to contact
            $contactFullName = $this->getContactData($data['invoiceAddress'], $contact)['fullName'];

            // set OrderAddress data
            $this->setOrderAddress(
                $order->getEntity()->getInvoiceAddress(),
                $data['invoiceAddress'],
                $contact,
                $account
            );
        }
        if (isset($data['deliveryAddress'])) {
            $this->setOrderAddress(
                $order->getEntity()->getDeliveryAddress(),
                $data['deliveryAddress'],
                $contact,
                $account
            );
        }

        // set customer name
        $customerName = $account !== null ? $account->getName() : $contactFullName;
        if ($customerName) {
            $order->setCustomerName($customerName);
        }

        // handle items
        if (!$this->processItems($data, $order, $locale, $userId)) {
            throw new OrderException('Error while processing items');
        }

        $order->setChanged(new DateTime());
        $order->setChanger($user);

        $this->updateApiEntity($order, $locale);

        if ($flush) {
            $this->em->flush();
        }

        return $order;
    }

    /**
     * @param Order $apiOrder
     */
    public function updateApiEntity(Order $apiOrder, $locale)
    {
        $items = $apiOrder->getItems();

        // perform price calucaltion
        $prices = $supplierItems = null;
        $totalPrice = $this->priceCalculator->calculate($items, $prices, $supplierItems);

        if ($supplierItems) {
            $supplierItems = array_values($supplierItems);
            // update media api entities
            $this->createMediaForSupplierItems($supplierItems, $locale);
            // set grouped items
            $apiOrder->setSupplierItems($supplierItems);
        }

        $this->createMediaForItems($items, $locale);

        // check if any price in cart has changed
{'fraction_non_alphanumeric': 0.11325479249620295, 'fraction_numerical': 0.0, 'mean_word_length': 2.7239376337511465, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 175, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5135593', 'n_tokens_mistral': 6517, 'n_tokens_neox': 6344, 'n_words': 1327}