From 12851b2b3c3c5c5fda238bd4b127d7d45c8c8554 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 11:04:54 +0700 Subject: [PATCH 01/16] fix: DOM text reinterpreted as HTML --- src/Frontend/AdminOrder/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Frontend/AdminOrder/index.js b/src/Frontend/AdminOrder/index.js index 415b8e0f..73a8be4e 100644 --- a/src/Frontend/AdminOrder/index.js +++ b/src/Frontend/AdminOrder/index.js @@ -53,15 +53,14 @@ $(document).ready(() => { if (checkForm(packageData)) { packageList.push(packageData); - let packageInfo = ''; - packageInfo = `${__('Package', + let packageInfo = `${__('Package', 'cdekdelivery')} №${packageList.length} (${packageData.length}х${packageData.width}х${packageData.height}):`; packageData.items.forEach(function(item) { packageInfo += `${item.name} х${item.quantity}, `; }); - $('#package_list').append(`

${packageInfo.slice(0, -2)}

`); + $('#package_list').append($('

').text(packageInfo.slice(0, -2))); calculateQuantity(); cleanForm(); From a6f99bfb4e98ac36c08bf6d147991f9283290393 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 13:47:33 +0700 Subject: [PATCH 02/16] feat: replace admin rest with ajax --- src/Controllers/CallbackController.php | 38 ++++++++++ src/Controllers/SettingsController.php | 93 ++++++++++-------------- src/Frontend/AdminSettings/index.js | 98 +++++++++++++++----------- src/Loader.php | 7 +- src/UI/Admin.php | 8 +-- 5 files changed, 140 insertions(+), 104 deletions(-) create mode 100644 src/Controllers/CallbackController.php diff --git a/src/Controllers/CallbackController.php b/src/Controllers/CallbackController.php new file mode 100644 index 00000000..32ecda8f --- /dev/null +++ b/src/Controllers/CallbackController.php @@ -0,0 +1,38 @@ +get_param('command')) { + case 'tokens.refresh': + TokensSyncCommand::new()($request->get_json_params()); + break; + default: + return new WP_REST_Response(['state' => 'unknown command'], WP_Http::BAD_REQUEST); + } + + return new WP_REST_Response(['state' => 'OK'], WP_Http::ACCEPTED); + } + + public function __invoke(): void + { + register_rest_route(Config::DELIVERY_NAME, '/cb', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => [__CLASS__, 'handle'], + 'permission_callback' => [Tokens::class, 'checkIncomingRequest'], + ]); + } +} diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index b7fc5e74..de4219d5 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -11,85 +11,70 @@ use Cdek\Actions\FlushTokenCacheAction; use Cdek\CdekApi; - use Cdek\Commands\TokensSyncCommand; use Cdek\Config; - use Cdek\Helpers\Tokens; - use WP_Http; - use WP_REST_Request; - use WP_REST_Response; - use WP_REST_Server; class SettingsController { - public static function callback(WP_REST_Request $request): WP_REST_Response + public static function auth(): void { - switch ($request->get_param('command')) { - case 'tokens.refresh': - TokensSyncCommand::new()($request->get_json_params()); - break; - default: - return new WP_REST_Response(['state' => 'unknown command'], WP_Http::BAD_REQUEST); + check_ajax_referer(Config::DELIVERY_NAME); + + if (!current_user_can('manage_woocommerce')) { + wp_die(-2, 403); } - return new WP_REST_Response(['state' => 'OK'], WP_Http::ACCEPTED); - } + if ((new CdekApi)->checkAuth()) { + wp_send_json_success(); + } - public static function checkAuth(): WP_REST_Response - { - return new WP_REST_Response(['state' => (new CdekApi)->checkAuth()], WP_Http::OK); + wp_send_json_error(); } /** * @throws \Cdek\Exceptions\External\ApiException * @throws \Cdek\Exceptions\External\LegacyAuthException */ - public static function cities(WP_REST_Request $request): WP_REST_Response + public static function cities(): void { - return new WP_REST_Response( - (new CdekApi)->citySuggest($request->get_param('q'), get_option('woocommerce_default_country', 'RU')), - WP_Http::OK, + check_ajax_referer(Config::DELIVERY_NAME); + + if (!current_user_can('manage_woocommerce')) { + wp_die(-2, 403); + } + + /** @noinspection GlobalVariableUsageInspection */ + wp_send_json_success( + (new CdekApi)->citySuggest( + sanitize_text_field(wp_unslash($_GET['q'])), + get_option('woocommerce_default_country', 'RU'), + ), ); } - public static function resetCache(): WP_REST_Response + public static function cache(): void { + check_ajax_referer(Config::DELIVERY_NAME); + + if (!current_user_can('manage_woocommerce')) { + wp_die(-2, 403); + } + FlushTokenCacheAction::new()(); - return new WP_REST_Response(['state' => 'OK'], WP_Http::OK); + wp_send_json_success(); } public function __invoke(): void { - register_rest_route(Config::DELIVERY_NAME, '/cities', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => [__CLASS__, 'cities'], - 'permission_callback' => static fn() => current_user_can('manage_woocommerce'), - 'args' => [ - 'q' => [ - 'description' => esc_html__('Request', 'cdekdelivery'), - 'required' => true, - 'type' => 'string', - ], - ], - ]); - - register_rest_route(Config::DELIVERY_NAME, '/cb', [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [__CLASS__, 'callback'], - 'permission_callback' => [Tokens::class, 'checkIncomingRequest'], - ]); - - register_rest_route(Config::DELIVERY_NAME, '/check-auth', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => [__CLASS__, 'checkAuth'], - 'permission_callback' => static fn() => current_user_can('manage_woocommerce'), - ]); - - register_rest_route(Config::DELIVERY_NAME, '/flush-cache', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => [__CLASS__, 'resetCache'], - 'permission_callback' => static fn() => current_user_can('manage_woocommerce'), - ]); + if (!wp_doing_ajax()) { + return; + } + + $prefix = Config::DELIVERY_NAME; + + add_action("wp_ajax_$prefix-auth", [__CLASS__, 'auth']); + add_action("wp_ajax_$prefix-cities", [__CLASS__, 'cities']); + add_action("wp_ajax_$prefix-cache", [__CLASS__, 'cache']); } } } diff --git a/src/Frontend/AdminSettings/index.js b/src/Frontend/AdminSettings/index.js index 16de5a8a..6c160d33 100644 --- a/src/Frontend/AdminSettings/index.js +++ b/src/Frontend/AdminSettings/index.js @@ -4,52 +4,63 @@ import { debounce } from 'lodash'; import './styles/main.scss'; import { createRoot, render } from '@wordpress/element'; import { DeliveryPrice } from './components/DeliveryPrice'; +import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; + +apiFetch({ + url: addQueryArgs(ajaxurl, + { action: `${window.cdek.prefix}-auth`, _wpnonce: window.cdek.nonce }), +}) + .then(r => { + if (r.success) { + $('.token-wrong').remove(); + return Promise.resolve(); + } + + return Promise.reject(); + }) + .catch(() => $('form h1') + .after($('
') + .html($('

') + .text(__( + 'Error receiving token from CDEK API. Make sure the integration keys are correct', + 'cdekdelivery'))))); + +const suggest = debounce((q) => apiFetch({ + url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-cities`, _wpnonce: window.cdek.nonce, q, + }), +}).then(r => { + if (r.data.length === 0) { + $('.city-suggest') + .append('
' + + __('Nothing found', 'cdekdelivery') + '
'); + return; + } -$.getJSON(window.cdek_admin_settings.api.check_auth) - .done(() => $('.token-wrong').remove()) - .fail((jqxhr) => { - console.error(jqxhr); - $('p:contains(\'Custom Shipping Method for Cdek\')') - .after('
[CDEKDelivery] ' + __( - 'Error receiving token. Make sure the integration keys are correct', - 'cdekdelivery') + '
'); - }); - -const suggest = debounce((q) => { - $.getJSON(window.cdek_admin_settings.api.cities, { q }) - .done(r => { - if (r.length === 0) { - $('.city-suggest') - .append('
' + - __('Nothing found', 'cdekdelivery') + '
'); - return; - } - - $('.city-suggest__404').remove(); - $('.city-suggest__item').remove(); - - r.forEach(e => { - $('.city-suggest') - .append($('
') - .html(e.full_name) - .on('click', () => { - $('input#woocommerce_official_cdek_city') - .val(e.full_name.split(',', 2)[0]); - $('input#woocommerce_official_cdek_city_code') - .val(e.code); - $('.city-suggest').remove(); - })); - }); - }).fail(() => { - $('.city-suggest__404').remove(); - $('.city-suggest__item').remove(); + $('.city-suggest__404').remove(); + $('.city-suggest__item').remove(); + r.data.forEach(e => { $('.city-suggest') - .append('
' + - __('Temporal error, try again', 'cdekdelivery') + '
'); - }) - .always(() => $('.city-loader').remove()); -}, 900); + .append($('
') + .html(e.full_name) + .on('click', () => { + $('input#woocommerce_official_cdek_city') + .val(e.full_name.split(',', 2)[0]); + $('input#woocommerce_official_cdek_city_code') + .val(e.code); + $('.city-suggest').remove(); + })); + }); +}).catch(() => { + $('.city-suggest__404').remove(); + $('.city-suggest__item').remove(); + + $('.city-suggest') + .append('
' + + __('Temporal error, try again', 'cdekdelivery') + '
'); +}).finally(() => $('.city-loader').remove()), 900); $('input#woocommerce_official_cdek_city').on('input', function() { $('.city-suggest').remove(); @@ -58,6 +69,7 @@ $('input#woocommerce_official_cdek_city').on('input', function() { $(this) .after('
') .after(''); + suggest(this.value); }); diff --git a/src/Loader.php b/src/Loader.php index 090d3127..7d9ccec1 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -17,6 +17,7 @@ use Cdek\Actions\RecalculateShippingAction; use Cdek\Actions\SaveCustomCheckoutFieldsAction; use Cdek\Blocks\CheckoutMapBlock; + use Cdek\Controllers\CallbackController; use Cdek\Controllers\IntakeController; use Cdek\Controllers\OrderController; use Cdek\Controllers\SettingsController; @@ -198,15 +199,17 @@ public function __invoke(string $pluginMainFile): void add_filter('plugin_action_links_'.self::$pluginMainFile, [Admin::class, 'addPluginLinks']); add_filter('plugin_row_meta', [Admin::class, 'addPluginRowMeta'], 10, 2); - add_action('rest_api_init', new SettingsController); + add_action('rest_api_init', new CallbackController); add_action('rest_api_init', new OrderController); add_action('rest_api_init', new IntakeController); + add_action('admin_init', new SettingsController); + add_filter('woocommerce_hidden_order_itemmeta', [DataCleaner::class, 'hideMeta']); add_filter('woocommerce_checkout_fields', [CheckoutHelper::class, 'restoreFields'], 1090); add_action( 'woocommerce_shipping_methods', - static fn($methods) => array_merge($methods, [Config::DELIVERY_NAME => ShippingMethod::class]), + static fn(array $methods) => array_merge($methods, [Config::DELIVERY_NAME => ShippingMethod::class]), ); add_action('woocommerce_checkout_process', new CheckoutValidator); diff --git a/src/UI/Admin.php b/src/UI/Admin.php index 85cce4a6..ff212b85 100644 --- a/src/UI/Admin.php +++ b/src/UI/Admin.php @@ -62,11 +62,9 @@ public static function registerAdminScripts(): void } UI::enqueueScript('cdek-admin-settings', 'cdek-admin-settings', true); - wp_localize_script('cdek-admin-settings', 'cdek_admin_settings', [ - 'api' => [ - 'cities' => UI::buildRestUrl('/cities'), - 'check_auth' => UI::buildRestUrl('/check-auth'), - ], + wp_localize_script('cdek-admin-settings', 'cdek', [ + 'nonce' => wp_create_nonce(Config::DELIVERY_NAME), + 'prefix' => Config::DELIVERY_NAME, ]); } From 6512ae34f832b7b3741669661ff79c3a9c3ae72f Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 14:16:21 +0700 Subject: [PATCH 03/16] feat: extract auth error to admin settings render (backend only) --- src/Controllers/SettingsController.php | 16 ---------------- src/Frontend/AdminSettings/index.js | 19 ------------------- src/ShippingMethod.php | 16 ++++++++++++++++ src/UI/Admin.php | 8 +++++--- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index de4219d5..025bd216 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -15,21 +15,6 @@ class SettingsController { - public static function auth(): void - { - check_ajax_referer(Config::DELIVERY_NAME); - - if (!current_user_can('manage_woocommerce')) { - wp_die(-2, 403); - } - - if ((new CdekApi)->checkAuth()) { - wp_send_json_success(); - } - - wp_send_json_error(); - } - /** * @throws \Cdek\Exceptions\External\ApiException * @throws \Cdek\Exceptions\External\LegacyAuthException @@ -72,7 +57,6 @@ public function __invoke(): void $prefix = Config::DELIVERY_NAME; - add_action("wp_ajax_$prefix-auth", [__CLASS__, 'auth']); add_action("wp_ajax_$prefix-cities", [__CLASS__, 'cities']); add_action("wp_ajax_$prefix-cache", [__CLASS__, 'cache']); } diff --git a/src/Frontend/AdminSettings/index.js b/src/Frontend/AdminSettings/index.js index 6c160d33..37918364 100644 --- a/src/Frontend/AdminSettings/index.js +++ b/src/Frontend/AdminSettings/index.js @@ -7,25 +7,6 @@ import { DeliveryPrice } from './components/DeliveryPrice'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; -apiFetch({ - url: addQueryArgs(ajaxurl, - { action: `${window.cdek.prefix}-auth`, _wpnonce: window.cdek.nonce }), -}) - .then(r => { - if (r.success) { - $('.token-wrong').remove(); - return Promise.resolve(); - } - - return Promise.reject(); - }) - .catch(() => $('form h1') - .after($('
') - .html($('

') - .text(__( - 'Error receiving token from CDEK API. Make sure the integration keys are correct', - 'cdekdelivery'))))); - const suggest = debounce((q) => apiFetch({ url: addQueryArgs(ajaxurl, { action: `${window.cdek.prefix}-cities`, _wpnonce: window.cdek.nonce, q, diff --git a/src/ShippingMethod.php b/src/ShippingMethod.php index 1cf74c22..2cb11d4d 100644 --- a/src/ShippingMethod.php +++ b/src/ShippingMethod.php @@ -14,6 +14,7 @@ use Cdek\Contracts\ExceptionContract; use Cdek\Traits\SettingsFields; use Throwable; + use WC_Admin_Settings; use WC_Settings_API; use WC_Shipping_Method; @@ -170,6 +171,21 @@ public function __isset(string $key): bool return $this->get_option($key, self::DEFAULTS[$key] ?? null) !== (self::DEFAULTS[$key] ?? null); } + final public function admin_options(): void + { + if (!(new CdekApi)->checkAuth()) { + WC_Admin_Settings::add_error( + esc_html__( + 'Error receiving token from CDEK API. Make sure the integration keys are correct', + 'cdekdelivery', + ), + ); + WC_Admin_Settings::show_messages(); + } + + parent::admin_options(); + } + final public function calculate_shipping($package = []): void { try { diff --git a/src/UI/Admin.php b/src/UI/Admin.php index ff212b85..5ac89be8 100644 --- a/src/UI/Admin.php +++ b/src/UI/Admin.php @@ -13,6 +13,7 @@ use Cdek\Helpers\UI; use Cdek\Loader; use Cdek\Traits\CanBeCreated; + use WC_Admin_Settings; class Admin { @@ -55,9 +56,10 @@ public static function addPluginRowMeta(array $links, string $file): array public static function registerAdminScripts(): void { + global $current_section, $current_tab; + // Not on Settings page. - /** @noinspection GlobalVariableUsageInspection */ - if (!isset($_GET['tab']) || $_GET['tab'] !== 'shipping') { + if($current_section !== Config::DELIVERY_NAME || $current_tab !== 'shipping') { return; } @@ -70,7 +72,7 @@ public static function registerAdminScripts(): void public function __invoke(): void { - add_action('load-woocommerce_page_wc-settings', [__CLASS__, 'registerAdminScripts']); + add_action('woocommerce_settings_start', [__CLASS__, 'registerAdminScripts']); } } } From ddafbafabc4e971b6c1eb38538213c4df43f7ee4 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 14:53:00 +0700 Subject: [PATCH 04/16] fix: php7.4 comma --- src/CdekApi.php | 2 +- src/Frontend/AdminSettings/index.js | 117 +++++++++++++++------------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/CdekApi.php b/src/CdekApi.php index 4ee9d1d0..a25412d1 100644 --- a/src/CdekApi.php +++ b/src/CdekApi.php @@ -152,7 +152,7 @@ public function cityCodeGet(string $city, string $postcode): ?string */ private function cityCodeGetWithFallback( string $city, - ?string $postcode = null, + ?string $postcode = null ): ?string { try { $result = HttpClient::sendJsonRequest( diff --git a/src/Frontend/AdminSettings/index.js b/src/Frontend/AdminSettings/index.js index 37918364..85443534 100644 --- a/src/Frontend/AdminSettings/index.js +++ b/src/Frontend/AdminSettings/index.js @@ -7,67 +7,76 @@ import { DeliveryPrice } from './components/DeliveryPrice'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; -const suggest = debounce((q) => apiFetch({ - url: addQueryArgs(ajaxurl, { - action: `${window.cdek.prefix}-cities`, _wpnonce: window.cdek.nonce, q, - }), -}).then(r => { - if (r.data.length === 0) { +(() => { + const suggest = debounce((q) => apiFetch({ + url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-cities`, + _wpnonce: window.cdek.nonce, + q, + }), + }).then(r => { + if (r.data.length === 0) { + $('.city-suggest') + .append('
' + + __('Nothing found', 'cdekdelivery') + '
'); + return; + } + + $('.city-suggest__404').remove(); + $('.city-suggest__item').remove(); + + r.data.forEach(e => { + $('.city-suggest') + .append($('
') + .html(e.full_name) + .on('click', () => { + $('input#woocommerce_official_cdek_city') + .val(e.full_name.split(',', 2)[0]); + $('input#woocommerce_official_cdek_city_code') + .val(e.code); + $('.city-suggest').remove(); + })); + }); + }).catch(() => { + $('.city-suggest__404').remove(); + $('.city-suggest__item').remove(); + $('.city-suggest') .append('
' + - __('Nothing found', 'cdekdelivery') + '
'); - return; - } + __('Temporal error, try again', 'cdekdelivery') + ''); + }).finally(() => $('.city-loader').remove()), 900); - $('.city-suggest__404').remove(); - $('.city-suggest__item').remove(); + $('input#woocommerce_official_cdek_city').on('input', function() { + $('.city-suggest').remove(); + $('.city-loader').remove(); - r.data.forEach(e => { - $('.city-suggest') - .append($('
') - .html(e.full_name) - .on('click', () => { - $('input#woocommerce_official_cdek_city') - .val(e.full_name.split(',', 2)[0]); - $('input#woocommerce_official_cdek_city_code') - .val(e.code); - $('.city-suggest').remove(); - })); + $(this) + .after('
') + .after(''); + + suggest(this.value); }); -}).catch(() => { - $('.city-suggest__404').remove(); - $('.city-suggest__item').remove(); - - $('.city-suggest') - .append('
' + - __('Temporal error, try again', 'cdekdelivery') + '
'); -}).finally(() => $('.city-loader').remove()), 900); - -$('input#woocommerce_official_cdek_city').on('input', function() { - $('.city-suggest').remove(); - $('.city-loader').remove(); - - $(this) - .after('
') - .after(''); - - suggest(this.value); -}); - -const deliveryRulesInput = $( - 'input#woocommerce_official_cdek_delivery_price_rules'); - -if (deliveryRulesInput.length) { - const div = window.document.createElement('div'); - deliveryRulesInput.after(div); - if (typeof render === 'function') { - render(, div); - } else { - createRoot(div).render(); + + const deliveryRulesInput = $( + 'input#woocommerce_official_cdek_delivery_price_rules'); + + if (deliveryRulesInput.length) { + const div = window.document.createElement('div'); + deliveryRulesInput.after(div); + + if (createRoot !== undefined) { + const root = createRoot(div); + + if (root !== undefined && typeof root.render === 'function') { + root.render(); + } else { + render(, div); + } + } else { + render(, div); + } } -} -(() => { const banAttachmentCheckbox = $( '#woocommerce_official_cdek_services_ban_attachment_inspection'); const tryingOnCheckbox = $('#woocommerce_official_cdek_services_trying_on'); From da36ee56ea1e7821fa5c1769b718106215e52a18 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 15:03:23 +0700 Subject: [PATCH 05/16] fix: css assets version --- src/Helpers/UI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Helpers/UI.php b/src/Helpers/UI.php index d5048e1e..44b346eb 100644 --- a/src/Helpers/UI.php +++ b/src/Helpers/UI.php @@ -60,7 +60,7 @@ public static function enqueueScript( } if ($hasStyles) { - wp_enqueue_style($handle, Loader::getPluginUrl("build/$fileName.css"), [], Loader::getPluginVersion()); + wp_enqueue_style($handle, Loader::getPluginUrl("build/$fileName.css"), [], $script_asset['version']); } wp_set_script_translations($handle, 'cdekdelivery', Loader::getPluginPath('lang')); From 58ae2b1071dc434c72ef8538ad85cea908b212d3 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 15:43:22 +0700 Subject: [PATCH 06/16] feat: tariff translations --- src/Actions/CalculateDeliveryAction.php | 78 ++++++++++++++----------- src/Model/Tariff.php | 8 +-- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/Actions/CalculateDeliveryAction.php b/src/Actions/CalculateDeliveryAction.php index 96c43282..99a3c817 100644 --- a/src/Actions/CalculateDeliveryAction.php +++ b/src/Actions/CalculateDeliveryAction.php @@ -23,6 +23,7 @@ class CalculateDeliveryAction { use CanBeCreated; + private ShippingMethod $method; private array $rates = []; @@ -33,7 +34,7 @@ class CalculateDeliveryAction public function __invoke(array $package, int $instanceID, bool $addTariffsToOffice = true): array { $this->method = ShippingMethod::factory($instanceID); - $api = new CdekApi($instanceID); + $api = new CdekApi($instanceID); if (empty($this->method->city_code) || !$api->checkAuth()) { return []; @@ -43,12 +44,12 @@ public function __invoke(array $package, int $instanceID, bool $addTariffsToOffi 'from' => [ 'code' => $this->method->city_code, ], - 'to' => [ + 'to' => [ 'postal_code' => trim($package['destination']['postcode']), 'city' => trim($package['destination']['city']), 'address' => trim($package['destination']['city']), 'country_code' => trim($package['destination']['country']), - ] + ], ]; try { @@ -68,8 +69,6 @@ public function __invoke(array $package, int $instanceID, bool $addTariffsToOffi ]; } - $tariffList = $this->method->get_option('tariff_list'); - $priceRules = json_decode($this->method->delivery_price_rules, true) ?: [ 'office' => [['type' => 'percentage', 'to' => null, 'value' => 100]], 'door' => [['type' => 'percentage', 'to' => null, 'value' => 100]], @@ -107,7 +106,7 @@ static function ($carry, $item) use ($package) { foreach ($calcResult['tariff_codes'] as $tariff) { if (isset($this->rates[$tariff['tariff_code']]) || - !in_array((string)$tariff['tariff_code'], $tariffList ?: [], true)) { + !in_array((string)$tariff['tariff_code'], $this->method->tariff_list ?: [], true)) { continue; } @@ -124,11 +123,19 @@ static function ($carry, $item) use ($package) { continue; } + if ($maxDay < $minDay) { + $maxDay = $minDay; + } + $this->rates[$tariff['tariff_code']] = [ 'id' => sprintf('%s:%s', Config::DELIVERY_NAME, $tariff['tariff_code']), - 'label' => sprintf( - esc_html__('CDEK: %s, (%s-%s days)', 'cdekdelivery'), - Tariff::getNameByCode($tariff['tariff_code']), + 'label' => ($minDay === $maxDay) ? sprintf( + esc_html__('%s, (%s day)', 'cdekdelivery'), + Tariff::getName($tariff['tariff_code'], $tariff['tariff_name']), + $minDay, + ) : sprintf( + esc_html__('%s, (%s-%s days)', 'cdekdelivery'), + Tariff::getName($tariff['tariff_code'], $tariff['tariff_name']), $minDay, $maxDay, ), @@ -150,36 +157,36 @@ static function ($carry, $item) use ($package) { } } - $deliveryMethod = $this->method; - - return array_map(static function ($tariff) use ( + return array_map(function ($tariff) use ( $priceRules, $api, - $deliveryParam, - $deliveryMethod + $deliveryParam ) { $rule = Tariff::isToOffice($tariff['meta_data'][MetaKeys::TARIFF_CODE]) ? $priceRules['office'] : $priceRules['door']; - if (isset($rule['type']) && $rule['type'] === 'free') { - $tariff['cost'] = 0; - return $tariff; - } + if (isset($rule['type'])) { + if ($rule['type'] === 'free') { + $tariff['cost'] = 0; + + return $tariff; + } - if (isset($rule['type']) && $rule['type'] === 'fixed') { - $tariff['cost'] = max( - function_exists('wcml_get_woocommerce_currency_option') ? - apply_filters('wcml_raw_price_amount', $rule['value'], 'RUB') : $rule['value'], - 0, - ); + if ($rule['type'] === 'fixed') { + $tariff['cost'] = max( + function_exists('wcml_get_woocommerce_currency_option') ? + apply_filters('wcml_raw_price_amount', $rule['value'], 'RUB') : $rule['value'], + 0, + ); - return $tariff; + return $tariff; + } } $deliveryParam['tariff_code'] = $tariff['meta_data'][MetaKeys::TARIFF_CODE]; $deliveryParam['type'] = Tariff::getType($deliveryParam['tariff_code']); - $serviceList = Service::factory($deliveryMethod, $deliveryParam['tariff_code']); + $serviceList = Service::factory($this->method, $deliveryParam['tariff_code']); if (!empty($serviceList)) { $deliveryParam['services'] = array_merge($serviceList, $deliveryParam['services'] ?? []); @@ -193,16 +200,16 @@ function_exists('wcml_get_woocommerce_currency_option') ? $cost = $tariffInfo['total_sum']; - if (isset($rule['type']) && $rule['type'] === 'amount') { - $cost += $rule['value']; - } elseif (isset($rule['type']) && $rule['type'] === 'percentage') { - $cost *= $rule['value'] / 100; + if (isset($rule['type'])) { + if ($rule['type'] === 'amount') { + $cost += $rule['value']; + } elseif ($rule['type'] === 'percentage') { + $cost *= $rule['value'] / 100; + } } if (function_exists('wcml_get_woocommerce_currency_option')) { - $costTMP = apply_filters('wcml_raw_price_amount', $cost, 'RUB'); - $coef = $costTMP / $cost; - $cost /= $coef; + $cost /= apply_filters('wcml_raw_price_amount', $cost, 'RUB') / $cost; } $tariff['cost'] = max(ceil($cost), 0); @@ -217,11 +224,14 @@ private function getPackagesData(array $contents): array $lengthList = []; $widthList = []; $heightList = []; + + $dimensionsInMM = get_option('woocommerce_dimension_unit') === 'mm'; + foreach ($contents as $productGroup) { $quantity = $productGroup['quantity']; $weight = $productGroup['data']->get_weight(); - $dimensions = get_option('woocommerce_dimension_unit') === 'mm' ? [ + $dimensions = $dimensionsInMM ? [ (int)((int)$productGroup['data']->get_length() / 10), (int)((int)$productGroup['data']->get_width() / 10), (int)((int)$productGroup['data']->get_height() / 10), diff --git a/src/Model/Tariff.php b/src/Model/Tariff.php index 6bb681dd..5d1b6ed5 100644 --- a/src/Model/Tariff.php +++ b/src/Model/Tariff.php @@ -266,12 +266,8 @@ public static function isFromDoor(int $code): bool self::DATA[$code]['mode'] === self::DOOR_PICKUP; } - public static function getNameByCode(int $code): string + public static function getName(int $code, string $name): string { - if (!isset(self::DATA[$code])) { - throw new RuntimeException("Unknown tariff $code"); - } - $tariffNameEdit = ShippingMethod::factory()->tariff_name; if (!empty($tariffNameEdit)) { @@ -285,7 +281,7 @@ public static function getNameByCode(int $code): string } } - return self::DATA[$code]['name']; + return "CDEK: $name"; } public static function list(): array From 2860a0a32086da895c996604d531ccaf8a543b54 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 15:56:04 +0700 Subject: [PATCH 07/16] fix: empty tariff get answer --- src/Actions/CalculateDeliveryAction.php | 12 ++---------- src/CdekApi.php | 14 ++++++++++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Actions/CalculateDeliveryAction.php b/src/Actions/CalculateDeliveryAction.php index 99a3c817..2f0bdbda 100644 --- a/src/Actions/CalculateDeliveryAction.php +++ b/src/Actions/CalculateDeliveryAction.php @@ -50,6 +50,7 @@ public function __invoke(array $package, int $instanceID, bool $addTariffsToOffi 'address' => trim($package['destination']['city']), 'country_code' => trim($package['destination']['country']), ], + 'packages' => $this->getPackagesData($package['contents']), ]; try { @@ -59,9 +60,6 @@ public function __invoke(array $package, int $instanceID, bool $addTariffsToOffi // do nothing } - $deliveryParam['packages'] = $this->getPackagesData($package['contents']); - unset($deliveryParam['packages']['weight_orig_unit']); - if ($this->method->insurance) { $deliveryParam['services'][] = [ 'code' => 'INSURANCE', @@ -192,13 +190,7 @@ function_exists('wcml_get_woocommerce_currency_option') ? $deliveryParam['services'] = array_merge($serviceList, $deliveryParam['services'] ?? []); } - $tariffInfo = $api->calculateGet($deliveryParam); - - if (empty($tariffInfo)) { - return $tariff; - } - - $cost = $tariffInfo['total_sum']; + $cost = $api->calculateGet($deliveryParam) ?? $tariff['cost']; if (isset($rule['type'])) { if ($rule['type'] === 'amount') { diff --git a/src/CdekApi.php b/src/CdekApi.php index a25412d1..0f740ecb 100644 --- a/src/CdekApi.php +++ b/src/CdekApi.php @@ -83,7 +83,7 @@ public function barcodeGet(string $uuid): ?array * @throws ApiException * @throws LegacyAuthException */ - public function calculateGet(array $deliveryParam): array + public function calculateGet(array $deliveryParam): ?float { $request = [ 'type' => $deliveryParam['type'], @@ -94,12 +94,18 @@ public function calculateGet(array $deliveryParam): array 'services' => array_key_exists('services', $deliveryParam) ? $deliveryParam['services'] : [], ]; - return HttpClient::sendJsonRequest( + $resp = HttpClient::sendJsonRequest( "{$this->apiUrl}calculator/tariff", 'POST', $this->tokenStorage->getToken(), $request, )->json(); + + if (empty($resp['total_sum'])) { + return null; + } + + return (float)$resp['total_sum']; } /** @@ -210,8 +216,8 @@ public function citySuggest(string $q, string $country): array 'GET', $this->tokenStorage->getToken(), [ - 'name' => $q, - 'country_code' => $country, + 'name' => $q, + 'country_code' => $country, ], )->json(); } From b9b7a0e6a088e7f8f9e5a702ef619143c984fa7e Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 21:47:54 +0700 Subject: [PATCH 08/16] fix: order creation --- src/Actions/CalculateDeliveryAction.php | 11 +- src/Actions/IntakeCreateAction.php | 2 +- src/Actions/OrderCreateAction.php | 40 +--- src/Actions/OrderDeleteAction.php | 2 +- ...ProcessWoocommerceCreateShippingAction.php | 3 +- src/Blocks/CheckoutMapBlock.php | 44 ++-- src/CdekApi.php | 2 +- src/Config.php | 1 - src/Contracts/ExceptionContract.php | 7 +- src/Contracts/MetaModelContract.php | 5 + src/Controllers/OrderController.php | 9 +- src/CoreApi.php | 14 -- src/ExceptionHandler.php | 2 +- .../External/InvalidRequestException.php | 7 +- src/Frontend/AdminOrder/styles/main.scss | 2 +- .../CheckoutMapBlock/components/frontend.js | 5 +- src/Frontend/CheckoutMapShortcode/index.js | 5 +- src/Helpers/UI.php | 4 +- src/Helpers/WeightConverter.php | 79 +++---- src/Loader.php | 5 + src/Model/Order.php | 48 +++-- src/Model/ShippingItem.php | 57 +++-- src/Note.php | 2 +- src/Transport/HttpClient.php | 6 +- src/Transport/HttpResponse.php | 43 ++-- src/UI/AdminNotices.php | 25 ++- src/UI/CheckoutMap.php | 17 +- src/UI/MetaBoxes.php | 204 +++++------------- templates/admin/call_courier_form.php | 84 -------- templates/admin/create-order.php | 21 -- templates/admin/form_package.php | 38 ---- templates/admin/status_list.php | 35 --- templates/call_courier_form.php | 119 ++++++++++ templates/form_package.php | 43 ++++ templates/{admin => }/form_package_many.php | 39 ++-- templates/{admin => }/order_created.php | 50 ++--- templates/public/open-map.php | 17 -- templates/status_list.php | 42 ++++ 38 files changed, 542 insertions(+), 597 deletions(-) delete mode 100644 templates/admin/call_courier_form.php delete mode 100644 templates/admin/create-order.php delete mode 100644 templates/admin/form_package.php delete mode 100644 templates/admin/status_list.php create mode 100644 templates/call_courier_form.php create mode 100644 templates/form_package.php rename templates/{admin => }/form_package_many.php (71%) rename templates/{admin => }/order_created.php (56%) delete mode 100644 templates/public/open-map.php create mode 100644 templates/status_list.php diff --git a/src/Actions/CalculateDeliveryAction.php b/src/Actions/CalculateDeliveryAction.php index 2f0bdbda..4b2e5c2d 100644 --- a/src/Actions/CalculateDeliveryAction.php +++ b/src/Actions/CalculateDeliveryAction.php @@ -98,10 +98,6 @@ static function ($carry, $item) use ($package) { continue; } - if (isset($delivery['errors'])) { - continue; - } - foreach ($calcResult['tariff_codes'] as $tariff) { if (isset($this->rates[$tariff['tariff_code']]) || !in_array((string)$tariff['tariff_code'], $this->method->tariff_list ?: [], true)) { @@ -116,11 +112,6 @@ static function ($carry, $item) use ($package) { $maxDay = (int)$tariff['period_max'] + (int)$this->method->extra_day; $cost = (int)$tariff['delivery_sum']; - if ((!isset($officeData['city']) && Tariff::isFromOffice($tariff['tariff_code'])) || - (!isset($doorData['city']) && Tariff::isFromDoor($tariff['tariff_code']))) { - continue; - } - if ($maxDay < $minDay) { $maxDay = $minDay; } @@ -245,7 +236,7 @@ private function getPackagesData(array $contents): array $heightList[] = $dimensions[1]; $widthList[] = $dimensions[2]; - $weight = WeightConverter::getWeight($weight); + $weight = WeightConverter::applyFallback($weight); $totalWeight += $quantity * $weight; } diff --git a/src/Actions/IntakeCreateAction.php b/src/Actions/IntakeCreateAction.php index a581c492..43fef666 100644 --- a/src/Actions/IntakeCreateAction.php +++ b/src/Actions/IntakeCreateAction.php @@ -51,7 +51,7 @@ public function __invoke(int $orderId, array $data): ValidationResult $tariffId = $shipping->tariff ?: $order->tariff_id; - if (Tariff::isFromDoor($tariffId)) { + if (Tariff::isFromDoor((int)$tariffId)) { $orderNumber = $order->number; $param = $this->createRequestDataWithOrderNumber($data, $orderNumber); } else { diff --git a/src/Actions/OrderCreateAction.php b/src/Actions/OrderCreateAction.php index 5799a05e..3248fc1a 100644 --- a/src/Actions/OrderCreateAction.php +++ b/src/Actions/OrderCreateAction.php @@ -11,7 +11,6 @@ use Cdek\CdekApi; use Cdek\Config; - use Cdek\Contracts\ExceptionContract; use Cdek\CoreApi; use Cdek\Exceptions\CacheException; use Cdek\Exceptions\External\ApiException; @@ -32,7 +31,6 @@ use Cdek\Note; use Cdek\Traits\CanBeCreated; use Cdek\Validator\PhoneValidator; - use Exception; use Throwable; class OrderCreateAction @@ -64,30 +62,22 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) } $this->shipping = $shipping; - $this->tariff = $shipping->tariff ?: $this->order->tariff_id; + $this->tariff = (int)($shipping->tariff ?: $this->order->tariff_id); try { $coreApi = new CoreApi; $existingOrder = $coreApi->orderGet($orderId); - try { - $historyCdekStatuses = $coreApi->orderHistory($existingOrder['uuid']); - } catch (ExceptionContract $e) { - $historyCdekStatuses = []; - } + $this->order->uuid = $existingOrder['uuid']; + $this->order->number = $existingOrder['track']; + $this->order->save(); return [ 'state' => true, 'code' => $existingOrder['track'], - 'statuses' => $this->outputStatusList( - !empty($historyCdekStatuses), - array_merge( - [$existingOrder['status']], - $historyCdekStatuses, - ), - ), - 'available' => !empty($historyCdekStatuses), + 'statuses' => $this->outputStatusList(), + 'available' => !$this->order->isLocked(), 'door' => Tariff::isFromDoor($this->tariff), ]; } catch (CoreAuthException|ApiException|CacheException $e) { @@ -123,13 +113,11 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) } } - private function outputStatusList(bool $actionAvailable, array $statuses = []): string + private function outputStatusList(?array $statuses = null): string { - $cdekStatuses = $statuses; - $actionOrderAvailable = $actionAvailable; ob_start(); - include(Loader::getPluginPath('templates/admin/status_list.php')); + include(Loader::getTemplate('status_list')); return ob_get_clean(); } @@ -157,14 +145,6 @@ private function createOrder(array $packages): array $this->order->uuid = $orderData['entity']['uuid']; $this->order->save(); - try { - $cdekStatuses = $this->order->loadLegacyStatuses(); - $actionOrderAvailable = $this->order->isLocked(); - } catch (Exception $e) { - $cdekStatuses = []; - $actionOrderAvailable = true; - } - if (!empty($cdekNumber)) { Note::send( $this->order->id, @@ -181,8 +161,8 @@ private function createOrder(array $packages): array return [ 'state' => true, 'code' => $cdekNumber, - 'statuses' => $this->outputStatusList($actionOrderAvailable, $cdekStatuses), - 'available' => $actionOrderAvailable, + 'statuses' => $this->outputStatusList($orderData['entity']['statuses']), + 'available' => $this->order->isLocked(), 'door' => Tariff::isFromDoor($this->tariff), ]; } diff --git a/src/Actions/OrderDeleteAction.php b/src/Actions/OrderDeleteAction.php index 432d7136..fe47818c 100644 --- a/src/Actions/OrderDeleteAction.php +++ b/src/Actions/OrderDeleteAction.php @@ -93,7 +93,7 @@ public function __invoke(int $orderId): ValidationResult IntakeDeleteAction::new()($orderId); - return new ValidationResult(true, esc_html__('Order has been deleted.', 'cdekdelivery')); + return new ValidationResult(true, esc_html__('Waybill has been deleted.', 'cdekdelivery')); } } } diff --git a/src/Actions/ProcessWoocommerceCreateShippingAction.php b/src/Actions/ProcessWoocommerceCreateShippingAction.php index add6a62a..d624c513 100644 --- a/src/Actions/ProcessWoocommerceCreateShippingAction.php +++ b/src/Actions/ProcessWoocommerceCreateShippingAction.php @@ -24,11 +24,12 @@ public function __invoke(WC_Order_Item_Shipping $shippingItem): void return; } - if (!Tariff::isToOffice($shipping->tariff)) { + if (!Tariff::isToOffice((int)$shipping->tariff)) { return; } $shipping->office = CheckoutHelper::getValueFromCurrentSession('office_code'); + $shipping->save(); } } } diff --git a/src/Blocks/CheckoutMapBlock.php b/src/Blocks/CheckoutMapBlock.php index 9dcdec25..b5745723 100644 --- a/src/Blocks/CheckoutMapBlock.php +++ b/src/Blocks/CheckoutMapBlock.php @@ -14,7 +14,6 @@ use Automattic\WooCommerce\StoreApi\Schemas\V1\CheckoutSchema; use Cdek\CdekApi; use Cdek\Config; - use Cdek\Helper; use Cdek\Helpers\CheckoutHelper; use Cdek\Helpers\UI; use Cdek\Model\Order; @@ -68,12 +67,6 @@ public static function extend_cart_data(): array } /** @noinspection PhpUnused */ - public static function extend_checkout_data(): array - { - return [ - 'office_code' => null, - ]; - } public static function extend_cart_schema(): array { @@ -87,7 +80,15 @@ public static function extend_cart_schema(): array ]; } + public static function extend_checkout_data(): array + { + return [ + 'office_code' => null, + ]; + } + /** @noinspection PhpUnused */ + public static function extend_checkout_schema(): array { return [ @@ -119,20 +120,28 @@ public static function saveOrderData(WC_Order $order, WP_REST_Request $request): return; } - if (Tariff::isToOffice($shipping->tariff)) { + if (Tariff::isToOffice((int)$shipping->tariff)) { $shipping->office = $request['extensions'][Config::DELIVERY_NAME]['office_code']; } } + public function get_editor_script_handles(): array + { + return ['cdek-checkout-map-block-editor']; + } + public function get_name(): string { return Config::DELIVERY_NAME; } - public function initialize(): void + public function get_script_data(): array { - UI::enqueueScript('cdek-checkout-map-block-frontend', 'cdek-checkout-map-block-frontend', false, true); - UI::enqueueScript('cdek-checkout-map-block-editor', 'cdek-checkout-map-block', false, true); + return [ + 'lang' => (mb_strpos(get_user_locale(), 'en') === 0) ? 'eng' : 'rus', + 'apiKey' => ShippingMethod::factory()->yandex_map_api_key, + 'officeDeliveryModes' => Tariff::listOfficeDeliveryModes(), + ]; } public function get_script_handles(): array @@ -140,17 +149,10 @@ public function get_script_handles(): array return ['cdek-checkout-map-block-frontend']; } - public function get_editor_script_handles(): array - { - return ['cdek-checkout-map-block-editor']; - } - - public function get_script_data(): array + public function initialize(): void { - return [ - 'apiKey' => ShippingMethod::factory()->yandex_map_api_key, - 'officeDeliveryModes' => Tariff::listOfficeDeliveryModes(), - ]; + UI::enqueueScript('cdek-checkout-map-block-frontend', 'cdek-checkout-map-block-frontend', false, true); + UI::enqueueScript('cdek-checkout-map-block-editor', 'cdek-checkout-map-block', false, true); } } } diff --git a/src/CdekApi.php b/src/CdekApi.php index 0f740ecb..0bd7596e 100644 --- a/src/CdekApi.php +++ b/src/CdekApi.php @@ -57,7 +57,7 @@ public function barcodeCreate(string $orderUuid): ?string [ 'orders' => ['order_uuid' => $orderUuid], 'format' => BarcodeFormat::getByIndex( - $this->deliveryMethod->get_option( + (int)$this->deliveryMethod->get_option( 'barcode_format', 0, ), diff --git a/src/Config.php b/src/Config.php index 44e6ca07..d2cece13 100644 --- a/src/Config.php +++ b/src/Config.php @@ -11,7 +11,6 @@ class Config { - public const PLUGIN_NAME = 'CDEKDelivery'; public const DELIVERY_NAME = 'official_cdek'; public const ORDER_META_BOX_KEY = 'official_cdek_order'; public const ORDER_AUTOMATION_HOOK_NAME = 'cdekdelivery_automation'; diff --git a/src/Contracts/ExceptionContract.php b/src/Contracts/ExceptionContract.php index 7ac7a1e2..d657bb23 100644 --- a/src/Contracts/ExceptionContract.php +++ b/src/Contracts/ExceptionContract.php @@ -9,7 +9,7 @@ namespace Cdek\Contracts { - use Cdek\Config; + use Cdek\Loader; use Exception; abstract class ExceptionContract extends Exception @@ -21,7 +21,10 @@ abstract class ExceptionContract extends Exception public function __construct(?array $data = null) { $this->data = $data ?? []; - $this->message = '['.Config::PLUGIN_NAME.'] '.($this->message ?: 'Unknown error'); + $this->message = '['. + Loader::getPluginName(). + '] '. + ($this->message ?: esc_html__('Unknown error', 'cdekdelivery')); parent::__construct($this->message); } diff --git a/src/Contracts/MetaModelContract.php b/src/Contracts/MetaModelContract.php index e2b5f6e3..b5360b9b 100644 --- a/src/Contracts/MetaModelContract.php +++ b/src/Contracts/MetaModelContract.php @@ -12,6 +12,7 @@ abstract class MetaModelContract { protected const ALIASES = []; protected array $meta; + protected array $dirty = []; /** @noinspection MissingReturnTypeInspection */ public function __get(string $key) @@ -43,6 +44,10 @@ public function __get(string $key) public function __set(string $key, $value): void { $this->meta[$key] = $value; + + if(!in_array($key, $this->dirty, true)) { + $this->dirty[] = $key; + } } public function __isset(string $key): bool diff --git a/src/Controllers/OrderController.php b/src/Controllers/OrderController.php index bf074350..a42ad247 100644 --- a/src/Controllers/OrderController.php +++ b/src/Controllers/OrderController.php @@ -38,6 +38,11 @@ public static function createOrder(WP_REST_Request $request): WP_REST_Response ); } + /** + * @throws \Cdek\Exceptions\External\LegacyAuthException + * @throws \Cdek\Exceptions\External\ApiException + * @throws \Cdek\Exceptions\OrderNotFoundException + */ public static function deleteOrder(WP_REST_Request $request): WP_REST_Response { return new WP_REST_Response(OrderDeleteAction::new()($request->get_param('id'))->response(), WP_Http::OK); @@ -161,7 +166,7 @@ public function __invoke(): void 'id' => [ 'description' => esc_html__('CDEK Order ID', 'cdekdelivery'), 'required' => true, - 'type' => 'number', + 'type' => 'integer', ], ], ]); @@ -175,7 +180,7 @@ public function __invoke(): void 'id' => [ 'description' => esc_html__('CDEK Order ID', 'cdekdelivery'), 'required' => true, - 'type' => 'number', + 'type' => 'integer', ], ], ]); diff --git a/src/CoreApi.php b/src/CoreApi.php index c60d308b..5be2c4b2 100644 --- a/src/CoreApi.php +++ b/src/CoreApi.php @@ -88,20 +88,6 @@ private function getToken(): string return $token; } - /** - * @throws ApiException - * @throws CoreAuthException - * @throws \Cdek\Exceptions\CacheException - */ - public function orderHistory(int $orderId): array - { - return HttpClient::sendJsonRequest( - $this->getEndpoint("orders/$orderId/history"), - 'GET', - $this->getToken(), - )->data(); - } - /** * @throws ShopRegistrationException * @throws CoreAuthException diff --git a/src/ExceptionHandler.php b/src/ExceptionHandler.php index 50b5218e..19b0fd53 100644 --- a/src/ExceptionHandler.php +++ b/src/ExceptionHandler.php @@ -33,7 +33,7 @@ public static function handle(Throwable $e): void { } // WP_Error при выводе на экран съедает часть data 0 ошибки, поэтому оригинальную ошибку добавляем 1 - $error = new WP_Error('cdek_error', 'Error happened at CDEKDelivery'); + $error = new WP_Error('cdek_error', 'Error happened at ' . Loader::getPluginName()); $error->add($e->getKey(), $e->getMessage(), $e->getData()); wp_die($error, '', $e->getStatusCode()); diff --git a/src/Exceptions/External/InvalidRequestException.php b/src/Exceptions/External/InvalidRequestException.php index 5e151839..29d1fd80 100644 --- a/src/Exceptions/External/InvalidRequestException.php +++ b/src/Exceptions/External/InvalidRequestException.php @@ -13,12 +13,13 @@ class InvalidRequestException extends HttpClientException { protected string $key = 'http.validation'; - public function __construct(array $error) + public function __construct(array $errors, ?array $request = null) { - $this->message = $this->message ?: $error['message']; + $this->message = $this->message ?: esc_html__('Invalid API request', 'cdekdelivery'); parent::__construct([ - 'error' => $error['code'], + 'errors' => $errors, + 'request' => $request, ]); } } diff --git a/src/Frontend/AdminOrder/styles/main.scss b/src/Frontend/AdminOrder/styles/main.scss index ecb9832c..e6919c65 100644 --- a/src/Frontend/AdminOrder/styles/main.scss +++ b/src/Frontend/AdminOrder/styles/main.scss @@ -1,4 +1,3 @@ - #delete-order-btn { cursor: pointer; color: #b32d2e; @@ -88,6 +87,7 @@ z-index: 50; /*background-color: #e3e5e5;*/ position: absolute; + top: 0; width: 91%; height: 94%; opacity: 50%; diff --git a/src/Frontend/CheckoutMapBlock/components/frontend.js b/src/Frontend/CheckoutMapBlock/components/frontend.js index 52fb30e5..b66ee5de 100644 --- a/src/Frontend/CheckoutMapBlock/components/frontend.js +++ b/src/Frontend/CheckoutMapBlock/components/frontend.js @@ -10,7 +10,7 @@ import cdekWidget from '@cdek-it/widget'; export const Block = ({ checkoutExtensionData, extensions, cart, validation, }) => { - const { apiKey, officeDeliveryModes } = getSetting('official_cdek_data'); + const { apiKey, officeDeliveryModes, lang } = getSetting('official_cdek_data'); const [showMap, setShowMap] = useState(false); const [validationError, setValidationError] = useState(null); @@ -60,7 +60,8 @@ export const Block = ({ if (window.widget === undefined) { window.widget = new cdekWidget({ - apiKey: apiKey, + apiKey, + lang, debug: true, defaultLocation: shippingRates[0].destination.city, officesRaw: JSON.parse(points), diff --git a/src/Frontend/CheckoutMapShortcode/index.js b/src/Frontend/CheckoutMapShortcode/index.js index 86414df1..47e56f16 100644 --- a/src/Frontend/CheckoutMapShortcode/index.js +++ b/src/Frontend/CheckoutMapShortcode/index.js @@ -43,9 +43,9 @@ const onChoose = (_type, _tariff, address) => { const officeInfo = el.parent().children('.cdek-office-info'); if (officeInfo.length === 0) { el.before( - `
${address.name} - [${address.code}]
`); + `
${address.name}
`); } else { - officeInfo.html(`${address.name} - [${address.code}]`); + officeInfo.html(`${address.name}`); } if ($('.cdek-office-code').data('map-auto-close')) { widget.close(); @@ -104,6 +104,7 @@ $(document.body) apiKey: window.cdek.apiKey, popup: true, debug: true, + lang: el.data('lang'), defaultLocation: el.data('city'), officesRaw: points, hideDeliveryOptions: { diff --git a/src/Helpers/UI.php b/src/Helpers/UI.php index 44b346eb..09de8f4b 100644 --- a/src/Helpers/UI.php +++ b/src/Helpers/UI.php @@ -25,7 +25,9 @@ public static function buildRestUrl( $args['_wpnonce'] = wp_create_nonce('wp_rest'); - return add_query_arg($args, rest_url($prefix.$route)); + $url = add_query_arg($args, rest_url($prefix.$route)); + + return Loader::debug() ? $url . '&' . Config::MAGIC_KEY : $url; } public static function enqueueScript( diff --git a/src/Helpers/WeightConverter.php b/src/Helpers/WeightConverter.php index ccbe1a1d..f25d9323 100644 --- a/src/Helpers/WeightConverter.php +++ b/src/Helpers/WeightConverter.php @@ -10,36 +10,46 @@ namespace Cdek\Helpers { use Cdek\ShippingMethod; - use RuntimeException; class WeightConverter { + private const AVAILABLE_MEASUREMENTS + = [ + 'g' => 1, + 'kg' => 1000, + 'lbs' => 453.6, + 'oz' => 28.25, + ]; - private const G_INTO_KG = 1000; - private const G_INTO_LBS = 453.6; - private const G_INTO_OZ = 28.35; + final public static function isSupported(string $m): bool + { + return array_key_exists($m, self::AVAILABLE_MEASUREMENTS); + } + /** + * @param mixed $weight + * + * @noinspection MissingParameterTypeDeclarationInspection + */ final public static function getWeightInGrams($weight): int { - $weightWithFallback = self::getWeight($weight); - $measurement = get_option('woocommerce_weight_unit'); - switch ($measurement) { - case 'g': - return absint(ceil($weightWithFallback)); - case 'kg': - return self::convertToG($weightWithFallback, self::G_INTO_KG); - case 'lbs': - return self::convertToG($weightWithFallback, self::G_INTO_LBS); - case 'oz': - return self::convertToG($weightWithFallback, self::G_INTO_OZ); + $m = get_option('woocommerce_weight_unit'); + + if (!self::isSupported($m)) { + $m = 'g'; } - throw new RuntimeException('CDEKDelivery: The selected unit of measure is not found'); + + return absint(ceil(self::AVAILABLE_MEASUREMENTS[$m] * self::applyFallback($weight))); } - final public static function getWeight($weight): float + /** + * @param mixed $weight + * + * @noinspection MissingParameterTypeDeclarationInspection + */ + final public static function applyFallback($weight): float { - if (empty($weight) || - ShippingMethod::factory()->product_package_default_toggle) { + if (empty($weight) || ShippingMethod::factory()->product_package_default_toggle) { $defaultWeight = (float)str_replace( ',', '.', @@ -51,30 +61,21 @@ final public static function getWeight($weight): float return (float)$weight; } - private static function convertToG(float $weight, float $coefficient): int - { - return absint(ceil($weight * $coefficient)); - } - + /** + * @param mixed $weight + * + * @noinspection MissingParameterTypeDeclarationInspection + */ final public static function getWeightInWcMeasurement($weight): float { - $measurement = get_option('woocommerce_weight_unit'); - switch ($measurement) { - case 'g': - return $weight; - case 'kg': - return self::convertToMeasurement($weight, self::G_INTO_KG); - case 'lbs': - return self::convertToMeasurement($weight, self::G_INTO_LBS); - case 'oz': - return self::convertToMeasurement($weight, self::G_INTO_OZ); + $m = get_option('woocommerce_weight_unit'); + + if (!self::isSupported($m)) { + $m = 'g'; } - throw new RuntimeException('CDEKDelivery: The selected unit of measure is not found'); - } - private static function convertToMeasurement(int $weight, float $coefficient): float - { - return $weight / $coefficient; + + return $weight / self::AVAILABLE_MEASUREMENTS[$m]; } } } diff --git a/src/Loader.php b/src/Loader.php index 7d9ccec1..924de337 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -158,6 +158,11 @@ public static function upgrade($up, array $options = []): void } } + public static function getTemplate(string $name): string + { + return self::getPluginPath("templates/$name.php"); + } + public static function getPluginPath(?string $path = null): string { return plugin_dir_path(self::$pluginMainFilePath).($path !== null ? ltrim($path, DIRECTORY_SEPARATOR) : ''); diff --git a/src/Model/Order.php b/src/Model/Order.php index 9305b298..90d61a32 100644 --- a/src/Model/Order.php +++ b/src/Model/Order.php @@ -14,7 +14,6 @@ use Cdek\Exceptions\OrderNotFoundException; use DateTime; use InvalidArgumentException; - use RuntimeException; use WC_Order; use WP_Post; @@ -49,6 +48,7 @@ class Order extends MetaModelContract 'payment_method', 'items', 'billing_email', + 'shipping_total', ]; private const CHECKOUT_FIELDS = [ @@ -67,7 +67,6 @@ class Order extends MetaModelContract ]; private const META_KEY = 'order_data'; private WC_Order $order; - private array $meta; private ?bool $locked = null; /** @@ -78,20 +77,19 @@ class Order extends MetaModelContract */ public function __construct($order) { - $this->meta = $this->order->get_meta(self::META_KEY) ?: []; - - if($order instanceof WC_Order) { + if ($order instanceof WC_Order) { $this->order = $order; - return; - } + } else { + $orderSearch = wc_get_order($order); - $orderSearch = wc_get_order($order); + if ($orderSearch === false) { + throw new OrderNotFoundException; + } - if($orderSearch === false) { - throw new OrderNotFoundException; + $this->order = $orderSearch; } - $this->order = $orderSearch; + $this->meta = $this->order->get_meta(self::META_KEY) ?: []; } /** @noinspection MissingReturnTypeInspection */ @@ -122,7 +120,9 @@ public function __set(string $key, $value): void final public function clean(): void { - unset($this->meta['order_uuid'], $this->meta['order_number'], $this->meta['cdek_order_uuid'], $this->meta['cdek_order_waybill']); + unset( + $this->meta['number'], $this->meta['uuid'], $this->meta['order_uuid'], $this->meta['order_number'], $this->meta['cdek_order_uuid'], $this->meta['cdek_order_waybill'], + ); $this->order->update_meta_data(self::META_KEY, $this->meta); $this->order->save(); @@ -164,15 +164,25 @@ final public function isPaid(): bool return $this->order->is_paid(); } - final public function loadLegacyStatuses(): array + /** + * @throws \Cdek\Exceptions\OrderNotFoundException + * @throws \Cdek\Exceptions\External\ApiException + * @throws \Cdek\Exceptions\External\UnparsableAnswerException + * @throws \Cdek\Exceptions\External\LegacyAuthException + */ + final public function loadLegacyStatuses(?array $statuses = null): array { if (empty($this->uuid)) { - throw new RuntimeException('[CDEKDelivery] Не найден UUID заказа.'); + return []; } - $orderInfo = (new CdekApi)->orderGet($this->uuid); - if ($orderInfo->entity() === null) { - throw new RuntimeException('[CDEKDelivery] Статусы не найдены. Заказ не найден.'); + if($statuses === null){ + $orderInfo = (new CdekApi)->orderGet($this->uuid); + if ($orderInfo->entity() === null) { + throw new OrderNotFoundException; + } + + $statuses = $orderInfo->entity()['statuses']; } $statuses = array_map(static fn(array $st) @@ -180,9 +190,9 @@ final public function loadLegacyStatuses(): array 'time' => DateTime::createFromFormat('Y-m-d\TH:i:sO', $st['date_time']), 'name' => $st['name'], 'code' => $st['code'], - ], $orderInfo->entity()['statuses']); + ], $statuses); - $this->locked = $statuses[0]['code'] === 'CREATED' || $statuses[0]['code'] === 'INVALID'; + $this->locked = $statuses[0]['code'] !== 'CREATED' && $statuses[0]['code'] !== 'INVALID'; return $statuses; } diff --git a/src/Model/ShippingItem.php b/src/Model/ShippingItem.php index 4d6b749f..1fed8466 100644 --- a/src/Model/ShippingItem.php +++ b/src/Model/ShippingItem.php @@ -14,10 +14,11 @@ use Cdek\MetaKeys; use Cdek\ShippingMethod; use InvalidArgumentException; + use WC_Meta_Data; use WC_Order_Item_Shipping; /** - * @property int tariff + * @property string tariff * @property string|null $office * @property string $length * @property string $height @@ -54,18 +55,14 @@ public function __construct(WC_Order_Item_Shipping $wcShippingItem) } $this->originalItem = $wcShippingItem; - $this->instanceId = $wcShippingItem->get_data()['instance_id']; - $this->meta = $wcShippingItem->get_meta_data(); - } - - final public function getInstanceId(): int - { - return $this->instanceId; - } + $this->instanceId = (int)$wcShippingItem->get_data()['instance_id']; + $this->meta = []; - final public function getMethod(): ShippingMethod - { - return ShippingMethod::factory()($this->instanceId); + foreach ($wcShippingItem->get_meta_data() as $meta) { + assert($meta instanceof WC_Meta_Data); + $data = $meta->get_data(); + $this->meta[$data['key']] = $data['value']; + } } /** @noinspection MissingReturnTypeInspection */ @@ -78,25 +75,47 @@ public function __get(string $key) return parent::__get(self::FIELDS_MAPPER[$key]); } - final public function updateTotal(float $val): void + public function __set(string $key, $value): void { - $this->originalItem->set_total($val); + if (!array_key_exists($key, self::FIELDS_MAPPER)) { + parent::__set($key, $value); + } + + parent::__set(self::FIELDS_MAPPER[$key], $value); } - final public function updateName(string $val): void + final public function clean(): void { - $this->originalItem->set_name($val); + // Doing nothing, since we don't need to clean anything + } + + final public function getInstanceId(): int + { + return $this->instanceId; + } + + final public function getMethod(): ShippingMethod + { + return ShippingMethod::factory($this->instanceId); } final public function save(): void { - $this->originalItem->set_meta_data($this->meta); + foreach ($this->dirty as $key) { + $this->originalItem->add_meta_data($key, $this->meta[$key], true); + } + $this->dirty = []; $this->originalItem->save(); } - final public function clean(): void + final public function updateName(string $val): void { - // Doing nothing, since we don't need to clean anything + $this->originalItem->set_name($val); + } + + final public function updateTotal(float $val): void + { + $this->originalItem->set_total($val); } } } diff --git a/src/Note.php b/src/Note.php index 385255c1..dd2690b5 100644 --- a/src/Note.php +++ b/src/Note.php @@ -14,7 +14,7 @@ class Note public static function send(int $orderId, string $message, bool $notifyCustomer = false): void { - $note = '[CDEKDelivery] '.$message; + $note = '['.Loader::getPluginName().'] '.$message; $order = wc_get_order($orderId); $order->add_order_note($note, $notifyCustomer ? 1 : 0); $order->save(); diff --git a/src/Transport/HttpClient.php b/src/Transport/HttpClient.php index 61f6fd2a..46ace72e 100644 --- a/src/Transport/HttpClient.php +++ b/src/Transport/HttpClient.php @@ -48,11 +48,11 @@ public static function sendJsonRequest( $result = self::processRequest($url, $method, $config); if ($result->isServerError()) { - throw new HttpServerException($result->error()); + throw new HttpServerException($result->error() ?: ['plain' => $result->body()]); } if ($result->getStatusCode() === 422) { - throw new InvalidRequestException($result->error()['fields']); + throw new InvalidRequestException($result->error()['fields'], Loader::debug() ? $data : null); } if ($result->getStatusCode() === 404) { @@ -60,7 +60,7 @@ public static function sendJsonRequest( } if (!$result->missInvalidLegacyRequest()) { - throw new InvalidRequestException($result->legacyRequestErrors()); + throw new InvalidRequestException($result->legacyRequestErrors(), Loader::debug() ? $data : null); } if ($result->isClientError()) { diff --git a/src/Transport/HttpResponse.php b/src/Transport/HttpResponse.php index cbe6173e..2060ba88 100644 --- a/src/Transport/HttpResponse.php +++ b/src/Transport/HttpResponse.php @@ -30,8 +30,8 @@ public function __construct(int $statusCode, string $body, array $headers, strin $this->statusCode = $statusCode; $this->body = $body; $this->headers = $headers; - $this->url = $url; - $this->method = $method; + $this->url = $url; + $this->method = $method; } public function body(): string @@ -82,11 +82,11 @@ public function entity(): ?array } /** - * @throws \Cdek\Exceptions\External\UnparsableAnswerException + * @throws UnparsableAnswerException */ - public function related(): array + public function error(): ?array { - return $this->json()['related_entities'] ?? []; + return $this->json()['error'] ?? $this->legacyRequestErrors()[0] ?? null; } /** @@ -97,18 +97,6 @@ public function legacyRequestErrors(): array return $this->json()['requests'][0]['errors'] ?? []; } - /** - * @throws UnparsableAnswerException - */ - public function error(): ?array - { - return $this->json()['error'] - ?? - $this->legacyRequestErrors()[0] - ?? - null; - } - public function getHeaders(): array { return $this->headers; @@ -129,16 +117,17 @@ public function isServerError(): bool return $this->statusCode >= 500 && $this->statusCode < 600; } - /** - * @throws \Cdek\Exceptions\External\UnparsableAnswerException - */ public function missInvalidLegacyRequest(): bool { - if (empty($this->json()['requests'][0]['state'])) { + try { + if (empty($this->json()['requests'][0]['state'])) { + return true; + } + + return $this->json()['requests'][0]['state'] !== 'INVALID'; + } catch (UnparsableAnswerException $e) { return true; } - - return $this->json()['requests'][0]['state'] !== 'INVALID'; } /** @@ -148,5 +137,13 @@ public function nextCursor(): ?string { return $this->json()['cursor']['next'] ?? null; } + + /** + * @throws \Cdek\Exceptions\External\UnparsableAnswerException + */ + public function related(): array + { + return $this->json()['related_entities'] ?? []; + } } } diff --git a/src/UI/AdminNotices.php b/src/UI/AdminNotices.php index cf4d31d6..c92d12cd 100644 --- a/src/UI/AdminNotices.php +++ b/src/UI/AdminNotices.php @@ -11,11 +11,14 @@ namespace Cdek\UI { use Cdek\Config; + use Cdek\Helpers\WeightConverter; + use Cdek\Loader; use Cdek\Traits\CanBeCreated; class AdminNotices { use CanBeCreated; + private const AVAILABLE_MEASUREMENTS = ['g', 'kg', 'lbs', 'oz']; public static function weightUnitsConflict(): void @@ -27,17 +30,19 @@ public static function weightUnitsConflict(): void } $measurement = get_option('woocommerce_weight_unit'); - if (!in_array($measurement, self::AVAILABLE_MEASUREMENTS, true)) { - echo '

'.sprintf( - __( - "CDEKDelivery: The selected weight unit %s is not supported by this plugin.\nYou can use the default value for product dimensions.\nYou can also contact plugin support for more information.\nOtherwise, the unit of measurement will be automatically treated as grams.", - 'cdekdelivery', - ), - esc_html($measurement), - ). - - '

'; + + if (WeightConverter::isSupported($measurement)) { + return; } + + echo '

'.Loader::getPluginName().': '.sprintf( + __( + "The selected weight unit %s is not supported by this plugin.\nYou can use the default value for product dimensions.\nYou can also contact plugin support for more information.\nOtherwise, the unit of measurement will be automatically treated as grams.", + 'cdekdelivery', + ), + esc_html($measurement), + ).'

'; + } public function __invoke(): void diff --git a/src/UI/CheckoutMap.php b/src/UI/CheckoutMap.php index 2c0f89f4..b78c41f9 100644 --- a/src/UI/CheckoutMap.php +++ b/src/UI/CheckoutMap.php @@ -12,7 +12,6 @@ use Cdek\CdekApi; use Cdek\Config; use Cdek\Helpers\CheckoutHelper; - use Cdek\Loader; use Cdek\Model\Tariff; use Cdek\ShippingMethod; @@ -37,9 +36,17 @@ public function __invoke($shippingMethodCurrent): void $points = $city !== null ? $api->officeListRaw($city) : '[]'; - $mapAutoClose = ShippingMethod::factory()->map_auto_close; - - include Loader::getPluginPath('templates/public/open-map.php'); + echo '
'. + esc_html__('Choose pick-up', 'cdekdelivery'). + '
'; } private function isTariffDestinationCdekOffice($shippingMethodCurrent): bool @@ -57,7 +64,7 @@ private function isTariffDestinationCdekOffice($shippingMethodCurrent): bool $tariffCode = explode(':', $shippingMethodIdSelected[0])[1]; - return Tariff::isToOffice($tariffCode); + return Tariff::isToOffice((int)$tariffCode); } } } diff --git a/src/UI/MetaBoxes.php b/src/UI/MetaBoxes.php index 57c3b751..fed642d7 100644 --- a/src/UI/MetaBoxes.php +++ b/src/UI/MetaBoxes.php @@ -15,21 +15,16 @@ use Cdek\Helpers\UI; use Cdek\Loader; use Cdek\Model\Order; - use Cdek\Model\Tariff; use Cdek\Traits\CanBeCreated; - use Exception; use Throwable; class MetaBoxes { use CanBeCreated; - public static function registerMetaBoxes(string $_post_type, $post): void + /** @noinspection MissingParameterTypeDeclarationInspection */ + public static function createOrderMetaBox($post): void { - if (!$post || !OrderUtil::is_order($post, wc_get_order_types())) { - return; - } - $order = new Order($post); $shipping = $order->getShipping(); @@ -38,184 +33,101 @@ public static function registerMetaBoxes(string $_post_type, $post): void return; } - add_action('admin_enqueue_scripts', [__CLASS__, 'registerOrderScripts']); - - $selectedTariff = $shipping->tariff; - - if ($selectedTariff === 0) { - return; - } - - if (!(new CdekApi($shipping->getInstanceId()))->checkAuth()) { - add_meta_box( - Config::ORDER_META_BOX_KEY, Loader::getPluginName(), [__CLASS__, 'noAuthMetaBox'], - ['woocommerce_page_wc-orders', 'shop_order'], - 'side', - 'core', - ); - - return; + $items = []; + foreach ($order->items as $item) { + /** @noinspection OnlyWritesOnParameterInspection */ + $items[$item['product_id']] = ['name' => $item['name'], 'quantity' => $item['quantity']]; } - $settings = $shipping->getMethod(); - - $address = $settings->get_option('address'); + if ($order->number !== null) { + try { + $order->loadLegacyStatuses(); + } catch (Throwable $e) { + } - if ((empty($address) || !isset(json_decode($address, true)['country'])) && - Tariff::isFromDoor($selectedTariff)) { - add_meta_box( - Config::ORDER_META_BOX_KEY, - Loader::getPluginName(), - [__CLASS__, 'noAddressMetaBox'], - ['woocommerce_page_wc-orders', 'shop_order'], - 'side', - 'core', - ); - - return; + if ($order->isLocked() !== false) { + echo '

+ '.Loader::getPluginName().': '.esc_html__( + 'Editing the order is not available due to a change in the order status in the CDEK system', + 'cdekdelivery', + ).' +

'; + } } - $office = $settings->get_option('pvz_code'); - - if ((empty($office) || !isset(json_decode($office, true)['country'])) && - Tariff::isFromOffice($selectedTariff)) { - add_meta_box( - Config::ORDER_META_BOX_KEY, - Loader::getPluginName(), - [__CLASS__, 'noOfficeMetaBox'], - ['woocommerce_page_wc-orders', 'shop_order'], - 'side', - 'core', + try { + include Loader::getTemplate( + $shipping->getMethod()->has_packages_mode ? 'form_package_many' : 'form_package', ); - return; + include Loader::getTemplate('order_created'); + } catch (Throwable $e) { } - - add_meta_box( - Config::ORDER_META_BOX_KEY, - Loader::getPluginName(), - [__CLASS__, 'createOrderMetaBox'], - ['woocommerce_page_wc-orders', 'shop_order'], - 'side', - 'core', - ); - } - - public static function noAddressMetaBox(): void - { - $settings_page_url = admin_url('admin.php?page=wc-settings&tab=shipping§ion='.Config::DELIVERY_NAME); - $pluginName = Loader::getPluginName(); - echo '
-

'.esc_html__('Shipping address not specified', 'cdekdelivery').'

-

'.str_replace( - '', - '', - sprintf( - esc_html__(/* translators: %s: Name of the plugin */ - 'Select the correct sending address in the settings plugin named %s', - 'cdekdelivery', - ), - esc_html($pluginName), - ), - ).'

-
'; - } - - public static function noOfficeMetaBox(): void - { - $settings_page_url = admin_url('admin.php?page=wc-settings&tab=shipping§ion='.Config::DELIVERY_NAME); - $pluginName = Loader::getPluginName(); - echo '
-

'.esc_html__('Shipping address not specified', 'cdekdelivery').'

-

'.str_replace( - '', - '', - sprintf( - esc_html__(/* translators: %s: Name of the plugin */ - 'Select the correct sending address in the settings plugin named %s', - 'cdekdelivery', - ), - esc_html($pluginName), - ), - ).'

-
'; } public static function noAuthMetaBox(): void { - $settings_page_url = admin_url('admin.php?page=wc-settings&tab=shipping§ion='.Config::DELIVERY_NAME); - $pluginName = Loader::getPluginName(); echo '

'.esc_html__('Authorization failed', 'cdekdelivery').'

'.str_replace( '', - '', + '', sprintf( esc_html__(/* translators: %s: Name of the plugin */ 'Enter the correct client ID and secret key in the settings plugin named %s', 'cdekdelivery', ), - esc_html($pluginName), + esc_html(Loader::getPluginName()), ), ).'

'; } - public static function createOrderMetaBox($post): void - { - $order = new Order($post); - - $shipping = $order->getShipping(); + /** @noinspection MissingParameterTypeDeclarationInspection */ - if($shipping === null) { + public static function registerMetaBoxes(string $_post_type, $post): void + { + if (empty($post) || !OrderUtil::is_order($post, wc_get_order_types())) { return; } - $items = []; - foreach ($order->items as $item) { - /** @noinspection OnlyWritesOnParameterInspection */ - $items[$item['product_id']] = ['name' => $item['name'], 'quantity' => $item['quantity']]; - } - - $dateMin = gmdate('Y-m-d'); - $dateMax = gmdate('Y-m-d', strtotime($dateMin." +31 days")); + $order = new Order($post); - $hasPackages = $shipping->getMethod()->has_packages_mode; - $orderNumber = $order->number ?: null; - $orderUuid = $order->uuid ?: null; + $shipping = $order->getShipping(); - try { - $cdekStatuses = $order->loadLegacyStatuses(); - $actionOrderAvailable = $order->isLocked(); - } catch (Exception $e) { - $cdekStatuses = []; - $actionOrderAvailable = true; + if ($shipping === null) { + return; } - if (!$actionOrderAvailable) { - self::notAvailableEditOrderData(); + add_action('admin_enqueue_scripts', [__CLASS__, 'registerOrderScripts']); + + if ($shipping->tariff === 0) { + return; } - try { - $fromDoor = Tariff::isFromDoor($shipping->tariff ?: $order->tariff_id); - $courierNumber = $order->getIntake()->number; - $length = $shipping->length; - $height = $shipping->height; - $width = $shipping->width; + if (!(new CdekApi($shipping->getInstanceId()))->checkAuth()) { + add_meta_box( + Config::ORDER_META_BOX_KEY, + Loader::getPluginName(), + [__CLASS__, 'noAuthMetaBox'], + ['woocommerce_page_wc-orders', 'shop_order'], + 'side', + 'core', + ); - include Loader::getPluginPath('templates/admin/create-order.php'); - } catch (Throwable $e) { + return; } - } - public static function notAvailableEditOrderData(): void - { - echo '

- CDEKDelivery: '.esc_html__( - 'Editing the order is not available due to a change in the order status in the CDEK system', - 'cdekdelivery', - ).' -

'; + add_meta_box( + Config::ORDER_META_BOX_KEY, + Loader::getPluginName(), + [__CLASS__, 'createOrderMetaBox'], + ['woocommerce_page_wc-orders', 'shop_order'], + 'side', + 'core', + ); } public static function registerOrderScripts(): void diff --git a/templates/admin/call_courier_form.php b/templates/admin/call_courier_form.php deleted file mode 100644 index 7d319c0a..00000000 --- a/templates/admin/call_courier_form.php +++ /dev/null @@ -1,84 +0,0 @@ - - -
-
-
-

:

- -
-
-

:

- - - - - - -
-
- - - - - - - - -
-
-

- -
- - - - - -
- -
- - -
- - "> -
diff --git a/templates/admin/create-order.php b/templates/admin/create-order.php deleted file mode 100644 index ebe9c5fd..00000000 --- a/templates/admin/create-order.php +++ /dev/null @@ -1,21 +0,0 @@ - - - -
-
style="display: none" > -

- -

- -

-

- -

-

- -

-

- -

-
-
diff --git a/templates/admin/status_list.php b/templates/admin/status_list.php deleted file mode 100644 index 52e07679..00000000 --- a/templates/admin/status_list.php +++ /dev/null @@ -1,35 +0,0 @@ - -

- -

-
-
-
- -
-
-
-
-
- -
-
-
- -
- + +
+
+
+

:

+ +
+
+

:

+ + + + + + +
+
+ + + + + + + tariff ?: $order->tariff_id))) { ?> + +
+
+

+ +
+ + + + + +
+ +
+ + +
+ + id/courier")) ?>"> +
diff --git a/templates/form_package.php b/templates/form_package.php new file mode 100644 index 00000000..f06594cd --- /dev/null +++ b/templates/form_package.php @@ -0,0 +1,43 @@ + +
+
number)) { ?>style="display: none" > +

+ +

+ +

+

+ +

+

+ +

+

+ +

+
+
diff --git a/templates/admin/form_package_many.php b/templates/form_package_many.php similarity index 71% rename from templates/admin/form_package_many.php rename to templates/form_package_many.php index a3bded7e..2467db70 100644 --- a/templates/admin/form_package_many.php +++ b/templates/form_package_many.php @@ -1,28 +1,30 @@
style="display: none" number)): ?>style="display: none" >
-

№1

+

№1

@@ -49,15 +51,18 @@

- +

- +

- +

@@ -69,7 +74,8 @@

- +

@@ -77,7 +83,8 @@

diff --git a/templates/admin/order_created.php b/templates/order_created.php similarity index 56% rename from templates/admin/order_created.php rename to templates/order_created.php index 16f20890..6bda4848 100644 --- a/templates/admin/order_created.php +++ b/templates/order_created.php @@ -1,44 +1,40 @@ getIntake(); ?> +
style="display: none" number)) { ?>style="display: none" >
-

+

number) ?>

+ include 'status_list.php'; + ?>
-

"> + echo esc_url(UI::buildRestUrl("order/$order->id/waybill")) ?>"> "> + echo esc_url(UI::buildRestUrl("order/$order->id/barcode")) ?>"> + if (!$order->isLocked()) { ?>

@@ -48,16 +44,16 @@
style="display: none;" number)) { ?>style="display: none;" style="margin-top: 10px;" >

:

+ echo esc_html($intake->number) ?>

">id/courier/delete")) ?>">

@@ -69,14 +65,14 @@
+ if (!$order->isLocked()) { ?>
"> + echo esc_url(UI::buildRestUrl("order/$order->id/delete")) ?>">
diff --git a/templates/public/open-map.php b/templates/public/open-map.php deleted file mode 100644 index c5dea985..00000000 --- a/templates/public/open-map.php +++ /dev/null @@ -1,17 +0,0 @@ - -
-
- diff --git a/templates/status_list.php b/templates/status_list.php new file mode 100644 index 00000000..5b5b74b4 --- /dev/null +++ b/templates/status_list.php @@ -0,0 +1,42 @@ +loadLegacyStatuses() : $statuses; +} catch (Throwable $e) { + $statuses = []; +} + +if (empty($statuses)): ?> +

+ +
+
format('Y.m.d H:i')) ?>
+
+ +
+
+
+
+
+ +
format('Y.m.d H:i')) ?>
+
+
+ +
+ From 3c01e44778dce166a65209c2d02689916cbe5e04 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 20 Nov 2024 22:17:55 +0700 Subject: [PATCH 09/16] feat: change status list date format --- templates/status_list.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/status_list.php b/templates/status_list.php index 5b5b74b4..134c5d00 100644 --- a/templates/status_list.php +++ b/templates/status_list.php @@ -19,7 +19,7 @@ else: ?>
format('Y.m.d H:i')) ?>
+ echo esc_html($statuses[0]['time']->format('H:i d.m.Y')) ?>
@@ -31,7 +31,7 @@
format('Y.m.d H:i')) ?>
+ echo esc_html($status['time']->format('H:i d.m.Y')) ?>

From ea5eb4791539e221fca812c93ae9e74ea19cd977 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 11:36:11 +0700 Subject: [PATCH 10/16] fix: admin order box --- src/Actions/IntakeCreateAction.php | 105 +--- src/Actions/IntakeDeleteAction.php | 2 +- src/Actions/OrderCreateAction.php | 45 +- .../AdminOrderBox.php} | 52 +- src/Controllers/CallbackController.php | 16 +- src/Controllers/IntakeController.php | 191 +++++-- src/Controllers/OrderController.php | 297 +++++----- .../images => AdminOrder/img}/loader.svg | 0 src/Frontend/AdminOrder/img/preloader.gif | Bin 5854 -> 0 bytes src/Frontend/AdminOrder/index.js | 506 +++++++----------- src/Frontend/AdminOrder/styles/main.scss | 265 ++++----- src/Frontend/AdminSettings/img/loader.svg | 3 + src/Frontend/AdminSettings/styles/main.scss | 2 +- src/Helpers/UI.php | 12 +- src/Loader.php | 8 +- src/Model/Order.php | 16 +- src/Model/ShippingItem.php | 3 + src/UI/Admin.php | 6 +- templates/call_courier_form.php | 119 ---- templates/common.php | 51 ++ templates/create.php | 29 + templates/create_many.php | 57 ++ templates/form_package.php | 43 -- templates/form_package_many.php | 91 ---- templates/intake.php | 82 +++ templates/order.php | 62 +++ templates/order_created.php | 79 --- templates/status_list.php | 42 -- templates/statuses.php | 35 ++ 29 files changed, 1050 insertions(+), 1169 deletions(-) rename src/{UI/MetaBoxes.php => Blocks/AdminOrderBox.php} (72%) rename src/Frontend/{AdminSettings/images => AdminOrder/img}/loader.svg (100%) delete mode 100644 src/Frontend/AdminOrder/img/preloader.gif create mode 100644 src/Frontend/AdminSettings/img/loader.svg delete mode 100644 templates/call_courier_form.php create mode 100644 templates/common.php create mode 100644 templates/create.php create mode 100644 templates/create_many.php delete mode 100644 templates/form_package.php delete mode 100644 templates/form_package_many.php create mode 100644 templates/intake.php create mode 100644 templates/order.php delete mode 100644 templates/order_created.php delete mode 100644 templates/status_list.php create mode 100644 templates/statuses.php diff --git a/src/Actions/IntakeCreateAction.php b/src/Actions/IntakeCreateAction.php index 43fef666..733bf73b 100644 --- a/src/Actions/IntakeCreateAction.php +++ b/src/Actions/IntakeCreateAction.php @@ -16,7 +16,7 @@ use Cdek\Model\ValidationResult; use Cdek\Note; use Cdek\Traits\CanBeCreated; - use Cdek\Validator\IntakeValidator; + use DateTimeImmutable; class IntakeCreateAction { @@ -35,32 +35,39 @@ public function __construct() * @throws \Cdek\Exceptions\ShippingNotFoundException * @throws \Cdek\Exceptions\OrderNotFoundException */ - public function __invoke(int $orderId, array $data): ValidationResult + public function __invoke(Order $order, array $data): ValidationResult { - $validate = IntakeValidator::validate($data); - if (!$validate->state) { - return $validate; - } - - $order = new Order($orderId); $shipping = $order->getShipping(); if ($shipping === null) { throw new ShippingNotFoundException; } - $tariffId = $shipping->tariff ?: $order->tariff_id; + $method = $shipping->getMethod(); - if (Tariff::isFromDoor((int)$tariffId)) { - $orderNumber = $order->number; - $param = $this->createRequestDataWithOrderNumber($data, $orderNumber); - } else { - $validate = IntakeValidator::validatePackage($data); - if (!$validate->state) { - return $validate; - } + $param = [ + 'need_call' => $data['call'] === 'true', + 'intake_date' => (new DateTimeImmutable($data['date']))->format('Y-m-d'), + 'intake_time_from' => $data['from'], + 'intake_time_to' => $data['to'], + 'comment' => $data['comment'] ?? null, + ]; - $param = $this->createRequestData($data); + if (Tariff::isFromDoor((int)($shipping->tariff ?: $order->tariff_id))) { + $param['cdek_number'] = $order->number; + } else { + $param['name'] = $data['desc']; + $param['weight'] = $data['weight']; + $param['from_location'] = [ + 'address' => $method->address, + 'code' => $method->city_code, + ]; + $param['sender'] = [ + 'name' => $method->seller_name, + 'phones' => [ + 'number' => $method->seller_phone, + ], + ]; } $result = $this->api->intakeCreate($param); @@ -118,74 +125,18 @@ public function __invoke(int $orderId, array $data): ValidationResult $intake->save(); Note::send( - $orderId, + $order->id, sprintf( esc_html__(/* translators: 1: number of intake 2: uuid of intake*/ 'Intake has been created: Number: %1$s | Uuid: %2$s', 'cdekdelivery', ), - $courierInfo['entity']['intake_number'], + $courierInfo['intake_number'], $courierInfo['uuid'], ), ); - return new ValidationResult( - true, sprintf( - esc_html__(/* translators: %s: uuid of application*/ 'Intake number: %s', 'cdekdelivery'), - $courierInfo['intake_number'], - ), - ); - } - - private function createRequestDataWithOrderNumber(array $data, string $orderNumber): array - { - $param['cdek_number'] = $orderNumber; - $param['intake_date'] = $data['date']; - $param['intake_time_from'] = $data['starttime']; - $param['intake_time_to'] = $data['endtime']; - $param['comment'] = $data['comment']; - $param['sender'] = [ - 'name' => $data['name'], - 'phones' => [ - 'number' => $data['phone'], - ], - ]; - $param['from_location'] = [ - 'address' => $data['address'], - ]; - if ($data['need_call'] === "true") { - $param['need_call'] = true; - } else { - $param['need_call'] = false; - } - - return $param; - } - - private function createRequestData(array $data): array - { - $param['intake_date'] = $data['date']; - $param['intake_time_from'] = $data['starttime']; - $param['intake_time_to'] = $data['endtime']; - $param['name'] = $data['desc']; - $param['weight'] = $data['weight']; - $param['length'] = $data['length']; - $param['width'] = $data['width']; - $param['height'] = $data['height']; - $param['comment'] = $data['comment']; - $param['sender'] = [ - 'name' => $data['name'], - 'phones' => [ - 'number' => $data['phone'], - ], - ]; - $param['from_location'] = [ - 'address' => $data['address'], - ]; - - $param['need_call'] = $data['need_call'] === 'true'; - - return $param; + return new ValidationResult(true); } } } diff --git a/src/Actions/IntakeDeleteAction.php b/src/Actions/IntakeDeleteAction.php index 56bc2539..da9fcad9 100644 --- a/src/Actions/IntakeDeleteAction.php +++ b/src/Actions/IntakeDeleteAction.php @@ -49,7 +49,7 @@ public function __invoke(int $orderId): ValidationResult } try { - $intake = $this->api->intakeDelete($courierMeta['courier_uuid']); + $intake = $this->api->intakeDelete($courierMeta->uuid); } catch (InvalidRequestException $e) { if ($e->getData()[0]['code'] === 'v2_entity_has_final_status') { return new ValidationResult(true); diff --git a/src/Actions/OrderCreateAction.php b/src/Actions/OrderCreateAction.php index 3248fc1a..d6adc102 100644 --- a/src/Actions/OrderCreateAction.php +++ b/src/Actions/OrderCreateAction.php @@ -28,6 +28,7 @@ use Cdek\Model\Service; use Cdek\Model\ShippingItem; use Cdek\Model\Tariff; + use Cdek\Model\ValidationResult; use Cdek\Note; use Cdek\Traits\CanBeCreated; use Cdek\Validator\PhoneValidator; @@ -50,7 +51,7 @@ class OrderCreateAction * @throws \Cdek\Exceptions\ShippingNotFoundException * @throws \Cdek\Exceptions\OrderNotFoundException */ - public function __invoke(int $orderId, int $attempt = 0, array $packages = null): array + public function __invoke(int $orderId, int $attempt = 0, array $packages = null): ValidationResult { $this->api = new CdekApi; $this->order = new Order($orderId); @@ -73,19 +74,14 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) $this->order->number = $existingOrder['track']; $this->order->save(); - return [ - 'state' => true, - 'code' => $existingOrder['track'], - 'statuses' => $this->outputStatusList(), - 'available' => !$this->order->isLocked(), - 'door' => Tariff::isFromDoor($this->tariff), - ]; + return new ValidationResult(true); } catch (CoreAuthException|ApiException|CacheException $e) { //Do nothing } try { - return $this->createOrder($packages); + $this->createOrder($packages); + return new ValidationResult(true); } catch (InvalidPhoneException $e) { Note::send( $this->order->id, @@ -95,10 +91,7 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) ), ); - return [ - 'state' => false, - 'message' => $e->getMessage(), - ]; + return new ValidationResult(false, $e->getMessage()); } catch (Throwable $e) { if ($attempt < 1 || $attempt > 5) { throw $e; @@ -106,22 +99,10 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) wp_schedule_single_event(time() + 60 * 5, Config::ORDER_AUTOMATION_HOOK_NAME, [$orderId, $attempt + 1]); - return [ - 'state' => false, - 'message' => $e->getMessage(), - ]; + return new ValidationResult(false, $e->getMessage()); } } - private function outputStatusList(?array $statuses = null): string - { - ob_start(); - - include(Loader::getTemplate('status_list')); - - return ob_get_clean(); - } - /** * @throws LegacyAuthException * @throws ApiException @@ -130,7 +111,7 @@ private function outputStatusList(?array $statuses = null): string * @throws InvalidPhoneException * @throws InvalidRequestException */ - private function createOrder(array $packages): array + private function createOrder(array $packages): void { $param = $this->buildRequestData(); $param['packages'] = $this->buildPackagesData($packages); @@ -157,14 +138,6 @@ private function createOrder(array $packages): array true, ); } - - return [ - 'state' => true, - 'code' => $cdekNumber, - 'statuses' => $this->outputStatusList($orderData['entity']['statuses']), - 'available' => $this->order->isLocked(), - 'door' => Tariff::isFromDoor($this->tariff), - ]; } /** @@ -308,7 +281,7 @@ private function buildPackagesData(array $packages = null): array 'id' => $product->get_id(), 'name' => $product->get_name(), 'weight' => $product->get_weight(), - 'quantity' => $el['quantity'], + 'quantity' => $el['qty'], 'price' => $product->get_price(), ]; }, $package['items']); diff --git a/src/UI/MetaBoxes.php b/src/Blocks/AdminOrderBox.php similarity index 72% rename from src/UI/MetaBoxes.php rename to src/Blocks/AdminOrderBox.php index fed642d7..cef9646f 100644 --- a/src/UI/MetaBoxes.php +++ b/src/Blocks/AdminOrderBox.php @@ -7,7 +7,7 @@ defined('ABSPATH') or exit; } -namespace Cdek\UI { +namespace Cdek\Blocks { use Automattic\WooCommerce\Utilities\OrderUtil; use Cdek\CdekApi; @@ -18,14 +18,18 @@ use Cdek\Traits\CanBeCreated; use Throwable; - class MetaBoxes + class AdminOrderBox { use CanBeCreated; /** @noinspection MissingParameterTypeDeclarationInspection */ - public static function createOrderMetaBox($post): void + public static function createOrderMetaBox($post, array $meta = []): void { - $order = new Order($post); + if ($post instanceof Order) { + $order = $post; + } else { + $order = new Order($post); + } $shipping = $order->getShipping(); @@ -33,36 +37,22 @@ public static function createOrderMetaBox($post): void return; } - $items = []; - foreach ($order->items as $item) { - /** @noinspection OnlyWritesOnParameterInspection */ - $items[$item['product_id']] = ['name' => $item['name'], 'quantity' => $item['quantity']]; - } + include Loader::getTemplate('common'); - if ($order->number !== null) { - try { - $order->loadLegacyStatuses(); - } catch (Throwable $e) { - } + if ($order->number === null) { + include Loader::getTemplate( + $shipping->getMethod()->has_packages_mode ? 'create_many' : 'create', + ); - if ($order->isLocked() !== false) { - echo '

- '.Loader::getPluginName().': '.esc_html__( - 'Editing the order is not available due to a change in the order status in the CDEK system', - 'cdekdelivery', - ).' -

'; - } + return; } try { - include Loader::getTemplate( - $shipping->getMethod()->has_packages_mode ? 'form_package_many' : 'form_package', - ); - - include Loader::getTemplate('order_created'); + $order->loadLegacyStatuses(); } catch (Throwable $e) { } + + include Loader::getTemplate('order'); } public static function noAuthMetaBox(): void @@ -97,16 +87,12 @@ public static function registerMetaBoxes(string $_post_type, $post): void $shipping = $order->getShipping(); - if ($shipping === null) { + if ($shipping === null || $shipping->tariff === null) { return; } add_action('admin_enqueue_scripts', [__CLASS__, 'registerOrderScripts']); - if ($shipping->tariff === 0) { - return; - } - if (!(new CdekApi($shipping->getInstanceId()))->checkAuth()) { add_meta_box( Config::ORDER_META_BOX_KEY, @@ -132,7 +118,7 @@ public static function registerMetaBoxes(string $_post_type, $post): void public static function registerOrderScripts(): void { - UI::enqueueScript('cdek-admin-create-order', 'cdek-create-order', true); + UI::enqueueScript('cdek-admin-create-order', 'cdek-create-order', true, false, true); } public function __invoke(): void diff --git a/src/Controllers/CallbackController.php b/src/Controllers/CallbackController.php index 32ecda8f..ee90ef4a 100644 --- a/src/Controllers/CallbackController.php +++ b/src/Controllers/CallbackController.php @@ -16,20 +16,20 @@ class CallbackController { public static function handle(WP_REST_Request $request): WP_REST_Response { - switch ($request->get_param('command')) { - case 'tokens.refresh': - TokensSyncCommand::new()($request->get_json_params()); - break; - default: - return new WP_REST_Response(['state' => 'unknown command'], WP_Http::BAD_REQUEST); + $command = $request->get_param('command'); + + if ($command === 'tokens.refresh') { + TokensSyncCommand::new()($request->get_json_params()); + + return new WP_REST_Response(null, WP_Http::ACCEPTED); } - return new WP_REST_Response(['state' => 'OK'], WP_Http::ACCEPTED); + return new WP_REST_Response(null, WP_Http::BAD_REQUEST); } public function __invoke(): void { - register_rest_route(Config::DELIVERY_NAME, '/cb', [ + register_rest_route(Config::DELIVERY_NAME, '/', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [__CLASS__, 'handle'], 'permission_callback' => [Tokens::class, 'checkIncomingRequest'], diff --git a/src/Controllers/IntakeController.php b/src/Controllers/IntakeController.php index 09aa89e4..edd40426 100644 --- a/src/Controllers/IntakeController.php +++ b/src/Controllers/IntakeController.php @@ -1,4 +1,4 @@ -get_param('id'), DataCleaner::getData($request, [ - 'date', - 'starttime', - 'endtime', - 'desc', - 'name', - 'phone', - 'address', - 'comment', - 'weight', - 'length', - 'width', - 'height', - 'need_call', - ]))->response(), WP_Http::OK, + check_ajax_referer(Config::DELIVERY_NAME); + + if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + $id = (int)$_REQUEST['id']; + + try { + $body = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + wp_die($e->getMessage()); + } + + $val = rest_validate_object_value_from_schema($body, [ + 'date' => [ + 'description' => esc_html__('Courier waiting date', 'cdekdelivery'), + 'required' => true, + 'type' => 'string', + 'validate_callback' => static fn($param) + => checkdate( + (int)substr($param, 5, 2), + (int)substr($param, 8, 2), + (int)substr($param, 0, 4), + ), + ], + 'from' => [ + 'required' => true, + 'type' => 'string', + 'validate_callback' => static function ($param) { + $hours = (int)substr($param, 0, 2); + $minutes = (int)substr($param, 3, 2); + + return $hours >= 9 && $hours <= 19 && $minutes <= 59 && $minutes >= 0; + }, + ], + 'to' => [ + 'required' => true, + 'type' => 'string', + 'validate_callback' => static function ($param) { + $hours = (int)substr($param, 0, 2); + $minutes = (int)substr($param, 3, 2); + + return $hours >= 12 && $hours <= 22 && $minutes <= 59 && $minutes >= 0; + }, + ], + 'comment' => [ + 'required' => false, + 'type' => 'string', + ], + 'call' => [ + 'required' => true, + 'type' => 'boolean', + ], + ], 'intake'); + + if (is_wp_error($val)) { + wp_die($val); + } + + $order = new Order($id); + + $shipping = $order->getShipping(); + $tariff = $shipping !== null ? $shipping->tariff : null; + + if (Tariff::isFromOffice((int)($tariff ?: $order->tariff_id))) { + $val = rest_validate_object_value_from_schema($body, [ + 'weight' => [ + 'required' => true, + 'type' => 'integer', + ], + 'desc' => [ + 'required' => true, + 'type' => 'string', + ], + ], 'intake'); + + if (is_wp_error($val)) { + wp_die($val); + } + } + + try { + $result = IntakeCreateAction::new()($order, $body); + $messages = $result->state ? null : [$result->message]; + } catch (InvalidRequestException $e) { + $messages = array_map(static fn(array $el) => $el['message'], $e->getData()['errors']); + } catch (ExceptionContract $e) { + $messages = [$e->getMessage()]; + } + + AdminOrderBox::createOrderMetaBox( + $order, + ['errors' => $messages], ); + + wp_die(); } - public static function delete(WP_REST_Request $data): WP_REST_Response + /** @noinspection GlobalVariableUsageInspection */ + public static function delete(): void { - return new WP_REST_Response(IntakeDeleteAction::new()((int)$data->get_param('id'))->response(), - WP_Http::OK); + check_ajax_referer(Config::DELIVERY_NAME); + + if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + $id = (int)$_REQUEST['id']; + + $result = IntakeDeleteAction::new()($id); + + if ($result->state) { + $meta = ['success' => [$result->message]]; + } else { + $meta = ['errors' => [$result->message]]; + } + + AdminOrderBox::createOrderMetaBox($id, $meta); + + wp_die(); } public function __invoke(): void { - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/courier', [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [__CLASS__, 'create'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('Order number', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - ], - ]); - - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/courier/delete', [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [__CLASS__, 'delete'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('Order number', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - ], - ]); + if (!wp_doing_ajax()) { + return; + } + + $prefix = Config::DELIVERY_NAME; + + add_action("wp_ajax_$prefix-intake_create", [__CLASS__, 'create']); + add_action("wp_ajax_$prefix-intake_delete", [__CLASS__, 'delete']); } } } diff --git a/src/Controllers/OrderController.php b/src/Controllers/OrderController.php index a42ad247..f6cbd6a2 100644 --- a/src/Controllers/OrderController.php +++ b/src/Controllers/OrderController.php @@ -1,4 +1,4 @@ -get_param('id'), - 0, - $request->get_param('packages'), - ), WP_Http::OK, - ); + check_ajax_referer(Config::DELIVERY_NAME); + + if (!current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + /** @noinspection GlobalVariableUsageInspection */ + $result = GenerateBarcodeAction::new()((new Order((int)wp_unslash($_GET['id'])))->uuid); + + if ($result['success']) { + wp_send_json_success($result['data']); + } + + wp_send_json_error($result); } /** - * @throws \Cdek\Exceptions\External\LegacyAuthException - * @throws \Cdek\Exceptions\External\ApiException - * @throws \Cdek\Exceptions\OrderNotFoundException + * @throws \Throwable + * @noinspection GlobalVariableUsageInspection */ - public static function deleteOrder(WP_REST_Request $request): WP_REST_Response + public static function create(): void { - return new WP_REST_Response(OrderDeleteAction::new()($request->get_param('id'))->response(), WP_Http::OK); + check_ajax_referer(Config::DELIVERY_NAME); + + if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + $id = (int)wp_unslash($_REQUEST['id']); + + try { + $body = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + wp_die($e->getMessage()); + } + + $val = rest_validate_array_value_from_schema($body, [ + 'required' => true, + 'type' => 'array', + 'minItems' => 1, + 'maxItems' => 255, + 'items' => [ + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => [ + 'length' => [ + 'required' => true, + 'type' => 'integer', + ], + 'width' => [ + 'required' => true, + 'type' => 'integer', + ], + 'height' => [ + 'required' => true, + 'type' => 'integer', + ], + 'items' => [ + 'required' => false, + 'type' => 'array', + 'minItems' => 1, + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'required' => true, + 'type' => 'integer', + ], + 'qty' => [ + 'required' => true, + 'type' => 'integer', + ], + ], + ], + ], + ], + ], + ], 'packages'); + + if (is_wp_error($val)) { + wp_die($val); + } + + try { + $result = OrderCreateAction::new()($id, 0, $body); + $messages = $result->state ? null : [$result->message]; + } catch (InvalidRequestException $e) { + $messages = array_map(static fn(array $el) => $el['message'], $e->getData()['errors']); + } catch (ExceptionContract $e) { + $messages = [$e->getMessage()]; + } + + AdminOrderBox::createOrderMetaBox( + $id, + ['errors' => $messages], + ); + + wp_die(); } /** - * @throws \Cdek\Exceptions\External\ApiException * @throws \Cdek\Exceptions\External\LegacyAuthException + * @throws \Cdek\Exceptions\External\ApiException + * @throws \Cdek\Exceptions\OrderNotFoundException + * @noinspection GlobalVariableUsageInspection */ - public static function getWaybill(WP_REST_Request $request): WP_REST_Response + public static function delete(): void { - return new WP_REST_Response( - GenerateWaybillAction::new()( - (new Order($request->get_param('id')))->uuid, - ), WP_Http::OK, - ); + check_ajax_referer(Config::DELIVERY_NAME); + + if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + $id = (int)$_REQUEST['id']; + + $result = OrderDeleteAction::new()($id); + + if ($result->state) { + $meta = ['success' => [$result->message]]; + } else { + $meta = ['errors' => [$result->message]]; + } + + AdminOrderBox::createOrderMetaBox($id, $meta); + + wp_die(); } /** * @throws \Cdek\Exceptions\External\ApiException * @throws \Cdek\Exceptions\External\LegacyAuthException + * @throws \Cdek\Exceptions\OrderNotFoundException */ - public static function getBarcode(WP_REST_Request $request): WP_REST_Response + public static function waybill(): void { - return new WP_REST_Response( - GenerateBarcodeAction::new()( - (new Order($request->get_param('id')))->uuid, - ), WP_Http::OK, - ); + check_ajax_referer(Config::DELIVERY_NAME); + + if (!current_user_can('edit_posts')) { + wp_die(-2, 403); + } + + try { + /** @noinspection GlobalVariableUsageInspection */ + $result = GenerateWaybillAction::new()((new Order((int)wp_unslash($_GET['id'])))->uuid); + + if ($result['success']) { + wp_send_json_success($result['data']); + } + + wp_send_json_error($result); + } catch (ExceptionContract $e) { + wp_send_json_error($e); + } } public function __invoke(): void { - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/create', [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [__CLASS__, 'createOrder'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('Order number', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - 'packages' => [ - 'description' => esc_html__('Packages', 'cdekdelivery'), - 'required' => true, - 'type' => 'array', - 'minItems' => 1, - 'maxItems' => 255, - 'items' => [ - 'type' => 'object', - 'additionalProperties' => false, - 'properties' => [ - 'length' => [ - 'description' => esc_html__('Packing length', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - 'width' => [ - 'description' => esc_html__('Packing width', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - 'height' => [ - 'description' => esc_html__('Packing height', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - 'items' => [ - 'description' => esc_html__('Products in packaging', 'cdekdelivery'), - 'required' => false, - 'type' => 'array', - 'minItems' => 1, - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => [ - 'description' => esc_html__('Product ID', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - 'quantity' => [ - 'description' => esc_html__( - 'Quantity of goods in packaging', - 'cdekdelivery', - ), - 'required' => true, - 'type' => 'integer', - ], - ], - ], - ], - ], - ], - ], - ], - ]); - - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/delete', [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => [__CLASS__, 'deleteOrder'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('Order number', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - ], - ]); - - - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/waybill', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => [__CLASS__, 'getWaybill'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('CDEK Order ID', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - ], - ]); - - register_rest_route(Config::DELIVERY_NAME, '/order/(?P\d+)/barcode', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => [__CLASS__, 'getBarcode'], - 'permission_callback' => static fn() => current_user_can('edit_posts'), - 'show_in_index' => true, - 'args' => [ - 'id' => [ - 'description' => esc_html__('CDEK Order ID', 'cdekdelivery'), - 'required' => true, - 'type' => 'integer', - ], - ], - ]); + if (!wp_doing_ajax()) { + return; + } + + $prefix = Config::DELIVERY_NAME; + + add_action("wp_ajax_$prefix-create", [__CLASS__, 'create']); + add_action("wp_ajax_$prefix-delete", [__CLASS__, 'delete']); + add_action("wp_ajax_$prefix-waybill", [__CLASS__, 'waybill']); + add_action("wp_ajax_$prefix-barcode", [__CLASS__, 'barcode']); } } } diff --git a/src/Frontend/AdminSettings/images/loader.svg b/src/Frontend/AdminOrder/img/loader.svg similarity index 100% rename from src/Frontend/AdminSettings/images/loader.svg rename to src/Frontend/AdminOrder/img/loader.svg diff --git a/src/Frontend/AdminOrder/img/preloader.gif b/src/Frontend/AdminOrder/img/preloader.gif deleted file mode 100644 index 828b22d51867097d6fded02c62a70425c1ca4789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5854 zcmeI$S5Q-HqsH-2LjpmHVju#6fK)+|-lUTRq^O_}sZs(WN(&v603q}$9RdNQDOCv| zpomCEstB82q^LBd*zmCT`R2^tb7s!Wx!B))7kgc=S@T}}=0DH73{eKkDh@$_AizsT z03MI;>+9?9?;jW#`1Pkp=ohPr{Z<6$iyh)7bC=Cy}(R;|!agX6@o!?OVznD^SJu%G_<`Rjad20Gt#+mAw< zTPH7$)6cQ@a?P|xjxRy3OfP6J+#NYS*Ra~^GjZL$cj&Lw%?C}xiIb{^ipVJzV+I* zy3v(zK$!O+prWUAxCoglY18|pe6-Z82%U$1>OLA^QmaYM*bu*N#@dGSli-myPLX*- zMVR5Fvm5-!sQQT0n-}YFpPPSDY1J+AVS9^^>HT7!R`M>NML$oKLta8hgFcu>XI_Pt zz3o>M=(fs|eNkhu#r?_nXxY_D+kxh%A0}#Ul`1kuDvsvs--A8P3VuIazpV+fq2dmy z>G%mp$;1(pZQ$)kTx}gBwmUa|979ezFc60Wzx6Q%}3Ld=^UQHpf9sDYsugg>i+mV1tk(SHf9(%XngjB7k_h!+5e zamR`N2q44RWG0cR$dvPxP|4c)Vp-{2?pan$!Y&biEz-&`sgB?6N;V>MojJR%2?WUz z=mxoZ;6?&(l7hSoOes(U&{{wUJ9_N_iMawpWfSTxqdICfyN0-l94r3=H1IE=1O5Ol z`rn`hEpG%mx#g#pfw3Rj)+=86&h*wl-r{ZBeF5*t;O6tw8}_m?B4rL)zHkHSRToQG z*mnjgSi2t>B}C*c)n$8`Je>HTxC0T3xAhjBVFcWCzJj@VE;KUIP)|HiIrt1IF;p9S zHdZd4Hz_Mj{ThSl1E~z|?5tc&zCeK_m9scojTp{fA^hk>RW-7<4E98*0n*r{0I#KQ z;cc@>01OPi85&Z2-DgjH0Un#jB25qF;DM3J z5J^8P9*_sN)R|ubDFp&MvX#1@^5k||>AK|MgrirJ{5MtT^1`wgoC`qdNbxWM0c-Kh z7@2HrQJn^O{brmAm{6R4rCBFC_3elS{l6KvZ&YtF=BCmmgMZ1^!k#6|#b}tsn=ptb zNu_YZ)6@tV0$GwdoO#zMgpw0Q!j#oih7s~kB2;~D!Z&L@Pk<;|U98a)y zC{|ved35xOsHVV0%tqY}GRuJ6nGDA99(3m$KJNssKi9fqyz)l9yN%CXN&FwUSrz9s ztc-dpY+#DBlVZk`y*AHYxM)y)KBri0Hz18#@qJSkUzKVW+RiROHREoe7CM7qYE=xp zHBOCbQmd_O=B4jjfXbymjg_7(CmSI%6yqDqH)19L{xm-pitiJb^HZ)W1YrznHa5_8wxFpizCP=~ zXHNc*pA6~k{>~xK8^NJ;khd-9@|`6OP8b>Pq7Z39;n~0~HkJhj;LFpLP@Y}+L|vHU zm!v2`IjEyF=lbXQATgrSV&q4>QwQN^cY1#;plO@puV~_HA3NbSbPKo@-*pA3i-QXU zX$$wjl2sNn^ax;)b#2_6bb`z~Z1G8Bv|EW(Zc~}mWzm0k zxBnAqE4q#({XMkZCx8d}$`IBafK{#08`XNNL~+a=_u+=P;DtNCsp8g&xs|l)n^j>9 zAK0WbWgVK)k^8MVsGP%qJ zX!Kc1R~;j7&H?h%)#+Q5ujc5)NcqR6MD4DU0#)v}g;%2==AxV*pr7BDFQO_pG zP#Ld+{_*^Grutu=KN!>f|2)@|`MVzL)7+dDT`AztjC~Gc)!d3=&m@DE-~M@6-}<@K zwLDs=;ihyhPz2>De%xd4ST5I{H2>Df;o-%zC%BX2{MoW^y3fWWa$=EnS%oD z@!+ZqZNjPP1G5WFdg2R_k`llHX1)p9R45Dwkb&b_L|9XkzdEa%S zpH;NPZ%K;zC}Ng-et>fhyba{a>@f(Mjm5nsyXJdRyImrMSxLqn z`w!*cSI2)&ni0VpPe*=Fb1Z)szwZC0th)0T%63dz&lEq2-=|K^LJEWuN?XDOQB-;K z_R_vIr^aANN#deGrczgvNBoy{F`x9Ngc*$VsC=V8ZI~@6V5htMRtqughoSc1~< zY%4=JOH)>C{8R>X#ar~7Gi1g4_{n93Yu`@ARf@z!uvG{#~=fOUuh zcD~uy3uf%I7P{pAZ5@ab+#PG=ho}e;2tSi%z$SNYe}WLNM9;`TpuWhYCd#fsi;*HN zow~u9ER(mQ#9|J}sj>GOI{c!{Ch5|yfAgnL6dUTWy&HhFB1&5mBz{Y9sp7TH@yw=k z&D9q|t;XVJ%juGXgt5*PQB4+XN4c<{Vc@4GWpyO>&)6FO2GMGh zf1T04jG;C-Le@5_jG2}DH@U1WOmyRwb zntL0nKTpi*48G4cfSA#m4)E6ry~C+&f1hdp3q(gD`2equXSDaBSp?MS9cxT4`4yYeR`iV}Z5VtF5^&~=B3Fn|-54Qh?$bEc z&nju6-QnZci0D+)jd~}MM6*f4gX<#?O5(*>mJ`hJ_`m`mof%(px+mNWC6{ovWyg+7^4!*m5QtX*Yyxp z*?Rml@7b?;1r`yN2yBIpvk~)cX&=IER-Y=PgVtZq^G}p9K;thIe$tEMry$e3-x+IH zTLifK>zAh&V>50HIRQ$kv9&~r+Pe-yc$)%2oEJ8!P$xZc#M~TF9LVUye9^Egq z4H9H6R}g`{ly+7GXJp;G6V6R&Rx4FqiO<8mm(4c!elNv6$BUDj)$d*QdVBleE zgDnFiSUi*CIYi6^h1X-rXKmIWvL~>!&1LnNr|LQ@jBUOPD45Q~v7ys58KBOI%D7PUym@~?5mA-@Gspt?uGD^5H5 z>&1+8Q>!Hpw)c`s(w)*#Gnt0C4!Z|tRSmCfjY#k5oWeS-vT|)UB^K|8RdCoBOgrnZ M6_VZ4006cB0^mW~Bme*a diff --git a/src/Frontend/AdminOrder/index.js b/src/Frontend/AdminOrder/index.js index 73a8be4e..a2f6df17 100644 --- a/src/Frontend/AdminOrder/index.js +++ b/src/Frontend/AdminOrder/index.js @@ -3,320 +3,224 @@ import { __ } from '@wordpress/i18n'; import $ from 'jquery'; import './styles/main.scss'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; $(document).ready(() => { - let packageList = []; - - checkOrderAvailable(); - - function checkOrderAvailable() { - const dataStatusAvailable = $('#cdek-status-block') - .data('status-available'); - if (dataStatusAvailable !== undefined && !dataStatusAvailable) { - $('#order_data') - .find('input[name="order_date"]') - .attr('disabled', true); - $('#order_data') - .find('input[name="order_date_hour"]') - .attr('disabled', true); - $('#order_data') - .find('input[name="order_date_minute"]') - .attr('disabled', true); - $('#order_data') - .find('select[name="customer_user"]') - .attr('disabled', true); - $('#order_data').find('a[class="edit_address"]').hide(); - } - } - - $('#selected_product').change(function() { - let productId = $('#selected_product').val(); - $('#product_' + productId).css('display', 'flex'); - }); - $('#save_package').click(function() { - $('#package_list').show(); - let packageData = {}; - packageData.length = $('input[name=package_length]').val(); - packageData.width = $('input[name=package_width]').val(); - packageData.height = $('input[name=package_height]').val(); - packageData.items = []; - $('.product_list').each(function(index, item) { - if ($(item).css('display') !== 'none') { - packageData.items.push({ - id: $(item).find('input[name=product_id]').val(), - name: $(item).find('input[type=text]').val(), - quantity: $(item).find('input[type=number]').val(), - }); - } - }); - - if (checkForm(packageData)) { - packageList.push(packageData); - - let packageInfo = `${__('Package', - 'cdekdelivery')} №${packageList.length} (${packageData.length}х${packageData.width}х${packageData.height}):`; - - packageData.items.forEach(function(item) { - packageInfo += `${item.name} х${item.quantity}, `; - }); - - $('#package_list').append($('

').text(packageInfo.slice(0, -2))); - - calculateQuantity(); - cleanForm(); - checkFullPackage(); - } - }); - + const packageList = []; let dataUrl = null; - $('#cdek-order-waybill, #cdek-order-barcode').click(function(e) { - e.preventDefault(); - e.stopPropagation(); - - const loader = $('#cdek-loader'); - + const revokeDataUrl = () => { if (dataUrl !== null) { URL.revokeObjectURL(dataUrl); } - - loader.show(); - - apiFetch({ method: 'GET', url: e.target.href }).then(resp => { - if (!resp.success) { - alert(resp.message); - return; - } - - const binaryString = window.atob(resp.data); - const uint8Array = new Uint8Array(binaryString.length); - for (let i = 0; i < binaryString.length; i++) { - uint8Array[i] = binaryString.charCodeAt(i); - } - dataUrl = window.URL.createObjectURL( - new Blob([uint8Array], { type: 'application/pdf' })); - - const a = window.document.createElement('a'); - a.target = '_blank'; - a.href = dataUrl; - window.document.body.appendChild(a); - a.click(); - a.remove(); - - window.document.addEventListener('beforeunload', - () => URL.revokeObjectURL(dataUrl)); - }) - .catch(e => console.error(e)) - .finally(() => loader.hide()); - }); - - $('#send_package').click(function(e) { - const loader = $('#cdek-loader'); - loader.show(); - - apiFetch({ - method: 'POST', url: e.target.dataset.action, data: { - packages: packageList, - }, - }) - .then(resp => { - if (!resp.state) { - $('#cdek-create-order-error').text(resp.message).show(); + }; + + const metaBox = $('#official_cdek_order') + .on('change', '.create select', + e => metaBox.find(`.item[data-id=${e.target.value}]`) + .attr('aria-hidden', 'false')) + .on('click', '.print', e => { + e.preventDefault(); + e.stopPropagation(); + + const loader = metaBox.find('.loader'); + loader.attr('aria-disabled', 'false'); + + revokeDataUrl(); + + apiFetch({ + method: 'GET', url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-${e.target.dataset.action}`, + _wpnonce: window.cdek.nonce, + id: e.target.dataset.id, + }), + }).then(resp => { + if (!resp.success) { + alert(resp.message); return; } - if (resp.door) { - $('#cdek-courier-result-block').hide(); - $('#cdek-order-courier').show(); + const binaryString = window.atob(resp.data); + const uint8Array = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + uint8Array[i] = binaryString.charCodeAt(i); } - $('#cdek-create-order-form').hide(); - $('#cdek-order-number').html(`№ ${resp.code}`); - $('#cdek-order-number-input').val(resp.code); - $('#cdek-info-order').show(); + dataUrl = window.URL.createObjectURL( + new Blob([uint8Array], { type: 'application/pdf' })); + + const a = window.document.createElement('a'); + a.target = '_blank'; + a.href = dataUrl; + window.document.body.appendChild(a); + a.click(); + a.remove(); + + window.document.addEventListener('beforeunload', + () => URL.revokeObjectURL(dataUrl)); }) - .catch(e => console.error(e)) - .finally(() => loader.hide()); - }); - - function checkFullPackage() { - let option = $('#selected_product option'); - if (option.length === 1) { - $('#setting_block').hide(); - $('#save_package_btn_block').hide(); - $('#send_package_btn_block').show(); - } - } - - function checkForm(packageData) { - if (packageData.length === '') { - alert(__('Packing length not specified', 'cdekdelivery')); - return false; - } - if (packageData.width === '') { - alert(__('Packing width not specified', 'cdekdelivery')); - return false; - } - if (packageData.height === '') { - alert(__('Packing height not specified', 'cdekdelivery')); - return false; - } - if (packageData.items.length === 0) { - alert(__('Products not added to packaging', 'cdekdelivery')); - return false; - } - return true; - } - - function calculateQuantity() { - $('.product_list').each(function(index, item) { - if ($(item).css('display') !== 'none') { - let max = $(item).find('input[type=number]').attr('max'); - let current = max - $(item).find('input[type=number]').val(); - if (current !== 0) { - $(item).find('input[type=number]').attr('max', current); - } else { - $('#selected_product option').each(function(ind, option) { - if ($(option).text() === - $(item).find('input[type=text]').val()) { - $(option).remove(); - } - }); + .catch(e => console.error(e)) + .finally(() => loader.attr('aria-disabled', 'true')); + }) + .on('click', '.create button.package', e => { + e.preventDefault(); + e.stopPropagation(); + + const packageData = { + length: parseInt(metaBox.find('input[name=length]').val()), + width: parseInt(metaBox.find('input[name=width]').val()), + height: parseInt(metaBox.find('input[name=height]').val()), + items: metaBox.find(`.item[data-id][aria-hidden=false]`) + .map((i, e) => ({ + id: parseInt(e.dataset.id), + name: $(e).text(), + qty: parseInt($(e).find('input[type=number]').val()), + })).toArray(), + }; + + if (packageData.length < 1) { + alert(__('Package length not specified', 'cdekdelivery')); + return; + } + if (packageData.width < 1) { + alert(__('Package width not specified', 'cdekdelivery')); + return; + } + if (packageData.height < 1) { + alert(__('Package height not specified', 'cdekdelivery')); + return; + } + if (packageData.items.length < 1) { + alert(__('Package not added to packaging', 'cdekdelivery')); + return; + } + + packageList.push(packageData); + + metaBox.find('.create .list') + .append($('

') + .text(__('Package', 'cdekdelivery') + + ` №${packageList.length} (${packageData.length}х${packageData.width}х${packageData.height}):` + + packageData.items.reduce( + (acc, e) => acc + `${e.name}${e.qty}, `, '').slice(0, -2))); + + metaBox.find(`.create .item[data-id][aria-hidden=false]`) + .each((i, e) => { + const input = $(e).find('input[type=number]'); + + const left = input.attr('max') - input.val(); + + if (left !== 0) { + input.attr('max', left); + return; } - } - }); - } - - function cleanForm() { - $('#selected_product').val(-1); - $('.product_list').each(function(index, item) { - $(item).find('input[type=number]').val(1); - $(item).css('display', 'none'); - $('input[name=package_length]').val(''); - $('input[name=package_width]').val(''); - $('input[name=package_height]').val(''); - }); - } - - $('#create-order-btn').click(function(e) { - $('#cdek-create-order-error').hide(); - $('#cdek-loader').show(); - apiFetch({ - method: 'POST', url: e.target.dataset.action, data: { - packages: [ - { - length: $('input[name=package_length]').val(), - width: $('input[name=package_width]').val(), - height: $('input[name=package_height]').val(), - }], - }, - }) - .then(resp => { - if (!resp.state) { - $('#cdek-create-order-error') - .text(resp.message) - .show(); - } else { - if (resp.door) { - $('#cdek-courier-result-block').hide(); - $('#cdek-order-courier').show(); - } - $('#cdek-status-block') - .data('status-available', resp.available); - checkOrderAvailable(); - $('#cdek-order-status-block').html(resp.statuses); - $('#cdek-create-order-form').hide(); - $('#cdek-order-number') - .html(`№ ${resp.code}`); - $('#cdek-order-number-input').val(resp.code); - $('#cdek-info-order').show(); - } + + metaBox.find(`.create select option[value=${e.dataset.id}]`) + .remove(); + }); + + metaBox.find('.create select').val(-1); + metaBox.find('.create .pack input[type=text]').val(''); + metaBox.find('.create .item[data-id][aria-hidden=false]') + .attr('aria-hidden', 'true'); + + if (metaBox.find('.create select option').length !== 1) { + return; + } + + metaBox.find('.create').attr('aria-invalid', 'false'); + }) + .on('click', '.create button', e => { + if (!Object.prototype.hasOwnProperty.call(e.target.dataset, 'id')) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + revokeDataUrl(); + + const loader = metaBox.find('.loader'); + loader.attr('aria-disabled', 'false'); + + apiFetch({ + method: 'POST', url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-create`, + _wpnonce: window.cdek.nonce, + id: e.target.dataset.id, + }), data: packageList.length !== 0 ? packageList : [ + { + length: metaBox.find('input[name=length]').val(), + width: metaBox.find('input[name=width]').val(), + height: metaBox.find('input[name=height]').val(), + }], parse: false, + }) + .then(r => r.text()).then(t => metaBox.find('.inside').html(t)) + .catch(e => console.error(e)) + .finally(() => loader.attr('aria-disabled', 'true')); + }) + .on('click', '.deletion', e => { + e.preventDefault(); + e.stopPropagation(); + + revokeDataUrl(); + + const loader = metaBox.find('.loader'); + loader.attr('aria-disabled', 'false'); + + apiFetch({ + method: 'POST', url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-${e.target.dataset.action}`, + _wpnonce: window.cdek.nonce, + id: e.target.dataset.id, + }), parse: false, }) - .catch(e => console.log(e)) - .finally(() => $('#cdek-loader').hide()); - }); - - $('#delete-order-btn').click(function(event) { - event.preventDefault(); - $(event.target).addClass('clicked'); - $('#cdek-create-order-error').hide(); - $('#cdek-courier-error').hide(); - $('#cdek-loader').show(); - apiFetch({ - method: 'POST', url: event.target.href, - }).then(resp => { - if (!resp.state) { - $('#cdek-delete-order-error') - .text(resp.message) - .show(); - $('#delete-order-btn').hide(); - } else { - alert(resp.message); - $(event.target).removeClass('clicked'); - $('#cdek-create-order-form').show(); - $('#cdek-info-order').hide(); - } - }).catch(e => console.error(e)).finally(() => $('#cdek-loader').hide()); - }); - - $('#cdek-courier-send-call').click(function(event) { - $('#cdek-courier-error').hide(); - $('#cdek-loader').show(); - apiFetch({ - method: 'POST', url: event.target.dataset.action, data: { - order_id: $('input[name=package_order_id]').val(), - date: $('#cdek-courier-date').val(), - starttime: $('#cdek-courier-startime').val(), - endtime: $('#cdek-courier-endtime').val(), - name: $('#cdek-courier-name').val(), - phone: $('#cdek-courier-phone').val(), - address: $('#cdek-courier-address').val(), - desc: $('#cdek-courier-package-desc').val(), - comment: $('#cdek-courier-comment').val(), - weight: $('#cdek-courier-weight').val(), - length: $('#cdek-courier-length').val(), - width: $('#cdek-courier-width').val(), - height: $('#cdek-courier-height').val(), - need_call: $('#cdek-courier-call').prop('checked'), - }, - }).then(resp => { - if (!resp) { - $('#cdek-courier-error').html(resp.message).show(); - } else { - $('#call-courier-form').hide(); - $('#cdek-order-courier').hide(); - $('#cdek-courier-info').text(resp.message).show(); - $('#cdek-courier-result-block').show(); - } - }).catch(e => console.error(e)).finally(() => $('#cdek-loader').hide()); - }); - - $('#cdek-order-courier').click(function() { - $('#call-courier-form').toggle(); - }); - - $('#cdek-courier-delete').click(function(event) { - $('#cdek-loader').show(); - apiFetch({ - method: 'POST', url: event.target.dataset.action, data: { - order_id: $('input[name=package_order_id]').val(), - }, - }).then(resp => { - if (resp) { - $('#cdek-courier-result-block').hide(); - $('#cdek-order-courier').show(); - } - }).catch(e => console.error(e)).finally(() => $('#cdek-loader').hide()); - }); - - $('#cdek-info-order') - .on('click', '#cdek-order-status-btn', function(event) { - let statusList = $('#cdek-order-status-list'); - let arrowUp = $('#cdek-btn-arrow-up'); - let arrowDown = $('#cdek-btn-arrow-down'); - - statusList.toggle(); - arrowUp.toggle(!statusList.is(':visible')); - arrowDown.toggle(statusList.is(':visible')); + .then(r => r.text()).then(t => metaBox.find('.inside').html(t)) + .catch(e => console.error(e)) + .finally(() => loader.attr('aria-disabled', 'true')); + }) + .on('click', '.toggle', e => { + const target = e.target.classList.contains('toggle') + ? e.target.parentElement + : e.target.parentElement.parentElement; + + target.ariaExpanded = target.ariaExpanded === 'true' + ? 'false' + : 'true'; + }) + .on('click', '.intake button', e => { + e.preventDefault(); + e.stopPropagation(); + + revokeDataUrl(); + + metaBox.find('.intake input[required]') + .each((i, e) => e.ariaInvalid = e.value === '' ? 'true' : 'false'); + + if (metaBox.find( + '.intake input[required][aria-invalid=true]').length > 0) { + return; + } + + const loader = metaBox.find('.loader'); + loader.attr('aria-disabled', 'false'); + + apiFetch({ + method: 'POST', url: addQueryArgs(ajaxurl, { + action: `${window.cdek.prefix}-intake_create`, + _wpnonce: window.cdek.nonce, + id: e.target.dataset.id, + }), data: { + date: metaBox.find('input[name=date]').val(), + from: metaBox.find('input[name=from]').val(), + to: metaBox.find('input[name=to]').val(), + desc: metaBox.find('input[name=desc]').val(), + comment: metaBox.find('input[name=comment]').val(), + weight: parseInt(metaBox.find('input[name=weight]').val()), + call: metaBox.find('input[name=call]').is(':checked'), + }, parse: false, + }) + .then(r => r.text()).then(t => metaBox.find('.inside').html(t)) + .catch(e => console.error(e)) + .finally(() => loader.attr('aria-disabled', 'true')); }); - }); diff --git a/src/Frontend/AdminOrder/styles/main.scss b/src/Frontend/AdminOrder/styles/main.scss index e6919c65..cf93e12a 100644 --- a/src/Frontend/AdminOrder/styles/main.scss +++ b/src/Frontend/AdminOrder/styles/main.scss @@ -1,126 +1,141 @@ -#delete-order-btn { - cursor: pointer; - color: #b32d2e; - text-decoration: none; - margin-left: auto; -} - -#delete-order-btn:hover { - text-decoration: underline; -} - -#delete-order-btn.clicked { - color: gray; - text-decoration: none; - cursor: default; -} - -#cdek-create-order-error { - color: #b32d2e; -} - -#cdek-delete-order-error { - color: #b32d2e; -} - -#cdek-courier-info { - margin: auto; -} - -#cdek-courier-error { - color: #b32d2e; -} - -#cdek-courier-block { - margin-top: 10px; -} - -#call-courier-form { - display: none; -} - -#cdek-courier-block input { - margin-top: 5px; -} - -#cdek-courier-block p { - margin: auto; -} - -#cdek-courier-delete { - color: #b32d2e; - text-decoration: none; -} - -#cdek-courier-delete:hover { - text-decoration-line: underline; -} - -#cdek-order-courier { - margin: auto; - color: #2271b1; - cursor: pointer; -} - -#cdek-order-courier:hover { - text-decoration-line: underline; -} - -#cdek-order-waybill, #cdek-order-barcode { - text-decoration: none; - margin: auto; -} - -#cdek-order-waybill:hover, #cdek-order-barcode:hover { - text-decoration-line: underline; -} - -#cdek-order-barcode { - display: block; -} - -#cdek-courier-delete { - cursor: pointer; -} - -#cdek-loader { - z-index: 50; - /*background-color: #e3e5e5;*/ - position: absolute; - top: 0; - width: 91%; - height: 94%; - opacity: 50%; - background: rgba(255, 255, 255, 0.8) url(../img/preloader.gif) center center no-repeat; -} - -.cdek-error { - padding: 5px; - background: #E58787; -} - -.cdek-order-status-elem-name { - padding: 3px; -} - -#cdek-order-status-btn { - display: inline-flex; - cursor: pointer; - width: 100%; -} - -#cdek-order-status-btn:hover { - background-color: #2271b12e; -} - -.cdek-btn-arrow { - margin-left: auto; -} - -#cdek-btn-arrow-down { - display: none; -} - -#cdek-order-status-list { - display: none; +#official_cdek_order { + .inside { + padding: 0; + + & > div { + padding: 0 12px; + + &.submitbox { + padding-bottom: 12px; + } + } + } + + .loader { + display: flex; + align-items: center; + justify-content: center; + z-index: 50; + position: absolute; + top: -13px; + bottom: 0; + right: 0; + left: 0; + background: rgba(238, 238, 238, .5); + + &[aria-disabled=true] { + display: none; + } + + span { + background: url(../img/loader.svg) no-repeat; + background-size: contain; + -webkit-animation: spin 1s linear infinite; + -moz-animation: spin 1s linear infinite; + animation: spin 1s linear infinite; + display: block; + width: 100px; + height: 100px; + } + } + + .print { + text-decoration: none; + margin: auto; + + :hover { + text-decoration-line: underline; + } + } + + .deletion { + text-decoration: underline; + } + + .intake { + .form { + margin: 10px 0; + + input { + margin: 2px 0; + } + } + + &[aria-expanded=false] .form { + display: none; + } + } + + .create { + &[aria-invalid] { + padding-bottom: 12px; + } + + &[aria-invalid="true"] { + button[data-id] { + display: none; + } + } + + &[aria-invalid="false"] > div:first-of-type { + display: none; + } + + .item { + display: flex; + align-items: center; + margin-top: 10px; + + span:last-of-type { + margin: 0 10px; + } + + input[type=number] { + width: 4em; + } + + &[aria-hidden=true] { + display: none; + } + } + } + + .actions { + display: block; + cursor: pointer; + + &:hover { + text-decoration: underline; + } + } + + .buttons { + display: flex; + justify-content: end; + } + + .status { + &[aria-expanded=false] .list { + display: none; + } + + &[aria-expanded=true] .toggle-indicator { + rotate: 180deg; + } + + .toggle { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + + &:hover { + background-color: #2271b12e; + } + } + + .name { + padding: 3px; + } + } } diff --git a/src/Frontend/AdminSettings/img/loader.svg b/src/Frontend/AdminSettings/img/loader.svg new file mode 100644 index 00000000..b0b3b3db --- /dev/null +++ b/src/Frontend/AdminSettings/img/loader.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ae56e8fdcc90d794af5fee5d31826885dbf64e08aab6b85b64ade617fce1fae +size 311 diff --git a/src/Frontend/AdminSettings/styles/main.scss b/src/Frontend/AdminSettings/styles/main.scss index 9300c5c7..0f0fd473 100644 --- a/src/Frontend/AdminSettings/styles/main.scss +++ b/src/Frontend/AdminSettings/styles/main.scss @@ -3,7 +3,7 @@ margin-top: 2px; width: 25px; height: 25px; - background: url(../images/loader.svg) no-repeat; + background: url(../img/loader.svg) no-repeat; background-size: contain; -webkit-animation: spin 1s linear infinite; -moz-animation: spin 1s linear infinite; diff --git a/src/Helpers/UI.php b/src/Helpers/UI.php index 09de8f4b..4d080bb4 100644 --- a/src/Helpers/UI.php +++ b/src/Helpers/UI.php @@ -27,14 +27,15 @@ public static function buildRestUrl( $url = add_query_arg($args, rest_url($prefix.$route)); - return Loader::debug() ? $url . '&' . Config::MAGIC_KEY : $url; + return Loader::debug() ? $url.'&'.Config::MAGIC_KEY : $url; } public static function enqueueScript( string $handle, string $fileName, bool $hasStyles = false, - bool $justRegister = false + bool $justRegister = false, + bool $needsNonce = false ): void { $script_asset_path = Loader::getPluginPath("build/$fileName.asset.php"); @@ -66,6 +67,13 @@ public static function enqueueScript( } wp_set_script_translations($handle, 'cdekdelivery', Loader::getPluginPath('lang')); + + if ($needsNonce) { + wp_localize_script($handle, 'cdek', [ + 'nonce' => wp_create_nonce(Config::DELIVERY_NAME), + 'prefix' => Config::DELIVERY_NAME, + ]); + } } } diff --git a/src/Loader.php b/src/Loader.php index 924de337..2bf2710c 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -30,7 +30,7 @@ use Cdek\UI\CdekWidget; use Cdek\UI\CheckoutMap; use Cdek\UI\Frontend; - use Cdek\UI\MetaBoxes; + use Cdek\Blocks\AdminOrderBox; use Cdek\Validator\CheckoutValidator; use RuntimeException; @@ -205,9 +205,9 @@ public function __invoke(string $pluginMainFile): void add_filter('plugin_row_meta', [Admin::class, 'addPluginRowMeta'], 10, 2); add_action('rest_api_init', new CallbackController); - add_action('rest_api_init', new OrderController); - add_action('rest_api_init', new IntakeController); + add_action('admin_init', new IntakeController); + add_action('admin_init', new OrderController); add_action('admin_init', new SettingsController); add_filter('woocommerce_hidden_order_itemmeta', [DataCleaner::class, 'hideMeta']); @@ -261,7 +261,7 @@ public function __invoke(string $pluginMainFile): void CdekWidget::new()(); Admin::new()(); Frontend::new()(); - MetaBoxes::new()(); + AdminOrderBox::new()(); AdminNotices::new()(); } diff --git a/src/Model/Order.php b/src/Model/Order.php index 90d61a32..b0af3dee 100644 --- a/src/Model/Order.php +++ b/src/Model/Order.php @@ -12,7 +12,8 @@ use Cdek\CdekApi; use Cdek\Contracts\MetaModelContract; use Cdek\Exceptions\OrderNotFoundException; - use DateTime; + use DateTimeImmutable; + use DateTimeInterface; use InvalidArgumentException; use WC_Order; use WP_Post; @@ -67,6 +68,7 @@ class Order extends MetaModelContract ]; private const META_KEY = 'order_data'; private WC_Order $order; + private ?ShippingItem $shipping = null; private ?bool $locked = null; /** @@ -141,11 +143,17 @@ final public function getIntake(): Intake final public function getShipping(): ?ShippingItem { + if ($this->shipping !== null) { + return $this->shipping; + } + $shippingMethods = $this->order->get_shipping_methods(); foreach ($shippingMethods as $method) { try { - return new ShippingItem($method); + $this->shipping = new ShippingItem($method); + + return $this->shipping; } catch (InvalidArgumentException $e) { continue; } @@ -176,7 +184,7 @@ final public function loadLegacyStatuses(?array $statuses = null): array return []; } - if($statuses === null){ + if ($statuses === null) { $orderInfo = (new CdekApi)->orderGet($this->uuid); if ($orderInfo->entity() === null) { throw new OrderNotFoundException; @@ -187,7 +195,7 @@ final public function loadLegacyStatuses(?array $statuses = null): array $statuses = array_map(static fn(array $st) => [ - 'time' => DateTime::createFromFormat('Y-m-d\TH:i:sO', $st['date_time']), + 'time' => DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $st['date_time']), 'name' => $st['name'], 'code' => $st['code'], ], $statuses); diff --git a/src/Model/ShippingItem.php b/src/Model/ShippingItem.php index 1fed8466..75e5010e 100644 --- a/src/Model/ShippingItem.php +++ b/src/Model/ShippingItem.php @@ -23,6 +23,7 @@ * @property string $length * @property string $height * @property string $width + * @property string $weight */ class ShippingItem extends MetaModelContract { @@ -33,6 +34,7 @@ class ShippingItem extends MetaModelContract 'length' => MetaKeys::LENGTH, 'height' => MetaKeys::HEIGHT, 'width' => MetaKeys::WIDTH, + 'weight' => MetaKeys::WEIGHT, ]; protected const ALIASES = [ @@ -41,6 +43,7 @@ class ShippingItem extends MetaModelContract MetaKeys::LENGTH => ['length'], MetaKeys::HEIGHT => ['height'], MetaKeys::WIDTH => ['width'], + MetaKeys::WEIGHT => ['weight'], ]; private int $instanceId; private WC_Order_Item_Shipping $originalItem; diff --git a/src/UI/Admin.php b/src/UI/Admin.php index 5ac89be8..16a60726 100644 --- a/src/UI/Admin.php +++ b/src/UI/Admin.php @@ -63,11 +63,7 @@ public static function registerAdminScripts(): void return; } - UI::enqueueScript('cdek-admin-settings', 'cdek-admin-settings', true); - wp_localize_script('cdek-admin-settings', 'cdek', [ - 'nonce' => wp_create_nonce(Config::DELIVERY_NAME), - 'prefix' => Config::DELIVERY_NAME, - ]); + UI::enqueueScript('cdek-admin-settings', 'cdek-admin-settings', true, false, true); } public function __invoke(): void diff --git a/templates/call_courier_form.php b/templates/call_courier_form.php deleted file mode 100644 index 14b7115b..00000000 --- a/templates/call_courier_form.php +++ /dev/null @@ -1,119 +0,0 @@ - - -
-
-
-

:

- -
-
-

:

- - - - - - -
-
- - - - - - - tariff ?: $order->tariff_id))) { ?> - -
-
-

- -
- - - - - -
- -
- - -
- - id/courier")) ?>"> -
diff --git a/templates/common.php b/templates/common.php new file mode 100644 index 00000000..bc2e167b --- /dev/null +++ b/templates/common.php @@ -0,0 +1,51 @@ + +
+isLocked()): ?> +
+

