diff --git a/.travis.yml b/.travis.yml index 2cff9d1..b90be43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,9 @@ before_script: - composer install $flags script: - - php vendor/bin/php-cs-fixer fix --diff --dry-run --using-cache no --config .php_cs + - php vendor/bin/psalm + - php vendor/bin/php-cs-fixer fix --diff --dry-run --using-cache=no --config=.php_cs + - php vendor/bin/phpunit -c phpunit.xml after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file + - bash <(curl -s https://codecov.io/bash) diff --git a/Command/StatusesCommand.php b/Command/StatusesCommand.php deleted file mode 100644 index 3168b18..0000000 --- a/Command/StatusesCommand.php +++ /dev/null @@ -1,99 +0,0 @@ -setName('statuses:update') - ->setDescription('Update statuses') - ->addArgument('accountId', InputArgument::OPTIONAL, 'Choose account, or make it for all'); - } - - public function __construct(ModuleManagerInterface $moduleManager, AccountManager $accountManager) - { - $this->moduleManager = $moduleManager; - $this->accountManager = $accountManager; - - parent::__construct(); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - if (!$this->lock()) { - $output->writeln('The command is already running in another process.'); - - return 0; - } - - $accountId = $input->getArgument('accountId') - ? (int) $input->getArgument('accountId') - : null; - - $paginator = []; - if (null !== $accountId) { - $paginator = [$this->accountManager - find($accountId)]; - } else { - $accountQuery = $this->accountManager->getRepository() - ->createQueryBuilder('account') - ->where('account.active = true') - ->andWhere('account.freeze != true') - ->addOrderBy('account.id') - ->getQuery() - ->setFirstResult(0) - ->setMaxResults(100); - $paginator = new Paginator($accountQuery); - } - - $count = 0; - foreach ($paginator as $account) { - try { - $count += $this->moduleManager - ->setAccount($account) - ->updateStatuses() - ; - } catch (AbstractModuleException $e) { - $output->writeln( - "Failed to update statuses for account {$account->getCrmUrl()}[{$account->getId()}]" - ); - $output->writeln("Error: {$e->getMessage()}"); - } - } - - $output->writeln("{$count} statuses updated."); - - $this->release(); - - return 0; - } -} diff --git a/Command/UpdateModuleCommand.php b/Command/UpdateModuleCommand.php deleted file mode 100644 index a5e6632..0000000 --- a/Command/UpdateModuleCommand.php +++ /dev/null @@ -1,99 +0,0 @@ -setName('module:update') - ->setDescription('Update module') - ->addArgument('accountId', InputArgument::OPTIONAL, 'Choose account, or make it for all'); - } - - public function __construct(ModuleManagerInterface $moduleManager, AccountManager $accountManager) - { - $this->moduleManager = $moduleManager; - $this->accountManager = $accountManager; - - parent::__construct(); - } - - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - if (!$this->lock()) { - $output->writeln('The command is already running in another process.'); - - return 0; - } - - $accountId = $input->getArgument('accountId') - ? $input->getArgument('accountId') - : null; - - $paginator = []; - if (null !== $accountId) { - $paginator = [$this->accountManager->find($accountId)]; - } else { - $accountQuery = $this->accountManager->getRepository() - ->createQueryBuilder('account') - ->where('account.active = true') - ->andWhere('account.freeze != true') - ->addOrderBy('account.id') - ->getQuery() - ->setFirstResult(0) - ->setMaxResults(100); - $paginator = new Paginator($accountQuery); - } - - $count = 0; - foreach ($paginator as $account) { - try { - $this->moduleManager - ->setAccount($account) - ->updateModuleConfiguration() - ; - ++$count; - } catch (\Exception $e) { - $output->writeln( - "Failed to update configuration for account {$account->getCrmUrl()}[{$account->getId()}]" - ); - $output->writeln("Error: {$e->getMessage()}"); - } - } - - $output->writeln("{$count} modules updated."); - - $this->release(); - - return 0; - } -} diff --git a/Controller/ApiController.php b/Controller/ApiController.php deleted file mode 100644 index 86d6121..0000000 --- a/Controller/ApiController.php +++ /dev/null @@ -1,462 +0,0 @@ -jmsSerializer = $jmsSerializer; - $this->moduleManager = $moduleManager; - $this->accountManager = $accountManager; - $this->deliveryOrderManager = $deliveryOrderManager; - } - - public function activity(Request $request): JsonResponse - { - if (!is_string($request->request->get('activity'))) { - return $this->getInvalidResponse('Parameter "activity" must be json', 400); - } - $activity = $request->request->get('activity'); - - try { - $requestModel = $this->jmsSerializer->deserialize( - $activity, - IntegrationModule::class, - 'json', - DeserializationContext::create()->setGroups(['activity']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - $this->moduleManager->getAccount()->setActive($requestModel->active); - $this->moduleManager->getAccount()->setFreeze($requestModel->freeze); - - $systemUrl = $request->request->get('systemUrl'); - $this->moduleManager->getAccount()->setCrmUrl($systemUrl); - - $this->accountManager->flush(); - - return $this->getSucessfullResponse(); - } - - public function calculate(Request $request): JsonResponse - { - if (!is_string($request->request->get('calculate'))) { - return $this->getInvalidResponse('Parameter "calculate" must be json', 400); - } - $requestData = $request->request->get('calculate'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestCalculate::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - try { - $responseModel = $this->doCalculate($requestModel); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - $response = new ResponseCalculateSuccessful($responseModel); - $response->result = $responseModel; - - return $this->getSucessfullResponse($response); - } - - public function save(Request $request): JsonResponse - { - if (!is_string($request->request->get('save'))) { - return $this->getInvalidResponse('Parameter "save" must be json', 400); - } - $requestData = $request->request->get('save'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestSave::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - if (null === $requestModel->delivery) { - return $this->getInvalidResponse('Invalid request format', 400); - } - - $delivery = $this->deliveryOrderManager->findOneBy([ - 'account' => $this->moduleManager->getAccount(), - 'orderId' => $requestModel->order, - ]); - - // ищем доставки, созданные без заказа в запросе get - if (null === $delivery) { - $delivery = $this->deliveryOrderManager - ->findOneBy([ - 'account' => $this->moduleManager->getAccount(), - 'externalId' => $requestModel->deliveryId, - ]); - } - - try { - $responseModel = $this->doSave($requestModel, $delivery); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - if (null === $delivery) { - $delivery = $this->deliveryOrderManager->create(); - } - - $delivery - ->setAccount($this->moduleManager->getAccount()) - ->setOrderId($requestModel->order) - ->setExternalId($responseModel->deliveryId) - ; - if ($responseModel->trackNumber) { - $delivery->setTrackNumber($responseModel->trackNumber); - } - - if (is_array($responseModel->additionalData)) { - foreach ($responseModel->additionalData as $key => $value) { - $setter = 'set' . ucfirst($key); - if (is_callable([$delivery, $setter])) { - $delivery->$setter($value); - } - } - } - - if (empty($delivery->getId())) { - $this->deliveryOrderManager->persist($delivery); - } - $this->deliveryOrderManager->flush(); - - return $this->getSucessfullResponse($responseModel); - } - - public function getDeliveryOrder(Request $request): JsonResponse - { - $externalId = $request->query->get('deliveryId'); - if (null === $externalId || empty($externalId)) { - return $this->getInvalidResponse('DeliveryId is required', 400); - } - - try { - $responseModel = $this->doGet($externalId); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - return $this->getSucessfullResponse($responseModel); - } - - public function delete(Request $request): JsonResponse - { - if (!is_string($request->request->get('delete'))) { - return $this->getInvalidResponse('Parameter "delete" must be json', 400); - } - $requestData = $request->request->get('delete'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestDelete::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - $delivery = $this->deliveryOrderManager->findOneBy([ - 'account' => $this->moduleManager->getAccount(), - 'externalId' => $requestModel->deliveryId, - ]); - - if (null === $delivery) { - return $this->getInvalidResponse("Delivery '{$requestModel->deliveryId}' not found", 404); - } - - try { - $this->doDelete($requestModel, $delivery); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - $this->deliveryOrderManager->remove($delivery); - $this->deliveryOrderManager->flush(); - - return $this->getSucessfullResponse(); - } - - public function shipmentPointList(Request $request): JsonResponse - { - $requestData = json_encode($request->query->all()); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestShipmentPointList::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - try { - $responseModel = $this->doShipmentPointList($requestModel); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - $response = new ResponseShipmentPointListSuccessful($responseModel); - $response->result = $responseModel; - - return $this->getSucessfullResponse($response); - } - - public function print(Request $request): Response - { - if (!is_string($request->request->get('print'))) { - return $this->getInvalidResponse('Parameter "print" must be json', 400); - } - $requestData = $request->request->get('print'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestPrint::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - try { - $plateData = $this->doPrint($requestModel); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\NotFoundException $e) { - return $this->getInvalidResponse($e->getMessage(), 404); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - if (count($plateData) > 1) { - $tmpFilename = tempnam(sys_get_temp_dir(), 'zip'); - $labelArchive = new \ZipArchive(); - $labelArchive->open($tmpFilename, \ZipArchive::CREATE); - foreach ($plateData as $fileName => $plate) { - $labelArchive->addFromString($fileName, $plate); - } - $labelArchive->close(); - $contents = file_get_contents($tmpFilename); - unlink($tmpFilename); - - $response = new Response($contents); - $response->headers->set('Content-Type', 'application/zip'); - } else { - $response = new Response(reset($plateData)); - $response->headers->set('Content-Type', 'application/pdf'); - } - - return $response; - } - - public function shipmentSave(Request $request): JsonResponse - { - if (!is_string($request->request->get('shipmentSave'))) { - return $this->getInvalidResponse('Parameter "shipmentSave" must be json', 400); - } - $requestData = $request->request->get('shipmentSave'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestShipmentSave::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - try { - $responseModel = $this->doShipmentSave($requestModel); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - return $this->getSucessfullResponse($responseModel); - } - - public function shipmentDelete(Request $request): JsonResponse - { - if (!is_string($request->request->get('shipmentDelete'))) { - return $this->getInvalidResponse('Parameter "shipmentDelete" must be json', 400); - } - $requestData = $request->request->get('shipmentDelete'); - try { - $requestModel = $this->jmsSerializer->deserialize( - $requestData, - RequestShipmentDelete::class, - 'json', - DeserializationContext::create()->setGroups(['get', 'request']) - ); - } catch (JmsException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - try { - $this->doShipmentDelete($requestModel); - } catch (Exception\ServerUnreachableException $e) { - return $this->getInvalidResponse($e->getMessage(), 521); - } catch (Exception\AbstractModuleException $e) { - return $this->getInvalidResponse($e->getMessage(), 400); - } - - return $this->getSucessfullResponse(); - } - - protected function getSucessfullResponse(object $responseResult = null): JsonResponse - { - if (!$responseResult instanceof AbstractResponseSuccessful) { - $response = new ResponseSuccessful(); - $response->result = $responseResult; - } else { - $response = $responseResult; - } - - $responseData = $this->jmsSerializer - ->serialize($response, 'json', SerializationContext::create()->setGroups(['response'])); - - return new JsonResponse(json_decode($responseData, true)); - } - - protected function getInvalidResponse(string $message, int $statusCode): JsonResponse - { - if ($statusCode >= 500) { - //корректно отдается в crm только в окружении prod - throw new SymfonyException\HttpException($statusCode, json_encode(['success' => false, 'errorMsg' => $message])); - } - - return new JsonResponse([ - 'success' => false, - 'errorMsg' => $message, - ], $statusCode); - } - - protected function doCalculate(RequestCalculate $requestModel): array - { - return $this->moduleManager->calculateDelivery($requestModel); - } - - protected function doGet(string $externalId): ResponseLoadDeliveryData - { - return $this->moduleManager->getDelivery($externalId); - } - - protected function doSave(RequestSave $requestModel, DeliveryOrder $delivery = null): ResponseSave - { - return $this->moduleManager->saveDelivery($requestModel, $delivery); - } - - protected function doDelete(RequestDelete $requestModel, DeliveryOrder $delivery): bool - { - return $this->moduleManager->deleteDelivery($requestModel, $delivery); - } - - protected function doShipmentPointList(RequestShipmentPointList $requestModel): array - { - return $this->moduleManager->shipmentPointList($requestModel); - } - - protected function doShipmentSave(RequestShipmentSave $requestModel): ResponseShipmentSave - { - return $this->moduleManager->saveShipment($requestModel); - } - - protected function doShipmentDelete(RequestShipmentDelete $requestModel): bool - { - return $this->moduleManager->deleteShipment($requestModel); - } - - protected function doPrint(RequestPrint $requestModel) - { - return $this->moduleManager->printDocument($requestModel); - } -} diff --git a/Controller/ClientIdSecuredControllerInterface.php b/Controller/ClientIdSecuredControllerInterface.php deleted file mode 100644 index 6f7b89d..0000000 --- a/Controller/ClientIdSecuredControllerInterface.php +++ /dev/null @@ -1,7 +0,0 @@ -getRootNode(); - - $rootNode - ->children() - ->scalarNode('module_manager_class') - ->cannotBeEmpty() - ->end() - ->scalarNode('account_class') - ->isRequired() - ->cannotBeEmpty() - ->end() - ->scalarNode('delivery_order_class') - ->isRequired() - ->cannotBeEmpty() - ->end() - - ->arrayNode('configuration') - ->children() - ->scalarNode('integration_code') - ->cannotBeEmpty() - ->end() - - ->arrayNode('countries') - ->prototype('scalar')->end() - ->requiresAtLeastOneElement() - ->defaultValue(['ru']) - ->end() - - ->arrayNode('locales') - ->requiresAtLeastOneElement() - ->useAttributeAsKey('locale') - - ->arrayPrototype() - ->children() - ->scalarNode('name') - ->isRequired() - ->end() - ->scalarNode('logo') - ->isRequired() - ->end() - ->end() - ->end() - ->end() - - ->variableNode('parameters')->end() - ->end() - ->end() - ->end() - ; - - return $treeBuilder; - } -} diff --git a/DependencyInjection/RetailCrmDeliveryModuleExtension.php b/DependencyInjection/RetailCrmDeliveryModuleExtension.php deleted file mode 100644 index f3a76a9..0000000 --- a/DependencyInjection/RetailCrmDeliveryModuleExtension.php +++ /dev/null @@ -1,75 +0,0 @@ -load('services.yaml'); - - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); - - $container->setParameter( - 'retailcrm.delivery_module.configuration', - $config['configuration'] - ); - - $moduleManagerClass = $config['module_manager_class']; - if (!class_exists($moduleManagerClass)) { - throw new \InvalidArgumentException("module_manager_class '{$moduleManagerClass}' does not exists"); - } - if (!is_subclass_of($moduleManagerClass, ModuleManagerInterface::class)) { - throw new \InvalidArgumentException("module_manager_class '{$moduleManagerClass}' must implement ModuleManagerInterace"); - } - $container->setParameter( - 'retailcrm.delivery_module.module_manager.class', - $moduleManagerClass - ); - - $accountClass = $config['account_class']; - if (!class_exists($accountClass)) { - throw new \InvalidArgumentException("account_class'] '{$accountClass}' does not exists"); - } - if (!is_subclass_of($accountClass, Account::class)) { - throw new \InvalidArgumentException("account_class '{$accountClass}' must extend " . Account::class); - } - $container->setParameter( - 'retailcrm.delivery_module.account.class', - $accountClass - ); - - $deliveryOrderClass = $config['delivery_order_class']; - if (!class_exists($deliveryOrderClass)) { - throw new \InvalidArgumentException("delivery_order_class'] '{$deliveryOrderClass}' does not exists"); - } - if (!is_subclass_of($deliveryOrderClass, DeliveryOrder::class)) { - throw new \InvalidArgumentException("delivery_order_class '{$deliveryOrderClass}' must extend " . DeliveryOrder::class); - } - $container->setParameter( - 'retailcrm.delivery_module.delivery_order.class', - $config['delivery_order_class'] - ); - } -} diff --git a/EventSubscriber/ClientIdSubscriber.php b/EventSubscriber/ClientIdSubscriber.php deleted file mode 100644 index 47193e7..0000000 --- a/EventSubscriber/ClientIdSubscriber.php +++ /dev/null @@ -1,72 +0,0 @@ -accountManager = $accountManager; - $this->moduleManager = $moduleManager; - } - - public static function getSubscribedEvents() - { - return [ - KernelEvents::CONTROLLER => 'onKernelController', - ]; - } - - public function onKernelController(ControllerEvent $event) - { - $controller = $event->getController(); - if (is_array($controller)) { - $controller = $controller[0]; - } - - if (!$controller instanceof ClientIdSecuredControllerInterface) { - return; - } - - $request = $event->getRequest(); - - if ($request->isMethod('post')) { - $clientId = $request->request->get('clientId'); - } else { - $clientId = $request->query->get('clientId'); - } - if (empty($clientId)) { - throw new AccessDeniedHttpException('ClientId required'); - } - - if (!Uuid::isValid($clientId)) { - throw new AccessDeniedHttpException('ClientId is not valid'); - } - - $account = $this->accountManager->findOneBy(['clientId' => $clientId]); - if (null === $account) { - throw new AccessDeniedHttpException('ClientId not found'); - } - - $this->moduleManager->setAccount($account); - } -} diff --git a/EventSubscriber/SerializeListener.php b/EventSubscriber/SerializeListener.php deleted file mode 100644 index 9473dfc..0000000 --- a/EventSubscriber/SerializeListener.php +++ /dev/null @@ -1,27 +0,0 @@ - Events::PRE_SERIALIZE, 'method' => 'onPreSerialize', 'class' => ResponseResult::class], - ]; - } - - public function onPreSerialize(PreSerializeEvent $event) - { - if (is_object($event->getObject())) { - $event->setType(get_class($event->getObject())); - } else { - $event->setType('string'); - } - } -} diff --git a/Exception/AbstractModuleException.php b/Exception/AbstractModuleException.php deleted file mode 100644 index 6366946..0000000 --- a/Exception/AbstractModuleException.php +++ /dev/null @@ -1,7 +0,0 @@ -") - */ - public $actions; - - /** - * Допустивые типы плательщиков за доставку. - * - * @var array - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("payerType") - * @Serializer\Type("array") - */ - public $payerType; - - /** - * Максимальное количество заказов при печати документов. - * - * @var int - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("platePrintLimit") - * @Serializer\Type("integer") - */ - public $platePrintLimit = 100; - - /** - * В методе calculate расчитывается стоимость доставки. - * - * @var bool - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("rateDeliveryCost") - * @Serializer\Type("boolean") - */ - public $rateDeliveryCost = true; - - /** - * Разрешить использование упаковок. - * - * @var bool - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("allowPackages") - * @Serializer\Type("boolean") - */ - public $allowPackages = false; - - /** - * Доставка наложенным платежом доступна/не доступна. - * - * @var bool - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("codAvailable") - * @Serializer\Type("boolean") - */ - public $codAvailable = false; - - /** - * Возможен самопривоз на терминал. - * - * @var bool - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("selfShipmentAvailable") - * @Serializer\Type("boolean") - */ - public $selfShipmentAvailable = false; - - /** - * Возможность работы с заказом, содержащим несколько позиций с одинаковым торговым предложением - * - * @var bool - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("duplicateOrderProductSupported") - * @Serializer\Type("boolean") - */ - public $duplicateOrderProductSupported = true; - - /** - * Разрешить отдельно передавать трек номер - * - * @var string - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("allowTrackNumber") - * @Serializer\Type("boolean") - */ - public $allowTrackNumber; - - /** - * Список стран откуда можно отправить посылку. Если массив пустой, то нет ограничения на страны. - * - * @var array - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("availableCountries") - * @Serializer\Type("array") - */ - public $availableCountries; - - /** - * Список обязательных полей заказа. - * - * @var array - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("requiredFields") - * @Serializer\Type("array") - */ - public $requiredFields; - - /** - * Список статусов службы доставки. - * - * @var Status[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("statusList") - * @Serializer\Type("array") - */ - public $statusList; - - /** - * Список печатных форм, предоставляемых службой. - * - * @var Plate[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("plateList") - * @Serializer\Type("array") - */ - public $plateList; - - /** - * Список дополнительных полей, необходимых для оформления доставки. - * - * @var DeliveryDataField[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("deliveryDataFieldList") - * @Serializer\Type("array") - */ - public $deliveryDataFieldList; - - /** - * Список дополнительных полей, необходимых для заявки на отгрузку. - * - * @var DeliveryDataField[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("shipmentDataFieldList") - * @Serializer\Type("array") - */ - public $shipmentDataFieldList; - - /** - * Массив настроек модуля - * - * @var Settings - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("settings") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\Settings") - */ - public $settings; -} diff --git a/Model/Contragent.php b/Model/Contragent.php deleted file mode 100644 index 26a4e21..0000000 --- a/Model/Contragent.php +++ /dev/null @@ -1,68 +0,0 @@ -") - */ - public $phones; - - /** - * E-mail. - * - * @var string - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("email") - * @Serializer\Type("string") - */ - public $email; - - /** - * Данные контрагента. - * - * @var Contragent - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("contragent") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\Model\Contragent") - */ - public $contragent; - - public function getNickName(): ?string - { - $result = trim( - $this->lastName . ' ' . $this->firstName . ' ' . $this->patronymic - ); - - return $result; - } -} diff --git a/Model/DeliveryAddress.php b/Model/DeliveryAddress.php deleted file mode 100644 index 8006880..0000000 --- a/Model/DeliveryAddress.php +++ /dev/null @@ -1,250 +0,0 @@ -") - * @Serializer\Groups({"set", "get", "orderHistory", "history-reference", "calculate"}) - * @Serializer\Accessor(getter="getFrom", setter="setFrom") - * - * @var \DateTime|null - */ - protected $from; - - /** - * Время доставки "до" - * - * @Serializer\SerializedName("to") - * @Serializer\Type("DateTime<'H:i'>") - * @Serializer\Groups({"set", "get", "orderHistory", "history-reference", "calculate"}) - * @Serializer\Accessor(getter="getTo", setter="setTo") - * - * @var \DateTime|null - */ - protected $to; - - /** - * Время доставки (произвольный текст) - * - * @Serializer\SerializedName("custom") - * @Serializer\Type("string") - * @Serializer\Groups({"set", "get", "orderHistory", "history-reference", "calculate"}) - * - * @var string|null - */ - protected $custom; - - /** - * @param string|\DateTime|null $from - * @param string|\DateTime|null $to - * @param string|null $custom - * - * @return self - */ - public function __construct($from = null, $to = null, $custom = null) - { - $this->setFrom($from); - $this->setTo($to); - $this->setCustom($custom); - } - - /** - * Разбор строки со временем доставки - * - * @param string $time - * - * @return self - */ - public static function fromString($time) - { - $result = new self(); - $result->setString($time); - - return $result; - } - - /** - * @return \DateTime|null - */ - public function getFrom() - { - if ($this->from) { - $this->from->setDate(1970, 01, 01); - - if ('00:00:00' === $this->from->format('H:i:s')) { - return null; - } - } - - return $this->from; - } - - /** - * @param \DateTime|string|null $from - * - * @return $this - */ - public function setFrom($from) - { - $this->from = $this->ensureTime($from); - $this->ensureConsistency(); - - return $this; - } - - /** - * @return \DateTime|null - */ - public function getTo() - { - if ($this->to) { - $this->to->setDate(1970, 01, 01); - - if ('23:59:59' === $this->to->format('H:i:s')) { - return null; - } - } - - return $this->to; - } - - /** - * @param \DateTime|string|null $to - * - * @return $this - */ - public function setTo($to) - { - $this->to = $this->ensureTime($to); - $this->ensureConsistency(); - - return $this; - } - - /** - * @return string - */ - public function getCustom() - { - return $this->custom; - } - - /** - * @param string $custom - * - * @return $this - */ - public function setCustom($custom) - { - $this->custom = $custom; - - return $this; - } - - /** - * @param string $time - * - * @return $this - */ - public function setString($time) - { - // точное время: 12.30, 12:30 - $exactPattern = '/^в?\s*(\d{2}[:\.]\d{2})$/u'; - // диапазон времени: 12-13, c 12.00 по 13:00 - $rangePattern = '/^с?\s*(?P\d{2}[:\.]?\d{0,2})\s*(-|по|до)\s*(?P\d{2}[:\.]?\d{0,2})/u'; - // диапазон времени: c 12.00 - $rangeFromPattern = '/^с?\s*(?P\d{2}[:\.]?\d{0,2})/u'; - // диапазон времени: до 13:00 - $rangeToPattern = '/^(-|по|до)\s*(?P\d{2}[:\.]?\d{0,2})/u'; - - if (preg_match($exactPattern, $time, $matches)) { - $timeObj = new \DateTime($matches[1]); - $this->setFrom(clone $timeObj); - $this->setTo(clone $timeObj); - } elseif (preg_match($rangePattern, $time, $matches)) { - $from = $matches['from']; - $to = $matches['to']; - - $from = preg_match($exactPattern, $from) ? $from : $from . ':00'; - $to = preg_match($exactPattern, $to) ? $to : $to . ':00'; - - try { - $this->setFrom(new \DateTime($from)); - $this->setTo(new \DateTime($to)); - } catch (\Exception $e) { - $this->setFrom(null); - $this->setTo(null); - $this->setCustom($time); - } - } elseif (preg_match($rangeFromPattern, $time, $matches)) { - $from = $matches['from']; - $from = preg_match($exactPattern, $from) ? $from : $from . ':00'; - - try { - $this->setFrom(new \DateTime($from)); - $this->setTo(null); - } catch (\Exception $e) { - $this->setFrom(null); - $this->setTo(null); - $this->setCustom($time); - } - } elseif (preg_match($rangeToPattern, $time, $matches)) { - $to = $matches['to']; - $to = preg_match($exactPattern, $to) ? $to : $to . ':00'; - - try { - $this->setFrom(null); - $this->setTo(new \DateTime($to)); - } catch (\Exception $e) { - $this->setFrom(null); - $this->setTo(null); - $this->setCustom($time); - } - } else { - $this->setFrom(null); - $this->setTo(null); - $this->setCustom($time); - } - - return $this; - } - - /** - * @return string - */ - public function getString() - { - $from = $this->getFrom(); - $to = $this->getTo(); - $custom = $this->getCustom(); - - if (!($from || $to)) { - return (string) $custom; - } - - $fromPrint = $from ? $from->format('H:i') : null; - $toPrint = $to ? $to->format('H:i') : null; - - if ($fromPrint && $fromPrint === $toPrint) { - return 'в ' . $fromPrint; - } - - $str = ''; - if ($fromPrint) { - $str .= 'с ' . $fromPrint; - } - if ($toPrint) { - $str .= ' до ' . $toPrint; - } - - return trim($str); - } - - /** - * Проверяет, соответствует ли время доставки диапазону из настроек - * - * @return bool - */ - public function equalsRange(array $range) - { - $fromEquals = false; - $toEquals = false; - - $from = $this->getFrom(); - $to = $this->getTo(); - - if ($from) { - if (isset($range['from'])) { - $fromEquals = $from->format('H:i') === $range['from']; - } - } else { - if (!isset($range['from']) || - !$range['from'] || - '00:00' === $range['from'] || - '00:00:00' === $range['from'] - ) { - $fromEquals = true; - } - } - - if ($to) { - if (isset($range['to'])) { - $toEquals = $to->format('H:i') === $range['to']; - } - } else { - if (!isset($range['to']) || - !$range['to'] || - '23:59' === $range['from'] || - '23:59:59' === $range['from'] - ) { - $toEquals = true; - } - } - - return $fromEquals && $toEquals; - } - - /** - * @return bool - */ - public function isEmpty() - { - return !($this->from || $this->to || $this->custom); - } - - /** - * @return string - */ - public function __toString() - { - return $this->getString(); - } - - protected function ensureTime($time) - { - if ($time) { - if (!$time instanceof \DateTime) { - $time = new \DateTime((string) $time); - } - $time->setDate(1970, 01, 01); - } - - return $time; - } - - /** - * Если для времени доставки указана только одна граница диапазона, то присвоим другой значение по умолчанию - */ - protected function ensureConsistency() - { - $from = $this->getFrom(); - $to = $this->getTo(); - - if (null === $from && null !== $to) { - $this->from = new \DateTime('1970-01-01T00:00:00'); - } elseif (null === $to && null !== $from) { - $this->to = new \DateTime('1970-01-01T23:59:59'); - } elseif (null === $to && null === $from) { - $this->to = null; - $this->from = null; - } - } -} diff --git a/Model/Entity/Account.php b/Model/Entity/Account.php deleted file mode 100644 index c3d6e4d..0000000 --- a/Model/Entity/Account.php +++ /dev/null @@ -1,165 +0,0 @@ -") - */ - protected $createdAt; - - /** - * @var string - * - * @Serializer\Groups({"get", "connect"}) - * @Serializer\Type("string") - */ - protected $crmUrl; - - /** - * @var string - * - * @Serializer\Groups({"get", "connect"}) - * @Serializer\Type("string") - */ - protected $crmApiKey; - - /** - * @var bool - */ - protected $active; - - /** - * @var bool - */ - protected $freeze; - - /** - * @var string - */ - protected $language; - - public function __construct() - { - $this->clientId = Uuid::uuid4(); - $this->createdAt = new \DateTime(); - $this->active = false; - $this->freeze = false; - } - - public function getId(): int - { - return $this->id; - } - - public function setClientId(UuidInterface $clientId): self - { - $this->clientId = $clientId; - - return $this; - } - - public function getClientId(): ?UuidInterface - { - return $this->clientId; - } - - public function getCreatedAt(): \DateTime - { - return $this->createdAt; - } - - public function setCreatedAt(\DateTime $createdAt): self - { - $this->createdAt = $createdAt; - - return $this; - } - - public function getCrmUrl(): ?string - { - return $this->crmUrl; - } - - public function setCrmUrl(?string $crmUrl): self - { - $this->crmUrl = rtrim($crmUrl, '/'); - - return $this; - } - - public function getCrmApiKey(): ?string - { - return $this->crmApiKey; - } - - public function setCrmApiKey(?string $crmApiKey): self - { - $this->crmApiKey = $crmApiKey; - - return $this; - } - - public function setActive(bool $active): self - { - $this->active = $active; - - return $this; - } - - public function isActive(): bool - { - return $this->active; - } - - public function setFreeze(bool $freeze): self - { - $this->freeze = $freeze; - - return $this; - } - - public function isFreeze(): bool - { - return $this->freeze; - } - - /** - * @Serializer\VirtualProperty - * @Serializer\Type("boolean") - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("isEnabled") - */ - public function isEnabled(): bool - { - return !$this->freeze && $this->active; - } - - public function setLanguage(string $language): self - { - $this->language = $language; - - return $this; - } - - public function getLanguage(): ?string - { - return $this->language; - } -} diff --git a/Model/IntegrationModule.php b/Model/IntegrationModule.php deleted file mode 100644 index c85226d..0000000 --- a/Model/IntegrationModule.php +++ /dev/null @@ -1,140 +0,0 @@ -") - */ - public $actions; - - /** - * Список стран для которых доступен модуль - * - * @var array - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("availableCountries") - * @Serializer\Type("array") - */ - public $availableCountries; - - /** - * URL настроек модуля - * - * @var string - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("accountUrl") - * @Serializer\Type("string") - */ - public $accountUrl; - - /** - * Массив конфигураций интеграций - * - * @var array - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("integrations") - * @Serializer\Type("array") - */ - public $integrations; -} diff --git a/Model/Manager.php b/Model/Manager.php deleted file mode 100644 index e841dc1..0000000 --- a/Model/Manager.php +++ /dev/null @@ -1,83 +0,0 @@ -lastName . ' ' . $this->firstName . ' ' . $this->patronymic - ); - - return $result; - } -} diff --git a/Model/Package.php b/Model/Package.php deleted file mode 100644 index 39e6688..0000000 --- a/Model/Package.php +++ /dev/null @@ -1,102 +0,0 @@ -") - */ - public $items; - - public function __construct($weight = null, $width = null, $length = null, $height = null) - { - $this->weight = $weight; - $this->width = $width; - $this->length = $length; - $this->height = $height; - } - - public function getVolume() - { - if (null !== $this->length - && null !== $this->width - && null !== $this->height - ) { - return $this->length * $this->width * $this->height; - } else { - return false; - } - } - - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata - ->addPropertyConstraint('weight', new Assert\NotBlank()); - } -} diff --git a/Model/PackageItem.php b/Model/PackageItem.php deleted file mode 100644 index cb01deb..0000000 --- a/Model/PackageItem.php +++ /dev/null @@ -1,107 +0,0 @@ -") - */ - public $properties; -} diff --git a/Model/PaymentType.php b/Model/PaymentType.php deleted file mode 100644 index 61be14c..0000000 --- a/Model/PaymentType.php +++ /dev/null @@ -1,22 +0,0 @@ -code = $code; - $this->label = $label; - } -} diff --git a/Model/Request/RequestCalculate.php b/Model/Request/RequestCalculate.php deleted file mode 100644 index f0eb59e..0000000 --- a/Model/Request/RequestCalculate.php +++ /dev/null @@ -1,121 +0,0 @@ -") - */ - public $packages; - - /** - * Объявленная стоимость. - * - * @var float - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("declaredValue") - * @Serializer\Type("float") - */ - public $declaredValue; - - /** - * Наложенный платеж. - * - * @var float - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("cod") - * @Serializer\Type("float") - */ - public $cod; - - /** - * Плательщик за доставку. - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("payerType") - * @Serializer\Type("string") - */ - public $payerType; - - /** - * Дата доставки. - * - * @var \DateTime - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("deliveryDate") - * @Serializer\Type("DateTime<'Y-m-d'>") - */ - public $deliveryDate; - - /** - * Время доставки. - * - * @var DeliveryTime - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("deliveryTime") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $deliveryTime; - - /** - * Валюта. - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("currency") - * @Serializer\Type("string") - */ - public $currency; - - /** - * Дополнительные данные доставки. - * - * @var array - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; -} diff --git a/Model/Request/RequestDelete.php b/Model/Request/RequestDelete.php deleted file mode 100644 index 69f8031..0000000 --- a/Model/Request/RequestDelete.php +++ /dev/null @@ -1,17 +0,0 @@ -") - */ - public $packages; - - /** - * Данные доставки - * - * @var RetailCrm\DeliveryModuleBundle\Model\SaveDeliveryData - * - * @Serializer\Groups({"request"}) - * @Serializer\SerializedName("delivery") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\SaveDeliveryData") - */ - public $delivery; - - /** - * Валюта - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("currency") - * @Serializer\Type("string") - */ - public $currency; - - public function getFullDeclaredValue() - { - $result = 0; - foreach ($this->packages as $package) { - foreach ($package->items as $item) { - $result += $item->declaredValue * $item->quantity; - } - } - - return $result; - } - - public function getFullItemsCodValue() - { - $result = 0; - foreach ($this->packages as $package) { - foreach ($package->items as $item) { - $result += $item->cod * $item->quantity; - } - } - - return $result; - } -} diff --git a/Model/Request/RequestShipmentDelete.php b/Model/Request/RequestShipmentDelete.php deleted file mode 100644 index 2541ea7..0000000 --- a/Model/Request/RequestShipmentDelete.php +++ /dev/null @@ -1,30 +0,0 @@ -") - */ - public $date; - - /** - * Время доставки ("custom" не ипользуется) - * - * @var RetailCrm\DeliveryModuleBundle\Model\DeliveryTime - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("time") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $time; - - /** - * Адрес отгрузки - * - * @var string - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("address") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $address; - - /** - * Массив идентификаторов оформленных доставок в службе доставки - * - * @var array - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("orders") - * @Serializer\Type("array") - */ - public $orders; - - /** - * @var string - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("comment") - * @Serializer\Type("string") - */ - public $comment; - - /** - * Дополнительные данные отгрузки - * - * @var array - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; -} diff --git a/Model/Request/RequestStatusUpdateItem.php b/Model/Request/RequestStatusUpdateItem.php deleted file mode 100644 index db0dd4f..0000000 --- a/Model/Request/RequestStatusUpdateItem.php +++ /dev/null @@ -1,57 +0,0 @@ -") - */ - public $history; - - /** - * Массив дополнительных данных доставки - * - * @var array - * - * @Serializer\Groups({"request"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; - - public function __construct() - { - $this->history = []; - } -} diff --git a/Model/RequestCalculate.php b/Model/RequestCalculate.php deleted file mode 100644 index 63acab0..0000000 --- a/Model/RequestCalculate.php +++ /dev/null @@ -1,120 +0,0 @@ -") - */ - public $packages; - - /** - * Объявленная стоимость. - * - * @var float - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("declaredValue") - * @Serializer\Type("float") - */ - public $declaredValue; - - /** - * Наложенный платеж. - * - * @var float - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("cod") - * @Serializer\Type("float") - */ - public $cod; - - /** - * Плательщик за доставку. - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("payerType") - * @Serializer\Type("string") - */ - public $payerType; - - /** - * Дата доставки. - * - * @var \DateTime - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("deliveryDate") - * @Serializer\Type("DateTime<'Y-m-d'>") - */ - public $deliveryDate; - - /** - * Время доставки. - * - * @var DeliveryTime - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("deliveryTime") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $deliveryTime; - - /** - * Валюта. - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("currency") - * @Serializer\Type("string") - */ - public $currency; - - /** - * Дополнительные данные доставки. - * - * @var array - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; -} diff --git a/Model/RequestDelete.php b/Model/RequestDelete.php deleted file mode 100644 index 69f8031..0000000 --- a/Model/RequestDelete.php +++ /dev/null @@ -1,17 +0,0 @@ -") - */ - public $packages; - - /** - * Данные доставки. - * - * @var SaveDeliveryData - * - * @Serializer\Groups({"request"}) - * @Serializer\SerializedName("delivery") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\SaveDeliveryData") - */ - public $delivery; - - /** - * Валюта. - * - * @var string - * - * @Serializer\Groups({"request", "calculate"}) - * @Serializer\SerializedName("currency") - * @Serializer\Type("string") - */ - public $currency; - - public function getFullDeclaredValue() - { - $result = 0; - foreach ($this->packages as $package) { - foreach ($package->items as $item) { - $result += $item->declaredValue * $item->quantity; - } - } - - return $result; - } - - public function getFullItemsCodValue() - { - $result = 0; - foreach ($this->packages as $package) { - foreach ($package->items as $item) { - $result += $item->cod * $item->quantity; - } - } - - return $result; - } -} diff --git a/Model/RequestShipmentDelete.php b/Model/RequestShipmentDelete.php deleted file mode 100644 index 2541ea7..0000000 --- a/Model/RequestShipmentDelete.php +++ /dev/null @@ -1,30 +0,0 @@ -") - */ - public $date; - - /** - * Время доставки ("custom" не ипользуется) - * - * @var RetailCrm\DeliveryModuleBundle\Model\DeliveryTime - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("time") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $time; - - /** - * Адрес отгрузки - * - * @var string - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("address") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $address; - - /** - * Массив идентификаторов оформленных доставок в службе доставки - * - * @var array - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("orders") - * @Serializer\Type("array") - */ - public $orders; - - /** - * @var string - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("comment") - * @Serializer\Type("string") - */ - public $comment; - - /** - * Дополнительные данные отгрузки - * - * @var array - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; -} diff --git a/Model/RequestStatusUpdateItem.php b/Model/RequestStatusUpdateItem.php deleted file mode 100644 index db0dd4f..0000000 --- a/Model/RequestStatusUpdateItem.php +++ /dev/null @@ -1,57 +0,0 @@ -") - */ - public $history; - - /** - * Массив дополнительных данных доставки - * - * @var array - * - * @Serializer\Groups({"request"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; - - public function __construct() - { - $this->history = []; - } -} diff --git a/Model/Response/ResponseAutocompleteItem.php b/Model/Response/ResponseAutocompleteItem.php deleted file mode 100644 index 6aac639..0000000 --- a/Model/Response/ResponseAutocompleteItem.php +++ /dev/null @@ -1,42 +0,0 @@ -value = $value; - $this->label = $label; - $this->description = $description; - } -} diff --git a/Model/Response/ResponseAutocompleteSuccessful.php b/Model/Response/ResponseAutocompleteSuccessful.php deleted file mode 100644 index 75b38ae..0000000 --- a/Model/Response/ResponseAutocompleteSuccessful.php +++ /dev/null @@ -1,26 +0,0 @@ -") - */ - public $result; -} diff --git a/Model/Response/ResponseCalculate.php b/Model/Response/ResponseCalculate.php deleted file mode 100644 index dffb547..0000000 --- a/Model/Response/ResponseCalculate.php +++ /dev/null @@ -1,137 +0,0 @@ -") - */ - public $pickuppointList; - - public function __construct() - { - $this->extraData = []; - } -} diff --git a/Model/Response/ResponseCalculateSuccessful.php b/Model/Response/ResponseCalculateSuccessful.php deleted file mode 100644 index 16b4bd7..0000000 --- a/Model/Response/ResponseCalculateSuccessful.php +++ /dev/null @@ -1,26 +0,0 @@ -") - */ - public $result; -} diff --git a/Model/Response/ResponseLoadDeliveryData.php b/Model/Response/ResponseLoadDeliveryData.php deleted file mode 100644 index e3654a5..0000000 --- a/Model/Response/ResponseLoadDeliveryData.php +++ /dev/null @@ -1,148 +0,0 @@ -") - */ - public $shipmentDate; - - /** - * Дата доставки - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryDate") - * @Serializer\Type("DateTime<'Y-m-d'>") - */ - public $deliveryDate; - - /** - * Время доставки - * - * @var RetailCrm\DeliveryModuleBundle\Model\DeliveryTime - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryTime") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $deliveryTime; - - /** - * Код тарифа - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("tariff") - * @Serializer\Type("string") - */ - public $tariff; - - /** - * Наименование тарифа - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("tariffName") - * @Serializer\Type("string") - */ - public $tariffName; - - /** - * Плательщик за доставку - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("payerType") - * @Serializer\Type("string") - */ - public $payerType; - - /** - * Текущий статус достаквки - * - * @var StatusInfo - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("status") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\StatusInfo") - */ - public $status; - - /** - * Дополнительные данные доставки - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; - - /** - * Адрес отгрузки - * - * @var DeliveryAddress - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("shipmentAddress") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $shipmentAddress; - - /** - * Адрес доставки - * - * @var DeliveryAddress - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryAddress") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $deliveryAddress; - - public $additionalData; - - public function __construct() - { - $this->extraData = []; - $this->additionalData = []; - } -} diff --git a/Model/Response/ResponseResult.php b/Model/Response/ResponseResult.php deleted file mode 100644 index bb0f2c3..0000000 --- a/Model/Response/ResponseResult.php +++ /dev/null @@ -1,7 +0,0 @@ -extraData = []; - $this->additionalData = []; - } -} diff --git a/Model/Response/ResponseShipmentSave.php b/Model/Response/ResponseShipmentSave.php deleted file mode 100644 index 0dd8d67..0000000 --- a/Model/Response/ResponseShipmentSave.php +++ /dev/null @@ -1,38 +0,0 @@ -") - */ - public $extraData; - - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata - ->addPropertyConstraint('shipmentId', new Assert\NotBlank()); - } -} diff --git a/Model/Response/ResponseSuccessful.php b/Model/Response/ResponseSuccessful.php deleted file mode 100644 index d45bb2f..0000000 --- a/Model/Response/ResponseSuccessful.php +++ /dev/null @@ -1,26 +0,0 @@ -value = $value; - $this->label = $label; - $this->description = $description; - } -} diff --git a/Model/ResponseAutocompleteSuccessful.php b/Model/ResponseAutocompleteSuccessful.php deleted file mode 100644 index 75b38ae..0000000 --- a/Model/ResponseAutocompleteSuccessful.php +++ /dev/null @@ -1,26 +0,0 @@ -") - */ - public $result; -} diff --git a/Model/ResponseCalculate.php b/Model/ResponseCalculate.php deleted file mode 100644 index dffb547..0000000 --- a/Model/ResponseCalculate.php +++ /dev/null @@ -1,137 +0,0 @@ -") - */ - public $pickuppointList; - - public function __construct() - { - $this->extraData = []; - } -} diff --git a/Model/ResponseCalculateSuccessful.php b/Model/ResponseCalculateSuccessful.php deleted file mode 100644 index 3247e35..0000000 --- a/Model/ResponseCalculateSuccessful.php +++ /dev/null @@ -1,17 +0,0 @@ -") - */ - public $result; -} diff --git a/Model/ResponseLoadDeliveryData.php b/Model/ResponseLoadDeliveryData.php deleted file mode 100644 index e3654a5..0000000 --- a/Model/ResponseLoadDeliveryData.php +++ /dev/null @@ -1,148 +0,0 @@ -") - */ - public $shipmentDate; - - /** - * Дата доставки - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryDate") - * @Serializer\Type("DateTime<'Y-m-d'>") - */ - public $deliveryDate; - - /** - * Время доставки - * - * @var RetailCrm\DeliveryModuleBundle\Model\DeliveryTime - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryTime") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $deliveryTime; - - /** - * Код тарифа - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("tariff") - * @Serializer\Type("string") - */ - public $tariff; - - /** - * Наименование тарифа - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("tariffName") - * @Serializer\Type("string") - */ - public $tariffName; - - /** - * Плательщик за доставку - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("payerType") - * @Serializer\Type("string") - */ - public $payerType; - - /** - * Текущий статус достаквки - * - * @var StatusInfo - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("status") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\StatusInfo") - */ - public $status; - - /** - * Дополнительные данные доставки - * - * @var string - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; - - /** - * Адрес отгрузки - * - * @var DeliveryAddress - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("shipmentAddress") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $shipmentAddress; - - /** - * Адрес доставки - * - * @var DeliveryAddress - * - * @Serializer\Groups({"response"}) - * @Serializer\SerializedName("deliveryAddress") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryAddress") - */ - public $deliveryAddress; - - public $additionalData; - - public function __construct() - { - $this->extraData = []; - $this->additionalData = []; - } -} diff --git a/Model/ResponseResult.php b/Model/ResponseResult.php deleted file mode 100644 index bb0f2c3..0000000 --- a/Model/ResponseResult.php +++ /dev/null @@ -1,7 +0,0 @@ -extraData = []; - $this->additionalData = []; - } -} diff --git a/Model/ResponseShipmentPointListSuccessful.php b/Model/ResponseShipmentPointListSuccessful.php deleted file mode 100644 index 2bd76c4..0000000 --- a/Model/ResponseShipmentPointListSuccessful.php +++ /dev/null @@ -1,17 +0,0 @@ -") - */ - public $result; -} diff --git a/Model/ResponseShipmentSave.php b/Model/ResponseShipmentSave.php deleted file mode 100644 index 0dd8d67..0000000 --- a/Model/ResponseShipmentSave.php +++ /dev/null @@ -1,38 +0,0 @@ -") - */ - public $extraData; - - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata - ->addPropertyConstraint('shipmentId', new Assert\NotBlank()); - } -} diff --git a/Model/ResponseSuccessful.php b/Model/ResponseSuccessful.php deleted file mode 100644 index f00460a..0000000 --- a/Model/ResponseSuccessful.php +++ /dev/null @@ -1,17 +0,0 @@ -") - */ - public $shipmentDate; - - /** - * Дата доставки. - * - * @var \DateTime - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("deliveryDate") - * @Serializer\Type("DateTime<'Y-m-d'>") - */ - public $deliveryDate; - - /** - * Время доставки ("custom" не ипользуется). - * - * @var \RetailCrm\DeliveryModuleBundle\Model\DeliveryTime - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("deliveryTime") - * @Serializer\Type("RetailCrm\DeliveryModuleBundle\Model\DeliveryTime") - */ - public $deliveryTime; - - /** - * Дополнительные данные доставки. - * - * @var array - * - * @Serializer\Groups({"get"}) - * @Serializer\SerializedName("extraData") - * @Serializer\Type("array") - */ - public $extraData; -} diff --git a/Model/Settings.php b/Model/Settings.php deleted file mode 100644 index b97c484..0000000 --- a/Model/Settings.php +++ /dev/null @@ -1,96 +0,0 @@ -") - */ - public $paymentTypes; - - /** - * @var array|ShipmentPoint[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("shipmentPoints") - * @Serializer\Type("array") - */ - public $shipmentPoints; - - /** - * @var array|Status[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("statuses") - * @Serializer\Type("array") - */ - public $statuses; - - /** - * @var array|ExtraData[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("deliveryExtraData") - * @Serializer\Type("array") - */ - public $deliveryExtraData; - - /** - * @var array|ExtraData[] - * - * @Serializer\Groups({"set", "get"}) - * @Serializer\SerializedName("shipmentExtraData") - * @Serializer\Type("array") - */ - public $shipmentExtraData; -} diff --git a/Model/Settings/PaymentType.php b/Model/Settings/PaymentType.php deleted file mode 100644 index 3b2ff3f..0000000 --- a/Model/Settings/PaymentType.php +++ /dev/null @@ -1,35 +0,0 @@ -") - */ - public $packages; -} diff --git a/Model/Status.php b/Model/Status.php deleted file mode 100644 index e3ec892..0000000 --- a/Model/Status.php +++ /dev/null @@ -1,48 +0,0 @@ -code = $code; - $this->name = $name; - $this->isEditable = $isEditable; - } -} diff --git a/Model/StatusInfo.php b/Model/StatusInfo.php deleted file mode 100644 index 9c2b246..0000000 --- a/Model/StatusInfo.php +++ /dev/null @@ -1,41 +0,0 @@ -") - */ - public $updatedAt; - - /** - * Комментарий к статусу - * - * @var string - * - * @Serializer\Groups({"get", "response"}) - * @Serializer\SerializedName("comment") - * @Serializer\Type("string") - */ - public $comment; -} diff --git a/Model/Store.php b/Model/Store.php deleted file mode 100644 index 87c2e96..0000000 --- a/Model/Store.php +++ /dev/null @@ -1,26 +0,0 @@ -extraData[$fieldCode])) { - return null; - } else { - return $this->extraData[$fieldCode]; - } - } -} diff --git a/Model/Unit.php b/Model/Unit.php deleted file mode 100644 index 4f45d32..0000000 --- a/Model/Unit.php +++ /dev/null @@ -1,33 +0,0 @@ -= 7.3 +* php-curl +* php-json +* php-zip +* Symfony >= 5.1 + +Documentation +------------- + +Documentation for this bundle is stored under `doc` in this repository. + +[Read the Documentation](doc/index.md) + +License +------- + +This bundle is released under the [MIT license](LICENSE) diff --git a/Resources/config/routing.yaml b/Resources/config/routing.yaml deleted file mode 100644 index 968e043..0000000 --- a/Resources/config/routing.yaml +++ /dev/null @@ -1,51 +0,0 @@ -############# -# API -############ -retailcrm_delivery_module_api_base: - path: /api - defaults: { _controller: retailcrm.delivery_module.api_controller::index } - -retailcrm_delivery_module_api_activity: - path: /api/activity - defaults: { _controller: retailcrm.delivery_module.api_controller::activity } - methods: POST - -retailcrm_delivery_module_api_calculate: - path: /api/calculate - defaults: { _controller: retailcrm.delivery_module.api_controller::calculate } - methods: POST - -retailcrm_delivery_module_api_save: - path: /api/save - defaults: { _controller: retailcrm.delivery_module.api_controller::save } - methods: POST - -retailcrm_delivery_module_api_get: - path: /api/get - defaults: { _controller: retailcrm.delivery_module.api_controller::getDeliveryOrder } - methods: GET - -retailcrm_delivery_module_api_delete: - path: /api/delete - defaults: { _controller: retailcrm.delivery_module.api_controller::delete } - methods: POST - -retailcrm_delivery_module_api_shipment_point_list: - path: /api/shipment-point-list - defaults: { _controller: retailcrm.delivery_module.api_controller::shipmentPointList } - methods: GET - -retailcrm_delivery_module_api_print: - path: /api/print - defaults: { _controller: retailcrm.delivery_module.api_controller::print } - methods: POST - -retailcrm_delivery_module_api_shipment_save: - path: /api/shipment-save - defaults: { _controller: retailcrm.delivery_module.api_controller::shipmentSave } - methods: POST - -retailcrm_delivery_module_api_shipment_delete: - path: /api/shipment-delete - defaults: { _controller: retailcrm.delivery_module.api_controller::shipmentDelete } - methods: POST \ No newline at end of file diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml deleted file mode 100644 index bf0df93..0000000 --- a/Resources/config/services.yaml +++ /dev/null @@ -1,58 +0,0 @@ -parameters: - retailcrm.delivery_module.api_controller: RetailCrm\DeliveryModuleBundle\Controller\ApiController - -services: - _defaults: - public: false - autowire: true - autoconfigure: true - - retailcrm.delivery_module.api_controller: - public: true - class: '%retailcrm.delivery_module.api_controller%' - - RetailCrm\DeliveryModuleBundle\Service\RetailCrmClientFactoryInterface: - class: RetailCrm\DeliveryModuleBundle\Service\RetailCrmClientFactory - - RetailCrm\DeliveryModuleBundle\Service\ModuleManagerInterface: - public: true - class: '%retailcrm.delivery_module.module_manager.class%' - arguments: - - '%retailcrm.delivery_module.configuration%' - - '@RetailCrm\DeliveryModuleBundle\Service\RetailCrmClientFactoryInterface' - - '@RetailCrm\DeliveryModuleBundle\Service\DeliveryOrderManager' - - '@jms_serializer' - - '@translator' - - '@router' - - RetailCrm\DeliveryModuleBundle\Service\AccountManager: - class: 'RetailCrm\DeliveryModuleBundle\Service\AccountManager' - arguments: - - '%retailcrm.delivery_module.account.class%' - - '@doctrine.orm.entity_manager' - - RetailCrm\DeliveryModuleBundle\Service\DeliveryOrderManager: - class: 'RetailCrm\DeliveryModuleBundle\Service\DeliveryOrderManager' - arguments: - - '%retailcrm.delivery_module.delivery_order.class%' - - '@doctrine.orm.entity_manager' - - RetailCrm\DeliveryModuleBundle\Command\StatusesCommand: - class: RetailCrm\DeliveryModuleBundle\Command\StatusesCommand - tags: ['console.command'] - - RetailCrm\DeliveryModuleBundle\Command\UpdateModuleCommand: - class: RetailCrm\DeliveryModuleBundle\Command\UpdateModuleCommand - tags: ['console.command'] - - RetailCrm\DeliveryModuleBundle\Controller\: - resource: '../../Controller' - public: true - tags: ['controller.service_arguments'] - - RetailCrm\DeliveryModuleBundle\EventSubscriber\SerializeListener: - tags: - - { name: jms_serializer.event_subscriber } - - RetailCrm\DeliveryModuleBundle\EventSubscriber\ClientIdSubscriber: - class: RetailCrm\DeliveryModuleBundle\EventSubscriber\ClientIdSubscriber diff --git a/Resources/translations/messages.en.yml b/Resources/translations/messages.en.yml deleted file mode 100644 index 816c98e..0000000 --- a/Resources/translations/messages.en.yml +++ /dev/null @@ -1,67 +0,0 @@ -header: - configureConnection: Configuration of connection with system - additionalSettings: Additional settings - adminParcelEdit: 'Edit tracking %trackId%' - adminParcelCreate: Create tracking - listConnection: Current connection - listTracking: Current tracking - serviceConnect: 'Service connection %delivery%' - adminConnectionEdit: 'Edit connection data: %uid%' - adminConnectionCreate: New connection - -button: - activate: Activate - save: Save - add: Add - listConnection: List of connections - listTracking: Parcel tracking - addTracking: Add tracking - updateConfiguration: Update the configuration - -label: - crmUrl: System address - crmKey: System API key - debug: Debug mode - isActive: Integration is active - isFreeze: Integration is freeze - language: Language - account_number: Account number - token: API token - isInProduction: Use production server - id: ID - connection: ID connection - orderId: ID order - trackId: ID tracking - isClosed: Tracking completed - connectionId: Connection ID - -select_value: - sender: Sender - receiver: Receiver - -error: - connect.failed: Data to connect to %delivery% or System API key is entered incorrectly, try again.

Re-enter data - unknown_country: Unknown country - unknown_shipping_country: Unknown shipping country - shipping_country_required: Shipping country must not be empty - shipper_postal_code_required: Shipper postal code required - delivery_postal_code_required: Delivery postal code required - manager_phone_required: Manager`s phone required - dhl: - ready_time_required: Ready time required - dury_payment_type_for_receiver: Duty Payment Type cant be sender for payer type receiver - receiver_account_number_required: Receiver account number required - -plate: - label: Label - -day_short: - monday: Mon - tuesday: Tue - wednesday: Wed - thursday: Thu - friday: Fri - saturday: Sat - sunday: Sun - -pagination.items.name: elements diff --git a/Resources/translations/messages.es.yml b/Resources/translations/messages.es.yml deleted file mode 100644 index a66d5b5..0000000 --- a/Resources/translations/messages.es.yml +++ /dev/null @@ -1,67 +0,0 @@ -header: - configureConnection: Configuración de la conexión del sistema - additionalSettings: Configuración avanzada - adminParcelEdit: 'Editar seguimiento %trackId%' - adminParcelCreate: Crear seguimiento - listConnection: Conexiones actuales - listTracking: Seguimiento actual - serviceConnect: 'Conexión de Servicio %delivery%' - adminConnectionEdit: 'Editar datos del cliente: %uid%' - adminConnectionCreate: Nueva conexión - -button: - activate: Activar - save: Guardar - add: Añadir - listConnection: A la lista de conexiones - listTracking: Seguimiento de paquetes - addTracking: Añadir seguimiento - updateConfiguration: Actualizar configuración - -label: - crmUrl: Dirección del Sistema - crmKey: Clave API del sistema - debug: Modo de depuración - isActive: Conexión activa - isFreeze: Conexión congelada - language: Idioma - account_number: Número de cuenta - token: token API - isInProduction: Utilizar servidor normal - id: ID - connection: ID de conexión - orderId: ID de pedido - trackId: ID de seguimiento - isClosed: Seguimiento completado - connectionId: Id de conexión - -select_value: - sender: Remitente - receiver: Receptor - -error: - connect.failed: Datos para conectarse a %delivery% o la clave API del sistema son incorrectos, intente de nuevo.

Introducir datos de nuevo - unknown_country: País desconocido - unknown_shipping_country: Páis de envio desconocido - shipping_country_required: Debe especificar el país de envío - shipper_postal_code_required: Debe especificar el código Postal del remitente - delivery_postal_code_required: Debe especificar el código Postal del destinatario - manager_phone_required: Debe especificar el Teléfono del mánager - dhl: - ready_time_required: Debe especificar la fecha del envío - dury_payment_type_for_receiver: El remitente no puede pagar los cargos adicionales si el que paga es el destinatario - receiver_account_number_required: Debe especificar el número de cuenta del destinatario - -plate: - label: Etiqueta - -day_short: - monday: Lu - tuesday: Ma - wednesday: Mie - thursday: Ju - friday: Vi - saturday: Sa - sunday: Do - -pagination.items.name: elementos diff --git a/Resources/translations/messages.ru.yml b/Resources/translations/messages.ru.yml deleted file mode 100644 index cfed3d8..0000000 --- a/Resources/translations/messages.ru.yml +++ /dev/null @@ -1,67 +0,0 @@ -header: - configureConnection: Настройка подключения к системе - additionalSettings: Дополнительные настройки - adminParcelEdit: 'Редактировать отслеживание %trackId%' - adminParcelCreate: Создать отслеживание - listConnection: Текущие подключения - listTracking: Текущие отслеживания - serviceConnect: 'Подключение сервиса %delivery%' - adminConnectionEdit: 'Редактировать данные клиента: %uid%' - adminConnectionCreate: Новое подключение - -button: - activate: Активировать - save: Сохранить - add: Добавить - listConnection: К списку подключений - listTracking: Отслеживание посылок - addTracking: Добавить отслеживание - updateConfiguration: Обновить конфигурацию - -label: - crmUrl: Адрес системы - crmKey: API ключ системы - debug: Режим отладки - isActive: Подключение активно - isFreeze: Подключение заморожено - language: Язык - account_number: Номер аккаунта - token: API токен - isInProduction: Использовать боевой сервер - id: ID - connection: ID подключения - orderId: ID заказа - trackId: ID отслеживания - isClosed: Отслеживание завершено - connectionId: Идентификатор подключения - -select_value: - sender: Отправитель - receiver: Получатель - -error: - connect.failed: Данные для подключения к %delivery% или API ключ системы введены неверно, попробуйте еще раз.

Ввести данные заново - unknown_country: Неизвестная страна - unknown_shipping_country: Неизвестная страна отправки - shipping_country_required: Необходимо указать страну отправки - shipper_postal_code_required: Необходимо указать почтовый индекс отправителя - delivery_postal_code_required: Необходимо указать почтовый индекс получателя - manager_phone_required: Необходимо указать телефон менеджера - dhl: - ready_time_required: Необходимо указать время отгрузки - dury_payment_type_for_receiver: Отправитель не может быть плательщиком за дополнительные сборы если тип плательщика получатель - receiver_account_number_required: Необходимо указать номер аккаунта получателя - -plate: - label: Наклейка - -day_short: - monday: Пн - tuesday: Вт - wednesday: Ср - thursday: Чт - friday: Пт - saturday: Сб - sunday: Вс - -pagination.items.name: элементов diff --git a/Resources/translations/validators.en.yaml b/Resources/translations/validators.en.yaml deleted file mode 100644 index 0c3f989..0000000 --- a/Resources/translations/validators.en.yaml +++ /dev/null @@ -1,11 +0,0 @@ -integration_module_access: - server_unreachable_exception: 'Failed to connect to the integration server' - module_access_exception: 'Invalid login or password' - -retailcrm_access: - access_denied: 'Access to the method "%method%" is denied' - curl_exception: 'Failed to connect to API' - service_unavailable: 'Service is temporarily unavailable' - invalid_json: 'API returned a response in an unsupported format' - requires_https: 'API address must start with https' - wrong_api_key: 'Invalid API key' diff --git a/Resources/translations/validators.es.yaml b/Resources/translations/validators.es.yaml deleted file mode 100644 index ed0cf55..0000000 --- a/Resources/translations/validators.es.yaml +++ /dev/null @@ -1,11 +0,0 @@ -integration_module_access: - server_unreachable_exception: 'No se ha podido conectar al servidor de integración' - module_access_exception: 'El Usuario o la contraseña no es correcta' - -retailcrm_access: - access_denied: 'Acceso al método "%method%" está prohibido' - curl_exception: 'No se ha podido conectar al API' - service_unavailable: 'Servicio no está disponible temporalmente' - invalid_json: 'API ha devuelto la respuesta en el formato no soportado' - requires_https: 'Dirección del API tiene que comenzar con https' - wrong_api_key: 'El API-key no es correcto' diff --git a/Resources/translations/validators.ru.yaml b/Resources/translations/validators.ru.yaml deleted file mode 100644 index 432289b..0000000 --- a/Resources/translations/validators.ru.yaml +++ /dev/null @@ -1,11 +0,0 @@ -integration_module_access: - server_unreachable_exception: 'Не удалось подключиться к интеграционному серверу' - module_access_exception: 'Не верный логин или пароль' - -retailcrm_access: - access_denied: 'Доступ к методу "%method%" запрещен' - curl_exception: 'Не удалось подключиться к API' - service_unavailable: 'Сервис временно недоступен' - invalid_json: 'API вернуло ответ в неподдерживаемом формате' - requires_https: 'Адрес API должен начинаться с https' - wrong_api_key: 'Неверный API-ключ' diff --git a/Service/AccountManager.php b/Service/AccountManager.php deleted file mode 100644 index d9da37c..0000000 --- a/Service/AccountManager.php +++ /dev/null @@ -1,55 +0,0 @@ -class = $accountClass; - $this->entityManager = $entityManager; - } - - public function getClass(): string - { - return $this->getRepository()->getClassName(); - } - - public function create(): Account - { - $class = $this->getClass(); - - return new $class(); - } - - public function find(string $id): ?Account - { - return $this->getRepository()->find($id); - } - - public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array - { - return $this->getRepository()->findBy($criteria, $orderBy, $limit, $offset); - } - - public function findOneBy(array $criteria, array $orderBy = null): ?Account - { - return $this->getRepository()->findOneBy($criteria, $orderBy); - } - - public function flush(): void - { - $this->entityManager->flush(); - } - - public function getRepository(): ObjectRepository - { - return $this->entityManager->getRepository($this->class); - } -} diff --git a/Service/DeliveryOrderManager.php b/Service/DeliveryOrderManager.php deleted file mode 100644 index e68390f..0000000 --- a/Service/DeliveryOrderManager.php +++ /dev/null @@ -1,65 +0,0 @@ -class = $deliveryOrderClass; - $this->entityManager = $entityManager; - } - - public function getClass(): string - { - return $this->class; - } - - public function create(): DeliveryOrder - { - $class = $this->getClass(); - - return new $class(); - } - - public function find(int $id): ?DeliveryOrder - { - return $this->getRepository()->find($id); - } - - public function findBy(array $criteria): array - { - return $this->getRepository()->findBy($criteria); - } - - public function findOneBy(array $criteria): ?DeliveryOrder - { - return $this->getRepository()->findOneBy($criteria); - } - - public function persist(object $entity): void - { - $this->entityManager->persist($entity); - } - - public function remove(object $entity): void - { - $this->entityManager->remove($entity); - } - - public function flush(): void - { - $this->entityManager->flush(); - } - - public function getRepository(): ObjectRepository - { - return $this->entityManager->getRepository($this->class); - } -} diff --git a/Service/ModuleManager.php b/Service/ModuleManager.php deleted file mode 100644 index d20f512..0000000 --- a/Service/ModuleManager.php +++ /dev/null @@ -1,355 +0,0 @@ -integrationCode = $moduleParameters['integration_code']; - $this->moduleParameters = $moduleParameters; - $this->retailCrmClientFactory = $retailCrmClientFactory; - $this->deliveryManager = $deliveryManager; - $this->jmsSerializer = $jmsSerializer; - $this->translator = $translator; - $this->router = $router; - $this->pinbaService = new PinbaService(); - } - - public function getAccountCode(): string - { - if (null === $this->account) { - throw new \LogicException('Account is not selected'); - } - - return sprintf('%s-%s', $this->integrationCode, $this->account->getId()); - } - - public function getAccount(): ?Account - { - return $this->account; - } - - public function setAccount(Account $account): ModuleManagerInterface - { - $this->account = $account; - - if ($this->account && $this->account->getLanguage() && $this->translator) { - $this->translator->setLocale($this->account->getLanguage()); - } - - $this->retailCrmClient = $this->retailCrmClientFactory->createRetailCrmClient($this->account); - - return $this; - } - - public function updateModuleConfiguration(): bool - { - if (null === $this->account) { - throw new \LogicException('Account is not selected'); - } - - $integrationModule = $this->buildIntegrationModule(); - $integrationModule = $this->jmsSerializer - ->serialize( - $integrationModule, - 'json', - SerializationContext::create()->setGroups(['get', 'request'])->setSerializeNull(true) - ); - - $client = $this->retailCrmClient; - $response = $this->pinbaService->timerHandler( - [ - 'api' => 'retailCrm', - 'method' => 'integrationModulesEdit', - ], - static function () use ($client, $integrationModule) { - return $client->request->integrationModulesEdit( - json_decode($integrationModule, true) - ); - } - ); - - if ($response['success'] ?? false) { - return true; - } else { - if ($this->logger) { - $errorMsg = $response['error_msg'] ?? ''; - $this->logger->warning("Failed to update module configuration[account={$this->getAccount()->getCrmUrl()}]:{$errorMsg}"); - } - - return false; - } - } - - protected function buildIntegrationModule(): IntegrationModule - { - $integrationModule = new IntegrationModule(); - - $integrationModule->code = $this->getAccountCode(); - $integrationModule->integrationCode = $this->integrationCode; - $integrationModule->active = $this->account->isActive(); - $integrationModule->name = $this->moduleParameters['locales'][$this->translator->getLocale()]['name']; - $integrationModule->logo = $this->moduleParameters['locales'][$this->translator->getLocale()]['logo']; - $integrationModule->clientId = $this->account->getClientId(); - $integrationModule->availableCountries = $this->moduleParameters['countries']; - $integrationModule->actions = [ - 'activity' => 'activity', - ]; - - $integrationModule->baseUrl = $this->router->generate( - 'retailcrm_delivery_module_api_base', - [], - UrlGeneratorInterface::ABSOLUTE_URL - ); - $integrationModule->accountUrl = $this->getAccountUrl(); - - $integrationModule->integrations = ['delivery' => $this->doBuildConfiguration()]; - - return $integrationModule; - } - - abstract protected function doBuildConfiguration(): Configuration; - - abstract protected function getAccountUrl(): string; - - public function calculateDelivery(RequestCalculate $data): array - { - throw new \LogicException('Method should be implemented'); - } - - public function saveDelivery(RequestSave $data, DeliveryOrder $delivery = null): ResponseSave - { - throw new \LogicException('Method should be implemented'); - } - - public function getDelivery(string $externalId): ResponseLoadDeliveryData - { - throw new \LogicException('Method should be implemented'); - } - - public function deleteDelivery(RequestDelete $request, DeliveryOrder $delivery): bool - { - throw new \LogicException('Method should be implemented'); - } - - /** - * @return \RetailCrm\DeliveryModuleBundle\Model\Terminal[] - */ - public function shipmentPointList(RequestShipmentPointList $request): array - { - throw new \LogicException('Method should be implemented'); - } - - public function saveShipment(RequestShipmentSave $data): ResponseShipmentSave - { - throw new \LogicException('Method should be implemented'); - } - - public function deleteShipment(RequestShipmentDelete $request): bool - { - throw new \LogicException('Method should be implemented'); - } - - public function printDocument(RequestPrint $request) - { - $deliveries = $this->deliveryManager->findBy([ - 'account' => $this->account, - 'externalId' => $request->deliveryIds, - ]); - - if (empty($deliveries)) { - throw new NotFoundException('Deliveries not found'); - } - - return $this->doPrint($request->type, $deliveries); - } - - protected function doPrint(string $documentType, array $deliveries): array - { - throw new \LogicException('Method should be implemented'); - } - - public function updateStatuses(): int - { - if (null === $this->account) { - throw new \LogicException('Account is not selected'); - } - - $deliveryQuery = $this->deliveryManager->createQueryBuilder('delivery') - ->select('delivery') - ->andWhere('delivery.account = :account') - ->andWhere('delivery.id >= :lastId') - ->andWhere('delivery.ended = FALSE') - ->orderBy('delivery.id ASC') - ->createQuery() - ->setMaxResults(static::STATUS_UPDATE_LIMIT) - ->setAccount($this->account) - ; - - $count = 0; - $lastId = 0; - while (true) { - $deliveryQuery->setParameter('lastId', $lastId); - $deliveries = $deliveryQuery->getResult(); - if (empty($deliveries)) { - break; - } - - foreach ($deliveries as $delivery) { - if ($delivery->getId() > $lastId) { - $lastId = $delivery->getId(); - } - } - - $deliveriesHistory = $this->doUpdateStatuses($deliveries); - if (!empty($deliveriesHistory)) { - $this->updateRetailCrmOrderStatuses($deliveriesHistory); - } - $count += count($deliveriesHistory); - $this->deliveryManager->flush(); - } - - return $count; - } - - public function getRetailCrmClient(): ApiClient - { - if (null === $this->retailCrmClient) { - throw new \LogicException('Account is not selected'); - } - - return $this->retailCrmClient; - } - - /** - * Получение актуальных статусов доставки от службы доставки. - * - * @param \RetailCrm\DeliveryModuleBundle\Entity\Parcel[] $deliveries - * - * @return \RetailCrm\DeliveryModuleBundle\Model\RequestStatusUpdateItem[] - */ - protected function doUpdateStatuses(array $deliveries): array - { - throw new \LogicException('Method should be implemented'); - } - - /** - * Обновление статусов в CRM. - * - * @param \RetailCrm\DeliveryModuleBundle\Model\RequestStatusUpdateItem[] $deliveriesHistory - * - * @throws \Exception - */ - protected function updateRetailCrmOrderStatuses(array $deliveriesHistory): void - { - if (count($deliveriesHistory) > 100) { - $parts = array_chunk($deliveriesHistory, 100); - } else { - $parts = [$deliveriesHistory]; - } - - foreach ($parts as $part) { - $request = $this->jmsSerializer - ->serialize($part, 'json', SerializationContext::create()->setGroups(['get', 'request'])); - - $client = $this->retailCrmClient; - $moduleCode = $this->getAccountCode(); - $response = $this->pinbaService->timerHandler( - [ - 'api' => 'retailCrm', - 'method' => 'deliveryTracking', - ], - static function () use ($client, $moduleCode, $request) { - return $client->request->deliveryTracking( - $moduleCode, - json_decode($request, true) - ); - } - ); - } - } -} diff --git a/Service/ModuleManagerInterface.php b/Service/ModuleManagerInterface.php deleted file mode 100644 index b168426..0000000 --- a/Service/ModuleManagerInterface.php +++ /dev/null @@ -1,52 +0,0 @@ -getCrmUrl())) { - throw new \LogicException('Crm url is empty'); - } - - if (empty($account->getCrmApiKey())) { - throw new \LogicException('Crm apiKey is empty'); - } - - $retailCrmClient = new ApiClient( - $account->getCrmUrl(), - $account->getCrmApiKey(), - ApiClient::V5 - ); - if ($logger) { - $retailCrmClient->setLogger($logger); - } - - return $retailCrmClient; - } -} diff --git a/Service/RetailCrmClientFactoryInterface.php b/Service/RetailCrmClientFactoryInterface.php deleted file mode 100644 index 8c0d156..0000000 --- a/Service/RetailCrmClientFactoryInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -moduleManager = $moduleManager; - } - - public function validate($account, Constraint $constraint) - { - if (!($constraint instanceof IntegrationModuleAccess)) { - throw new UnexpectedTypeException($constraint, IntegrationModuleAccess::class); - } - - try { - $this->moduleManager->checkAccess(); - } catch (ServerUnreachableException $e) { - $this->context - ->buildViolation('integration_module_access.server_unreachable_exception') - ->atPath($constraint->path) - ->addViolation() - ; - } catch (AbstractModuleException $e) { - $this->context - ->buildViolation('integration_module_access.module_access_exception') - ->atPath($constraint->path) - ->addViolation() - ; - } - } -} diff --git a/Validator/Constraints/RetailCrmAccess.php b/Validator/Constraints/RetailCrmAccess.php deleted file mode 100644 index 74811f9..0000000 --- a/Validator/Constraints/RetailCrmAccess.php +++ /dev/null @@ -1,23 +0,0 @@ -moduleManager = $moduleManager; - } - - public function validate($account, Constraint $constraint) - { - if (!($constraint instanceof RetailCrmAccess)) { - throw new UnexpectedTypeException($constraint, RetailCrmAccess::class); - } - - $client = $this->moduleManager->getRetailCrmClient(); - - try { - $response = $client->request->credentials(); - if (!$response->isSuccessful()) { - throw new RetailCrmApiException($response->offsetGet('errorMsg')); - } - - $credentials = $response->offsetGet('credentials'); - foreach ($constraint->requiredApiMethods as $method) { - if (!in_array($method, $credentials)) { - $this->context - ->buildViolation('retailcrm_access.access_denied', ['%method%' => $method]) - ->atPath('crmApiKey') - ->addViolation() - ; - } - } - } catch (CurlException $e) { - $this->context - ->buildViolation('retailcrm_access.curl_exception') - ->atPath('crmUrl') - ->addViolation() - ; - } catch (LimitException $e) { - $this->context - ->buildViolation('retailcrm_access.service_unavailable') - ->atPath('crmUrl') - ->addViolation() - ; - } catch (InvalidJsonException $e) { - $this->context - ->buildViolation('retailcrm_access.invalid_json') - ->atPath('crmUrl') - ->addViolation() - ; - } catch (\InvalidArgumentException $e) { - $this->context - ->buildViolation('retailcrm_access.requires_https') - ->atPath('crmUrl') - ->addViolation() - ; - } catch (RetailCrmApiException $e) { - $this->context - ->buildViolation('retailcrm_access.wrong_api_key') - ->atPath('crmUrl') - ->addViolation() - ; - } - } -} diff --git a/composer.json b/composer.json index 77b9f3a..1121ac6 100644 --- a/composer.json +++ b/composer.json @@ -1,34 +1,49 @@ { "name": "retailcrm/delivery-module-bundle", - "description": "Delivery module skeleton for RetailCrm", - "license": "MIT", "type": "symfony-bundle", - "autoload": { - "psr-4": { - "RetailCrm\\DeliveryModuleBundle\\": "" + "description": "Delivery module skeleton for RetailCRM", + "license": "MIT", + "authors": [ + { + "name": "RetailCRM", + "email": "support@retailcrm.ru" } - }, - "config": { - "sort-packages": true - }, + ], "require": { "php": "^7.3", "ext-json": "*", "ext-zip": "*", - "doctrine/orm": "^2.7", - "jms/serializer": "^3.4", - "jms/serializer-bundle": "^3.5", - "ramsey/uuid": "^3.9", - "ramsey/uuid-doctrine": "^1.5", - "retailcrm/api-client-php": "^5.0.0", - "symfony/framework-bundle": "^3.4|^4.0|^5.1", + "guzzlehttp/guzzle": "^7.0", + "psr/log": "^1.1", + "symfony/framework-bundle": "^5.1", "symfony/lock": "^5.1", + "symfony/polyfill-uuid": "^1.20", "symfony/routing": "^5.1", + "symfony/serializer": "^5.1", "symfony/translation": "^5.1", "symfony/validator": "^5.1" }, "require-dev": { - "doctrine/doctrine-fixtures-bundle": "^3.3", - "friendsofphp/php-cs-fixer": "^2.0" + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^9.4", + "vimeo/psalm": "^4.1" + }, + "autoload": { + "psr-4": { + "RetailCrm\\DeliveryModuleBundle\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "RetailCrm\\DeliveryModuleBundle\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "vendor/bin/phpunit", + "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml" + }, + "config": { + "sort-packages": true } } diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..a98226c --- /dev/null +++ b/doc/index.md @@ -0,0 +1,284 @@ +Getting started with RetailCrmDeliveryModuleBundle +================================================== + +## Prerequisites + +This version of the bundle requires Symfony 5.1. + +#### Translations + +If you wish to use default texts provided in this bundle, you have to make sure you have translator enabled in your config: + +``` yaml + # config/packages/translation.yaml + + framework: + translator: { fallback: en } +``` + +For more information about translations, check [Symfony documentation](http://symfony.com/doc/current/book/translation.html). + + +## Installation + +Installation is a 7 steps process: + +1. Download RetailCrmDeliveryModuleBundle +2. Enable the Bundle +3. Create your model class +4. Create your integration module factory +5. Create your delivery service +6. Create your tracker +7. Configure the RetailCrmDeliveryModuleBundle + + +### Step 1: Install RetailCrmDeliveryModuleBundle + +The preferred way to install this bundle is to rely on [Composer](http://getcomposer.org). + +Just check on [Packagist](http://packagist.org/packages/retailcrm/delivery-module-bundle) the version you want to install (in the following example, we used "dev-master") and add it to your `composer.json`: + +``` json +{ + "require": { + // ... + "retailcrm/delivery-module-bundle": "dev-master" + } +} +``` + + +### Step 2: Enable the bundle + +Finally, enable the bundle in the kernel: + +``` php + ['all' => true], +]; +``` + + +### Step 3: Create model classes + +This bundle needs to persist some classes to a database: + +- `Account` +- `Delivery` + +Your first job, then, is to create these classes for your application. +These classes can look and act however you want: add any properties or methods you find useful. + +These classes have just a few requirements: + +1. They must extend one of the base classes from the bundle +2. They must have an `id` field + +In the following sections, you'll see examples of how your classes should +look, depending on how you're storing your data. + +Your classes can live inside any bundle in your application. + +**Warning:** + +> If you override the __construct() method in your classes, be sure to call parent::__construct(), as the base class depends on this to initialize some fields. + + +#### Doctrine ORM classes + +If you're persisting your data via the Doctrine ORM, then your classes should live in the `Entity` namespace of your bundle and look like this to start: + +``` php +urlGenerator = $urlGenerator; + + parent::__construct($configuration); + } + + protected function getBaseUrl(): string + { + return $this->urlGenerator->generate('base_url', [], UrlGeneratorInterface::ABSOLUTE_URL); + } + + protected function getAccountUrl(): string + { + return $this->urlGenerator->generate('account_url', [], UrlGeneratorInterface::ABSOLUTE_URL); + } + + protected function getDeliveryConfiguration(Account $account): array + { + $configuration = []; + + $configuration['description'] = sprintf("Account %s[%s] configuration", $account->getUrl(), $account->getClientId()); + + $configuration['actions'] = [ + 'calculate' => 'delivery/calculate', + 'save' => 'delivery/save', + 'get' => 'delivery/get', + 'delete' => 'delivery/delete', + 'shipmentPointList' => 'delivery/shipment-point-list', + 'shipmentSave' => 'delivery/shipment-save', + 'shipmentDelete' => 'delivery/shipment-delete', + 'tariffList' => 'delivery/tariff-list', + 'print' => 'delivery/print', + ]; + + $configuration['payerType'] = [ + 'receiver', + 'sender', + ]; + + // TODO: add your logic + + return $configuration; + } +} +``` + + +### Step 5: Create your delivery service +The DeliveryService class responsibility is to provide a gateway for delivery API. + +Custom delivery service must implement: +- `RetailCrm\DeliveryModuleBundle\DeliveryService\ProcessableDeliveryServiceInterface` if delivery provides create/delete processing +- `RetailCrm\DeliveryModuleBundle\DeliveryService\TrackableDeliveryServiceInterface` if delivery provides tracking service + + +### Step 6: Create your tracker +The Tracker class responsibility is to provide a tracking service. + +You can use default tracker `retail_crm_delivery_module.tracker.default` or crete your custom. + +Tracker is class must implement the `RetailCrm\DeliveryModuleBundle\Tracking\TrackerInterface`. +This interface defines one method called `tracking` to perform delivery tracking. + + +### Step 7: Configure RetailCrmDeliveryModuleBundle + +Import the routing configuration file in `config/routes.yaml`: + +``` yaml +# config/routes.yaml +retail_crm_delivery_module_callback_delivery: + resource: "@RetailCrmDeliveryModuleBundle/Resources/config/routing/delivery_callback.xml" + +retail_crm_delivery_module_callback_integration_module: + resource: "@RetailCrmDeliveryModuleBundle/Resources/config/routing/integration_module_callback.xml" +``` + +Add RetailCrmDeliveryModuleBundle settings in `config/packages/retail_crm_delivery_module.yaml`: + +``` yaml +# config/packages/retail_crm_delivery_module.yaml +retail_crm_delivery_module: + db_driver: orm # Drivers available: orm or custom + + model: + account_class: App\Entity\Account + delivery_class: App\Entity\Delivery + + service: + manager: + account: retail_crm_delivery_module.manager.account.default # Or your custom + delivery: retail_crm_delivery_module.manager.delivery.default # Or your custom + integration_module_factory: App\IntegrationModule\CustomIntegrationModuleFactory + delivery_service: App\DeliveryService\CustomDeliveryService + tracker: retail_crm_delivery_module.tracker.default # Or your custom +``` + +Update your database schema + +Now the bundle is configured, the last thing you need to do is update your database schema because you have added a new entity. + +For ORM run the following command. + + $ php bin/console doctrine:schema:update --force diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..93ed756 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,36 @@ + + + + + + + tests + + + + + + src + + + + + + + + \ No newline at end of file diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..49b3329 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/src/Command/TrackingCommand.php b/src/Command/TrackingCommand.php new file mode 100644 index 0000000..9013d87 --- /dev/null +++ b/src/Command/TrackingCommand.php @@ -0,0 +1,108 @@ +accountManager = $accountManager; + $this->tracker = $tracker; + + parent::__construct($name); + } + + /** + * {@inheritdoc} + */ + protected function configure(): void + { + $this + ->setDescription('Delivery tracking') + ->addArgument('accountId', InputArgument::OPTIONAL, 'Choose account, or make it for all') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (!$this->lock()) { + $output->writeln('The command is already running in another process.'); + + return 1; + } + + $startTime = new \DateTime(); + + $accountId = $input->hasArgument('accountId') + ? (int) $input->getArgument('accountId') + : null + ; + + $successfulCount = $failureCount = 0; + + $accounts = $this->getActiveAccounts($accountId); + + /** @var Account $account */ + foreach ($accounts as $account) { + try { + $this->tracker->tracking($account); + + ++$successfulCount; + } catch (\Exception $e) { + $output->writeln("Failed to update statuses for account {$account->getCrmUrl()}[{$account->getClientId()}]"); + $output->writeln("Error: {$e->getMessage()}"); + + ++$failureCount; + } + } + + $this->release(); + + $output->writeln(sprintf('Accounts updated successful = %s', $successfulCount)); + $output->writeln(sprintf('Accounts failed updates = %s', $failureCount)); + $output->writeln(sprintf('Memory = %s', memory_get_peak_usage())); + $output->writeln(sprintf('Time of execution = %s', $startTime->diff(new \DateTime())->format('%I:%S'))); + + return 0; + } + + protected function getActiveAccounts(int $accountId = null): \Generator + { + if (null !== $accountId) { + $account = $this->accountManager->findActiveAccountById($accountId); + + if (null !== $account) { + yield $account; + } else { + yield from []; + } + } else { + return $this->accountManager->findActiveAccounts(); + } + } +} diff --git a/src/Command/UpdateIntegrationModuleCommand.php b/src/Command/UpdateIntegrationModuleCommand.php new file mode 100644 index 0000000..00b80d1 --- /dev/null +++ b/src/Command/UpdateIntegrationModuleCommand.php @@ -0,0 +1,115 @@ +accountManager = $accountManager; + $this->integrationModuleFactory = $integrationModuleFactory; + $this->crmApiGateway = $crmApiGateway; + + parent::__construct($name); + } + + /** + * {@inheritdoc} + */ + protected function configure(): void + { + $this + ->setDescription('Updates integration module') + ->addArgument('accountId', InputArgument::OPTIONAL, 'Choose account, or make it for all') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (!$this->lock()) { + $output->writeln('The command is already running in another process.'); + + return 1; + } + + $startTime = new \DateTime(); + + $accountId = $input->hasArgument('accountId') + ? (int) $input->getArgument('accountId') + : null + ; + + $successfulCount = $failureCount = 0; + + $accounts = $this->getActiveAccounts($accountId); + + /** @var Account $account */ + foreach ($accounts as $account) { + $integrationModule = $this->integrationModuleFactory->createIntegrationModule($account); + + try { + $this->crmApiGateway->updateIntegrationModule($account, $integrationModule); + + ++$successfulCount; + } catch (\Exception $e) { + $output->writeln("Failed to update integration module for account {$account->getCrmUrl()}[{$account->getClientId()}]"); + $output->writeln("{$e->getMessage()}"); + + ++$failureCount; + } + } + + $this->release(); + + $output->writeln(sprintf('Accounts updated successful = %s', $successfulCount)); + $output->writeln(sprintf('Accounts failed updates = %s', $failureCount)); + $output->writeln(sprintf('Memory = %s', memory_get_peak_usage())); + $output->writeln(sprintf('Time of execution = %s', $startTime->diff(new \DateTime())->format('%I:%S'))); + + return 0; + } + + protected function getActiveAccounts(int $accountId = null): \Generator + { + if (null !== $accountId) { + $account = $this->accountManager->findActiveAccountById($accountId); + if (null !== $account) { + yield $account; + } else { + yield from []; + } + } else { + return $this->accountManager->findActiveAccounts(); + } + } +} diff --git a/src/Controller/AccountValueResolver.php b/src/Controller/AccountValueResolver.php new file mode 100644 index 0000000..52348d8 --- /dev/null +++ b/src/Controller/AccountValueResolver.php @@ -0,0 +1,42 @@ +accountManager = $accountManager; + } + + public function supports(Request $request, ArgumentMetadata $argument) + { + return Account::class === $argument->getType() || is_subclass_of($argument->getType(), Account::class); + } + + public function resolve(Request $request, ArgumentMetadata $argument) + { + $clientId = $request->get('clientId'); + if (empty($clientId)) { + throw new AccessDeniedHttpException('Parameter "clientId" is required'); + } + + $account = $this->accountManager->findAccountByClientId($clientId); + if (null === $account) { + throw new NotFoundHttpException('Account not found'); + } + + yield $account; + } +} diff --git a/src/Controller/DeliveryCallbackController.php b/src/Controller/DeliveryCallbackController.php new file mode 100644 index 0000000..fe9b280 --- /dev/null +++ b/src/Controller/DeliveryCallbackController.php @@ -0,0 +1,341 @@ +decoder = $decoder; + $this->tariffAdapter = $tariffAdapter; + $this->deliveryAdapter = $deliveryAdapter; + $this->shipmentAdapter = $shipmentAdapter; + $this->plateAdapter = $plateAdapter; + } + + public function onTariffList(Request $request, Account $account): JsonResponse + { + if (null === $this->tariffAdapter) { + throw new \LogicException('Method is not configured'); + } + + try { + $result = $this->tariffAdapter->getTariffList($account); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onCalculate(Request $request, Account $account): JsonResponse + { + if (null === $this->tariffAdapter) { + throw new \LogicException('Method is not configured'); + } + + $calculate = $request->request->get('calculate'); + if (empty($calculate)) { + throw new BadRequestHttpException('Parameter "calculate" is required'); + } + + try { + $decodedCalculate = $this->decoder->decode($calculate, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + try { + $result = $this->tariffAdapter->calculate($account, $decodedCalculate); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onSave(Request $request, Account $account): JsonResponse + { + if (null === $this->deliveryAdapter) { + throw new \LogicException('Method is not configured'); + } + + $save = $request->request->get('save'); + if (empty($save)) { + throw new BadRequestHttpException('Parameter "save" is required'); + } + + try { + $decodedSave = $this->decoder->decode($save, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + try { + $result = $this->deliveryAdapter->createDelivery($account, $decodedSave); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onGet(Request $request, Account $account): JsonResponse + { + if (null === $this->deliveryAdapter) { + throw new \LogicException('Method is not configured'); + } + + $deliveryId = $request->query->get('deliveryId'); + if (empty($deliveryId)) { + throw new BadRequestHttpException('Parameter "deliveryId" is required'); + } + + try { + $result = $this->deliveryAdapter->getDelivery($account, $deliveryId); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryNotFoundException $e) { + throw new NotFoundHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onDelete(Request $request, Account $account): JsonResponse + { + if (null === $this->deliveryAdapter) { + throw new \LogicException('Method is not configured'); + } + + $delete = $request->request->get('delete'); + if (empty($delete)) { + throw new BadRequestHttpException('Parameter "delete" is required'); + } + + try { + $decodedDelete = $this->decoder->decode($delete, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + try { + $success = $this->deliveryAdapter->deleteDelivery($account, $decodedDelete); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryNotFoundException $e) { + throw new NotFoundHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => $success]); + } + + public function onShipmentPointList(Request $request, Account $account): JsonResponse + { + if (null === $this->shipmentAdapter) { + throw new \LogicException('Method is not configured'); + } + + $query = [ + 'country' => $request->query->get('country'), + 'region' => $request->query->get('region'), + 'regionId' => $request->query->getInt('regionId'), + 'city' => $request->query->get('city'), + 'cityId' => $request->query->getInt('cityId'), + ]; + + try { + $result = $this->shipmentAdapter->getShipmentPointList($account, $query); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onShipmentSave(Request $request, Account $account): JsonResponse + { + if (null === $this->shipmentAdapter) { + throw new \LogicException('Method is not configured'); + } + + $shipmentSave = $request->request->get('shipmentSave'); + if (empty($shipmentSave)) { + throw new BadRequestHttpException('Parameter "shipmentSave" is required'); + } + + try { + $decodedShipmentSave = $this->decoder->decode($shipmentSave, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + try { + $result = $this->shipmentAdapter->createShipment($account, $decodedShipmentSave); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => true, 'result' => $result]); + } + + public function onShipmentDelete(Request $request, Account $account): JsonResponse + { + if (null === $this->shipmentAdapter) { + throw new \LogicException('Method is not configured'); + } + + $shipmentDelete = $request->request->get('shipmentDelete'); + if (empty($shipmentDelete)) { + throw new BadRequestHttpException('Parameter "shipmentDelete" is required'); + } + + try { + $decodedShipmentDelete = $this->decoder->decode($shipmentDelete, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + try { + $success = $this->shipmentAdapter->deleteShipment($account, $decodedShipmentDelete); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (ShipmentNotFound $e) { + throw new NotFoundHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + return $this->json(['success' => $success]); + } + + public function onPrint(Request $request, Account $account): Response + { + if (null === $this->plateAdapter) { + throw new \LogicException('Method is not configured'); + } + + $print = $request->request->get('print'); + if (empty($print)) { + throw new BadRequestHttpException('Parameter "print" is required'); + } + + try { + $decodedPrint = $this->decoder->decode($print, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage()); + } + + try { + $printedPlates = $this->plateAdapter->getPrintedPlates($account, $decodedPrint); + } catch (BadRequestException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } catch (DeliveryServiceUnavailableException $e) { + throw new ServiceUnavailableHttpException($e->getRetryAfter(), $e->getMessage(), $e); + } catch (DeliveryServiceException $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Internal Server Error', $e); + } + + if (empty($printedPlates)) { + throw new BadRequestHttpException('Empty printed plate list'); + } + + if (1 === count($printedPlates)) { + return new Response(reset($printedPlates), 200, ['Content-Type' => 'application/pdf']); + } + + $tmpFilename = tempnam(sys_get_temp_dir(), 'zip'); + if (!$tmpFilename) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Failed to create temp file for zip archive'); + } + + $archive = new \ZipArchive(); + $archive->open($tmpFilename, \ZipArchive::CREATE); + foreach ($printedPlates as $filename => $printedPlate) { + $archive->addFromString($filename, $printedPlate); + } + $archive->close(); + + $content = file_get_contents($tmpFilename); + if (!$content) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Failed to read zip archive'); + } + + unlink($tmpFilename); + + return new Response($content, 200, ['Content-Type' => 'application/zip']); + } +} diff --git a/src/Controller/IntegrationModuleCallbackController.php b/src/Controller/IntegrationModuleCallbackController.php new file mode 100644 index 0000000..a0bba1b --- /dev/null +++ b/src/Controller/IntegrationModuleCallbackController.php @@ -0,0 +1,58 @@ +decoder = $decoder; + $this->accountManager = $accountManager; + } + + public function onActivity(Request $request, Account $account): JsonResponse + { + $activity = $request->request->get('activity'); + if (empty($activity)) { + throw new BadRequestHttpException('Parameter "activity" is required'); + } + + try { + $decodedActivity = $this->decoder->decode($activity, self::DATA_FORMAT); + } catch (SerializerException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); + } + + $systemUrl = $request->request->get('systemUrl'); + if (empty($systemUrl)) { + throw new BadRequestHttpException('Parameter "systemUrl" is required'); + } + + $account + ->setActive((bool) $decodedActivity['active']) + ->setFrozen((bool) $decodedActivity['freeze']) + ->setCrmUrl($systemUrl) + ; + + $this->accountManager->saveAccount($account); + + return $this->json(['success' => true]); + } +} diff --git a/src/DeliveryService/DeliveryAdapterInterface.php b/src/DeliveryService/DeliveryAdapterInterface.php new file mode 100644 index 0000000..84c3e78 --- /dev/null +++ b/src/DeliveryService/DeliveryAdapterInterface.php @@ -0,0 +1,14 @@ +retryAfter = $retryAfter; + + parent::__construct($message, $code, $previous); + } + + public function getRetryAfter(): int + { + return $this->retryAfter; + } +} diff --git a/src/DeliveryService/Exception/ExceptionInterface.php b/src/DeliveryService/Exception/ExceptionInterface.php new file mode 100644 index 0000000..ea4339a --- /dev/null +++ b/src/DeliveryService/Exception/ExceptionInterface.php @@ -0,0 +1,7 @@ +getRootNode(); + + $rootNode + ->children() + ->scalarNode('db_driver') + ->cannotBeEmpty() + ->cannotBeOverwritten() + ->validate() + ->ifNotInArray(self::AVAILABLE_DRIVERS) + ->thenInvalid('The driver %s is not supported. Please choose one of ' . json_encode(self::AVAILABLE_DRIVERS)) + ->end() + ->end() + + ->arrayNode('model') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('account_class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('delivery_class')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + + ->booleanNode('use_tracking')->defaultValue(true)->end() + + ->arrayNode('service') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('manager') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('account') + ->defaultValue('retail_crm_delivery_module.manager.account.default') + ->end() + ->scalarNode('delivery') + ->defaultValue('retail_crm_delivery_module.manager.delivery.default') + ->end() + ->end() + ->end() + + ->scalarNode('tracker') + ->defaultValue('retail_crm_delivery_module.tracker.default') + ->end() + ->end() + ->end() + + ->arrayNode('integration_module') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('factory')->isRequired()->cannotBeEmpty()->end() + + ->arrayNode('configuration') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('integration_code')->isRequired()->cannotBeEmpty()->end() + + ->arrayNode('locales') + ->arrayPrototype() + ->children() + ->scalarNode('name')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('logo')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + ->requiresAtLeastOneElement() + ->useAttributeAsKey('locale') + ->validate() + ->ifTrue(function ($v) { + return array_diff(array_keys($v), self::AVAILABLE_LOCALES); + }) + ->thenInvalid('The locales is not valid') + ->end() + ->end() + + ->arrayNode('countries') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + + ->arrayNode('delivery_service') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('adapter') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('tariff')->cannotBeEmpty()->end() + ->scalarNode('delivery')->cannotBeEmpty()->end() + ->scalarNode('shipment')->cannotBeEmpty()->end() + ->scalarNode('plate')->cannotBeEmpty()->end() + ->scalarNode('status')->cannotBeEmpty()->end() + ->end() + ->end() + + ->variableNode('parameters')->end() + ->end() + ->end() + ->end() + + ->validate() + ->ifTrue(function ($v) { + return 'custom' === $v['db_driver'] && 'retail_crm_delivery_module.manager.account.default' === $v['service']['manager']['account']; + }) + ->thenInvalid('You need to specify your own account manager service when using the "custom" driver') + ->end() + + ->validate() + ->ifTrue(function ($v) { + return 'custom' === $v['db_driver'] && 'retail_crm_delivery_module.manager.delivery.default' === $v['service']['manager']['delivery']; + }) + ->thenInvalid('You need to specify your own delivery manager service when using the "custom" driver') + ->end() + ; + + return $treeBuilder; + } +} diff --git a/src/DependencyInjection/RetailCrmDeliveryModuleExtension.php b/src/DependencyInjection/RetailCrmDeliveryModuleExtension.php new file mode 100644 index 0000000..53f7409 --- /dev/null +++ b/src/DependencyInjection/RetailCrmDeliveryModuleExtension.php @@ -0,0 +1,92 @@ +processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__) . '/Resources/config')); + + if ('custom' !== $config['db_driver']) { + $loader->load(sprintf('%s.xml', $config['db_driver'])); + } + + foreach (self::SERVICES as $service) { + $loader->load(sprintf('%s.xml', $service)); + } + + // Models + $container->setParameter('retail_crm_delivery_module.model.account.class', $config['model']['account_class']); + $container->setParameter('retail_crm_delivery_module.model.delivery.class', $config['model']['delivery_class']); + + // Services + $container->setAlias('retail_crm_delivery_module.manager.account', $config['service']['manager']['account']); + $container->getAlias('retail_crm_delivery_module.manager.account')->setPublic(true); + + $container->setAlias('retail_crm_delivery_module.manager.delivery', $config['service']['manager']['delivery']); + $container->getAlias('retail_crm_delivery_module.manager.delivery')->setPublic(true); + + if ($config['use_tracking']) { + $loader->load('tracker.xml'); + + $container->setAlias('retail_crm_delivery_module.tracker', $config['service']['tracker']); + $container->getAlias('retail_crm_delivery_module.tracker')->setPublic(true); + } else { + $container->removeDefinition('retail_crm_delivery_module.command.tacking'); + } + + // Integration module + $container->setAlias('retail_crm_delivery_module.integration_module.factory', $config['integration_module']['factory']); + $container->getAlias('retail_crm_delivery_module.integration_module.factory')->setPublic(true); + + $container->setParameter('retail_crm_delivery_module.integration_module.configuration', $config['integration_module']['configuration']); + + // Delivery service + if (isset($config['delivery_service']['adapter']['tariff'])) { + $container->setAlias('retail_crm_delivery_module.delivery_service.adapter.tariff', $config['delivery_service']['adapter']['tariff']); + $container->getAlias('retail_crm_delivery_module.delivery_service.adapter.tariff')->setPublic(true); + } + + if (isset($config['delivery_service']['adapter']['delivery'])) { + $container->setAlias('retail_crm_delivery_module.delivery_service.adapter.delivery', $config['delivery_service']['adapter']['delivery']); + $container->getAlias('retail_crm_delivery_module.delivery_service.adapter.delivery')->setPublic(true); + } + + if (isset($config['delivery_service']['adapter']['shipment'])) { + $container->setAlias('retail_crm_delivery_module.delivery_service.adapter.shipment', $config['delivery_service']['adapter']['shipment']); + $container->getAlias('retail_crm_delivery_module.delivery_service.adapter.shipment')->setPublic(true); + } + + if (isset($config['delivery_service']['adapter']['plate'])) { + $container->setAlias('retail_crm_delivery_module.delivery_service.adapter.plate', $config['delivery_service']['adapter']['plate']); + $container->getAlias('retail_crm_delivery_module.delivery_service.adapter.plate')->setPublic(true); + } + + if (isset($config['delivery_service']['adapter']['status'])) { + $container->setAlias('retail_crm_delivery_module.delivery_service.adapter.status', $config['delivery_service']['adapter']['status']); + $container->getAlias('retail_crm_delivery_module.delivery_service.adapter.status')->setPublic(true); + } + + $container->setParameter('retail_crm_delivery_module.delivery_service.parameters', $config['delivery_service']['parameters']); + } +} diff --git a/src/Entity/Account.php b/src/Entity/Account.php new file mode 100644 index 0000000..99f117a --- /dev/null +++ b/src/Entity/Account.php @@ -0,0 +1,9 @@ +entityManager = $entityManager; + $this->class = $class; + } + + public function getClass(): string + { + return $this->class; + } + + public function findAccountBy(array $criteria): ?object + { + return $this->getRepository()->findOneBy($criteria); + } + + public function findAccountById(int $id): ?object + { + return $this->findAccountBy(['id' => $id]); + } + + public function findAccountByClientId(string $clientId): ?object + { + return $this->findAccountBy(['clientId' => $clientId]); + } + + public function findActiveAccountById(int $id): ?object + { + return $this->findAccountBy(['id' => $id, 'active' => true, 'freeze' => false]); + } + + public function findActiveAccountByClientId(string $clientId): ?object + { + return $this->findAccountBy(['clientId' => $clientId, 'active' => true, 'freeze' => false]); + } + + public function findActiveAccounts(): \Generator + { + $queryBuilder = $this->entityManager->createQueryBuilder(); + + $query = $queryBuilder + ->select('account') + ->from(Account::class, 'account') + ->andWhere('account.active = true') + ->andWhere('account.freeze = false') + ->andWhere('account.id > :lastId') + ->addOrderBy('account.id') + ->setMaxResults(self::QUERY_MAX_RESULTS) + ->getQuery() + ; + + $lastId = 0; + while (true) { + $query->setParameter('lastId', $lastId); + + $result = $query->getResult(); + if (empty($result)) { + yield from []; + + break; + } + + /** @var Account $item */ + foreach ($result as $item) { + $lastId = $item->getId(); + + yield $item; + } + + $this->entityManager->clear(); + } + } + + public function saveAccount(BaseAccount $account): void + { + $this->entityManager->persist($account); + $this->entityManager->flush(); + } + + protected function getRepository(): ObjectRepository + { + return $this->entityManager->getRepository($this->getClass()); + } +} diff --git a/src/Entity/Delivery.php b/src/Entity/Delivery.php new file mode 100644 index 0000000..60b4554 --- /dev/null +++ b/src/Entity/Delivery.php @@ -0,0 +1,9 @@ +entityManager = $entityManager; + $this->class = $class; + } + + public function getClass(): string + { + return $this->class; + } + + public function findDeliveryBy(array $criteria): ?object + { + return $this->getRepository()->findOneBy($criteria); + } + + public function findDeliveryById(int $id): ?object + { + return $this->findDeliveryBy(['id' => $id]); + } + + public function findDeliveryByExternalId(string $externalId): ?object + { + return $this->findDeliveryBy(['externalId' => $externalId]); + } + + public function findActiveDeliveriesByAccount(BaseAccount $account): \Generator + { + $query = $this->entityManager->createQueryBuilder() + ->select('delivery') + ->from(Delivery::class, 'delivery') + ->andWhere('delivery.ended = FALSE') + ->andWhere('delivery.account = :account') + ->andWhere('delivery.id > :lastId') + ->orderBy('delivery.id ASC') + ->setMaxResults(self::QUERY_MAX_RESULTS) + ->getQuery() + ; + + $query->setParameter('account', $account); + + $lastId = 0; + while (true) { + $query->setParameter('lastId', $lastId); + + $result = $query->getResult(); + if (empty($result)) { + yield from []; + + break; + } + + /** @var Delivery $item */ + foreach ($result as $item) { + $lastId = $item->getId(); + + yield $item; + } + + $this->entityManager->clear(); + } + } + + public function saveDelivery(BaseDelivery $delivery): void + { + $this->entityManager->persist($delivery); + $this->entityManager->flush(); + } + + public function removeDelivery(BaseDelivery $delivery): void + { + $this->entityManager->remove($delivery); + $this->entityManager->flush(); + } + + protected function getRepository(): ObjectRepository + { + return $this->entityManager->getRepository($this->getClass()); + } +} diff --git a/src/Integration/ApiGatewayExceptionInterface.php b/src/Integration/ApiGatewayExceptionInterface.php new file mode 100644 index 0000000..8b762bd --- /dev/null +++ b/src/Integration/ApiGatewayExceptionInterface.php @@ -0,0 +1,8 @@ + [ + 'api' => [ + 'version' => 'v5', + ], + ], + ]; + + /** @var ClientInterface */ + private $client; + + public function __construct(ClientInterface $client) + { + $this->client = $client; + } + + public function getCredentials(Account $account): array + { + $response = $this->request($account, 'GET', 'credentials', ['extra' => ['api' => ['version' => false]]]); + + return $response['credentials'] ?? []; + } + + public function getPaymentTypes(Account $account): array + { + $response = $this->request($account, 'GET', 'reference/payment-types'); + + return $response['paymentTypes'] ?? []; + } + + public function getSites(Account $account): array + { + $response = $this->request($account, 'GET', 'reference/sites'); + + return $response['sites'] ?? []; + } + + public function getStatuses(Account $account): array + { + $response = $this->request($account, 'GET', 'reference/statuses'); + + return $response['statuses'] ?? []; + } + + public function getStores(Account $account): array + { + $response = $this->request($account, 'GET', 'reference/stores'); + + return $response['stores'] ?? []; + } + + public function updateIntegrationModule(Account $account, array $integrationModule): void + { + if (empty($integrationModule['code'])) { + throw new \LogicException('Parameter "code" is required'); + } + + $endpoint = sprintf('integration-modules/%s/edit', $integrationModule['code']); + + $options = [ + 'form_params' => [ + 'integrationModule' => json_encode($integrationModule), + ], + ]; + + $this->request($account, 'POST', $endpoint, $options); + } + + public function updateDeliveryStatuses(Account $account, string $integrationCode, array $statuses): void + { + if (empty($integrationCode)) { + throw new \LogicException('Parameter "integrationCode" is required'); + } + + $endpoint = sprintf('delivery/generic/%s/tracking', $integrationCode); + + $options = [ + 'form_params' => [ + 'statusUpdate' => json_encode($statuses), + ], + ]; + + $this->request($account, 'POST', $endpoint, $options); + } + + public function request(Account $account, string $method, string $endpoint, array $options = []): array + { + $options = array_merge_recursive( + static::$defaultOptions, + [ + 'extra' => [ + 'service_code' => static::getCode(), + 'account_id' => $account->getId(), + 'client_id' => $account->getClientId(), + 'debug' => $account->isDebug(), + ], + ], + $options + ); + + switch (strtoupper($method)) { + case 'GET': + $options['query']['apiKey'] = $account->getCrmApiKey(); + + break; + + case 'POST': + $options['form_params']['apiKey'] = $account->getCrmApiKey(); + + break; + } + + $uri = $this->buildUri($account, $endpoint, $options); + + try { + $response = $this->client->request($method, $uri, $options); + } catch (GuzzleClientException $e) { + $response = $e->getResponse(); + + if (403 === $response->getStatusCode()) { + throw new ForbiddenException($e->getMessage(), $e); + } + } catch (GuzzleServerException $e) { + $response = $e->getResponse(); + + if (503 === $response->getStatusCode()) { + throw new LimitException($e->getMessage(), $e); + } + } catch (GuzzleHttpException $e) { + throw new HttpException($e->getMessage(), $e); + } + + $body = json_decode($response->getBody(), true); + if (!($body['success'] ?? false)) { + throw new ApiGatewayException( + $body['errorMsg'] ?? 'An error has occurred while processing your request', + $body['errors'] ?? [] + ); + } + + return $body; + } + + private function buildUri(Account $account, string $endpoint, array $options = []): string + { + $baseUrl = sprintf('%s/api', $account->getCrmUrl()); + if ($options['extra']['api']['version'] ?? false) { + $baseUrl .= sprintf('/%s', $options['extra']['api']['version']); + } + + return sprintf('%s/%s', $baseUrl, $endpoint); + } +} diff --git a/src/Integration/Crm/ApiGatewayInterface.php b/src/Integration/Crm/ApiGatewayInterface.php new file mode 100644 index 0000000..9954764 --- /dev/null +++ b/src/Integration/Crm/ApiGatewayInterface.php @@ -0,0 +1,25 @@ +errors = $errors; + + parent::__construct($message, $code, $previous); + } + + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/src/Integration/Crm/Exception/ExceptionInterface.php b/src/Integration/Crm/Exception/ExceptionInterface.php new file mode 100644 index 0000000..d82424c --- /dev/null +++ b/src/Integration/Crm/Exception/ExceptionInterface.php @@ -0,0 +1,7 @@ +configuration = $configuration; + } + + public function createIntegrationModule(Account $account): array + { + return [ + 'code' => sprintf('%s-%s', $this->configuration['integration_code'], $account->getId()), + 'integrationCode' => $this->configuration['integration_code'], + 'active' => $account->isActive(), + 'freeze' => $account->isFrozen(), + 'name' => $this->configuration['locales'][$account->getLanguage()]['name'], + 'logo' => $this->configuration['locales'][$account->getLanguage()]['logo'], + 'clientId' => $account->getClientId(), + 'baseUrl' => $this->getBaseUrl(), + 'actions' => [ + 'activity' => 'integration-module/activity', + ], + 'availableCountries' => $this->configuration['countries'], + 'accountUrl' => $this->getAccountUrl(), + 'integrations' => [ + 'delivery' => $this->getDeliveryConfiguration($account), + ], + ]; + } + + abstract protected function getBaseUrl(): string; + + abstract protected function getAccountUrl(): string; + + abstract protected function getDeliveryConfiguration(Account $account): array; +} diff --git a/src/IntegrationModule/IntegrationModuleFactoryInterface.php b/src/IntegrationModule/IntegrationModuleFactoryInterface.php new file mode 100644 index 0000000..e4d1aa2 --- /dev/null +++ b/src/IntegrationModule/IntegrationModuleFactoryInterface.php @@ -0,0 +1,10 @@ +clientId = uuid_create(UUID_TYPE_RANDOM); + $this->active = true; + $this->frozen = false; + $this->debug = false; + $this->defaultPayerType = 'sender'; + $this->costCalculateBy = 'auto'; + $this->nullDeclaredValue = false; + $this->lockedByDefault = false; + $this->deliveryExtraData = []; + $this->shipmentExtraData = []; + $this->createdAt = new \DateTime(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getCrmUrl(): ?string + { + return $this->crmUrl; + } + + public function setCrmUrl(string $crmUrl): self + { + $this->crmUrl = rtrim($crmUrl, '/'); + + return $this; + } + + public function getCrmApiKey(): ?string + { + return $this->crmApiKey; + } + + public function setCrmApiKey(string $crmApiKey): self + { + $this->crmApiKey = $crmApiKey; + + return $this; + } + + public function getClientId(): ?string + { + return $this->clientId; + } + + public function setClientId(string $clientId): self + { + $this->clientId = $clientId; + + return $this; + } + + public function getLanguage(): ?string + { + return $this->language; + } + + public function setLanguage(string $language): self + { + $this->language = $language; + + return $this; + } + + public function isActive(): bool + { + return $this->active; + } + + public function setActive(bool $active): self + { + $this->active = $active; + + return $this; + } + + public function isFrozen(): bool + { + return $this->frozen; + } + + public function setFrozen(bool $frozen): self + { + $this->frozen = $frozen; + + return $this; + } + + public function isEnabled(): bool + { + return !$this->frozen && $this->active; + } + + public function isDebug(): bool + { + return $this->debug; + } + + public function setDebug(bool $debug): self + { + $this->debug = $debug; + + return $this; + } + + public function getDefaultPayerType(): string + { + return $this->defaultPayerType; + } + + public function setDefaultPayerType(string $defaultPayerType): self + { + $this->defaultPayerType = $defaultPayerType; + + return $this; + } + + public function getCostCalculateBy(): string + { + return $this->costCalculateBy; + } + + public function setCostCalculateBy(string $costCalculateBy): self + { + $this->costCalculateBy = $costCalculateBy; + + return $this; + } + + public function isNullDeclaredValue(): bool + { + return $this->nullDeclaredValue; + } + + public function setNullDeclaredValue(bool $nullDeclaredValue): self + { + $this->nullDeclaredValue = $nullDeclaredValue; + + return $this; + } + + public function isLockedByDefault(): bool + { + return $this->lockedByDefault; + } + + public function setLockedByDefault(bool $lockedByDefault): self + { + $this->lockedByDefault = $lockedByDefault; + + return $this; + } + + public function getDeliveryExtraData(): array + { + return $this->deliveryExtraData; + } + + public function setDeliveryExtraData(array $deliveryExtraData): self + { + $this->deliveryExtraData = $deliveryExtraData; + + return $this; + } + + public function getShipmentExtraData(): array + { + return $this->shipmentExtraData; + } + + public function setShipmentExtraData(array $shipmentExtraData): self + { + $this->shipmentExtraData = $shipmentExtraData; + + return $this; + } + + public function getTrackedAt(): ?\DateTime + { + return $this->trackedAt; + } + + public function setTrackedAt(\DateTime $trackedAt): self + { + $this->trackedAt = $trackedAt; + + return $this; + } + + public function getCreatedAt(): \DateTime + { + return $this->createdAt; + } +} diff --git a/src/Model/AccountManager.php b/src/Model/AccountManager.php new file mode 100644 index 0000000..6c34c05 --- /dev/null +++ b/src/Model/AccountManager.php @@ -0,0 +1,13 @@ +getClass(); + + return new $class(); + } +} diff --git a/src/Model/AccountManagerInterface.php b/src/Model/AccountManagerInterface.php new file mode 100644 index 0000000..fe56f73 --- /dev/null +++ b/src/Model/AccountManagerInterface.php @@ -0,0 +1,24 @@ +account = $account; $this->ended = false; + $this->createdAt = new \DateTime(); } - /** - * @return int - */ - public function getId() + public function getId(): ?int { return $this->id; } + public function getAccount(): Account + { + return $this->account; + } + public function setAccount(Account $account): self { $this->account = $account; @@ -49,9 +49,9 @@ abstract class DeliveryOrder return $this; } - public function getAccount(): ?Account + public function getOrderId(): ?int { - return $this->account; + return $this->orderId; } public function setOrderId(int $orderId): self @@ -61,9 +61,9 @@ abstract class DeliveryOrder return $this; } - public function getOrderId(): int + public function getExternalId(): ?string { - return $this->orderId; + return $this->externalId; } public function setExternalId(string $externalId): self @@ -73,9 +73,21 @@ abstract class DeliveryOrder return $this; } - public function getExternalId(): string + public function getTrackNumber(): ?string { - return $this->externalId; + return $this->trackNumber; + } + + public function setTrackNumber(string $trackNumber): self + { + $this->trackNumber = $trackNumber; + + return $this; + } + + public function isEnded(): bool + { + return $this->ended; } public function setEnded(bool $ended): self @@ -85,18 +97,8 @@ abstract class DeliveryOrder return $this; } - public function getTrackNumber(): string + public function getCreatedAt(): \DateTime { - return $this->externalId; - } - - public function setTrackNumber(string $trackNumber): self - { - return $this; - } - - public function getEnded(): bool - { - return $this->ended; + return $this->createdAt; } } diff --git a/src/Model/DeliveryManager.php b/src/Model/DeliveryManager.php new file mode 100644 index 0000000..6cc19f9 --- /dev/null +++ b/src/Model/DeliveryManager.php @@ -0,0 +1,13 @@ +getClass(); + + return new $class($account); + } +} diff --git a/src/Model/DeliveryManagerInterface.php b/src/Model/DeliveryManagerInterface.php new file mode 100644 index 0000000..ca774b6 --- /dev/null +++ b/src/Model/DeliveryManagerInterface.php @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/controllers.xml b/src/Resources/config/controllers.xml new file mode 100644 index 0000000..a0aa4e9 --- /dev/null +++ b/src/Resources/config/controllers.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/doctrine/Account.orm.xml b/src/Resources/config/doctrine/Account.orm.xml new file mode 100644 index 0000000..9ba73a5 --- /dev/null +++ b/src/Resources/config/doctrine/Account.orm.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/doctrine/Delivery.orm.xml b/src/Resources/config/doctrine/Delivery.orm.xml new file mode 100644 index 0000000..95576be --- /dev/null +++ b/src/Resources/config/doctrine/Delivery.orm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/Resources/config/integrations.xml b/src/Resources/config/integrations.xml new file mode 100644 index 0000000..9166769 --- /dev/null +++ b/src/Resources/config/integrations.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/orm.xml b/src/Resources/config/orm.xml new file mode 100644 index 0000000..81e0594 --- /dev/null +++ b/src/Resources/config/orm.xml @@ -0,0 +1,28 @@ + + + + + + RetailCrm\DeliveryModuleBundle\Entity\Account + RetailCrm\DeliveryModuleBundle\Entity\Delivery + + + + + + %retail_crm_delivery_module.model.account.class% + + + + + + %retail_crm_delivery_module.model.delivery.class% + + + + + diff --git a/src/Resources/config/routing/delivery_callback.xml b/src/Resources/config/routing/delivery_callback.xml new file mode 100644 index 0000000..792cb54 --- /dev/null +++ b/src/Resources/config/routing/delivery_callback.xml @@ -0,0 +1,45 @@ + + + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onTariffList + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onCalculate + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onSave + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onGet + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onDelete + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onShipmentPointList + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onShipmentSave + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onShipmentDelete + + + + RetailCrm\DeliveryModuleBundle\Controller\DeliveryCallbackController::onPrint + + + diff --git a/src/Resources/config/routing/integration_module_callback.xml b/src/Resources/config/routing/integration_module_callback.xml new file mode 100644 index 0000000..e0455f3 --- /dev/null +++ b/src/Resources/config/routing/integration_module_callback.xml @@ -0,0 +1,13 @@ + + + + + + RetailCrm\DeliveryModuleBundle\Controller\IntegrationModuleCallbackController::onActivity + + + diff --git a/src/Resources/config/serializers.xml b/src/Resources/config/serializers.xml new file mode 100644 index 0000000..52b2006 --- /dev/null +++ b/src/Resources/config/serializers.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/Resources/config/tracker.xml b/src/Resources/config/tracker.xml new file mode 100644 index 0000000..1442403 --- /dev/null +++ b/src/Resources/config/tracker.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/validators.xml b/src/Resources/config/validators.xml new file mode 100644 index 0000000..0612d28 --- /dev/null +++ b/src/Resources/config/validators.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/Resources/translations/validators.en.yml b/src/Resources/translations/validators.en.yml new file mode 100644 index 0000000..01b8686 --- /dev/null +++ b/src/Resources/translations/validators.en.yml @@ -0,0 +1,6 @@ +crm_access: + access_denied: 'Access to the method "%method%" is denied' + forbidden_exception: 'Wrong API key' + limit_exception: 'Service is temporarily unavailable' + http_exception: 'Failed to connect to API' + api_gateway_exception: 'An error has occurred while processing your request' diff --git a/src/Resources/translations/validators.es.yml b/src/Resources/translations/validators.es.yml new file mode 100644 index 0000000..ef52aa6 --- /dev/null +++ b/src/Resources/translations/validators.es.yml @@ -0,0 +1,6 @@ +crm_access: + access_denied: 'Acceso al método "%method%" está prohibido' + forbidden_exception: 'Clave API incorrecta' + limit_exception: 'Servicio no está disponible temporalmente' + http_exception: 'No se ha podido conectar al API' + api_gateway_exception: 'Ha ocurrido un error al procesar tu solicitud' diff --git a/src/Resources/translations/validators.ru.yml b/src/Resources/translations/validators.ru.yml new file mode 100644 index 0000000..7af3cbf --- /dev/null +++ b/src/Resources/translations/validators.ru.yml @@ -0,0 +1,7 @@ +crm_access: + access_denied: 'Доступ к методу "%method%" запрещен' + forbidden_exception: 'Неверный API-ключ' + limit_exception: 'Сервис временно недоступен' + http_exception: 'Не удалось подключиться к API' + api_gateway_exception: 'Возникла ошибка при обработке запроса' + diff --git a/RetailCrmDeliveryModuleBundle.php b/src/RetailCrmDeliveryModuleBundle.php similarity index 100% rename from RetailCrmDeliveryModuleBundle.php rename to src/RetailCrmDeliveryModuleBundle.php diff --git a/src/Tracking/Tracker.php b/src/Tracking/Tracker.php new file mode 100644 index 0000000..1ed86b9 --- /dev/null +++ b/src/Tracking/Tracker.php @@ -0,0 +1,80 @@ +accountManager = $accountManager; + $this->deliveryManager = $deliveryManager; + $this->statusAdapter = $statusAdapter; + $this->integrationModuleFactory = $integrationModuleFactory; + $this->crmApiGateway = $crmApiGateway; + } + + public function tracking(Account $account): void + { + $deliveries = $this->deliveryManager->findActiveDeliveriesByAccount($account); + + $statuses = []; + foreach ($deliveries as $delivery) { + $statuses[] = $this->statusAdapter->getStatus($delivery); + + if (0 === count($statuses) % self::LIMIT) { + $this->updateDeliveryStatuses($account, $statuses); + + $statuses = []; + } + } + + if (count($statuses) > 0) { + $this->updateDeliveryStatuses($account, $statuses); + } + + $account->setTrackedAt(new \DateTime()); + + $this->accountManager->saveAccount($account); + } + + protected function updateDeliveryStatuses(Account $account, array $statuses): void + { + $integrationModule = $this->integrationModuleFactory->createIntegrationModule($account); + + $integrationCode = $integrationModule['integrationCode'] ?? null; + if (null === $integrationCode) { + throw new \LogicException('Parameter "integrationCode" is required'); + } + + $this->crmApiGateway->updateDeliveryStatuses($account, $integrationCode, $statuses); + } +} diff --git a/src/Tracking/TrackerInterface.php b/src/Tracking/TrackerInterface.php new file mode 100644 index 0000000..4512075 --- /dev/null +++ b/src/Tracking/TrackerInterface.php @@ -0,0 +1,10 @@ +crmApiGateway = $crmApiGateway; + } + + public function validate($account, Constraint $constraint): void + { + if (!($account instanceof Account)) { + throw new UnexpectedTypeException($account, Account::class); + } + + if (!($constraint instanceof CrmAccess)) { + throw new UnexpectedTypeException($constraint, CrmAccess::class); + } + + try { + $credentials = $this->crmApiGateway->getCredentials($account); + + foreach ($constraint->requiredApiMethods as $method) { + if (!in_array($method, $credentials)) { + $this->context + ->buildViolation('retail_crm_access.access_denied', ['%method%' => $method]) + ->atPath('crmApiKey') + ->addViolation() + ; + } + } + } catch (ForbiddenException $e) { + $this->context + ->buildViolation('crm_access.forbidden_exception') + ->setCause($e->getMessage()) + ->atPath('crmApiKey') + ->addViolation() + ; + } catch (LimitException $e) { + $this->context + ->buildViolation('crm_access.limit_exception') + ->setCause($e->getMessage()) + ->atPath('crmUrl') + ->addViolation() + ; + } catch (HttpException $e) { + $this->context + ->buildViolation('crm_access.http_exception') + ->setCause($e->getMessage()) + ->atPath('crmUrl') + ->addViolation() + ; + } catch (ApiGatewayException $e) { + $this->context + ->buildViolation('crm_access.api_gateway_exception') + ->setCause($e->getMessage()) + ->atPath('crmUrl') + ->addViolation() + ; + } + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29