src/WebBundle/Service/OrderService.php line 2436

Open in your IDE?
  1. <?php
  2. namespace WebBundle\Service;
  3. use DateTime;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Doctrine\ORM\NonUniqueResultException;
  6. use Doctrine\ORM\NoResultException;
  7. use Doctrine\ORM\ORMException;
  8. use Exception;
  9. use FlexApp\Enum\OrderJobNameEnum;
  10. use FlexApp\Exceptions\EmailAlreadyRegisteredException;
  11. use FlexApp\Service\LogsManager;
  12. use FlexApp\Service\Order\OrderUserService;
  13. use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
  14. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  15. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  16. use Psr\Log\LoggerInterface;
  17. use SoapFault;
  18. use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
  19. use Symfony\Component\HttpFoundation\RedirectResponse;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  22. use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
  23. use Symfony\Component\Security\Core\Security;
  24. use Twig\Error\LoaderError;
  25. use Twig\Error\RuntimeError;
  26. use Twig\Error\SyntaxError;
  27. use WebBundle\Constant\CookieKeysConstant;
  28. use WebBundle\Entity\Article;
  29. use WebBundle\Entity\BuyOrder;
  30. use WebBundle\Entity\BuyOrderArticle;
  31. use WebBundle\Entity\ListCountry;
  32. use WebBundle\Entity\OrderAddress;
  33. use WebBundle\Entity\OrderPayment;
  34. use WebBundle\Entity\User;
  35. use WebBundle\Enum\BuyOrderLiftEnum;
  36. use WebBundle\Enum\BuyOrderStatusEnum;
  37. use WebBundle\Enum\CountryNumberEnum;
  38. use WebBundle\Enum\DeliveryTypeTimeEnum;
  39. use WebBundle\Enum\MeasureOrderEnum;
  40. use WebBundle\Enum\PaymentStatusEnum;
  41. use WebBundle\Enum\PaymentTypeEnum;
  42. use WebBundle\Enum\ReasonForResetOrderEnum;
  43. use WebBundle\Enum\SexEnum;
  44. use WebBundle\Enum\VatTypeEnum;
  45. use WebBundle\Filler\OrderFiller;
  46. use WebBundle\Filler\OrderItemsFiller;
  47. use WebBundle\Helper\App;
  48. use WebBundle\Helper\ConversionHelper;
  49. use WebBundle\Helper\CookieHelper;
  50. use WebBundle\Helper\DateHelper;
  51. use WebBundle\Helper\LocaleHelper;
  52. use WebBundle\Helper\OrderHelper;
  53. use WebBundle\Helper\OrderItemHelper;
  54. use WebBundle\Helper\OrderPaymentHelper;
  55. use WebBundle\Helper\PriceHelper;
  56. use WebBundle\Helper\StrHelper;
  57. use WebBundle\Helper\UserHelper;
  58. use WebBundle\Repository\ArticleRepository;
  59. use WebBundle\Repository\BuyOrderRepository;
  60. use WebBundle\Repository\CurrencyRateRepository;
  61. use WebBundle\Repository\ListCountryRepository;
  62. use WebBundle\Repository\ListVatRepository;
  63. use WebBundle\Repository\UserRepository;
  64. use WebBundle\Scenario\ChangeOrderStatusScenario;
  65. use WebBundle\Serializer\Normalizer\OrderListNormalizer;
  66. class OrderService extends ExtendService
  67. {
  68.     /** @required */
  69.     public OrderFiller $orderFiller;
  70.     /** @required */
  71.     public OrderItemsFiller $orderItemsFiller;
  72.     /** @required */
  73.     public CurrencyRateRepository $currencyRateRepository;
  74.     /** @required */
  75.     public ListVatRepository $listVatRepository;
  76.     /** @required */
  77.     public WarehouseService $warehouseService;
  78.     /** @required */
  79.     public BuyOrderManagerService $buyOrderManagerService;
  80.     /** @required */
  81.     public BuyOrderDeliveryService $buyOrderDeliveryService;
  82.     /** @required */
  83.     public PortalService $portalService;
  84.     /** @required */
  85.     public TranslationService $translationService;
  86.     /** @required */
  87.     public UserService $userService;
  88.     /** @required */
  89.     public Security $security;
  90.     private OrderAddressService $addressService;
  91.     private OrderHistoryService $historyService;
  92.     private OrderPaymentService $paymentService;
  93.     private OrderItemService $itemService;
  94.     private OrderOneCService $oneCService;
  95.     private SampleService $sampleService;
  96.     private OrderUserService $orderUserService;
  97.     private AuthorizationChecker $authorizationChecker;
  98.     protected bool $asConsole false;
  99.     private ?BuyOrder $oldOrder null;
  100.     private OrderListNormalizer $orderListNormalizer;
  101.     private BuyOrderRepository $buyOrderRepository;
  102.     private ArticleRepository $articleRepository;
  103.     private UserRepository $userRepo;
  104.     private ListCountryRepository $listCountryRepository;
  105.     private EntityManagerInterface $em;
  106.     private LogsManager $logsManager;
  107.     private LoggerInterface $logger;
  108.     // Метка зеркала от которого отправлен запрос в 1С
  109.     protected int $mirror 0;
  110.     protected string $token;
  111.     // Если этот ключ равен true, то в 1С будет послан сигнал, что срок должен стать обычным, а не минутным
  112.     public bool $timer false;
  113.     private bool $needRunScenario true;
  114.     public function __construct(
  115.         AuthorizationChecker $authorizationChecker,
  116.         OrderAddressService $addressService,
  117.         OrderHistoryService $historyService,
  118.         OrderPaymentService $paymentService,
  119.         OrderItemService $itemService,
  120.         OrderOneCService $oneCService,
  121.         SampleService $sampleService,
  122.         OrderUserService $orderUserService,
  123.         EntityManagerInterface $em,
  124.         OrderListNormalizer $orderListNormalizer,
  125.         BuyOrderRepository $repo,
  126.         ArticleRepository $articleRepository,
  127.         UserRepository $userRepository,
  128.         ListCountryRepository $listCountryRepository,
  129.         LoggerInterface $logger,
  130.         LogsManager $logsManager
  131.     ) {
  132.         $this->asConsole false;
  133.         $this->authorizationChecker $authorizationChecker;
  134.         $this->addressService $addressService;
  135.         $this->historyService $historyService;
  136.         $this->paymentService $paymentService;
  137.         $this->itemService $itemService;
  138.         $this->oneCService $oneCService;
  139.         $this->sampleService $sampleService;
  140.         $this->orderUserService $orderUserService;
  141.         $this->em $em;
  142.         $mirror 1;
  143.         if (App::getContainer() && App::getContainer()->hasParameter('mirror')) {
  144.             $mirror App::getParameter('mirror');
  145.         }
  146.         $this->mirror $mirror;
  147.         $this->orderListNormalizer $orderListNormalizer;
  148.         $this->buyOrderRepository $repo;
  149.         $this->articleRepository $articleRepository;
  150.         $this->userRepo $userRepository;
  151.         $this->listCountryRepository $listCountryRepository;
  152.         $this->logger $logger;
  153.         $this->logsManager $logsManager;
  154.         $this->token UserHelper::getInstance()->getToken();
  155.     }
  156.     /**
  157.      * @param string $name - название лога
  158.      * @param string $hash - идентификатор заказа
  159.      * @param string|array $data - данные
  160.      * @param string $s - доп метка лога
  161.      * @throws Exception
  162.      */
  163.     private function log(string $namestring $hash$datastring $s '')
  164.     {
  165.         // Попытка распарсить вложенный JSON
  166.         if (
  167.             is_array($data)
  168.             && isset($data['value'])
  169.             && is_string($data['value'])
  170.         ) {
  171.             $decoded json_decode($data['value'], true);
  172.             if (json_last_error() === JSON_ERROR_NONE) {
  173.                 $data $decoded;
  174.             }
  175.         }
  176.         // Форматируем JSON красиво для логов
  177.         $prettyJson is_array($data)
  178.             ? json_encode($dataJSON_PRETTY_PRINT JSON_UNESCAPED_UNICODE JSON_UNESCAPED_SLASHES)
  179.             : (string) $data;
  180.         // Отправляем в Rabbit
  181.         $this->logsManager->logInfo(
  182.             [
  183.                 'action' => StrHelper::toUpper($name),
  184.                 'hash' => $hash,
  185.                 'message' => $s,
  186.                 'params' => $prettyJson,
  187.             ],
  188.             OrderJobNameEnum::ORDER_SERVICE_LOG
  189.         );
  190.         $this->logger->error(
  191.             join(' ', [
  192.                 StrHelper::toUpper($name),
  193.                 '[' $hash ']',
  194.                 $s ':',
  195.                 (is_array($data) ? json_encode($data) : $data),
  196.             ])
  197.         );
  198.     }
  199.     public function setAsConsole(bool $asConsole): OrderService
  200.     {
  201.         $this->asConsole $asConsole;
  202.         return $this;
  203.     }
  204.     /**
  205.      * @param string|null $token
  206.      * @return array|null
  207.      * @throws Exception
  208.      */
  209.     public function getOrders(?string $token null): ?array
  210.     {
  211.         $token $token ?? $this->token;
  212.         return $this->buyOrderRepository->getBuyOrders(['token' => $token]);
  213.     }
  214.     /**
  215.      * @param ?string $token
  216.      * @return ?array
  217.      * @throws Exception
  218.      */
  219.     public function getListOrder(?string $token): ?array
  220.     {
  221.         $token $token ?? $this->token;
  222.         $list = [];
  223.         foreach ($this->buyOrderRepository->getBuyOrders(['token' => $token]) as $order) {
  224.             $list[] = $this->orderListNormalizer->normalize($order);
  225.         }
  226.         return $list;
  227.     }
  228.     /**
  229.      * Создание заказа
  230.      *
  231.      * @param string $token
  232.      * @param ?string $name
  233.      * @param ?bool $listNormalize
  234.      * @return BuyOrder|array
  235.      * @throws Exception
  236.      */
  237.     public function create(string $token, ?string $name null, ?bool $listNormalize false)
  238.     {
  239.         $order = new BuyOrder();
  240.         $order->setCreateDate(new DateTime());
  241.         $order->setUpdateDate(new DateTime());
  242.         $name $this->getNameOrder($token, ($name ?: '%orderName%'));
  243.         $order->setHash(md5($token $name date('YmdHis')));
  244.         $order->setStep(1);
  245.         $order->setDeliveryType(0);
  246.         $order->setDeliveryStatus(false);
  247.         $order->setName($nametrue);
  248.         $order->setStatus(BuyOrderStatusEnum::REQUEST_DELIVERY);
  249.         $order->setMirror($this->mirror);
  250.         $order->setToken($token);
  251.         $order->setLift(2);
  252.         $order->setLocale(App::getCurLocale());
  253.         $order->setPayType(PaymentTypeEnum::BANK_TRANSFER);
  254.         $order->setEmailSend(true);
  255.         $order->setAddressEqual(true);
  256.         // todo убрать после перехода на заказ 2.0
  257.         $country App::getCountryList()[App::getCurCountry()] ?? ['id' => 40];
  258.         /** @var ListCountry $countryEntity */
  259.         $countryEntity $this->em->getReference(ListCountry::class, $country['id']);
  260.         $order->setDeliveryCountry($countryEntity);
  261.         //проверяем существование пользователя
  262.         $user $this->userRepo->getUserByToken($token);
  263.         if ($user) {
  264.             $order->setUser($user);
  265.             // если сотрудник, то по умолчанию ставим ключ тестового заказа
  266.             if (UserHelper::isEmployee($order->getEmail())) {
  267.                 $order->setMarkerTest(true);
  268.             }
  269.         }
  270.         // ищем адреса пользователя сначала без создания
  271.         $address $this->addressService->getDefaultAddress($order->getToken(), false$user);
  272.         if (! empty($address) && $address->getId() != $country['id']) {
  273.             $address null;
  274.         }
  275.         // Не нашли адрес, пробуем поискать среди старых заказов
  276.         if (!$address) {
  277.             /** @var BuyOrder $oldOrder */
  278.             $oldOrder $this->buyOrderRepository->getBuyOrderWithAddress(
  279.                 $order->getToken(),
  280.                 true,
  281.                 App::getCurLocale() == 'it'
  282.             );
  283.             if ($oldOrder) {
  284.                 $address $this->addressService->getAddressFromOrder($oldOrder);
  285.             }
  286.         }
  287.         // если нет адреса, создаем адрес для заказа
  288.         if (!$address) {
  289.             $address $this->addressService->create($order->getToken(), $user);
  290.         }
  291.         $order->setAddressRecipient($address);
  292.         // todo временная установка Delivery данных
  293.         if ($address->getSex()) {
  294.             $order->setSex($address->getSex());
  295.         }
  296.         if ($address->getName()) {
  297.             $order->setClientName($this->addressService->clear($address->getName()));
  298.         }
  299.         if ($address->getSurname()) {
  300.             $order->setClientSurname($this->addressService->clear($address->getSurname()));
  301.         }
  302.         if ($address->getEmail()) {
  303.             $order->setEmail($address->getEmail());
  304.         }
  305.         if ($address->getZipCode()) {
  306.             $order->setDeliveryIndex($address->getZipCode());
  307.         }
  308.         if ($address->getCity()) {
  309.             $order->setDeliveryCity($address->getCity());
  310.         }
  311.         if ($address->getRegion()) {
  312.             $order->setDeliveryRegion($address->getRegion());
  313.         }
  314.         if ($address->getStreet()) {
  315.             $order->setDeliveryAddress($address->getStreet());
  316.         }
  317.         if ($address->getBuilding()) {
  318.             $order->setDeliveryBuilding($address->getBuilding());
  319.         }
  320.         if ($address->getApartmentOffice()) {
  321.             $order->setDeliveryApartmentOffice($address->getApartmentOffice());
  322.         }
  323.         if ($address->getCompanyName()) {
  324.             $order->setCompanyName($this->addressService->clear($address->getCompanyName()));
  325.         }
  326.         if ($address->getVatType()) {
  327.             $order->setVatType($address->getVatType());
  328.         }
  329.         if ($address->getVatNumber()) {
  330.             $order->setVatNumber($address->getVatNumber());
  331.         }
  332.         if ($address->getTaxNumber()) {
  333.             $order->setTaxNumber($address->getTaxNumber());
  334.         }
  335.         if ($address->getPhoneCode()) {
  336.             $order->setDeliveryPhoneCode($address->getPhoneCode());
  337.         }
  338.         if ($address->getPhone()) {
  339.             $order->setDeliveryPhone($address->getPhone());
  340.         }
  341.         if ($address->getCountry()) {
  342.             $order->setDeliveryCountry($address->getCountry());
  343.         }
  344.         if (!$order->getDeliveryPhoneCode()) {
  345.             if (!$address->getCountry()) {
  346.                 $countryEntity App::getCurCountryEntity();
  347.                 $address->setCountry($countryEntity);
  348.             }
  349.             $order->setDeliveryPhoneCode($address->getCountry()->getPhoneCode());
  350.         }
  351.         $currency CookieHelper::get(CookieKeysConstant::CURRENCY);
  352.         if (!in_array($currencyLocaleHelper::getCurrencyList($address->getCountry()->getCode()))) {
  353.             $currency LocaleHelper::getCur(['country' => $address->getCountry()->getCode()]);
  354.         }
  355.         $order->setCurrency($currency);
  356.         $order->setMeasure(LocaleHelper::getUserMeasure(['country' => $address->getCountry()->getCode()]));
  357.         $order->setVatPercent(OrderHelper::vatPercent($order));
  358.         $this->buyOrderRepository->save($order);
  359.         $this->log('CREATEORDER'$order->getHash(), OrderHelper::arrayData($order));
  360.         return $listNormalize $this->orderListNormalizer->normalize($order) : $order;
  361.     }
  362.     /**
  363.      * @throws Exception
  364.      */
  365.     public function editName(string $hashstring $name): array
  366.     {
  367.         return $this->orderListNormalizer->normalize($this->changeName($hash$name));
  368.     }
  369.     /**
  370.      * Удаление заказа
  371.      *
  372.      * @param string $hash
  373.      * @return array
  374.      * @throws Exception
  375.      */
  376.     public function delete(string $hash): array
  377.     {
  378.         $order $this->getOrderObj($hashtrue);
  379.         $token $order->getToken();
  380.         //DelOrder
  381.         if ($token == $this->token && $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
  382.             $this->setStatus($orderBuyOrderStatusEnum::DELETED);
  383.             $this->buyOrderRepository->save($order);
  384.             if ($order->getNumber()) {
  385.                 $this->oneCService->delete($order);
  386.             }
  387.         }
  388.         return [
  389.             'res' => 'ok',
  390.             'count' => count($order->getArticles()),
  391.             'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount(UserHelper::getInstance()->getToken()),
  392.             'projects' => [$this->getProjectData($order)],
  393.         ];
  394.     }
  395.     /**
  396.      * @param string $hash
  397.      * @param bool $debug - если true, будут выведены и технические данные
  398.      * @return array
  399.      * @throws NonUniqueResultException
  400.      * @throws Exception
  401.      */
  402.     public function getHistory(string $hash, ?bool $debug false): array
  403.     {
  404.         $order $this->getOrderObj($hashfalsetrue);
  405.         $order $this->orderData($order, ['history' => true]);
  406.         $history $this->historyService->buildHistory($order$debug);
  407.         if ($history) {
  408.             return $history;
  409.         }
  410.         return [];
  411.     }
  412.     /**
  413.      * Возвращает объект заказ
  414.      *
  415.      * @param string $hash
  416.      * @param bool $full
  417.      * @param bool $oh получать с историей
  418.      * @return BuyOrder
  419.      * @throws NonUniqueResultException|NoResultException
  420.      */
  421.     public function getOrderObj(string $hash, ?bool $full false, ?bool $oh false): BuyOrder
  422.     {
  423.         /** @var BuyOrder $order */
  424.         $order $this->buyOrderRepository->getOrder($hash$full$oh);
  425.         if (!$order) {
  426.             throw new NotFoundHttpException('Order not found. (getOrderObj)');
  427.         }
  428.         // проверяем
  429.         if ($order->getStatus() < BuyOrderStatusEnum::AWAITING_PAYMENT && $order->getStep() != 1) {
  430.             $order->setStep(1);
  431.             $this->buyOrderRepository->save($order);
  432.         }
  433.         return $order;
  434.     }
  435.     /**
  436.      * Возвращает нормализованный заказ
  437.      *
  438.      * @param string $hash
  439.      * @param ?bool $full
  440.      * @return array
  441.      * @throws Exception
  442.      */
  443.     public function getOrder(string $hash, ?bool $full false): array
  444.     {
  445.         $order $this->getOrderObj($hash);
  446.         $token $order->getToken();
  447.         if (
  448.             $token == $this->token
  449.             && $order->getAddressRecipient()
  450.             && $order->getAddressRecipient()->getCountry() == null
  451.         ) {
  452.             $address $this->addressService->getDefaultAddress($token);
  453.             $order->setStep(1);
  454.             $order->setAddressRecipient($address);
  455.         }
  456.         // если плательщик еще не назначен, надо взять его из адреса доставки
  457.         if ($order->getAddressRecipient() && $order->getAddressPayer() == null) {
  458.             $order->setAddressPayer($order->getAddressRecipient());
  459.         }
  460.         //проверим актуальность предложения
  461.         $this->checkingOffer($order);
  462.         $this->buyOrderRepository->save($order);
  463.         return $this->orderData($order, ['full' => $full]);
  464.     }
  465.     /**
  466.      * Формирует массив данных заказа для "ReactJS"
  467.      *
  468.      * @param BuyOrder $buyOrder
  469.      * @param ?array $context
  470.      * @param ?array $errors
  471.      * @return array
  472.      * @throws Exception
  473.      */
  474.     public function orderData(BuyOrder $buyOrder, ?array $context = [], ?array $errors = []): array
  475.     {
  476.         $orderData $this->orderFiller->orderData(
  477.             $buyOrder,
  478.             $this->token,
  479.             $this->isAccessAdminOrder($buyOrder),
  480.             $this->getPickupWarehouses(),
  481.             $context,
  482.             $errors
  483.         );
  484.         $orderData['history'] = $this->historyService->buildHistory($orderData);
  485.         $managerLogin $buyOrder->getBuyOrderManager() ? $buyOrder->getManagerLogin() : null;
  486.         $orderData['consultants'] = $consultants $this->portalService->getConsultData(
  487.             App::getCurLocale(),
  488.             App::getCurCountry(),
  489.             UserHelper::getInstance()->getToken(),
  490.             $managerLogin
  491.         );
  492.         $orderData['consultant'] = $managerLogin
  493.             ? (
  494.                 !empty($consultants[$managerLogin])
  495.                     ? $consultants[$managerLogin]
  496.                     : [
  497.                         'scheduleTime' => [],
  498.                         'email' => $buyOrder->getBuyOrderManager()->getManagerEmail(),
  499.                         'phone' => $buyOrder->getBuyOrderManager()->getManagerPhone(),
  500.                         'daysOfWeek' => [],
  501.                         'userNickName' => $buyOrder->getBuyOrderManager()->getManagerName(),
  502.                         'imagePath' => '',
  503.                     ]
  504.             )
  505.             : null;
  506.         if (!empty($orderData['consultant'])) {
  507.             $orderData['consultants'] = [
  508.                 $managerLogin => $orderData['consultant'],
  509.             ];
  510.         }
  511.         return $orderData;
  512.     }
  513.     /**
  514.      * Меняет имя заказа
  515.      * @throws Exception
  516.      */
  517.     public function changeName(string $hashstring $name): BuyOrder
  518.     {
  519.         $order $this->getOrderObj($hash);
  520.         return $this->changeNameByOrder($order$name);
  521.     }
  522.     public function changeNameByOrder(BuyOrder $orderstring $name): BuyOrder
  523.     {
  524.         if ($order->getToken() == UserHelper::getInstance()->getToken()) {
  525.             $name $this->getNameOrder($order->getToken(), strip_tags($name), $order->getId());
  526.             $order->setName($name);
  527.             $this->buyOrderRepository->save($order);
  528.         }
  529.         return $order;
  530.     }
  531.     /**
  532.      * Изменение данных заказа
  533.      *
  534.      * @param string $hash - идентификатор заказа
  535.      * @param string $json - данные по заказу
  536.      * @return null|BuyOrder
  537.      * @throws Exception
  538.      */
  539.     public function change(string $hashstring $json): ?BuyOrder
  540.     {
  541.         try {
  542.             $order $this->getOrderObj($hash);
  543.         } catch (NotFoundHttpException NoResultException $e) {
  544.             return null;
  545.         }
  546.         return $this->changeByOrder($order$json);
  547.     }
  548.     /**
  549.      * Изменение данных заказа
  550.      *
  551.      * @param BuyOrder $order - заказ
  552.      * @param string $json - данные по заказу
  553.      * @return null|BuyOrder
  554.      * @throws Exception
  555.      */
  556.     public function changeByOrder(BuyOrder $orderstring $json): ?BuyOrder
  557.     {
  558.         $json trim($json);
  559.         // добавим памяти для больших заказов
  560.         ini_set('memory_limit''4096M');
  561.         $this->log('CHANGEORDER'$order->getHash(), $json);
  562.         $data json_decode($jsontrue);
  563.         if (!$this->orderAvailableToChange($data$order)) {
  564.             return null;
  565.         }
  566.         if (isset($data['payment']['type'])) {
  567.             $data['payType'] ??= $data['payment']['type'];
  568.         }
  569.         if ($order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
  570.             if (
  571.                 $order->getStatus() == BuyOrderStatusEnum::PARTIALLY_PAID
  572.                 && $data['payType'] != $order->getPayType()
  573.                 && $this->getOrderBalance($order) != 0
  574.             ) {
  575.                 $order->setPayType($data['payType']);
  576.                 $this->buyOrderRepository->save($order);
  577.                 return $order;
  578.             }
  579.             $this->log('CANT_CHANGE_ORDER_FROM_FRONT_IF_ITS_PAYED'$order->getHash(), $json);
  580.             return null;
  581.         }
  582.         if (
  583.             in_array(
  584.                 ($data['recipient']['country']['id'] ?? 0),
  585.                 [
  586.                     CountryNumberEnum::DENMARK,
  587.                     CountryNumberEnum::NORWAY,
  588.                     CountryNumberEnum::SWITZERLAND,
  589.                     CountryNumberEnum::SWEDEN,
  590.                     CountryNumberEnum::HONG_KONG,
  591.                     CountryNumberEnum::SINGAPORE,
  592.                 ]
  593.             )
  594.             && $order->getPayType() === PaymentTypeEnum::BANK_TRANSFER
  595.             && (
  596.                 empty($data['payment']['type'])
  597.                 || (int)$data['payment']['type'] === PaymentTypeEnum::BANK_TRANSFER
  598.             )
  599.         ) {
  600.             $data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
  601.         }
  602.         //ToDo: Восстановить проверку налогового кода италии после того как будет добавлена соответствующая проверка на фронтенде
  603.         //Проверка налогового кода италии, на случай если удалось обойти проверку на UI-е
  604.         //        if ($data['delivery']['country'] == CountryNumberEnum::ITALY && ! empty($data['taxNumber'])) {
  605.         //            if (! $this->italianTaxCodeService->isValidTaxCode($data['taxNumber'])) {
  606.         //                $errors = json_encode($this->italianTaxCodeService->getErrors());
  607.         //                $this->setError($errors);
  608.         //                $this->log(
  609.         //                    'ITALIAN_TAX_CODE_IS_NOT_VALID',
  610.         //                    $order->getHash(),
  611.         //                    $errors
  612.         //                );
  613.         //                return $order;
  614.         //            }
  615.         //        }
  616.         if (
  617.             isset($data['delivery']['country'])
  618.             && (int) $data['delivery']['country'] === CountryNumberEnum::ES
  619.             && !empty($data['taxNumber'])
  620.             && !$this->isValidTaxNumber($order$data)
  621.         ) {
  622.             return $order;
  623.         }
  624.         if (
  625.             $order->getToken() != $this->token
  626.             && !$this->adminChangeMode()
  627.             && (empty($data['checkHash'])
  628.                 || !OrderHelper::checkMarker($order->getHash(), $data['checkHash'])
  629.             )
  630.         ) {
  631.             return null;
  632.         }
  633.         $this->cloneOrderIfNeeded($order);
  634.         $data $this->oneCService->normalizeData($data);
  635.         // сначала задаем адрес доставки так как дальше плясать от страны надо
  636.         if (!empty($data['recipient'])) {
  637.             $currentCountryId = ($order->getAddressRecipient()
  638.                 && $order->getAddressRecipient()->getCountry()
  639.                 && $order->getAddressRecipient()->getCountry()->getId()
  640.             )
  641.                 ? $order->getAddressRecipient()->getCountry()->getId()
  642.                 : null;
  643.             if (
  644.                 // todo временная залипуха для перехода на новую сущность адресов
  645.                 $order->getDeliveryIndex()
  646.                 && $order->getDeliveryCity()
  647.                 && $order->getDeliveryAddress()
  648.                 && $order->getDeliveryBuilding()
  649.                 && $order->getDeliveryPhone()
  650.                 && $order->getSex()
  651.                 && !$order->getAddressRecipient()
  652.             ) {
  653.                 $orderAddress $this->addressService->getAddressFromOrder($order);
  654.             } else {
  655.                 if (
  656.                     // todo временно для старого заказа
  657.                     $order->getAddressRecipient()
  658.                     && isset($data['recipient']['id'])
  659.                     && $data['recipient']['id'] === 0
  660.                 ) {
  661.                     $orderAddress $this->addressService->create($order->getToken());
  662.                 }
  663.                 $orderAddress $this->addressService->setData(
  664.                     $data['recipient'],
  665.                     $order->getAddressRecipient(),
  666.                     $order->getToken()
  667.                 );
  668.             }
  669.             $countryId $data['recipient']['country']['id'] ?? null;
  670.             if ($countryId && $currentCountryId && $countryId != $currentCountryId) {
  671.                 $order->setVatPercent(null);
  672.             }
  673.             if (
  674.                 isset($data['recipient']['vatType'])
  675.                 && $data['recipient']['vatType'] != $order->getVatType()
  676.             ) {
  677.                 $order->setVatType($data['recipient']['vatType']);
  678.             }
  679.             $order->setAddressRecipient($orderAddress);
  680.             // если гипотетически сменилась страна, прогоним через проверку
  681.             OrderHelper::params($order);
  682.             $this->buyOrderRepository->save($order);
  683.         }
  684.         if (isset($data['addressEqual']) && $order->getAddressEqual() != $data['addressEqual']) {
  685.             $order->setAddressEqual($data['addressEqual']);
  686.         }
  687.         if (empty($data['addressEqual']) && !empty($data['payer'])) {
  688.             $addressPayer null;
  689.             // если клиент выбрал разные адреса плательщика и получателя и в заказе был
  690.             // один и тот же адрес, то надо создать новый для изменения
  691.             if (
  692.                 !$order->getAddressEqual()
  693.                 && $order->getAddressPayer()
  694.                 && $order->getAddressPayer()->getId() != $order->getAddressRecipient()->getId()
  695.             ) {
  696.                 $addressPayer $order->getAddressPayer();
  697.             }
  698.             if ($data['recipient']['id'] == $data['payer']['id']) {
  699.                 unset($data['payer']['id']);
  700.             }
  701.             $data['payer']['addressType'] = OrderAddress::IS_PAYER;
  702.             $orderAddress $this->addressService->setData(
  703.                 $data['payer'],
  704.                 $addressPayer,
  705.                 $order->getToken()
  706.             );
  707.             if (! $orderAddress->getEmail()) {
  708.                 $orderAddress->setEmail($order->getEmail());
  709.             }
  710.             if (! $orderAddress->getEmail()) {
  711.                 $orderAddress->setEmail($order->getEmail());
  712.             }
  713.             $order->setAddressPayer($orderAddress);
  714.         } else {
  715.             $order->setAddressPayer($order->getAddressRecipient());
  716.         }
  717.         $this->buyOrderRepository->save($order);
  718.         $order->setVatPercent(OrderHelper::vatPercent($order));
  719.         $this->orderFiller->fillWarehouseData($order$data);
  720.         if (
  721.             isset($data['confirmed'])
  722.             && $order->getEmail() != 'r.zagorudko@tile.expert'
  723.             && $order->getEmail() != 'zaromeo@yandex.ru'
  724.         ) {
  725.             $this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
  726.         }
  727.         if (isset($data['step']) && in_array($data['step'], [1234])) {
  728.             if ($order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
  729.                 $order->setStep($data['step']);
  730.             } else {
  731.                 $order->setStep(3);
  732.             }
  733.         }
  734.         if (
  735.             isset($data['payment']['type'])
  736.             && $data['payment']['type'] != $order->getPayType()
  737.             && PaymentTypeEnum::isAvailableMethod((int) $data['payment']['type'])
  738.         ) {
  739.             if (
  740.                 OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
  741.                 && $data['payment']['type'] == PaymentTypeEnum::BANK_TRANSFER
  742.             ) {
  743.                 $data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
  744.             }
  745.             $order->setPayType(PaymentTypeEnum::changeOldPayPalToNew((int) $data['payment']['type']));
  746.         }
  747.         $order->setPersonalDataAgree(!empty($data['personalDataAgree']));
  748.         if (isset($data['markerTest']) && $order->getToken() == $this->token) {
  749.             $order->setMarkerTest(!empty($data['markerTest']));
  750.         }
  751.         if (!empty($data['name'])) {
  752.             $name $this->getNameOrder($order->getToken(), strip_tags($data['name']), $order->getId());
  753.             $order->setName($nametrue);
  754.         }
  755.         if (!empty($data['sex'])) {
  756.             $order->setSex(SexEnum::getFemaleIfNotMale((int) $data['sex']));
  757.         }
  758.         $order->setTaxNumber($data['taxNumber'] ?? $order->getTaxNumber());
  759.         if (!empty($data['clientName'])) {
  760.             $order->setClientName($this->addressService->clear($data['clientName']));
  761.         }
  762.         if (!empty($data['clientSurname'])) {
  763.             $order->setClientSurname($this->addressService->clear($data['clientSurname']));
  764.         }
  765.         if (!empty($data['description'])) {
  766.             $order->setDescription($this->addressService->clear($data['description']));
  767.         }
  768.         if (!empty($data['carrierName'])) {
  769.             $order->setCarrierName($this->addressService->clear($data['carrierName']));
  770.         }
  771.         if (!empty($data['delivery'])) {
  772.             $this->orderFiller->fillOrderDeliveryFromFrontData($data['delivery'], $order$this->oldOrder);
  773.         }
  774.         if (!empty($data['payType']) && $order->getStatus() < BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
  775.             $type PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['payType']);
  776.             $type PaymentTypeEnum::changeOldPayPalToNew($type);
  777.             if (
  778.                 OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
  779.                 && $type === PaymentTypeEnum::BANK_TRANSFER
  780.             ) {
  781.                 $type PaymentTypeEnum::VISA_MASTERCARD;
  782.             }
  783.             $order->setPayType($type);
  784.         }
  785.         $this->orderFiller->fillOrderVatFromFrontData($data['vat'] ?? [], $order);
  786.         $this->orderFiller->fillCompanyNameForOrder($order, ($data['companyName'] ?? null) ?: null);
  787.         if (
  788.             !empty($data['email'])
  789.             && empty($data['checkHash']) // 1С не меняет мэйлы в заказе, а значит и валидность не проверяем
  790.         ) {
  791.             $checkEmail $this->userService->checkEmail($data['email']);
  792.             if ($checkEmail->getStatus()) {
  793.                 $order->setEmail($data['email']);
  794.             } else {
  795.                 $msg $this->translationService->trans(
  796.                     'buyOrder.fields.error.email_exist',
  797.                     App::getCurLocale(),
  798.                     [
  799.                         '%login%' => App::generateUrl('app_login', ['_locale' => App::getCurLocale(true)]),
  800.                     ]
  801.                 );
  802.                 $this->errors[] = $msg;
  803.                 $this->log('CHECKEMAIL'$order->getHash(), $msg);
  804.             }
  805.         }
  806.         // должна быть после recipient, но до items чтобы правильно просчитать цены
  807.         $this->localization($order$data);
  808.         // todo временная команда создания нового адреса по заказу если его нет
  809.         if ($order->getAddressRecipient() == null/* && $order->getDeliveryIndex() != null*/) {
  810.             $addressRecipient $this->addressService->getAddressFromOrder($order);
  811.             $order->setAddressRecipient($addressRecipient);
  812.         }
  813.         $data $this->updateItemsForOrder($data$order$this->oldOrderfalse);
  814.         // todo ================================================================================================================
  815.         $markerResetOfOldOrder OrderHelper::markerReset($this->oldOrderfalse);
  816.         $markerResetOfNewOrder OrderHelper::markerReset($orderfalse);
  817.         // если что-то изменилось с фронта - то поставим статус заказа в По запросу
  818.         if (
  819.             $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
  820.             && md5($markerResetOfOldOrder) != md5($markerResetOfNewOrder)
  821.         ) {
  822.             $this->setStatus($orderBuyOrderStatusEnum::REQUEST_DELIVERY);
  823.         }
  824.         // заказ сбрасываем только если он не оплачен, уже отправлялся в 1С и данные изменились
  825.         if (
  826.             $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
  827.             && ($order->getSendCreate1C()
  828.                 && md5($markerResetOfOldOrder) != md5($markerResetOfNewOrder)
  829.                 || !empty($data['reset'])
  830.             )
  831.         ) {
  832.             $info = [
  833.                 'old' => $markerResetOfOldOrder,
  834.                 'now' => $markerResetOfNewOrder,
  835.             ];
  836.             $order->setBankTransferRequested(false);
  837.             $this->reset(
  838.                 $order,
  839.                 ReasonForResetOrderEnum::CHANGED_ITEMS_OR_INDEX_OR_COUNTRY(),
  840.                 'Changed items or index or country "' json_encode($infoJSON_UNESCAPED_UNICODE)
  841.             );
  842.         }
  843.         if (isset($data['confirmed'])) {
  844.             // фиксируем на всякий случай, что новые сроки подтвержденные
  845.             $this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
  846.         }
  847.         $this->buyOrderRepository->save($order);
  848.         $data $this->sendOrderTo1CIfItReady($order$data);
  849.         $this->buyOrderRepository->save($order);
  850.         return $this->changeOrderFrom1C($order$data);
  851.     }
  852.     public function changeOrderByHashFrom1CWithJsonAsString(string $hashstring $json): ?BuyOrder
  853.     {
  854.         try {
  855.             $order $this->getOrderObj($hash);
  856.         } catch (NotFoundHttpException NoResultException NonUniqueResultException $e) {
  857.             $this->log('ERROR_CHANGEORDER_BYHASH_FROM1C_WITHJSONASSTRING'$e->getMessage(), $json);
  858.             return null;
  859.         }
  860.         $json trim($json);
  861.         $data json_decode($jsontrue);
  862.         $response $this->changeOrderFrom1C($order$data);
  863.         if ($this->needRunScenario && isset($data['status'])) {
  864.             $scenario = new ChangeOrderStatusScenario(
  865.                 $order->getStatus(),
  866.                 $this->oldOrder->getStatus(),
  867.                 true,
  868.                 [],
  869.                 $order,
  870.                 $this->oldOrder,
  871.                 $this->historyService,
  872.                 $this->oneCService
  873.             );
  874.             $scenario->run();
  875.             $this->needRunScenario false;
  876.         }
  877.         return $response;
  878.     }
  879.     public function changeOrderFrom1CWithJsonAsString(BuyOrder $orderstring $json): ?BuyOrder
  880.     {
  881.         $json trim($json);
  882.         $data json_decode($jsontrue);
  883.         return $this->changeOrderFrom1C($order$data);
  884.     }
  885.     /**
  886.      * Изменение данных заказа из 1С - включая синхронизации и обновление всех данных заказа
  887.      *
  888.      * @param BuyOrder $order - заказ
  889.      * @param array $data - данные по заказу
  890.      * @return null|BuyOrder
  891.      * @throws Exception
  892.      */
  893.     public function changeOrderFrom1C(BuyOrder $order, array $data): ?BuyOrder
  894.     {
  895.         // добавим памяти для больших заказов
  896.         ini_set('memory_limit''4096M');
  897.         $this->log('CHANGEORDERFROM1C'$order->getHash(), json_encode($data));
  898.         if (
  899.             !empty($data['checkHash'])
  900.             && !OrderHelper::checkMarker($order->getHash(), $data['checkHash'])
  901.         ) {
  902.             $this->log('ERROR_CHANGE_ORDER_FROM_1C'$order->getHash(), 'Incorrect marker');
  903.             return null;
  904.         }
  905.         $this->buyOrderRepository->refresh($order);
  906.         if (!$this->orderAvailableToChange($data$order)) {
  907.             return null;
  908.         }
  909.         $this->cloneOrderIfNeeded($order);
  910.         $data $this->oneCService->normalizeData($data);
  911.         $this->orderFiller->fillHolidaysForItems($order$data);
  912.         if (isset($data['step']) && in_array($data['step'], [123])) {
  913.             if ($order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID) {
  914.                 $order->setStep($data['step']);
  915.             } else {
  916.                 $order->setStep(3);
  917.             }
  918.         }
  919.         if (
  920.             isset($data['payment']['type'])
  921.             && $data['payment']['type'] != $order->getPayType()
  922.             && PaymentTypeEnum::isAvailableMethod((int) $data['payment']['type'])
  923.         ) {
  924.             if (
  925.                 OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
  926.                 && $data['payment']['type'] == PaymentTypeEnum::BANK_TRANSFER
  927.             ) {
  928.                 $data['payment']['type'] = PaymentTypeEnum::VISA_MASTERCARD;
  929.             }
  930.             $order->setPayType(PaymentTypeEnum::changeOldPayPalToNew((int) $data['payment']['type']));
  931.         }
  932.         if (!empty($data['name'])) {
  933.             $name $this->getNameOrder($order->getToken(), strip_tags($data['name']), $order->getId());
  934.             $order->setName($nametrue);
  935.         }
  936.         $order->setTaxNumber($data['taxNumber'] ?? $order->getTaxNumber());
  937.         if (!empty($data['clientName'])) {
  938.             $order->setClientName($this->addressService->clear($data['clientName']));
  939.         }
  940.         if (!empty($data['clientSurname'])) {
  941.             $order->setClientSurname($this->addressService->clear($data['clientSurname']));
  942.         }
  943.         if (!empty($data['description'])) {
  944.             $order->setDescription($this->addressService->clear($data['description']));
  945.         }
  946.         if (!empty($data['carrierName'])) {
  947.             $order->setCarrierName($this->addressService->clear($data['carrierName']));
  948.         }
  949.         if (!empty($data['payType']) && $order->getStatus() < BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
  950.             $type PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['payType']);
  951.             $type PaymentTypeEnum::changeOldPayPalToNew($type);
  952.             if (
  953.                 OrderPaymentHelper::orderCountryWithoutInvoiceAsPaymentType($order)
  954.                 && $type === PaymentTypeEnum::BANK_TRANSFER
  955.             ) {
  956.                 $type PaymentTypeEnum::VISA_MASTERCARD;
  957.             }
  958.             $order->setPayType($type);
  959.         }
  960.         // ставим спец цену в заказе запрещающую изменения цен
  961.         if (isset($data['specPrice'])) {
  962.             $order->setSpecPrice(!empty($data['specPrice']));
  963.             try {
  964.                 $data $this->updateItemsForOrder($data$order$this->oldOrder, !empty($data['checkHash']));
  965.             } catch (Exception $e) {
  966.                 $this->log('ERROR_CHANGE_ORDER_ITEMS_FROM_1C'$order->getHash(), 'Error for updated items: ' $e->getMessage());
  967.                 return null;
  968.             }
  969.         }
  970.         if (isset($data['confirmed'])) {
  971.             // фиксируем на всякий случай, что новые сроки подтвержденные
  972.             $this->setNewTermsOfDelivery($order, (bool) $data['confirmed']);
  973.         }
  974.         if (isset($data['feedback']) && is_scalar($data['feedback'])) {
  975.             $feedback is_string($data['feedback'])
  976.                 ? (strcasecmp('true'$data['feedback']) === 0)
  977.                 : (bool) $data['feedback'];
  978.             $order->setProhibitSendReviewRequest(!$feedback);
  979.         }
  980.         $this->checkSumOrSumBackData($data$order);
  981.         if (isset($data['dateCalc'])) {
  982.             // отправка письма 20 должна быть только если заказ ещё не оплачен,
  983.             // а так же это не из сетПэй
  984.             $send20 = empty($data['checkHash'])
  985.                 && !empty($data['status']) //Добавил "!" для исправления ошибки, нужна дополнительная проверка по логике
  986.                 && !in_array($data['status'], [BuyOrderStatusEnum::PARTIALLY_PAIDBuyOrderStatusEnum::PAID]);
  987.             $order $this->offsetDate($data$order$send20);
  988.         }
  989.         $params $this->setDataFromResponse($order$data);
  990.         // создавать тему на портале нужно когда известен номер заказа
  991.         if ($this->portalService->themePortal($order$this->oldOrder)) {
  992.             $this->buyOrderRepository->save($order);
  993.         }
  994.         if (!empty($data['sendReview'])) {
  995.             // отправляем приглашение на отзыв если был добавлен ключ для отправки
  996.             if (
  997.                 App::getParameter('order_send_review')
  998.                 && $this->oldOrder->getSendReview() != $order->getSendReview()
  999.                 && $order->getSendReview() == 1
  1000.             ) {
  1001.                 $this->historyService->saveHistoryAndSendEmail(
  1002.                     $order,
  1003.                     'order.review',
  1004.                     App::getParameter('order_send_review')
  1005.                 );
  1006.             }
  1007.         }
  1008.         if (isset($data['stage']) && $order->getEmail()) {
  1009.             $stageAlias $data['stage'] == 'delayed_delivery_4' 'delivery_delayed_3';
  1010.             $this->historyService->saveHistoryAndSendEmail(
  1011.                 $order,
  1012.                 $stageAlias,
  1013.                 OrderHistoryService::SEND_EMAIL,
  1014.                 $params
  1015.             );
  1016.         }
  1017.         if (!empty($data['confirmDateOffset'])) {
  1018.             $order->setOffsetDate(DateHelper::standard($data['confirmDateOffset']));
  1019.             $this->historyService->saveHistoryAndSendEmail(
  1020.                 $order,
  1021.                 'delay_confirmation',
  1022.                 OrderHistoryService::SEND_EMAIL
  1023.             );
  1024.         }
  1025.         // записываем новые хеши данных заказа
  1026.         // хеш нужно обязательно перестраивать перед обновлением иначе будут дубль запросы в 1С
  1027.         $this->setNewHashOrder($order);
  1028.         // меняем дату обновления только если заказ обновлялся не консольно, то есть не роботом
  1029.         // это нужно чтобы мертвый заказ со временем перестал провериться на актуальность,
  1030.         // если пользователь в него не входил
  1031.         if (!$this->asConsole) {
  1032.             $order->setUpdateDate(new DateTime());
  1033.         }
  1034.         $this->buyOrderRepository->save($order);
  1035.         return $order;
  1036.     }
  1037.     /**
  1038.      * Сброс и отправка заказа в 1С
  1039.      */
  1040.     public function resetAndSendOrderTo1C(string $hash): bool
  1041.     {
  1042.         if (!$this->asConsole) {
  1043.             echo 'Only console.' PHP_EOL;
  1044.             return false;
  1045.         }
  1046.         try {
  1047.             $order $this->getOrderObj($hash);
  1048.         } catch (NotFoundHttpException NonUniqueResultException $e) {
  1049.             echo 'Order not found.' PHP_EOL;
  1050.             return false;
  1051.         }
  1052.         if ($order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
  1053.             echo 'Order status more than 3.' PHP_EOL;
  1054.             return false;
  1055.         }
  1056.         if (!$order->getSendCreate1C()) {
  1057.             echo 'Order not sent to 1C yet.' PHP_EOL;
  1058.             return false;
  1059.         }
  1060.         $this->reset($orderReasonForResetOrderEnum::RESEND_ADMIN(), 'Resend order by admin command');
  1061.         if (!OrderHelper::readyCreate($order)) {
  1062.             echo 'Order doesnt ready to create.' PHP_EOL;
  1063.             return false;
  1064.         }
  1065.         $this->oneCService->createOrderOneC(App::getRequest(), $order);
  1066.         // создавать тему на портале нужно когда известен номер заказа
  1067.         $this->portalService->themePortal($order$order);
  1068.         $this->setNewHashOrder($order);
  1069.         $this->buyOrderRepository->save($order);
  1070.         return true;
  1071.     }
  1072.     /**
  1073.      * Перемещение или копирование заказа
  1074.      * @param array $data
  1075.      * @return array
  1076.      * @throws Exception
  1077.      */
  1078.     public function transferItems(array $data): array
  1079.     {
  1080.         if (empty($data)) {
  1081.             return ['err' => []];
  1082.         }
  1083.         $error = [];
  1084.         if (empty($data['ides']) || count($data['ides']) == 0) {
  1085.             $error[] = 'ides is empty';
  1086.         }
  1087.         if (empty($data['hashSource'])) {
  1088.             $error[] = 'hashSource is empty';
  1089.         }
  1090.         if (empty($data['hashReceiver'])) {
  1091.             $error[] = 'hashReceiver is empty';
  1092.         }
  1093.         if (count($error) > 0) {
  1094.             return ['err' => $error];
  1095.         }
  1096.         $orderSource $this->getOrderObj($data['hashSource']);
  1097.         /** @var BuyOrderArticle $item */
  1098.         foreach ($orderSource->getArticles() as $item) {
  1099.             if (in_array($item->getId(), $data['ides'])) {
  1100.                 // если получатель массив тогда копируем
  1101.                 if (is_array($data['hashReceiver'])) {
  1102.                     foreach ($data['hashReceiver'] as $hashReceiver) {
  1103.                         $orderReceiver $this->getOrderObj($hashReceiver);
  1104.                         if ($findItem $this->findItemInOrder($orderReceiver$item)) {
  1105.                             $this->itemService->setItemData(
  1106.                                 $orderReceiver,
  1107.                                 $findItem,
  1108.                                 $item->getAmount(),
  1109.                                 $item->getType()
  1110.                             );
  1111.                             $this->em->persist($findItem);
  1112.                         } else {
  1113.                             $this->itemService->createCopyItem($item$orderReceiver);
  1114.                         }
  1115.                         $this->em->persist($orderReceiver);
  1116.                         $this->oneCService->sendOrResendOrderToOneC($orderReceiver);
  1117.                     }
  1118.                     $this->em->flush();
  1119.                 } else { // если строка значит перемещаем
  1120.                     $orderReceiver $this->getOrderObj($data['hashReceiver']);
  1121.                     // проверяем существует ли в данном заказе уже этот артикул, если нет, перемещаем
  1122.                     if ($this->findItemInOrder($orderReceiver$item) == null) {
  1123.                         $changeItem false;
  1124.                         if ($item->getCurrency() != $orderReceiver->getCurrency()) {
  1125.                             $item->setCurrency($orderReceiver->getCurrency());
  1126.                             $changeItem true;
  1127.                         }
  1128.                         if ($item->getMeasure() != $orderReceiver->getMeasure()) {
  1129.                             $item->setMeasure($orderReceiver->getMeasure());
  1130.                             $changeItem true;
  1131.                         }
  1132.                         if ($changeItem) {
  1133.                             $this->itemService->setItemData(
  1134.                                 $orderReceiver,
  1135.                                 $item,
  1136.                                 $item->getAmount(),
  1137.                                 $item->getType()
  1138.                             );
  1139.                         }
  1140.                         $item->setBuyOrder($orderReceiver);
  1141.                         $this->em->persist($orderReceiver);
  1142.                         $this->em->persist($item);
  1143.                         $this->em->flush();
  1144.                         $this->oneCService->sendOrResendOrderToOneC($orderReceiver);
  1145.                     }
  1146.                 }
  1147.                 $this->em->persist($orderSource);
  1148.                 $this->em->flush();
  1149.                 $this->oneCService->sendOrResendOrderToOneC($orderSource);
  1150.             }
  1151.         }
  1152.         $order $this->orderData($orderSource);
  1153.         return ['orderSource' => $order];
  1154.     }
  1155.     /**
  1156.      * @param BuyOrder $order
  1157.      * @param BuyOrderArticle $item
  1158.      * @return null|BuyOrderArticle
  1159.      */
  1160.     private function findItemInOrder(BuyOrder $orderBuyOrderArticle $item): ?BuyOrderArticle
  1161.     {
  1162.         foreach ($order->getArticles() as $itemOrder) {
  1163.             if ($itemOrder->chooseCode() == $item->chooseCode()) {
  1164.                 return $itemOrder;
  1165.             }
  1166.         }
  1167.         return null;
  1168.     }
  1169.     /**
  1170.      * @param BuyOrder $order
  1171.      * @param array $ides
  1172.      * @return array
  1173.      * @throws Exception
  1174.      */
  1175.     public function delItems(BuyOrder $order, array $ides): array
  1176.     {
  1177.         $this->log('DELETE_ITEM_ORDER_TO_NEW_FLOW'$order->getHash(), $ides);
  1178.         $isDel false;
  1179.         /** @var BuyOrderArticle $item */
  1180.         foreach ($order->getArticles() as $item) {
  1181.             if (in_array($item->getId(), $ides)) {
  1182.                 $isDel true;
  1183.                 $order->removeArticle($item);
  1184.                 $this->em->remove($item);
  1185.             }
  1186.         }
  1187.         if ($isDel) {
  1188.             $this->buyOrderRepository->save($order);
  1189.             $this->reset($orderReasonForResetOrderEnum::DELETE_ITEM(), 'DELETE ITEMS IN ORDER');
  1190.         }
  1191.         return $this->orderData($order);
  1192.     }
  1193.     /**
  1194.      * Удаление артикула из заказа
  1195.      *
  1196.      * @param int $itemId
  1197.      * @param string $orderHash
  1198.      * @return array|null
  1199.      * @throws NonUniqueResultException
  1200.      * @throws NoResultException
  1201.      */
  1202.     public function deleteArticleFromOrder(
  1203.         int $itemId,
  1204.         string $orderHash
  1205.     ): ?array {
  1206.         // заказ можно менять только до момента оплаты
  1207.         $params['status'] = [123];
  1208.         $params['token'] = $this->token;
  1209.         $params['hash'] = $orderHash;
  1210.         $order $this->buyOrderRepository->getBuyOrder($params);
  1211.         if (null === $order) {
  1212.             return null;
  1213.         }
  1214.         /** @var Article $item */
  1215.         $item $this->articleRepository->getArticle($itemId);
  1216.         if (null === $item) {
  1217.             return null;
  1218.         }
  1219.         // ищем артикул в заказе
  1220.         $orderItem null;
  1221.         /** @var BuyOrderArticle|null $orderItem */
  1222.         /** @var BuyOrderArticle $oneArticleInCurrentOrder */
  1223.         foreach ($order->getArticles() as $key => $oneArticleInCurrentOrder) {
  1224.             if ($oneArticleInCurrentOrder->getArticle()->getId() === $item->getId()) {
  1225.                 $orderItem $oneArticleInCurrentOrder;
  1226.             }
  1227.         }
  1228.         if (null === $orderItem) {
  1229.             return null;
  1230.         }
  1231.         $this->itemService->deleteItemFromOrder($order$orderItem);
  1232.         // сброс заказа в начальное состояние в 1С
  1233.         $this->reset($orderReasonForResetOrderEnum::DELETE_ITEM(), 'DELETE ITEM');
  1234.         return [
  1235.             'count' => $order->getArticles()->count(),
  1236.             'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  1237.             'projects' => [$this->getProjectData($order)],
  1238.         ];
  1239.     }
  1240.     /**
  1241.      * Сброс заказа в состояние черновика
  1242.      *
  1243.      * @param BuyOrder $order
  1244.      * @param ReasonForResetOrderEnum $reasonForResetOrder
  1245.      * @param ?string $info
  1246.      * @return BuyOrder
  1247.      * @throws SoapFault
  1248.      */
  1249.     public function reset(
  1250.         BuyOrder $order,
  1251.         ReasonForResetOrderEnum $reasonForResetOrder,
  1252.         ?string $info null
  1253.     ): BuyOrder // todo после перехода на заказ 2.0 поставить private
  1254.         //проверяем статус заказа, если больше 3-х, то сбрасывать заказ нельзя
  1255.         if ($order->getStatus() && $order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
  1256.             $this->log('RESETORDERSITE'$order->getHash(), $info'The order cannot be changed!');
  1257.             return $order;
  1258.         }
  1259.         $orderSendCreate1CYet $order->getSendCreate1C();
  1260.         // если заказ рассчитывается вручную и сбрасывается,
  1261.         // надо установить ключ письма для отложенной отправки письма о сбросе по заказу
  1262.         if (
  1263.             $order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
  1264.             && $order->getDeliveryCalculateType() === 0
  1265.         ) {
  1266.             $order->setKeyEmail('reset_time_change');
  1267.         }
  1268.         $this->setStatus($orderBuyOrderStatusEnum::REQUEST_DELIVERY, ['fromResetOrder' => $reasonForResetOrder->isUserDidCancelRequest()]);
  1269.         if ($order->getSpecPrice()) {
  1270.             $this->log('RESET SPEC PRICE'$order->getHash(), $info);
  1271.             $order->setSpecPrice(null);
  1272.         }
  1273.         // сохраняем сброс заказа на сайте до отправки в 1С
  1274.         $this->orderFiller->fillOrderAsDraft($order);
  1275.         if ($orderSendCreate1CYet) {
  1276.             $this->log('RESETORDERONEC'$order->getHash(), $info);
  1277.             $this->oneCService->resetOrderOneC($order$info$this->timer);
  1278.         } else {
  1279.             $this->log('RESETORDERSITE'$order->getHash(), $info);
  1280.         }
  1281.         return $order;
  1282.     }
  1283.     /**
  1284.      * Проверяет включен ли режим редактирования администратора
  1285.      *
  1286.      * @return bool
  1287.      * @throws Exception
  1288.      */
  1289.     private function adminChangeMode(): bool
  1290.     {
  1291.         return $this->authorizationChecker->isGranted('ROLE_ADMIN');
  1292.     }
  1293.     /**
  1294.      * Проверяет не консультант это случаем
  1295.      *
  1296.      * @return bool
  1297.      * @throws Exception
  1298.      */
  1299.     private function isConsultant(): bool
  1300.     {
  1301.         return $this->authorizationChecker->isGranted('ROLE_CONS');
  1302.     }
  1303.     /**
  1304.      * Запрос ручного расчета доставки и стоимости или ускорения доставки
  1305.      *
  1306.      * @param string $hash
  1307.      * @param ?string $json
  1308.      * @return array
  1309.      * @throws Exception
  1310.      * @internal param BuyOrder $order
  1311.      */
  1312.     public function requestCalculateDeliveryByHash(string $hash, ?string $json null): ?array
  1313.     {
  1314.         try {
  1315.             $order $this->getOrderObj($hash);
  1316.         } catch (NoResultException NonUniqueResultException $e) {
  1317.             return null;
  1318.         }
  1319.         return $this->requestCalculateDelivery($order$json);
  1320.     }
  1321.     /**
  1322.      * Запрос ручного расчета доставки и стоимости или ускорения доставки
  1323.      * @throws Exception
  1324.      */
  1325.     public function requestCalculateDelivery(BuyOrder $order, ?string $json null): ?array
  1326.     {
  1327.         if ($order->getToken() !== $this->token && !$this->adminChangeMode()) {
  1328.             return null;
  1329.         }
  1330.         $result['order'] = $order;
  1331.         if (!$json && !$order->getSendCreate1C() && $order->getDeliveryIndex()) {
  1332.             $json json_encode(['delivery' => ['index' => $order->getDeliveryIndex()]]);
  1333.         }
  1334.         $jsonData null;
  1335.         if ($order->getUser() === null && App::getCurUser() === null) {
  1336.             $jsonData json_decode($jsontrue);
  1337.             if (
  1338.                 $order->getEmail() === null
  1339.                 && isset($jsonData['email'])
  1340.                 && ! $this->findUserByEmail($jsonData['email'])
  1341.             ) {
  1342.                 $address $order->getAddressRecipient();
  1343.                 $order->setEmail($jsonData['email']);
  1344.                 $address->setEmail($jsonData['email']);
  1345.                 $order->setAddressRecipient($address);
  1346.             }
  1347.         }
  1348.         // Данные клиента
  1349.         if ($json) {
  1350.             $order $this->changeByOrder($order$json);
  1351.         } else {
  1352.             $jsonData ??= json_decode($jsontrue);
  1353.             $this->localization($order$jsonData);
  1354.         }
  1355.         $isReadyOrder $this->oneCService->readyRequestDelivery($order);
  1356.         if ($isReadyOrder) {
  1357.             $result $this->oneCService->delivery($order);
  1358.             $order $this->offsetDate($result$order);
  1359.             //Учитывая изменение статуса заказ нужно запускать сценарий
  1360.             $this->needRunScenario true;
  1361.             $this->setStatus($orderBuyOrderStatusEnum::AWAITING_CALCULATION, ['json_data' => $json]);
  1362.             if (!empty($result['delivery'])) {
  1363.                 $this->setDeliveryOrder($result$order);
  1364.                 $this->setStatus($orderBuyOrderStatusEnum::AWAITING_PAYMENT);
  1365.             }
  1366.             $this->buyOrderRepository->save($order);
  1367.             return $this->orderData($order);
  1368.         }
  1369.         return $this->orderData($order, [], ['not ready order']);
  1370.     }
  1371.     /**
  1372.      * Устанавливает сроки смещения
  1373.      *
  1374.      * @param array|null $data
  1375.      * @param BuyOrder $order
  1376.      * @param ?bool $send20
  1377.      * @return BuyOrder
  1378.      * @throws Exception
  1379.      */
  1380.     public function offsetDate(?array $dataBuyOrder $order, ?bool $send20 false): BuyOrder
  1381.     {
  1382.         if (empty($data['dateCalc'])) {
  1383.             $dateOffset = new DateTime();
  1384.             $dateOffset->modify('+2 day');
  1385.             $order->setOffsetDate($dateOffset);
  1386.             return $order;
  1387.         }
  1388.         // сбрасываем старую причину
  1389.         $order->setOffsetExplain(null);
  1390.         $order->setOffsetDate(DateHelper::standard($data['dateCalc']));
  1391.         if (!empty($data['dateCalcReason']) && in_array($data['dateCalcReason'], [123])) {
  1392.             $order->setOffsetReason($data['dateCalcReason']);
  1393.             if ($data['dateCalcReason'] == && !empty($data['explain'])) { // причина указанная БМ-ом
  1394.                 $order->setOffsetExplain($data['explain']);
  1395.                 $alias $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
  1396.                     'verification_possibility_deliver_2'
  1397.                     'verification_possibility_deliver_1';
  1398.             } elseif ($data['dateCalcReason'] == 2) { // фабрика уточняет сроки пр-ва
  1399.                 $alias $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
  1400.                     'verification_possibility_deliver_2b'
  1401.                     'verification_possibility_deliver_1b';
  1402.             } else { // каникулы на фабрике
  1403.                 $alias $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION
  1404.                     'verification_possibility_deliver_2a'
  1405.                     'verification_possibility_deliver_1a';
  1406.             }
  1407.             $this->historyService->saveHistoryAndSendEmail(
  1408.                 $order,
  1409.                 $alias,
  1410.                 OrderHistoryService::SEND_EMAIL
  1411.             );
  1412.         } elseif ($send20) {
  1413.             $this->historyService->saveHistoryAndSendEmail(
  1414.                 $order,
  1415.                 'calculation_shipping_more_time',
  1416.                 OrderHistoryService::SEND_EMAIL
  1417.             );
  1418.         }
  1419.         return $order;
  1420.     }
  1421.     /**
  1422.      * Смена статуса заказа
  1423.      *
  1424.      * @param BuyOrder $order
  1425.      * @param int $status
  1426.      * @param ?array $params
  1427.      * @return BuyOrder
  1428.      * @throws Exception
  1429.      */
  1430.     public function setStatus(BuyOrder $orderint $status, ?array $params = []): BuyOrder
  1431.     {
  1432.         // защита от несанкционированной смены статуса заказа в черновик
  1433.         if ($this->isForbiddenStatusChange($status$order)) {
  1434.             $this->needRunScenario false;
  1435.             return $order;
  1436.         }
  1437.         $oldStatus $this->oldOrder $this->oldOrder->getStatus() : 0;
  1438.         $this->cloneOrderIfNeeded($order);
  1439.         $sendEmail = !($params['syncFromOneC'] ?? false);
  1440.         // проверка дубль заказов
  1441.         $this->addressService->mergeAddresses($order);
  1442.         if ($oldStatus === $status && $status === BuyOrderStatusEnum::PAID_NOT_CONFIRMED) {
  1443.             $this->historyService->saveHistoryAndSendEmail($order'reminder_acceptance_period'$sendEmail);
  1444.             $this->needRunScenario false;
  1445.             return $order;
  1446.         }
  1447.         if ($this->dontChangeOrderStatus(isset($params['fileBase64']), $oldStatus$status)) {
  1448.             $this->needRunScenario false;
  1449.             return $order;
  1450.         }
  1451.         if ($this->needRunScenario) {
  1452.             $scenario = new ChangeOrderStatusScenario(
  1453.                 $status,
  1454.                 $oldStatus,
  1455.                 $sendEmail,
  1456.                 $params,
  1457.                 $order,
  1458.                 $this->oldOrder,
  1459.                 $this->historyService,
  1460.                 $this->oneCService
  1461.             );
  1462.             $scenario->run();
  1463.             $this->needRunScenario false;
  1464.         }
  1465.         return $order;
  1466.     }
  1467.     /**
  1468.      * Устанавливает сроки заказ
  1469.      *
  1470.      * @param array $data
  1471.      * @param BuyOrder $order
  1472.      * @throws Exception
  1473.      */
  1474.     public function setDeliveryOrder(array $dataBuyOrder $order)
  1475.     {
  1476.         if (!empty($data['delivery'])) {
  1477.             $this->log('DELIVERY DATA'$order->getHash(), json_encode($data['delivery']));
  1478.             $oldDate $order->getDeliveryTimeMax() ? $order->getDeliveryTimeMax()->getTimestamp() : 0;
  1479.             $oldTimeDelivery OrderHelper::time($ordertrue);
  1480.             $delivery $data['delivery'];
  1481.             if (isset($delivery['type'])) {
  1482.                 if ($delivery['type'] == 1) {
  1483.                     $order->setDeliveryType(1);
  1484.                 } else {
  1485.                     $order->setDeliveryType(0);
  1486.                 }
  1487.             }
  1488.             if (!empty($delivery['price'])) {
  1489.                 $price PriceHelper::formatPrice($delivery['price']);
  1490.                 $order->setDelivery($price);
  1491.                 if (is_numeric($price) && $price != 0) {
  1492.                     $order->setStatus(BuyOrderStatusEnum::AWAITING_PAYMENT);
  1493.                 }
  1494.             } elseif (!empty($delivery['status'])) {
  1495.                 $order->setDelivery(0);
  1496.             }
  1497.             if (!empty($delivery['priceEuro'])) {
  1498.                 $price PriceHelper::formatPrice($delivery['priceEuro']);
  1499.                 $order->setDeliveryPriceEuro($price);
  1500.             } elseif (!empty($delivery['status'])) {
  1501.                 //если 1С передало статус 3, то считаем, что доставка рассчитана, но бесплатна
  1502.                 $order->setDeliveryPriceEuro(0);
  1503.             }
  1504.             if (isset($delivery['lift'])) {
  1505.                 if (BuyOrderLiftEnum::isValid((int) $delivery['lift'])) {
  1506.                     $order->setLift((int) $delivery['lift']);
  1507.                 } else {
  1508.                     $order->setLift(BuyOrderLiftEnum::UNDEFINED);
  1509.                 }
  1510.             }
  1511.             if (!empty($delivery['weightReport'])) {
  1512.                 $weightReport = (float) str_replace(',''.'$delivery['weightReport']);
  1513.                 $order->setWeightReport($weightReport);
  1514.             } elseif (isset($delivery['weightReport'])) {
  1515.                 $order->setWeightReport(null);
  1516.             }
  1517.             OrderHelper::updateWeightGrossForOrderInNeeded($order$delivery['weightGross'] ?? null);
  1518.             if (!empty($delivery['dateExecution'])) {
  1519.                 // проверяем дату на минуты
  1520.                 $dateExecution DateHelper::standard($delivery['dateExecution']);
  1521.                 $order->setPayDateExecution($dateExecution);
  1522.             } elseif (isset($delivery['dateExecution'])) {
  1523.                 $order->setPayDateExecution(null);
  1524.             }
  1525.             if (!empty($delivery['timeMin'])) {
  1526.                 if ($timeMin DateHelper::standard($delivery['timeMin'])) {
  1527.                     if (
  1528.                         $order->getDeliveryTimeMin() != null &&
  1529.                         (
  1530.                             $order->getDeliveryOldTimeMin() == null ||
  1531.                             $order->getDeliveryTimeMin()->getTimestamp() != $order->getDeliveryTimeMin()->getTimestamp()
  1532.                         )
  1533.                     ) {
  1534.                         $order->setDeliveryOldTimeMin($order->getDeliveryTimeMin());
  1535.                     }
  1536.                     $order->setDeliveryTimeMin($timeMin);
  1537.                 }
  1538.             } elseif (isset($delivery['timeMin'])) {
  1539.                 $order->setDeliveryTimeMin(null);
  1540.             }
  1541.             if (!empty($delivery['timeMax'])) {
  1542.                 if ($timeMax DateHelper::standard($delivery['timeMax'])) {
  1543.                     if (
  1544.                         $order->getDeliveryTimeMax() != null
  1545.                         && ($order->getDeliveryOldTimeMax() == null
  1546.                             || $order->getDeliveryTimeMax()->getTimestamp() != $order->getDeliveryTimeMax()->getTimestamp()
  1547.                         )
  1548.                     ) {
  1549.                         $order->setDeliveryOldTimeMax($order->getDeliveryTimeMax());
  1550.                     }
  1551.                     $order->setDeliveryTimeMax($timeMax);
  1552.                 }
  1553.             } elseif (isset($delivery['timeMax'])) {
  1554.                 $order->setDeliveryTimeMax(null);
  1555.             }
  1556.             if (!empty($delivery['timeFastPayMin'])) {
  1557.                 if ($timeFastPayMin DateHelper::standard($delivery['timeFastPayMin'])) {
  1558.                     $order->setDeliveryTimeFastPayMin($timeFastPayMin);
  1559.                 }
  1560.             } elseif (isset($delivery['timeFastPayMin'])) {
  1561.                 $order->setDeliveryTimeFastPayMin(null);
  1562.             }
  1563.             if (!empty($delivery['timeFastPayMax'])) {
  1564.                 if ($timeFastPayMax DateHelper::standard($delivery['timeFastPayMax'])) {
  1565.                     $order->setDeliveryTimeFastPayMax($timeFastPayMax);
  1566.                 }
  1567.             } elseif (isset($delivery['timeFastPayMax'])) {
  1568.                 $order->setDeliveryTimeFastPayMax(null);
  1569.             }
  1570.             $timeConfirm false;
  1571.             if (!empty($delivery['timeConfirmMin'])) {
  1572.                 // если текущих сроков нет, то берем старые
  1573.                 if ($order->getDeliveryTimeMin() == null && $order->getDeliveryOldTimeMin()) {
  1574.                     $order->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
  1575.                 }
  1576.                 if ($timeConfirmMin DateHelper::standard($delivery['timeConfirmMin'])) {
  1577.                     $order->setDeliveryTimeConfirmMin($timeConfirmMin);
  1578.                     $timeConfirm true;
  1579.                 }
  1580.             } elseif (isset($delivery['timeConfirmMin'])) {
  1581.                 $order->setDeliveryTimeConfirmMin(null);
  1582.             }
  1583.             if (!empty($delivery['timeConfirmMax'])) {
  1584.                 // если текущих сроков нет, то берем старые
  1585.                 if ($order->getDeliveryTimeMax() == null && $order->getDeliveryOldTimeMax()) {
  1586.                     $order->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
  1587.                 }
  1588.                 if ($timeConfirmMax DateHelper::standard($delivery['timeConfirmMax'])) {
  1589.                     $order->setDeliveryTimeConfirmMax($timeConfirmMax);
  1590.                     $timeConfirm true;
  1591.                 }
  1592.             } elseif (isset($delivery['timeConfirmMax'])) {
  1593.                 $order->setDeliveryTimeConfirmMax(null);
  1594.             }
  1595.             // дополнительная проверка для стоимости доставки
  1596.             if ($order->getDeliveryTimeMin() === null && $order->getDelivery()) {
  1597.                 $order->setDelivery(null);
  1598.             }
  1599.             if (!empty($data['items'])) {
  1600.                 $artTimeTypes = [];
  1601.                 /** @var BuyOrderArticle $item */
  1602.                 foreach ($order->getArticles() as $item) {
  1603.                     if (isset($data['items'][$item->chooseCode()])) {
  1604.                         if (
  1605.                             !empty($data['items'][$item->chooseCode()]['holiday']) &&
  1606.                             (
  1607.                                 $data['items'][$item->chooseCode()]['holiday'] == 'true' ||
  1608.                                 $data['items'][$item->chooseCode()]['holiday'] === true
  1609.                             )
  1610.                         ) {
  1611.                             $item->setHoliday(true);
  1612.                         } else {
  1613.                             $item->setHoliday(false);
  1614.                         }
  1615.                         if (!empty($data['items'][$item->chooseCode()]['timeMin'])) {
  1616.                             if ($timeMin DateHelper::standard($data['items'][$item->chooseCode()]['timeMin'])) {
  1617.                                 $item->setDeliveryTimeMin($timeMin);
  1618.                             }
  1619.                         } elseif (isset($data['items'][$item->chooseCode()]['timeMin'])) {
  1620.                             $item->setDeliveryTimeMin(null);
  1621.                         }
  1622.                         if (!empty($data['items'][$item->chooseCode()]['timeMax'])) {
  1623.                             if ($timeMax DateHelper::standard($data['items'][$item->chooseCode()]['timeMax'])) {
  1624.                                 $item->setDeliveryTimeMax($timeMax);
  1625.                             }
  1626.                         } elseif (isset($data['items'][$item->chooseCode()]['timeMax'])) {
  1627.                             $item->setDeliveryTimeMax(null);
  1628.                         }
  1629.                         if (!empty($data['items'][$item->chooseCode()]['timeFastPayMin'])) {
  1630.                             if (
  1631.                                 $timeFastPayMin DateHelper::standard(
  1632.                                     $data['items'][$item->chooseCode()]['timeFastPayMin']
  1633.                                 )
  1634.                             ) {
  1635.                                 $item->setDeliveryTimeMinFaster($timeFastPayMin);
  1636.                             }
  1637.                         } elseif (isset($data['items'][$item->chooseCode()]['timeFastPayMin'])) {
  1638.                             $item->setDeliveryTimeMinFaster(null);
  1639.                         }
  1640.                         if (!empty($data['items'][$item->chooseCode()]['timeFastPayMax'])) {
  1641.                             if (
  1642.                                 $timeFastPayMax DateHelper::standard(
  1643.                                     $data['items'][$item->chooseCode()]['timeFastPayMax']
  1644.                                 )
  1645.                             ) {
  1646.                                 $item->setDeliveryTimeMaxFaster($timeFastPayMax);
  1647.                             }
  1648.                         } elseif (isset($data['items'][$item->chooseCode()]['timeMax'])) {
  1649.                             $item->setDeliveryTimeMaxFaster(null);
  1650.                         }
  1651.                         if (!empty($data['items'][$item->chooseCode()]['timeConfirmMin'])) {
  1652.                             // если текущих сроков нет, то берем старые
  1653.                             if ($item->getDeliveryTimeMin() == null && $order->getDeliveryOldTimeMin()) {
  1654.                                 $item->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
  1655.                             }
  1656.                             if (
  1657.                                 $timeMin DateHelper::standard(
  1658.                                     $data['items'][$item->chooseCode()]['timeConfirmMin']
  1659.                                 )
  1660.                             ) {
  1661.                                 $item->setDeliveryTimeConfirmMin($timeMin);
  1662.                                 // на случай если сброшены обычные сроки берем их из заказа
  1663.                                 if (!$item->getDeliveryTimeMin() && $order->getDeliveryTimeMin()) {
  1664.                                     $item->setDeliveryTimeMin($order->getDeliveryTimeMin());
  1665.                                 }
  1666.                             }
  1667.                         } elseif (isset($data['items'][$item->chooseCode()]['timeConfirmMin'])) {
  1668.                             $item->setDeliveryTimeConfirmMin(null);
  1669.                         }
  1670.                         if (!empty($data['items'][$item->chooseCode()]['timeConfirmMax'])) {
  1671.                             // если текущих сроков нет, то берем старые
  1672.                             if ($item->getDeliveryTimeMax() == null && $order->getDeliveryOldTimeMax()) {
  1673.                                 $item->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
  1674.                             }
  1675.                             if (
  1676.                                 $timeMax DateHelper::standard(
  1677.                                     $data['items'][$item->chooseCode()]['timeConfirmMax']
  1678.                                 )
  1679.                             ) {
  1680.                                 $item->setDeliveryTimeConfirmMax($timeMax);
  1681.                                 // на случай если сброшены обычные сроки берем их из заказа
  1682.                                 if (!$item->getDeliveryTimeMax() && $order->getDeliveryTimeMax()) {
  1683.                                     $item->setDeliveryTimeMax($order->getDeliveryTimeMax());
  1684.                                 }
  1685.                             }
  1686.                         } elseif (isset($data['items'][$item->chooseCode()]['timeConfirmMax'])) {
  1687.                             $item->setDeliveryTimeConfirmMax(null);
  1688.                         }
  1689.                         if (!empty($data['items'][$item->chooseCode()]['timeType'])) {
  1690.                             $itemTimeType = (int) $data['items'][$item->chooseCode()]['timeType'];
  1691.                             // костыль так как 1С неправильно присылает тип срока если ранее он был рассчитан
  1692.                             $itemTimeType = (DeliveryTypeTimeEnum::PREVIOUSLY_CONFIRMED === $itemTimeType)
  1693.                                 ? DeliveryTypeTimeEnum::CALCULATED
  1694.                                 $itemTimeType;
  1695.                             $artTimeTypes[] = $itemTimeType;
  1696.                             $item->setArticleTimeType($itemTimeType);
  1697.                         }
  1698.                     }
  1699.                 }
  1700.                 // меняем статус об ожидании подтверждения клиентом заказа
  1701.                 if ($timeConfirm) {
  1702.                     $this->setStatus($orderBuyOrderStatusEnum::PAID_NOT_CONFIRMED);
  1703.                 }
  1704.                 // если есть
  1705.                 if (
  1706.                     count($artTimeTypes) > 0
  1707.                     && $order->getStatus()
  1708.                     && $order->getStatus() != BuyOrderStatusEnum::PAID_NOT_CONFIRMED
  1709.                 ) {
  1710.                     $order->setDeliveryTimeType(min($artTimeTypes));
  1711.                 }
  1712.                 // Только после setDeliveryTimeType
  1713.                 if (isset($delivery['calculateType'])) {
  1714.                     $order->setDeliveryCalculateType(!empty($delivery['calculateType']));
  1715.                 } elseif ($order->getDeliveryCalculateType() == null) {
  1716.                     // Назначаем авто расчет сроков, если тип расчета не был передан
  1717.                     $order->setDeliveryCalculateType(1);
  1718.                 }
  1719.                 // если по заказу не запрашивали счет и срок рассчитан автоматически, срок предложения ставим в 1 день
  1720.                 if (
  1721.                     $order->getPayDateExecution() != null
  1722.                     && $order->getDeliveryTimeType() === DeliveryTypeTimeEnum::CALCULATED
  1723.                     && $order->getBankTransferRequested() == null
  1724.                     && !empty($data['status']) && $data['status'] == 3
  1725.                 ) {
  1726.                     $d = new DateTime(date('Y-m-d'strtotime('now')) . ' 23:59:59');
  1727.                     $this->log(
  1728.                         'PAYDATEEXECUTION',
  1729.                         $order->getHash(),
  1730.                         [
  1731.                             'was' => $order->getPayDateExecution()->format('Y-m-d H:i:s'),
  1732.                             'now' => $d->format('Y-m-d H:i:s'),
  1733.                         ]
  1734.                     );
  1735.                     $order->setPayDateExecution($d);
  1736.                 }
  1737.                 // если сроки все же не проставлены, а статус заказа не черновик ставим старые
  1738.                 if ($order->getStatus() && $order->getStatus() > BuyOrderStatusEnum::AWAITING_CALCULATION) {
  1739.                     if ($order->getDeliveryTimeMin() == null) {
  1740.                         $order->setDeliveryTimeMin($order->getDeliveryOldTimeMin());
  1741.                     }
  1742.                     if ($order->getDeliveryTimeMax() == null) {
  1743.                         $order->setDeliveryTimeMax($order->getDeliveryOldTimeMax());
  1744.                     }
  1745.                 }
  1746.             }
  1747.             // специальное письмо для образцов при возможности доставки раньше
  1748.             if (OrderHelper::samplesDeliveryFaster($order$oldDate)) {
  1749.                 $this->historyService->saveHistoryAndSendEmail(
  1750.                     $order,
  1751.                     'order_will_be_delivered_faster',
  1752.                     OrderHistoryService::SEND_EMAIL,
  1753.                     ['%oldTimeDelivery%' => $oldTimeDelivery]
  1754.                 );
  1755.                 $order->setDeliveryTimeType(DeliveryTypeTimeEnum::CONFIRMED);
  1756.             }
  1757.             // отправка отложенного письма
  1758.             if (
  1759.                 $order->getKeyEmail() != null
  1760.                 && $order->getStatus()
  1761.                 && BuyOrderStatusEnum::isRequiredNotice($order->getStatus())
  1762.             ) {
  1763.                 // отправляем письмо о сбросе, только если сроки не рассчитались
  1764.                 // и если есть артикулы в заказе
  1765.                 if ($order->getDeliveryTimeMin() == null && $order->getArticles()->count() > 0) {
  1766.                     $this->historyService->saveHistoryAndSendEmail(
  1767.                         $order,
  1768.                         $order->getKeyEmail(),
  1769.                         OrderHistoryService::SEND_EMAIL
  1770.                     );
  1771.                     $order->setKeyEmail(null);
  1772.                 }
  1773.             }
  1774.         }
  1775.         if (!empty($data['deliveryOld'])) { //старый срок поставки по заказу
  1776.             if (!empty($data['deliveryOld']['timeMin'])) {
  1777.                 if ($timeMin DateHelper::standard($data['deliveryOld']['timeMin'])) {
  1778.                     $order->setDeliveryOldTimeMin($timeMin);
  1779.                 }
  1780.             }
  1781.             if (!empty($data['deliveryOld']['timeMax'])) {
  1782.                 if ($timeMax DateHelper::standard($data['deliveryOld']['timeMax'])) {
  1783.                     $order->setDeliveryOldTimeMax($timeMax);
  1784.                 }
  1785.             }
  1786.         }
  1787.         if (!empty($data['deliveryNew'])) { // новый срок поставки по заказу
  1788.             if (!empty($data['deliveryNew']['timeMin'])) {
  1789.                 if ($timeMin DateHelper::standard($data['deliveryNew']['timeMin'])) {
  1790.                     $order->setDeliveryTimeMin($timeMin);
  1791.                 }
  1792.             }
  1793.             if (!empty($data['deliveryNew']['timeMax'])) {
  1794.                 if ($timeMax DateHelper::standard($data['deliveryNew']['timeMax'])) {
  1795.                     $order->setDeliveryTimeMax($timeMax);
  1796.                 }
  1797.             }
  1798.         }
  1799.     }
  1800.     /**
  1801.      * Заполняет в заказе данные на основании ответа от "1С"
  1802.      * Не должен содержать бизнес логику
  1803.      *
  1804.      * @param BuyOrder $order
  1805.      * @param array $data
  1806.      * @throws Exception
  1807.      */
  1808.     private function setDataFromResponse(BuyOrder $order, array $data): array
  1809.     {
  1810.         // помечаем зеркало, через которое было заполнение данных
  1811.         $order->setMirror($this->mirror);
  1812.         // если еще не сохранялась старая версия данных заказа
  1813.         $this->oldOrder ??= clone $order;
  1814.         if (isset($data['dateCancel'])) {
  1815.             $order->setCancelDate(DateHelper::standard($data['dateCancel']));
  1816.         }
  1817.         $this->setManagerToOrder($order$data['manager'] ?? null);
  1818.         if (isset($data['showMSG'])) {
  1819.             $order->setShowMSG(!empty($data['showMSG']));
  1820.         }
  1821.         if (isset($data['reason'])) {
  1822.             $order->setWriteOffReason((int) $data['reason']);
  1823.         }
  1824.         if (isset($data['recipientNumber'])) {
  1825.             $order->setRecipientNumber(strip_tags($data['recipientNumber']));
  1826.         }
  1827.         if (isset($data['weightLimit'])) {
  1828.             $weightLimit = (float) str_replace(',''.'$data['weightLimit']);
  1829.             $order->setWeightLimit($weightLimit);
  1830.         }
  1831.         if (isset($data['customsPayment'])) {
  1832.             $customsPayment = (float) str_replace(',''.'$data['customsPayment']);
  1833.             $order->setCustomsPayment($customsPayment);
  1834.         }
  1835.         if (isset($data['number'])) {
  1836.             $order->setNumber($data['number']);
  1837.         }
  1838.         if (!empty($data['numberDonor'])) {
  1839.             $order->setNumberDonor($data['numberDonor']);
  1840.         }
  1841.         if (!empty($data['acceptPay'])) {
  1842.             $order->setAcceptPay((bool) $data['acceptPay']);
  1843.         }
  1844.         if (!empty($data['reDelivery'])) {
  1845.             $order->setDeliveryStatus(true);
  1846.         }
  1847.         if (!empty($data['reserveCreated'])) {
  1848.             $order->setReserve(new DateTime());
  1849.         }
  1850.         if (isset($data['carrier']['name'])) {
  1851.             $order->setCarrierName(empty($data['carrier']['name']) ? null $data['carrier']['name']);
  1852.         }
  1853.         if (isset($data['carrier']['contactData'])) {
  1854.             $order->setCarrierContactData(
  1855.                 empty($data['carrier']['contactData']) ? null $data['carrier']['contactData']
  1856.             );
  1857.         }
  1858.         if (isset($data['carrier']['tracking'])) {
  1859.             $order->setTrackingNumber(empty($data['carrier']['tracking']) ? null $data['carrier']['tracking']);
  1860.         }
  1861.         // дата отправки заказа
  1862.         if (!empty($data['sendingDate'])) {
  1863.             $order->setSendingDate(DateHelper::standard($data['sendingDate']));
  1864.         }
  1865.         // предполагаемая дата отгрузки
  1866.         if (!empty($data['dateStock'])) {
  1867.             $order->setShipDate(DateHelper::standard($data['dateStock']));
  1868.         }
  1869.         // предполагаемая дата отгрузки
  1870.         if (!empty($data['deliveryDateFact'])) {
  1871.             $order->setFactDate(DateHelper::standard($data['deliveryDateFact']));
  1872.         }
  1873.         if (isset($data['refNumber'])) {
  1874.             $order->setRefNumber($data['refNumber']);
  1875.         }
  1876.         if (!empty($data['sendReview'])) {
  1877.             $order->setSendReview($data['sendReview']);
  1878.         }
  1879.         if (!empty($data['warehouseAddress'])) {
  1880.             $warehouseCode = !empty($data['code']) ? $data['code'] : null;
  1881.             $warehouseAddress $order->getWarehouseData();
  1882.             $warehouseAddress['code'] = $warehouseCode ?: '';
  1883.             $warehouseAddress['schedule'] = !empty($data['schedule'])
  1884.                 ? ($warehouseCode $this->translationService->trans('schedule_' $warehouseCode) : $data['schedule'])
  1885.                 : '';
  1886.             $warehouseAddress['warehouseAddress'] = $warehouseCode
  1887.                 $this->translationService->trans('warehouse_' $warehouseCode)
  1888.                 : $data['warehouseAddress'];
  1889.             $warehouseAddress['warehouseContact'] = !empty($data['warehouseContact']) ? $data['warehouseContact'] : '';
  1890.             $warehouseAddress['warehouseSchema'] = !empty($data['warehouseSchema']) ? $data['warehouseSchema'] : '';
  1891.             if (isset($data['warehouseLanguage'])) {
  1892.                 $warehouseAddress['warehouseLanguage'] = !empty($data['warehouseLanguage']) ? $data['warehouseLanguage'] : '';
  1893.             }
  1894.             if (isset($data['warehouseEmail'])) {
  1895.                 $warehouseAddress['warehouseEmail'] = !empty($data['warehouseEmail']) ? $data['warehouseEmail'] : '';
  1896.             }
  1897.             $order->setWarehouseData($warehouseAddress);
  1898.         }
  1899.         $this->setDeliveryOrder($data$order);
  1900.         if (isset($data['proposedDate'])) {
  1901.             $order->setProposedDate(new DateTime($data['proposedDate']));
  1902.             // устанавливаем тип периода с конкретной датой поставки
  1903.             $order->setDeliveryTimeType(4);
  1904.             // ставим статус в пути
  1905.             $this->setStatus($orderBuyOrderStatusEnum::TRANSIT);
  1906.             $data['status'] = BuyOrderStatusEnum::TRANSIT;
  1907.             $tableParams = [];
  1908.             if (!empty($data['carrierPhone'])) {
  1909.                 $tableParams['carrierPhone'] = $data['carrierPhone'];
  1910.             }
  1911.             if (!empty($data['carrierSite'])) {
  1912.                 $tableParams['carrierSite'] = $data['carrierSite'];
  1913.             }
  1914.             if (!empty($data['proposedDateEx'])) {
  1915.                 $tableParams['proposedDateEx'] = $data['proposedDateEx'];
  1916.             }
  1917.             // если отправлен неполный заказ
  1918.             if (!empty($data['table2'])) {
  1919.                 $alias $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124840])
  1920.                     ? (OrderHelper::isCarrierData($order)
  1921.                         ? (!empty($data['proposedDateEx'])
  1922.                             ? 'particle_delivery_without_transitor_data_2b'
  1923.                             'calculated_date_for_article_delivery_usa_tracking'
  1924.                         )
  1925.                         : (!empty($data['proposedDateEx'])
  1926.                             ? 'particle_delivery_without_transitor_data_2a'
  1927.                             'calculated_date_for_article_delivery_usa'
  1928.                         )
  1929.                     )
  1930.                     : (OrderHelper::isCarrierData($order)
  1931.                         ? (!empty($data['proposedDateEx'])
  1932.                             ? 'particle_delivery_without_transitor_data_2b'
  1933.                             'calculated_date_article_delivery_tracking'
  1934.                         )
  1935.                         : (!empty($data['proposedDateEx'])
  1936.                             ? 'particle_delivery_without_transitor_data_2a'
  1937.                             'calculated_date_for_article_delivery'
  1938.                         )
  1939.                     );
  1940.                 // если дополнительная поставка заказа
  1941.                 if (isset($data['reDelivery']) && $data['reDelivery']) {
  1942.                     $alias $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124840])
  1943.                         ? (OrderHelper::isCarrierData($order)
  1944.                             ? 'additional_delivery_container_tk_data'
  1945.                             'additional_delivery_container_without_tk_data'
  1946.                         )
  1947.                         : (OrderHelper::isCarrierData($order)
  1948.                             ? 'without_using_container_segment_tk_data'
  1949.                             'without_using_container_segment_without_tk_data'
  1950.                         );
  1951.                 }
  1952.                 $tableParams['table2'] = $data['table2'];
  1953.             } else {
  1954.                 $alias $order->getDeliveryCountry() && in_array($order->getDeliveryCountry()->getId(), [124840])
  1955.                     ? (OrderHelper::isCarrierData($order)
  1956.                         ? (!empty($data['proposedDateEx'])
  1957.                             ? 'fully_delivery_without_transitor_data_1b'
  1958.                             'order_will_be_delivered_soon_tracking'
  1959.                         )
  1960.                         : (!empty($data['proposedDateEx'])
  1961.                             ? 'fully_delivery_without_transitor_data_1a'
  1962.                             'order_will_be_delivered_soon'
  1963.                         )
  1964.                     )
  1965.                     : (OrderHelper::isCarrierData($order)
  1966.                         ? (!empty($data['proposedDateEx'])
  1967.                             ? 'fully_delivery_without_transitor_data_1b'
  1968.                             'transit_tracking'
  1969.                         )
  1970.                         : (!empty($data['proposedDateEx'])
  1971.                             ? 'fully_delivery_without_transitor_data_1a'
  1972.                             'transit'
  1973.                         )
  1974.                     );
  1975.             }
  1976.             $this->historyService->saveHistoryAndSendEmail(
  1977.                 $order,
  1978.                 $alias,
  1979.                 OrderHistoryService::SEND_EMAIL,
  1980.                 $tableParams
  1981.             );
  1982.         }
  1983.         $params = [];
  1984.         if (!empty($data['fileBase64'])) {
  1985.             $params['fileBase64'] = $data['fileBase64'];
  1986.         }
  1987.         if (!empty($data['table2'])) {
  1988.             $params['table2'] = $data['table2'];
  1989.         }
  1990.         if (!empty($data['LetterVariant'])) {
  1991.             $this->historyService->sendInvoice($order$data);
  1992.         }
  1993.         if (!empty($data['compensation'])) {
  1994.             $order->setCompensation((float) str_replace(',''.'$data['compensation']));
  1995.             // Рома сказал не фиксировать возврат при назначении компенсации
  1996.         }
  1997.         if (!empty($data['sumComp'])) {
  1998.             $params['sumComp'] = $data['sumComp'];
  1999.         }
  2000.         if (!empty($data['sumCurrency'])) {
  2001.             $params['sumCurrency'] = $data['sumCurrency'];
  2002.         }
  2003.         // обязательно после функции setDeliveryOrder - установки сроков поставки
  2004.         if (!empty($data['status'])) {
  2005.             $newStatus = (int) $data['status'];
  2006.             // если оплата пришла от "1С" и дата действия платежа с просрочкой,
  2007.             // то надо слать метку в функцию смены статуса для отправки соответствующего письма
  2008.             $params['fromOneC'] = !empty($data['checkHash']);
  2009.             // при статусе 19 нужно так же передать фабрику, которая стала причиной отмены заказа
  2010.             if (!empty($data['factory'])) {
  2011.                 $params['%factory%'] = $data['factory'];
  2012.             }
  2013.             if ($newStatus === BuyOrderStatusEnum::PAID && !OrderHelper::checkPayDateExecution($order)) {
  2014.                 $params['oldPayment'] = true;
  2015.             }
  2016.             if (isset($data['table3'])) {
  2017.                 // если есть, то передаем данные физических параметров скомплектованного заказа
  2018.                 $params['table3'] = $data['table3'];
  2019.             }
  2020.             if (isset($data['sum'])) {
  2021.                 $params['sum'] = $data['sum'];
  2022.             }
  2023.             if (isset($data['underpayment'])) {
  2024.                 $params['underpayment'] = $data['underpayment'];
  2025.             }
  2026.             if (isset($data['sumOrder'])) {
  2027.                 $params['sumOrder'] = $data['sumOrder'];
  2028.             }
  2029.             if (!empty($data['reason'])) {
  2030.                 $params['reason'] = $data['reason'];
  2031.             }
  2032.             if (!empty($data['sumCurrency'])) {
  2033.                 $params['sumCurrency'] = $data['sumCurrency'];
  2034.             }
  2035.             if (!empty($data['proposedDateEx'])) {
  2036.                 $params['proposedDateEx'] = $data['proposedDateEx'];
  2037.             }
  2038.             if (
  2039.                 $newStatus !== BuyOrderStatusEnum::REQUEST_DELIVERY
  2040.                 || $order->getStatus() != BuyOrderStatusEnum::AWAITING_CALCULATION
  2041.             ) {
  2042.                 $this->setStatus($order$newStatus$params);
  2043.             }
  2044.         }
  2045.         return $params;
  2046.     }
  2047.     /**
  2048.      * Возвращает список адресов складов
  2049.      * @throws Exception
  2050.      */
  2051.     public function getPickupWarehouses(?bool $onlyId false): array
  2052.     {
  2053.         if ($onlyId) {
  2054.             return $this->warehouseService->getIdsPickupWarehouses();
  2055.         }
  2056.         return $this->warehouseService->getPickupWarehouses();
  2057.     }
  2058.     /**
  2059.      * Возвращает массив заказов клиента которые доступны для изменения
  2060.      *
  2061.      * @param string $token
  2062.      * @return array
  2063.      * @throws Exception
  2064.      */
  2065.     public function orderShortList(string $token): array
  2066.     {
  2067.         $list = [];
  2068.         /** @var BuyOrder $order */
  2069.         foreach ($this->buyOrderRepository->getBuyOrders(['token' => $token'status' => [123]]) as $order) {
  2070.             $list[] = $this->getProjectData($order);
  2071.         }
  2072.         return $list;
  2073.     }
  2074.     /**
  2075.      * Проверяет актуальность предложения
  2076.      *
  2077.      * @param BuyOrder $order
  2078.      * @param ?bool $noUpdate если true, то сброса и обновления не будет, вернёт только нужно ли обновление или нет
  2079.      * @return bool
  2080.      * @throws Exception
  2081.      */
  2082.     public function checkingOffer(BuyOrder $order, ?bool $noUpdate false): bool
  2083.     {
  2084.         if ($order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
  2085.             return false;
  2086.         }
  2087.         $change false;
  2088.         // todo убрать после теста
  2089.         $this->addressService->mergeAddresses($order);
  2090.         if ($order->getPaymentEuro() && $order->getCurrency() != 'EUR') {
  2091.             $session App::getRequest()->getSession();
  2092.             if (!$session->get($order->getHash() . '-curRate')) {
  2093.                 $curRate $this->em->getRepository('WebBundle:CurrencyRate')
  2094.                     ->findOneBy(['currency' => $order->getCurrency()]);
  2095.                 $curRate = (float) ($curRate $curRate->getVal() : 1);
  2096.                 $session->set($order->getHash() . '-curRate'$curRate);
  2097.                 $order->setCurRate($curRate);
  2098.                 $this->buyOrderRepository->save($order);
  2099.             }
  2100.         }
  2101.         // проверка изменений локализации
  2102.         $this->localization($order, []);
  2103.         $this->buyOrderRepository->save($order);
  2104.         // берем только заказы по которым не было оплаты и заказ в обработке или рассчитанные
  2105.         if (
  2106.             in_array($order->getStatus(), [BuyOrderStatusEnum::AWAITING_PAYMENTBuyOrderStatusEnum::AWAITING_CALCULATION])
  2107.             || $order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
  2108.             && OrderHelper::readyCreate($order)
  2109.         ) {
  2110.             if (
  2111.                 // обнуляем даты, если заказ не оплачен в установленный срок
  2112.                 !$order->getNoChange()
  2113.                 && (
  2114.                     $order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
  2115.                     && !OrderHelper::checkPayDateExecution($order)
  2116.                 )
  2117.             ) {
  2118.                 //                    отключили чтобы не "спамить" клиента по распоряжению Ромы Загорудько
  2119.                 //                    заказ был посчитан вручную
  2120.                 //                    if ($order->getDeliveryCalculateType() > 1) {
  2121.                 //                        $this->historyService->saveHistory($order, 'reset_not_pay', 0);
  2122.                 //                    }
  2123.                 // Сброшены сроки поставки в следствии истечения времени на оплату
  2124.                 $order $this->reset($orderReasonForResetOrderEnum::EXPIRATION_OF_THE_PAYMENT_TIME(), 'Reset time delivery, reason - expiration of the payment time');
  2125.                 $change true;
  2126.             } elseif (
  2127.                 // если изменились данные влияющие на доставку
  2128.                 !$order->getNoChange()
  2129.                 && $order->getMarkerReset() != OrderHelper::markerReset($order)
  2130.             ) {
  2131.                 // В заказе были изменения, влияющие на расчет доставки
  2132.                 $order $this->reset($orderReasonForResetOrderEnum::CHANGE_ORDER(), 'Reset time delivery, reason - change of data');
  2133.                 if ($order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION) { // был запрошен расчёт доставка
  2134.                     $this->historyService->saveHistoryAndSendEmail(
  2135.                         $order,
  2136.                         'reset_change',
  2137.                         OrderHistoryService::SEND_EMAIL
  2138.                     );
  2139.                 }
  2140.                 $change true;
  2141.             } elseif ($order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY && !OrderHelper::checkPayDateExecution($order)) {
  2142.                 $order $this->reset($orderReasonForResetOrderEnum::CHECK_PAY_DATE_EXECUTION(), 'Reset checkPayDateExecution');
  2143.                 $change true;
  2144.             }
  2145.         }
  2146.         if ($noUpdate || $this->asConsole) {
  2147.             return $change;
  2148.         }
  2149.         // были изменения в заказе, надо прогнать через change
  2150.         if (
  2151.             $change
  2152.             || !$order->getNoChange()
  2153.             && (
  2154.                 $order->getMarkerChange() != OrderHelper::md5MarkerChange($order)
  2155.                 || !$order->getSendCreate1C()
  2156.             )
  2157.         ) {
  2158.             $this->changeByOrder(
  2159.                 $order,
  2160.                 json_encode(OrderHelper::itemsArr($order, ['id' => $order->getHash()]))
  2161.             );
  2162.         }
  2163.         return $change;
  2164.     }
  2165.     /**
  2166.      * @param BuyOrder $order
  2167.      * @param array $data
  2168.      * @throws Exception
  2169.      */
  2170.     public function localization(BuyOrder $order, array $data)
  2171.     {
  2172.         $this->cloneOrderIfNeeded($order);
  2173.         if (!$order->getUser() && $order->getAddressRecipient() && $order->getAddressRecipient()->getUser()) {
  2174.             $order->setUser($order->getAddressRecipient()->getUser());
  2175.         }
  2176.         // эти данные меняет только хозяин заказа
  2177.         if ($this->token !== $order->getToken()) {
  2178.             return;
  2179.         }
  2180.         if (!empty($data['locale'])) {
  2181.             $langArr explode('|'App::getParameter('langs_available'));
  2182.             if (count($langArr) > 0) {
  2183.                 if (!in_array($data['locale'], $langArr)) {
  2184.                     $data['locale'] = $langArr[0];
  2185.                 }
  2186.                 if ($order->getLocale() != $data['locale']) {
  2187.                     $curDate = new DateTime('NOW');
  2188.                     $this->log(
  2189.                         'CHANGE LOCALE',
  2190.                         $order->getHash(),
  2191.                         [
  2192.                             'oldLocale' => $order->getLocale(),
  2193.                             'newLocale' => $data['locale'],
  2194.                             'date' => $curDate->format('Y-m-d H:i:s'),
  2195.                         ]
  2196.                     );
  2197.                     $order->setLocale($data['locale']);
  2198.                 }
  2199.             }
  2200.         }
  2201.         // получаем код страны
  2202.         $codeISO StrHelper::toLower(
  2203.             $order->getAddressRecipient() && $order->getAddressRecipient()->getCountry()
  2204.                 ? $order->getAddressRecipient()->getCountry()->getCode()
  2205.                 : App::getCurCountry()
  2206.         );
  2207.         $currencyArr LocaleHelper::getCurrencyList($codeISO);
  2208.         $measureArr LocaleHelper::getMeasureList($codeISO);
  2209.         $data['currency'] = empty($data['currency']) ? $order->getCurrency() : $data['currency'];
  2210.         $data['measure'] = empty($data['measure']) ? $order->getMeasure() : $data['measure'];
  2211.         // для США и канады надо сис счисления брать из url в новом заказе
  2212.         if (
  2213.             in_array($codeISO, ['us''ca'])
  2214.             && $order->getMeasure() != CookieHelper::get(CookieKeysConstant::MEASURE_FT)
  2215.         ) {
  2216.             $data['measure'] = CookieHelper::get(CookieKeysConstant::MEASURE_FT);
  2217.         }
  2218.         // для России, в случае, если заказ образцов, то валюту ставим Euro
  2219.         if (OrderHelper::onlySamples($order) && $codeISO == 'ru') {
  2220.             $data['currency'] = 'EUR';
  2221.         }
  2222.         if (count($currencyArr) > 0) {
  2223.             // для России в случае, если заказ образцов, то валюту ставим Euro
  2224.             if (OrderHelper::onlySamples($order) && $codeISO == 'ru') {
  2225.                 $currencyArr[] = 'EUR';
  2226.             }
  2227.             if (!in_array($data['currency'], $currencyArr)) {
  2228.                 $data['currency'] = $currencyArr[0];
  2229.             }
  2230.             if ($order->getCurrency() != $data['currency']) {
  2231.                 $order->setCurrency($data['currency']);
  2232.             }
  2233.         }
  2234.         if (count($measureArr) > 0) {
  2235.             if (!in_array($data['measure'], $measureArr)) {
  2236.                 $data['measure'] = $measureArr[0];
  2237.             }
  2238.             if ($order->getMeasure() != $data['measure']) {
  2239.                 $order->setMeasure($data['measure']);
  2240.             }
  2241.         }
  2242.         // обрабатываем данные по пользователю
  2243.         $this->setUserData($order);
  2244.     }
  2245.     /**
  2246.      * Возвращает название для заказа
  2247.      *
  2248.      * @param string $token Токен пользователя
  2249.      * @param string $name Текущее название
  2250.      * @param ?int $id Идентификатор заказа
  2251.      * @return string
  2252.      * @throws Exception
  2253.      */
  2254.     protected function getNameOrder(string $tokenstring $name, ?int $id null): string
  2255.     {
  2256.         $name strip_tags(urldecode($name));
  2257.         $orders $this->buyOrderRepository->getNames($token$id);
  2258.         $locale App::getCurLocale();
  2259.         /** @var $order BuyOrder */
  2260.         $names = [];
  2261.         foreach ($orders as $order) {
  2262.             $names[] = str_replace(
  2263.                 '%orderName%',
  2264.                 $this->translationService->trans('buyOrder.name' . ($order['status'] < ''), $locale),
  2265.                 $order['name']
  2266.             );
  2267.         }
  2268.         $newName $name;
  2269.         $num 1;
  2270.         while (in_array($newName$names)) {
  2271.             $num++;
  2272.             $newName $name ' (' $num ')';
  2273.         }
  2274.         return $newName;
  2275.     }
  2276.     /**
  2277.      * @param string $url
  2278.      * @param array|null $data
  2279.      * @return Response
  2280.      * @throws Exception
  2281.      */
  2282.     public function getPdfInPage(string $url, ?array $data = []): Response
  2283.     {
  2284.         $snappy App::getContainer()->get('knp_snappy.pdf');
  2285.         $session App::getRequest()->getSession();
  2286.         $session->save();
  2287.         $snappy->setTimeout(120);
  2288.         $orderNumber $data['orderNumber'] ?? '';
  2289.         return new Response(
  2290.             $snappy->getOutput(
  2291.                 $url,
  2292.                 [
  2293.                     'orientation' => 'Landscape',
  2294.                     'page-size' => 'A4',
  2295.                     'print-media-type' => true,
  2296.                     'cookie' => $_COOKIE,
  2297.                 ]
  2298.             ),
  2299.             200,
  2300.             [
  2301.                 'Content-Type' => 'application/pdf',
  2302.                 'Content-Disposition' => 'attachment; filename="tile.expert-' $orderNumber '.pdf"',
  2303.             ]
  2304.         );
  2305.     }
  2306.     /**
  2307.      *
  2308.      * @param $order
  2309.      * @return Response
  2310.      * @throws LoaderError
  2311.      * @throws RuntimeError
  2312.      * @throws SyntaxError
  2313.      * @throws Exception
  2314.      */
  2315.     public function getPdfOrder($order): Response
  2316.     {
  2317.         /** @var BuyOrder $order */
  2318.         $snappy App::getContainer()->get('knp_snappy.pdf');
  2319.         $itemsData OrderHelper::dataForLetter($order);
  2320.         $html App::getTwig()->render('@Web/BuyOrder/order-pdf.html.twig', [
  2321.             'order' => $order,
  2322.             'articles' => $itemsData,
  2323.             'curLocale' => App::getCurLocale(),
  2324.         ]);
  2325.         $html str_replace('"/''"' App::getParameter('full_domain') . '/'$html);
  2326.         return new PdfResponse(
  2327.             $snappy->getOutputFromHtml($html),
  2328.             'tile.expert-' $order->getNumber() . '.pdf'
  2329.         );
  2330.     }
  2331.     /**
  2332.      * @param BuyOrder $order
  2333.      * @return Response
  2334.      * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  2335.      */
  2336.     public function convertToExcel($order): Response
  2337.     {
  2338.         $items OrderHelper::dataForLetter($order);
  2339.         $orderNumber $order->getNumber() ?: $order->getHash();
  2340.         ob_start();
  2341.         $spreadsheet = new Spreadsheet();
  2342.         $sheet $spreadsheet->getActiveSheet();
  2343.         $sheet->getColumnDimension('A')->setWidth(100);
  2344.         $sheet->getColumnDimension('B')->setWidth(30);
  2345.         $sheet->getColumnDimension('C')->setWidth(30);
  2346.         $sheet->getColumnDimension('D')->setWidth(30);
  2347.         $sheet->getColumnDimension('E')->setWidth(30);
  2348.         $sheet->getColumnDimension('F')->setWidth(30);
  2349.         $sheet->setCellValue('A1'$this->translationService->trans('buyOrder.th.article'));
  2350.         $sheet->setCellValue(
  2351.             'B1',
  2352.             $this->translationService->trans('buyOrder.th.factory') . " / " $this->translationService->trans('buyOrder.th.collection')
  2353.         );
  2354.         $sheet->setCellValue('C1'$this->translationService->trans('buyOrder.th.price'));
  2355.         $sheet->setCellValue('D1'$this->translationService->trans('buyOrder.th.quality'));
  2356.         $sheet->setCellValue('E1'$this->translationService->trans('buyOrder.th.amount'));
  2357.         $sheet->setCellValue('F1'$this->translationService->trans('buyOrder.info.delivery.estimated_date'));
  2358.         $counter 2;
  2359.         foreach ($items as $item) {
  2360.             $itemSize '';
  2361.             if ($item['type'] == 3) {
  2362.                 $sampleType OrderItemHelper::humanSampleType($item['sampleType']);
  2363.                 $itemSize .= $sampleType['name'];
  2364.                 $itemSize .= ' ' OrderItemHelper::sampleFormat(
  2365.                     $item,
  2366.                     $item['orderParam'],
  2367.                     $item['orderMeasure'] == 'ft'
  2368.                 );
  2369.             } else {
  2370.                 if ($item['orderMeasure'] == 'ft') {
  2371.                     $itemSize .= '~' ConversionHelper::convertInch((float) $item['sizeX'], ConversionHelper::CM) .
  2372.                         '″х' ConversionHelper::convertInch((float) $item['sizeY'], ConversionHelper::CM) . '″';
  2373.                 } else {
  2374.                     $itemSize .= $item['sizeX'] . 'x' $item['sizeY'] . ' ' $this->translationService->trans('left_menu_cm');
  2375.                 }
  2376.             }
  2377.             $itemSize html_entity_decode(strip_tags($itemSize));
  2378.             $material $item['material'] ? $this->translationService->trans($item['material']->getAlias()) : '';
  2379.             $sheet->setCellValue(
  2380.                 'A' $counter,
  2381.                 $material ' ' $item['name'] . ' ' .
  2382.                 $this->translationService->trans('collection_by') . ' ' $item['collection']->getFactory()->getName() . ' ' $itemSize
  2383.             );
  2384.             $factoryName $item['collection']->getFactory() ? $item['collection']->getFactory()->getActualName() : "";
  2385.             $sheet->setCellValue(
  2386.                 'B' $counter,
  2387.                 $item['collection']->getActualName() . " / " $factoryName
  2388.             );
  2389.             $sheet->setCellValue(
  2390.                 'C' $counter,
  2391.                 $item['price'] . ' ' $item['currency'] . '/' $this->translationService->trans($item['measure'])
  2392.             );
  2393.             $measure = (isset($item['measure']) && !empty($item['measure'])) ? $this->translationService->trans($item['measure']) : '';
  2394.             $boxes = ($item['type'] == 1) ? $item['boxes'] : 1;
  2395.             $sheet->setCellValue(
  2396.                 'D' $counter,
  2397.                 (floor($item['amount'] * 100) / 100) . ' ' $measure ' / ' $boxes
  2398.             );
  2399.             $sheet->setCellValue('E' $counterround($item['price'] * $item['amount'], 2) . ' ' $item['currency']);
  2400.             $sheet->setCellValue('F' $counterstrip_tags(OrderItemHelper::getItemTime($item['article'])));
  2401.             $counter++;
  2402.         }
  2403.         $summary $this->getOrderSummary($order);
  2404.         foreach ($summary as $item) {
  2405.             $sheet->setCellValue(
  2406.                 'A' . (++$counter),
  2407.                 $item["title"]
  2408.             );
  2409.             $value html_entity_decode(strip_tags($item["value"]));
  2410.             $sheet->setCellValue(
  2411.                 'B' . ($counter),
  2412.                 $value
  2413.             );
  2414.         }
  2415.         $writer = new Xlsx($spreadsheet);
  2416.         $writer->save('php://output');
  2417.         return new Response(
  2418.             ob_get_clean(),
  2419.             200,
  2420.             [
  2421.                 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  2422.                 'Content-Disposition' => 'attachment; filename="tile.expert-' $orderNumber '.xlsx"',
  2423.             ]
  2424.         );
  2425.     }
  2426.     /**
  2427.      * Return order summary values
  2428.      *
  2429.      * @param BuyOrder $order
  2430.      * @return array
  2431.      * @throws Exception
  2432.      */
  2433.     public function getOrderSummary(BuyOrder $order): array
  2434.     {
  2435.         $sum OrderHelper::sum($order);
  2436.         $vatCondition OrderHelper::asVat($order) == false || OrderHelper::homeCountryVat($order);
  2437.         $payType 'buyOrder.info.payment_cost.payPal';
  2438.         switch ($order->getPayType()) {
  2439.             case PaymentTypeEnum::BANK_TRANSFER:
  2440.                 $payType 'buyOrder.info.payment_cost.Bank';
  2441.                 break;
  2442.             case PaymentTypeEnum::VISA_MASTERCARD:
  2443.                 $payType 'buyOrder.info.payment_cost.Card';
  2444.                 break;
  2445.         }
  2446.         $summary = [
  2447.             "goods" => [
  2448.                 "title" => 'buyOrder.info.goods',
  2449.                 "value" => $vatCondition $sum['all'] : $sum['sum'],
  2450.                 "currency" => true,
  2451.             ],
  2452.             "delivery_cost" => [
  2453.                 "title" => 'buyOrder.info.delivery_cost',
  2454.                 "value" => $order->getDelivery() !== null $order->getDelivery() : $this->translationService->trans(
  2455.                     'order.status.request_delivery'
  2456.                 ),
  2457.                 "currency" => LocaleHelper::getCurrency($order->getCurrency()),
  2458.             ],
  2459.             "vat" => [
  2460.                 "title" => 'buyOrder.info.VAT.row-name',
  2461.                 "value" => OrderHelper::vatPercent($order),
  2462.                 "percents" => true,
  2463.             ],
  2464.             "payment_percent" => [
  2465.                 "title" => $payType,
  2466.                 "value" => OrderHelper::paymentPercent($order),
  2467.                 "currency" => true,
  2468.             ],
  2469.             "total" => [
  2470.                 "title" => 'buyOrder.info.total',
  2471.                 "value" => OrderHelper::totalSum($order),
  2472.                 "currency" => true,
  2473.             ],
  2474.             "delivery_to" => [
  2475.                 "title" => 'order.delivery_to',
  2476.                 "value" => preg_replace(
  2477.                     "/(^,)|(,$)/",
  2478.                     "",
  2479.                     join(',', [
  2480.                         $order->getDeliveryIndex(),
  2481.                         $order->getDeliveryCountry(),
  2482.                         $order->getDeliveryCity(),
  2483.                     ])
  2484.                 ),
  2485.             ],
  2486.         ];
  2487.         foreach ($summary as $key => &$item) {
  2488.             $item["title"] = preg_replace('/%d% /'''$this->translationService->trans($item["title"]));
  2489.             if (!empty($item["currency"])) {
  2490.                 $item["value"] .= " " LocaleHelper::getCurrency($order->getCurrency());
  2491.             } else {
  2492.                 if (!empty($item["percents"])) {
  2493.                     $item["value"] .= " %";
  2494.                 }
  2495.             }
  2496.         }
  2497.         return $summary;
  2498.     }
  2499.     /**
  2500.      * @param string $hash
  2501.      * @return array
  2502.      * @throws NonUniqueResultException
  2503.      * @throws Exception
  2504.      */
  2505.     public function delAllData(string $hash): array
  2506.     {
  2507.         $res = [
  2508.             'user' => false,
  2509.             'orders' => 0,
  2510.         ];
  2511.         $order $this->getOrderObj($hashtrue);
  2512.         if (!$this->isAccessAdminOrder($order)) {
  2513.             throw new ForbiddenOverwriteException('Access forbidden!');
  2514.         }
  2515.         $user $order->getUser();
  2516.         if ($user) {
  2517.             $res['user'] = true;
  2518.             // Пишем лог по пользователю
  2519.             $this->log(
  2520.                 'DELALLDATAUSER',
  2521.                 $user->getId(),
  2522.                 $this->logMasterUser([
  2523.                     'id' => $user->getId(),
  2524.                     'unid' => $user->getUnid(),
  2525.                     'email' => $user->getEmail(),
  2526.                     'token' => $user->getToken(),
  2527.                 ])
  2528.             );
  2529.             $this->em->remove($user);
  2530.         }
  2531.         foreach ($this->buyOrderRepository->findByToken($order->getToken() ?? '') as $o) {
  2532.             //Пишем лог по заказу
  2533.             $this->log(
  2534.                 'DELALLDATAORDER',
  2535.                 $o->getHash(),
  2536.                 OrderHelper::arrayData($otrue)
  2537.             );
  2538.             $this->em->remove($o);
  2539.             $res['orders']++;
  2540.         }
  2541.         $res['msg'] = 'Deleted!';
  2542.         $this->em->flush();
  2543.         return $res;
  2544.     }
  2545.     /**
  2546.      * @param string $hash
  2547.      * @return RedirectResponse
  2548.      * @throws NonUniqueResultException
  2549.      * @throws Exception
  2550.      */
  2551.     public function recoveryCancelOrder(string $hash): RedirectResponse
  2552.     {
  2553.         $order $this->getOrderObj($hashtrue);
  2554.         if (!$this->isAccessAdminOrder($order)) {
  2555.             throw new ForbiddenOverwriteException('Access forbidden!');
  2556.         }
  2557.         $order->setStatus(BuyOrderStatusEnum::REQUEST_DELIVERY);
  2558.         $this->buyOrderRepository->save($order);
  2559.         $this->log(
  2560.             'RECOVERYORDER',
  2561.             $order->getHash(),
  2562.             $this->logMasterUser(OrderHelper::arrayData($order))
  2563.         );
  2564.         return new RedirectResponse(App::generateUrl('app_buy_order', ['orderId' => $hash]));
  2565.     }
  2566.     /**
  2567.      * Проверка доступа
  2568.      * @param BuyOrder $order
  2569.      * @return bool
  2570.      * @throws Exception
  2571.      */
  2572.     public function isAccessAdminOrder(BuyOrder $order): bool
  2573.     {
  2574.         $masterUser App::getCurUser();
  2575.         if (
  2576.             $masterUser
  2577.             && ($this->authorizationChecker->isGranted('ROLE_CONS')
  2578.                 && (
  2579.                     StrHelper::toLower($masterUser->getEmail()) == StrHelper::toLower($order->getManagerEmail())
  2580.                     || StrHelper::toLower($masterUser->getUsername()) == StrHelper::toLower($order->getManagerLogin())
  2581.                 )
  2582.                 || $this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')
  2583.             )
  2584.             && ($order->getUser() == null
  2585.                 || strpos(StrHelper::toLower($order->getUser()->getEmail()), 'tile.expert') === false
  2586.                 && strpos(StrHelper::toLower($order->getUser()->getEmail()), 'treto.ru') === false
  2587.             )
  2588.         ) {
  2589.             return true;
  2590.         }
  2591.         return false;
  2592.     }
  2593.     /**
  2594.      * Проверка, является ли выполняемое изменение статуса запрещенным
  2595.      * @param int $status
  2596.      * @param BuyOrder $order
  2597.      * @return bool
  2598.      * @see OrderServiceSetStatusTest::testIsForbiddenStatusChange()
  2599.      *
  2600.      */
  2601.     private function isForbiddenStatusChange(int $statusBuyOrder $order): bool
  2602.     {
  2603.         /* Смена статуса заказа в черновик */
  2604.         if (
  2605.             $status BuyOrderStatusEnum::PARTIALLY_PAID
  2606.             && $order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT
  2607.         ) {
  2608.             return true;
  2609.         }
  2610.         /* Проверка, является ли новый статус допустимым при нынешнем статусе "Частично оплачено" */
  2611.         /* Входит ли новый статус в список запрещённых для смены из частично оплаченного */
  2612.         if (
  2613.             $order->getStatus() === BuyOrderStatusEnum::PARTIALLY_PAID
  2614.             && in_array(
  2615.                 $status,
  2616.                 [
  2617.                     BuyOrderStatusEnum::REQUEST_DELIVERY,
  2618.                     BuyOrderStatusEnum::AWAITING_CALCULATION,
  2619.                     BuyOrderStatusEnum::AWAITING_PAYMENT,
  2620.                     BuyOrderStatusEnum::DELETED,
  2621.                 ]
  2622.             )
  2623.         ) {
  2624.             return true;
  2625.         }
  2626.         return false;
  2627.     }
  2628.     /**
  2629.      * Добавляет инфо о пользователе инициировавшем обработку
  2630.      * @param array $data
  2631.      * @return array
  2632.      * @throws Exception
  2633.      */
  2634.     private function logMasterUser(array $data): array
  2635.     {
  2636.         $user App::getCurUser();
  2637.         return array_merge($data, [
  2638.             'masterUser' => [
  2639.                 'token' => $this->token,
  2640.                 'unid' => $user $user->getUnid() : null,
  2641.                 'username' => $user $user->getUsername() : null,
  2642.             ],
  2643.         ]);
  2644.     }
  2645.     /**
  2646.      * Добавление/изменение артикула
  2647.      *
  2648.      * @param int $itemId
  2649.      * @param array $ordersAmount
  2650.      * @param null|float $itemCount
  2651.      * @param int|null $type - тип позиции
  2652.      * @return array|bool
  2653.      * @throws NonUniqueResultException
  2654.      * @throws ORMException
  2655.      * @throws Exception
  2656.      */
  2657.     public function changeElement(
  2658.         int $itemId,
  2659.         array $ordersAmount,
  2660.         ?float $itemCount null,
  2661.         ?int $type BuyOrderArticle::TYPE_NORMAL
  2662.     ) {
  2663.         // заказ можно менять только до момента оплаты
  2664.         $params['status'] = [123];
  2665.         $params['token'] = $this->token;
  2666.         if (count($ordersAmount) > 0) {
  2667.             $params['hashes'] = array_keys($ordersAmount);
  2668.         }
  2669.         $item $this->articleRepository->getArticle($itemId);
  2670.         if (!$item) {
  2671.             return false;
  2672.         }
  2673.         $addedToCart $item->getAddedToCart();
  2674.         $count 0;
  2675.         $orders = [];
  2676.         $clientExistsOrders $this->buyOrderRepository->getBuyOrders($params);
  2677.         foreach ($clientExistsOrders as $order) {
  2678.             /** @var BuyOrder $order */
  2679.             if (
  2680.                 OrderHelper::canIAddThatTypeToOrder($order$type ?? BuyOrderArticle::TYPE_NORMAL)
  2681.                 && (in_array($order->getHash(), $params['hashes'] ?? [])
  2682.                     || $order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
  2683.                     || (
  2684.                         $order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
  2685.                         && $order->getDeliveryCalculateType() !== 0
  2686.                     )
  2687.                 )
  2688.             ) {
  2689.                 $orders[] = $order;
  2690.                 continue;
  2691.             }
  2692.             if (isset($ordersAmount[$order->getHash()]) && $ordersAmount[$order->getHash()] === 0) {
  2693.                 $orders[] = $order;
  2694.             }
  2695.         }
  2696.         // если у клиента еще нет заказа, или нет заказа, в который можем добавить, создаем новый
  2697.         if (empty($orders)) {
  2698.             $orders[] = $this->create($this->token);
  2699.         }
  2700.         // для установки типа оплаты по умолчанию
  2701.         if (
  2702.             $type === BuyOrderArticle::TYPE_SAMPLE
  2703.             && count($orders) === 1
  2704.             && current($orders)->getArticles()->count() == 0
  2705.         ) {
  2706.             $order current($orders);
  2707.             $order->setPayType(OrderHelper::country($order) == 643 PaymentTypeEnum::VISA_MASTERCARD PaymentTypeEnum::BANK_TRANSFER);
  2708.         }
  2709.         $projects = [];
  2710.         $ordersHasSample = [];
  2711.         foreach ($orders as $order) {
  2712.             $orderHash $order->getHash();
  2713.             // ищем артикул в заказе
  2714.             $orderItem null;
  2715.             /** @var BuyOrderArticle|null $orderItem */
  2716.             if ($order->getArticles()) {
  2717.                 /** @var BuyOrderArticle $oneArticleInCurrentOrder */
  2718.                 foreach ($order->getArticles() as $oneArticleInCurrentOrder) {
  2719.                     if (
  2720.                         $oneArticleInCurrentOrder->getArticle()->getId() === $item->getId()
  2721.                         && $oneArticleInCurrentOrder->getType() === ($type ?? BuyOrderArticle::TYPE_NORMAL)
  2722.                     ) {
  2723.                         $orderItem $oneArticleInCurrentOrder;
  2724.                         break;
  2725.                     }
  2726.                 }
  2727.                 $count += $order->getArticles()->count();
  2728.             }
  2729.             // если не установлено количество для выбранных заказов, ставим 0.1
  2730.             if (
  2731.                 !array_key_exists($orderHash$ordersAmount)
  2732.                 || (
  2733.                     empty($ordersAmount[$orderHash])
  2734.                     && !empty($itemCount)
  2735.                 )
  2736.             ) {
  2737.                 $ordersAmount[$orderHash] = !empty($itemCount) ? $itemCount 0.1;
  2738.             }
  2739.             if (!empty($ordersAmount[$orderHash])) {
  2740.                 $amount round(
  2741.                     LocaleHelper::getAmount(
  2742.                         $item,
  2743.                         (float) $ordersAmount[$orderHash],
  2744.                         OrderHelper::params(
  2745.                             $order,
  2746.                             $item->getMeasure()
  2747.                                 ? ['measureId' => $item->getMeasure()->getId()]
  2748.                                 : []
  2749.                         ),
  2750.                         $type
  2751.                     ),
  2752.                     5
  2753.                 );
  2754.                 // еще не добавляли артикул в заказ
  2755.                 if (null === $orderItem) {
  2756.                     $key 'ADDITEM';
  2757.                     $item->setAddedToCart($addedToCart 1);
  2758.                     $orderItem $this->itemService->createOrderItem($item$type$order);
  2759.                     $this->itemService->setItemData($order$orderItem$amount$type);
  2760.                     if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
  2761.                         // Проверка глобальных остатков образца
  2762.                         $sampleCode $orderItem->chooseCode();
  2763.                         $country OrderHelper::country($order);
  2764.                         try {
  2765.                             $sampleData $this->sampleService->getSampleData(
  2766.                                 $sampleCode,
  2767.                                 $country,
  2768.                                 false
  2769.                             );
  2770.                             $availableAmount = (float) ($sampleData['amountRem'] ?? 0);
  2771.                             $reservedCount $this->getReservedSampleCount($sampleCode$order->getHash());
  2772.                             $requestedAmount = (float) $orderItem->getAmount();
  2773.                             $maxAvailable max(0$availableAmount $reservedCount);
  2774.                             if ($reservedCount $requestedAmount $availableAmount) {
  2775.                                 if ($maxAvailable <= 0) {
  2776.                                     $order->removeArticle($orderItem);
  2777.                                     $this->em->remove($orderItem);
  2778.                                     $this->em->flush();
  2779.                                     return [
  2780.                                         'msg' => [
  2781.                                             'header' => $this->translationService->trans(
  2782.                                                 'sample_not_enough_stock.header',
  2783.                                                 App::getCurLocale(),
  2784.                                                 [
  2785.                                                     '%article%' => $sampleCode,
  2786.                                                 ]
  2787.                                             ),
  2788.                                             'body' => $this->translationService->trans(
  2789.                                                 'sample_not_enough_stock.body',
  2790.                                                 App::getCurLocale(),
  2791.                                                 [
  2792.                                                     '%article%' => $sampleCode,
  2793.                                                     '%available%' => 0,
  2794.                                                 ]
  2795.                                             ),
  2796.                                         ],
  2797.                                         'count' => $count,
  2798.                                         'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  2799.                                         'projects' => $projects,
  2800.                                     ];
  2801.                                 }
  2802.                                 $orderItem->setAmount($maxAvailable);
  2803.                                 $this->em->flush();
  2804.                             }
  2805.                         } catch (\Exception $e) {
  2806.                             // Если не удалось получить данные образца из 1C, продолжаем без проверки
  2807.                             $this->logger->error('Failed to check sample availability: ' $e->getMessage());
  2808.                         }
  2809.                         foreach ($order->getArticles() as $oItem) {
  2810.                             if ($orderItem->getId() == $oItem->getId()) {
  2811.                                 continue;
  2812.                             }
  2813.                             if (
  2814.                                 $oItem->getType() == BuyOrderArticle::TYPE_SAMPLE
  2815.                                 && $res $this->checkingDoubleSample(
  2816.                                     $orderItem,
  2817.                                     (int) $oItem->getId(),
  2818.                                     (string) $oItem->chooseCode()
  2819.                                 )
  2820.                             ) {
  2821.                                 $ordersHasSample[] = $res;
  2822.                                 break;
  2823.                             }
  2824.                         }
  2825.                     }
  2826.                     $count++;
  2827.                 } elseif (round($amount5) != round($orderItem->getAmount(), 5)) {
  2828.                     $key 'CHANGEITEM';
  2829.                     $this->itemService->setItemData($order$orderItem$amount$orderItem->getType());
  2830.                     if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
  2831.                         $sampleCode $orderItem->chooseCode();
  2832.                         $country OrderHelper::country($order);
  2833.                         try {
  2834.                             $sampleData $this->sampleService->getSampleData(
  2835.                                 $sampleCode,
  2836.                                 $country,
  2837.                                 false
  2838.                             );
  2839.                             $availableAmount = (float) ($sampleData['amountRem'] ?? 0);
  2840.                             $reservedCount $this->getReservedSampleCount($sampleCode$order->getHash());
  2841.                             $requestedAmount = (float) $orderItem->getAmount();
  2842.                             $maxAvailable max(0$availableAmount $reservedCount);
  2843.                             if ($reservedCount $requestedAmount $availableAmount) {
  2844.                                 if ($maxAvailable <= 0) {
  2845.                                     $order->removeArticle($orderItem);
  2846.                                     $this->em->remove($orderItem);
  2847.                                     $this->em->flush();
  2848.                                     return [
  2849.                                         'msg' => [
  2850.                                             'header' => $this->translationService->trans(
  2851.                                                 'sample_not_enough_stock.header',
  2852.                                                 App::getCurLocale(),
  2853.                                                 [
  2854.                                                     '%article%' => $sampleCode,
  2855.                                                 ]
  2856.                                             ),
  2857.                                             'body' => $this->translationService->trans(
  2858.                                                 'sample_not_enough_stock.body',
  2859.                                                 App::getCurLocale(),
  2860.                                                 [
  2861.                                                     '%article%' => $sampleCode,
  2862.                                                     '%available%' => 0,
  2863.                                                 ]
  2864.                                             ),
  2865.                                         ],
  2866.                                         'count' => $count,
  2867.                                         'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  2868.                                         'projects' => $projects,
  2869.                                     ];
  2870.                                 }
  2871.                                 $orderItem->setAmount($maxAvailable);
  2872.                                 $this->em->flush();
  2873.                             }
  2874.                         } catch (\Exception $e) {
  2875.                             $this->logger->error('Failed to check sample availability on update: ' $e->getMessage());
  2876.                         }
  2877.                     }
  2878.                     $count++;
  2879.                 } else {
  2880.                     // ТК ничего не изменилось - не будем логировать и сохранять
  2881.                     continue;
  2882.                 }
  2883.                 $this->log(
  2884.                     $key,
  2885.                     $order->getHash(),
  2886.                     [
  2887.                         'name' => $orderItem->getArticle()->getName(),
  2888.                         'amount' => $orderItem->getAmount(),
  2889.                         'type' => $orderItem->getType(),
  2890.                         'measure' => $orderItem->getMeasure(),
  2891.                         'currency' => $orderItem->getCurrency(),
  2892.                     ]
  2893.                 );
  2894.                 $this->setStatus($orderBuyOrderStatusEnum::REQUEST_DELIVERY);
  2895.                 try {
  2896.                     // что-то изменили - зафиксируем
  2897.                     $this->em->persist($orderItem);
  2898.                     $this->em->persist($item);
  2899.                     $this->em->persist($order);
  2900.                     $this->em->flush();
  2901.                 } catch (Exception $exception) {
  2902.                     $this->log(
  2903.                         'CRASH_UPDATE_ORDER',
  2904.                         $order->getHash(),
  2905.                         [
  2906.                             'name' => $orderItem->getArticle()->getName(),
  2907.                             'amount' => $orderItem->getAmount(),
  2908.                             'type' => $orderItem->getType(),
  2909.                             'measure' => $orderItem->getMeasure(),
  2910.                             'currency' => $orderItem->getCurrency(),
  2911.                         ]
  2912.                     );
  2913.                     throw $exception;
  2914.                 }
  2915.                 // просто сбрасываем в 1С - что заказ изменён
  2916.                 $this->reset($orderReasonForResetOrderEnum::CHANGE_ORDER(), 'CHANGE ORDER');
  2917.             }
  2918.             $projects[] = $this->getProjectData($order);
  2919.             ;
  2920.         }
  2921.         $ordersHasSampleCount count($ordersHasSample);
  2922.         if ($ordersHasSampleCount == 1) {
  2923.             return [
  2924.                 'msg' => [
  2925.                     'header' => $this->translationService->trans(
  2926.                         'sample_already_added.header',
  2927.                         App::getCurLocale(),
  2928.                         [
  2929.                             '%name%' => $item->getName(),
  2930.                         ]
  2931.                     ),
  2932.                     'body' => $this->translationService->trans(
  2933.                         'sample_already_added.body',
  2934.                         App::getCurLocale(),
  2935.                         [
  2936.                             '%nameOrder%' => $ordersHasSample[0],
  2937.                         ]
  2938.                     ),
  2939.                 ],
  2940.                 'count' => $count,
  2941.                 'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  2942.                 'projects' => $projects,
  2943.             ];
  2944.         }
  2945.         if ($ordersHasSampleCount 1) {
  2946.             return [
  2947.                 'msg' => [
  2948.                     'header' => $this->translationService->trans(
  2949.                         'sample_already_added.header',
  2950.                         App::getCurLocale(),
  2951.                         [
  2952.                             '%name%' => $item->getName(),
  2953.                         ]
  2954.                     ),
  2955.                     'body' => $this->translationService->trans(
  2956.                         'article.in_orders',
  2957.                         App::getCurLocale(),
  2958.                         [
  2959.                             '%d%' => join(', '$ordersHasSample),
  2960.                         ]
  2961.                     ),
  2962.                 ],
  2963.                 'count' => $count,
  2964.                 'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  2965.                 'projects' => $projects,
  2966.             ];
  2967.         }
  2968.         return [
  2969.             'count' => $count,
  2970.             'totalArticlesCount' => $this->buyOrderRepository->getBuyOrderArticleCount($this->token),
  2971.             'projects' => $projects,
  2972.         ];
  2973.     }
  2974.     /**
  2975.      * @param string $hash
  2976.      * @param bool $new
  2977.      * @return bool|RedirectResponse
  2978.      */
  2979.     public function checkHashRedirect(string $hash, ?bool $new true)
  2980.     {
  2981.         if (mb_strlen($hash) < 30) {
  2982.             $hash $this->buyOrderRepository->getHashByNumber($hash);
  2983.             if ($hash) {
  2984.                 if ($new) {
  2985.                     return new RedirectResponse($this->generateUrl('app_order', ['hash' => $hash]), 301);
  2986.                 }
  2987.                 return new RedirectResponse($this->generateUrl('app_buy_order', ['orderId' => $hash]), 301);
  2988.             }
  2989.         }
  2990.         return false;
  2991.     }
  2992.     /**
  2993.      * @param int $itemId
  2994.      * @param int $type
  2995.      * @param ?float $itemCount
  2996.      * @return array
  2997.      * @throws Exception
  2998.      */
  2999.     public function getOrderAddList(int $itemIdint $type BuyOrderArticle::TYPE_NORMAL, ?float $itemCount 0.1): array
  3000.     {
  3001.         $response = [];
  3002.         $orderList = [];
  3003.         $orders $this->buyOrderRepository->getBuyOrders([
  3004.             'token' => $this->token,
  3005.             'status' => [123],
  3006.             'type' => $type,
  3007.         ]);
  3008.         foreach ($orders as $order) {
  3009.             if (OrderHelper::canIAddThatTypeToOrder($order$type)) {
  3010.                 $orderList[] = $order;
  3011.             }
  3012.         }
  3013.         $countOrder count($orderList);
  3014.         $isStatusAdd false;
  3015.         // если нет проекта в который можно добавить артикул, то создаём проект
  3016.         if ($countOrder 0) {
  3017.             foreach ($orderList as $order) {
  3018.                 if (
  3019.                     $order->getStatus() == BuyOrderStatusEnum::REQUEST_DELIVERY
  3020.                     || $order->getStatus() == BuyOrderStatusEnum::AWAITING_PAYMENT
  3021.                     && $order->getDeliveryCalculateType() !== 0
  3022.                 ) {
  3023.                     $isStatusAdd true;
  3024.                     break;
  3025.                 } elseif ($order->getStatus() > BuyOrderStatusEnum::AWAITING_PAYMENT) {
  3026.                     --$countOrder;
  3027.                 }
  3028.             }
  3029.         }
  3030.         //Если у клиента нет заказа, в который можно добавить артикул, создаём заказ
  3031.         if ($countOrder === 0) {
  3032.             $orderList[] = $this->create($this->token);
  3033.             $isStatusAdd true;
  3034.             $countOrder 1;
  3035.         }
  3036.         // Если у клиента только один доступный для изменения заказ, то сразу переадресовываем клиента на него
  3037.         if ($isStatusAdd && $countOrder === 1) {
  3038.             $response['redirect'] = $this->generateUrl(
  3039.                 'app_order_change_item',
  3040.                 [
  3041.                     '_locale' => App::getCurLocale(true),
  3042.                     'itemId' => $itemId,
  3043.                     'itemCount' => $itemCount ?? 1,
  3044.                     'type' => $type,
  3045.                 ],
  3046.                 0
  3047.             );
  3048.         } else {
  3049.             $active = [];
  3050.             $headers = [];
  3051.             $itemName '';
  3052.             $ordersHasSample = [];
  3053.             /** @var BuyOrder $order */
  3054.             foreach ($orderList as $order) {
  3055.                 foreach ($order->getArticles() as $orderItem) {
  3056.                     // для списка заказов в которые артикул был уже добавлен
  3057.                     if ($orderItem->getArticle() && $orderItem->getArticle()->getId() == $itemId) {
  3058.                         $active[$order->getHash()] = true;
  3059.                     }
  3060.                     // если добавляется образец, надо проверить его на совпадение
  3061.                     if ($type == BuyOrderArticle::TYPE_SAMPLE) {
  3062.                         $itemAdd $this->articleRepository->getArticle($itemId);
  3063.                         if ($itemAdd) {
  3064.                             $itemName $itemAdd->getName();
  3065.                             // получаем данные по образцу для сравнения
  3066.                             $sampleData $this->sampleService->getSampleData(
  3067.                                 $itemAdd->getCode(),
  3068.                                 $order->getDeliveryCountry()->getId(),
  3069.                                 (bool) $order->getVatType(),
  3070.                                 $order->getCurrency(),
  3071.                                 $order->getMeasure()
  3072.                             );
  3073.                             if ($sampleData) {
  3074.                                 if (
  3075.                                     $res $this->checkingDoubleSample(
  3076.                                         $orderItem,
  3077.                                         $itemId,
  3078.                                         (string) $sampleData['code']
  3079.                                     )
  3080.                                 ) {
  3081.                                     $ordersHasSample[] = $res;
  3082.                                 }
  3083.                             }
  3084.                         }
  3085.                     } else {
  3086.                         break;
  3087.                     }
  3088.                 }
  3089.                 $headers[$order->getId()] = strip_tags(OrderHelper::header($ordertrue));
  3090.             }
  3091.             $response = [
  3092.                 'html' => $this->render(
  3093.                     '@Web/BuyOrder/list.html.twig',
  3094.                     [
  3095.                         'articleId' => $itemId,
  3096.                         'orders' => $orderList,
  3097.                         'active' => $active,
  3098.                         'itemCount' => $itemCount,
  3099.                         'headers' => $headers,
  3100.                         'type' => $type,
  3101.                     ]
  3102.                 ),
  3103.             ];
  3104.             if (count($ordersHasSample) > 0) {
  3105.                 $response['msg'] = [
  3106.                     'header' => $this->translationService->trans(
  3107.                         'sample_already_added.header',
  3108.                         App::getCurLocale(),
  3109.                         [
  3110.                             '%name%' => $itemName,
  3111.                         ]
  3112.                     ),
  3113.                     'body' => $this->translationService->trans(
  3114.                         'article.in_orders',
  3115.                         App::getCurLocale(),
  3116.                         [
  3117.                             '%d%' => '"' join('", "'$ordersHasSample) . '"',
  3118.                         ]
  3119.                     ),
  3120.                 ];
  3121.             }
  3122.         }
  3123.         return $response;
  3124.     }
  3125.     /**
  3126.      * @param BuyOrderArticle $item
  3127.      * @param int $id
  3128.      * @param string $code
  3129.      * @return false|string
  3130.      * @throws Exception
  3131.      */
  3132.     public function checkingDoubleSample(BuyOrderArticle $itemint $idstring $code)
  3133.     {
  3134.         if ($item->getArticle()->getId() != $id && $item->chooseCode() == $code) {
  3135.             return ('<a href="' .
  3136.                 App::generateUrl('app_buy_order', ['orderId' => $item->getBuyOrder()->getHash()]) .
  3137.                 '" target="_blank">' OrderHelper::humanName(
  3138.                     $item->getBuyOrder()->getName(),
  3139.                     $item->getBuyOrder()->getStatus(),
  3140.                     $item->getBuyOrder()->getBankTransferRequested()
  3141.                 ) . '</a>');
  3142.         } else {
  3143.             return false;
  3144.         }
  3145.     }
  3146.     /**
  3147.      * Подсчет зарезервированных образцов по всем активным заказам
  3148.      *
  3149.      * @param string $sampleCode Код образца
  3150.      * @param string|null $excludeOrderHash Исключить заказ с данным хешем из подсчета
  3151.      * @return int Количество зарезервированных образцов
  3152.      */
  3153.     public function getReservedSampleCount(string $sampleCode, ?string $excludeOrderHash null): int
  3154.     {
  3155.         // Статусы активных заказов: 1 = REQUEST_DELIVERY, 2 = AWAITING_PAYMENT, 3 = ?
  3156.         $activeStatuses = [123];
  3157.         $qb $this->em->createQueryBuilder();
  3158.         $qb->select('SUM(boa.amount) as total')
  3159.             ->from('WebBundle:BuyOrder''bo')
  3160.             ->join('bo.articles''boa')
  3161.             ->where('bo.status IN (:statuses)')
  3162.             ->andWhere('boa.type = :sampleType')
  3163.             ->andWhere('boa.sampleCode = :sampleCode')
  3164.             ->setParameter('statuses'$activeStatuses)
  3165.             ->setParameter('sampleType'BuyOrderArticle::TYPE_SAMPLE)
  3166.             ->setParameter('sampleCode'$sampleCode);
  3167.         if ($excludeOrderHash !== null) {
  3168.             $qb->andWhere('bo.hash != :excludeHash')
  3169.                ->setParameter('excludeHash'$excludeOrderHash);
  3170.         }
  3171.         $result $qb->getQuery()->getSingleScalarResult();
  3172.         return (int) ($result ?: 0);
  3173.     }
  3174.     /**
  3175.      * Переотправка заказа в 1С, сброс: если уже был отправлен
  3176.      */
  3177.     public function recreateOrderTo1C(string $hash): bool
  3178.     {
  3179.         $order $this->getOrderObj($hash);
  3180.         if ($order->getSendCreate1C()) {
  3181.             $this->log('RESETORDERONEC'$order->getHash(), 'resend order by admin');
  3182.             $this->oneCService->resetOrderOneC($order'resend order by admin');
  3183.         } else {
  3184.             $this->log('RESETORDERSITE'$order->getHash(), 'resend order-SITE by admin');
  3185.         }
  3186.         $result $this->oneCService->createOrderOneC(App::getRequest(), $order);
  3187.         return $result['Result'] && empty($result['Result']['Error']);
  3188.     }
  3189.     /**
  3190.      * Синхронизация заказа с "1C"
  3191.      *
  3192.      * @param string $hash
  3193.      * @return bool
  3194.      * @throws Exception
  3195.      */
  3196.     public function syncOrder(string $hash): bool
  3197.     {
  3198.         $order $this->getOrderObj($hash);
  3199.         // сохраняем заказ перед изменением
  3200.         $this->oldOrder = clone $order;
  3201.         $result $this->oneCService->getOrder($hash);
  3202.         if (empty($result['Result'])) {
  3203.             throw new Exception('Fail decode json');
  3204.         }
  3205.         if (!empty($result['NumberOrder'])) {
  3206.             $order->setNumber($result['NumberOrder']);
  3207.         }
  3208.         if (!empty($result['currency'])) {
  3209.             $order->setCurrency($result['currency']);
  3210.         }
  3211.         $this->setManagerToOrder($order$result['manager'] ?? null);
  3212.         if (!empty($result['measure'])) {
  3213.             $order->setMeasure(MeasureOrderEnum::M);
  3214.             if ($result['measure'] == MeasureOrderEnum::FT) {
  3215.                 $order->setMeasure(MeasureOrderEnum::FT);
  3216.             }
  3217.         }
  3218.         if (!empty($result['vat'])) {
  3219.             $vat $result['vat'];
  3220.             if (isset($vat['type'])) {
  3221.                 $order->setVatType((int) $vat['type']);
  3222.             }
  3223.             if (!empty($vat['number'])) {
  3224.                 $order->setVatNumber($vat['number']);
  3225.             }
  3226.         }
  3227.         if (!empty($result['delivery'])) {
  3228.             $delivery $result['delivery'];
  3229.             if (!empty($delivery['country'])) {
  3230.                 /** @var ListCountry $countryEntity */
  3231.                 $countryEntity $this->em->getReference('WebBundle\Entity\ListCountry', (int) $delivery['country']);
  3232.                 $order->setDeliveryCountry($countryEntity);
  3233.                 // Чистим индекс для России если была до этого другая страна и индекс имел буквы
  3234.                 if ($delivery['country'] == 643 && !empty($delivery['index']) && !is_int($delivery['index'])) {
  3235.                     $delivery['index'] = null;
  3236.                     $order->setDeliveryIndex(null);
  3237.                 }
  3238.             }
  3239.             if (!empty($delivery['city'])) {
  3240.                 $order->setDeliveryCity($delivery['city']);
  3241.             }
  3242.             if (isset($delivery['lift'])) {
  3243.                 if (BuyOrderLiftEnum::isValid((int) $delivery['lift'])) {
  3244.                     $order->setLift((int) $delivery['lift']);
  3245.                 } else {
  3246.                     $order->setLift(BuyOrderLiftEnum::UNDEFINED);
  3247.                 }
  3248.             }
  3249.             if (!empty($delivery['index'])) {
  3250.                 $order->setDeliveryIndex($delivery['index']);
  3251.             }
  3252.             if (!empty($delivery['region'])) {
  3253.                 $order->setDeliveryRegion($delivery['region']);
  3254.             }
  3255.             if (!empty($delivery['street'])) {
  3256.                 $order->setDeliveryAddress($delivery['street']);
  3257.             }
  3258.             if (!empty($delivery['building'])) {
  3259.                 $order->setDeliveryBuilding($delivery['building']);
  3260.             }
  3261.             if (!empty($delivery['apartmentOffice'])) {
  3262.                 $order->setDeliveryApartmentOffice($delivery['apartmentOffice']);
  3263.             }
  3264.             if (!empty($delivery['price'])) {
  3265.                 $order->setDelivery(PriceHelper::formatPrice($delivery['price']));
  3266.             }
  3267.             if (!empty($delivery['weightReport'])) {
  3268.                 $order->setWeightReport((float) str_replace(',''.'$delivery['weightReport']));
  3269.             }
  3270.             OrderHelper::updateWeightGrossForOrderInNeeded($order$delivery['weightGross'] ?? null);
  3271.             if (isset($delivery['carrierName'])) {
  3272.                 $order->setCarrierName(strip_tags($delivery['carrierName']));
  3273.             }
  3274.             if (isset($delivery['deliveryType'])) {
  3275.                 $order->setDeliveryType($delivery['deliveryType'] == 0);
  3276.             }
  3277.             if (!empty($delivery['dateExecution'])) {
  3278.                 $order->setPayDateExecution(DateHelper::standard($delivery['dateExecution']));
  3279.             }
  3280.             if (!empty($delivery['timeMin'])) {
  3281.                 $order->setDeliveryTimeMin(DateHelper::standard($delivery['timeMin']));
  3282.             }
  3283.             if (!empty($delivery['timeMax'])) {
  3284.                 $order->setDeliveryTimeMax(DateHelper::standard($delivery['timeMax']));
  3285.             }
  3286.             if (!empty($delivery['timeConfirmMin'])) {
  3287.                 $order->setDeliveryTimeConfirmMin(DateHelper::standard($delivery['timeConfirmMin']));
  3288.             }
  3289.             if (!empty($delivery['timeConfirmMax'])) {
  3290.                 $order->setDeliveryTimeConfirmMax(DateHelper::standard($delivery['timeConfirmMax']));
  3291.             }
  3292.             // если заказ оплачен и срок сдвинули в большую сторону надо отправлять сообщение об этом
  3293.             if (
  3294.                 $order->getStatus() > BuyOrderStatusEnum::PAID_NOT_CONFIRMED
  3295.                 && (
  3296.                     $this->oldOrder
  3297.                     && $this->oldOrder->getDeliveryTimeMin()
  3298.                     && $this->oldOrder->getDeliveryTimeMax()
  3299.                     && $order->getDeliveryTimeMin()
  3300.                     && $order->getDeliveryTimeMax()
  3301.                     && (
  3302.                         $this->oldOrder->getDeliveryTimeMin()->getTimestamp() < $order->getDeliveryTimeMin()->getTimestamp()
  3303.                         || $this->oldOrder->getDeliveryTimeMax()->getTimestamp() < $order->getDeliveryTimeMax()->getTimestamp()
  3304.                     )
  3305.                 )
  3306.             ) {
  3307.                 $this->historyService->saveHistoryAndSendEmail(
  3308.                     $order,
  3309.                     'confirm_change_time_shipment',
  3310.                     OrderHistoryService::SEND_EMAIL
  3311.                 );
  3312.             }
  3313.         }
  3314.         $rItems $result['articles'];
  3315.         /** @var BuyOrderArticle $item */
  3316.         foreach ($order->getArticles() as $item) {
  3317.             $item->setArticleTimeType(
  3318.                 $item->getArticleTimeType() ?? DeliveryTypeTimeEnum::CALCULATED
  3319.             );
  3320.             if (empty($rItems[$item->chooseCode()])) {
  3321.                 $this->historyService->saveHistoryAndSendEmail(
  3322.                     $order,
  3323.                     'event.item_deleted',
  3324.                     OrderHistoryService::NO_SEND_EMAIL,
  3325.                     [
  3326.                         '%name%' => $item->getArticle()->getName(),
  3327.                     ],
  3328.                     1
  3329.                 );
  3330.                 $this->em->remove($item);
  3331.                 $this->em->flush();
  3332.                 continue;
  3333.             }
  3334.             $a $rItems[$item->chooseCode()];
  3335.             $amount round(PriceHelper::formatPrice($a['amount']), 5);
  3336.             $price PriceHelper::formatPrice(!empty($a['Minprice']) ? $a['Minprice'] : $a['price']);
  3337.             $priceEuro PriceHelper::formatPrice(!empty($a['MinpriceEuro']) ? $a['MinpriceEuro'] : $a['priceEuro']);
  3338.             if (!empty($a['sum'])) {
  3339.                 $price round(LocaleHelper::getFloatFromString($a['sum']) / $amount2);
  3340.                 $priceEuro round(PriceHelper::formatPrice($a['sumEuro']) / $amount2);
  3341.             }
  3342.             if (!empty($rItem['type'])) {
  3343.                 $item->setType($rItem['type']);
  3344.             }
  3345.             if (!empty($rItem['sample_format'])) {
  3346.                 $item->setSampleFormat($rItem['sample_format']);
  3347.             }
  3348.             if (!empty($rItem['sample_type'])) {
  3349.                 $item->setSampleType((int) $rItem['sample_type']);
  3350.             }
  3351.             if (!empty($rItem['priceEuro'])) {
  3352.                 $item->setPriceEUR($priceEuro);
  3353.             }
  3354.             if ($item->getType() == BuyOrderArticle::TYPE_SAMPLE) {
  3355.                 $amount ceil($amount);
  3356.             }
  3357.             $item->setAmount($amount);
  3358.             $item->setPrice($price);
  3359.             $this->setEuroForRussia($order);
  3360.             $item->setCurrency($order->getCurrency());
  3361.             $item->setMeasure($order->getMeasure());
  3362.             $this->setDateItemSync($order$item$a);
  3363.             unset($rItems[$item->chooseCode()]);
  3364.             $this->em->persist($order);
  3365.             $this->em->persist($item);
  3366.         }
  3367.         $this->em->flush();
  3368.         $codes $this->articleRepository->getArticlesForCodes(array_keys($rItems));
  3369.         foreach ($rItems as $key => $rItem) {
  3370.             if (!array_key_exists($key$codes)) {
  3371.                 continue;
  3372.             }
  3373.             $item $codes[$key];
  3374.             $amount round(PriceHelper::formatPrice($rItem['amount']), 5);
  3375.             $price PriceHelper::formatPrice(!empty($rItem['Minprice']) ? $rItem['Minprice'] : $rItem['price']);
  3376.             $priceEuro PriceHelper::formatPrice(!empty($rItem['MinpriceEuro']) ? $rItem['MinpriceEuro'] : $rItem['priceEuro']);
  3377.             $orderItem $this->itemService->createOrderItem(
  3378.                 $item,
  3379.                 empty($rItem['type']) ? $rItem['type'],
  3380.                 $order
  3381.             );
  3382.             if ($orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE) {
  3383.                 $amount ceil($amount);
  3384.             }
  3385.             $orderItem->setAmount($amount);
  3386.             $orderItem->setPrice($price);
  3387.             $orderItem->setPriceEUR($priceEuro);
  3388.             if (!empty($rItem['sample_format'])) {
  3389.                 $orderItem->setSampleFormat($rItem['sample_format']);
  3390.             }
  3391.             if (!empty($rItem['sample_type'])) {
  3392.                 $orderItem->setSampleType((int) $rItem['sample_type']);
  3393.             }
  3394.             $orderItem->setWeight($item->getWeight());
  3395.             $orderItem->setPackagingCount(
  3396.                 LocaleHelper::getPackagingCount(
  3397.                     $item,
  3398.                     OrderHelper::params($order, [
  3399.                         'measureId' => $item->getMeasure()->getId(),
  3400.                         'measure' => $orderItem->getMeasure(),
  3401.                     ]),
  3402.                     $orderItem->getType()
  3403.                 )
  3404.             );
  3405.             $orderItem->setPackaging(
  3406.                 $orderItem->getType() == BuyOrderArticle::TYPE_SAMPLE $item->getPackagingCount()
  3407.             );
  3408.             $orderItem->setBuyOrder($order);
  3409.             $this->setEuroForRussia($order);
  3410.             $orderItem->setCurrency($order->getCurrency());
  3411.             $orderItem->setMeasure($order->getMeasure());
  3412.             $this->setDateItemSync($order$orderItem$rItem);
  3413.             $this->historyService->saveHistoryAndSendEmail(
  3414.                 $order,
  3415.                 'event.item_added_sync',
  3416.                 OrderHistoryService::NO_SEND_EMAIL,
  3417.                 [
  3418.                     '%name%' => $orderItem->getArticle()->getName(),
  3419.                     '%amountOld%' => 0,
  3420.                     '%amount%' => $amount,
  3421.                     '%price%' => $price,
  3422.                     '%currency%' => $orderItem->getCurrency(),
  3423.                     '%measure%' => $orderItem->getMeasure(),
  3424.                 ],
  3425.                 1
  3426.             );
  3427.             $order->addArticle($orderItem);
  3428.             $this->em->persist($order);
  3429.             $this->em->persist($orderItem);
  3430.         }
  3431.         $this->em->flush();
  3432.         if (!empty($result['Status'])) {
  3433.             $this->buyOrderRepository->save($order);
  3434.             if (
  3435.                 $result['Status'] == BuyOrderStatusEnum::AWAITING_CALCULATION
  3436.                 && $order->getDelivery() !== null
  3437.                 && $order->getDeliveryTimeMin() !== null
  3438.             ) {
  3439.                 $result['Status'] = BuyOrderStatusEnum::AWAITING_PAYMENT;
  3440.             }
  3441.             $this->setStatus($order$result['Status'], [
  3442.                 'fromOneC' => true,
  3443.                 'syncFromOneC' => true,
  3444.             ]);
  3445.         }
  3446.         $this->buyOrderRepository->save($order);
  3447.         $this->setUserData($order);
  3448.         $this->setNewHashOrder($order);
  3449.         $this->buyOrderRepository->save($order);
  3450.         return true;
  3451.     }
  3452.     /**
  3453.      * @param BuyOrder $order
  3454.      * @param BuyOrderArticle $item
  3455.      * @param array $a
  3456.      * @throws Exception
  3457.      */
  3458.     private function setDateItemSync(BuyOrder $orderBuyOrderArticle $item, array $a)
  3459.     {
  3460.         if ($order->getPayDateExecution() && !empty($a['timeMin'])) {
  3461.             if ($timeMin DateHelper::standard($a['timeMin'])) {
  3462.                 $item->setDeliveryTimeMin($timeMin);
  3463.             } else {
  3464.                 $date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
  3465.                 $date->modify('+' $a['timeMin'] . ' day');
  3466.                 $item->setDeliveryTimeMin($date);
  3467.             }
  3468.         }
  3469.         if ($order->getPayDateExecution() && !empty($a['timeMax'])) {
  3470.             if ($timeMax DateHelper::standard($a['timeMax'])) {
  3471.                 $item->setDeliveryTimeMax($timeMax);
  3472.             } else {
  3473.                 $date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
  3474.                 $date->modify('+' $a['timeMax'] . ' day');
  3475.                 $item->setDeliveryTimeMax($date);
  3476.             }
  3477.         }
  3478.         if ($order->getPayDateExecution() && !empty($a['timeConfirmMin'])) {
  3479.             if ($timeConfirmMin DateHelper::standard($a['timeConfirmMin'])) {
  3480.                 $item->setDeliveryTimeConfirmMin($timeConfirmMin);
  3481.             } else {
  3482.                 $date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
  3483.                 $date->modify('+' $a['timeConfirmMin'] . ' day');
  3484.                 $item->setDeliveryTimeConfirmMin($date);
  3485.             }
  3486.         }
  3487.         if ($order->getPayDateExecution() && !empty($a['timeConfirmMax'])) {
  3488.             if ($timeConfirmMax DateHelper::standard($a['timeConfirmMax'])) {
  3489.                 $item->setDeliveryTimeConfirmMax($timeConfirmMax);
  3490.             } else {
  3491.                 $date = new DateTime($order->getPayDateExecution()->format('Y-m-d'));
  3492.                 $date->modify('+' $a['timeConfirmMax'] . ' day');
  3493.                 $item->setDeliveryTimeConfirmMax($date);
  3494.             }
  3495.         }
  3496.     }
  3497.     /**
  3498.      * @param BuyOrder $order
  3499.      * @throws Exception
  3500.      */
  3501.     private function setUserData(BuyOrder $order)
  3502.     {
  3503.         //создание или обновление пользователя
  3504.         if (
  3505.             empty($data['checkHash'])
  3506.             && $order->getEmail()
  3507.             && (
  3508.                 $order->getUser() == null
  3509.                 || $order->getVatType() == 1
  3510.                 && $order->getVatNumber()
  3511.                 && $order->getUnidVatPlayer() == null
  3512.                 || OrderHelper::isChangeDataUser($order$this->oldOrder)
  3513.             )
  3514.         ) {
  3515.             try {
  3516.                 $user $this->orderUserService->resolve(App::getRequest(), $order);
  3517.                 $order->setUser($user);
  3518.                 if ($order->getAddressRecipient() && !$order->getAddressRecipient()->getUser()) {
  3519.                     $order->getAddressRecipient()->setUser($user);
  3520.                 }
  3521.                 if ($user && $user->getCorporateUnid()) {
  3522.                     $order->setUnidVatPlayer($user->getCorporateUnid());
  3523.                 }
  3524.             } catch (EmailAlreadyRegisteredException $e) {
  3525.                 $msg $this->translationService->trans('buyOrder.fields.error.email_exist'App::getCurLocale(), [
  3526.                     '%login%' => App::generateUrl('app_login', ['_locale' => App::getCurLocale(true)]),
  3527.                 ]);
  3528.                 $this->errors[] = $msg;
  3529.             }
  3530.         }
  3531.     }
  3532.     /**
  3533.      * @param BuyOrder $order
  3534.      * @return bool
  3535.      * @deprecated
  3536.      */
  3537.     public function mayChange(BuyOrder $order): bool
  3538.     {
  3539.         return ($order->getStatus() && $order->getStatus() == BuyOrderStatusEnum::AWAITING_CALCULATION);
  3540.     }
  3541.     /**
  3542.      * Формирует массив стран куда возможен заказ
  3543.      * @param BuyOrder $order
  3544.      * @return array
  3545.      * @throws Exception
  3546.      * @deprecated
  3547.      */
  3548.     public function getCountry(BuyOrder $order): array
  3549.     {
  3550.         $countryList $this->listCountryRepository->getListForByOrder();
  3551.         $countryAuto = [];
  3552.         $cnt = [
  3553.             'auto' => [],
  3554.             'noAuto' => [],
  3555.         ];
  3556.         $country = [
  3557.             'regions' => [],
  3558.             'regionShow' => false,
  3559.         ];
  3560.         foreach ($countryList as $c) {
  3561.             if (!empty($c['code']) && !empty($c['alias'])) {
  3562.                 $auto in_array($c['id'], $countryAuto) ? 'auto' 'noAuto';
  3563.                 $cnt[$auto][strtolower($c['code'])] = [
  3564.                     // alias обязательно первый, так как по нему сортировка работает
  3565.                     'alias' => $this->translationService->trans($c['alias']),
  3566.                     'id' => $c['id'],
  3567.                     'payPalPercent' => $c['payPalPercent'],
  3568.                     'payPalPercentOwn' => $c['payPalPercentOwn'],
  3569.                     'phoneCode' => $c['phoneCode'],
  3570.                     'phoneMask' => $c['phoneMask'],
  3571.                     'zipCodeMask' => $c['zipCodeMask'],
  3572.                     'minValidPhoneNumbers' => $c['minValidPhoneNumbers'],
  3573.                     'strong' => ($auto == 'auto'),
  3574.                 ];
  3575.                 if (!empty($c['states'])) {
  3576.                     $region = [];
  3577.                     foreach ($c['states'] as $r) {
  3578.                         $a = [
  3579.                             'code' => $r['code'],
  3580.                             'name' => $r['name'],
  3581.                             'countryId' => $c['id'],
  3582.                         ];
  3583.                         if (
  3584.                             $order->getDeliveryCountry() &&
  3585.                             $order->getDeliveryCountry()->getId() == $c['id'] ||
  3586.                             $order->getDeliveryCountry() == null &&
  3587.                             App::getCurCountry() == strtolower($c['code'])
  3588.                         ) {
  3589.                             $country['regionShow'] = true;
  3590.                             $a['show'] = true;
  3591.                             // определяем выбранный регион доставки если поле уже заполнено, только в том случае,
  3592.                             // если регион выбранный в заказе соответствует выбранной стране
  3593.                             if (
  3594.                                 $order->getDeliveryRegion() &&
  3595.                                 in_array($order->getDeliveryRegion(), [$r['code'], strtolower($r['name'])])
  3596.                             ) {
  3597.                                 $a['sel'] = true;
  3598.                             }
  3599.                         }
  3600.                         $region[] = $a;
  3601.                     }
  3602.                     $country['regions'] = array_merge($country['regions'], $region);
  3603.                 }
  3604.             }
  3605.         }
  3606.         // если есть страны по которым возможен авто расчет
  3607.         if (count($cnt['auto']) > 0) {
  3608.             $country['0'] = [
  3609.                 'alias' => 'buyOrder.info.online_calculation',
  3610.                 'class' => 'selectBox-optgroup',
  3611.             ];
  3612.             asort($cnt['auto']);
  3613.             $country array_merge($country$cnt['auto']);
  3614.             $country['1'] = [
  3615.                 'alias' => 'buyOrder.info.request_calculation',
  3616.                 'class' => 'selectBox-optgroup',
  3617.             ];
  3618.         }
  3619.         asort($cnt['noAuto']);
  3620.         $country array_merge($country$cnt['noAuto']);
  3621.         foreach (OrderHelper::noEU()['char'] as $row) {
  3622.             if (isset($country[$row])) {
  3623.                 $country[$row]['noEU'] = 1;
  3624.             }
  3625.         }
  3626.         foreach (OrderHelper::vatRequired()['char'] as $row) {
  3627.             if (isset($country[$row])) {
  3628.                 $country[$row]['VatRequired'] = 1;
  3629.             }
  3630.         }
  3631.         if ($order->getDeliveryCountry() != null && $order->getDeliveryCountry()->getCode() != null) {
  3632.             $country[strtolower($order->getDeliveryCountry()->getCode())]['selected'] = true;
  3633.         } else {
  3634.             $uCountry strtolower(LocaleHelper::getUserCountry());
  3635.             if (isset($country[$uCountry])) {
  3636.                 $country[$uCountry]['selected'] = true;
  3637.             } else {
  3638.                 $country[strtolower(substr(App::getCurLocale(true), -22))]['selected'] = true;
  3639.             }
  3640.         }
  3641.         // в заказе не должен вариант en отображаться
  3642.         unset($country['en']);
  3643.         return $country;
  3644.     }
  3645.     /**
  3646.      * Подтверждение изменённых сроков
  3647.      *
  3648.      * @param string $token
  3649.      * @param string $orderId
  3650.      * @param bool $val
  3651.      * @return mixed
  3652.      * @throws Exception
  3653.      * @deprecated
  3654.      */
  3655.     public function confirmTimes(string $tokenstring $orderIdbool $val)
  3656.     {
  3657.         $params = [
  3658.             'hash' => $orderId,
  3659.             'status' => BuyOrderStatusEnum::PAID_NOT_CONFIRMED,
  3660.         ];
  3661.         if (!$this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')) {
  3662.             $params['token'] = $token;
  3663.         }
  3664.         $order $this->buyOrderRepository->getBuyOrder($params);
  3665.         if (null === $order) {
  3666.             return false;
  3667.         }
  3668.         $result $this->oneCService->confirm($order$val);
  3669.         if (!empty($result['dateStock'])) {
  3670.             $order->setShipDate(DateHelper::standard($result['dateStock']));
  3671.         }
  3672.         if (!empty($result['sendingDate'])) {
  3673.             $order->setSendingDate(DateHelper::standard($result['sendingDate']));
  3674.         }
  3675.         $this->setStatus($order, ($val BuyOrderStatusEnum::TREKKING_AWAITS BuyOrderStatusEnum::CANCELLED));
  3676.         $order OrderHelper::updateDeliveryTimeTypeAll($orderDeliveryTypeTimeEnum::CONFIRMED());
  3677.         $this->buyOrderRepository->save($order);
  3678.         return array_merge(
  3679.             $result,
  3680.             [
  3681.                 'status' => [
  3682.                     'id' => $order->getStatus(),
  3683.                     'text' => $this->translationService->trans(BuyOrderStatusEnum::from($order->getStatus())->getAlias()),
  3684.                 ],
  3685.                 'id' => $order->getId(),
  3686.                 'delivery' => OrderHelper::time($order),
  3687.             ]
  3688.         );
  3689.     }
  3690.     /**
  3691.      * Записываем новые хеши данных заказа
  3692.      * хеш нужно обязательно перестраивать перед обновлением иначе будут дубль запросы в 1С
  3693.      *
  3694.      * @param BuyOrder $order
  3695.      * @return void
  3696.      * @throws Exception
  3697.      */
  3698.     public function setNewHashOrder(BuyOrder $order): void
  3699.     {
  3700.         $order->setMarkerReset(OrderHelper::markerReset($order));
  3701.         $order->setMarkerChange(OrderHelper::md5MarkerChange($order));
  3702.     }
  3703.     /**
  3704.      * @param BuyOrder $order
  3705.      * @return array
  3706.      * @throws Exception
  3707.      */
  3708.     public function getProjectData(BuyOrder $order): array
  3709.     {
  3710.         return [
  3711.             'name' => OrderHelper::humanName(
  3712.                 $order->getName(),
  3713.                 $order->getStatus(),
  3714.                 $order->getBankTransferRequested(),
  3715.             ),
  3716.             'url' => App::generateUrl('app_buy_order', [
  3717.                 'orderId' => $order->getHash(),
  3718.             ]),
  3719.             'hash' => $order->getHash(),
  3720.             'number' => $order->getNumber(),
  3721.         ];
  3722.     }
  3723.     public function checkOrderByLink(string $linkUser $user): void
  3724.     {
  3725.         $path_list = ['buy-order'];
  3726.         $parts explode('/'$link);
  3727.         /**
  3728.          * order link have the following format http://host/local/Order page/order hash
  3729.          * So result of explode will have the following structure
  3730.          * [
  3731.          *      0 => 'http:',
  3732.          *      1 => '',
  3733.          *      2 => 'hast',
  3734.          *      3 => 'local',
  3735.          *      4 => 'order path(buy-order|order)',
  3736.          *      5 => 'order hash',
  3737.          * ]
  3738.          */
  3739.         if (!in_array(($parts[4] ?? ''), $path_list)) {
  3740.             return;
  3741.         }
  3742.         if (empty($parts[5])) {
  3743.             return;
  3744.         }
  3745.         $order $this->buyOrderRepository->getBuyOrder(['hash' => $parts[5]]);
  3746.         if (empty($order)) {
  3747.             return;
  3748.         }
  3749.         $order->setStep(2);
  3750.         $order->setUser($user);
  3751.         $order->setEmail($user->getEmail());
  3752.         $this->em->persist($order);
  3753.         $this->em->flush();
  3754.     }
  3755.     /**
  3756.      * Фиксируем, что новые сроки подтвержденные
  3757.      *
  3758.      * @param BuyOrder $order
  3759.      * @param bool|null $confirmed
  3760.      * @return void
  3761.      * @throws Exception
  3762.      */
  3763.     public function setNewTermsOfDelivery(BuyOrder $order, ?bool $confirmed): void
  3764.     {
  3765.         $order->setDeliveryTimeType(DeliveryTypeTimeEnum::CONFIRMED);
  3766.         // 7 - заказ переводим в ожидающий погрузки, 10 -в отменённый
  3767.         $this->setStatus($order$confirmed BuyOrderStatusEnum::TREKKING_AWAITS BuyOrderStatusEnum::CANCELLED);
  3768.         $this->oneCService->confirm($order$confirmed);
  3769.     }
  3770.     public function setOrderStatusByPayment(OrderPayment $payment): void
  3771.     {
  3772.         $order $payment->getBuyOrder();
  3773.         $alreadyPayment OrderHelper::paymentSum($order);
  3774.         $orderStatus BuyOrderStatusEnum::AWAITING_PAY_SYS;
  3775.         if ($payment->getStatus() === PaymentStatusEnum::SUCCESS) {
  3776.             $orderStatus BuyOrderStatusEnum::PAID;
  3777.             if (OrderHelper::totalSum($order) - $alreadyPayment 0) {
  3778.                 $orderStatus BuyOrderStatusEnum::PARTIALLY_PAID;
  3779.             }
  3780.         }
  3781.         $this->setStatus($order$orderStatus);
  3782.         $this->buyOrderRepository->save($order);
  3783.     }
  3784.     /**
  3785.      * @param bool $file64
  3786.      * @param int $oldStatus
  3787.      * @param int $status
  3788.      * @return bool false - Обновляем статус заказа, true - не обновляем
  3789.      */
  3790.     private function dontChangeOrderStatus(bool $file64int $oldStatusint $status): bool
  3791.     {
  3792.         if ($status === BuyOrderStatusEnum::PARTIALLY_DELIVERED) {
  3793.             return false;
  3794.         }
  3795.         if ($status === BuyOrderStatusEnum::DELIVERED && $file64) {
  3796.             return false;
  3797.         }
  3798.         if ($oldStatus === BuyOrderStatusEnum::EXPORT && $status === BuyOrderStatusEnum::DELIVERED) {
  3799.             return false;
  3800.         }
  3801.         if (
  3802.             ($file64
  3803.                 || $oldStatus !== $status
  3804.                 || $status === BuyOrderStatusEnum::PARTIALLY_PAID
  3805.             )
  3806.             && ($oldStatus <= BuyOrderStatusEnum::PARTIALLY_PAID || $status >= BuyOrderStatusEnum::PARTIALLY_PAID)
  3807.             && $status !== BuyOrderStatusEnum::DELIVERED
  3808.         ) {
  3809.             return false;
  3810.         }
  3811.         return true;
  3812.     }
  3813.     /**
  3814.      * Добавим платёж в заказ, если необходимо
  3815.      * Если передана транзакция, то оплата уже существует в нашей системе, создавать её не нужно
  3816.      * Если передан статус и статус - запрос возврата (23), то не записываем его как транзакцию
  3817.      */
  3818.     private function addPaymentForOrder(array $dataBuyOrder $order): void
  3819.     {
  3820.         $data['IDTransaction'] ??= $data['IDTransaction '] ?? null;
  3821.         if (!empty($data['IDTransaction'])) {
  3822.             return;
  3823.         }
  3824.         if (!empty($data['status']) && ((int) $data['status']) === BuyOrderStatusEnum::REFUND_OF_FUNDS) {
  3825.             return;
  3826.         }
  3827.         $sum $this->getSum($data);
  3828.         $type $this->getPaymentTypeForOrder($data);
  3829.         $order->setPayType($type);
  3830.         $payment $this->paymentService->addPayment(
  3831.             $order,
  3832.             [
  3833.                 'status' => 1,
  3834.                 'amount' => $sum,
  3835.                 'paySys' => $type,
  3836.                 'curRate' => $data['curRate'] ?? null,
  3837.                 'currency' => empty($data['sumCurrency']) ? $order->getCurrency() : $data['sumCurrency'],
  3838.                 'percent' => OrderHelper::paymentPercent($order$type),
  3839.             ]
  3840.         );
  3841.         $order->addPayment($payment);
  3842.     }
  3843.     private function getSum(array $data): float
  3844.     {
  3845.         if (!empty($data['sum'])) {
  3846.             return LocaleHelper::getFloatFromString($data['sum']);
  3847.         }
  3848.         return -LocaleHelper::getFloatFromString($data['sumBack']);
  3849.     }
  3850.     /**
  3851.      * @param array $data
  3852.      * @param BuyOrder $order
  3853.      * @throws Exception
  3854.      */
  3855.     private function checkSumOrSumBackData(array $dataBuyOrder $order): void
  3856.     {
  3857.         // sumBack для возвратов
  3858.         if (empty($data['sum']) && empty($data['sumBack'])) {
  3859.             return;
  3860.         }
  3861.         $this->addPaymentForOrder($data$order);
  3862.         // записываем в историю запись о возврате и отправляем письмо
  3863.         if (
  3864.             !empty($data['sumBack'])
  3865.             && empty($data['operation'])
  3866.             && $order->getStatus() != BuyOrderStatusEnum::PARTIALLY_PAID
  3867.         ) {
  3868.             $this->historyService->saveHistoryAndSendEmail(
  3869.                 $order,
  3870.                 'back_payment',
  3871.                 OrderHistoryService::SEND_EMAIL
  3872.             );
  3873.         }
  3874.     }
  3875.     /**
  3876.      * @param array $data
  3877.      * @return int
  3878.      */
  3879.     private function getPaymentTypeForOrder(array $data): int
  3880.     {
  3881.         $type $this->oldOrder->getPayType();
  3882.         if (empty($type)) {
  3883.             $type PaymentTypeEnum::getBankTransferTypeIfPayTypeNotAvailable($data['sumType'] ?? null);
  3884.         }
  3885.         if (!empty($data['method']) && PaymentTypeEnum::isAvailableMethod((int) $data['method'])) {
  3886.             $type = (int) $data['method'];
  3887.         }
  3888.         return PaymentTypeEnum::changeOldPayPalToNew($type);
  3889.     }
  3890.     /**
  3891.      * Отправляем в 1С заказ если он готов
  3892.      * отправляем все изменения заказа
  3893.      *
  3894.      * @param BuyOrder $order
  3895.      * @param array $data
  3896.      * @return array
  3897.      * @throws SoapFault
  3898.      */
  3899.     private function sendOrderTo1CIfItReady(BuyOrder $order, array $data): array
  3900.     {
  3901.         if (
  3902.             empty($data['checkHash'])
  3903.             && $order->getStatus() < BuyOrderStatusEnum::PARTIALLY_PAID
  3904.             && OrderHelper::readyCreate($order)
  3905.         ) {
  3906.             $result $this->oneCService->createOrderOneC(App::getRequest(), $orderfalse$this->timer);
  3907.             $data array_merge($data$result);
  3908.         }
  3909.         return $data;
  3910.     }
  3911.     private function orderAvailableToChange(array $dataBuyOrder $order): bool
  3912.     {
  3913.         if (empty($data)) {
  3914.             return false;
  3915.         }
  3916.         if ($order->getStatus() === BuyOrderStatusEnum::DELETED) {
  3917.             return false;
  3918.         }
  3919.         return true;
  3920.     }
  3921.     /**
  3922.      * Сохраняем заказ перед изменением
  3923.      */
  3924.     private function cloneOrderIfNeeded(BuyOrder $order): void
  3925.     {
  3926.         if (null === $this->oldOrder) {
  3927.             $this->oldOrder = clone $order;
  3928.         }
  3929.     }
  3930.     private function setManagerToOrder(BuyOrder $order, ?array $manager): void
  3931.     {
  3932.         $orderManager $this->buyOrderManagerService->getBuyOrderManager(
  3933.             $manager['email'] ?? null,
  3934.             $manager['login'] ?? null,
  3935.             $manager['name'] ?? null,
  3936.             $manager['phone'] ?? null,
  3937.         );
  3938.         if ($orderManager) {
  3939.             $order->setBuyOrderManager($orderManager);
  3940.         }
  3941.     }
  3942.     public function getPaymentOrdersByDate(?DateTime $start, ?DateTime $end): array
  3943.     {
  3944.         if (null === $start) {
  3945.             $start = (new DateTime('now'))->modify('-3 day');
  3946.         }
  3947.         if (null === $end) {
  3948.             $end = new DateTime('now');
  3949.         }
  3950.         return $this->buyOrderRepository->paymentOrder($start->format('Y-m-d'), $end->format('Y-m-d'));
  3951.     }
  3952.     /**
  3953.      * Этот метод меняет токены юзера у заказа и проставляет юзерИД в заказы с токеном $token
  3954.      */
  3955.     public function changeUserInOrders(User $userstring $token): void
  3956.     {
  3957.         $orders $this->buyOrderRepository->findByToken($token);
  3958.         foreach ($orders as $order) {
  3959.             if ($order->getUser() !== null) {
  3960.                 continue;
  3961.             }
  3962.             $order->setUser($user);
  3963.             $order->setToken($user->getToken());
  3964.             $order->setEmail($user->getEmail());
  3965.             $this->buyOrderRepository->save($orderfalse);
  3966.         }
  3967.         $this->buyOrderRepository->flush();
  3968.     }
  3969.     /**
  3970.      * Для России, в случае, если заказ образцов, то валюту ставим Euro
  3971.      */
  3972.     private function setEuroForRussia(BuyOrder $order): void
  3973.     {
  3974.         if (
  3975.             OrderHelper::onlySamples($order)
  3976.             && $order->getDeliveryCountry()
  3977.             && $order->getDeliveryCountry()->getId() == 643
  3978.             && $order->getCurrency() != 'EUR'
  3979.         ) {
  3980.             $order->setCurrency('EUR');
  3981.         }
  3982.     }
  3983.     /**
  3984.      * Обновляем данные и тип артикулов, только если заказ не оплачен либо запрос от "1С"
  3985.      *
  3986.      * @throws Exception
  3987.      */
  3988.     private function updateItemsForOrder(array $dataBuyOrder $orderBuyOrder $oldOrderbool $isRequestFrom1C): array
  3989.     {
  3990.         if (!$isRequestFrom1C && $order->getStatus() >= BuyOrderStatusEnum::PARTIALLY_PAID) {
  3991.             return $data;
  3992.         }
  3993.         // Изменение логики удаления артов из заказа - теперь если items не совпадает с артами заказа - обновим
  3994.         // Но! остаётся вопрос про последний арт - в этом случае не удалим, в будущем (на новом заказе) фронт
  3995.         // обещает всегда items - соответственно перенаполнение его здесь только для 1С сделать
  3996.         // Если менялась система исчисления или валюта надо проверить, что все артикулы переданы
  3997.         if (
  3998.             empty($data['items'])
  3999.             || $oldOrder->getMeasure() != $order->getMeasure()
  4000.             || $oldOrder->getCurrency() != $order->getCurrency()
  4001.         ) {
  4002.             $data OrderHelper::itemsArr($order$data);
  4003.         }
  4004.         if ($this->itemService->updateItems($order$oldOrder$data['items'], $isRequestFrom1C)) {
  4005.             $this->buyOrderRepository->save($order);
  4006.         }
  4007.         return $data;
  4008.     }
  4009.     /**
  4010.      * @param BuyOrder $order
  4011.      * @return float
  4012.      */
  4013.     private function getOrderBalance(BuyOrder $order): float
  4014.     {
  4015.         $paymentSum OrderHelper::paymentSum($order);
  4016.         $totalSum OrderHelper::totalSum($order);
  4017.         return $totalSum $paymentSum;
  4018.     }
  4019.     /**
  4020.      * @param BuyOrder $order
  4021.      * @param array $data
  4022.      * @return bool
  4023.      * @throws Exception
  4024.      */
  4025.     private function isValidTaxNumber(BuyOrder $order, array $data): bool
  4026.     {
  4027.         $pattern VatTypeEnum::INDIVIDUAL === (int) ($data['vat']['type'] ?? VatTypeEnum::INDIVIDUAL)
  4028.             ? '/^(([a-z][0-9]{7}[a-z])|([a-z][0-9]{8})|([0-9]{8}[a-z]))$/i'
  4029.             '/^[a-z][0-9]{8}$/i';
  4030.         if (!== preg_match($patternstr_replace(' '''$data['taxNumber']))) {
  4031.             $errors = [
  4032.                 'taxNumber' => 'Spain tax number has an invalid format.',
  4033.             ];
  4034.             $this->setError($errors['taxNumber']);
  4035.             $this->log(
  4036.                 'SPAIN_TAX_CODE_IS_NOT_VALID',
  4037.                 $order->getHash(),
  4038.                 $errors
  4039.             );
  4040.             return false;
  4041.         }
  4042.         return true;
  4043.     }
  4044.     private function findUserByEmail(string $email): ?User
  4045.     {
  4046.         $userRepository $this->em->getRepository(User::class);
  4047.         return $userRepository->findOneBy(['email' => $email]);
  4048.     }
  4049. }