diff --git a/src/Actions/CreateOrderAction.php b/src/Actions/CreateOrderAction.php index 1a6bdea..13a8dc3 100644 --- a/src/Actions/CreateOrderAction.php +++ b/src/Actions/CreateOrderAction.php @@ -8,10 +8,18 @@ namespace Cdek\Actions { use Cdek\CdekApi; + use Cdek\CoreApi; use Cdek\Config; + use Cdek\Exceptions\AuthException; + use Cdek\Exceptions\CdekApiException; + use Cdek\Exceptions\CdekClientException; + use Cdek\Exceptions\CdekServerException; use Cdek\Exceptions\PhoneNotValidException; + use Cdek\Exceptions\RestApiInvalidRequestException; + use Cdek\Exceptions\ShippingMethodNotFoundException; use Cdek\Helper; use Cdek\Helpers\CheckoutHelper; + use Cdek\Helpers\ScheduleLocker; use Cdek\Helpers\StringHelper; use Cdek\Helpers\WeightCalc; use Cdek\MetaKeys; @@ -20,6 +28,7 @@ use Cdek\Note; use Exception; use Throwable; + use JsonException; use WC_Order; class CreateOrderAction @@ -27,26 +36,21 @@ class CreateOrderAction private const ALLOWED_PRODUCT_TYPES = ['variation', 'simple']; private CdekApi $api; + private CoreApi $coreApi; /** - * @throws \Cdek\Exceptions\RestApiInvalidRequestException|\Throwable|\JsonException + * @throws RestApiInvalidRequestException|Throwable|JsonException */ public function __invoke(int $orderId, int $attempt = 0, array $packages = null): array { $this->api = new CdekApi; + $this->coreApi = new CoreApi('common'); $order = wc_get_order($orderId); $postOrderData = OrderMetaData::getMetaByOrderId($orderId); - if (!empty($postOrderData['order_number']) || !empty($postOrderData['order_uuid'])) { - return [ - 'state' => false, - 'message' => esc_html__('Order already exists', 'cdekdelivery'), - ]; - } - $shippingMethod = CheckoutHelper::getOrderShippingMethod($order); $tariffCode = $shippingMethod->get_meta(MetaKeys::TARIFF_CODE) ?: - $shippingMethod->get_meta('tariff_code') ?: $postOrderData['tariff_id']; + $shippingMethod->get_meta('tariff_code') ?: $postOrderData['tariff_id']; $postOrderData = [ 'currency' => $order->get_currency() ?: 'RUB', 'tariff_code' => $tariffCode, @@ -55,45 +59,16 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) ]; try { - $param = $this->buildRequestData($order, $postOrderData); - $param['packages'] = $this->buildPackagesData($order, $postOrderData, $packages); - - $orderData = $this->api->createOrder($param); - - sleep(5); - - $cdekNumber = $this->getCdekOrderNumber($orderData['entity']['uuid']); - try { - $cdekStatuses = Helper::getCdekOrderStatuses($orderData['entity']['uuid']); - $actionOrderAvailable = Helper::getCdekActionOrderAvailable($cdekStatuses); - } catch (Exception $e) { - $cdekStatuses = []; - $actionOrderAvailable = true; - } - - $postOrderData['order_number'] = $cdekNumber ?? $orderData['entity']['uuid']; - $postOrderData['order_uuid'] = $orderData['entity']['uuid']; - OrderMetaData::updateMetaByOrderId($orderId, $postOrderData); - - ob_start(); - include(WP_PLUGIN_DIR.'/cdek/templates/admin/status_list.php'); - $cdekStatusesRender = ob_get_clean(); + return $this->sendOrderInfo($postOrderData, $this->coreApi->getOrderById($orderId)); + } catch (CdekClientException $e) { + if($e->getCode() === 404) { + return $this->createOrder($order, $postOrderData, $packages); + } - if (!empty($cdekNumber)) { - Note::send($orderId, sprintf(esc_html__(/* translators: 1: tracking number */ 'Tracking number: %1$s', - 'cdekdelivery'), - $cdekNumber), true); + throw $e; } - return [ - 'state' => true, - 'code' => $cdekNumber, - 'statuses' => $cdekStatusesRender, - 'available' => $actionOrderAvailable, - 'door' => Tariff::isTariffFromDoor($postOrderData['tariff_code']), - ]; - } catch (PhoneNotValidException $e) { Note::send($order->get_id(), sprintf(esc_html__(/* translators: 1: error message */ 'Cdek shipping error: %1$s', 'cdekdelivery'), @@ -117,6 +92,14 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) } } + /** + * @param WC_Order $order + * @param $postOrderData + * + * @return array + * @throws PhoneNotValidException + * @throws ShippingMethodNotFoundException + */ private function buildRequestData(WC_Order $order, $postOrderData): array { $countryCode = trim(($order->get_shipping_country() ?: $order->get_billing_country()) ?? 'RU'); @@ -381,5 +364,100 @@ private function getCdekOrderNumber(string $orderUuid, int $iteration = 1): ?str return $orderInfo['entity']['cdek_number'] ?? $this->getCdekOrderNumber($orderUuid, $iteration + 1); } + + private function getStatusList(bool $actionAvailable, array $statuses = []): string + { + $cdekStatuses = $statuses; + $actionOrderAvailable = $actionAvailable; + ob_start(); + include(WP_PLUGIN_DIR.'/cdek/templates/admin/status_list.php'); + return ob_get_clean(); + } + + /** + * @param $order + * @param $postOrderData + * @param $packages + * + * @return array + * @throws AuthException + * @throws CdekApiException + * @throws CdekClientException + * @throws CdekServerException + * @throws PhoneNotValidException + * @throws RestApiInvalidRequestException + * @throws ShippingMethodNotFoundException + * @throws JsonException + */ + private function createOrder($order, $postOrderData, $packages): array + { + $param = $this->buildRequestData($order, $postOrderData); + $param['packages'] = $this->buildPackagesData($order, $postOrderData, $packages); + + $orderData = $this->api->createOrder($param); + + sleep(5); + + $cdekNumber = $this->getCdekOrderNumber($orderData['entity']['uuid']); + + try { + $cdekStatuses = Helper::getCdekOrderStatuses($orderData['entity']['uuid']); + $actionOrderAvailable = Helper::getCdekActionOrderAvailable($cdekStatuses); + } catch (Exception $e) { + $cdekStatuses = []; + $actionOrderAvailable = true; + } + + $postOrderData['order_number'] = $cdekNumber ?? $orderData['entity']['uuid']; + $postOrderData['order_uuid'] = $orderData['entity']['uuid']; + OrderMetaData::updateMetaByOrderId($order->get_id(), $postOrderData); + + if (!empty($cdekNumber)) { + Note::send($order->get_id(), sprintf(esc_html__(/* translators: 1: tracking number */ 'Tracking number: %1$s', + 'cdekdelivery'), + $cdekNumber), true); + } + + return [ + 'state' => true, + 'code' => $cdekNumber, + 'statuses' => $this->getStatusList($actionOrderAvailable, $cdekStatuses), + 'available' => $actionOrderAvailable, + 'door' => Tariff::isTariffFromDoor($postOrderData['tariff_code']), + ]; + } + + /** + * @param $postOrderData + * @param $existOrder + * + * @return array + * @throws AuthException + * @throws JsonException + */ + private function sendOrderInfo($postOrderData, $existOrder): array + { + $cdekStatuses[] = $existOrder['status']; + + try { + $historyCdekStatuses = $this->coreApi->getHistory($existOrder['uuid']); + }catch (CdekApiException $e){ + $historyCdekStatuses = []; + } + + return [ + 'state' => true, + 'code' => $existOrder['track'], + 'statuses' => $this->getStatusList( + !empty($historyCdekStatuses), + array_merge( + $cdekStatuses, + $historyCdekStatuses, + ), + ), + 'available' => !empty($historyCdekStatuses), + 'door' => Tariff::isTariffFromDoor($postOrderData['tariff_code']), + ]; + } } } diff --git a/src/Actions/DispatchOrderAutomationAction.php b/src/Actions/DispatchOrderAutomationAction.php index 4d812d3..a10aa45 100644 --- a/src/Actions/DispatchOrderAutomationAction.php +++ b/src/Actions/DispatchOrderAutomationAction.php @@ -2,18 +2,30 @@ namespace Cdek\Actions; +use ActionScheduler_Lock; +use Cdek\CoreApi; use Cdek\Config; +use Cdek\Exceptions\AuthException; +use Cdek\Exceptions\CdekApiException; +use Cdek\Exceptions\CdekClientException; +use Cdek\Exceptions\CdekServerException; use Cdek\Exceptions\ShippingMethodNotFoundException; use Cdek\Helper; use Cdek\Helpers\CheckoutHelper; +use Cdek\Helpers\ScheduleLocker; use Cdek\Note; +use JsonException; use WC_Order; class DispatchOrderAutomationAction { + const LOCK_TYPE = 'cdek_dispatch_order_automation_lock'; /** - * @param int|WC_Order $orderId + * @param int|WC_Order $orderId + * + * @throws CdekApiException + * @throws JsonException */ public function __invoke($orderId, $postedData = null, ?WC_Order $originalOrder = null): void { @@ -31,7 +43,7 @@ public function __invoke($orderId, $postedData = null, ?WC_Order $originalOrder return; } - $actualShippingMethod = Helper::getActualShippingMethod($shipping->get_instance_id()); + $actualShippingMethod = Helper::getActualShippingMethod((int)$shipping->get_instance_id()); if ($actualShippingMethod->get_option('automate_orders') !== 'yes') { return; @@ -39,17 +51,42 @@ public function __invoke($orderId, $postedData = null, ?WC_Order $originalOrder $awaitingGateways = $actualShippingMethod->get_option('automate_wait_gateways', []); - if (!empty($awaitingGateways) && - in_array($order->get_payment_method(), $awaitingGateways, true) && - !$order->is_paid()) { + if ( + !empty($awaitingGateways) + && + in_array($order->get_payment_method(), $awaitingGateways, true) + && + !$order->is_paid() + ) { return; } - if (as_schedule_single_action(time() + 60 * 5, Config::ORDER_AUTOMATION_HOOK_NAME, [ - $order->get_id(), - 1, - ], 'cdekdelivery')) { - Note::send($order->get_id(), esc_html__('Created order automation task', 'cdekdelivery')); + $lock = ScheduleLocker::instance(); + $lockType = ScheduleLocker::LOCK_TYPE_AUTOMATION_ORDER . '_' . $order->get_id(); + + if ($lock->is_locked($lockType)) { + return; + } + + if (!$lock->set($lockType)) { + return; + } + + try { + (new CoreApi('common'))->getOrderById($orderId); + } catch (AuthException|CdekServerException $e) { + Note::send($orderId, $e->getCode() . ': ' . $e->getMessage(), true); + } catch (CdekClientException $e) { + if($e->getCode() === 404){ + if (as_schedule_single_action(time() + 60 * 5, Config::ORDER_AUTOMATION_HOOK_NAME, [ + $order->get_id(), + 1, + ], 'cdekdelivery')) { + Note::send($order->get_id(), esc_html__('Created order automation task', 'cdekdelivery')); + } + }else{ + Note::send($orderId, $e->getMessage(), true); + } } } } diff --git a/src/Actions/Schedule/ReindexOrders.php b/src/Actions/Schedule/ReindexOrders.php index 5a27ce8..001595d 100644 --- a/src/Actions/Schedule/ReindexOrders.php +++ b/src/Actions/Schedule/ReindexOrders.php @@ -17,6 +17,11 @@ class ReindexOrders extends TaskContract { + /** + * @throws CdekScheduledTaskException + * @throws CdekApiException + * @throws \JsonException + */ public function __construct(string $taskId) { parent::__construct($taskId); diff --git a/src/CdekApi.php b/src/CdekApi.php index 75f631f..601a1a9 100644 --- a/src/CdekApi.php +++ b/src/CdekApi.php @@ -13,6 +13,8 @@ use Cdek\Enums\BarcodeFormat; use Cdek\Exceptions\AuthException; use Cdek\Exceptions\CdekApiException; + use Cdek\Exceptions\CdekClientException; + use Cdek\Exceptions\CdekServerException; use Cdek\Exceptions\RestApiInvalidRequestException; use Cdek\Helpers\DBTokenStorage; use Cdek\Transport\HttpClient; @@ -65,7 +67,7 @@ private function getApiUrl(): string } /** - * @throws \Cdek\Exceptions\CdekApiException|\JsonException + * @throws CdekApiException|\JsonException */ final public function checkAuth(): bool { @@ -108,7 +110,7 @@ public function fetchToken(): string /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ final public function getOrder(string $uuid): array { @@ -120,8 +122,15 @@ final public function getOrder(string $uuid): array } /** - * @throws \Cdek\Exceptions\RestApiInvalidRequestException - * @throws \Cdek\Exceptions\CdekApiException + * @param array $params + * + * @return array + * @throws AuthException + * @throws CdekApiException + * @throws CdekClientException + * @throws CdekServerException + * @throws RestApiInvalidRequestException + * @throws \JsonException */ public function createOrder(array $params): array { @@ -141,7 +150,7 @@ public function createOrder(array $params): array } /** - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function getFileByLink(string $link): string { @@ -150,7 +159,7 @@ public function getFileByLink(string $link): string /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function createWaybill(string $orderUuid): array { @@ -164,7 +173,7 @@ public function createWaybill(string $orderUuid): array /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function createBarcode(string $orderUuid): array { @@ -186,7 +195,7 @@ public function createBarcode(string $orderUuid): array /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function getBarcode(string $uuid): array { @@ -199,7 +208,7 @@ public function getBarcode(string $uuid): array /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function getWaybill(string $uuid): array { @@ -212,7 +221,7 @@ public function getWaybill(string $uuid): array /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function deleteOrder($uuid): array { @@ -225,7 +234,7 @@ public function deleteOrder($uuid): array /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function calculateTariffList($deliveryParam): array { @@ -245,7 +254,7 @@ public function calculateTariffList($deliveryParam): array } /** - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException * @throws \JsonException */ public function calculateTariff($deliveryParam) @@ -269,7 +278,7 @@ public function calculateTariff($deliveryParam) /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function getCityCode(string $city, ?string $postcode): int { @@ -291,7 +300,7 @@ public function getCityCode(string $city, ?string $postcode): int } /** - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException * @throws \JsonException */ public function getOffices($filter) @@ -317,7 +326,7 @@ public function getOffices($filter) /** * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException */ public function callCourier($param): array { @@ -330,7 +339,7 @@ public function callCourier($param): array } /** - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException * @throws \JsonException */ public function courierInfo($uuid): array @@ -343,7 +352,7 @@ public function courierInfo($uuid): array } /** - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException * @throws \JsonException */ public function callCourierDelete($uuid): array diff --git a/src/CoreApi.php b/src/CoreApi.php index d7ef518..b0132d1 100644 --- a/src/CoreApi.php +++ b/src/CoreApi.php @@ -15,6 +15,7 @@ use Cdek\Helpers\CoreTokenStorage; use Cdek\Model\TaskOutputData; use Cdek\Transport\HttpClient; + use JsonException; final class CoreApi { @@ -27,7 +28,7 @@ public function __construct(string $tokenType = 'common') /** * @throws ShopRegistrationException - * @throws \JsonException + * @throws JsonException */ public function syncShop(string $authIntToken): string { @@ -60,7 +61,7 @@ public function syncShop(string $authIntToken): string /** * @throws AuthException - * @throws \JsonException + * @throws JsonException */ public function fetchShopTokens(string $authIntToken, string $shopId): array { @@ -83,8 +84,8 @@ public function fetchShopTokens(string $authIntToken, string $shopId): array * @throws CdekApiException * @throws CdekServerException * @throws CdekClientException - * @throws \JsonException - * @throws \Cdek\Exceptions\AuthException + * @throws JsonException + * @throws AuthException */ public function listTasks(?string $next = null): array { @@ -99,9 +100,42 @@ public function listTasks(?string $next = null): array } /** - * @throws \JsonException - * @throws \Cdek\Exceptions\AuthException - * @throws \Cdek\Exceptions\CdekApiException + * @throws CdekApiException + * @throws CdekServerException + * @throws CdekClientException + * @throws JsonException + * @throws AuthException + */ + public function getOrderById(int $orderId): array + { + return HttpClient::sendJsonRequest( + $this->getEndpoint("orders/$orderId"), + 'GET', + $this->getToken(), + )->data(); + } + + + /** + * @throws CdekApiException + * @throws JsonException + * @throws CdekClientException + * @throws AuthException + * @throws CdekServerException + */ + public function getHistory(int $orderId): array + { + return HttpClient::sendJsonRequest( + $this->getEndpoint("orders/$orderId/history"), + 'GET', + $this->getToken(), + )->data(); + } + + /** + * @throws JsonException + * @throws AuthException + * @throws CdekApiException */ private function getEndpoint(?string $path = null): string { @@ -115,9 +149,9 @@ private function getEndpoint(?string $path = null): string } /** - * @throws \Cdek\Exceptions\AuthException - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException + * @throws AuthException + * @throws CdekApiException + * @throws JsonException */ private function getToken(): string { @@ -131,11 +165,11 @@ private function getToken(): string } /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - * @throws \Cdek\Exceptions\AuthException - * @throws \Cdek\Exceptions\CdekServerException - * @throws \Cdek\Exceptions\CdekClientException + * @throws CdekApiException + * @throws JsonException + * @throws AuthException + * @throws CdekServerException + * @throws CdekClientException */ public function taskInfo(string $taskId, TaskOutputData $data): array { @@ -151,11 +185,11 @@ public function taskInfo(string $taskId, TaskOutputData $data): array } /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \Cdek\Exceptions\CdekServerException - * @throws \Cdek\Exceptions\CdekClientException - * @throws \JsonException - * @throws \Cdek\Exceptions\AuthException + * @throws CdekApiException + * @throws CdekServerException + * @throws CdekClientException + * @throws JsonException + * @throws AuthException */ public function putTaskResult(string $taskId, TaskOutputData $data): array { diff --git a/src/Helpers/ScheduleLocker.php b/src/Helpers/ScheduleLocker.php new file mode 100644 index 0000000..5a4540b --- /dev/null +++ b/src/Helpers/ScheduleLocker.php @@ -0,0 +1,20 @@ +getAll() + ); } public static function tryGetRequesterIp(): ?string