diff --git a/doc/1.Setup/Catalog settings (product statuses).md b/doc/1.Setup/Catalog settings.md similarity index 57% rename from doc/1.Setup/Catalog settings (product statuses).md rename to doc/1.Setup/Catalog settings.md index b901e41..2c2eac2 100644 --- a/doc/1.Setup/Catalog settings (product statuses).md +++ b/doc/1.Setup/Catalog settings.md @@ -1,4 +1,12 @@ -### Настройки каталога *(статусы товаров)* +### Настройки каталога + +В версии 4.4.4 добавлен функционал передачи описания товара в каталог. В настройках каталога необходимо выбрать, какое описание передавать краткое или полное. По умолчанию передается полное описание товара. + +Поле description(описание) выводится в карточке товара, так же его можно использовать в twig-шаблонах. Например, для вывода в печатных формах. +Пример получения описание торгового предложения: +```twig +{% for availableOrderProduct in order.availableOrderProducts %} {{ availableOrderProduct.getOffer().getDescription() }} {% endfor %} +``` В настройке представлены статусы товаров в WooCommerce *(Товары -> карточка товара -> блок Опубликовано)*. Из товаров, чей статус будет соответствовать выбранному, будет сгенерирован ICML-файл каталога. Для выбора необходимо поставить галочку напротив нужного статуса и сохранить настройки. diff --git a/resources/pot/retailcrm-es_ES.pot b/resources/pot/retailcrm-es_ES.pot index 2519584..9cbe16f 100644 --- a/resources/pot/retailcrm-es_ES.pot +++ b/resources/pot/retailcrm-es_ES.pot @@ -317,4 +317,16 @@ msgid "Attention!" msgstr "¡Atención!" msgid "If payment type linked to the CRM integration module choosed, payment must be proceed in the CRM" -msgstr "Al seleccionar en el enlace de tipos de pago un método de pago de integración en CRM, el pago se debe realizar por parte del CRM" \ No newline at end of file +msgstr "Al seleccionar en el enlace de tipos de pago un método de pago de integración en CRM, el pago se debe realizar por parte del CRM" + +msgid "Product description" +msgstr "Descripción del Producto" + +msgid "Full description" +msgstr "Descripción completa" + +msgid "Short description" +msgstr "Descripción corta" + +msgid "In the catalog, you can use a full or short description of the product" +msgstr "En el catálogo, puedes utilizar una descripción del producto corta o completa" \ No newline at end of file diff --git a/resources/pot/retailcrm-ru_RU.pot b/resources/pot/retailcrm-ru_RU.pot index d0713cd..c0405ae 100644 --- a/resources/pot/retailcrm-ru_RU.pot +++ b/resources/pot/retailcrm-ru_RU.pot @@ -328,5 +328,15 @@ msgstr "Внимание!" msgid "If payment type linked to the CRM integration module choosed, payment must be proceed in the CRM" msgstr "При указании в соответствии типа оплаты, привязанного к интеграционному модулю в CRM, оплата должна происходить на стороне CRM" +msgid "Product description" +msgstr "Описание товара" +msgid "Full description" +msgstr "Полное описание" + +msgid "Short description" +msgstr "Краткое описание" + +msgid "In the catalog, you can use a full or short description of the product" +msgstr "В каталоге можно использовать полное или краткое описание товара" diff --git a/src/assets/css/whatsapp-icon.css b/src/assets/css/whatsapp-icon.css index e4fcefd..0f8c2be 100644 --- a/src/assets/css/whatsapp-icon.css +++ b/src/assets/css/whatsapp-icon.css @@ -1,5 +1,6 @@ .whatsapp-icon { position: fixed; + z-index: 999; left: 0; bottom: 55px; width: 60px; diff --git a/src/assets/css/whatsapp-icon.min.css b/src/assets/css/whatsapp-icon.min.css index 405d904..b115ba3 100644 --- a/src/assets/css/whatsapp-icon.min.css +++ b/src/assets/css/whatsapp-icon.min.css @@ -1 +1 @@ -.whatsapp-icon{position:fixed;left:0;bottom:55px;width:60px;height:60px;box-sizing:border-box}.whatsapp-icon_left{left:32px;right:auto}.whatsapp-icon_right{right:32px;left:auto}.whatsapp-icon__icon{width:100%;height:100%}.chat-btn__text{color:#8A96A6;font-weight:600;font-size:12px;line-height:14px;text-align:center;margin:8px 0 0} \ No newline at end of file +.whatsapp-icon{position:fixed;z-index: 999;left:0;bottom:55px;width:60px;height:60px;box-sizing:border-box}.whatsapp-icon_left{left:32px;right:auto}.whatsapp-icon_right{right:32px;left:auto}.whatsapp-icon__icon{width:100%;height:100%}.chat-btn__text{color:#8A96A6;font-weight:600;font-size:12px;line-height:14px;text-align:center;margin:8px 0 0} \ No newline at end of file diff --git a/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php b/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php index b2f2fce..c355934 100644 --- a/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php +++ b/src/include/abstracts/class-wc-retailcrm-abstracts-settings.php @@ -127,6 +127,21 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration 'id' => 'catalog_options' ); + $this->form_fields['product_description'] = [ + 'type' => 'select', + 'class' => 'select', + 'title' => __('Product description', 'retailcrm'), + 'options' => [ + 'full' => __('Full description', 'retailcrm'), + 'short' => __('Short description', 'retailcrm'), + ], + 'desc_tip' => true, + 'description' => __( + 'In the catalog, you can use a full or short description of the product', + 'retailcrm' + ), + ]; + foreach (get_post_statuses() as $status_key => $status_value) { $this->form_fields['p_' . $status_key] = array( 'title' => $status_value, @@ -134,7 +149,7 @@ abstract class WC_Retailcrm_Abstracts_Settings extends WC_Integration 'description' => '', 'class' => 'checkbox', 'type' => 'checkbox', - 'desc_tip' => true, + 'desc_tip' => true, ); } diff --git a/src/include/class-wc-retailcrm-icml.php b/src/include/class-wc-retailcrm-icml.php index 677fb7a..dd0bc73 100644 --- a/src/include/class-wc-retailcrm-icml.php +++ b/src/include/class-wc-retailcrm-icml.php @@ -1,4 +1,5 @@ settings = get_option(WC_Retailcrm_Base::$option_key); - $this->shop = get_bloginfo( 'name' ); + $this->shop = get_bloginfo('name'); $this->file = ABSPATH . 'simla.xml'; $this->tmpFile = sprintf('%s.tmp', $this->file); } @@ -80,8 +81,11 @@ if (!class_exists('WC_Retailcrm_Icml')) : $status_args = $this->checkPostStatuses(); $this->get_wc_products_taxonomies($status_args); + $dom = dom_import_simplexml(simplexml_load_file($this->tmpFile))->ownerDocument; + $dom->formatOutput = true; + $formatted = $dom->saveXML(); unset($dom, $this->xml); @@ -170,7 +174,7 @@ if (!class_exists('WC_Retailcrm_Icml')) : { $categories = self::filterRecursive($categories); - foreach($categories as $category) { + foreach ($categories as $category) { if (!array_key_exists('name', $category) || !array_key_exists('id', $category)) { continue; } @@ -278,7 +282,8 @@ if (!class_exists('WC_Retailcrm_Icml')) : * @param $key * @param $e */ - private function setOffersProperties($value, $key, &$e) { + private function setOffersProperties($value, $key, &$e) + { if (in_array($key, $this->properties) && $key != 'params') { /** @var SimpleXMLElement $e */ $e->addChild($key, htmlspecialchars($value)); @@ -292,7 +297,8 @@ if (!class_exists('WC_Retailcrm_Icml')) : * @param $key * @param $e */ - private function setOffersParams($value, $key, &$e) { + private function setOffersParams($value, $key, &$e) + { if ( array_key_exists('code', $value) && array_key_exists('name', $value) && @@ -323,7 +329,8 @@ if (!class_exists('WC_Retailcrm_Icml')) : $haystack[$key] = self::filterRecursive($haystack[$key]); } - if (is_null($haystack[$key]) + if ( + is_null($haystack[$key]) || $haystack[$key] === '' || (is_array($haystack[$key]) && count($haystack[$key]) == 0) ) { @@ -341,26 +348,27 @@ if (!class_exists('WC_Retailcrm_Icml')) : * * @return void */ - private function get_wc_products_taxonomies($status_args) { + private function get_wc_products_taxonomies($status_args) + { if (!$status_args) { - $status_args = array('publish'); + $status_args = ['publish']; } $attribute_taxonomies = wc_get_attribute_taxonomies(); - $product_attributes = array(); + $product_attributes = []; foreach ($attribute_taxonomies as $product_attribute) { $attribute_id = wc_attribute_taxonomy_name_by_id(intval($product_attribute->attribute_id)); $product_attributes[$attribute_id] = $product_attribute->attribute_label; } - $full_product_list = array(); + $full_product_list = []; $products = wc_get_products( - array( + [ 'limit' => -1, 'status' => $status_args - ) + ] ); foreach ($products as $offer) { @@ -391,8 +399,9 @@ if (!class_exists('WC_Retailcrm_Icml')) : * * @return array */ - private function get_wc_categories_taxonomies() { - $categories = array(); + private function get_wc_categories_taxonomies() + { + $categories = []; $taxonomy = 'product_cat'; $orderby = 'parent'; $show_count = 0; // 1 for yes, 0 for no @@ -401,7 +410,7 @@ if (!class_exists('WC_Retailcrm_Icml')) : $title = ''; $empty = 0; - $args = array( + $args = [ 'taxonomy' => $taxonomy, 'orderby' => $orderby, 'show_count' => $show_count, @@ -409,16 +418,16 @@ if (!class_exists('WC_Retailcrm_Icml')) : 'hierarchical' => $hierarchical, 'title_li' => $title, 'hide_empty' => $empty - ); + ]; $wcatTerms = get_categories($args); foreach ($wcatTerms as $term) { - $category = array( + $category = [ 'id' => $term->term_id, 'parentId' => $term->parent, 'name' => $term->name - ); + ]; $thumbnail_id = function_exists('get_term_meta') ? get_term_meta($term->term_id, 'thumbnail_id', true) @@ -533,13 +542,25 @@ if (!class_exists('WC_Retailcrm_Icml')) : ]; if ($product->get_sku() != '') { - $params[] = array('code' => 'article', 'name' => 'Артикул', 'value' => $product->get_sku()); + $params[] = ['code' => 'article', 'name' => 'Article', 'value' => $product->get_sku()]; if (isset($this->settings['bind_by_sku']) && $this->settings['bind_by_sku'] == WC_Retailcrm_Base::YES) { $product_data['xmlId'] = $product->get_sku(); } } + if (isset($this->settings['product_description'])) { + $productDescription = $this->getDescription($product); + + if (empty($productDescription) && $parent instanceof WC_Product_Variable) { + $this->getDescription($parent); + } + + if ($productDescription != '') { + $params[] = ['code' => 'description', 'name' => 'Description', 'value' => $productDescription]; + } + } + if (!empty($params)) { $product_data['params'] = $params; } @@ -556,8 +577,9 @@ if (!class_exists('WC_Retailcrm_Icml')) : * * @return array */ - private function checkPostStatuses() { - $status_args = array(); + private function checkPostStatuses() + { + $status_args = []; foreach (get_post_statuses() as $key => $value) { if (isset($this->settings['p_' . $key]) && $this->settings['p_' . $key] == WC_Retailcrm_Base::YES) { @@ -567,6 +589,20 @@ if (!class_exists('WC_Retailcrm_Icml')) : return $status_args; } + + /** + * Get product description + * + * @param WC_Product | WC_Product_Variable $product WC product. + * + * @return string + */ + private function getDescription($product) + { + return $this->settings['product_description'] == 'full' + ? $product->get_description() + : $product->get_short_description(); + } } endif; diff --git a/src/languages/retailcrm-es_ES.mo b/src/languages/retailcrm-es_ES.mo index 3af405c..684591a 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.mo b/src/languages/retailcrm-ru_RU.mo index 2b07ad7..8844dae 100644 Binary files a/src/languages/retailcrm-ru_RU.mo and b/src/languages/retailcrm-ru_RU.mo differ diff --git a/tests/helpers/class-wc-retailcrm-test-case-helper.php b/tests/helpers/class-wc-retailcrm-test-case-helper.php index e06eb30..69ba518 100644 --- a/tests/helpers/class-wc-retailcrm-test-case-helper.php +++ b/tests/helpers/class-wc-retailcrm-test-case-helper.php @@ -1,4 +1,5 @@ '', 'order-meta-data-retailcrm' => json_encode(['woo_order' => 'crm_order']), 'customer-meta-data-retailcrm' => json_encode(['woo_customer' => 'crm_customer']), + 'product_description' => 'full', ]; update_option(WC_Retailcrm_Base::$option_key, $options); @@ -78,16 +80,18 @@ class WC_Retailcrm_Test_Case_Helper extends WC_Unit_Test_Case } else { global $wpdb; - foreach ([ - $wpdb->posts, - $wpdb->postmeta, - $wpdb->comments, - $wpdb->commentmeta, - $wpdb->term_relationships, - $wpdb->termmeta, - ] as $table ) { + foreach ( + [ + $wpdb->posts, + $wpdb->postmeta, + $wpdb->comments, + $wpdb->commentmeta, + $wpdb->term_relationships, + $wpdb->termmeta, + ] as $table + ) { //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $wpdb->query( "DELETE FROM {$table}" ); + $wpdb->query("DELETE FROM {$table}"); } foreach ([$wpdb->terms, $wpdb->term_taxonomy] as $table) { diff --git a/tests/test-wc-retailcrm-base.php b/tests/test-wc-retailcrm-base.php index 6e97d95..d3ba7f8 100644 --- a/tests/test-wc-retailcrm-base.php +++ b/tests/test-wc-retailcrm-base.php @@ -119,6 +119,7 @@ class WC_Retailcrm_Base_Test extends WC_Retailcrm_Test_Case_Helper $this->assertArrayHasKey('deactivate_update_order', $this->baseRetailcrm->form_fields); $this->assertArrayHasKey('bind_by_sku', $this->baseRetailcrm->form_fields); $this->assertArrayHasKey('update_number', $this->baseRetailcrm->form_fields); + $this->assertArrayHasKey('product_description', $this->baseRetailcrm->form_fields); } public function test_retailcrm_form_fields_value() diff --git a/tests/test-wc-retailcrm-icml.php b/tests/test-wc-retailcrm-icml.php index a33d1d4..322b42a 100644 --- a/tests/test-wc-retailcrm-icml.php +++ b/tests/test-wc-retailcrm-icml.php @@ -14,33 +14,39 @@ class WC_Retailcrm_Icml_Test extends WC_Retailcrm_Test_Case_Helper { public function setUp() { - for ($i = 0; $i < 10; $i++) { - WC_Helper_Product::create_simple_product(); - } - - wp_insert_term( - 'Test', // the term - 'product_cat', // the taxonomy - array( - 'description'=> 'Test', - 'slug' => 'test' - ) - ); + WC_Helper_Product::create_simple_product(); + WC_Helper_Product::create_variation_product(); } public function testGenerate() { $icml = new WC_Retailcrm_Icml(); + $icml->generate(); - $this->assertFileExists(ABSPATH . 'simla.xml'); + $xml = simplexml_load_file(ABSPATH . 'simla.xml'); - $res = $xml->xpath('/yml_catalog/shop/categories/category[@id]'); - $this->assertNotEmpty($res); + $this->assertNotEmpty($xml); - foreach ($res as $node) { - $this->assertEquals('category', $node->getName()); + $xmlArray = json_decode(json_encode($xml), true); + + $this->assertNotEmpty($xmlArray['shop']['categories']['category']); + $this->assertCount(2, $xmlArray['shop']['categories']['category']); + $this->assertNotEmpty($xmlArray['shop']['offers']['offer']); + $this->assertCount(7, $xmlArray['shop']['offers']['offer']); + $this->assertNotEmpty($xmlArray['shop']['offers']['offer'][0]); + $this->assertNotEmpty($xmlArray['shop']['offers']['offer'][1]); + + foreach ($xmlArray['shop']['offers']['offer'] as $product) { + $this->assertNotEmpty($product['name']); + $this->assertNotEmpty($product['productName']); + $this->assertNotEmpty($product['price']); + $this->assertNotEmpty($product['url']); + $this->assertNotEmpty($product['param']); + $this->assertNotEmpty($product['vatRate']); + $this->assertEquals('none', $product['vatRate']); + $this->assertContains('Dummy', $product['productName']); } } }