+ : + +

+
+ + + + +
+

+ +

+
+ + + + + +
+

+ +

+
+ + + + + +
+

+ +

+
+ + diff --git a/templates/create.php b/templates/create.php new file mode 100644 index 00000000..aae04187 --- /dev/null +++ b/templates/create.php @@ -0,0 +1,29 @@ + +
+

+

+ +

+

+ +

+

+ +

+

+ +

+
diff --git a/templates/create_many.php b/templates/create_many.php new file mode 100644 index 00000000..276f5c21 --- /dev/null +++ b/templates/create_many.php @@ -0,0 +1,57 @@ +items as $item) { + $items[$item['product_id']] = ['name' => $item['name'], 'qty' => $item['quantity']]; +} +?> + +
+
+

№1

+ + $item): ?> + + +
+

+ +

+

+ +

+

+ +

+
+ +
+ +
+ + +
diff --git a/templates/form_package.php b/templates/form_package.php deleted file mode 100644 index f06594cd..00000000 --- a/templates/form_package.php +++ /dev/null @@ -1,43 +0,0 @@ - -
-
number)) { ?>style="display: none" > -

- -

- -

-

- -

-

- -

-

- -

-
-
diff --git a/templates/form_package_many.php b/templates/form_package_many.php deleted file mode 100644 index 2467db70..00000000 --- a/templates/form_package_many.php +++ /dev/null @@ -1,91 +0,0 @@ - -
number)): ?>style="display: none" > -
-

