diff --git a/assets/src/js/setup-wizard/commission/AdminCommission.vue b/assets/src/js/setup-wizard/commission/AdminCommission.vue index 26a2558fbd..9295f94c71 100644 --- a/assets/src/js/setup-wizard/commission/AdminCommission.vue +++ b/assets/src/js/setup-wizard/commission/AdminCommission.vue @@ -61,7 +61,12 @@ this.fixedCommission.fixed = commission.additional_fee ? Number( commission.additional_fee ) : 0; this.fixedCommission.percentage = commission.admin_percentage ? Number( commission.admin_percentage ) : 0; this.selectedCommission = commission.commission_type ? String(commission.commission_type) : 'fixed'; - this.commission = commission.commission_category_based_values; + let commission_category_based_values = commission.commission_category_based_values; + + commission_category_based_values.all = ! commission_category_based_values.all || Array.isArray( commission_category_based_values.all ) ? {} : commission_category_based_values.all; + commission_category_based_values.items = ! commission_category_based_values.items || Array.isArray( commission_category_based_values.items ) ? {} : commission_category_based_values.items; + + this.commission = commission_category_based_values; }, methods: { diff --git a/composer.json b/composer.json index 558bc241a7..efc22e53ea 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "minimum-stability": "dev", "require": { "php": ">=7.4", - "appsero/client": "^v2.0.2", + "appsero/client": "^v2.0.4", "jakeasmith/http_build_url": "^1", - "appsero/updater": "^v2.3.0" + "appsero/updater": "^v2.3.1" }, "require-dev": { "wp-coding-standards/wpcs": "dev-develop", diff --git a/composer.lock b/composer.lock index ca8d2b0c9f..ce56506fac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "85da992aa34ef2564d3d8cca7fe129a8", + "content-hash": "6a657eac8bbca8bc3e8cf80e6f08fca0", "packages": [ { "name": "appsero/client", - "version": "v2.0.3", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/Appsero/client.git", - "reference": "575ecd2ddb3900bd2626771f12d5723b69175b60" + "reference": "12ff65b9770286d21edf314e7acfcd26fdde3315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Appsero/client/zipball/575ecd2ddb3900bd2626771f12d5723b69175b60", - "reference": "575ecd2ddb3900bd2626771f12d5723b69175b60", + "url": "https://api.github.com/repos/Appsero/client/zipball/12ff65b9770286d21edf314e7acfcd26fdde3315", + "reference": "12ff65b9770286d21edf314e7acfcd26fdde3315", "shasum": "" }, "require": { @@ -56,22 +56,22 @@ ], "support": { "issues": "https://github.com/Appsero/client/issues", - "source": "https://github.com/Appsero/client/tree/v2.0.3" + "source": "https://github.com/Appsero/client/tree/v2.0.4" }, - "time": "2024-09-18T04:41:01+00:00" + "time": "2024-11-25T05:58:23+00:00" }, { "name": "appsero/updater", - "version": "v2.3.0", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/Appsero/updater.git", - "reference": "937c658f8a41b7739c5f4915f2a110aa6bf778da" + "reference": "0e233fd177eba9cca67f94c5d65d8bb221384983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Appsero/updater/zipball/937c658f8a41b7739c5f4915f2a110aa6bf778da", - "reference": "937c658f8a41b7739c5f4915f2a110aa6bf778da", + "url": "https://api.github.com/repos/Appsero/updater/zipball/0e233fd177eba9cca67f94c5d65d8bb221384983", + "reference": "0e233fd177eba9cca67f94c5d65d8bb221384983", "shasum": "" }, "type": "library", @@ -93,9 +93,9 @@ "description": "Appsero client updater module", "support": { "issues": "https://github.com/Appsero/updater/issues", - "source": "https://github.com/Appsero/updater/tree/v2.3.0" + "source": "https://github.com/Appsero/updater/tree/v2.3.1" }, - "time": "2024-09-06T04:30:41+00:00" + "time": "2024-11-25T05:50:04+00:00" }, { "name": "jakeasmith/http_build_url", @@ -406,12 +406,12 @@ "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "4f0f997f2a84e694e658c29a1f22e8a34377f1aa" + "reference": "515bc471fce72631265184d96d1777770f16d230" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/4f0f997f2a84e694e658c29a1f22e8a34377f1aa", - "reference": "4f0f997f2a84e694e658c29a1f22e8a34377f1aa", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/515bc471fce72631265184d96d1777770f16d230", + "reference": "515bc471fce72631265184d96d1777770f16d230", "shasum": "" }, "require": { @@ -450,7 +450,7 @@ "issues": "https://github.com/hamcrest/hamcrest-php/issues", "source": "https://github.com/hamcrest/hamcrest-php/tree/master" }, - "time": "2024-10-19T14:00:42+00:00" + "time": "2024-11-24T15:17:53+00:00" }, { "name": "mockery/mockery", @@ -540,12 +540,12 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -585,7 +585,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -593,7 +593,7 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", @@ -983,12 +983,12 @@ "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "d0304203dd7c6f8eab21ed8adabe5e3be380bdbe" + "reference": "31ef149a1ee85ec0d355e58418d78d927e89d185" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/d0304203dd7c6f8eab21ed8adabe5e3be380bdbe", - "reference": "d0304203dd7c6f8eab21ed8adabe5e3be380bdbe", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/31ef149a1ee85ec0d355e58418d78d927e89d185", + "reference": "31ef149a1ee85ec0d355e58418d78d927e89d185", "shasum": "" }, "require": { @@ -1054,7 +1054,7 @@ "type": "open_collective" } ], - "time": "2024-10-14T00:01:10+00:00" + "time": "2024-11-11T18:03:33+00:00" }, { "name": "phpcsstandards/phpcsutils", @@ -1062,12 +1062,12 @@ "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "a6a0a723bc4efce5b8a12646f28bb83c4610dfac" + "reference": "9b2671b7342d2c8ad8ec78c8bdac45958d9de065" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/a6a0a723bc4efce5b8a12646f28bb83c4610dfac", - "reference": "a6a0a723bc4efce5b8a12646f28bb83c4610dfac", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/9b2671b7342d2c8ad8ec78c8bdac45958d9de065", + "reference": "9b2671b7342d2c8ad8ec78c8bdac45958d9de065", "shasum": "" }, "require": { @@ -1143,7 +1143,7 @@ "type": "open_collective" } ], - "time": "2024-10-13T23:57:40+00:00" + "time": "2024-11-17T04:37:55+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1151,12 +1151,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + "reference": "0448d60087a382392a1b2a1abe434466e03dcc87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0448d60087a382392a1b2a1abe434466e03dcc87", + "reference": "0448d60087a382392a1b2a1abe434466e03dcc87", "shasum": "" }, "require": { @@ -1213,7 +1213,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2" }, "funding": [ { @@ -1221,7 +1221,7 @@ "type": "github" } ], - "time": "2024-08-22T04:23:01+00:00" + "time": "2024-10-31T05:58:25+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1470,12 +1470,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fcd1efebc9510d2e1cff8fcf1b13ad2a7827d675" + "reference": "aa6e85808145e6357a07fcad937e736cf4022e75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fcd1efebc9510d2e1cff8fcf1b13ad2a7827d675", - "reference": "fcd1efebc9510d2e1cff8fcf1b13ad2a7827d675", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa6e85808145e6357a07fcad937e736cf4022e75", + "reference": "aa6e85808145e6357a07fcad937e736cf4022e75", "shasum": "" }, "require": { @@ -1486,7 +1486,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1565,7 +1565,7 @@ "type": "tidelift" } ], - "time": "2024-10-19T09:36:22+00:00" + "time": "2024-11-21T14:43:04+00:00" }, { "name": "sebastian/cli-parser", @@ -2537,12 +2537,12 @@ "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "9e60f9f8748736d3d06fea8ef26dccbdc5ba4902" + "reference": "823dc9afb8bce7f2cf4ccd7dac3176a4bf4ef146" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/9e60f9f8748736d3d06fea8ef26dccbdc5ba4902", - "reference": "9e60f9f8748736d3d06fea8ef26dccbdc5ba4902", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/823dc9afb8bce7f2cf4ccd7dac3176a4bf4ef146", + "reference": "823dc9afb8bce7f2cf4ccd7dac3176a4bf4ef146", "shasum": "" }, "require": { @@ -2610,7 +2610,7 @@ "type": "open_collective" } ], - "time": "2024-10-16T22:25:17+00:00" + "time": "2024-11-24T17:49:11+00:00" }, { "name": "tareq1988/wp-php-cs-fixer", @@ -2706,12 +2706,12 @@ "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "2133137c33fa898df70b5f879a65d83af4dbb97d" + "reference": "8c069fe12ee7f395e8eba48565ba479cd3ac81aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/2133137c33fa898df70b5f879a65d83af4dbb97d", - "reference": "2133137c33fa898df70b5f879a65d83af4dbb97d", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/8c069fe12ee7f395e8eba48565ba479cd3ac81aa", + "reference": "8c069fe12ee7f395e8eba48565ba479cd3ac81aa", "shasum": "" }, "require": { @@ -2765,7 +2765,7 @@ "type": "custom" } ], - "time": "2024-10-05T00:46:24+00:00" + "time": "2024-11-22T14:46:22+00:00" }, { "name": "wp-phpunit/wp-phpunit", @@ -2773,12 +2773,12 @@ "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "a759b93f3ef66c4455a8fc027cce12e6bcacce2e" + "reference": "26397ba876ada5a7dd47ee22dabdc823a2cee4bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/a759b93f3ef66c4455a8fc027cce12e6bcacce2e", - "reference": "a759b93f3ef66c4455a8fc027cce12e6bcacce2e", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/26397ba876ada5a7dd47ee22dabdc823a2cee4bf", + "reference": "26397ba876ada5a7dd47ee22dabdc823a2cee4bf", "shasum": "" }, "default-branch": true, @@ -2814,7 +2814,7 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2024-07-17T01:13:44+00:00" + "time": "2024-11-13T01:22:47+00:00" }, { "name": "yoast/phpunit-polyfills", @@ -2822,12 +2822,12 @@ "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "a4a261f6fb8ed016a9e9ea287ead829f232bc3f7" + "reference": "36f2d3fe72766d45a2319eece04dce099cb72f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/a4a261f6fb8ed016a9e9ea287ead829f232bc3f7", - "reference": "a4a261f6fb8ed016a9e9ea287ead829f232bc3f7", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/36f2d3fe72766d45a2319eece04dce099cb72f37", + "reference": "36f2d3fe72766d45a2319eece04dce099cb72f37", "shasum": "" }, "require": { @@ -2877,15 +2877,15 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2024-10-13T18:33:27+00:00" + "time": "2024-11-18T19:20:35+00:00" } ], "aliases": [], "minimum-stability": "dev", "stability-flags": { - "phpcompatibility/phpcompatibility-wp": 20, - "tareq1988/wp-php-cs-fixer": 20, "wp-coding-standards/wpcs": 20, + "tareq1988/wp-php-cs-fixer": 20, + "phpcompatibility/phpcompatibility-wp": 20, "wp-phpunit/wp-phpunit": 20, "yoast/phpunit-polyfills": 20 }, @@ -2894,9 +2894,9 @@ "platform": { "php": ">=7.4" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "7.4" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/includes/Admin/Notices/Manager.php b/includes/Admin/Notices/Manager.php index 7a9251d9a6..f763bf1d4b 100644 --- a/includes/Admin/Notices/Manager.php +++ b/includes/Admin/Notices/Manager.php @@ -50,6 +50,7 @@ private function init_hooks() { add_filter( 'dokan_admin_notices', [ $this, 'show_permalink_setting_notice' ] ); add_filter( 'dokan_admin_notices', [ $this, 'show_admin_logo_update_notice' ] ); add_action( 'wp_ajax_dismiss_dokan_admin_logo_update_notice', [ $this, 'dismiss_dokan_admin_logo_update_notice' ] ); + add_filter( 'dokan_admin_notices', [ $this, 'show_admin_plugin_update_notice' ] ); } /** @@ -189,4 +190,39 @@ private function dismiss_notice( string $option_name ) { update_option( $option_name, 'yes' ); wp_send_json_success(); } + + /** + * Show admin notice if dokan lite is updated to v3.14.0 and dokan pro is not updated to minimum v3.14.0. + * + * @since DOKAN_SINCE + * + * @param $notices + * + * @return mixed + */ + public function show_admin_plugin_update_notice( $notices ) { + if ( + version_compare( DOKAN_PLUGIN_VERSION, '3.14.0', '>=' ) && + defined( 'DOKAN_PRO_PLUGIN_VERSION' ) && + version_compare( DOKAN_PRO_PLUGIN_VERSION, '3.14.0', '<' ) + ) { + $notices[] = [ + 'priority' => 1, + 'show_close_button' => false, + 'type' => 'alert', + 'scope' => 'global', + 'title' => __( 'Dokan Update Required', 'dokan-lite' ), + 'description' => __( 'To ensure all the feature compatibility and accessibility, Dokan Pro minimum v3.14.0 is required.', 'dokan-lite' ), + 'actions' => [ + [ + 'type' => 'primary', + 'text' => __( 'Update Now', 'dokan-lite' ), + 'action' => admin_url( 'plugins.php' ), + ], + ], + ]; + } + + return $notices; + } } diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php index 8ef59f0d82..2866ef1f42 100644 --- a/includes/Admin/Settings.php +++ b/includes/Admin/Settings.php @@ -538,12 +538,13 @@ public function get_settings_fields() { ], ], 'commission_category_based_values' => [ - 'name' => 'commission_category_based_values', - 'type' => 'category_based_commission', + 'name' => 'commission_category_based_values', + 'type' => 'category_based_commission', 'dokan_pro_commission' => 'yes', - 'label' => __( 'Admin Commission', 'dokan-lite' ), - 'desc' => __( 'Amount you will get from each sale', 'dokan-lite' ), - 'show_if' => [ + 'label' => __( 'Admin Commission', 'dokan-lite' ), + 'desc' => __( 'Amount you will get from each sale', 'dokan-lite' ), + 'required' => 'yes', + 'show_if' => [ 'commission_type' => [ 'equal' => 'category_based', ], diff --git a/includes/Assets.php b/includes/Assets.php index 48a935ee85..f87825b08a 100644 --- a/includes/Assets.php +++ b/includes/Assets.php @@ -36,6 +36,7 @@ public function __construct() { */ public function load_dokan_admin_notices_scripts() { wp_enqueue_script( 'dokan-promo-notice-js' ); + wp_enqueue_script( 'dokan-admin-notice-js' ); $vue_localize_script = apply_filters( 'dokan_promo_notice_localize_script', [ 'ajaxurl' => admin_url( 'admin-ajax.php' ), @@ -541,6 +542,11 @@ public function get_scripts() { 'deps' => [ 'jquery', 'dokan-vue-vendor' ], 'version' => filemtime( $asset_path . 'js/dokan-promo-notice.js' ), ], + 'dokan-admin-notice-js' => [ + 'src' => $asset_url . '/js/dokan-admin-notice.js', + 'deps' => [ 'jquery', 'dokan-vue-vendor' ], + 'version' => filemtime( $asset_path . 'js/dokan-admin-notice.js' ), + ], 'dokan-reverse-withdrawal' => [ 'src' => $asset_url . '/js/reverse-withdrawal.js', 'deps' => [ 'jquery', 'dokan-util-helper', 'dokan-vue-vendor', 'dokan-date-range-picker' ], diff --git a/includes/Commission.php b/includes/Commission.php index 7e21d1c041..76fe588735 100644 --- a/includes/Commission.php +++ b/includes/Commission.php @@ -582,7 +582,15 @@ public function get_commission( $args = [], $auto_save = false ) { $category_id = $category_id ? $category_id : 0; } - if ( ! empty( $product_id ) && empty( $total_amount ) ) { + /** + * If the $total_amount is empty and $order_item_id is empty then we will calculate the commission based on the product price. + * There is a case where the $total_amount is empty and $order_item_id is empty but the $product_id is not empty + * In this case, we will calculate the commission based on the product price. + * Also there is an issue when 100% coupon is applied see the below link for more details + * + * @see https://github.com/getdokan/dokan/pull/2440#issuecomment-2488159960 + */ + if ( ! empty( $product_id ) && empty( $total_amount ) && empty( $order_item_id ) ) { $product = dokan()->product->get( $product_id ); // If product price is empty the setting the price as 0 diff --git a/includes/Commission/Upugrader/Update_Category_Commission.php b/includes/Commission/Upugrader/Update_Category_Commission.php new file mode 100644 index 0000000000..a10d568b4f --- /dev/null +++ b/includes/Commission/Upugrader/Update_Category_Commission.php @@ -0,0 +1,222 @@ +queue()->add( + self::PROCESS_BATCH_HOOK_CREATOR, + [], + 'dokan_updater_category_processing_creator' + ); + } + + /** + * Batch queue creator. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function process_batch_creator() { + // Get total number of products + $total = $this->category_count(); + + if ( is_wp_error( $total ) || $total === 0 ) { + return; + } + + $total = $total + 50; + $offset = 0; + + do { + $this->schedule_next_batch( $offset ); + + // Calculate next offset + $offset = $offset + self::BATCH_SIZE; + } while ( $offset < $total ); + } + + /** + * Process a batch of categories + * + * @since DOKAN_PRO_SINCE + * + * @param int $page_number Current page number + * + * @return void + */ + public function process_batch( $offset ) { + // Get categories for this batch + $categories = $this->get_categories_batch( $offset ); + + if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) { + foreach ( $categories as $category ) { + $this->schedule_cat_item( $category->term_id ); + } + } + } + + /** + * Schedule the next batch of categories + * + * @since DOKAN_PRO_SINCE + * + * @param int $page_number Next page number to process + * + * @return void + */ + protected function schedule_next_batch( $offset ) { + WC()->queue()->add( + self::PROCESS_BATCH_HOOK, + [ $offset ], + 'dokan_updater_category_processing' + ); + } + + /** + * Schedule a category item for processing. + * + * @since DOKAN_SINCE + * + * @param $term + * + * @return void + */ + private function schedule_cat_item( $term ) { + WC()->queue()->add( + self::PROCESS_ITEM_HOOK, + [ $term ], + 'dokan_updater_category_item_processing' + ); + } + + /** + * Get a batch of categories. + * + * @since DOKAN_PRO_SINCE + * + * @param int $page_number Page number to fetch + * + * @return array Array of term objects + */ + protected function get_categories_batch( $offset ) { + $args = [ + 'taxonomy' => 'product_cat', + 'number' => self::BATCH_SIZE, + 'orderby' => 'name', + 'order' => 'ASC', + 'hide_empty' => false, + 'offset' => $offset, + ]; + + return get_terms( $args ); + } + + /** + * Get the total number of categories + * + * @since DOKAN_SINCE + * + * @return int[]|string|string[]|\WP_Error|\WP_Term[] + */ + protected function category_count() { + return get_terms( + array( + 'taxonomy' => 'product_cat', + 'hide_empty' => false, + 'fields' => 'count', + ) + ); + } + + /** + * Process a single category. + * + * @since DOKAN_PRO_SINCE + * + * @param int $term Category term object + * + * @return void + */ + public function process_single_category( $term_id ) { + $dokan_selling = get_option( 'dokan_selling', [] ); + $category_commission = dokan_get_option( 'commission_category_based_values', 'dokan_selling', [] ); + + $commission_type = get_term_meta( $term_id, 'per_category_admin_commission_type', true ); + $admin_additional_fee = get_term_meta( $term_id, 'per_category_admin_additional_fee', true ); + $commission = get_term_meta( $term_id, 'per_category_admin_commission', true ); + + if ( ! empty( $commission_type ) ) { + $category_commission_item = [ + 'flat' => $admin_additional_fee, + 'percentage' => $commission, + ]; + + if ( Flat::SOURCE === $commission_type ) { + $category_commission_item['percentage'] = 0; + $category_commission_item['flat'] = $commission; + } elseif ( Percentage::SOURCE === $commission_type ) { + $category_commission_item['percentage'] = $commission; + $category_commission_item['flat'] = 0; + } + + $category_commission['items'][ $term_id ] = $category_commission_item; + } + + $dokan_selling['commission_category_based_values'] = $category_commission; + update_option( 'dokan_selling', $dokan_selling ); + } + + /** + * Check if processing is currently running. + * + * @since DOKAN_PRO_SINCE + * + * @return bool + */ + public function is_processing() { + return WC()->queue()->get_next( self::PROCESS_BATCH_HOOK ) !== null; + } +} diff --git a/includes/Commission/Upugrader/Update_Product_Commission.php b/includes/Commission/Upugrader/Update_Product_Commission.php new file mode 100644 index 0000000000..752e9833f9 --- /dev/null +++ b/includes/Commission/Upugrader/Update_Product_Commission.php @@ -0,0 +1,219 @@ +queue()->add( + self::PROCESS_BATCH_HOOK_CREATOR, + [], + 'dokan_updater_product_processing_creator' + ); + } + + /** + * Batch queue creator. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function process_batch_creator() { + // Get total number of products + $total_products = $this->get_total_products(); + $total_products = $total_products + 50; + + if ( $total_products === 0 ) { + return; + } + + $offset = 0; + + do { + $this->schedule_next_batch( $offset, $total_products ); + + // Calculate next offset + $offset = $offset + self::BATCH_SIZE; + } while ( $offset < $total_products ); + } + + /** + * Process a batch of products + * + * @since DOKAN_PRO_SINCE + * + * @param int $offset Current offset + * @param int $total_products Total number of products + * + * @return void + */ + public function process_batch( $offset, $total_products ) { + // Get products for this batch + $products = $this->get_products_batch( $offset ); + + foreach ( $products as $product ) { + $this->schedule_item( $product->get_id() ); + } + } + + /** + * Schedule the next batch of products + * + * @since DOKAN_PRO_SINCE + * + * @param int $offset Current offset + * @param int $total_products Total number of products + * + * @return void + */ + protected function schedule_next_batch( $offset, $total_products ) { + WC()->queue()->add( + self::PROCESS_BATCH_HOOK, [ + $offset, + $total_products, + ], + 'dokan_updater_product_processing' + ); + } + + /** + * Schedule a single product for processing. + * + * @since DOKAN_SINCE + * + * @param $item + * + * @return void + */ + private function schedule_item( $item ) { + WC()->queue()->add( + self::PROCESS_ITEM_HOOK, + [ $item ], + 'dokan_updater_product_item_processing' + ); + } + + /** + * Get a batch of products + * + * @since DOKAN_PRO_SINCE + * + * @param int $offset Current offset + * + * @return WC_Product[] Array of product objects + */ + protected function get_products_batch( $offset ) { + $args = [ + 'status' => 'publish', + 'limit' => self::BATCH_SIZE, + 'offset' => $offset, + 'orderby' => 'ID', + 'order' => 'ASC', + ]; + + return wc_get_products( $args ); + } + + /** + * Get total number of products + * + * @since DOKAN_PRO_SINCE + * + * @return int + */ + protected function get_total_products() { + $args = [ + 'status' => 'any', + 'limit' => -1, + 'return' => 'ids', + ]; + + $products = wc_get_products( $args ); + + return count( $products ); + } + + /** + * Process a single product + * Customize this method based on what you need to do with each product + * + * @since DOKAN_PRO_SINCE + * + * @param int $product + * + * @return void + */ + public function process_single_product( $product_id ) { + $commission = dokan()->product->get_commission_settings( $product_id ); + + $commission_type_old = $commission->get_type(); + $commission->set_type( Fixed::SOURCE ); + + if ( Flat::SOURCE === $commission_type_old ) { + $commission->set_flat( $commission->get_percentage() ); + $commission->set_percentage( 0 ); + } elseif ( Percentage::SOURCE === $commission_type_old ) { + $commission->set_flat( 0 ); + } + + dokan()->product->save_commission_settings( + $product_id, + [ + 'type' => $commission->get_type(), + 'percentage' => $commission->get_percentage(), + 'flat' => $commission->get_flat(), + ] + ); + } + + /** + * Check if processing is currently running + * + * @since DOKAN_PRO_SINCE + * + * @return bool + */ + public function is_processing() { + return WC()->queue()->get_next( self::PROCESS_BATCH_HOOK ) !== null; + } +} diff --git a/includes/Commission/Upugrader/Update_Vendor_Commission.php b/includes/Commission/Upugrader/Update_Vendor_Commission.php new file mode 100644 index 0000000000..eaacdc9a00 --- /dev/null +++ b/includes/Commission/Upugrader/Update_Vendor_Commission.php @@ -0,0 +1,203 @@ +queue()->add( + self::PROCESS_BATCH_HOOK_CREATOR, + [], + 'dokan_updater_vendor_processing_creator' + ); + } + + /** + * Batch queue creator. + * + * @since DOKAN_SINCE + * + * @return void + */ + public function process_batch_creator() { + $counts = dokan_get_seller_status_count(); + $total_items = $counts['total']; + $max_pages = ceil( $total_items / self::BATCH_SIZE ); + $max_pages = $max_pages + 5; + + for ( $page = 1; $page <= $max_pages; $page++ ) { + // Schedule the current page for batch processing + WC()->queue()->add( + self::PROCESS_BATCH_HOOK, + [ $page, $max_pages ], + 'dokan_updater_vendor_processing' + ); + } + } + + /** + * Process a batch of vendors + * + * @since DOKAN_PRO_SINCE + * + * @param int $page_number Current page number + * @param int $max_pages Total number of pages + * + * @return void + */ + public function process_batch( $page_number, $max_pages ) { // phpcs:ignore + $vendors = $this->get_vendors_batch( $page_number ); + + if ( ! empty( $vendors ) ) { + foreach ( $vendors as $vendor ) { + $this->schedule_item( $vendor->get_id() ); + } + } + } + + /** + * Get a batch of vendors + * + * @since DOKAN_PRO_SINCE + * + * @param int $page_number Page number to fetch + * + * @return \WeDevs\Dokan\Vendor\Vendor[] Array of vendor objects + */ + protected function get_vendors_batch( $page_number ) { + return dokan()->vendor->all( + [ + 'paged' => $page_number, + 'number' => self::BATCH_SIZE, + ] + ); + } + + /** + * Schedule an individual vendor for processing + * + * @since DOKAN_PRO_SINCE + * + * @param int $vendor_id + * + * @return void + */ + private function schedule_item( $vendor_id ) { + WC()->queue()->add( + self::PROCESS_ITEM_HOOK, + [ $vendor_id ], + 'dokan_updater_vendor_item_processing' + ); + } + + /** + * Process a single vendor + * + * @since DOKAN_PRO_SINCE + * + * @param int $vendor_id Vendor ID + * + * @return void + */ + public function process_single_vendor( $vendor_id ) { + try { + $vendor = dokan()->vendor->get( $vendor_id ); + $commission = $vendor->get_commission_settings(); + + $commission_type_old = $commission->get_type(); + $commission->set_type( Fixed::SOURCE ); + + $percentage = $commission->get_percentage(); + + if ( Flat::SOURCE === $commission_type_old ) { + $commission->set_percentage( 0 ); + $commission->set_flat( $percentage ); + } elseif ( Percentage::SOURCE === $commission_type_old ) { + $commission->set_percentage( $percentage ); + $commission->set_flat( 0 ); + } + + $vendor->save_commission_settings( + [ + 'type' => $commission->get_type(), + 'flat' => $commission->get_flat(), + 'percentage' => $commission->get_percentage(), + 'category_commissions' => $commission->get_category_commissions(), + ] + ); + + // Log success + $this->log_vendor_update( $vendor_id, true ); + } catch ( \Exception $e ) { + // Log error + $this->log_vendor_update( $vendor_id, false, $e->getMessage() ); + } + } + + /** + * Log vendor update status + * + * @since DOKAN_PRO_SINCE + * + * @param int $vendor_id + * @param bool $success + * @param string $error_message + * + * @return void + */ + private function log_vendor_update( $vendor_id, $success, $error_message = '' ) { + $log_key = 'dokan_commission_upgrade_vendor_' . $vendor_id; + update_option( + $log_key, [ + 'status' => $success ? 'success' : 'error', + 'error_message' => $error_message, + 'timestamp' => current_time( 'mysql' ), + ] + ); + } + + /** + * Check if processing is currently running + * + * @since DOKAN_PRO_SINCE + * + * @return bool + */ + public function is_processing() { + return WC()->queue()->get_next( self::PROCESS_BATCH_HOOK ) !== null + || WC()->queue()->get_next( self::PROCESS_ITEM_HOOK ) !== null; + } +} diff --git a/includes/Install/Installer.php b/includes/Install/Installer.php index 1d11c51141..4a49cd0ae4 100755 --- a/includes/Install/Installer.php +++ b/includes/Install/Installer.php @@ -41,9 +41,8 @@ public function do_install() { $was_installed_before = get_option( 'dokan_theme_version', false ); - update_option( 'dokan_theme_version', DOKAN_PLUGIN_VERSION ); - if ( ! $was_installed_before ) { + update_option( 'dokan_theme_version', DOKAN_PLUGIN_VERSION ); update_option( 'dokan_admin_setup_wizard_ready', false ); set_transient( '_dokan_setup_page_redirect', true, 30 ); } diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php index 961e19556d..00cd55d102 100644 --- a/includes/Product/Hooks.php +++ b/includes/Product/Hooks.php @@ -491,7 +491,7 @@ class="woocommerce-help-tip" - + @@ -540,14 +540,18 @@ public static function save_per_product_commission_options( $post_id ) { } if ( isset( $_POST['_per_product_admin_commission'] ) ) { // phpcs:ignore - $admin_commission = ( '' === $_POST['_per_product_admin_commission'] ) ? '' : sanitize_text_field( $_POST['_per_product_admin_commission'] ); // phpcs:ignore + $_per_product_admin_commission = wc_format_decimal( sanitize_text_field( $_POST['_per_product_admin_commission'] ) ); // phpcs:ignore + + if ( 0 <= $_per_product_admin_commission && 100 >= $_per_product_admin_commission ) { + $admin_commission = ( '' === $_POST['_per_product_admin_commission'] ) ? '' : $_per_product_admin_commission; // phpcs:ignore + } } if ( isset( $_POST['_per_product_admin_additional_fee'] ) ) { // phpcs:ignore $additional_fee = ( '' === $_POST['_per_product_admin_additional_fee'] ) ? '' : sanitize_text_field( $_POST['_per_product_admin_additional_fee'] ); // phpcs:ignore } - update_post_meta( $post_id, '_per_product_admin_commission', wc_format_decimal( $admin_commission ) ); + update_post_meta( $post_id, '_per_product_admin_commission', $admin_commission ); update_post_meta( $post_id, '_per_product_admin_additional_fee', wc_format_decimal( $additional_fee ) ); } } diff --git a/includes/REST/AdminNoticeController.php b/includes/REST/AdminNoticeController.php index e044b5fcf5..d5e2a8552f 100644 --- a/includes/REST/AdminNoticeController.php +++ b/includes/REST/AdminNoticeController.php @@ -5,6 +5,7 @@ use WeDevs\Dokan\Admin\Notices\Helper; use WP_REST_Response; use WP_REST_Server; +use WP_REST_Request; use WeDevs\Dokan\Abstracts\DokanRESTAdminController; /** @@ -36,6 +37,16 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'dokan_get_admin_notices' ], 'permission_callback' => [ $this, 'check_permission' ], + 'args' => [ + 'scope' => [ + 'description' => __( 'Choose notice scope: "local" displays only on Dokan pages, "global" displays across the entire site.', 'dokan-lite' ), + 'type' => 'string', + 'enum' => [ 'local', 'global' ], + 'required' => false, + 'default' => '', + 'sanitize_callback' => 'sanitize_text_field', + ], + ], ], ] ); @@ -53,13 +64,26 @@ public function register_routes() { /** * Get dokan specific notices + * @param WP_REST_Request $request * * @return WP_REST_Response */ - public function dokan_get_admin_notices() { + public function dokan_get_admin_notices( WP_REST_Request $request ) { + $notice_scope = $request->get_param( 'scope' ); + $notice_scope = ! empty( $notice_scope ) ? $notice_scope : 'local'; + $notices = Helper::dokan_get_admin_notices(); - return rest_ensure_response( $notices ); + // Filter notices by scope + $filter_notices = array_filter( + $notices, + function ( $notice ) use ( $notice_scope ) { + return $notice_scope === ( $notice['scope'] ?? 'local' ); + } + ); + $filter_notices = array_values( $filter_notices ); + + return rest_ensure_response( $filter_notices ); } /** diff --git a/includes/Upgrade/AdminNotice.php b/includes/Upgrade/AdminNotice.php index 980536c346..258af341f9 100644 --- a/includes/Upgrade/AdminNotice.php +++ b/includes/Upgrade/AdminNotice.php @@ -37,8 +37,9 @@ public static function show_notice( $notices ) { $notices[] = [ 'type' => 'info', 'title' => __( 'Dokan Data Update Required', 'dokan-lite' ), - 'description' => __( 'We need to update your install to the latest version', 'dokan-lite' ), + 'description' => __( 'Updating your Dokan data is required to continue functional operations.', 'dokan-lite' ), 'priority' => 1, + 'scope' => 'global', 'actions' => [ [ 'type' => 'primary', diff --git a/includes/Upgrade/Hooks.php b/includes/Upgrade/Hooks.php index c7cd4d9aa5..4c9bfc5f84 100644 --- a/includes/Upgrade/Hooks.php +++ b/includes/Upgrade/Hooks.php @@ -2,6 +2,10 @@ namespace WeDevs\Dokan\Upgrade; +use WeDevs\Dokan\Commission\Upugrader\Update_Category_Commission; +use WeDevs\Dokan\Commission\Upugrader\Update_Product_Commission; +use WeDevs\Dokan\Commission\Upugrader\Update_Vendor_Commission; + class Hooks { /** @@ -18,5 +22,13 @@ public function __construct() { add_action( 'wp_ajax_dokan_do_upgrade', [ AdminNotice::class, 'do_upgrade' ] ); add_action( 'dokan_upgrade_is_not_required', [ Upgrades::class, 'update_db_dokan_version' ] ); add_action( 'dokan_upgrade_finished', [ Upgrades::class, 'update_db_dokan_version' ] ); + + $p_scheduler = new Update_Product_Commission(); + $v_scheduler = new Update_Vendor_Commission(); + $c_scheduler = new Update_Category_Commission(); + + $p_scheduler->init_hooks(); + $v_scheduler->init_hooks(); + $c_scheduler->init_hooks(); } } diff --git a/includes/Upgrade/Upgrades/BackgroundProcesses/V_3_14_0_UpdateCommissions.php b/includes/Upgrade/Upgrades/BackgroundProcesses/V_3_14_0_UpdateCommissions.php deleted file mode 100644 index bd3238f51f..0000000000 --- a/includes/Upgrade/Upgrades/BackgroundProcesses/V_3_14_0_UpdateCommissions.php +++ /dev/null @@ -1,130 +0,0 @@ -update_global_settings( $item['data'] ); - } - - if ( isset( $item['task'] ) && 'vendors-commission' === $item['task'] ) { - return $this->update_vendors_settings( $item['data'] ); - } - - if ( isset( $item['task'] ) && 'products-commission' === $item['task'] ) { - return $this->update_products_settings( $item['data'] ); - } - - return false; - } - - /** - * Update global category commissions. - * - * @since DOKAN_SINCE - * - * @param WP_Term[] $terms - * - * @return true - */ - private function update_global_settings( $terms ) { - $dokan_selling = get_option( 'dokan_selling', [] ); - $category_commission = dokan_get_option( 'commission_category_based_values', 'dokan_selling', [] ); - - foreach ( $terms as $term ) { - $commission_type = get_term_meta( $term->term_id, 'per_category_admin_commission_type', true ); - $admin_additional_fee = get_term_meta( $term->term_id, 'per_category_admin_additional_fee', true ); - $commission = get_term_meta( $term->term_id, 'per_category_admin_commission', true ); - - if ( ! empty( $commission_type ) ) { - $category_commission['items'][ $term->term_id ] = [ - 'flat' => $admin_additional_fee, - 'percentage' => $commission, - ]; - } - } - - $dokan_selling['commission_category_based_values'] = $category_commission; - update_option( 'dokan_selling', $dokan_selling ); - - return true; - } - - /** - * Update vendor commission settings. - * - * @since DOKAN_SINCE - * - * @param \WeDevs\Dokan\Vendor\Vendor[] $vendors - * - * @return bool - */ - private function update_vendors_settings( $vendors ) { - foreach ( $vendors as $vendor ) { - $commission = $vendor->get_commission_settings(); - $commission->set_type( Fixed::SOURCE ); - - $vendor->save_commission_settings( - [ - 'percentage' => $commission->get_percentage(), - 'type' => $commission->get_type(), - 'flat' => $commission->get_flat(), - 'category_commissions' => $commission->get_category_commissions(), - ] - ); - } - - return true; - } - - /** - * Update product commission settings. - * - * @since DOKAN_SINCE - * - * @param \WP_Post[] $posts - * - * @return bool - */ - private function update_products_settings( $posts ) { - foreach ( $posts as $post ) { - $commission = dokan()->product->get_commission_settings( $post->ID ); - $commission->set_type( Fixed::SOURCE ); - - dokan()->product->save_commission_settings( - $post->ID, - [ - 'percentage' => $commission->get_percentage(), - 'type' => $commission->get_type(), - 'flat' => $commission->get_flat(), - ] - ); - } - - return true; - } -} diff --git a/includes/Upgrade/Upgrades/V_3_14_0.php b/includes/Upgrade/Upgrades/V_3_14_0.php index bdaefbc903..339b67187c 100644 --- a/includes/Upgrade/Upgrades/V_3_14_0.php +++ b/includes/Upgrade/Upgrades/V_3_14_0.php @@ -4,7 +4,11 @@ use WeDevs\Dokan\Abstracts\DokanUpgrader; use WeDevs\Dokan\Commission\Formula\Fixed; -use WeDevs\Dokan\Upgrade\Upgrades\BackgroundProcesses\V_3_14_0_UpdateCommissions; +use WeDevs\Dokan\Commission\Formula\Flat; +use WeDevs\Dokan\Commission\Formula\Percentage; +use WeDevs\Dokan\Commission\Upugrader\Update_Category_Commission; +use WeDevs\Dokan\Commission\Upugrader\Update_Product_Commission; +use WeDevs\Dokan\Commission\Upugrader\Update_Vendor_Commission; class V_3_14_0 extends DokanUpgrader { @@ -16,125 +20,46 @@ class V_3_14_0 extends DokanUpgrader { * @return void */ public static function update_global_commission_type() { - $options = get_option( 'dokan_selling', [] ); - $commission_type = isset( $options['commission_type'] ) ? $options['commission_type'] : Fixed::SOURCE; + $options = get_option( 'dokan_selling', [] ); + + $commission_type = isset( $options['commission_type'] ) ? $options['commission_type'] : Fixed::SOURCE; + $admin_percentage = isset( $options['admin_percentage'] ) ? $options['admin_percentage'] : 0; if ( in_array( $commission_type, array_keys( dokan()->commission->get_legacy_commission_types() ), true ) ) { $options['commission_type'] = Fixed::SOURCE; - update_option( 'dokan_selling', $options ); - } - } - /** - * Update global category commisions. - * - * @since DOKAN_SINCE - * - * @return void - */ - public static function update_global_category_commission_types() { - $has_categories = true; - $page_number = 1; - $processor = new V_3_14_0_UpdateCommissions(); - - while ( $has_categories ) { - $args = [ - 'taxonomy' => 'product_cat', - 'number' => 20, - 'orderby' => 'name', - 'order' => 'ASC', - 'hide_empty' => false, - 'offset' => ( $page_number - 1 ) * 20, - ]; - - $terms = get_terms( $args ); - - if ( empty( $terms ) ) { - $has_categories = false; - } else { - $args = [ - 'data' => $terms, - 'task' => 'global-commission', - ]; - $processor->push_to_queue( $args ); + if ( Flat::SOURCE === $commission_type ) { + $options['admin_percentage'] = 0; + $options['additional_fee'] = $admin_percentage; + } elseif ( Percentage::SOURCE === $commission_type ) { + $options['admin_percentage'] = $admin_percentage; + $options['additional_fee'] = 0; } - - ++$page_number; + update_option( 'dokan_selling', $options ); } - - $processor->dispatch_process(); } /** - * Update vendor comission settings. + * Update vendor and product comission settings. * * @since DOKAN_SINCE * * @return void */ - public static function update_category_commission_of_vendors() { - $page_number = 1; - $has_vendors = true; - $processor = new V_3_14_0_UpdateCommissions(); - - while ( $has_vendors ) { - $vendors = dokan()->vendor->all( - [ - 'paged' => $page_number, - ] - ); - - if ( empty( $vendors ) ) { - $has_vendors = false; - } else { - $args = [ - 'data' => $vendors, - 'task' => 'vendors-commission', - ]; - $processor->push_to_queue( $args ); - } - - ++$page_number; + public static function update_commission() { + $product_scheduler = new Update_Product_Commission(); + if ( ! $product_scheduler->is_processing() ) { + $product_scheduler->start_processing(); } - $processor->dispatch_process(); - } - - /** - * Update product commission settings. - * - * @since DOKAN_SINCE - * - * @return void - */ - public static function update_category_commission_of_products() { - $page_number = 1; - $has_products = true; - $processor = new V_3_14_0_UpdateCommissions(); - - while ( $has_products ) { - $products = dokan()->product->all( - [ - 'posts_per_page' => 10, - 'paged' => $page_number, - ] - ); - - $products = $products->posts; - - if ( empty( $products ) ) { - $has_products = false; - } else { - $args = [ - 'data' => $products, - 'task' => 'products-commission', - ]; - $processor->push_to_queue( $args ); - } - - ++$page_number; + $vendor_scheduler = new Update_Vendor_Commission(); + if ( ! $vendor_scheduler->is_processing() ) { + $vendor_scheduler->start_processing(); } - $processor->dispatch_process(); + $category_scheduler = new Update_Category_Commission(); + if ( ! $category_scheduler->is_processing() ) { + $category_scheduler->start_processing(); + } } } diff --git a/src/admin/components/AdminNotice.vue b/src/admin/components/AdminNotice.vue index 69d02dd2ba..88fce3fee8 100644 --- a/src/admin/components/AdminNotice.vue +++ b/src/admin/components/AdminNotice.vue @@ -62,6 +62,10 @@ export default { type: Number, default: 5000 }, + scope: { + type: String, + default: '' + } }, data() { @@ -82,8 +86,9 @@ export default { methods: { fetch() { + const notice_scope = this.scope ? `?scope=${this.scope}` : ''; $.ajax( { - url: `${dokan_promo.rest.root}${dokan_promo.rest.version}/admin/notices/${this.endpoint}`, + url: `${dokan_promo.rest.root}${dokan_promo.rest.version}/admin/notices/${this.endpoint}${notice_scope}`, method: 'get', beforeSend: function ( xhr ) { xhr.setRequestHeader( 'X-WP-Nonce', dokan_promo.rest.nonce ); diff --git a/src/admin/components/CombineInput.vue b/src/admin/components/CombineInput.vue index 3f44f660cb..f4951985ab 100644 --- a/src/admin/components/CombineInput.vue +++ b/src/admin/components/CombineInput.vue @@ -81,8 +81,6 @@ import Debounce from "debounce"; let newPercentage = this.validatePercentage( newVal.percentage ); let oldPercentage = this.validatePercentage( oldVal.percentage ); - console.log(newPercentage, oldPercentage); - if ( ! newPercentage || '' === newPercentage || Number( newPercentage ) < 0 || Number( newPercentage ) > 100 ) { newPercentage = oldPercentage; } diff --git a/src/admin/components/Commission/CategoryBasedCommission.vue b/src/admin/components/Commission/CategoryBasedCommission.vue index 754e0eaf35..c380948270 100644 --- a/src/admin/components/Commission/CategoryBasedCommission.vue +++ b/src/admin/components/Commission/CategoryBasedCommission.vue @@ -19,7 +19,7 @@

{{__( 'All Categories', 'dokan-lite' )}}

@@ -32,7 +32,7 @@ id="percentage_commission" name="percentage_commission" ref='percentage' - :value="commission.all.percentage" + :value="formatValue( commission.all.percentage )" v-on:input="e => handleAllCategoryInput(e.target.value, 'percentage', commission.all.percentage )" style="border: none !important;" /> @@ -49,7 +49,7 @@ id="fixed_commission" name="fixed_commission" ref='fixed' - :value="commission.all.flat" + :value="formatValue( commission.all.flat )" v-on:input="e => handleAllCategoryInput(e.target.value, 'flat', commission.all.flat )" style="border: none !important;" /> @@ -66,7 +66,7 @@

- {{ item.name }} + #{{ item.term_id }}

@@ -79,7 +79,7 @@ id="percentage_commission" name="percentage_commission" ref='percentage' - :value="getCommissionValue( 'percentage', item.term_id )" + :value="formatValue( getCommissionValue( 'percentage', item.term_id ) )" v-on:input="e => commissinItemHandler( e.target.value, 'percentage', item.term_id, getCommissionValue( 'percentage', item.term_id ) )" style="border: none !important;" /> @@ -96,8 +96,8 @@ id="fixed_commission" name="fixed_commission" ref='flat' - :value="getCommissionValue( 'flat', item.term_id )" - v-on:input="e => commissinItemHandler( e.target.value, 'flat', item.term_id, getCommissionValue( 'percentage', item.term_id ) )" + :value="formatValue( getCommissionValue( 'flat', item.term_id ) )" + v-on:input="e => commissinItemHandler( e.target.value, 'flat', item.term_id, getCommissionValue( 'flat', item.term_id ) )" style="border: none !important;" /> @@ -108,6 +108,8 @@ diff --git a/src/admin/components/Fields.vue b/src/admin/components/Fields.vue index 4210fab88d..bb9a9ec9ef 100644 --- a/src/admin/components/Fields.vue +++ b/src/admin/components/Fields.vue @@ -108,10 +108,10 @@ /> -

+

{{ getError( fieldData.label ) }}

-

+

{{ getValidationErrorMessage( fieldData.name ) }}

diff --git a/src/admin/notice/App.vue b/src/admin/notice/App.vue new file mode 100644 index 0000000000..f0c4882d33 --- /dev/null +++ b/src/admin/notice/App.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/src/admin/notice/main.js b/src/admin/notice/main.js new file mode 100644 index 0000000000..bdd5a2befa --- /dev/null +++ b/src/admin/notice/main.js @@ -0,0 +1,14 @@ +import Vue from 'vue'; +import App from './App.vue'; +import Mixin from '../../utils/Mixin'; + +const { jQuery: $ } = window; + +if ( $( '#dokan-admin-notices' ).length ) { + Vue.mixin( Mixin ); + + new Vue( { + el: '#dokan-admin-notices', + render: ( h ) => h( App ), + } ); +} diff --git a/src/admin/pages/Settings.vue b/src/admin/pages/Settings.vue index e58739efc1..522f88d425 100644 --- a/src/admin/pages/Settings.vue +++ b/src/admin/pages/Settings.vue @@ -433,13 +433,36 @@ if ( ! this.errors.includes( field ) ) { this.errors.push( field ); - // If flat or percentage commission is set. Remove the required field. - if ( 'flat' === value['commission_type'] || 'percentage' === value['commission_type'] ) { + if ( 'flat' === value['commission_type'] || 'percentage' === value['commission_type'] || 'combine' === value['commission_type'] || 'fixed' === value['commission_type'] ) { + this.errors = this.arrayRemove( this.errors, 'commission_category_based_values' ); + } + + if ( 'category_based' === value['commission_type'] ) { this.errors = this.arrayRemove( this.errors, 'admin_percentage' ); this.errors = this.arrayRemove( this.errors, 'additional_fee' ); } } } + + if ( field in value && 'category_based' === value['commission_type'] ) { + let alreadyAdded = ! this.errors.includes( field ); + + // Validate the commission_category_based_values + if ( + 'commission_category_based_values' in value && + typeof value['commission_category_based_values'] === 'object' && + value?.commission_category_based_values?.all && + value?.commission_category_based_values?.all?.flat && + String( value?.commission_category_based_values?.all?.flat ).length > 0 && + value?.commission_category_based_values?.all?.percentage && + String( value?.commission_category_based_values?.all?.percentage ).length > 0 && + alreadyAdded + ) { + this.errors = this.arrayRemove( this.errors, 'commission_category_based_values' ); + } else { + this.errors.push( 'commission_category_based_values' ); + } + } } ); } ); @@ -1096,7 +1119,7 @@ } .metabox-holder { - width: 40%; + width: 100%; .settings-header { display: block; @@ -1129,7 +1152,7 @@ @media only screen and (max-width: 768px) { .dokan-settings-wrap { .nav-tab-wrapper { - width: 35% !important; + width: 35%; .nav-tab { .nav-content { @@ -1145,7 +1168,7 @@ } .metabox-holder { - width: 65%; + width: 100%; .settings-header { .settings-content { diff --git a/src/admin/pages/VendorSingle.vue b/src/admin/pages/VendorSingle.vue index 2295d5f37f..fb78b5d75d 100644 --- a/src/admin/pages/VendorSingle.vue +++ b/src/admin/pages/VendorSingle.vue @@ -574,22 +574,7 @@ export default { } if ( ! response.admin_commission_type ) { - this.store.admin_commission_type = 'flat'; - } - - if ( this.store.admin_additional_fee ) { - this.store.admin_additional_fee = accounting.formatNumber( this.store.admin_additional_fee, dokan.currency.precision, dokan.currency.thousand, dokan.currency.decimal, dokan.currency.format ); - } - - /** - * if admin commission type is flat and no admin commission is set then it will not not set - * - * if admin commission type is flat and admin commission is set to 0 then it will show 0 - * - * blank string is for not set - */ - if (this.store.admin_commission_type === 'flat' && this.store.admin_commission !== '') { - this.store.admin_commission = accounting.formatNumber( this.store.admin_commission, dokan.currency.precision, dokan.currency.thousand, dokan.currency.decimal, dokan.currency.format ); + this.store.admin_commission_type = ''; } }, diff --git a/tests/pw/feature-map/feature-map.yml b/tests/pw/feature-map/feature-map.yml index 8ab3067c01..1b6367e91f 100644 --- a/tests/pw/feature-map/feature-map.yml +++ b/tests/pw/feature-map/feature-map.yml @@ -455,7 +455,7 @@ features: vendor: vendor can view store settings menu page [lite]: true - vendor can view Shipstation settings menu page: true + vendor can view ShipStation settings menu page: true vendor can view social profile settings menu page: true vendor can view rma settings menu page: true vendor can view store seo settings menu page: true @@ -651,6 +651,15 @@ admin can refresh license: true admin can deactivate license: true +- page: 'Feature lock' + features: + admin: + admin can view license expiration notice: false + admin can view expired license notice: false + admin can view license renewal prompt after license expired: false + admin can't enable module after license expired: false + admin can't alter pro settings after license expired: false + - page: 'Modules' features: admin: @@ -665,18 +674,28 @@ - page: 'Auction Integration' features: admin: + admin can enable auction integration module: false + admin can disable auction integration module: false + admin can enable auction for newly added vendor: false + admin can disable auction for specific vendor: false admin can add auction product: true + admin can view auctions activity: false + admin can filter auctions activity: false + admin can export auctions activity: false + admin can view auctions winners: false + admin can filter auctions winners: false + admin can export auctions winners: false vendor: vendor can view auction menu page: true + vendor can search auction product: true + vendor can filter auction product by date: false + vendor can reset auction product filter: false + vendor can't bid own product: true vendor can add auction product: true - vendor can edit auction product: true vendor can view auction product: true - vendor can't bid own product: true - vendor can search auction product: true + vendor can duplicate auction product: true vendor can permanently delete auction product: true - vendor can view auction activity page: true - vendor can filter auction activity: true - vendor can search auction activity: true + vendor can edit auction product: true vendor can update auction product title: true vendor can update auction product category (single): true vendor can add auction product category (multiple): true @@ -725,14 +744,22 @@ vendor can import auction product addon: true vendor can export auction product addon: true vendor can remove auction product addon: true - + vendor can view auction activity page: true + vendor can filter auctions activity by date: false + vendor can reset auctions activity filter: false + vendor can search auctions activity (auction): false + vendor can search auctions activity (name): false + vendor can search auctions activity (email): false customer: customer can bid auction product: true + customer can buy auction product after win: false customer can buy auction product with buy it now price: true - page: 'Color Scheme Customizer' features: admin: + admin can enable color scheme customizer module: false + admin can disable color scheme customizer module: false admin can switch predefined color palette: true admin can add custom color palette: true admin can update custom color palette: true @@ -740,13 +767,30 @@ - page: 'Delivery Time' features: admin: + admin can enable delivery time module: false + admin can disable delivery time module: false + admin can allow vendor to override delivery time settings: false + admin can set delivery support options: false + admin can set delivery date label: false + admin can set delivery blocked buffer: false + admin can set delivery time slot: false + admin can set delivery order per slot: false + admin can set delivery box info: false + admin can require delivery date and time: false + admin can set delivery days: false admin can update delivery time on order details: false vendor: vendor can view delivery time menu page: true + vendor can change view style of delivery time calendar: true + vendor can filter delivery time: true + vendor can view delivery time orders on calendar: false vendor can view delivery time settings menu page: true vendor can set delivery time settings: true - vendor can filter delivery time: true - vendor can change view style of delivery time calendar: true + vendor can set delivery support options: false + vendor can set delivery blocked buffer: false + vendor can set delivery time slot: false + vendor can set delivery order per slot: false + vendor can set delivery days: false vendor can update delivery time on order details: false customer: customer can buy product with delivery time: true @@ -755,11 +799,14 @@ - page: 'Elementor' features: admin: - no test is written for elementor module (out of scope): false + admin can enable Elementor module: false + admin can disable Elementor module: false - page: 'EU Compliance Fields' features: admin: + admin can enable eu compliance fields module: false + admin can disable eu compliance fields module: false admin can enable EU compliance fields for vendors: true admin can enable EU compliance fields on vendor registration: true admin can enable EU compliance fields for customers: true @@ -800,6 +847,9 @@ - page: 'Follow Store' features: + admin: + admin can enable follow store module: false + admin can disable follow store module: false vendor: vendor can view followers menu page: true vendor can view followers: true @@ -814,6 +864,8 @@ - page: 'Geolocation' features: admin: + admin can enable geolocation module: false + admin can disable geolocation module: false # admin can set Dokan geolocation settings [duplicate]: false admin can set map location position (top): true admin can set map location position (left): true @@ -825,17 +877,19 @@ admin can disable filters before location map: true admin can enable product location tab on single product page: true admin can disable product location tab on single product page: true - admin can set map radius search unit and distance: false - admin can set map zoom level: false - admin can set map default location: false - admin can add geolocation widget (product location) widget: false - admin can add geolocation widget (store location) widget: false - admin can add geolocation widget (geolocation filter form) widget: false - # admin can add geolocation filters shortcode [duplicate]: false - # vendor: + admin can set map radius search unit and distance (km): true + admin can set map radius search unit and distance (miles): true + # admin can set map zoom level: false + # admin can set map default location: false + # admin can add geolocation widget (product location) widget: false + # admin can add geolocation widget (store location) widget: false + # admin can add geolocation widget (geolocation filter form) widget: false + # admin can add geolocation filters shortcode [duplicate]: true + # vendor: # vendor can add location to store [duplicate]: false # vendor can add location to product [duplicate]: false - # customer: + customer: + customer can slide map radius bar: true # customer can view map on shop page [duplicate]: false # customer can view map on store list page [duplicate]: false # customer can view map on single product page [duplicate]: false @@ -847,6 +901,8 @@ - page: 'Live Chat' features: admin: + admin can enable live chat module: false + admin can disable live chat module: false # admin can set Dokan live chat settings [duplicate]: true admin can enable chat button on vendor page: true admin can disable chat button on vendor page: true @@ -862,8 +918,10 @@ - page: 'Live Search' features: - # admin: - # admin can set Dokan live search settings [duplicate]: true + admin: + admin can enable live search module: false + admin can disable live search module: false + # admin can set Dokan live search settings [duplicate]: true customer: customer can search product using live search (suggestion box): true customer can search product with category using live search (suggestion box): true @@ -873,22 +931,52 @@ - page: 'MangoPay' features: admin: - admin can add Mangopay payment method: true + admin can enable MangoPay module: false + admin can disable MangoPay module: false + admin can add MangoPay payment method: true + admin can remove MangoPay payment method: false + admin can view MangoPay notes on order details: false + admin can refund order using MangoPay payment method: false + vendor: + vendor can add MangoPay payment method: false + vendor can remove MangoPay payment method: false + admin can refund order using MangoPay payment method: false + customer: + customer can buy product using MangoPay payment method: false - page: 'Min Max Quantities' features: + admin: + admin can enable min max quantities module: false + admin can disable min max quantities module: false vendor: vendor can add product min-max options: true vendor can set min-max settings: true + customer: + customer can buy product following min-max quantities rule: false + customer can buy product following min-max amount rule: false - page: 'PayPal Marketplace' features: admin: - admin can add Paypal Marketplace payment method: true + admin can enable PayPal Marketplace module: false + admin can disable PayPal Marketplace module: false + admin can add PayPal Marketplace payment method: true + admin can remove PayPal Marketplace payment method: false + admin can view PayPal Marketplace notes on order details: false + admin can refund order using PayPal Marketplace payment method: false + vendor: + vendor can add PayPal Marketplace payment method: false + vendor can remove PayPal Marketplace payment method: false + admin can refund order using PayPal Marketplace payment method: false + customer: + customer can buy product using PayPal Marketplace payment method: false - page: 'Printful' features: - # admin: + admin: + admin can enable Printful module: false + admin can disable Printful module: false # admin can set Dokan Printful settings: true [duplicate]: true vendor: vendor can view printful menu page: true @@ -904,6 +992,9 @@ - page: 'Product Addon' features: + admin: + admin can enable product addon module: false + admin can disable product addon module: false vendor: vendor can view product addons menu page: true vendor can add global product addon: true @@ -912,6 +1003,7 @@ vendor can export global product addon field: true vendor can remove product addon field: true vendor can add product addon: true + vendor can update product addon: false vendor can import product addon: true vendor can export product addon: true vendor can remove product addon: true @@ -922,6 +1014,8 @@ - page: 'Product Advertising' features: admin: + admin can enable product advertising module: false + admin can disable product advertising module: false # admin can set Dokan product advertising settings [duplicate]: true admin can set product advertising slot: false admin can set product advertising expiry days: false @@ -953,6 +1047,9 @@ - page: 'Product Enquiry' features: + admin: + admin can enable product enquiry module: false + admin can disable product enquiry module: false customer: customer can enquire product: true guest: @@ -961,6 +1058,8 @@ - page: 'Product Q&A' features: admin: + admin can enable product Q&A module: false + admin can disable product Q&A module: false admin can view product QA menu page: true admin can view product question details: true admin can filter questions by vendor: true @@ -989,6 +1088,9 @@ - page: 'Product Subscription' features: + admin: + admin can enable product subscription module: false + admin can disable product subscription module: false vendor: vendor can view user subscriptions menu page: true vendor can view product subscription details: true @@ -1007,16 +1109,32 @@ - page: 'Rank Math SEO' features: admin: - no test is written for rank math seo module: false + admin can enable Rank Math SEO module: false + admin can disable Rank Math SEO module: false + vendor: + vendor can view rank math seo options on product edit page: false - page: 'Razorpay' features: admin: + admin can enable Razorpay module: false + admin can disable Razorpay module: false admin can add Razorpay payment method: true + admin can remove Razorpay payment method: false + admin can view Razorpay notes on order details: false + admin can refund order using Razorpay payment method: false + vendor: + vendor can add Razorpay payment method: false + vendor can remove Razorpay payment method: false + admin can refund order using Razorpay payment method: false + customer: + customer can buy product using Razorpay payment method: false - page: 'Report Abuse' features: admin: + admin can enable report abuse module: false + admin can disable report abuse module: false admin can view abuse reports menu page: true admin can view abuse report details: true admin can filter abuse reports by abuse reason: true @@ -1031,44 +1149,74 @@ - page: 'Request for Quotation' features: + admin: + admin can enable RFQ module: false + admin can disable RFQ module: false + admin can add RFQ shortcode: false + admin can enable quote for out of stock products: false + admin can enable ajax Add to quote: false + admin can set quote redirect settings: false + admin can set offered price: false + admin can enable customer can convert quote to order: false + admin can enable quote converter display: false quote rules: - admin: - admin can view quote rules menu page: true - admin can add quote rule: true - admin can edit quote rule: true - admin can trash quote rule: true - admin can restore quote rule: true - admin can permanently delete quote rule: true - admin can perform bulk action on quote rules: true - quotes: - admin: - admin can view quotes menu page: true - admin can add quote: true - admin can edit quote: true - admin can trash quote: true - admin can restore quote: true - admin can permanently delete quote: true - admin can approve quote: true - admin can convert quote to order: true - admin can perform quote bulk actions: true - vendor: - vendor can view request quotes menu page: true - vendor can view request quote details: true - vendor can update quote request: true - vendor can approve quote request: true - vendor can convert quote request to order: true - customer: - customer can view request for quote menu page: true - customer can view requested quote page: true - customer can view requested quote details: true - customer can update quote request: true - customer can pay for order converted from quote request: true - customer can quote product: true - guest: - guest customer can quote product: true + admin can view quote rules menu page: true + admin can add quote rule: true + admin can edit quote rule: true + admin can trash quote rule: true + admin can restore quote rule: true + admin can permanently delete quote rule: true + admin can perform bulk action on quote rules: true + quote requests: + admin can view quotes menu page: true + admin can add quote: true + admin can edit quote: true + admin can update quote: false + admin can trash quote: true + admin can restore quote: true + admin can permanently delete quote: true + admin can approve quote: true + admin can reject quote: false + admin can reopen quote: false + admin can convert quote to order: true + admin can perform quote bulk actions: true + admin can filter quote requests by status: false + admin can filter quote requests by date: false + admin can filter quote requests by customer: false + admin can reset quote filter: false + admin can add quote shipping cost on order: false + admin can remove quote shipping cost on order: false + vendor: + vendor can view request quotes menu page: true + vendor can view request quote details: true + vendor can search quote: false + vendor can filter quote by status: false + vendor can update quote request: true + vendor can approve quote request: true + vendor can reject quote request: false + vendor can reopen quote request: false + vendor can delete quote request: false + vendor can convert quote request to order: true + vendor can enable rfq when catalog mode is enabled: false + customer: + customer can view request for quote menu page: true + customer can view requested quote page: true + customer can view requested quote details: true + customer can clear quote cart: false + customer can update quote request: true + customer can accept quote request: false + customer can cancel quote request: false + customer can pay for order converted from quote request: true + customer can quote product: true + customer can filter quotes by status: false + guest: + guest customer can quote product: true - page: 'Return and Warranty Request' features: + admin: + admin can enable RMA module: false + admin can disable RMA module: false vendor: vendor can view return request menu page: true vendor can view return request details: true @@ -1079,11 +1227,14 @@ customer: customer can view return request menu page: true customer can request warranty: true + customer can request warranty (replace): false customer can send rma message: true - page: 'Seller Badge' features: admin: + admin can enable seller badge module: false + admin can disable seller badge module: false admin can view seller badge menu page: true admin can preview seller badge: true admin can view seller badge details: true @@ -1102,14 +1253,31 @@ vendor can search seller badge: true vendor can filter seller badges: true +- page: 'Seller Vacation' + features: + admin: + admin can enable seller vacation module: false + admin can disable seller vacation module: false + vendor: + vendor can add vacation (instant): false + vendor can add vacation (datewise): false + - page: 'ShipStation Integration' features: + admin: + admin can enable ShipStation Integration module: false + admin can disable ShipStation Integration module: false vendor: - vendor can set shipStation settings: true + # vendor can view ShipStation settings menu page: true + vendor can generate ShipStation credentials: true + vendor can revoke ShipStation credentials: true + vendor can set ShipStation settings: true - page: 'Single Product Multiple Vendor (SPMV)' features: admin: + admin can enable SPMV module: false + admin can disable SPMV module: false admin can assign SPMV product to other vendor: true vendor: vendor can view SPMV menu page: true @@ -1130,6 +1298,8 @@ - page: 'Store Reviews' features: admin: + admin can enable store reviews module: false + admin can disable store reviews module: false admin can view store reviews menu page: true admin can view store review: true admin can edit store review: true @@ -1148,6 +1318,8 @@ - page: 'Store Support' features: admin: + admin can enable store support module: false + admin can disable store support module: false admin can view store support menu page: true unread count decrease after admin viewing a support ticket: true admin can view support ticket details: true @@ -1192,29 +1364,62 @@ - page: 'Stripe Connect' features: admin: - admin can add stripe payment method: true + admin can enable Stripe Connect module: false + admin can disable Stripe Connect module: false + admin can add Stripe Connect payment method: true + admin can remove Stripe Connect payment method: false + admin can view Stripe Connect notes on order details: false + admin can refund order using Stripe Connect payment method: false + vendor: + vendor can add Stripe Connect payment method: false + vendor can remove Stripe Connect payment method: false + admin can refund order using Stripe Connect payment method: false + customer: + customer can buy product using Stripe Connect payment method: false - page: 'Stripe Express' features: admin: - admin can add Strip Express payment method: true + admin can enable Stripe Express module: false + admin can disable Stripe Express module: false + admin can add Stripe Express payment method: true + admin can remove Stripe Express payment method: false + admin can view Stripe Express notes on order details: false + admin can refund order using Stripe Express payment method: false + vendor: + vendor can add Stripe Express payment method: false + vendor can remove Stripe Express payment method: false + admin can refund order using Stripe Express payment method: false + customer: + customer can buy product using Stripe Express payment method: false - page: 'Table Rate Shipping' features: admin: + admin can enable table rate shipping module: false + admin can disable table rate shipping module: false admin can add vendor table rate shipping method: true admin can add vendor distance rate shipping method: true vendor: vendor can add table rate shipping: true vendor can add distance rate shipping: true + customer: + customer can purchase product using table rate shipping: false + customer can purchase product using distance rate shipping: false - page: 'Vendor Analytics' features: + admin: + admin can enable vendor analytics module: false + admin can disable vendor analytics module: false vendor: vendor can view analytics menu page: true - page: 'Vendor Product Importer and Exporter' features: + admin: + admin can enable product importer and exporter module: false + admin can disable product importer and exporter module: false vendor: vendor can view tools menu page: true vendor can export product as xml: true @@ -1224,6 +1429,9 @@ - page: 'Vendor Staff Manager' features: + admin: + admin can enable vendor staff manager module: false + admin can disable vendor staff manager module: false vendor: vendor can view staff menu page: true vendor can add new staff: true @@ -1236,6 +1444,8 @@ - page: 'Vendor Subscription' features: admin: + admin can enable vendor subscription module: false + admin can disable vendor subscription module: false admin can view subscriptions menu page: true admin can filter subscribed vendors by vendor: true admin can filter subscribed vendors by subscription pack: true @@ -1256,6 +1466,8 @@ - page: 'Vendor Verification' features: admin: + admin can enable vendor verification module: false + admin can disable vendor verification module: false admin can change verified icon: true admin can add vendor verification method: true admin can edit vendor verification method: true @@ -1295,6 +1507,8 @@ - page: 'Wholesale' features: admin: + admin can enable wholesale module: false + admin can disable wholesale module: false admin can view wholesale customers menu page: true admin can search wholesale customer: true admin can disable customer's wholesale capability: true @@ -1303,7 +1517,7 @@ admin can view wholesale customer orders: true admin can delete wholesale customer: true admin can perform bulk action on wholesale customers: true - All users can see wholesale price: true + all users can see wholesale price: true vendor: vendor can create wholesale product: true customer: @@ -1317,6 +1531,8 @@ - page: 'WooCommerce Booking Integration' features: admin: + admin can enable woocommerce booking integration module: false + admin can disable woocommerce booking integration module: false admin can add booking product: true vendor: vendor can view booking menu page: true diff --git a/tests/pw/package.json b/tests/pw/package.json index a30b0a7f40..f7b2cc3e47 100644 --- a/tests/pw/package.json +++ b/tests/pw/package.json @@ -13,6 +13,7 @@ "site:reset": "NO_SETUP=false npm run site:setup && npm run env:setup", "site:setup": "npx playwright test --project=local_site_setup --config=e2e.config.ts", "env:setup": "npx playwright test --project=e2e_setup --config=e2e.config.ts", + "auth:setup": "NO_SETUP=true npx playwright test --project=auth_setup --config=e2e.config.ts", "test:api": "npx playwright test --project=api_tests --config=api.config.ts", "test:e2e": "npx playwright test --project=e2e_tests --config=e2e.config.ts", "test:api:pro": "DOKAN_PRO=true npx playwright test --project=api_tests --config=api.config.ts", diff --git a/tests/pw/pages/commissionPage.ts b/tests/pw/pages/commissionPage.ts index a59d7e8c9f..f21a027591 100644 --- a/tests/pw/pages/commissionPage.ts +++ b/tests/pw/pages/commissionPage.ts @@ -22,26 +22,26 @@ export class CommissionPage extends AdminPage { // await this.selectByValue(setupWizardAdmin.commissionType, commission.commissionType); await this.clearAndType(setupWizardAdmin.percentage, commission.commissionPercentage); - await this.wait(0.5); //todo: need to resolve + await this.wait(1); //todo: need to resolve await this.clearAndType(setupWizardAdmin.fixed, commission.commissionFixed); - await this.wait(0.5); + await this.wait(1); } else { // await this.selectByValueAndWaitForResponse(data.subUrls.api.dokan.multistepCategories, setupWizardAdmin.commissionType, commission.commissionType); if (commission.commissionCategory.allCategory) { await this.clearAndType(setupWizardAdmin.categoryPercentage(commission.commissionCategory.category), commission.commissionPercentage); - await this.wait(0.5); + await this.wait(1); await this.clearAndType(setupWizardAdmin.categoryFixed(commission.commissionCategory.category), commission.commissionFixed); - await this.wait(0.5); + await this.wait(1); } else { const categoryExpanded = await this.isVisible(setupWizardAdmin.expandedCategories); if (!categoryExpanded) { await this.click(setupWizardAdmin.expandCategories); } await this.clearAndType(setupWizardAdmin.categoryPercentageById(commission.commissionCategory.category), commission.commissionPercentage); - await this.wait(0.5); + await this.wait(1); await this.clearAndType(setupWizardAdmin.categoryFixedById(commission.commissionCategory.category), commission.commissionFixed); - await this.wait(0.5); + await this.wait(1); } } } @@ -124,7 +124,7 @@ export class CommissionPage extends AdminPage { // set commission for vendor async setCommissionForVendor(sellerId: string, commission: commission) { - await this.goto(data.subUrls.backend.dokan.vendorDetailsEdit(sellerId)); + await this.gotoUntilNetworkidle(data.subUrls.backend.dokan.vendorDetailsEdit(sellerId)); await this.selectByValue(vendors.editVendor.commissionType, commission.commissionType); @@ -141,14 +141,14 @@ export class CommissionPage extends AdminPage { // set commission to product async setCommissionForProduct(productId: string, commission: commission) { - await this.goto(data.subUrls.backend.wc.productDetails(productId)); + await this.gotoUntilNetworkidle(data.subUrls.backend.wc.productDetails(productId)); // add commission await this.click(productsAdmin.product.subMenus.advanced); await this.clearAndType(productsAdmin.product.advanced.commissionPercentage, commission.commissionPercentage); - await this.wait(0.5); + await this.wait(1); await this.clearAndType(productsAdmin.product.advanced.commissionFixed, commission.commissionFixed); - await this.wait(0.5); + await this.wait(1); // update product await this.clickAndWaitForResponseAndLoadState(data.subUrls.ajax, productsAdmin.product.publish); diff --git a/tests/pw/pages/geolocationPage.ts b/tests/pw/pages/geolocationPage.ts index b34d88ae97..31b1b52ba1 100644 --- a/tests/pw/pages/geolocationPage.ts +++ b/tests/pw/pages/geolocationPage.ts @@ -97,4 +97,32 @@ export class GeolocationPage extends AdminPage { break; } } + + // view map radius unit and distance + async viewMapRadiusSearchUnitAndDistance(unit: 'km' | 'miles', distance: { min: string; max: string }): Promise { + await this.gotoUntilNetworkidle(data.subUrls.frontend.shop); + + switch (unit) { + case 'km': + await this.toContainText(selector.customer.cShop.radiusSearch.radiusUnit, `Radius ${distance.max}km`); + break; + + case 'miles': + await this.toContainText(selector.customer.cShop.radiusSearch.radiusUnit, `Radius ${distance.max}miles`); + break; + + default: + break; + } + } + + async slideMapRadiusBar(slideUnit: string): Promise { + await this.gotoUntilNetworkidle(data.subUrls.frontend.shop); + await this.focus(selector.customer.cShop.radiusSearch.slider); + await this.setAttributeValue(selector.customer.cShop.radiusSearch.slider, 'value', '0'); + for (let i = 0; i < Number(slideUnit); i++) { + await this.press('ArrowRight'); + } + await this.toHaveValue(selector.customer.cShop.radiusSearch.slider, slideUnit); + } } diff --git a/tests/pw/pages/noticeAndPromotionPage.ts b/tests/pw/pages/noticeAndPromotionPage.ts index 6a836457c1..d37193ca57 100644 --- a/tests/pw/pages/noticeAndPromotionPage.ts +++ b/tests/pw/pages/noticeAndPromotionPage.ts @@ -91,7 +91,7 @@ export class NoticeAndPromotionPage extends AdminPage { await this.notToBeVisible(selector.admin.dokan.diagnostic.noticeDiv); } - // allow diagnostic tracking + // disallow diagnostic tracking async disallowDiagnosticTracking() { await this.goIfNotThere(data.subUrls.backend.adminDashboard, 'networkidle'); await this.clickAndWaitForLoadState(selector.admin.dokan.diagnostic.disallowCollectData); diff --git a/tests/pw/pages/selectors.ts b/tests/pw/pages/selectors.ts index 0726386a56..9d534059c0 100644 --- a/tests/pw/pages/selectors.ts +++ b/tests/pw/pages/selectors.ts @@ -223,14 +223,14 @@ export const selector = { // dokan promotion promotion: { - promotion: '.dokan-notice-slides .dokan-promotion', - message: '.dokan-promotion .dokan-message', + promotion: 'div.dokan-notice-slides div.dokan-promotion', + message: 'div.dokan-promotion div.dokan-message', }, // dokan notice notice: { - noticeDiv: '.dokan-admin-notices', - noticeDiv1: '.dokan-admin-notice.dokan-alert', + noticeDiv: 'div.dokan-admin-notices', + noticeDiv1: '//div[@class="dokan-admin-notices"]//div[contains(@class,"dokan-admin-notice")and not(contains(@class,"dokan-promotion"))]', closeNotice: '.close-notice', slider: '.slide-notice', sliderPrev: '.slide-notice .prev', @@ -5708,6 +5708,8 @@ export const selector = { edit: (productName: string) => `//a[normalize-space()="${productName}"]/../..//span[@class="edit"]`, permanentlyDelete: (productName: string) => `//a[normalize-space()="${productName}"]/../..//span[@class="delete"]`, view: (productName: string) => `//a[normalize-space()="${productName}"]/../..//span[@class="view"]`, + duplicate: (productName: string) => `//a[normalize-space()="${productName}"]/../..//span[@class="duplicate"]`, + duplicateSuccessMessage: '//div[contains(@class,"dokan-alert dokan-alert-success")]//strong[normalize-space(text())="Product successfully duplicated"]', confirmDelete: '.swal2-confirm', cancelDelete: '.swal2-cancel', @@ -6545,7 +6547,18 @@ export const selector = { shipStationText: '.dokan-settings-content h1', visitStore: '//a[normalize-space()="Visit Store"]', - authenticationKey: '//label[normalize-space()="Authentication Key"]/..//code', + generateCredentials: 'button#dokan-shipstation-generate-credentials-btn', + generateSuccessMessage: '//div[@id="swal2-html-container" and normalize-space()="API credentials generated successfully."]', + revokeCredentials: 'button#dokan-shipstation-revoke-credentials-btn', + confirmRevoke: 'button.swal2-confirm', + revokeSuccessMessage: '//div[@id="swal2-html-container" and normalize-space()="API credentials revoked successfully."]', + + credentials: { + authenticationKey: '//label[normalize-space()="Authentication Key"]/..//code', + consumerKey: '//label[normalize-space()="Consumer Key"]/..//code', + consumerSecret: '//label[normalize-space()="Consumer Secret"]/..//code', + }, + selectedStatus: '//label[@for="dokan-shipstation-export-statuses"]/..//li[@class="select2-selection__choice"]', exportOrderStatusesInput: '//label[normalize-space()="Export Order Statuses"]/..//span[@class="select2-selection select2-selection--multiple"]//input[@class="select2-search__field"]', shippedOrderStatusDropdown: '.select2-selection__arrow', @@ -6553,8 +6566,7 @@ export const selector = { result: '.select2-results__option.select2-results__option--highlighted', saveChanges: '#dokan-store-shipstation-form-submit', - saveSuccessMessage: '#swal2-html-container', - successOk: '.swal2-confirm', + saveSuccessMessage: '//div[@id="swal2-html-container" and normalize-space()="Settings saved successfully."]', }, // social profile settings @@ -7010,6 +7022,11 @@ export const selector = { }, }, + radiusSearch: { + radiusUnit: 'span.dokan-range-slider-value', + slider: 'input.dokan-range-slider', + }, + // Filter filters: { filterDiv: 'form.dokan-geolocation-location-filters', diff --git a/tests/pw/pages/shipStationPage.ts b/tests/pw/pages/shipStationPage.ts new file mode 100644 index 0000000000..b6051810f6 --- /dev/null +++ b/tests/pw/pages/shipStationPage.ts @@ -0,0 +1,35 @@ +import { Page } from '@playwright/test'; +import { VendorPage } from '@pages/vendorPage'; +import { selector } from '@pages/selectors'; +import { data } from '@utils/testData'; + +// selectors +const settingsShipStation = selector.vendor.vShipStationSettings; + +export class ShipStationPage extends VendorPage { + constructor(page: Page) { + super(page); + } + + // generate shipStation credentials + async generateShipStationCredentials() { + await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipStation); + await this.clickAndAcceptAndWaitForResponse(data.subUrls.api.dokan.shipStation, settingsShipStation.generateCredentials, 201); + await this.toBeVisible(settingsShipStation.generateSuccessMessage); + + await this.toBeVisible(settingsShipStation.revokeCredentials); + await this.multipleElementVisible(settingsShipStation.credentials); + } + + // revoke shipStation credentials + async revokeShipStationCredentials() { + await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipStation); + + await this.click(settingsShipStation.revokeCredentials); + await this.clickAndAcceptAndWaitForResponse(data.subUrls.api.dokan.shipStation, settingsShipStation.confirmRevoke); + await this.toBeVisible(settingsShipStation.revokeSuccessMessage); + + await this.toBeVisible(settingsShipStation.generateCredentials); + await this.multipleElementNotVisible(settingsShipStation.credentials); + } +} diff --git a/tests/pw/pages/vendorAuctionsPage.ts b/tests/pw/pages/vendorAuctionsPage.ts index ec9bf05def..97d72de070 100644 --- a/tests/pw/pages/vendorAuctionsPage.ts +++ b/tests/pw/pages/vendorAuctionsPage.ts @@ -149,6 +149,16 @@ export class AuctionsPage extends VendorPage { await this.toBeVisible(auctionProductsVendor.productCell(productName)); } + // duplicate auction product + async duplicateAuctionProduct(productName: string) { + await this.searchAuctionProduct(productName); + await this.removeAttribute(auctionProductsVendor.rowActions(productName), 'class'); // forcing the row actions to be visible, to avoid flakiness + await this.hover(auctionProductsVendor.productCell(productName)); + await this.clickAndWaitForResponseAndLoadState(data.subUrls.frontend.vDashboard.auction, auctionProductsVendor.duplicate(productName), 302); + await this.toBeVisible(auctionProductsVendor.duplicateSuccessMessage); + await this.toBeVisible(auctionProductsVendor.productCell(productName + ' (Copy)')); + } + // delete auction product async deleteAuctionProduct(productName: string) { await this.searchAuctionProduct(productName); @@ -235,10 +245,6 @@ export class AuctionsPage extends VendorPage { await this.removeAttribute(auctionProductsVendor.auction.auctionEndDate, 'readonly'); await this.clearAndType(auctionProductsVendor.auction.auctionStartDate, generalOption.startDate); await this.clearAndType(auctionProductsVendor.auction.auctionEndDate, generalOption.endDate); - await this.check(auctionProductsVendor.auction.enableAutomaticRelisting); - await this.clearAndType(auctionProductsVendor.auction.relistIfFailAfterNHours, generalOption.relistIfFailAfterNHours); - await this.clearAndType(auctionProductsVendor.auction.relistIfNotPaidAfterNHours, generalOption.relistIfNotPaidAfterNHours); - await this.clearAndType(auctionProductsVendor.auction.relistAuctionDurationInH, generalOption.relistAuctionDurationInH); await this.saveProduct(); @@ -251,6 +257,18 @@ export class AuctionsPage extends VendorPage { await this.toHaveValue(auctionProductsVendor.auction.buyItNowPrice, buyItNowPrice); await this.toHaveValue(auctionProductsVendor.auction.auctionStartDate, generalOption.startDate); await this.toHaveValue(auctionProductsVendor.auction.auctionEndDate, generalOption.endDate); + } + + // add product Relist option + async addProductRelistingOption(productName: string, generalOption: product['auction']) { + await this.goToAuctionProductEditById(productName); + await this.check(auctionProductsVendor.auction.enableAutomaticRelisting); + await this.clearAndType(auctionProductsVendor.auction.relistIfFailAfterNHours, generalOption.relistIfFailAfterNHours); + await this.clearAndType(auctionProductsVendor.auction.relistIfNotPaidAfterNHours, generalOption.relistIfNotPaidAfterNHours); + await this.clearAndType(auctionProductsVendor.auction.relistAuctionDurationInH, generalOption.relistAuctionDurationInH); + + await this.saveProduct(); + await this.toBeChecked(auctionProductsVendor.auction.enableAutomaticRelisting); await this.toHaveValue(auctionProductsVendor.auction.relistIfFailAfterNHours, generalOption.relistIfFailAfterNHours); await this.toHaveValue(auctionProductsVendor.auction.relistIfNotPaidAfterNHours, generalOption.relistIfNotPaidAfterNHours); diff --git a/tests/pw/pages/vendorSettingsPage.ts b/tests/pw/pages/vendorSettingsPage.ts index 6c08fcedb5..35bd303841 100644 --- a/tests/pw/pages/vendorSettingsPage.ts +++ b/tests/pw/pages/vendorSettingsPage.ts @@ -70,9 +70,9 @@ export class VendorSettingsPage extends VendorPage { await this.toBeVisible(settingsVendor.updateSettings); } - // vendor shipstation render properly - async vendorShipstationSettingsRenderProperly() { - await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipstation); + // vendor ShipStation render properly + async vendorShipStationSettingsRenderProperly() { + await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipStation); // shipStation text is visible await this.toBeVisible(settingsShipStation.shipStationText); @@ -80,8 +80,13 @@ export class VendorSettingsPage extends VendorPage { // visit store link is visible await this.toBeVisible(settingsShipStation.visitStore); - // authentication key is visible - await this.toBeVisible(settingsShipStation.authenticationKey); + const isCredentialsGenerated = await this.isVisible(settingsShipStation.revokeCredentials); + + if (isCredentialsGenerated) { + await this.multipleElementVisible(settingsShipStation.credentials); + } else { + await this.toBeVisible(settingsShipStation.generateCredentials); + } // export order statuses is visible await this.toBeVisible(settingsShipStation.exportOrderStatusesInput); @@ -438,26 +443,27 @@ export class VendorSettingsPage extends VendorPage { await this.clearAndType(settingsVendor.minMax.maximumAmountToPlaceAnOrder, minMax.maximumAmount); } - // vendor set Shipstation settings + // vendor set ShipStation settings async setShipStation(shipStation: vendor['shipStation']): Promise { - await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipstation); + await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipStation); + // export order statuses const allStatus = await this.getMultipleElementTexts(settingsShipStation.selectedStatus); - const statusIsSelected = allStatus.includes('×' + shipStation.status); + const statusIsSelected = allStatus.includes(`×${shipStation.status}`); if (!statusIsSelected) { await this.clearAndType(settingsShipStation.exportOrderStatusesInput, shipStation.status); await this.toContainText(settingsShipStation.result, shipStation.status); await this.press(data.key.enter); } - // await this.click(settingsShipStation.shippedOrderStatusDropdown); - // await this.clearAndType(settingsShipStation.shippedOrderStatusInput, shipStation.status);// todo: need to fix -> locator issue - // await this.toContainText(settingsShipStation.result, shipStation.status); - // await this.press(data.key.enter); + // shipped order status + await this.click(settingsShipStation.shippedOrderStatusDropdown); + await this.clearAndType(settingsShipStation.shippedOrderStatusInput, shipStation.status); + await this.toContainText(settingsShipStation.result, shipStation.status); + await this.press(data.key.enter); - await this.clickAndAcceptAndWaitForResponse(data.subUrls.ajax, settingsShipStation.saveChanges); - await this.toContainText(settingsShipStation.saveSuccessMessage, 'Your changes has been updated!'); - await this.click(settingsShipStation.successOk); + await this.clickAndAcceptAndWaitForResponse(data.subUrls.api.dokan.shipStation, settingsShipStation.saveChanges, 201); + await this.toBeVisible(settingsShipStation.saveSuccessMessage); } // vendor set social profile settings diff --git a/tests/pw/pages/vendorShippingPage.ts b/tests/pw/pages/vendorShippingPage.ts index 17bc97a414..6198cf5cee 100644 --- a/tests/pw/pages/vendorShippingPage.ts +++ b/tests/pw/pages/vendorShippingPage.ts @@ -18,7 +18,7 @@ export class VendorShippingPage extends VendorPage { async vendorShippingSettingsRenderProperly() { await this.goIfNotThere(data.subUrls.frontend.vDashboard.settingsShipping); - // shipstation text is visible + // ShipStation text is visible await this.toBeVisible(vendorShipping.shippingSettingsText); // visit store link is visible diff --git a/tests/pw/tests/api/shipstation.spec.ts b/tests/pw/tests/api/shipstation.spec.ts new file mode 100644 index 0000000000..f035765a47 --- /dev/null +++ b/tests/pw/tests/api/shipstation.spec.ts @@ -0,0 +1,69 @@ +//COVERAGE_TAG: GET /dokan/v1/shipstation/credentials/(?P[\d]+) +//COVERAGE_TAG: POST /dokan/v1/shipstation/credentials/create +//COVERAGE_TAG: DELETE /dokan/v1/shipstation/credentials/(?P[\d]+) +//COVERAGE_TAG: GET /dokan/v1/shipstation/order-statuses +//COVERAGE_TAG: POST /dokan/v1/shipstation/order-statuses/(?P[\d]+) +//COVERAGE_TAG: DELETE /dokan/v1/shipstation/order-statuses/(?P[\d]+) + +import { test, expect, request } from '@playwright/test'; +import { ApiUtils } from '@utils/apiUtils'; +import { endPoints } from '@utils/apiEndPoints'; +import { payloads } from '@utils/payloads'; +import { schemas } from '@utils/schemas'; + +const { VENDOR_ID } = process.env; + +test.describe('ShipStation api test', () => { + test.skip(true, 'remove after pr is merged'); + let apiUtils: ApiUtils; + + test.beforeAll(async () => { + apiUtils = new ApiUtils(await request.newContext()); + }); + + test.afterAll(async () => { + await apiUtils.dispose(); + }); + + test('create ShipStation credential', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.post(endPoints.createShipStationCredential, { data: { vendor_id: VENDOR_ID } }); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationCredentialSchema); + }); + + test('get ShipStation credential', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.get(endPoints.getShipStationCredential(VENDOR_ID)); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationCredentialSchema); + }); + + test('delete ShipStation credential', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.delete(endPoints.deleteShipStationCredential(VENDOR_ID)); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationCredentialSchema); + }); + + test('create ShipStation order status settings', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.post(endPoints.createShipStationOrderStatusSettings, { data: { ...payloads.shipStationOrderStatusSettings, vendor_id: VENDOR_ID } }); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationOrderStatusSettingSchema); + }); + + test('get ShipStation order status settings', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.get(endPoints.getShipStationOrderStatusSettings(VENDOR_ID)); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationOrderStatusSettingSchema); + }); + + test('delete ShipStation order status settings', { tag: ['@pro'] }, async () => { + const [response, responseBody] = await apiUtils.delete(endPoints.deleteShipStationOrderStatusSettings(VENDOR_ID)); + expect(response.ok()).toBeTruthy(); + expect(responseBody).toBeTruthy(); + expect(responseBody).toMatchSchema(schemas.shipStationSchema.shipStationOrderStatusSettingSchema); + }); +}); diff --git a/tests/pw/tests/e2e/commission.spec.ts b/tests/pw/tests/e2e/commission.spec.ts index 055e8b1707..826030b3c5 100644 --- a/tests/pw/tests/e2e/commission.spec.ts +++ b/tests/pw/tests/e2e/commission.spec.ts @@ -6,7 +6,7 @@ import { payloads } from '@utils/payloads'; import { dbUtils } from '@utils/dbUtils'; import { dbData } from '@utils/dbData'; -const { DOKAN_PRO, PRODUCT_ID } = process.env; +const { PRODUCT_ID } = process.env; test.describe('Commission test', () => { let admin: CommissionPage; @@ -101,4 +101,7 @@ test.describe('Commission test', () => { const [, , orderId] = await apiUtils.createOrderWithStatus(PRODUCT_ID, payloads.createOrder, data.order.orderStatus.onhold, payloads.vendorAuth); await admin.viewCommissionMetaBox(orderId); }); + + // todo: admin can view commission on product list, order list, and order details, sub order details on parent order + // todo: vendor can view earning on product list, product details, order list, and order details }); diff --git a/tests/pw/tests/e2e/followStore.spec.ts b/tests/pw/tests/e2e/followStore.spec.ts index 63913ceedb..110a1eda2b 100644 --- a/tests/pw/tests/e2e/followStore.spec.ts +++ b/tests/pw/tests/e2e/followStore.spec.ts @@ -64,6 +64,8 @@ test.describe('Follow stores functionality test', () => { await customer.followUnfollowStore(data.predefined.vendorStores.vendor2, 'Follow', data.predefined.vendorStores.followFromSingleStore); }); + // todo: parameterize above tests also update feature-map + //vendor test('vendor can view followers menu page', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { diff --git a/tests/pw/tests/e2e/geolocation.spec.ts b/tests/pw/tests/e2e/geolocation.spec.ts index bf0df53e6e..8b2af3822c 100644 --- a/tests/pw/tests/e2e/geolocation.spec.ts +++ b/tests/pw/tests/e2e/geolocation.spec.ts @@ -14,6 +14,10 @@ test.describe('Geolocation test', () => { admin = new GeolocationPage(aPage); }); + test.beforeEach(async () => { + await dbUtils.setOptionValue(dbData.dokan.optionName.geolocation, dbData.dokan.geolocationSettings); + }); + test.afterAll(async () => { await dbUtils.setOptionValue(dbData.dokan.optionName.geolocation, dbData.dokan.geolocationSettings); await aPage.close(); @@ -46,4 +50,15 @@ test.describe('Geolocation test', () => { await admin.viewProductLocationTab(data.predefined.simpleProduct.product1.name, status as 'enable' | 'disable'); }); }); + + ['km', 'miles'].forEach((unit: string) => { + test(`admin can set map radius search unit and distance ${unit} `, { tag: ['@pro', '@admin'] }, async () => { + await dbUtils.updateOptionValue(dbData.dokan.optionName.geolocation, { distance_unit: unit, distance_min: '0', distance_max: '10' }); + await admin.viewMapRadiusSearchUnitAndDistance(unit as 'km' | 'miles', { min: '0', max: '10' }); + }); + }); + + test('customer can slide map radius bar', { tag: ['@pro', '@customer'] }, async () => { + await admin.slideMapRadiusBar('5'); + }); }); diff --git a/tests/pw/tests/e2e/plugin.spec.ts b/tests/pw/tests/e2e/plugin.spec.ts index 31e78fd3e2..20069ea654 100644 --- a/tests/pw/tests/e2e/plugin.spec.ts +++ b/tests/pw/tests/e2e/plugin.spec.ts @@ -63,11 +63,11 @@ test.describe.skip('Plugin functionality test', () => { }); test.skip('admin can delete Dokan pro plugin', { tag: ['@pro', '@admin', '@serial'] }, async () => { - await admin.activatePlugin(data.plugin.pluginName.dokanLite); + // await admin.deletePlugin(data.plugin.pluginName.dokanLite); }); test.skip('admin can delete Dokan plugin', { tag: ['@lite', '@admin', '@serial'] }, async () => { - await admin.activatePlugin(data.plugin.pluginName.dokanLite); + // await admin.deletePlugin(data.plugin.pluginName.dokanLite); }); //todo: replace (one zip with another) plugin test diff --git a/tests/pw/tests/e2e/productsDetailsAuction.spec.ts b/tests/pw/tests/e2e/productsDetailsAuction.spec.ts index 4d8a8d119b..17e75f50d0 100644 --- a/tests/pw/tests/e2e/productsDetailsAuction.spec.ts +++ b/tests/pw/tests/e2e/productsDetailsAuction.spec.ts @@ -184,6 +184,15 @@ test.describe('Auction Product details functionality test', () => { await vendor.addProductGeneralOption(productIdBasic, { ...data.product.auction, itemCondition: 'used', auctionType: 'reverse' }); }); + test('vendor can enable product relist options', { tag: ['@pro', '@vendor'] }, async () => { + await vendor.addProductRelistingOption(productIdBasic, data.product.auction); + }); + + test('vendor can update product relist options', { tag: ['@pro', '@vendor'] }, async () => { + test.skip(true, 'not implemented yet'); + await vendor.addProductRelistingOption(productIdFull, { ...data.product.auction, relistIfFailAfterNHours: '5', relistIfNotPaidAfterNHours: '6', relistAuctionDurationInH: '7' }); + }); + // product inventory options test('vendor can add auction product inventory options (SKU)', { tag: ['@pro', '@vendor'] }, async () => { diff --git a/tests/pw/tests/e2e/productsDetailsBookings.spec.ts b/tests/pw/tests/e2e/productsDetailsBookings.spec.ts index 460ed23418..de220422ee 100644 --- a/tests/pw/tests/e2e/productsDetailsBookings.spec.ts +++ b/tests/pw/tests/e2e/productsDetailsBookings.spec.ts @@ -389,7 +389,7 @@ test.describe('Booking Product details functionality test', () => { await vendor.addProductAddon(productIdBasic, data.product.productInfo.addon); }); - test('vendor can import booking product addon', { tag: ['@pro', '@vendor'] }, async () => { + test.skip('vendor can import booking product addon', { tag: ['@pro', '@vendor'] }, async () => { const addon = payloads.createProductAddon(); await vendor.importAddon(productIdBasic, serialize([addon]), addon.name); }); diff --git a/tests/pw/tests/e2e/shipstation.spec.ts b/tests/pw/tests/e2e/shipstation.spec.ts new file mode 100644 index 0000000000..32d3bc57ba --- /dev/null +++ b/tests/pw/tests/e2e/shipstation.spec.ts @@ -0,0 +1,29 @@ +import { test, Page } from '@playwright/test'; +import { ShipStationPage } from '@pages/shipStationPage'; +import { data } from '@utils/testData'; + +test.describe('ShipStation test', () => { + test.skip(true, 'remove after pr is merged'); + let vendor: ShipStationPage; + let vPage: Page; + + test.beforeAll(async ({ browser }) => { + const vendorContext = await browser.newContext(data.auth.vendorAuth); + vPage = await vendorContext.newPage(); + vendor = new ShipStationPage(vPage); + }); + + test.afterAll(async () => { + await vPage.close(); + }); + + // vendor + + test('vendor can generate ShipStation credentials', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { + await vendor.generateShipStationCredentials(); + }); + + test('vendor can revoke ShipStation credentials', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { + await vendor.revokeShipStationCredentials(); + }); +}); diff --git a/tests/pw/tests/e2e/vendorAuction.spec.ts b/tests/pw/tests/e2e/vendorAuction.spec.ts index 770ac1f3ad..d8f319c61b 100644 --- a/tests/pw/tests/e2e/vendorAuction.spec.ts +++ b/tests/pw/tests/e2e/vendorAuction.spec.ts @@ -69,6 +69,11 @@ test.describe('Auction Product test', () => { await vendor.searchAuctionProduct(auctionProductName); }); + test('vendor can duplicate auction product', { tag: ['@pro', '@vendor'] }, async () => { + const [, , auctionProductName] = await apiUtils.createProduct(payloads.createAuctionProduct(), payloads.vendorAuth); + await vendor.duplicateAuctionProduct(auctionProductName); + }); + test('vendor can permanently delete auction product', { tag: ['@pro', '@vendor'] }, async () => { const [, , auctionProductName] = await apiUtils.createProduct(payloads.createAuctionProduct(), payloads.vendorAuth); await vendor.deleteAuctionProduct(auctionProductName); diff --git a/tests/pw/tests/e2e/vendorSettings.spec.ts b/tests/pw/tests/e2e/vendorSettings.spec.ts index 0dec1a08fd..178573403e 100644 --- a/tests/pw/tests/e2e/vendorSettings.spec.ts +++ b/tests/pw/tests/e2e/vendorSettings.spec.ts @@ -34,8 +34,9 @@ test.describe('Vendor settings test', () => { await vendor.vendorStoreSettingsRenderProperly(); }); - test('vendor can view Shipstation settings menu page', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { - await vendor.vendorShipstationSettingsRenderProperly(); + test('vendor can view ShipStation settings menu page', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { + test.skip(true, 'pr not merged yet'); + await vendor.vendorShipStationSettingsRenderProperly(); }); test('vendor can view social profile settings menu page', { tag: ['@pro', '@exploratory', '@vendor'] }, async () => { @@ -113,11 +114,13 @@ test.describe('Vendor settings test', () => { test('vendor can set min-max settings', { tag: ['@pro', '@vendor'] }, async () => { await vendor.setStoreSettings(data.vendor.vendorInfo, 'min-max'); + // disable min-max await dbUtils.updateOptionValue(dbData.dokan.optionName.selling, { enable_min_max_quantity: 'off', enable_min_max_amount: 'off' }); }); - test('vendor can set shipStation settings', { tag: ['@pro', '@vendor'] }, async () => { + test('vendor can set ShipStation settings', { tag: ['@pro', '@vendor'] }, async () => { + test.skip(true, 'pr not merged yet'); await vendor.setShipStation(data.vendor.shipStation); }); diff --git a/tests/pw/tests/e2e/wholesale.spec.ts b/tests/pw/tests/e2e/wholesale.spec.ts index 706d4b51cc..d0da1b7b2d 100644 --- a/tests/pw/tests/e2e/wholesale.spec.ts +++ b/tests/pw/tests/e2e/wholesale.spec.ts @@ -149,7 +149,7 @@ test.describe('Wholesale test (customer)', () => { await cPage.close(); }); - test('All users can see wholesale price', { tag: ['@pro', '@customer'] }, async () => { + test('all users can see wholesale price', { tag: ['@pro', '@customer'] }, async () => { test.skip(true, '@todo fix this test'); await admin.viewWholeSalePrice(productName); }); diff --git a/tests/pw/utils/apiEndPoints.ts b/tests/pw/utils/apiEndPoints.ts index e3b962ca18..a6b8f444dd 100644 --- a/tests/pw/utils/apiEndPoints.ts +++ b/tests/pw/utils/apiEndPoints.ts @@ -365,7 +365,7 @@ export const endPoints = { updateVerificationRequest: (requestId: string) => `${SERVER_URL}/dokan/v1/verification-requests/${requestId}`, deleteVerificationRequest: (requestId: string) => `${SERVER_URL}/dokan/v1/verification-requests/${requestId}`, - //commission + // commission getCommission: `${SERVER_URL}/dokan/v1/commission`, // shipping status @@ -375,6 +375,14 @@ export const endPoints = { getSingleShipment: (orderId: string, shipmentId: string) => `${SERVER_URL}/dokan/v1/shipping-status/orders/${orderId}/shipment/${shipmentId}`, updateShipment: (orderId: string, shipmentId: string) => `${SERVER_URL}/dokan/v1/shipping-status/orders/${orderId}/shipment/${shipmentId}`, + // ShipStation + getShipStationCredential: (vendorId: string) => `${SERVER_URL}/dokan/v1/shipstation/credentials/${vendorId}`, + createShipStationCredential: `${SERVER_URL}/dokan/v1/shipstation/credentials/create`, + deleteShipStationCredential: (vendorId: string) => `${SERVER_URL}/dokan/v1/shipstation/credentials/${vendorId}`, + getShipStationOrderStatusSettings: (vendorId: string) => `${SERVER_URL}/dokan/v1/shipstation/order-statuses/${vendorId}`, + createShipStationOrderStatusSettings: `${SERVER_URL}/dokan/v1/shipstation/order-statuses`, + deleteShipStationOrderStatusSettings: (vendorId: string) => `${SERVER_URL}/dokan/v1/shipstation/order-statuses/${vendorId}`, + wc: { // coupons getAllCoupons: `${SERVER_URL}/wc/v3/coupons`, diff --git a/tests/pw/utils/apiUtils.ts b/tests/pw/utils/apiUtils.ts index fc05a15b20..84d3e99fe6 100644 --- a/tests/pw/utils/apiUtils.ts +++ b/tests/pw/utils/apiUtils.ts @@ -1533,6 +1533,30 @@ export class ApiUtils { return [responseBody, orderId, shipmentId]; } + /** + * ShipStation api methods + */ + + async createShipStationCredential(vendorId: string, auth?: auth): Promise { + const [, responseBody] = await this.post(endPoints.createShipStationCredential, { data: { vendor_id: vendorId }, headers: auth }); + return responseBody; + } + + async deleteShipStationCredential(vendorId: string, auth?: auth): Promise { + const [, responseBody] = await this.delete(endPoints.deleteShipStationCredential(vendorId), { headers: auth }); + return responseBody; + } + + async createShipStationOrderStatusSettings(payload: object, auth?: auth): Promise { + const [, responseBody] = await this.post(endPoints.createShipStationOrderStatusSettings, { data: payload, headers: auth }); + return responseBody; + } + + async deleteShipStationOrderStatusSettings(vendorId: string, auth?: auth): Promise { + const [, responseBody] = await this.delete(endPoints.deleteShipStationOrderStatusSettings(vendorId), { headers: auth }); + return responseBody; + } + /** * wp api methods */ diff --git a/tests/pw/utils/dbData.ts b/tests/pw/utils/dbData.ts index a60b983ef2..4ea204278a 100644 --- a/tests/pw/utils/dbData.ts +++ b/tests/pw/utils/dbData.ts @@ -1043,7 +1043,7 @@ export const dbData = { show_location_map_pages: 'all', // all, store_listing, shop show_filters_before_locations_map: 'on', // on, off show_product_location_in_wc_tab: 'on', // on, off - distance_unit: 'km', + distance_unit: 'km', // km, miles distance_min: '0', distance_max: '10', map_zoom: '11', diff --git a/tests/pw/utils/payloads.ts b/tests/pw/utils/payloads.ts index 7c0e52e8d8..4016105b53 100644 --- a/tests/pw/utils/payloads.ts +++ b/tests/pw/utils/payloads.ts @@ -3575,7 +3575,7 @@ export const payloads = { reportAbuse: 'report_abuse', rma: 'rma', sellerVacation: 'seller_vacation', - shipstation: 'shipstation', + shipStation: 'shipstation', auction: 'auction', spmv: 'spmv', storeReviews: 'store_reviews', @@ -4872,6 +4872,18 @@ export const payloads = { }, ], + // ShipStation + + createCredential: { + vendor_id: '', + }, + + shipStationOrderStatusSettings: { + vendor_id: '', + export_statuses: ['wc-pending', 'wc-processing', 'wc-on-hold', 'wc-completed', 'wc-cancelled'], + shipped_status: 'wc-completed', + }, + // shortcodes // dokan dashboard shortcode diff --git a/tests/pw/utils/schemas.ts b/tests/pw/utils/schemas.ts index 9ec47f474c..6af4cee463 100644 --- a/tests/pw/utils/schemas.ts +++ b/tests/pw/utils/schemas.ts @@ -2954,4 +2954,19 @@ export const schemas = { shipmentSchema: shipmentSchema, shipmentsSchema: z.array(shipmentSchema), }, + + // ShipStation schema + shipStationSchema: { + shipStationCredentialSchema: z.object({ + key_id: z.string().or(z.number()), + consumer_key: z.string(), + consumer_secret: z.string(), + }), + + shipStationOrderStatusSettingSchema: z.object({ + vendor_id: z.string().or(z.number()), + export_statuses: z.array(z.string()), + shipped_status: z.string(), + }), + }, }; diff --git a/tests/pw/utils/testData.ts b/tests/pw/utils/testData.ts index 4b22809c0c..a66f123b08 100644 --- a/tests/pw/utils/testData.ts +++ b/tests/pw/utils/testData.ts @@ -1204,7 +1204,7 @@ export const data = { settingsVerification: 'dashboard/settings/verification', settingsDeliveryTime: 'dashboard/settings/delivery-time', settingsShipping: 'dashboard/settings/shipping', - settingsShipstation: 'dashboard/settings/shipstation', + settingsShipStation: 'dashboard/settings/shipstation', settingsSocialProfile: 'dashboard/settings/social', settingsRma: 'dashboard/settings/rma', settingsSeo: 'dashboard/settings/seo', @@ -1246,6 +1246,7 @@ export const data = { productQuestionsBulkActions: 'dokan/v1/product-questions/bulk_action', productAnswers: 'dokan/v1/product-answers', subscriptions: 'dokan/v1/subscription', + shipStation: 'dokan/v1/shipstation', verifications: 'dokan/v1/verification-requests', verificationMethods: 'dokan/v1/verification-methods', }, diff --git a/webpack.config.js b/webpack.config.js index d0d36e08ea..f603004a21 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,6 +17,7 @@ const entryPoint = { './src/utils/vue-vendor.js', ], 'dokan-promo-notice': './src/promo-notice/main.js', + 'dokan-admin-notice': './src/admin/notice/main.js', 'reverse-withdrawal': './assets/src/js/reverse-withdrawal.js', 'product-category-ui': './assets/src/js/product-category-ui.js', 'dokan-admin-product': './assets/src/js/dokan-admin-product.js',