diff --git a/CHANGELOG.md b/CHANGELOG.md index ce47b43..e950806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2024-09-13 4.8.4 +* Updated work with promotional items when loyalty program is enabled + ## 2024-09-11 4.8.3 * Added loyalty program coupon entry in the form by click diff --git a/VERSION b/VERSION index f99c658..57c4b30 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.8.3 +4.8.4 diff --git a/doc/1.Setup/Cron tasks.md b/doc/1.Setup/Cron tasks.md index c468c05..5fc51f2 100644 --- a/doc/1.Setup/Cron tasks.md +++ b/doc/1.Setup/Cron tasks.md @@ -14,6 +14,7 @@ 'icml' => 'three_hours', 'history' => 'five_minutes', 'inventories' => 'fiveteen_minutes', + 'loyalty_upload_price' => 'four_hours' ] ``` > Важно! При использовании фильтра **retailcrm_cron_schedules**, можно использовать ключи: 'icml', 'history', 'inventories'. @@ -49,5 +50,3 @@ function change_cron_tasks($cronTasks) return $cronTasks; } ``` - - diff --git a/resources/pot/retailcrm-es_ES.pot b/resources/pot/retailcrm-es_ES.pot index a09c4d5..13c0093 100644 --- a/resources/pot/retailcrm-es_ES.pot +++ b/resources/pot/retailcrm-es_ES.pot @@ -451,8 +451,11 @@ msgstr "Programa de fidelización" msgid "Activate program loyalty" msgstr "Activar programa de fidelización" -msgid "Enable this setting for activate program loyalty on site" -msgstr "Activa esta configuración para activar programa de fidelización en la página web" +msgid "Attention! When activating the loyalty program, the method of ICML catalog generation changes. Details in" +msgstr "¡Atención! Al activar el programa de fidelización, cambia el método de generación del catálogo ICML. Detalles en la" + +msgid "documentation loyalty program" +msgstr "documentación del programa de fidelización" msgid "Terms of loyalty program" msgstr "Condiciones del programa de fidelización" @@ -591,3 +594,27 @@ msgstr "Utiliza el cupón:" msgid "Points will be awarded upon completion of the order:" msgstr "Los puntos se concederán al finalizar el pedido:" + +msgid "Unloading promotional prices of offers" +msgstr "Descarga de precios promocionales de ofertas comerciales" + +msgid "Every 4 hours" +msgstr "Cada 4 horas" + +msgid "Upload prices now" +msgstr "Descargar precios ahora" + +msgid "Uploaded discount price" +msgstr "Descarga de precios promocionales" + +msgid "This functionality loads the promotional prices offers into Simla.com" +msgstr "Esta función carga los precios promocionales de las ofertas comerciales en Simla.com" + +msgid "Promotional prices unloaded" +msgstr "Se han cargado los precios promocionales" + +msgid "Woocommerce promotional price" +msgstr "Precio promocional Woocommerce" + +msgid "Promotional price type for Woocommerce store, generated automatically. Necessary for correct synchronization work when loyalty program is enabled (Do not delete. Do not deactivate)" +msgstr "Tipo de precio promocional para la tienda Woocommerce, generado automáticamente. Necesario para el correcto funcionamiento de la sincronización cuando el programa de fidelización está habilitado (No eliminar. No desactivar)" diff --git a/resources/pot/retailcrm-ru_RU.pot b/resources/pot/retailcrm-ru_RU.pot index d6cf7c0..53fdc24 100644 --- a/resources/pot/retailcrm-ru_RU.pot +++ b/resources/pot/retailcrm-ru_RU.pot @@ -460,8 +460,11 @@ msgstr "Программа лояльности" msgid "Activate program loyalty" msgstr "Активировать программу лояльности" -msgid "Enable this setting for activate program loyalty on site" -msgstr "Активируйте эту настройку для активации программы лояльности на сайте" +msgid "Attention! When activating the loyalty program, the method of ICML catalog generation changes. Details in" +msgstr "Внимание! При активации программы лояльности, изменяется способ генерации ICML каталога. Подробности в" + +msgid "documentation loyalty program" +msgstr "документации программы лояльности" msgid "Terms of loyalty program" msgstr "Условия программы лояльности" @@ -600,3 +603,27 @@ msgstr "Используйте купон:" msgid "Points will be awarded upon completion of the order:" msgstr "По завершению заказа будет начислено баллов:" + +msgid "Unloading promotional prices of offers" +msgstr "Выгрузка акционных цен торговых предложений" + +msgid "Every 4 hours" +msgstr "Каждые 4 часа" + +msgid "Upload prices now" +msgstr "Выгрузить цены сейчас" + +msgid "Uploaded discount price" +msgstr "Выгрузка акционных цен" + +msgid "This functionality loads the promotional prices offers into Simla.com" +msgstr "Эта функция загружает акционные цены торговых предложений в Simla.com" + +msgid "Promotional prices unloaded" +msgstr "Акционные цены выгружены" + +msgid "Woocommerce promotional price" +msgstr "Акционная цена Woocommerce" + +msgid "Promotional price type for Woocommerce store, generated automatically. Necessary for correct synchronization work when loyalty program is enabled (Do not delete. Do not deactivate)" +msgstr "Акционный тип цены для магазина Woocommerce, сгенерированный автоматически. Необходим для корректной работы синхронизации при включенной программы лояльности (Не удалять. Не деактивировать)" diff --git a/src/assets/js/retailcrm-cron-info.js b/src/assets/js/retailcrm-cron-info.js index 25083e3..5ca2076 100644 --- a/src/assets/js/retailcrm-cron-info.js +++ b/src/assets/js/retailcrm-cron-info.js @@ -16,6 +16,7 @@ jQuery(function () { this.history = 0; this.inventories = 0; this.messageSuccessful = ''; + this.loyaltyUploadPrice = 0; this.adminUrl = AdminUrl.url; @@ -33,12 +34,14 @@ jQuery(function () { _this.icml = response.icml; _this.inventories = response.inventories; _this.messageSuccessful = response.translate.tr_successful; + _this.loyaltyUploadPrice = response.loyaltyUploadPrice _this.displayInfoAboutCron( response.translate.tr_td_cron, response.translate.tr_td_icml, response.translate.tr_td_history, response.translate.tr_td_inventories, + response.translate.tr_td_loyaltyUploadPrice ); }) @@ -47,7 +50,7 @@ jQuery(function () { jQuery(this.submitButton).click(this.clearCronTasks); } - RetailcrmCronInfo.prototype.displayInfoAboutCron = function (cron, icml, history, inventories) { + RetailcrmCronInfo.prototype.displayInfoAboutCron = function (cron, icml, history, inventories, loyaltyUploadPrice) { this.table = jQuery(this.title).next(); this.table.append(''); this.infoTable = jQuery('tbody[class="retail-debug-info"]').get(0); @@ -56,6 +59,7 @@ jQuery(function () { jQuery(this.infoTable).append("" + icml + " " + this.icml + ""); jQuery(this.infoTable).append("" + history + " " + this.history + ""); jQuery(this.infoTable).append("" + inventories + " " + this.inventories + ""); + jQuery(this.infoTable).append("" + loyaltyUploadPrice + "" + this.loyaltyUploadPrice + ""); } RetailcrmCronInfo.prototype.clearCronTasks = function () { diff --git a/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php b/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php index 9adf83b..8f7cb1a 100644 --- a/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php +++ b/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php @@ -72,6 +72,25 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration + + __('Loyalty program', 'retailcrm'), 'class' => 'checkbox', 'type' => 'checkbox', - 'description' => __('Enable this setting for activate program loyalty on site', 'retailcrm') + 'description' => '' . + __('Attention! When activating the loyalty program, the method of ICML catalog generation changes. Details in', 'retailcrm') . + ' <\b>' . __("documentation loyalty program", "retailcrm") ]; $this->form_fields['loyalty_terms'] = [ @@ -651,6 +672,18 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration ), ]; + $this->form_fields[] = [ + 'label' => __('Upload prices now', 'retailcrm'), + 'title' => __('Uploaded discount price', 'retailcrm'), + 'type' => 'button', + 'desc_tip' => true, + 'id' => 'upload-loyalty-price-retailcrm', + 'description' => __( + 'This functionality loads the promotional prices offers into Simla.com', + 'retailcrm' + ), + ]; + $this->form_fields['icml'] = [ 'label' => __('Generating ICML', 'retailcrm'), 'title' => __('Generating ICML catalog by wp-cron', 'retailcrm'), diff --git a/src/include/api/class-wc-retailcrm-client-v5.php b/src/include/api/class-wc-retailcrm-client-v5.php index 9d58993..e682805 100644 --- a/src/include/api/class-wc-retailcrm-client-v5.php +++ b/src/include/api/class-wc-retailcrm-client-v5.php @@ -3037,6 +3037,23 @@ class WC_Retailcrm_Client_V5 ); } + public function getPriceTypes() + { + return $this->client->makeRequest( + '/reference/price-types', + WC_Retailcrm_Request::METHOD_GET + ); + } + + public function editPriceType($priceType) + { + return $this->client->makeRequest( + sprintf('/reference/price-types/%s/edit', $priceType['code']), + WC_Retailcrm_Request::METHOD_POST, + ['priceType' => json_encode($priceType)] + ); + } + /** * Return current site * diff --git a/src/include/class-wc-retailcrm-base.php b/src/include/class-wc-retailcrm-base.php index c2404d5..9f8b65b 100644 --- a/src/include/class-wc-retailcrm-base.php +++ b/src/include/class-wc-retailcrm-base.php @@ -127,6 +127,9 @@ if (!class_exists('WC_Retailcrm_Base')) { add_action('woocommerce_applied_coupon', [$this, 'apply_coupon'], 11, 1); add_action('woocommerce_review_order_before_payment', [$this, 'reviewCreditBonus'], 11, 1); add_action('wp_trash_post', [$this, 'trash_order_action'], 10, 1); + add_action('retailcrm_loyalty_upload_price', [$this, 'upload_loyalty_price']); + add_action('admin_print_footer_scripts', [$this, 'ajax_upload_loyalty_price'], 99); + add_action('wp_ajax_upload_loyalty_price', [$this, 'upload_loyalty_price']); if ( !$this->get_option('deactivate_update_order') @@ -190,12 +193,23 @@ if (!class_exists('WC_Retailcrm_Base')) { */ public function api_sanitized($settings) { + $isLoyaltyUploadPrice = false; + + if ( + isset($settings['icml'], $settings['loyalty']) + && $settings['icml'] === static::YES + && $settings['loyalty'] === static::YES + ) { + $isLoyaltyUploadPrice = true; + } + $timeInterval = apply_filters( 'retailcrm_cron_schedules', [ 'icml' => 'three_hours', 'history' => 'five_minutes', 'inventories' => 'fiveteen_minutes', + 'loyalty_upload_price' => 'four_hours' ] ); @@ -223,6 +237,12 @@ if (!class_exists('WC_Retailcrm_Base')) { wp_clear_scheduled_hook('retailcrm_icml'); } + if ($isLoyaltyUploadPrice && !wp_next_scheduled('retailcrm_loyalty_upload_price')) { + wp_schedule_event(time(), $timeInterval['loyalty_upload_price'], 'retailcrm_loyalty_upload_price'); + } elseif (!$isLoyaltyUploadPrice) { + wp_clear_scheduled_hook('retailcrm_loyalty_upload_price'); + } + if (!$this->get_errors() && !get_option('retailcrm_active_in_crm')) { $this->activate_integration($settings); } @@ -268,6 +288,7 @@ if (!class_exists('WC_Retailcrm_Base')) { wp_clear_scheduled_hook('retailcrm_icml'); wp_clear_scheduled_hook('retailcrm_history'); wp_clear_scheduled_hook('retailcrm_inventories'); + wp_clear_scheduled_hook('retailcrm_loyalty_upload_price'); //Add new cron tasks $this->api_sanitized($this->settings); @@ -361,6 +382,16 @@ if (!class_exists('WC_Retailcrm_Base')) { } } + public function upload_loyalty_price() + { + if (!$this->apiClient instanceof WC_Retailcrm_Proxy) { + return null; + } + + $discountPriceUpload = new WC_Retailcrm_Upload_Discount_Price($this->apiClient); + $discountPriceUpload->upload(); + } + /** * Get history * @@ -930,12 +961,15 @@ if (!class_exists('WC_Retailcrm_Base')) { $icml = $defaultValue; $history = $defaultValue; $inventories = $defaultValue; + $loyaltyUploadPrice = $defaultValue; + $translate = [ 'tr_td_cron' => __('Cron launches', 'retailcrm'), 'tr_td_icml' => __('Generation ICML', 'retailcrm'), 'tr_td_history' => __('Syncing history', 'retailcrm'), 'tr_successful' => __('Cron tasks cleared', 'retailcrm'), 'tr_td_inventories' => __('Syncing inventories', 'retailcrm'), + 'tr_td_loyaltyUploadPrice' => __('Unloading promotional prices of offers', 'retailcrm') ]; if (isset($this->settings['history']) && $this->settings['history'] == static::YES) { @@ -944,6 +978,10 @@ if (!class_exists('WC_Retailcrm_Base')) { if (isset($this->settings['icml']) && $this->settings['icml'] == static::YES) { $icml = date('H:i:s d-m-Y', wp_next_scheduled('retailcrm_icml')); + + if (isset($this->settings['loyalty']) && $this->settings['loyalty'] === static::YES) { + $loyaltyUploadPrice = date('H:i:s d-m-Y', wp_next_scheduled('retailcrm_loyalty_upload_price')); + } } if (isset($this->settings['sync']) && $this->settings['sync'] == static::YES) { @@ -956,6 +994,7 @@ if (!class_exists('WC_Retailcrm_Base')) { 'icml' => $icml, 'inventories' => $inventories, 'translate' => $translate, + 'loyaltyUploadPrice' => $loyaltyUploadPrice ] ); diff --git a/src/include/class-wc-retailcrm-icml.php b/src/include/class-wc-retailcrm-icml.php index 5d0875f..9edc4c8 100644 --- a/src/include/class-wc-retailcrm-icml.php +++ b/src/include/class-wc-retailcrm-icml.php @@ -38,6 +38,8 @@ if (!class_exists('WC_Retailcrm_Icml')) : protected $unloadServices = false; + protected $activeLoyalty = false; + /** * WC_Retailcrm_Icml constructor. * @@ -53,6 +55,10 @@ if (!class_exists('WC_Retailcrm_Icml')) : isset($this->settings['icml_unload_services']) && $this->settings['icml_unload_services'] === WC_Retailcrm_Base::YES ); + + if (isset($this->settings['loyalty']) && $this->settings['loyalty'] === WC_Retailcrm_Base::YES) { + $this->activeLoyalty = true; + } } public function changeBindBySku($useXmlId) @@ -251,7 +257,10 @@ if (!class_exists('WC_Retailcrm_Icml')) : 'productId' => ($product->get_parent_id() > 0) ? $parent->get_id() : $product->get_id(), 'name' => $product->get_name(), 'productName' => ($product->get_parent_id() > 0) ? $parent->get_title() : $product->get_title(), - 'price' => wc_get_price_including_tax($product), + 'price' => $this->activeLoyalty + ? wc_get_price_including_tax($product, ["price" => $product->get_regular_price()]) + : wc_get_price_including_tax($product) + , 'picture' => $images, 'url' => ($product->get_parent_id() > 0) ? $parent->get_permalink() : $product->get_permalink(), 'quantity' => $quantity, diff --git a/src/include/class-wc-retailcrm-loyalty.php b/src/include/class-wc-retailcrm-loyalty.php index 8b9902c..282cfce 100644 --- a/src/include/class-wc-retailcrm-loyalty.php +++ b/src/include/class-wc-retailcrm-loyalty.php @@ -450,11 +450,14 @@ if (!class_exists('WC_Retailcrm_Loyalty')) : foreach ($cartItems as $item) { $product = $item['data']; + $productRegularPrice = wc_get_price_including_tax($product, ["price" => $product->get_regular_price()]); + $discount = $productRegularPrice - ($item['line_total'] / $item['quantity']); + $order['items'][] = [ 'offer' => $useXmlId ? ['xmlId' => $product->get_sku()] : ['externalId' => $product->get_id()], 'quantity' => $item['quantity'], - 'initialPrice' => wc_get_price_including_tax($product), - 'discountManualAmount' => ($item['line_subtotal'] - $item['line_total']) / $item['quantity'] + 'initialPrice' => $productRegularPrice, + 'discountManualAmount' => $discount ]; } diff --git a/src/include/class-wc-retailcrm-plugin.php b/src/include/class-wc-retailcrm-plugin.php index 1e9bdac..7a91dd3 100644 --- a/src/include/class-wc-retailcrm-plugin.php +++ b/src/include/class-wc-retailcrm-plugin.php @@ -58,6 +58,10 @@ class WC_Retailcrm_Plugin 'fiveteen_minutes' => [ 'interval' => 900, // seconds 'display' => __('Every 15 minutes') + ], + 'four_hours' => [ + 'interval' => 14400, //seconds + 'display' => __('Every 4 hours') ] ], apply_filters('retailcrm_add_cron_interval', $schedules) @@ -116,6 +120,10 @@ class WC_Retailcrm_Plugin if (wp_next_scheduled('retailcrm_inventories')) { wp_clear_scheduled_hook('retailcrm_inventories'); } + + if (wp_next_scheduled('retailcrm_loyalty_upload_price')) { + wp_clear_scheduled_hook('retailcrm_loyalty_upload_price'); + } } /** diff --git a/src/include/class-wc-retailcrm-upload-discount-price.php b/src/include/class-wc-retailcrm-upload-discount-price.php new file mode 100644 index 0000000..c36101d --- /dev/null +++ b/src/include/class-wc-retailcrm-upload-discount-price.php @@ -0,0 +1,238 @@ +settings = get_option(WC_Retailcrm_Base::$option_key); + $this->apiClient = $aplClient; + + if (isset($this->settings['loyalty']) && $this->settings['loyalty'] === WC_Retailcrm_Base::YES) { + $this->activeLoyalty = true; + } + } + + public function upload() + { + if (!$this->activeLoyalty) { + return; + } + + $error = $this->uploadSettings(); + + if ($error !== '') { + writeBaseLogs($error); + + return; + } + + $productStatuses = $this->getProductStatuses(); + + if (!$productStatuses) { + $productStatuses = ['publish']; + } + + $page = 1; + $requestData = []; + + do { + $products = wc_get_products( + [ + 'limit' => 1000, + 'status' => $productStatuses, + 'page' => $page, + 'paginate' => true + ] + ); + + /** WP version >= 6 */ + if (function_exists('wp_cache_flush_runtime')) { + wp_cache_flush_runtime(); + } else { + wp_cache_flush(); + } + + if (empty($products)) { + writeBaseLogs('Can`t get products!'); + + return; + } + + try { + foreach ($products->products as $offer) { + $type = $offer->get_type(); + + if (strpos($type, 'variable') !== false || strpos($type, 'variation') !== false) { + foreach ($offer->get_children() as $childId) { + $childProduct = wc_get_product($childId); + + if (!$childProduct) { + continue; + } + + $sendOffer = $this->getOfferData($childProduct); + + if ($sendOffer !== []) { + $requestData[] = $sendOffer; + } + } + } else { + $sendOffer = $this->getOfferData($offer); + + if ($sendOffer !== []) { + $requestData[] = $sendOffer; + } + } + } + + $chunks = array_chunk($requestData, 250); + + foreach ($chunks as $chunk) { + $this->apiClient->storePricesUpload($chunk, $this->site); + time_nanosleep(0, 200000000); + } + + unset($chunks); + } catch (\Throwable $exception) { + writeBaseLogs($exception->getMessage() . PHP_EOL . $exception->getTraceAsString()); + + return; + } + + ++$page; + } while ($page <= $products->max_num_pages); + } + + private function getOfferData(WC_Product $product) + { + $currentPrice = wc_get_price_including_tax($product); + $defaultPrice = wc_get_price_including_tax($product, ["price" => $product->get_regular_price()]); + + if ($currentPrice === $defaultPrice) { + return []; + } + + return [ + 'externalId' => $product->get_id(), + 'site' => $this->site, + 'prices' => [ + [ + 'code' => self::DISCOUNT_TYPE_PRICE, + 'price' => $currentPrice + ] + ] + ]; + } + + private function getProductStatuses() + { + $statuses = []; + + foreach (get_post_statuses() as $key => $value) { + if (isset($this->settings['p_' . $key]) && $this->settings['p_' . $key] == WC_Retailcrm_Base::YES) { + $statuses[] = $key; + } + } + + return $statuses; + } + + private function uploadSettings() + { + if (!$this->apiClient instanceof WC_Retailcrm_Proxy + && !$this->apiClient instanceof WC_Retailcrm_Client_V5 + ) { + return 'API client has not been initialized'; + } + + $this->site = $this->apiClient->getSingleSiteForKey(); + + if (empty($this->site)) { + return 'Error with CRM credentials: need an valid apiKey assigned to one certain site'; + } + + $response = $this->apiClient->getPriceTypes(); + + if ( + !$response instanceof WC_Retailcrm_Response + || !$response->offsetExists('priceTypes') + || empty($response['priceTypes']) + ) { + return 'Error getting price types'; + } + + $defaultPrice = null; + $discountPriceType = null; + + foreach ($response['priceTypes'] as $priceType) { + if ($priceType['default'] === true) { + $defaultPrice = $priceType; + } + + if ($priceType['code'] === self::DISCOUNT_TYPE_PRICE) { + $discountPriceType = $priceType; + } + } + + if ($discountPriceType === null) { + $discountPriceType = [ + 'code' => self::DISCOUNT_TYPE_PRICE, + 'name' => __('Woocommerce promotional price', 'retailcrm'), + 'active' => true, + 'description' => __('Promotional price type for Woocommerce store, generated automatically. + Necessary for correct synchronization work when loyalty program is enabled + (Do not delete. Do not deactivate)', 'retailcrm'), + 'ordering' => 999, + 'promo' => true + ]; + + if (isset($defaultPrice['geo'])) { + $discountPriceType['geo'] = $defaultPrice['geo']; + } + + if (isset($defaultPrice['groups'])) { + $discountPriceType['groups'] = $defaultPrice['groups']; + } + + if (isset($defaultPrice['currency'])) { + $discountPriceType['currency'] = $defaultPrice['currency']; + } + + if (isset($defaultPrice['filterExpression'])) { + $discountPriceType['filterExpression'] = $defaultPrice['filterExpression']; + } + + $response = $this->apiClient->editPriceType($discountPriceType); + + if (!$response instanceof WC_Retailcrm_Response || !$response['success']) { + return 'Error creating price type'; + } + } elseif ($discountPriceType['active'] === false || $discountPriceType['promo'] === false) { + $discountPriceType['active'] = true; + $discountPriceType['promo'] = true; + + $response = $this->apiClient->editPriceType($discountPriceType); + + if (!$response instanceof WC_Retailcrm_Response || !$response['success']) { + return 'Error activate price type'; + } + } + + return ''; + } + } + +endif; diff --git a/src/include/order/class-wc-retailcrm-order-item.php b/src/include/order/class-wc-retailcrm-order-item.php index 38b0a59..dd55769 100644 --- a/src/include/order/class-wc-retailcrm-order-item.php +++ b/src/include/order/class-wc-retailcrm-order-item.php @@ -106,7 +106,12 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data */ private function calculatePrice(WC_Order_Item_Product $item, int $decimalPlaces) { - $price = ($item['line_subtotal'] / $item->get_quantity()) + ($item['line_subtotal_tax'] / $item->get_quantity()); + if (isset($this->settings['loyalty']) && $this->settings['loyalty'] === WC_Retailcrm_Base::YES) { + $product = $item->get_product(); + $price = wc_get_price_including_tax($product, ["price" => $product->get_regular_price()]); + } else { + $price = ($item['line_subtotal'] / $item->get_quantity()) + ($item['line_subtotal_tax'] / $item->get_quantity()); + } return round($price, $decimalPlaces); } @@ -124,7 +129,8 @@ class WC_Retailcrm_Order_Item extends WC_Retailcrm_Abstracts_Data int $decimalPlaces, $crmItem = null ) { - if ($crmItem) { + + if ($crmItem && isset($this->settings['loyalty']) && $this->settings['loyalty'] === WC_Retailcrm_Base::YES) { $loyaltyDiscount = 0; foreach ($crmItem['discounts'] as $discount) { diff --git a/src/languages/retailcrm-es_ES.l10n.php b/src/languages/retailcrm-es_ES.l10n.php index 5d58e8e..38195ad 100644 --- a/src/languages/retailcrm-es_ES.l10n.php +++ b/src/languages/retailcrm-es_ES.l10n.php @@ -202,7 +202,8 @@ return [ "Los bienes con la opción 'virtual' activada se cargarán en Simla como servicios", "Loyalty program" => "Programa de fidelización", "Activate program loyalty" => "Activar programa de fidelización", - "Enable this setting for activate program loyalty on site" => "Activa esta configuración para activar el programa de fidelización en la página web", + "Attention! When activating the loyalty program, the method of ICML catalog generation changes. Details in" => "¡Atención! Al activar el programa de fidelización, cambia el método de generación del catálogo ICML. Detalles en la", + "documentation loyalty program" => "documentación del programa de fidelización", "Terms of loyalty program" => "Condiciones del programa de fidelización", "Insert the terms and conditions of the loyalty program" => "Inserte las condiciones del programa de fidelización", "Conditions of personal data processing" => "Condiciones de procesamiento de datos personales", @@ -248,6 +249,15 @@ return [ "bonuses" => "bonificaciones", "Use coupon:" => "Utiliza el cupón:", "Points will be awarded upon completion of the order:" => "Los puntos se concederán al finalizar el pedido:", + "Unloading promotional prices of offers" => "Descarga de precios promocionales de ofertas comerciales", + "Every 4 hours" => "Cada 4 horas", + "Upload prices now" => "Descargar precios ahora", + "Uploaded discount price" => "Descarga de precios promocionales", + "This functionality loads the promotional prices offers into Simla.com" => "Esta función carga los precios promocionales de las ofertas comerciales en Simla.com", + "Promotional prices unloaded" => "Se han cargado los precios promocionales", + "Woocommerce promotional price" => "Precio promocional Woocommerce", + "Promotional price type for Woocommerce store, generated automatically. Necessary for correct synchronization work when loyalty program is enabled (Do not delete. Do not deactivate)" => + "Tipo de precio promocional para la tienda Woocommerce, generado automáticamente. Necesario para el correcto funcionamiento de la sincronización cuando el programa de fidelización está habilitado (No eliminar. No desactivar)" ], "language" => "es", "x-generator" => "GlotPress/2.4.0-alpha", diff --git a/src/languages/retailcrm-es_ES.mo b/src/languages/retailcrm-es_ES.mo index 051c9cf..dc5cdc5 100644 Binary files a/src/languages/retailcrm-es_ES.mo and b/src/languages/retailcrm-es_ES.mo differ diff --git a/src/languages/retailcrm-ru_RU.l10n.php b/src/languages/retailcrm-ru_RU.l10n.php index 5afac2b..19a12cd 100644 --- a/src/languages/retailcrm-ru_RU.l10n.php +++ b/src/languages/retailcrm-ru_RU.l10n.php @@ -201,7 +201,8 @@ return [ "Товары с включенной опцией 'виртуальные' будут выгружаться в CRM как услуги", "Loyalty program" => "Программа лояльности", "Activate program loyalty" => "Активировать программу лояльности", - "Enable this setting for activate program loyalty on site" => "Активируйте эту настройку для активации программы лояльности на сайте", + "Attention! When activating the loyalty program, the method of ICML catalog generation changes. Details in" => "Внимание! При активации программы лояльности, изменяется способ генерации ICML каталога. Подробности в", + "documentation loyalty program" => "документации программы лояльности", "Terms of loyalty program" => "Условия программы лояльности", "Insert the terms and conditions of the loyalty program" => "Вставьте условия участия в программе лояльности", "Conditions of personal data processing" => "Условия обработки персональных данных", @@ -247,6 +248,15 @@ return [ "bonuses" => "бонусов", "Use coupon:" => "Используйте купон:", "Points will be awarded upon completion of the order:" => "По завершению заказа будет начислено баллов:", + "Unloading promotional prices of offers" => "Выгрузка акционных цен торговых предложений", + "Every 4 hours" => "Каждые 4 часа", + "Upload prices now" => "Выгрузить цены сейчас", + "Uploaded discount price" => "Выгрузка акционных цен", + "This functionality loads the promotional prices offers into Simla.com" => "Эта функция загружает акционные цены торговых предложений в Simla.com", + "Promotional prices unloaded" => "Акционные цены выгружены", + "Woocommerce promotional price" => "Акционная цена Woocommerce", + "Promotional price type for Woocommerce store, generated automatically. Necessary for correct synchronization work when loyalty program is enabled (Do not delete. Do not deactivate)" => + "Акционный тип цены для магазина Woocommerce, сгенерированный автоматически. Необходим для корректной работы синхронизации при включенной программы лояльности (Не удалять. Не деактивировать)" ], "language" => "ru", "x-generator" => "GlotPress/2.4.0-alpha", diff --git a/src/languages/retailcrm-ru_RU.mo b/src/languages/retailcrm-ru_RU.mo index 5cb7b69..e004250 100644 Binary files a/src/languages/retailcrm-ru_RU.mo and b/src/languages/retailcrm-ru_RU.mo differ diff --git a/src/readme.txt b/src/readme.txt index 45e1b7c..19bc3dd 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -5,7 +5,7 @@ Tags: Интеграция, Simla.com, simla Requires PHP: 7.0 Requires at least: 5.3 Tested up to: 6.5 -Stable tag: 4.8.3 +Stable tag: 4.8.4 License: GPLv1 or later License URI: http://www.gnu.org/licenses/gpl-1.0.html @@ -82,6 +82,9 @@ Asegúrate de tener una clave API específica para cada tienda. Las siguientes i == Changelog == += 4.8.4 = +* Updated work with promotional items when loyalty program is enabled + = 4.8.3 = * Added loyalty program coupon entry in the form by click diff --git a/src/retailcrm.php b/src/retailcrm.php index 0848cdf..222313a 100644 --- a/src/retailcrm.php +++ b/src/retailcrm.php @@ -5,7 +5,7 @@ * Description: Integration plugin for WooCommerce & Simla.com * Author: RetailDriver LLC * Author URI: http://retailcrm.pro/ - * Version: 4.8.3 + * Version: 4.8.4 * Tested up to: 6.5 * Requires Plugins: woocommerce * WC requires at least: 5.4 @@ -136,6 +136,7 @@ if (!class_exists( 'WC_Integration_Retailcrm')) : require_once(self::checkCustomFile('include/components/class-wc-retailcrm-loyalty-form.php')); require_once(self::checkCustomFile('include/validators/loyalty-validator/class-wc-retailcrm-loyalty-constraint.php')); require_once(self::checkCustomFile('include/validators/loyalty-validator/class-wc-retailcrm-loyalty-validator.php')); + require_once(self::checkCustomFile('include/class-wc-retailcrm-upload-discount-price.php')); } /** diff --git a/src/uninstall.php b/src/uninstall.php index 15381d1..4ea8c5c 100644 --- a/src/uninstall.php +++ b/src/uninstall.php @@ -16,7 +16,7 @@ * * @link https://wordpress.org/plugins/woo-retailcrm/ * - * @version 4.8.3 + * @version 4.8.4 * * @package RetailCRM */ diff --git a/tests/datasets/data-upload-price-retailcrm.php b/tests/datasets/data-upload-price-retailcrm.php new file mode 100644 index 0000000..3a5198c --- /dev/null +++ b/tests/datasets/data-upload-price-retailcrm.php @@ -0,0 +1,56 @@ + + * @license http://retailcrm.ru Proprietary + * @link http://retailcrm.ru + * @see http://help.retailcrm.ru + */ +class DataUploadPriceRetailCrm +{ + public static function dataGetPriceTypes() { + return [ + 'success' => true, + 'priceTypes' => [ + [ + 'code' => 'test', + 'name' => 'test', + 'active' => true, + 'description' => 'test', + 'ordering' => 999, + 'promo' => true, + 'default' => false + ], + [ + 'code' => 'default', + 'name' => 'default', + 'active' => true, + 'description' => 'default', + 'ordering' => 999, + 'promo' => true, + 'default' => true + ], + ] + ]; + } + + public static function willSendPriceType() { + return [ + 'code' => 'woo-promotion-lp', + 'name' => 'Woocommerce promotional price', + 'active' => true, + 'description' => 'Promotional price type for Woocommerce store, generated automatically. + Necessary for correct synchronization work when loyalty program is enabled + (Do not delete. Do not deactivate)', + 'ordering' => 999, + 'promo' => true, + ]; + } +} diff --git a/tests/test-wc-retailcrm-upload-discount-price.php b/tests/test-wc-retailcrm-upload-discount-price.php new file mode 100644 index 0000000..0d3894c --- /dev/null +++ b/tests/test-wc-retailcrm-upload-discount-price.php @@ -0,0 +1,74 @@ + + * @license http://retailcrm.ru Proprietary + * @link http://retailcrm.ru + * @see http://help.retailcrm.ru + */ +class WC_Retailcrm_Upload_Discount_Price_Test extends WC_Retailcrm_Test_Case_Helper +{ + protected $apiMock; + protected $responseMock; + + public function setUp() + { + WC_Helper_Product::create_simple_product(); + WC_Helper_Product::create_variation_product(); + + $this->setOptions(); + + $this->responseMock = $this->getMockBuilder('\WC_Retailcrm_Response_Helper') + ->disableOriginalConstructor() + ->setMethods(['isSuccessful']) + ->getMock() + ; + + $this->apiMock = $this->getMockBuilder('\WC_Retailcrm_Client_V5') + ->disableOriginalConstructor() + ->setMethods(['storePricesUpload', 'getSingleSiteForKey', 'getPriceTypes', 'editPriceType']) + ->getMock() + ; + + $this->responseMock->setResponse(['success' => true]); + $this->setMockResponse($this->responseMock, 'isSuccessful', true); + $this->setMockResponse($this->apiMock, 'getSingleSiteForKey', 'woo'); + + $this->responseMock->setResponse(DataUploadPriceRetailCrm::dataGetPriceTypes()); + $this->setMockResponse($this->apiMock, 'getPriceTypes', $this->responseMock); + } + + public function testUpload() + { + $this->apiMock + ->expects($this->exactly(1)) + ->method('storePricesUpload') + ->with($this->callback( + function ($parameter) { + if (is_array($parameter)) { + return true; + } + + return false; + } + ), $this->equalTo('woo')) + ; + + $this->apiMock + ->expects($this->exactly(1)) + ->method('editPriceType') + ->with($this->identicalTo(DataUploadPriceRetailCrm::willSendPriceType())) + ->willReturn($this->responseMock) + ; + + $uploadService = new WC_Retailcrm_Upload_Discount_Price($this->apiMock); + $uploadService->upload(); + } +}