№1

-
- -
-
- $item): ?> - - -
-
-

- - -

-

- - -

-

- - -

-
-
- - - - -
-

- -

-
- - -
diff --git a/templates/intake.php b/templates/intake.php new file mode 100644 index 00000000..e3e6bc35 --- /dev/null +++ b/templates/intake.php @@ -0,0 +1,82 @@ + + +
+
+ +
+
+ + + + +
+ + tariff ?: $order->tariff_id))) : ?> + +
+
+ + +
+ +
+ +
+ +
+
+ +
+
diff --git a/templates/order.php b/templates/order.php new file mode 100644 index 00000000..0dab5fdc --- /dev/null +++ b/templates/order.php @@ -0,0 +1,62 @@ +getIntake(); +?> +isLocked()): ?> +
+

+ : + +

+
+ + +
+

+ number) ?>

+ number)): ?> +

+ + number) ?>

+ +
+
+ + +
+ +isLocked()) : ?> + number)) : ?> + + + +
+
+ number)) : ?> + + + + + + + + +
+ diff --git a/templates/order_created.php b/templates/order_created.php deleted file mode 100644 index 6bda4848..00000000 --- a/templates/order_created.php +++ /dev/null @@ -1,79 +0,0 @@ -getIntake(); -?> - - -
number)) { ?>style="display: none" > -
-
-
-

number) ?>

-
- -
- id/waybill")) ?>"> - id/barcode")) ?>"> - isLocked()) { ?> -

-

- -
- -
number)) { ?>style="display: none;" style="margin-top: 10px;" > -
-

: number) ?>

-

id/courier/delete")) ?>">

-
- -
- -
- -
-
- isLocked()) { ?> -
- - -
diff --git a/templates/status_list.php b/templates/status_list.php deleted file mode 100644 index 134c5d00..00000000 --- a/templates/status_list.php +++ /dev/null @@ -1,42 +0,0 @@ -loadLegacyStatuses() : $statuses; -} catch (Throwable $e) { - $statuses = []; -} - -if (empty($statuses)): ?> -

- -
-
format('H:i d.m.Y')) ?>
-
- -
-
-
-
-
- -
format('H:i d.m.Y')) ?>
-
-
- -
- diff --git a/templates/statuses.php b/templates/statuses.php new file mode 100644 index 00000000..f5cb2b09 --- /dev/null +++ b/templates/statuses.php @@ -0,0 +1,35 @@ +loadLegacyStatuses() : $statuses; +} catch (Throwable $e) { + $statuses = []; +} + +if (empty($statuses)): ?> +

+ + + From 80b4fcf6efb6f30182e8db652d64419b2a9604d8 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 11:52:32 +0700 Subject: [PATCH 11/16] feat: add note of intake deletion --- src/Actions/IntakeDeleteAction.php | 8 ++++---- src/CdekApi.php | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Actions/IntakeDeleteAction.php b/src/Actions/IntakeDeleteAction.php index da9fcad9..b1eb5806 100644 --- a/src/Actions/IntakeDeleteAction.php +++ b/src/Actions/IntakeDeleteAction.php @@ -49,7 +49,7 @@ public function __invoke(int $orderId): ValidationResult } try { - $intake = $this->api->intakeDelete($courierMeta->uuid); + $this->api->intakeDelete($courierMeta->uuid); } catch (InvalidRequestException $e) { if ($e->getData()[0]['code'] === 'v2_entity_has_final_status') { return new ValidationResult(true); @@ -65,18 +65,18 @@ public function __invoke(int $orderId): ValidationResult ); } - $courierMeta->clean(); - Note::send( $orderId, sprintf( esc_html__(/* translators: %s: request number */ 'Intake %s has been deleted', 'cdekdelivery', ), - $intake, + $courierMeta->number, ), ); + $courierMeta->clean(); + return new ValidationResult(true, esc_html__('Intake has been deleted', 'cdekdelivery')); } } diff --git a/src/CdekApi.php b/src/CdekApi.php index 0bd7596e..3a833d11 100644 --- a/src/CdekApi.php +++ b/src/CdekApi.php @@ -289,13 +289,13 @@ public function intakeCreate(array $param): HttpResponse * @throws ApiException * @throws LegacyAuthException */ - public function intakeDelete(string $uuid): ?string + public function intakeDelete(string $uuid): void { - return HttpClient::sendJsonRequest( + HttpClient::sendJsonRequest( "{$this->apiUrl}intakes/$uuid", 'DELETE', $this->tokenStorage->getToken(), - )->entity()['uuid'] ?? null; + )->json(); } /** From ccb83f04965233e3f5b8381f8aa7dd44748fa0dc Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 12:00:00 +0700 Subject: [PATCH 12/16] fix: saving office code for block checkout --- src/Blocks/CheckoutMapBlock.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blocks/CheckoutMapBlock.php b/src/Blocks/CheckoutMapBlock.php index b5745723..12355306 100644 --- a/src/Blocks/CheckoutMapBlock.php +++ b/src/Blocks/CheckoutMapBlock.php @@ -122,6 +122,7 @@ public static function saveOrderData(WC_Order $order, WP_REST_Request $request): if (Tariff::isToOffice((int)$shipping->tariff)) { $shipping->office = $request['extensions'][Config::DELIVERY_NAME]['office_code']; + $shipping->save(); } } From de9e7c2c2008f875d4a77043db016a103d137c82 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 12:38:07 +0700 Subject: [PATCH 13/16] feat: add message for empty recipient phone --- src/Actions/OrderCreateAction.php | 2 +- src/Exceptions/InvalidPhoneException.php | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Actions/OrderCreateAction.php b/src/Actions/OrderCreateAction.php index d6adc102..42b38929 100644 --- a/src/Actions/OrderCreateAction.php +++ b/src/Actions/OrderCreateAction.php @@ -146,7 +146,7 @@ private function createOrder(array $packages): void private function buildRequestData(): array { $countryCode = $this->order->country ?: 'RU'; - $recipientNumber = $this->order->phone; + $recipientNumber = $this->order->phone ?: ''; PhoneValidator::new()($recipientNumber, $countryCode); $deliveryMethod = $this->shipping->getMethod(); diff --git a/src/Exceptions/InvalidPhoneException.php b/src/Exceptions/InvalidPhoneException.php index 1075ba18..d603fafb 100644 --- a/src/Exceptions/InvalidPhoneException.php +++ b/src/Exceptions/InvalidPhoneException.php @@ -17,16 +17,20 @@ class InvalidPhoneException extends ExceptionContract public function __construct(string $phone) { - $this->message = $this->message ?: sprintf(/* translators: %s: Recipient phone number */ esc_html__( - 'Incorrect phone number: %s', - 'cdekdelivery', - ), - $phone, - ); + if (empty($phone)) { + $this->message = $this->message ?: esc_html__('Recipient phone number is empty', 'cdekdelivery'); + } else { + $this->message = $this->message ?: sprintf(/* translators: %s: Recipient phone number */ esc_html__( + 'Incorrect recipient phone number: %s', + 'cdekdelivery', + ), + $phone, + ); + } parent::__construct( [ - 'phone' => $phone, + 'phone' => $phone, ], ); } From 61a88803608622171555d6b94e9ac912ed522dd6 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 13:34:47 +0700 Subject: [PATCH 14/16] fix: core api phone validation not answering --- src/Actions/IntakeDeleteAction.php | 21 ++++++++++----------- src/Actions/OrderCreateAction.php | 10 ++++++++-- src/Frontend/AdminOrder/styles/main.scss | 6 +----- src/Validator/CheckoutValidator.php | 5 +++++ src/Validator/PhoneValidator.php | 3 +++ 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Actions/IntakeDeleteAction.php b/src/Actions/IntakeDeleteAction.php index b1eb5806..6556d31a 100644 --- a/src/Actions/IntakeDeleteAction.php +++ b/src/Actions/IntakeDeleteAction.php @@ -51,18 +51,17 @@ public function __invoke(int $orderId): ValidationResult try { $this->api->intakeDelete($courierMeta->uuid); } catch (InvalidRequestException $e) { - if ($e->getData()[0]['code'] === 'v2_entity_has_final_status') { - return new ValidationResult(true); + if (($e->getData()['errors'][0]['code'] !== 'v2_entity_has_final_status') || + !str_contains($e->getData()['errors'][0]['message'], 'REMOVED')) { + return new ValidationResult( + false, sprintf(/* translators: %s: Error message */ esc_html__( + 'Intake has not been deleted. (%s)', + 'cdekdelivery', + ), + $e->getData()['errors'][0]['message'], + ), + ); } - - return new ValidationResult( - false, sprintf(/* translators: %s: Error message */ esc_html__( - 'Error. The courier request has not been created. (%s)', - 'cdekdelivery', - ), - $e->getData()[0]['message'], - ), - ); } Note::send( diff --git a/src/Actions/OrderCreateAction.php b/src/Actions/OrderCreateAction.php index 55c5fe48..f15bdf18 100644 --- a/src/Actions/OrderCreateAction.php +++ b/src/Actions/OrderCreateAction.php @@ -23,7 +23,6 @@ use Cdek\Exceptions\ShippingNotFoundException; use Cdek\Helpers\StringHelper; use Cdek\Helpers\WeightConverter; - use Cdek\Loader; use Cdek\Model\Order; use Cdek\Model\Service; use Cdek\Model\ShippingItem; @@ -81,6 +80,7 @@ public function __invoke(int $orderId, int $attempt = 0, array $packages = null) try { $this->createOrder($packages); + return new ValidationResult(true); } catch (InvalidPhoneException $e) { Note::send( @@ -146,7 +146,13 @@ private function createOrder(array $packages): void private function buildRequestData(): array { $countryCode = $this->order->country ?: 'RU'; - $recipientNumber = PhoneValidator::new()($this->order->phone ?: '', $countryCode); + $recipientNumber = $this->order->phone ?: ''; + + try { + $recipientNumber = PhoneValidator::new()($recipientNumber, $countryCode); + } catch (CoreAuthException|ApiException|CacheException $e) { + //Do nothing + } $deliveryMethod = $this->shipping->getMethod(); diff --git a/src/Frontend/AdminOrder/styles/main.scss b/src/Frontend/AdminOrder/styles/main.scss index cf93e12a..130fc167 100644 --- a/src/Frontend/AdminOrder/styles/main.scss +++ b/src/Frontend/AdminOrder/styles/main.scss @@ -5,7 +5,7 @@ & > div { padding: 0 12px; - &.submitbox { + &:last-of-type { padding-bottom: 12px; } } @@ -67,10 +67,6 @@ } .create { - &[aria-invalid] { - padding-bottom: 12px; - } - &[aria-invalid="true"] { button[data-id] { display: none; diff --git a/src/Validator/CheckoutValidator.php b/src/Validator/CheckoutValidator.php index b9464167..672e0574 100644 --- a/src/Validator/CheckoutValidator.php +++ b/src/Validator/CheckoutValidator.php @@ -11,6 +11,9 @@ use Cdek\CdekApi; use Cdek\Config; + use Cdek\Exceptions\CacheException; + use Cdek\Exceptions\External\ApiException; + use Cdek\Exceptions\External\CoreAuthException; use Cdek\Helpers\CheckoutHelper; use Cdek\Model\Tariff; use Throwable; @@ -65,6 +68,8 @@ public function __invoke(): void } else { try { PhoneValidator::new()($phone, CheckoutHelper::getValueFromCurrentSession('country')); + } catch (CoreAuthException|ApiException|CacheException $e) { + return; } catch (Throwable $e) { wc_add_notice($e->getMessage(), 'error'); } diff --git a/src/Validator/PhoneValidator.php b/src/Validator/PhoneValidator.php index 8f5d97df..2a916da4 100644 --- a/src/Validator/PhoneValidator.php +++ b/src/Validator/PhoneValidator.php @@ -20,6 +20,9 @@ class PhoneValidator /** * @throws \Cdek\Exceptions\InvalidPhoneException + * @throws \Cdek\Exceptions\External\ApiException + * @throws \Cdek\Exceptions\External\CoreAuthException + * @throws \Cdek\Exceptions\CacheException */ public function __invoke(string $phone, string $countryCode = null): string { From b47920b5b7145356d0380ff00ee4a7f58c33fc40 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 13:54:39 +0700 Subject: [PATCH 15/16] fix: empty intake number --- src/Actions/IntakeDeleteAction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Actions/IntakeDeleteAction.php b/src/Actions/IntakeDeleteAction.php index 6556d31a..37222f73 100644 --- a/src/Actions/IntakeDeleteAction.php +++ b/src/Actions/IntakeDeleteAction.php @@ -42,10 +42,10 @@ public function __invoke(int $orderId): ValidationResult { $courierMeta = new Intake($orderId); - if (empty($courierMeta->uuid)) { + if (empty($courierMeta->uuid) || empty($courierMeta->number)) { $courierMeta->clean(); - return new ValidationResult(true); + return new ValidationResult(true, esc_html__('Intake is not found in system', 'cdekdelivery')); } try { From 542ee924ff4357748c853597cea561231abf851f Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 22 Nov 2024 16:02:32 +0700 Subject: [PATCH 16/16] feat: add int conversions --- src/Actions/CalculateDeliveryAction.php | 6 +++--- src/UI/AdminNotices.php | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Actions/CalculateDeliveryAction.php b/src/Actions/CalculateDeliveryAction.php index 4b2e5c2d..6ddc6d58 100644 --- a/src/Actions/CalculateDeliveryAction.php +++ b/src/Actions/CalculateDeliveryAction.php @@ -104,7 +104,7 @@ static function ($carry, $item) use ($package) { continue; } - if (!$addTariffsToOffice && Tariff::isToOffice($tariff['tariff_code'])) { + if (!$addTariffsToOffice && Tariff::isToOffice((int)$tariff['tariff_code'])) { continue; } @@ -151,7 +151,7 @@ static function ($carry, $item) use ($package) { $api, $deliveryParam ) { - $rule = Tariff::isToOffice($tariff['meta_data'][MetaKeys::TARIFF_CODE]) ? $priceRules['office'] : + $rule = Tariff::isToOffice((int)$tariff['meta_data'][MetaKeys::TARIFF_CODE]) ? $priceRules['office'] : $priceRules['door']; if (isset($rule['type'])) { @@ -173,7 +173,7 @@ function_exists('wcml_get_woocommerce_currency_option') ? } $deliveryParam['tariff_code'] = $tariff['meta_data'][MetaKeys::TARIFF_CODE]; - $deliveryParam['type'] = Tariff::getType($deliveryParam['tariff_code']); + $deliveryParam['type'] = Tariff::getType((int)$deliveryParam['tariff_code']); $serviceList = Service::factory($this->method, $deliveryParam['tariff_code']); diff --git a/src/UI/AdminNotices.php b/src/UI/AdminNotices.php index c92d12cd..beb3bb12 100644 --- a/src/UI/AdminNotices.php +++ b/src/UI/AdminNotices.php @@ -19,8 +19,6 @@ class AdminNotices { use CanBeCreated; - private const AVAILABLE_MEASUREMENTS = ['g', 'kg', 'lbs', 'oz']; - public static function weightUnitsConflict(): void { /** @noinspection GlobalVariableUsageInspection */