├── docs
├── qa.md
├── xt.md
├── getting-started.md
├── _config.yml
└── index.md
├── css
└── admin.css
├── .gitignore
├── screenshots
├── screenshot-1.gif
├── screenshot-2.jpg
└── screenshot-3.jpg
├── includes
├── SiteHealthXT.php
├── LoggerProductSave.php
├── AbstractWalker.php
├── ProductsSkipIfSkuEmpty.php
├── ProductsExclusion.php
├── ProductsPrices.php
├── MetaColumn.php
├── LoaderIcon.php
├── SendWarehouse.php
├── MenuTools.php
├── ProductsScheduler.php
├── UseCodeAsArticle.php
├── OrderNotes.php
├── CategoriesFilter.php
├── Helper.php
├── SiteHealthDebugSection.php
├── SalePrices.php
├── SiteHealthWebHooks.php
├── OrderShipment.php
├── MSImagesTrait.php
├── TaxSupport.php
├── Logger.php
├── VariationsHider.php
├── SiteHealth.php
├── ProductsHiding.php
├── ProductVariableImage.php
├── CurrencyConverter.php
├── OrderNumber.php
├── functions.php
├── ProductSingleSync.php
├── Settings.php
├── OrderStatusesFromSite.php
├── ProductAttributes.php
├── ProductGallery.php
├── ProductsServices.php
├── ProductGrouped.php
├── ProductsCategories.php
└── ProductsImage.php
├── tests
├── bootstrap.php
├── includes
│ ├── Base.php
│ ├── ProductAttributes.php
│ ├── CurrencyConverter.php
│ ├── Products.php
│ ├── ProductStocks.php
│ └── ProductVariable.php
├── functions.php
├── rest-api-local
│ └── RestApi.php
└── data
│ └── currency.json
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── workflows
│ ├── to-release.yml
│ ├── zip.yml
│ └── deploy-to-test.yml
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── .editorconfig
├── phpunit.xml.dist
├── composer.json
├── .distignore
├── README.md
└── wooms.php
/docs/qa.md:
--------------------------------------------------------------------------------
1 | # Частые вопросы
2 |
--------------------------------------------------------------------------------
/docs/xt.md:
--------------------------------------------------------------------------------
1 | # Расширенная версия
2 | ...
--------------------------------------------------------------------------------
/css/admin.css:
--------------------------------------------------------------------------------
1 | .column-wooms-sync {
2 | width: 1rem;
3 | }
--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------
1 | # С чего начать?
2 |
3 | Раз два три
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | vendor
3 | phpunit/data
4 | .phpunit.result.cache
5 |
--------------------------------------------------------------------------------
/screenshots/screenshot-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpcraft-ru/wooms/HEAD/screenshots/screenshot-1.gif
--------------------------------------------------------------------------------
/screenshots/screenshot-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpcraft-ru/wooms/HEAD/screenshots/screenshot-2.jpg
--------------------------------------------------------------------------------
/screenshots/screenshot-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpcraft-ru/wooms/HEAD/screenshots/screenshot-3.jpg
--------------------------------------------------------------------------------
/includes/SiteHealthXT.php:
--------------------------------------------------------------------------------
1 |
2 |
%s
', 'Если опция активна, то плагин будет пропускать синхронизацию продуктов без артикула: https://github.com/wpcraft-ru/wooms/issues/461'); 40 | }, 41 | $page = 'mss-settings', 42 | $section = 'woomss_section_other', 43 | $args = [ 44 | 'key' => sprintf("wooms_config[%s]", CONFIG_KEY), 45 | 'value' => get_option('wooms_config')[CONFIG_KEY] ?? false, 46 | 'label_for' => CONFIG_KEY, 47 | ] 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /includes/ProductsExclusion.php: -------------------------------------------------------------------------------- 1 | ', $args['key'], $args['value']); 43 | printf('%s
', 'Укажите название поля по которому поймем что этот продукт загружать на сайт не нужно. Тип поля в МойСклад должен быть - Флажок'); 44 | }, 45 | $page = 'mss-settings', 46 | $section = 'woomss_section_other', 47 | $args = [ 48 | 'key' => OPTION_KEY, 49 | 'value' => get_option( OPTION_KEY ), 50 | ] 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /includes/ProductsPrices.php: -------------------------------------------------------------------------------- 1 | get_id(); 16 | 17 | $price = 0; 18 | $price_meta = []; 19 | 20 | if ($price_name = get_option('wooms_price_id')) { 21 | foreach ($data_api["salePrices"] as $price_item) { 22 | if ($price_item["priceType"]['name'] == $price_name) { 23 | $price = $price_item["value"]; 24 | $price_meta = $price_item; 25 | } 26 | } 27 | } 28 | 29 | if (empty($price)) { 30 | $price = floatval($data_api['salePrices'][0]['value']); 31 | $price_meta = $data_api['salePrices'][0]; 32 | } 33 | 34 | $price = apply_filters('wooms_product_price', $price, $data_api, $product_id, $price_meta); 35 | 36 | $price = floatval($price) / 100; 37 | $price = round($price, 2); 38 | $product->set_regular_price($price); 39 | 40 | return $product; 41 | } 42 | 43 | function add_settings() 44 | { 45 | $key = 'wooms_price_id'; 46 | register_setting('mss-settings', $key); 47 | add_settings_field( 48 | $id = 'wooms_price_id', 49 | $title = 'Тип Цены', 50 | $callback = function($args) { 51 | printf('', $args['key'], $args['value']); 52 | echo 'Укажите наименование цены, если нужно выбрать специальный тип цен. Система будет проверять такой тип цены и если он указан то будет подставлять его вместо базового.
'; 53 | }, 54 | $page = 'mss-settings', 55 | $section = 'woomss_section_other', 56 | $args = [ 57 | 'key' => $key, 58 | 'value' => get_option($key), 59 | ] 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /tests/includes/Products.php: -------------------------------------------------------------------------------- 1 | get_name() ) { 60 | return true; 61 | } 62 | return false; 63 | 64 | } ); 65 | 66 | 67 | /** 68 | * Яндекс Станция - простой продукт 69 | */ 70 | function getJsonForSimpleProduct_code00045() { 71 | $rows = getProductsRows(); 72 | foreach ( $rows as $row ) { 73 | if ( $row['code'] == "00045" ) { 74 | return $row; 75 | } 76 | } 77 | return false; 78 | } 79 | -------------------------------------------------------------------------------- /includes/MetaColumn.php: -------------------------------------------------------------------------------- 1 | 'Синхронизация с МойСклад' 37 | ); 38 | 39 | return array_merge( $columns, $added_columns ); 40 | } 41 | 42 | /** 43 | * Displays the column content for the given column. 44 | * 45 | * @param string $column_name Column to display the content for. 46 | * @param int $post_ID Post to display the column content for. 47 | * 48 | * @return void 49 | */ 50 | public static function column_content( $column_name, $post_ID ) { 51 | 52 | if ( $column_name === 'wooms-sync' ) { 53 | 54 | $uuid = get_post_meta($post_ID, 'wooms_id', true); 55 | 56 | if (!$uuid) { 57 | echo 'Товар не синхронизирован с МойСклад'; 58 | } else { 59 | echo 'Товар не синхронизирован с МойСклад'; 60 | } 61 | } 62 | } 63 | 64 | } 65 | 66 | MetaColumn::init(); 67 | -------------------------------------------------------------------------------- /includes/LoaderIcon.php: -------------------------------------------------------------------------------- 1 | '; 21 | }); 22 | 23 | 24 | add_action('admin_head', function () { 25 | 26 | if ('toplevel_page_moysklad' != get_current_screen()->id) { 27 | return; 28 | } 29 | ?> 30 | 75 | \WooMS\get_api_url('entity/store/' . $warehouse_id), 38 | "type" => "store", 39 | ); 40 | 41 | return $data; 42 | } 43 | 44 | /** 45 | * Setting 46 | */ 47 | public static function settings_init() { 48 | 49 | register_setting( 'mss-settings', 'wooms_orders_send_warehouse' ); 50 | 51 | if(get_option('woomss_warehouse_id')){ 52 | add_settings_field( 53 | $id = 'wooms_orders_send_warehouse', 54 | $title = 'Отправлять выбранный склад в Заказе', 55 | $callback = function($args){ 56 | printf( '', $args['key'], checked( 1, $args['value'], false ) ); 57 | printf( '%s
', 'Активация опции позволяет передавать выбранный склад в Заказе' ); 58 | }, 59 | $page = 'mss-settings', 60 | $section = 'wooms_section_orders', 61 | $args = [ 62 | 'key' => 'wooms_orders_send_warehouse', 63 | 'value' => get_option('wooms_orders_send_warehouse'), 64 | ] 65 | ); 66 | } 67 | 68 | } 69 | 70 | /** 71 | * display_wooms_orders_send_warehouse 72 | */ 73 | public static function display_wooms_orders_send_warehouse($args){ 74 | } 75 | } 76 | 77 | SendWarehouse::init(); 78 | -------------------------------------------------------------------------------- /tests/rest-api-local/RestApi.php: -------------------------------------------------------------------------------- 1 | $now, 21 | 'query_arg' => [ 22 | 'offset' => 10, 23 | 'limit' => 10, 24 | ], 25 | 'rows_in_bunch' => 20, 26 | 'timestamp' => $now, 27 | 'end_timestamp' => 0, 28 | ]; 29 | 30 | $r = \WooMS\Products\walker( $args ); 31 | 32 | transaction_query( 'rollback' ); 33 | 34 | ddcli($r); 35 | 36 | if ( 'restart' != $r['result'] ) { 37 | throw new Error('$r[result] should be restart'); 38 | } 39 | if ( 20 != $r['args_next_iteration']['query_arg']['offset'] ) { 40 | return false; 41 | } 42 | 43 | if ( empty( $r['args_next_iteration']['session_id'] ) ) { 44 | return false; 45 | } 46 | 47 | return true; 48 | 49 | } ); 50 | 51 | 52 | test('assotment sync - base test - rest api', function(){ 53 | transaction_query('start'); 54 | 55 | $args = array( 56 | 'post_type' => [ 'product', 'product_variation' ], 57 | 'numberposts' => 20, 58 | ); 59 | 60 | $products = get_posts( $args ); 61 | 62 | foreach ( $products as $product ) { 63 | update_post_meta( $product->ID, \WooMS\ProductStocks::$walker_hook_name, true ); 64 | } 65 | 66 | $result = \WooMS\ProductStocks::batch_handler(); 67 | transaction_query('rollback'); 68 | 69 | if($result){ 70 | return true; 71 | } 72 | 73 | 74 | return false; 75 | 76 | }); 77 | 78 | 79 | 80 | /** 81 | * wp test tdd/RestApi.php 82 | */ 83 | test('check woooms request new api', function(){ 84 | 85 | $data = request('https://api.moysklad.ru/api/remap/1.2/entity/product'); 86 | 87 | if(isset($data['context'])){ 88 | return true; 89 | } 90 | 91 | return false; 92 | 93 | }); 94 | 95 | test('check woooms request new api', function(){ 96 | 97 | $data = request('entity/product'); 98 | 99 | if(isset($data['context'])){ 100 | return true; 101 | } 102 | 103 | return false; 104 | 105 | }); 106 | -------------------------------------------------------------------------------- /includes/MenuTools.php: -------------------------------------------------------------------------------- 1 | %s', 'Управление МойСклад' ); 49 | $items = [ 50 | sprintf( 'Настройки', admin_url( "admin.php?page=mss-settings" ) ), 51 | 'Вход в МойСклад', 52 | sprintf( 'Здоровье сайта', admin_url( "site-health.php" ) ), 53 | 'Рекомендуемые хостинги', 54 | 'Контакты', 55 | ]; 56 | ?> 57 | 62 | Укажите логин и пароль на странице настроек', admin_url( 'admin.php?page=mss-settings' ) ); 67 | } else { 68 | if ( empty( $_GET['a'] ) ) { 69 | 70 | do_action( 'wooms_tools_sections' ); 71 | 72 | // deprecated 73 | do_action( 'woomss_tool_actions_btns' ); 74 | 75 | } else { 76 | 77 | printf( 'Вернуться...', remove_query_arg( 'a', self::$url ) ); 78 | do_action( 'woomss_tool_actions' ); 79 | do_action( 'woomss_tool_actions_' . $_GET['a'] ); 80 | 81 | } 82 | } 83 | 84 | 85 | } 86 | 87 | 88 | } 89 | 90 | MenuTools::init(); 91 | -------------------------------------------------------------------------------- /tests/includes/ProductStocks.php: -------------------------------------------------------------------------------- 1 | get_manage_stock()){ 32 | throw new Error('get_manage_stock - not working'); 33 | } 34 | 35 | if(empty($row['quantity'])){ 36 | throw new Error('$row[quantity] is empty'); 37 | } 38 | 39 | if($row['quantity'] !== $product->get_stock_quantity()){ 40 | throw new Error('quantity not good'); 41 | 42 | } 43 | 44 | return true; 45 | 46 | }); 47 | 48 | test('assortment sync - base test - one product', function(){ 49 | transaction_query('start'); 50 | 51 | $data = \WooMS\Tests\get_assortment(); 52 | 53 | foreach($data['rows'] as $row){ 54 | if($row['id'] === 'e94c3184-7644-11ee-0a80-143f001044a3'){ 55 | break; 56 | } 57 | } 58 | 59 | $product_id = wooms_get_product_id_by_uuid('e94c3184-7644-11ee-0a80-143f001044a3'); 60 | 61 | $product = wc_get_product($product_id); 62 | 63 | $product = \WooMS\ProductStocks::update_stock($product, $row); 64 | 65 | transaction_query('rollback'); 66 | 67 | if(empty($row['quantity'])){ 68 | throw new Error('$row[quantity] is empty'); 69 | } 70 | if($row['quantity'] === $product->get_stock_quantity()){ 71 | return true; 72 | } 73 | 74 | return false; 75 | 76 | }); 77 | 78 | 79 | test('assortment sync - base test', function(){ 80 | transaction_query('start'); 81 | 82 | $data = \WooMS\Tests\get_assortment(); 83 | 84 | $ids = \WooMS\ProductStocks::process_rows($data['rows']); 85 | 86 | transaction_query('rollback'); 87 | 88 | if($ids){ 89 | return true; 90 | } 91 | 92 | return false; 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /includes/ProductsScheduler.php: -------------------------------------------------------------------------------- 1 | ', $args['key'], checked(1, $args['value'], false)); 59 | }, 60 | $page = 'mss-settings', 61 | $section, 62 | $args = [ 63 | 'key' => OPTION_KEY . '[walker_cron_enabled]', 64 | 'value' => get_config('walker_cron_enabled'), 65 | ] 66 | ); 67 | 68 | add_settings_field( 69 | $id = 'walker_cron_timer', 70 | $title = 'Перерыв синхронизации в часах', 71 | $callback = function($args){ 72 | printf('', $args['key'], $args['value']); 73 | }, 74 | $page = 'mss-settings', 75 | $section, 76 | $args = [ 77 | 'key' => OPTION_KEY . '[walker_cron_timer]', 78 | 'value' => get_config('walker_cron_timer') ?? 12, 79 | ] 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /includes/UseCodeAsArticle.php: -------------------------------------------------------------------------------- 1 | set_sku($data_api['code']); 34 | return $product; 35 | } 36 | 37 | public static function get_product_id_by_code($product_id, $data_api) 38 | { 39 | if ( self::is_disable() ) { 40 | return $product_id; 41 | } 42 | 43 | if ($product_id_by_code = wc_get_product_id_by_sku($data_api['code'])) { 44 | return $product_id_by_code; 45 | } 46 | 47 | return $product_id; 48 | } 49 | 50 | public static function is_disable() 51 | { 52 | if (get_option('wooms_use_code_as_article_enable')) { 53 | return false; 54 | } 55 | 56 | return true; 57 | } 58 | 59 | /** 60 | * Setting 61 | */ 62 | public static function add_settings() 63 | { 64 | $option_key = 'wooms_use_code_as_article_enable'; 65 | 66 | register_setting('mss-settings', $option_key); 67 | add_settings_field( 68 | $id = $option_key, 69 | $title = 'Использовать код как артикул', 70 | $callback = function ($args) { 71 | printf( 72 | '', 73 | $args['key'], 74 | checked(1, $args['value'], $echo = false) 75 | ); 76 | printf('%s
', 'Если включена опция, то плагин будет пытаться связывать товары по коду из МойСклад и артикулу из Сайта'); 77 | printf('%s
', 'Подробнее: https://github.com/wpcraft-ru/wooms/issues/98'); 78 | }, 79 | $page = 'mss-settings', 80 | $section = 'woomss_section_other', 81 | $args = [ 82 | 'key' => $option_key, 83 | 'value' => get_option($option_key), 84 | ] 85 | ); 86 | } 87 | } 88 | 89 | UseCodeAsArticle::init(); 90 | -------------------------------------------------------------------------------- /includes/OrderNotes.php: -------------------------------------------------------------------------------- 1 | get_shipping_method()) { 29 | $notes['shipment_method'] = sprintf("Метод доставки: %s", $shipment_method); 30 | } 31 | 32 | if($formatted_shipping_address = $order->get_formatted_shipping_address()){ 33 | $formatted_shipping_address = str_replace('%s
', 65 | 'Включите эту опцию, если нужно передавать дополнительные заметки в Заказе: адрес доставки, телефон и т д' 66 | ); 67 | }, 68 | $page = 'mss-settings', 69 | $section = 'wooms_section_orders', 70 | $args = [ 71 | 'key' => $order_additional_notes, 72 | 'value' => get_option($order_additional_notes) 73 | ] 74 | ); 75 | } 76 | } 77 | 78 | OrderNotes::init(); -------------------------------------------------------------------------------- /includes/CategoriesFilter.php: -------------------------------------------------------------------------------- 1 | %s', $args['key'], $args['value'] ); 90 | printf( '%s
', 91 | 'Тут можно указать группы для фильтрации товаров через запятую. Например: "Мебель/Диваны,Пицца,Одежда/Обувь/Ботинки"' 92 | ); 93 | }, 94 | $setings = 'mss-settings', 95 | $group = 'wooms_product_cat', 96 | $arts = [ 97 | 'key' => 'wooms_set_folders', 98 | 'value' => get_option( 'wooms_set_folders' ), 99 | ] 100 | ); 101 | 102 | } 103 | 104 | } 105 | 106 | CategoriesFilter::init(); 107 | -------------------------------------------------------------------------------- /includes/Helper.php: -------------------------------------------------------------------------------- 1 | [ 'product', 'product_variation' ], 24 | 'meta_key' => 'wooms_id_' . $uuid, 25 | ] ); 26 | 27 | if ( isset( $posts[0]->ID ) ) { 28 | return $posts[0]->ID; 29 | } 30 | 31 | $posts = get_posts( [ 32 | 'post_type' => [ 'product', 'product_variation' ], 33 | 'meta_key' => 'wooms_id', 34 | 'meta_value' => $uuid 35 | ] ); 36 | 37 | if ( empty( $posts[0]->ID ) ) { 38 | return false; 39 | } else { 40 | return $posts[0]->ID; 41 | } 42 | } 43 | 44 | public static function log( string $message, $class = 'WooMS', array $data = [] ) { 45 | if ( ! Logger::is_enable() ) { 46 | return; 47 | } 48 | 49 | if ( ! empty( $data ) ) { 50 | 51 | if ( is_array( $data ) ) { 52 | $data = json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); 53 | } else { 54 | $data = wc_print_r( $data, true ); 55 | } 56 | 57 | $data = wp_trim_words( $data, 300 ); 58 | $message .= PHP_EOL . '-' . PHP_EOL . $data; 59 | } 60 | 61 | $source = str_replace( '\\', '-', $class ); 62 | 63 | $logger = wc_get_logger(); 64 | $context = array( 'source' => $source ); 65 | $logger->info( $message, $context ); 66 | } 67 | 68 | public static function log_error( string $message, $class = 'WooMS', array $data = [] ) { 69 | 70 | if ( ! Logger::is_enable() ) { 71 | return; 72 | } 73 | 74 | if ( ! empty( $data ) ) { 75 | 76 | if ( is_array( $data ) ) { 77 | $data = json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); 78 | } else { 79 | $data = wc_print_r( $data, true ); 80 | } 81 | 82 | $data = wp_trim_words( $data, 300 ); 83 | $message .= PHP_EOL . '-' . PHP_EOL . $data; 84 | } 85 | 86 | $source = str_replace( '\\', '-', $class ); 87 | 88 | $logger = wc_get_logger(); 89 | $context = array( 'source' => $source ); 90 | $logger->error( $message, $context ); 91 | 92 | } 93 | 94 | 95 | public static function get_timestamp_last_job_by_hook($hook){ 96 | $store = \ActionScheduler::store(); 97 | $data = $store->query_actions([ 98 | 'hook' => $hook, 99 | 'orderby' => 'date', 100 | 'order' => 'DESC', 101 | ]); 102 | 103 | if(empty($data[0])){ 104 | return null; 105 | } 106 | 107 | $date = $store->get_date($data[0]); 108 | $date->setTimezone(new \DateTimeZone(wp_timezone_string())); 109 | return $date->format('Y-m-d H:i:s'); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /includes/SiteHealthDebugSection.php: -------------------------------------------------------------------------------- 1 | version; 39 | 40 | $result = [ 41 | 'label' => 'Версия WooCommerce', 42 | 'value' => sprintf('%s %s', $wc_version, '✔️'), 43 | ]; 44 | 45 | if (version_compare($wc_version, '3.6.0', '<=')) { 46 | $result['value'] = sprintf('Ваша версия WooCommerce плагина %s. Обновите пожалуйста WooCommerce чтобы WooMS & WooMS XT работали %s', $wc_version, '❌'); 47 | } 48 | 49 | $debug_info['wooms-plugin-debug']['fields']['Woocommerce'] = $result; 50 | 51 | return $debug_info; 52 | } 53 | 54 | 55 | /** 56 | * debuging and adding to debug sections of health page 57 | * 58 | * @param [type] $debug_info 59 | * @return void 60 | */ 61 | public static function add_info_to_debug($debug_info) 62 | { 63 | 64 | $base_plugin_data = get_plugin_data(self::$plugin_dir . self::$base_plugin_url); 65 | $base_version = $base_plugin_data['Version']; 66 | 67 | $debug_info['wooms-plugin-debug'] = [ 68 | 'label' => 'Wooms', 69 | 'fields' => [ 70 | 'Wooms Version' => [ 71 | 'label' => 'Версия WooMS', 72 | 'value' => sprintf('%s %s', $base_version, '✔️'), 73 | ] 74 | ], 75 | ]; 76 | 77 | $debug_info = apply_filters('add_wooms_plugin_debug', $debug_info); 78 | 79 | return $debug_info; 80 | } 81 | 82 | /** 83 | * checking login and password moy sklad 84 | * 85 | * @param [type] $debug_info 86 | * @return void 87 | */ 88 | public static function check_login_and_password($debug_info) 89 | { 90 | 91 | if (!get_transient('wooms_check_login_password')) { 92 | return $debug_info; 93 | } 94 | 95 | $debug_info['wooms-plugin-debug']['fields']['wooms-login-check'] = [ 96 | 'label' => 'Версия WooMS', 97 | 'value' => get_transient('wooms_check_login_password'), 98 | ]; 99 | return $debug_info; 100 | } 101 | 102 | } 103 | 104 | SiteHealthDebugSection::init(); 105 | -------------------------------------------------------------------------------- /includes/SalePrices.php: -------------------------------------------------------------------------------- 1 | get_id(); 30 | 31 | $price_name = esc_html(get_option('wooms_price_sale_name')); 32 | 33 | 34 | if (empty($price_name)) { 35 | $product->set_sale_price(''); 36 | return $product; 37 | } 38 | 39 | if (empty($value['salePrices'])) { 40 | $product->set_sale_price(''); 41 | do_action( 42 | 'wooms_logger_error', 43 | __CLASS__, 44 | sprintf('Нет цен для продукта %s', $product_id) 45 | ); 46 | 47 | return $product; 48 | } 49 | 50 | $sale_price = 0; 51 | $price_meta = []; 52 | foreach ($value['salePrices'] as $price) { 53 | 54 | if ($price['priceType']["name"] == $price_name && floatval($price['value']) > 0) { 55 | $sale_price = floatval($price['value'] / 100); 56 | $price_meta = $price; 57 | } 58 | } 59 | 60 | $sale_price = apply_filters('wooms_sale_price', $sale_price, $value, $product_id, $price_meta); 61 | 62 | if ($sale_price) { 63 | $sale_price = round($sale_price, 2); 64 | $sale_price = (string) $sale_price; 65 | $product->set_sale_price($sale_price); 66 | 67 | do_action( 68 | 'wooms_logger', 69 | __CLASS__, 70 | sprintf( 71 | 'Цена распродажи %s сохранена для продукта %s (%s)', 72 | $sale_price, 73 | $product->get_name(), 74 | $product_id 75 | ) 76 | ); 77 | } else { 78 | $product->set_sale_price(''); 79 | } 80 | 81 | return $product; 82 | } 83 | 84 | 85 | /** 86 | * Add settings 87 | */ 88 | public static function settings_init() 89 | { 90 | register_setting('mss-settings', 'wooms_price_sale_name'); 91 | add_settings_field( 92 | $id = 'wooms_price_sale_name', 93 | $title = 'Тип Цены Распродажи', 94 | $callback = function ($args) { 95 | printf('', $args['key'], $args['value']); 96 | echo 'Укажите наименование цены для Распродаж. Система будет проверять такой тип цены и если он указан то будет сохранять его в карточке Продукта.
'; 97 | echo 'Если оставить поле пустым, то цена Распродажи у всех продуктов будут удалены после очередной синхронизации.
'; 98 | }, 99 | $page = 'mss-settings', 100 | $section = 'woomss_section_other', 101 | $args = [ 102 | 'key' => 'wooms_price_sale_name', 103 | 'value' => sanitize_text_field(get_option('wooms_price_sale_name')), 104 | ] 105 | ); 106 | } 107 | } 108 | 109 | SalePrices::init(); 110 | -------------------------------------------------------------------------------- /includes/SiteHealthWebHooks.php: -------------------------------------------------------------------------------- 1 | 'check_webhooks', 20 | ]; 21 | 22 | return $tests; 23 | }); 24 | 25 | add_action('wp_ajax_health-check-check-webhooks', [__CLASS__, 'check_webhooks']); 26 | 27 | add_filter('add_wooms_plugin_debug', [__CLASS__, 'wooms_check_moy_sklad_user_tarrif']); 28 | } 29 | 30 | 31 | /** 32 | * Check can we add webhooks 33 | * 34 | * @param [type] $debug_info 35 | * @return void 36 | */ 37 | public static function wooms_check_moy_sklad_user_tarrif($debug_info) 38 | { 39 | 40 | if (!get_transient('wooms_check_moysklad_tariff')) { 41 | return $debug_info; 42 | } 43 | 44 | $debug_info['wooms-plugin-debug']['fields']['wooms-tariff-for-orders'] = [ 45 | 'label' => 'Подписка МойСклад', 46 | 'value' => get_transient('wooms_check_moysklad_tariff'), 47 | ]; 48 | 49 | 50 | return $debug_info; 51 | } 52 | 53 | 54 | /** 55 | * Check can we add webhooks 56 | * 57 | * @return bool 58 | */ 59 | public static function check_webhooks() 60 | { 61 | 62 | 63 | // создаем веб хук в МойСклад 64 | $data = array( 65 | 'url' => rest_url('/wooms/v1/order-update/'), 66 | 'action' => "UPDATE", 67 | "entityType" => "customerorder", 68 | ); 69 | 70 | $api_result = request('entity/webhook', $data); 71 | 72 | $result = [ 73 | 'label' => "Проверка подписки МойСклад", 74 | 'status' => 'good', 75 | 'badge' => [ 76 | 'label' => 'Уведомление WooMS', 77 | 'color' => 'blue', 78 | ], 79 | 'description' => sprintf("Все хорошо! Спасибо что используете наш плагин %s", '🙂'), 80 | 'test' => 'wooms_check_weebhooks' // this is only for class in html block 81 | ]; 82 | 83 | if (empty($api_result['errors'][0]['code'])) { 84 | wp_send_json_success($result); 85 | } 86 | 87 | if (30006 == $api_result['errors'][0]['code']) { 88 | $result['status'] = 'critical'; 89 | $result['badge']['color'] = 'red'; 90 | $result['description'] = sprintf("%s %s", $api_result['errors'][0]['error'], '❌'); 91 | set_transient('wooms_check_moysklad_tariff', $result['description'], 60 * 60); 92 | } 93 | 94 | // Checking permissions too 95 | $data_api_p = request('context/employee', [], 'GET'); 96 | 97 | foreach ($data_api_p['permissions']['webhook'] as $permission) { 98 | if (!$permission) { 99 | $description = "У данного пользователя не хватает прав для работы с вебхуками"; 100 | $result['description'] = sprintf('%s %s', $description, '❌'); 101 | if (!empty($api_result['errors'])) { 102 | $result['description'] = sprintf("1. %s 2. %s %s", $api_result['errors'][0]['error'], $description, '❌'); 103 | } 104 | } 105 | 106 | // Добовляем значение для вывода ошибки в здаровье сайта 107 | set_transient('wooms_check_moysklad_tariff', $result['description'], 60 * 60); 108 | } 109 | 110 | wp_send_json_success($result); 111 | } 112 | } 113 | 114 | SiteHealthWebHooks::init(); 115 | -------------------------------------------------------------------------------- /includes/OrderShipment.php: -------------------------------------------------------------------------------- 1 | 1, 65 | 'price' => $order->get_shipping_total() * 100, 66 | 'assortment' => array( 67 | 'meta' => $meta, 68 | ), 69 | 'reserve' => 0, 70 | ); 71 | 72 | return $data; 73 | } 74 | 75 | /** 76 | * get meta for shipment item 77 | * 78 | * @param $order_shipment_item_code 79 | */ 80 | public static function get_meta_for_shipment_item($order_shipment_item_code) 81 | { 82 | $url = 'entity/service'; 83 | $url = add_query_arg('filter=code', $order_shipment_item_code, $url); 84 | $data = request($url); 85 | 86 | if (empty($data['rows'][0]['meta'])) { 87 | return false; 88 | } 89 | 90 | $meta = $data['rows'][0]['meta']; 91 | return $meta; 92 | } 93 | 94 | /** 95 | * Settings UI 96 | */ 97 | public static function add_settings() 98 | { 99 | 100 | $order_shipment_item_key = 'wooms_order_shipment_item_code'; 101 | register_setting('mss-settings', $order_shipment_item_key); 102 | add_settings_field( 103 | $id = $order_shipment_item_key, 104 | $title = 'Код позиции для передачи стоимости доставки', 105 | $callback = function ($args) { 106 | printf('', $args['key'], $args['value']); 107 | printf( 108 | '%s
', 109 | 'Если нужно передавать стоимость доставки, укажите тут код соответствующей услуги из МойСклад (поле Код в карточке Услуги)' 110 | ); 111 | }, 112 | $page = 'mss-settings', 113 | $section = 'wooms_section_orders', 114 | $args = [ 115 | 'key' => $order_shipment_item_key, 116 | 'value' => get_option($order_shipment_item_key) 117 | ] 118 | ); 119 | } 120 | } 121 | 122 | OrderShipment::init(); 123 | -------------------------------------------------------------------------------- /includes/MSImagesTrait.php: -------------------------------------------------------------------------------- 1 | 'Basic ' . base64_encode(get_option('woomss_login') . ':' . get_option('woomss_pass')), 32 | ]; 33 | 34 | $args = [ 35 | 'headers' => $header_array, 36 | ]; 37 | 38 | $get = wp_remote_get($image_url, $args); 39 | 40 | if( is_wp_error( $get ) ) { 41 | do_action( 42 | 'wooms_logger_error', 43 | __CLASS__, 44 | sprintf('Ошибка загрузки картинки: %s', $get->get_error_message()), 45 | $get->get_error_code() 46 | ); 47 | 48 | return false; 49 | 50 | } 51 | 52 | if (empty($get['response']['code'])) { 53 | return false; 54 | } 55 | 56 | if (403 == $get['response']['code']) { 57 | $http_response = $get['http_response']; 58 | 59 | if ($http_response->get_status() == 403) { 60 | $response = $http_response->get_response_object(); 61 | $url_image = $http_response->get_response_object()->url; 62 | 63 | $get2 = wp_remote_get($url_image); 64 | $mirror = wp_upload_bits($filename, '', wp_remote_retrieve_body($get2)); 65 | } 66 | } else { 67 | 68 | $mirror = wp_upload_bits($filename, '', wp_remote_retrieve_body($get)); 69 | } 70 | 71 | $type = $filename_data['type']; 72 | 73 | if (!$type) 74 | return false; 75 | 76 | 77 | $attachment = array( 78 | 'post_title' => $filename, 79 | 'post_mime_type' => $type 80 | ); 81 | 82 | $attach_id = wp_insert_attachment($attachment, $mirror['file'], $product_id); 83 | 84 | require_once(ABSPATH . 'wp-admin/includes/image.php'); 85 | 86 | $attach_data = wp_generate_attachment_metadata($attach_id, $mirror['file']); 87 | 88 | update_post_meta($attach_id, 'wooms_url', $image_url); 89 | 90 | wp_update_attachment_metadata($attach_id, $attach_data); 91 | 92 | do_action( 93 | 'wooms_logger', 94 | __CLASS__, 95 | sprintf('Image is downloaded %s (ИД %s, filename: %s)', $product_id, $attach_id, $filename) 96 | ); 97 | 98 | return $attach_id; 99 | } 100 | 101 | 102 | /** 103 | * Check exist image by URL 104 | */ 105 | public static function check_exist_image_by_url($url_api) 106 | { 107 | $posts = get_posts('post_type=attachment&meta_key=wooms_url&meta_value=' . $url_api); 108 | if (empty($posts)) { 109 | return false; 110 | } else { 111 | 112 | do_action( 113 | 'wooms_logger', 114 | __CLASS__, 115 | sprintf('We have such image (%s) already', $posts[0]->ID) 116 | ); 117 | 118 | return $posts[0]->ID; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /includes/TaxSupport.php: -------------------------------------------------------------------------------- 1 | get_tax_class(); 34 | 35 | switch ($tax_class) { 36 | case 20: 37 | $position['vat'] = 20; 38 | break; 39 | case 18: 40 | $position['vat'] = 18; 41 | break; 42 | case 10: 43 | $position['vat'] = 10; 44 | break; 45 | default: 46 | $position['vat'] = 0; 47 | break; 48 | } 49 | 50 | // if($position['vat'] != 0){ 51 | // // dd($product, $position); 52 | // } 53 | 54 | return $position; 55 | } 56 | 57 | 58 | public static function update_product($product, $data_api) 59 | { 60 | if (!self::is_enable()) { 61 | return $product; 62 | } 63 | 64 | // $product = wc_get_product($product); 65 | 66 | if (!isset($data_api['effectiveVat'])) { 67 | return $product; 68 | } 69 | 70 | // dd(\WC_Tax::get_tax_class_slugs()); 71 | // dd($product->get_tax_class()); 72 | 73 | switch ($data_api['effectiveVat']) { 74 | case 20: 75 | $product->set_tax_class('20%'); 76 | break; 77 | case 18: 78 | $product->set_tax_class('18%'); 79 | break; 80 | case 10: 81 | $product->set_tax_class('10%'); 82 | break; 83 | default: 84 | $product->set_tax_class(''); 85 | break; 86 | } 87 | 88 | return $product; 89 | } 90 | 91 | 92 | /** 93 | * XXX - maybe it will be needed in the future 94 | */ 95 | public static function add_order_tax($data_order, $order_id) 96 | { 97 | 98 | if (!self::is_enable()) { 99 | return $data_order; 100 | } 101 | 102 | return $data_order; 103 | } 104 | 105 | public static function is_enable() 106 | { 107 | if (get_option('wooms_tax_support')) { 108 | return true; 109 | } 110 | 111 | return false; 112 | } 113 | 114 | public static function add_settings() 115 | { 116 | $section_key = 'wooms_section_orders'; 117 | $option_key = 'wooms_tax_support'; 118 | register_setting('mss-settings', $option_key); 119 | add_settings_field( 120 | $id = $option_key . '_input', 121 | $title = 'Включить работу с налогами', 122 | $callback = function ($args) { 123 | printf( 124 | '', 125 | $args['key'], 126 | checked(1, $args['value'], $echo = false) 127 | ); 128 | 129 | printf('%s
', 'Эксперементальная опция. Детали и обсуждение тут: https://github.com/wpcraft-ru/wooms/issues/173'); 130 | }, 131 | $page = 'mss-settings', 132 | $section_key, 133 | $args = [ 134 | 'key' => $option_key, 135 | 'value' => get_option($option_key), 136 | ] 137 | ); 138 | } 139 | } 140 | 141 | TaxSupport::init(); 142 | -------------------------------------------------------------------------------- /includes/Logger.php: -------------------------------------------------------------------------------- 1 | $source); 82 | $logger->error($data, $context); 83 | } 84 | 85 | /** 86 | * add log 87 | */ 88 | public static function add_log($type = 'wooms', $title = '', $description = '') 89 | { 90 | if (!self::is_enable()) { 91 | return; 92 | } 93 | 94 | $data = ''; 95 | 96 | $data .= strval($title); 97 | 98 | if (!empty($description)) { 99 | if (is_array($description)) { 100 | $description = json_encode($description, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 101 | } else { 102 | $description = wc_print_r($description, true); 103 | } 104 | 105 | $description = wp_trim_words($description, $num_words = 300, $more = null); 106 | $data .= ':' . PHP_EOL . $description; 107 | } 108 | 109 | $source = $type; 110 | $source = str_replace('\\', '-', $source); 111 | 112 | // $source = 'wooms'; 113 | // if( ! empty($type) ){ 114 | // $type = str_replace('\\', '-', $type); 115 | // $source .= '-' . $type; 116 | // } 117 | 118 | $logger = wc_get_logger(); 119 | $context = array('source' => $source); 120 | $logger->info($data, $context); 121 | } 122 | 123 | /** 124 | * render_settings_page 125 | */ 126 | public static function add_settings() 127 | { 128 | 129 | $option_name = 'wooms_logger_enable'; 130 | 131 | register_setting('mss-settings', $option_name); 132 | add_settings_field( 133 | $id = $option_name, 134 | $title = 'Логирование', 135 | $callback = function ($args) { 136 | printf( 137 | '', 138 | $args['key'], 139 | checked(1, $args['value'], false) 140 | ); 141 | printf('При включении, ошибки и ключевые изменения данных будут записываться в журнал WooCommerce
', admin_url('admin.php?page=wc-status&tab=logs')); 142 | }, 143 | $page = 'mss-settings', 144 | $section = 'woomss_section_other', 145 | $args = [ 146 | 'key' => $option_name, 147 | 'value' => get_option($option_name), 148 | ] 149 | ); 150 | } 151 | } 152 | 153 | Logger::init(); 154 | -------------------------------------------------------------------------------- /includes/VariationsHider.php: -------------------------------------------------------------------------------- 1 | 'product_variation', 103 | // 'post_parent' => $product_parent, 104 | 'numberposts' => 20, 105 | 'fields' => 'ids', 106 | 'offset' => $offset, 107 | 'meta_query' => array( 108 | array( 109 | 'key' => 'wooms_session_id', 110 | 'value' => self::get_session(), 111 | 'compare' => '!=', 112 | ), 113 | array( 114 | 'key' => 'wooms_id', 115 | 'compare' => 'EXISTS', 116 | ), 117 | ), 118 | ); 119 | 120 | $variations = get_posts( $args ); 121 | 122 | $i = 0; 123 | 124 | foreach ( $variations as $variations_id ) { 125 | $variation = wc_get_product( $variations_id ); 126 | $variation->set_stock_status( 'outofstock' ); 127 | $variation->save(); 128 | $i ++; 129 | do_action('wooms_logger', __CLASS__, sprintf('Скрытие вариации: %s', $variations_id) ); 130 | 131 | } 132 | 133 | set_transient( 'wooms_offset_hide_variations', $offset + $i ); 134 | 135 | if ( empty( $product_parent ) ) { 136 | delete_transient( 'wooms_offset_hide_variations' ); 137 | set_transient('wooms_variations_hiding_pause', 1, HOUR_IN_SECONDS); 138 | 139 | do_action('wooms_logger', __CLASS__, sprintf('Скрытие вариаций завершено: %s', date("Y-m-d H:i:s")) ); 140 | 141 | } 142 | } 143 | 144 | /** 145 | * Method for getting the value of an option 146 | */ 147 | public static function get_session() { 148 | $session_id = get_option( 'wooms_session_id' ); 149 | if ( empty( $session_id ) ) { 150 | return false; 151 | } 152 | 153 | return $session_id; 154 | } 155 | } 156 | 157 | VariationsHider::init(); 158 | -------------------------------------------------------------------------------- /tests/data/currency.json: -------------------------------------------------------------------------------- 1 | Array 2 | ( 3 | [context] => Array 4 | ( 5 | [employee] => Array 6 | ( 7 | [meta] => Array 8 | ( 9 | [href] => https://api.moysklad.ru/api/remap/1.2/context/employee 10 | [metadataHref] => https://api.moysklad.ru/api/remap/1.2/entity/employee/metadata 11 | [type] => employee 12 | [mediaType] => application/json 13 | ) 14 | 15 | ) 16 | 17 | ) 18 | 19 | [meta] => Array 20 | ( 21 | [href] => https://api.moysklad.ru/api/remap/1.2/entity/currency/ 22 | [type] => currency 23 | [mediaType] => application/json 24 | [size] => 2 25 | [limit] => 1000 26 | [offset] => 0 27 | ) 28 | 29 | [rows] => Array 30 | ( 31 | [0] => Array 32 | ( 33 | [meta] => Array 34 | ( 35 | [href] => https://api.moysklad.ru/api/remap/1.2/entity/currency/1f3ac651-9192-11e7-7a69-97110016a959 36 | [metadataHref] => https://api.moysklad.ru/api/remap/1.2/entity/currency/metadata 37 | [type] => currency 38 | [mediaType] => application/json 39 | [uuidHref] => https://online.moysklad.ru/app/#currency/edit?id=1f3ac651-9192-11e7-7a69-97110016a959 40 | ) 41 | 42 | [id] => 1f3ac651-9192-11e7-7a69-97110016a959 43 | [system] => 1 44 | [name] => руб 45 | [fullName] => Российский рубль 46 | [rate] => 1 47 | [multiplicity] => 1 48 | [indirect] => 49 | [rateUpdateType] => manual 50 | [code] => 643 51 | [isoCode] => RUB 52 | [majorUnit] => Array 53 | ( 54 | [gender] => masculine 55 | [s1] => рубль 56 | [s2] => рубля 57 | [s5] => рублей 58 | ) 59 | 60 | [minorUnit] => Array 61 | ( 62 | [gender] => feminine 63 | [s1] => копейка 64 | [s2] => копейки 65 | [s5] => копеек 66 | ) 67 | 68 | [archived] => 69 | [default] => 1 70 | ) 71 | 72 | [1] => Array 73 | ( 74 | [meta] => Array 75 | ( 76 | [href] => https://api.moysklad.ru/api/remap/1.2/entity/currency/c2404754-ed3c-11e9-0a80-043d00056ac5 77 | [metadataHref] => https://api.moysklad.ru/api/remap/1.2/entity/currency/metadata 78 | [type] => currency 79 | [mediaType] => application/json 80 | [uuidHref] => https://online.moysklad.ru/app/#currency/edit?id=c2404754-ed3c-11e9-0a80-043d00056ac5 81 | ) 82 | 83 | [id] => c2404754-ed3c-11e9-0a80-043d00056ac5 84 | [system] => 1 85 | [name] => доллар 86 | [fullName] => Доллар США 87 | [rate] => 93.2174 88 | [multiplicity] => 1 89 | [indirect] => 90 | [rateUpdateType] => auto 91 | [code] => 840 92 | [isoCode] => USD 93 | [majorUnit] => Array 94 | ( 95 | [gender] => masculine 96 | [s1] => доллар 97 | [s2] => доллара 98 | [s5] => долларов 99 | ) 100 | 101 | [minorUnit] => Array 102 | ( 103 | [gender] => masculine 104 | [s1] => цент 105 | [s2] => цента 106 | [s5] => центов 107 | ) 108 | 109 | [archived] => 110 | [default] => 111 | ) 112 | 113 | ) 114 | 115 | ) 116 | 117 | -------------------------------------------------------------------------------- /includes/SiteHealth.php: -------------------------------------------------------------------------------- 1 | 'Тариф МойСклад', 33 | 'value' => sprintf('Для корректной работы плагина нужно сменить тариф %s', '❌'), 34 | ]; 35 | 36 | 37 | return $debug_info; 38 | } 39 | 40 | /** 41 | * adding hooks for site health 42 | * 43 | * @param [type] $tests 44 | * @return void 45 | */ 46 | function health_tests($tests) 47 | { 48 | 49 | $tests['direct']['wooms_check_woocommerce_version_for_wooms'] = [ 50 | 'test' => __NAMESPACE__ . '\\' . 'wooms_check_woocommerce_version_for_wooms', 51 | ]; 52 | 53 | 54 | $tests['async']['wooms_check_credentials'] = [ 55 | 'test' => __NAMESPACE__ . '\\' . 'wooms_check_login_password', 56 | ]; 57 | 58 | return $tests; 59 | } 60 | 61 | /** 62 | * Checking version WooCommerce 63 | * 64 | * @return void 65 | */ 66 | function wooms_check_woocommerce_version_for_wooms() 67 | { 68 | 69 | $wc_version = WC()->version; 70 | $result = [ 71 | 'label' => 'Проверка версии WooCommerce для работы плагина WooMS & WooMS XT', 72 | 'status' => 'good', 73 | 'badge' => [ 74 | 'label' => 'Уведомление WooMS', 75 | 'color' => 'blue', 76 | ], 77 | 'description' => sprintf('Все хорошо! Спасибо что выбрали наш плагин %s', '🙂'), 78 | 'test' => 'wooms_check_woocommerce_version_for_wooms' // this is only for class in html block 79 | ]; 80 | 81 | if (version_compare($wc_version, '3.6.0', '<=')) { 82 | $result['status'] = 'critical'; 83 | $result['badge']['color'] = 'red'; 84 | $result['actions'] = sprintf( 85 | '', 86 | admin_url('plugins.php'), 87 | sprintf("Обновить WooCommerce") 88 | ); 89 | $result['description'] = sprintf('Ваша версия WooCommerce плагина %s. Обновите пожалуйста WooCommerce чтобы WooMS & WooMS XT работали ', $wc_version); 90 | } 91 | 92 | return $result; 93 | } 94 | 95 | 96 | /** 97 | * checking credentials 98 | * 99 | * @return void 100 | */ 101 | function wooms_check_login_password() 102 | { 103 | check_ajax_referer('health-check-site-status'); 104 | 105 | if (!current_user_can('view_site_health_checks')) { 106 | wp_send_json_error(); 107 | } 108 | 109 | $url = 'security/token'; 110 | $data_api = request($url, [], 'POST'); 111 | 112 | $result = [ 113 | 'label' => "Проверка логина и пароля МойСклад", 114 | 'status' => 'good', 115 | 'badge' => [ 116 | 'label' => 'Уведомление WooMS', 117 | 'color' => 'blue', 118 | ], 119 | 'description' => sprintf("Все хорошо! Спасибо что используете наш плагин %s", '🙂'), 120 | 'test' => 'wooms_check_credentials' // this is only for class in html block 121 | ]; 122 | 123 | if (!array_key_exists('errors', $data_api)) { 124 | wp_send_json_success($result); 125 | } 126 | 127 | if (array_key_exists('errors', $data_api)) { 128 | $result['status'] = 'critical'; 129 | $result['badge']['color'] = 'red'; 130 | $result['description'] = sprintf("Что то пошло не так при подключении к МойСклад", '🤔'); 131 | } 132 | 133 | /** 134 | * 1056 is mean that login or the password is not correct 135 | */ 136 | if ($data_api["errors"][0]['code'] === 1056) { 137 | $result['description'] = sprintf("Неверный логин или пароль от МойСклад %s", '🤔'); 138 | $result['actions'] = sprintf( 139 | '', 140 | 'admin.php?page=mss-settings', 141 | sprintf("Поменять доступы") 142 | ); 143 | } 144 | 145 | set_transient('wooms_check_login_password', true, 60 * 30); 146 | 147 | wp_send_json_success($result); 148 | } 149 | 150 | -------------------------------------------------------------------------------- /includes/ProductsHiding.php: -------------------------------------------------------------------------------- 1 | set_status('draft'); 48 | 49 | $product->set_catalog_visibility('hidden'); 50 | $product->save(); 51 | 52 | Helper::log(sprintf('Скрытие продукта: %s', $product_id), __NAMESPACE__); 53 | 54 | } 55 | 56 | $state['ids'] = $ids; 57 | if (empty($state['count'])) { 58 | $state['count'] = count($products); 59 | } else { 60 | $state['count'] += count($products); 61 | } 62 | 63 | as_schedule_single_action(time(), HOOK_NAME, [$state], 'WooMS'); 64 | 65 | do_action('wooms_hide_old_product', $products); 66 | 67 | return true; 68 | } 69 | 70 | function get_session() 71 | { 72 | return \WooMS\Products\get_state('session_id'); 73 | } 74 | 75 | function add_task_for_hide() 76 | { 77 | if (is_disable()) { 78 | return; 79 | } 80 | 81 | as_schedule_single_action(time(), HOOK_NAME, [], 'WooMS'); 82 | } 83 | 84 | function remove_task_for_hide() 85 | { 86 | as_unschedule_all_actions(HOOK_NAME); 87 | } 88 | 89 | /** 90 | * Obtaining products with specific attributes 91 | * 92 | * @param int $offset 93 | */ 94 | function get_products_old_session() 95 | { 96 | $session = get_session(); 97 | if (empty($session)) { 98 | return false; 99 | } 100 | 101 | 102 | $args = array( 103 | 'post_type' => ['product'], 104 | 'post_status' => 'publish', 105 | 'numberposts' => 30, 106 | 'fields' => 'ids', 107 | 108 | 'tax_query' => array( 109 | array( 110 | 'taxonomy' => 'product_visibility', 111 | 'terms' => array('exclude-from-catalog', 'exclude-from-search'), 112 | 'field' => 'name', 113 | 'operator' => 'NOT IN', 114 | ), 115 | ), 116 | 'meta_query' => array( 117 | array( 118 | 'key' => 'wooms_session_id', 119 | 'value' => $session, 120 | 'compare' => '!=', 121 | ), 122 | array( 123 | 'key' => 'wooms_id', 124 | 'compare' => 'EXISTS', 125 | ), 126 | ), 127 | 128 | ); 129 | 130 | return get_posts($args); 131 | } 132 | 133 | /** 134 | * проверяем надо ли скрывать продукты 135 | */ 136 | function is_disable() 137 | { 138 | if (get_option('wooms_product_hiding_disable')) { 139 | return true; 140 | } 141 | 142 | return false; 143 | } 144 | 145 | 146 | function display_state() 147 | { 148 | 149 | $strings = []; 150 | 151 | if (is_disable()) { 152 | $strings[] = 'Обработчик скрытия продуктов отключен в настройках'; 153 | } 154 | 155 | if (as_next_scheduled_action(HOOK_NAME)) { 156 | $strings[] = 'Статус: Продукты скрываются в фоне очередями'; 157 | 158 | } else { 159 | $strings[] = sprintf('Очередь последний раз завершилась: %s', wooms_get_timestamp_last_job_by_hook(HOOK_NAME)); 160 | } 161 | 162 | $strings[] = sprintf('Очередь задач: открыть', admin_url('admin.php?page=wc-status&tab=action-scheduler&s=wooms_schedule_clear_old_products_walker&orderby=schedule&order=desc')); 163 | 164 | $strings[] = sprintf( 'Журнал обработки: открыть', admin_url( 'admin.php?page=wc-status&tab=logs&source=WooMS-ProductsHider' ) ); 165 | 166 | echo '%s
', $string); 169 | } 170 | } 171 | 172 | function add_settings() 173 | { 174 | $option_name = 'wooms_product_hiding_disable'; 175 | register_setting('mss-settings', $option_name); 176 | add_settings_field( 177 | $id = $option_name, 178 | $title = 'Отключить скрытие продуктов', 179 | $callback = function ($args) { 180 | printf('', $args['name'], checked(1, $args['value'], false)); 181 | printf('%s
', 'Если включить опцию, то обработчик скрытия продуктов из каталога будет отключен. Иногда это бывает полезно.'); 182 | }, 183 | $page = 'mss-settings', 184 | $section = 'woomss_section_other', 185 | $args = [ 186 | 'name' => $option_name, 187 | 'value' => get_option($option_name), 188 | ] 189 | ); 190 | } 191 | -------------------------------------------------------------------------------- /includes/ProductVariableImage.php: -------------------------------------------------------------------------------- 1 | 'product_variation', 44 | 'numberposts' => 5, 45 | 'meta_query' => array( 46 | array( 47 | 'key' => self::$image_meta_key, 48 | 'compare' => 'EXISTS', 49 | ), 50 | ), 51 | )); 52 | 53 | if (empty($variants)) { 54 | set_transient('wooms_variations_image_sync_finish_timestamp', time(), HOUR_IN_SECONDS); 55 | } 56 | 57 | foreach ($variants as $variant) { 58 | self::download_img_for_product($variant->ID); 59 | } 60 | } 61 | 62 | 63 | /** 64 | * download_img_for_product 65 | */ 66 | public static function download_img_for_product($variation_id) 67 | { 68 | 69 | $img_meta = get_post_meta($variation_id, self::$image_meta_key, true); 70 | $img_meta = json_decode($img_meta, true); 71 | 72 | 73 | if (empty($img_meta['meta']['downloadHref'])) { 74 | return false; 75 | } 76 | 77 | $url_download = $img_meta['meta']['downloadHref']; 78 | 79 | $image_name = $img_meta['filename']; 80 | 81 | $check_id = self::uploadRemoteImageAndAttach($url_download, $variation_id, $image_name); 82 | 83 | $variation = wc_get_product($variation_id); 84 | $variation->set_image_id($check_id); 85 | $variation->delete_meta_data(self::$image_meta_key); 86 | $variation->save(); 87 | } 88 | 89 | 90 | /** 91 | * add_image_task 92 | * 93 | * use hook $variation = apply_filters('wooms_variation_save', $variation, $variant_data, $product_id); 94 | */ 95 | public static function add_image_task($variation, $variant_data, $product_id) 96 | { 97 | 98 | if (empty($variant_data['images']['meta']['href'])) { 99 | return $variation; 100 | } 101 | 102 | $href = $variant_data['images']['meta']['href']; 103 | $img_metadata = request($href); 104 | 105 | if (empty($img_metadata['rows'][0])) { 106 | return $variation; 107 | } 108 | 109 | $img_metadata = $img_metadata['rows'][0]; 110 | $img_metadata = json_encode($img_metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); 111 | 112 | $variation->update_meta_data(self::$image_meta_key, $img_metadata); 113 | 114 | return $variation; 115 | } 116 | 117 | 118 | /** 119 | * Cron task restart 120 | */ 121 | public static function add_schedule_hook() 122 | { 123 | 124 | if (self::is_wait()) { 125 | return; 126 | } 127 | 128 | if (as_next_scheduled_action('wooms_variaion_image_sync')) { 129 | return; 130 | } 131 | 132 | // Adding schedule hook 133 | as_schedule_single_action( 134 | time() + 60, 135 | 'wooms_variaion_image_sync', 136 | [], 137 | 'WooMS' 138 | ); 139 | } 140 | 141 | 142 | /** 143 | * check need walker start or not 144 | */ 145 | public static function is_wait() 146 | { 147 | if (get_transient('wooms_variations_image_sync_finish_timestamp')) { 148 | return true; 149 | } 150 | 151 | return false; 152 | } 153 | 154 | 155 | /** 156 | * get_state 157 | */ 158 | public static function get_state($key = '') 159 | { 160 | $state = get_transient(self::$state_key); 161 | if (empty($key)) { 162 | return $state; 163 | } 164 | 165 | if (isset($state[$key])) { 166 | return $state[$key]; 167 | } 168 | 169 | return null; 170 | } 171 | 172 | 173 | /** 174 | * set_state 175 | */ 176 | public static function set_state($key = '', $value = '') 177 | { 178 | 179 | $state = get_transient(self::$state_key); 180 | 181 | if (is_array($state)) { 182 | $state[$key] = $value; 183 | } else { 184 | $state = [ 185 | $key => $value 186 | ]; 187 | } 188 | 189 | set_transient(self::$state_key, $state); 190 | 191 | return $state; 192 | } 193 | } 194 | 195 | ProductVariableImage::init(); 196 | -------------------------------------------------------------------------------- /includes/CurrencyConverter.php: -------------------------------------------------------------------------------- 1 | $price, 47 | 'цена после конвертации' => $price_by_rate, 48 | ] 49 | ); 50 | 51 | return $price_by_rate; 52 | } 53 | 54 | public static function get_price_converted($price_meta, $currency_ms){ 55 | 56 | $woocommerce_currency = get_woocommerce_currency(); 57 | 58 | $rate = 1; 59 | 60 | foreach($currency_ms['rows'] as $currency_row){ 61 | if($currency_row['meta']['href'] == $price_meta['currency']['meta']['href']){ 62 | $rate = $currency_row['rate']; 63 | 64 | } 65 | } 66 | 67 | $price = $price_meta['value'] * $rate; 68 | return $price; 69 | 70 | } 71 | 72 | public static function get_currency(){ 73 | $currency = get_transient('wooms_currency_api'); 74 | if($currency){ 75 | return $currency; 76 | } 77 | 78 | $currency = request('entity/currency/'); 79 | set_transient('wooms_currency_api', $currency, DAY_IN_SECONDS); 80 | return $currency; 81 | } 82 | 83 | public static function cache_data(){ 84 | delete_transient('wooms_currency_api'); 85 | $currency = request('entity/currency/'); 86 | set_transient('wooms_currency_api', $currency, DAY_IN_SECONDS); 87 | } 88 | 89 | 90 | public static function update_price_by_rate($price = 0, $api_currency = 'RUB'){ 91 | 92 | $currency = self::get_currency(); 93 | 94 | $rate = 1; 95 | 96 | foreach($currency['rows'] as $currency_row){ 97 | if($currency_row['isoCode'] == $api_currency){ 98 | $rate = $currency_row['rate']; 99 | } 100 | } 101 | 102 | $price = $price * $rate; 103 | 104 | return $price; 105 | } 106 | 107 | 108 | public static function get_currency_code_price_meta($price_meta = []) 109 | { 110 | if (empty($price_meta['currency']['meta']['href'])) { 111 | return false; 112 | } 113 | 114 | $price_currency_href = $price_meta['currency']['meta']['href']; 115 | 116 | $currency = self::get_currency(); 117 | 118 | if (empty($currency['rows'])) { 119 | return false; 120 | } 121 | 122 | $currency_code = 'RUB'; 123 | foreach ($currency['rows'] as $currency_item) { 124 | if ($price_currency_href == $currency_item['meta']['href']) { 125 | $currency_code = $currency_item['isoCode']; 126 | break; 127 | } 128 | } 129 | 130 | return $currency_code; 131 | } 132 | 133 | 134 | public static function is_enable() 135 | { 136 | if (get_option(self::OPTION_KEY)) { 137 | return true; 138 | } 139 | 140 | return false; 141 | } 142 | 143 | /** 144 | * Setting 145 | */ 146 | public static function add_settings() 147 | { 148 | $option_key = self::OPTION_KEY; 149 | 150 | register_setting('mss-settings', $option_key); 151 | add_settings_field( 152 | $id = $option_key, 153 | $title = 'Автоконвертация валюты', 154 | $callback = function ($args) { 155 | printf( 156 | '', 157 | $args['key'], 158 | checked(1, $args['value'], $echo = false) 159 | ); 160 | printf('%s
', 'Если включена опция, то плагин будет конвертировать валюту из МойСклад в валюту указанную в настройках WooCommerce'); 161 | printf('%s
', 'Подробнее: https://github.com/wpcraft-ru/wooms/issues/277'); 162 | }, 163 | $page = 'mss-settings', 164 | $section = 'woomss_section_other', 165 | $args = [ 166 | 'key' => $option_key, 167 | 'value' => get_option($option_key), 168 | ] 169 | ); 170 | } 171 | } 172 | 173 | CurrencyConverter::init(); 174 | -------------------------------------------------------------------------------- /includes/OrderNumber.php: -------------------------------------------------------------------------------- 1 | update_meta_data('_order_number', $data_api['name']); 38 | } 39 | 40 | return $order; 41 | } 42 | 43 | 44 | public static function get_order_number($order_number, $order) 45 | { 46 | if (!self::is_enable()) { 47 | return $order_number; 48 | } 49 | 50 | if ($ms_order_number = $order->get_meta('_order_number', true, 'edit')) { 51 | $order_number = $ms_order_number; 52 | } 53 | 54 | return $order_number; 55 | } 56 | 57 | /** 58 | * 59 | * use hook $data = apply_filters('wooms_order_data', $data, $order_id); 60 | */ 61 | public static function disable_order_number($data, $order_id) 62 | { 63 | if (!self::is_enable()) { 64 | return $data; 65 | } 66 | 67 | unset($data['name']); 68 | // dd($data); 69 | 70 | return $data; 71 | } 72 | 73 | 74 | public static function is_enable() 75 | { 76 | if (get_option('wooms_order_number_from_moysklad')) { 77 | return true; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | /** 84 | * Settings UI 85 | */ 86 | public static function add_settings() 87 | { 88 | self::setting_wooms_order_number_from_moysklad(); 89 | self::setting_wooms_get_number_async_enable(); 90 | } 91 | 92 | public static function setting_wooms_get_number_async_enable() 93 | { 94 | 95 | if (!get_option('wooms_order_number_from_moysklad')) { 96 | return; 97 | } 98 | 99 | $option_name = 'wooms_get_number_async_enable'; 100 | register_setting('mss-settings', $option_name); 101 | add_settings_field( 102 | $id = $option_name, 103 | $title = 'Включить асинхронный механизм получения номера', 104 | $callback = function ($args) { 105 | printf('', $args['key'], checked(1, $args['value'], false)); 106 | printf( 107 | '%s
', 108 | 'Может быть полезно включить если тормозит API МойСклад и оформление Заказов в магазине ломается' 109 | ); 110 | }, 111 | $page = 'mss-settings', 112 | $section = 'wooms_section_orders', 113 | $args = [ 114 | 'key' => $option_name, 115 | 'value' => get_option($option_name) 116 | ] 117 | ); 118 | } 119 | 120 | public static function setting_wooms_order_number_from_moysklad() 121 | { 122 | $option_name = 'wooms_order_number_from_moysklad'; 123 | register_setting('mss-settings', $option_name); 124 | add_settings_field( 125 | $id = $option_name, 126 | $title = 'Номер Заказа брать из МойСклад', 127 | $callback = function ($args) { 128 | printf('', $args['key'], checked(1, $args['value'], false)); 129 | printf( 130 | '%s
', 131 | 'Включите эту опцию, чтобы номер Заказа брался из МойСклад' 132 | ); 133 | printf( 134 | '%s
', 135 | 'Подробнее: https://github.com/wpcraft-ru/wooms/issues/319' 136 | ); 137 | }, 138 | $page = 'mss-settings', 139 | $section = 'wooms_section_orders', 140 | $args = [ 141 | 'key' => $option_name, 142 | 'value' => get_option($option_name) 143 | ] 144 | ); 145 | } 146 | 147 | /** 148 | * Если используются номера заказов из Мой Склад, чиним поиску по номеру заказа 149 | * 150 | * issue https://github.com/wpcraft-ru/wooms/issues/331 151 | */ 152 | public static function search_by_number_from_moysklad($query) 153 | { 154 | if (!self::is_enable()){ 155 | return; 156 | } 157 | if(!is_admin()){ 158 | return; 159 | } 160 | if(!isset($query->query)){ 161 | return; 162 | } 163 | if(!isset($query->query['s'])){ 164 | return; 165 | } 166 | if(is_numeric($query->query['s']) === false){ 167 | return; 168 | } 169 | 170 | $custom_order_id = $query->query['s']; 171 | $query->query_vars['post__in']=array(); 172 | $query->query['s']=''; 173 | $query->set('meta_key','_order_number'); 174 | $query->set('meta_value',$custom_order_id); 175 | } 176 | 177 | } 178 | 179 | OrderNumber::init(); 180 | -------------------------------------------------------------------------------- /tests/includes/ProductVariable.php: -------------------------------------------------------------------------------- 1 | Управление'; 62 | $settings_link = 'Настройки'; 63 | array_unshift( $links, $mng_link ); 64 | array_unshift( $links, $settings_link ); 65 | return $links; 66 | } ); 67 | 68 | 69 | /** 70 | * сообщяем про то что Extra плагин более не актуален 71 | */ 72 | add_action( 'after_plugin_row_wooms-extra/wooms-extra.php', function ($data, $response) { 73 | 74 | $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' ); 75 | 76 | printf( 77 | '%s
', 'Вариации ждут очереди на обновление'); 289 | } 290 | 291 | printf( 292 | '', 293 | admin_url('admin.php?page=wc-status&tab=action-scheduler&s=wooms_product_single_update_schedule&orderby=schedule&order=desc'), 294 | 'Открыть очередь задач' 295 | ); 296 | } 297 | } 298 | 299 | ProductSingleSync::init(); 300 | -------------------------------------------------------------------------------- /includes/Settings.php: -------------------------------------------------------------------------------- 1 | sprintf( '%s', 'https://github.com/wpcraft-ru/wooms/', 'Разработка и решение проблем' ), 79 | 'docs' => sprintf( '%s', 'https://github.com/wpcraft-ru/wooms/wiki', 'Документация' ), 80 | 81 | 'ms' => sprintf( '%s', 'https://online.moysklad.ru/', 'Вход в МойСклад' ), 82 | ]; 83 | 84 | $nav_items = apply_filters( 'wooms_settings_nav_items', $nav_items ); 85 | 86 | ?> 87 | 93 | ', get_option( 'woomss_pass' ) ); 133 | } 134 | 135 | /** 136 | * display_form_login 137 | */ 138 | public static function display_form_login() { 139 | printf( '', get_option( 'woomss_login' ) ); 140 | 141 | printf( '%s
', 'Вводить нужно только логин и пароль здесь. На стороне МойСклад ничего настраивать не нужно.' ); 142 | } 143 | 144 | /** 145 | * Settings - Other 146 | */ 147 | public static function settings_other() { 148 | add_settings_section( 'woomss_section_other', 'Прочие настройки', null, 'mss-settings' ); 149 | 150 | register_setting( 'mss-settings', 'wooms_use_uuid' ); 151 | add_settings_field( 152 | $id = 'wooms_use_uuid', 153 | $title = 'Использование UUID', 154 | $callback = array( __CLASS__, 'display_field_wooms_use_uuid' ), 155 | $page = 'mss-settings', 156 | $section = 'woomss_section_other' 157 | ); 158 | 159 | register_setting( 'mss-settings', 'wooms_replace_title' ); 160 | add_settings_field( 161 | $id = 'wooms_replace_title', 162 | $title = 'Замена заголовка при обновлении', 163 | $callback = array( __CLASS__, 'display_wooms_replace_title' ), 164 | $page = 'mss-settings', 165 | $section = 'woomss_section_other' 166 | ); 167 | 168 | register_setting( 'mss-settings', 'wooms_replace_description' ); 169 | add_settings_field( 170 | $id = 'wooms_replace_description', 171 | $title = 'Замена описания при обновлении', 172 | $callback = array( __CLASS__, 'display_wooms_replace_desc' ), 173 | $page = 'mss-settings', 174 | $section = 'woomss_section_other' 175 | ); 176 | } 177 | 178 | /** 179 | * display_wooms_replace_desc 180 | */ 181 | public static function display_wooms_replace_desc() { 182 | 183 | $option_name = 'wooms_replace_description'; 184 | printf( '', $option_name, checked( 1, get_option( $option_name ), false ) ); 185 | ?> 186 |187 | Если включить опцию, то плагин будет обновлять описание продукта из МойСклад всегда. 188 |
189 | ', $option_name, checked( 1, get_option( $option_name ), false ) ); 200 | ?> 201 |202 | Если включить опцию, то плагин будет обновлять заголовки продукта из МойСклад. Иначе при наличии заголовока 203 | он не будет обновлен. 204 |
205 | ', $option_name, checked( 1, get_option( $option_name ), false ) ); 216 | ?> 217 | 218 |Если товары не попадают из МойСклад на сайт - попробуйте включить эту опцию.
219 |
220 | По умолчанию используется связь продуктов по артикулу. Это позволяет обеспечить синхронизацию без удаления
221 | всех продуктов с сайта при их наличии. Но без артикула
222 | товары не будут синхронизироваться. Если товаров на сайте нет, либо их можно удалить без вреда, то можно
223 | включить синхронизацию по UUID. В этом случае артикулы
224 | будут не нужны.
При создании записи о продукте произойдет связка по UUID (meta_key = wooms_id)
225 |
226 |
%s
', __('Для связи статусов, нужно включить опцию передачи статусов из Сайта на Склад', 'wooms')); 158 | return; 159 | } 160 | 161 | $option_key = 'wooms_order_statuses_match'; 162 | 163 | $statuses = wc_get_order_statuses(); 164 | if (empty($statuses) or !is_array($statuses)) { 165 | printf( 166 | '%s
', 167 | __('Что то пошло не так, сообщите о проблеме в тех поддержку', 'wooms') 168 | ); 169 | return; 170 | } 171 | 172 | printf( 173 | '%s
', 174 | __('Нужно написать какие статусы указывать в МойСклад, при смене статуса Заказов на Сайте, названия должны совпадать со статусами в МойСклад.', 'wooms') 175 | ); 176 | 177 | $option_value = get_option($option_key); 178 | if (empty($option_value)) { 179 | $option_value = array(); 180 | } 181 | 182 | foreach ($statuses as $status_key => $status_name) { 183 | 184 | if (empty($option_value[$status_key])) { 185 | switch ($status_key) { 186 | case 'wc-pending': 187 | $option_value[$status_key] = 'Новый'; 188 | break; 189 | 190 | case 'wc-processing': 191 | $option_value[$status_key] = 'Подтвержден'; 192 | break; 193 | 194 | case 'wc-on-hold': 195 | $option_value[$status_key] = 'Новый'; 196 | break; 197 | 198 | case 'wc-completed': 199 | $option_value[$status_key] = 'Отгружен'; 200 | break; 201 | 202 | case 'wc-cancelled': 203 | $option_value[$status_key] = 'Отменен'; 204 | break; 205 | 206 | case 'wc-refunded': 207 | $option_value[$status_key] = 'Возврат'; 208 | break; 209 | 210 | case 'wc-failed': 211 | $option_value[$status_key] = 'Не удался'; 212 | break; 213 | 214 | default: 215 | $option_value[$status_key] = 'Новый'; 216 | break; 217 | } 218 | } 219 | 220 | 221 | printf( 222 | '%s (%s) >
', 223 | $status_name, 224 | $status_key, 225 | $option_key, 226 | $status_key, 227 | $option_value[$status_key] 228 | ); 229 | } 230 | } 231 | 232 | /** 233 | * Send statuses to MoySklad 234 | */ 235 | public static function display_enable_orders_statuses_updater() 236 | { 237 | $option = 'wooms_enable_orders_statuses_updater'; 238 | printf('', $option, checked(1, get_option($option), false)); 239 | ?> 240 |Передатчик статусов с Сайта в МойСклад при активации будет выполняться 1 раз в минуту. Можно менять механизм с помощью программистов через хуки WP.
241 | 242 | get_id(); 51 | 52 | 53 | if (!empty($item['weight'])) { 54 | $product->set_weight($item['weight']); 55 | } 56 | 57 | if (!empty($item['attributes'])) { 58 | foreach ($item['attributes'] as $attribute) { 59 | if (empty($attribute['name'])) { 60 | continue; 61 | } 62 | 63 | if ($attribute['name'] == 'Ширина') { 64 | $product->set_width($attribute['value']); 65 | continue; 66 | } 67 | 68 | if ($attribute['name'] == 'Высота') { 69 | $product->set_height($attribute['value']); 70 | continue; 71 | } 72 | 73 | if ($attribute['name'] == 'Длина') { 74 | $product->set_length($attribute['value']); 75 | continue; 76 | } 77 | } 78 | } 79 | 80 | 81 | $product_attributes = $product->get_attributes('edit'); 82 | 83 | if (empty($product_attributes)) { 84 | $product_attributes = array(); 85 | } 86 | 87 | $product_attributes = apply_filters('wooms_attributes', $product_attributes, $product_id, $item); 88 | 89 | do_action( 'wooms_logger', __CLASS__, 90 | sprintf('Артибуты Продукта: %s (%s) сохранены', $product->get_title(), $product->get_id()), 91 | $product_attributes 92 | ); 93 | 94 | $product->set_attributes($product_attributes); 95 | 96 | return $product; 97 | } 98 | 99 | /** 100 | * Get attribute id by label 101 | * or false 102 | */ 103 | public static function get_attribute_id_by_label($label = '') 104 | { 105 | if (empty($label)) { 106 | return false; 107 | } 108 | 109 | $attr_taxonomies = wc_get_attribute_taxonomies(); 110 | if (empty($attr_taxonomies)) { 111 | return false; 112 | } 113 | 114 | if (!is_array($attr_taxonomies)) { 115 | return false; 116 | } 117 | 118 | foreach ($attr_taxonomies as $attr) { 119 | if ($attr->attribute_label == $label) { 120 | return (int) $attr->attribute_id; 121 | } 122 | } 123 | 124 | return false; 125 | } 126 | 127 | /** 128 | * Сохраняем прочие атрибуты, не попавшивае под базовые условия 129 | */ 130 | public static function save_other_attributes($product_attributes, $product_id, $value) 131 | { 132 | if( ! empty($value['attributes']) ){ 133 | foreach ($value['attributes'] as $attribute) { 134 | if(empty($attribute['name'])){ 135 | continue; 136 | } 137 | 138 | if(in_array($attribute['name'], array('Ширина', 'Высота', 'Длина', 'Страна') )){ 139 | continue; 140 | } 141 | 142 | //Если это не число и не строка - пропуск, тк другие типы надо обрабатывать иначе 143 | $allow_data_type_for_attribures = array('string', 'number', 'customentity'); 144 | 145 | /** 146 | * add new type for attributes 147 | * 148 | * @issue https://github.com/wpcraft-ru/wooms/issues/184 149 | */ 150 | $allow_data_type_for_attribures = apply_filters('wooms_allow_data_types_for_attributes', $allow_data_type_for_attribures); 151 | if( ! in_array($attribute['type'], $allow_data_type_for_attribures) ){ 152 | continue; 153 | } 154 | 155 | if( ! empty($attribute['value']['name'])){ 156 | $value = $attribute['value']['name']; 157 | } else { 158 | $value = $attribute['value']; 159 | } 160 | 161 | $attribute_name = $attribute['name']; 162 | 163 | $attribute_taxonomy_id = self::get_attribute_id_by_label($attribute_name); 164 | if($attribute_taxonomy_id){ 165 | $taxonomy_slug = wc_attribute_taxonomy_name_by_id($attribute_taxonomy_id); 166 | } 167 | 168 | $attribute_slug = sanitize_title( $attribute_name ); 169 | 170 | if(empty($attribute_taxonomy_id)){ 171 | 172 | $attribute_object = new \WC_Product_Attribute(); 173 | $attribute_object->set_name( $attribute_name ); 174 | $attribute_object->set_options( array($value) ); 175 | $attribute_object->set_position( 0 ); 176 | $attribute_object->set_visible( 1 ); 177 | $product_attributes[$attribute_slug] = $attribute_object; 178 | 179 | } else { 180 | 181 | //Очищаем индивидуальный атрибут с таким именем если есть 182 | if(isset($product_attributes[$attribute_slug])){ 183 | unset($product_attributes[$attribute_slug]); 184 | } 185 | 186 | $attribute_object = new \WC_Product_Attribute(); 187 | $attribute_object->set_id( $attribute_taxonomy_id ); 188 | $attribute_object->set_name( $taxonomy_slug ); 189 | $attribute_object->set_options( array($value) ); 190 | $attribute_object->set_position( 0 ); 191 | $attribute_object->set_visible( 1 ); 192 | $product_attributes[$taxonomy_slug] = $attribute_object; 193 | } 194 | 195 | } 196 | } 197 | return $product_attributes; 198 | } 199 | 200 | 201 | /** 202 | * Country - update 203 | */ 204 | public static function update_country($product_attributes, $product_id, $value) 205 | { 206 | if( empty($value['country']["meta"]["href"]) ) { 207 | return $product_attributes; 208 | } else { 209 | $url = $value['country']["meta"]["href"]; 210 | } 211 | 212 | $data_api = request($url); 213 | 214 | if(empty($data_api["name"])){ 215 | return $product_attributes; 216 | } else { 217 | $country = sanitize_text_field($data_api["name"]); 218 | 219 | $attribute_object = new \WC_Product_Attribute(); 220 | $attribute_object->set_name( "Страна" ); 221 | $attribute_object->set_options( array($country) ); 222 | $attribute_object->set_position( '0' ); 223 | $attribute_object->set_visible( 1 ); 224 | $attribute_object->set_variation( 0 ); 225 | $product_attributes[] = $attribute_object; 226 | } 227 | 228 | return $product_attributes; 229 | } 230 | 231 | 232 | /** 233 | * Settings UI 234 | */ 235 | public static function add_settings() 236 | { 237 | $option_name = 'wooms_attr_enabled'; 238 | register_setting('mss-settings', $option_name); 239 | add_settings_field( 240 | $id = $option_name, 241 | $title = 'Включить синхронизацию доп. полей как атрибутов', 242 | $callback = function($args){ 243 | printf('', $args['name'], checked( 1, $args['value'], false )); 244 | printf('%s
', 'Позволчет синхронизировать доп поля МойСклад как атрибуты продукта. Вес, ДВШ - сохраняются в базовые поля продукта, остальные поля как индивидуальные атрибуты.'); 245 | printf('%s
', 'Тестовый режим. Не включайте эту функцию на реальном сайте, пока не проверите ее на тестовой копии сайта.'); 246 | }, 247 | $page = 'mss-settings', 248 | $section = 'woomss_section_other', 249 | $args = [ 250 | 'name' => $option_name, 251 | 'value' => get_option($option_name), 252 | ] 253 | ); 254 | } 255 | } 256 | 257 | ProductAttributes::init(); 258 | -------------------------------------------------------------------------------- /includes/ProductGallery.php: -------------------------------------------------------------------------------- 1 | Статус: %s', 'галлереи продуктов загружаются очередями в фоне'); 75 | } else { 76 | $strings[] = sprintf('Статус: %s', 'в ожидании новых задач'); 77 | } 78 | 79 | $strings[] = sprintf('Очередь задач: открыть', admin_url('admin.php?page=wc-status&tab=action-scheduler&s=gallery_images_download_schedule&orderby=schedule&order=desc')); 80 | 81 | if (defined('WC_LOG_HANDLER') && 'WC_Log_Handler_DB' == WC_LOG_HANDLER) { 82 | $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs&source=WooMS-ProductGallery')); 83 | } else { 84 | $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs')); 85 | } 86 | 87 | ?> 88 |Внимание! Для корректной перезаписи изображений, необходимо провести повторную синхронизацию товаров. Если синхронизация товаров происходит по крону, то дождаться окончания очередной сессии синхронизации товаров
'; 314 | printf(' %s', $option, checked(1, get_option($option), false), $desc); 315 | } 316 | 317 | /** 318 | * setting_images_sync_enabled 319 | */ 320 | public static function setting_images_sync_enabled() 321 | { 322 | $option = 'woomss_gallery_replace_to_sync'; 323 | $desc = 'Внимание! Это тест опция! Если включить опцию, то плагин будет загружать изображения галереи из МойСклад.'; 324 | printf(' %s', $option, checked(1, get_option($option), false), $desc); 325 | } 326 | } 327 | 328 | ProductGallery::init(); 329 | -------------------------------------------------------------------------------- /includes/ProductsServices.php: -------------------------------------------------------------------------------- 1 | 0, 73 | 'limit' => 30 74 | ]; 75 | 76 | self::set_state('query_arg', $query_arg_default); 77 | } 78 | 79 | // if(get_option('')) 80 | 81 | try { 82 | 83 | $query_arg = self::get_state('query_arg'); 84 | 85 | $url = 'entity/service'; 86 | 87 | $url = add_query_arg($query_arg, $url); 88 | 89 | $filters = []; 90 | 91 | $filters = apply_filters('wooms_url_get_service_filter', $filters); 92 | 93 | $url = add_query_arg('filter', implode(';', $filters), $url); 94 | 95 | $url = apply_filters('wooms_url_get_service', $url); 96 | 97 | do_action( 98 | 'wooms_logger', 99 | __CLASS__, 100 | sprintf('Услуги. Отправлен запрос: %s', $url), 101 | $state 102 | ); 103 | 104 | $data = request($url); 105 | 106 | //Check for errors and send message to UI 107 | if (isset($data['errors'][0]["error"])) { 108 | throw new \Exception($data['errors'][0]["error"]); 109 | } 110 | 111 | //If no rows, that send 'end' and stop walker 112 | if (isset($data['rows']) && empty($data['rows'])) { 113 | self::set_state('lock', 0); 114 | self::walker_finish(); 115 | return true; 116 | } 117 | 118 | $i = 0; 119 | foreach ($data['rows'] as $key => $item) { 120 | 121 | $i++; 122 | 123 | if(apply_filters('wooms_skip_service', false, $item)){ 124 | continue; 125 | } 126 | 127 | do_action('wooms_product_data_item', $item); 128 | } 129 | 130 | //update count 131 | self::set_state('count', self::get_state('count') + $i); 132 | 133 | //update offset 134 | $query_arg['offset'] = $query_arg['offset'] + count($data['rows']); 135 | 136 | self::set_state('query_arg', $query_arg); 137 | 138 | self::set_state('lock', 0); 139 | 140 | self::add_schedule_hook(true); 141 | 142 | return true; 143 | 144 | } catch (\Exception $e) { 145 | self::set_state('lock', 0); 146 | do_action( 147 | 'wooms_logger_error', 148 | __CLASS__, 149 | $e->getMessage() 150 | ); 151 | return false; 152 | } 153 | } 154 | 155 | 156 | /** 157 | * Add schedule hook 158 | */ 159 | public static function add_schedule_hook($force = false) 160 | { 161 | if (!self::is_enable()) { 162 | return; 163 | } 164 | 165 | if (self::is_wait()) { 166 | return; 167 | } 168 | 169 | if (as_next_scheduled_action(self::$walker_hook_name) && !$force) { 170 | return; 171 | } 172 | 173 | if ($force) { 174 | self::set_state('force', 1); 175 | } else { 176 | self::set_state('force', 0); 177 | } 178 | 179 | // Adding schedule hook 180 | as_schedule_single_action(time() + 5, self::$walker_hook_name, self::get_state(), 'WooMS'); 181 | } 182 | 183 | /** 184 | * Проверяем стоит ли обработчик на паузе? 185 | */ 186 | public static function is_wait() 187 | { 188 | if (self::get_state('end_timestamp')) { 189 | return true; 190 | } 191 | 192 | return false; 193 | } 194 | 195 | /** 196 | * is_enable 197 | */ 198 | public static function is_enable() 199 | { 200 | if (get_option('wooms_products_services_enable')) { 201 | return true; 202 | } 203 | 204 | return false; 205 | } 206 | 207 | /** 208 | * settings_ui 209 | */ 210 | public static function add_settings() 211 | { 212 | $option_id = 'wooms_products_services_enable'; 213 | register_setting('mss-settings', $option_id); 214 | add_settings_field( 215 | $id = $option_id, 216 | $title = 'Включить работу с услугами (импорт услуг из МойСклад)', 217 | $callback = function ($args) { 218 | printf('', $args['name'], checked(1, $args['value'], false)); 219 | printf('%s
', 'Тестовый режим. Не включайте эту функцию на реальном сайте, пока не проверите ее на тестовой копии сайта.'); 220 | }, 221 | $page = 'mss-settings', 222 | $section = 'woomss_section_other', 223 | $args = [ 224 | 'name' => $option_id, 225 | 'value' => @get_option($option_id), 226 | ] 227 | ); 228 | } 229 | 230 | /** 231 | * Finish walker 232 | */ 233 | public static function walker_finish() 234 | { 235 | self::set_state('end_timestamp', date("Y-m-d H:i:s")); 236 | 237 | do_action('wooms_recount_terms'); 238 | 239 | as_unschedule_all_actions(self::$walker_hook_name); 240 | 241 | do_action( 242 | 'wooms_logger', 243 | __CLASS__, 244 | sprintf('Услуги. Обработчик завершил работу: %s', self::get_state('end_timestamp')) 245 | ); 246 | 247 | return true; 248 | } 249 | 250 | /** 251 | * update_product 252 | * 253 | * @todo - remove? 254 | * 255 | * @param \WC_Product $product 256 | * @param array $api_data 257 | */ 258 | public static function update_product($product, $api_data) 259 | { 260 | if (!self::is_enable()) { 261 | return $product; 262 | } 263 | 264 | if ($api_data["meta"]["type"] != 'service') { 265 | return $product; 266 | } 267 | 268 | $product->set_virtual(true); 269 | $product->set_manage_stock(false); 270 | $product->set_stock_status($status = 'instock'); 271 | 272 | do_action( 273 | 'wooms_logger', 274 | __CLASS__, 275 | sprintf('Продукт Услуга - сброс данных об остатках: %s (id::%s)', $product->get_name(), $product->get_id()) 276 | ); 277 | 278 | return $product; 279 | } 280 | 281 | /** 282 | * Resetting state after start the main walker 283 | * And restart schedules for sync variations 284 | */ 285 | public static function reset_walker() 286 | { 287 | self::set_state('count', 0); 288 | self::set_state('lock', 0); 289 | self::set_state('end_timestamp', 0); 290 | self::set_state('timestamp_start', 0); 291 | self::add_schedule_hook(); 292 | } 293 | 294 | /** 295 | * display_state 296 | */ 297 | public static function display_state() 298 | { 299 | if (!self::is_enable()) { 300 | return; 301 | } 302 | 303 | $strings = []; 304 | 305 | if (as_next_scheduled_action(self::$walker_hook_name)) { 306 | $strings[] = sprintf('Статус: %s', 'Выполняется очередями в фоне'); 307 | } else { 308 | $strings[] = sprintf('Статус: %s', 'Ждет задач в очереди'); 309 | } 310 | 311 | if ($end_timestamp = self::get_state('end_timestamp')) { 312 | $strings[] = sprintf('Последняя успешная синхронизация (отметка времени UTC): %s', $end_timestamp); 313 | } 314 | 315 | $strings[] = sprintf('Очередь задач: открыть', admin_url('admin.php?page=wc-status&tab=action-scheduler&s=wooms_services_walker_batch&orderby=schedule&order=desc')); 316 | 317 | if (defined('WC_LOG_HANDLER') && 'WC_Log_Handler_DB' == WC_LOG_HANDLER) { 318 | $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs&source=WooMS-ProductsServices')); 319 | } else { 320 | $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs')); 321 | } 322 | 323 | ?> 324 |%s
', 'Тестовый режим. Не включайте эту функцию на реальном сайте, пока не проверите ее на тестовой копии сайта.'); 360 | }, 361 | $page = 'mss-settings', 362 | $section = 'woomss_section_other', 363 | $args = [ 364 | 'name' => $option_id, 365 | 'value' => @get_option($option_id), 366 | ] 367 | ); 368 | } 369 | 370 | /** 371 | * is_enable 372 | */ 373 | public static function is_enable() 374 | { 375 | if (get_option('wooms_products_bundle_enable')) { 376 | return true; 377 | } 378 | 379 | return false; 380 | } 381 | } 382 | 383 | ProductGrouped::init(); 384 | -------------------------------------------------------------------------------- /includes/ProductsCategories.php: -------------------------------------------------------------------------------- 1 | set_category_ids( array( $term_id ) ); 45 | 46 | return $product; 47 | } 48 | 49 | 50 | 51 | /** 52 | * add ancestors 53 | * 54 | * issue https://github.com/wpcraft-ru/wooms/issues/282 55 | */ 56 | public static function add_ancestors( \WC_Product $product, $row, $data ) { 57 | if ( ! get_option( 'wooms_categories_include_children' ) ) { 58 | return $product; 59 | } 60 | 61 | if ( empty( $row['productFolder']['meta']['href'] ) ) { 62 | return $product; 63 | } 64 | 65 | if ( ! $term_id = self::check_term_by_ms_uuid( $row['productFolder']['meta']['href'] ) ) { 66 | return $product; 67 | } 68 | 69 | $term_ancestors = get_ancestors( $term_id, 'product_cat', 'taxonomy' ); 70 | 71 | $term_ancestors[] = $term_id; 72 | $product->set_category_ids( $term_ancestors ); 73 | 74 | 75 | return $product; 76 | } 77 | 78 | 79 | 80 | public static function product_categories_update( $productfolder ) { 81 | 82 | if ( empty( $productfolder['rows'] ) ) { 83 | throw new Error( 'No categories for products', 500 ); 84 | } 85 | 86 | $rows = apply_filters('wooms_productfolder', $productfolder['rows']); 87 | 88 | $ids = []; 89 | foreach ( $rows as $row ) { 90 | $ids[] = self::product_category_update( $row, $rows ); 91 | } 92 | 93 | if ( empty( $ids ) ) { 94 | throw new Error( 'product_category_update = empty $list[]' ); 95 | } 96 | 97 | //delete categories not about current iteration 98 | $categories = get_categories( [ 99 | 'taxonomy' => 'product_cat', 100 | 'hide_empty' => false, 101 | ] ); 102 | if($categories){ 103 | foreach($categories as $term){ 104 | if(in_array($term->term_id, $ids)){ 105 | continue; 106 | } 107 | wp_delete_term($term->term_id, 'product_cat'); 108 | } 109 | } 110 | 111 | return $ids; 112 | 113 | } 114 | 115 | public static function product_category_update( $row, $rows ) { 116 | 117 | if ( empty( $row['id'] ) ) { 118 | throw new Error( 'product_category_update = no $row[id]' ); 119 | } 120 | 121 | $term_id = self::check_term_by_ms_uuid( $row['id'] ); 122 | 123 | if ( $term_id ) { 124 | 125 | update_term_meta( $term_id, 'wooms_updated_category', $row['updated'] ); 126 | 127 | $args = []; 128 | 129 | if ( isset( $row['productFolder']['meta']['href'] ) ) { 130 | $url_parent = $row['productFolder']['meta']['href']; 131 | foreach ( $rows as $parent_row ) { 132 | if ( $parent_row['meta']['href'] == $url_parent ) { 133 | $term_id_parent = self::product_category_update( $parent_row, $rows ); 134 | $args['parent'] = $term_id_parent; 135 | break; 136 | } 137 | } 138 | } else { 139 | $args['parent'] = 0; 140 | } 141 | 142 | wp_update_term( $term_id, $taxonomy = 'product_cat', $args ); 143 | 144 | return $term_id; 145 | } else { 146 | 147 | $term_new = array( 148 | 'wooms_id' => $row['id'], 149 | 'name' => $row['name'], 150 | 'archived' => $row['archived'], 151 | ); 152 | 153 | if ( isset( $row['productFolder']['meta']['href'] ) ) { 154 | $url_parent = $row['productFolder']['meta']['href']; 155 | 156 | foreach ( $rows as $parent_row ) { 157 | if ( $parent_row['meta']['href'] == $url_parent ) { 158 | $term_id_parent = self::product_category_update( $parent_row, $rows ); 159 | $term_new['parent'] = $term_id_parent; 160 | break; 161 | } 162 | } 163 | } 164 | 165 | // https://github.com/wpcraft-ru/wooms/issues/524#issuecomment-1860552168 166 | $term = wp_insert_term( $row['name'], 'product_cat', $term_new ); 167 | $term_id = ! is_wp_error( $term ) ? $term['term_id'] : null; 168 | 169 | update_term_meta( $term_id, 'wooms_id', $row['id'] ); 170 | 171 | update_term_meta( $term_id, 'wooms_updated_category', $row['updated'] ); 172 | 173 | if ( $session_id = get_option( 'wooms_session_id' ) ) { 174 | update_term_meta( $term_id, 'wooms_session_id', $session_id ); 175 | } 176 | 177 | do_action( 'wooms_add_category', $term_id, $row, $rows ); 178 | 179 | return $term_id; 180 | } 181 | 182 | } 183 | 184 | 185 | public static function prepare( $data, $row ) { 186 | 187 | return $data; 188 | 189 | } 190 | 191 | 192 | public static function reset() { 193 | $productfolder = request( 'entity/productfolder' ); 194 | 195 | self::product_categories_update( $productfolder ); 196 | 197 | do_action('wooms_product_categories_update', $productfolder); 198 | 199 | } 200 | 201 | 202 | /** 203 | * If isset term return term_id, else return false 204 | */ 205 | public static function check_term_by_ms_uuid( $id ) { 206 | 207 | //if uuid as url - get uuid only 208 | $id = str_replace( 'https://online.moysklad.ru/api/remap/1.2/entity/productfolder/', '', $id ); 209 | $id = str_replace( 'https://api.moysklad.ru/api/remap/1.2/entity/productfolder/', '', $id ); 210 | 211 | 212 | $terms = get_terms( array( 213 | 'taxonomy' => array( 'product_cat' ), 214 | 'hide_empty' => false, 215 | 'meta_query' => array( 216 | array( 217 | 'key' => 'wooms_id', 218 | 'value' => $id, 219 | ), 220 | ), 221 | ) ); 222 | 223 | if ( empty( $terms[0] ) ) { 224 | return null; 225 | } 226 | return $terms[0]->term_id; 227 | } 228 | 229 | 230 | /** 231 | * Meta box in category 232 | * 233 | * @since 2.1.2 234 | * 235 | * @param $term 236 | */ 237 | public static function display_data_category( $term ) { 238 | 239 | $meta_data = get_term_meta( $term->term_id, 'wooms_id', true ); 240 | $meta_data_updated = get_term_meta( $term->term_id, 'wooms_updated_category', true ); 241 | 242 | ?> 243 |%s
', 'Опция позволяет указывать категории у продукта с учетом всего дерева - от верхнего предка, до всех потомков' ); 322 | printf( 'Подробнее: https://github.com/wpcraft-ru/wooms/issues/282
', 'https://github.com/wpcraft-ru/wooms/issues/282' ); 323 | }, 324 | $page = 'mss-settings', 325 | $section = 'wooms_product_cat', 326 | $args = [ 327 | 'key' => $option_name, 328 | 'value' => get_option( $option_name ) 329 | ] 330 | ); 331 | } 332 | 333 | /** 334 | * is_disable 335 | */ 336 | public static function is_disable() { 337 | if ( get_option( 'woomss_categories_sync_enabled' ) ) { 338 | return true; 339 | } 340 | 341 | return false; 342 | } 343 | 344 | /** 345 | * add_setting_categories_sync_enabled 346 | */ 347 | public static function add_setting_categories_sync_enabled() { 348 | 349 | /** 350 | * TODO заменить woomss_categories_sync_enabled на wooms_categories_sync_disable 351 | */ 352 | $option_name = 'woomss_categories_sync_enabled'; 353 | 354 | register_setting( 'mss-settings', $option_name ); 355 | add_settings_field( 356 | $id = $option_name, 357 | $title = 'Отключить синхронизацию категорий', 358 | $callback = function ($args) { 359 | printf( '', $args['key'], checked( 1, $args['value'], false ) ); 360 | printf( '%s', 'Если включить опцию, то при обновлении продуктов категории не будут учтываться в соответствии с группами МойСклад.' ); 361 | }, 362 | $page = 'mss-settings', 363 | $section = 'wooms_product_cat', 364 | $args = [ 365 | 'key' => $option_name, 366 | 'value' => get_option( $option_name ) 367 | ] 368 | ); 369 | } 370 | 371 | 372 | public static function recount( ) { 373 | wc_recount_all_terms(); 374 | } 375 | 376 | } 377 | 378 | ProductsCategories::init(); 379 | -------------------------------------------------------------------------------- /includes/ProductsImage.php: -------------------------------------------------------------------------------- 1 | 'disable']; 23 | } 24 | 25 | $args = array( 26 | 'post_type' => 'product', 27 | 'meta_query' => array( 28 | array( 29 | 'key' => 'wooms_url_for_get_thumbnail', 30 | 'compare' => 'EXISTS', 31 | ), 32 | ), 33 | 'no_found_rows' => true, 34 | 'update_post_term_cache' => false, 35 | 'update_post_meta_cache' => false, 36 | 'cache_results' => false, 37 | ); 38 | 39 | $items = get_posts($args); 40 | 41 | if (empty($items)) { 42 | 43 | do_action( 44 | 'wooms_logger', 45 | __NAMESPACE__, 46 | sprintf('Главные изображения продуктов загружены') 47 | ); 48 | 49 | return ['result' => 'finish']; 50 | } 51 | 52 | $ids = []; 53 | 54 | foreach ($items as $key => $value) { 55 | if (product_image_download($value->ID)) { 56 | $ids[] = $value->ID; 57 | } 58 | 59 | delete_post_meta($value->ID, 'wooms_url_for_get_thumbnail'); 60 | } 61 | 62 | $state['ids'] = $ids; 63 | if(empty($state['count'])){ 64 | $state['count'] = count($items); 65 | } else { 66 | $state['count'] += count($items); 67 | } 68 | 69 | as_schedule_single_action(time(), HOOK_NAME, [$state], 'WooMS'); 70 | 71 | return ['result' => 'restart']; 72 | } 73 | 74 | function restart() 75 | { 76 | as_schedule_single_action(time(), HOOK_NAME, [], 'WooMS'); 77 | } 78 | 79 | 80 | function uploadRemoteImageAndAttach($image_url, $product_id, $filename = 'image.jpg') 81 | { 82 | if ($check_id = check_exist_image_by_url($image_url)) { 83 | return $check_id; 84 | } 85 | 86 | $uploads_dir = wp_upload_dir(); 87 | $post_name = get_post_field('post_name', $product_id); 88 | $filename_data = wp_check_filetype($filename); 89 | $filename = $post_name . '.' . $filename_data['ext']; 90 | $filename = sanitize_file_name($filename); 91 | $filename = wp_unique_filename($uploads_dir['path'], $filename); 92 | 93 | $header_array = [ 94 | 'Authorization' => 'Basic ' . base64_encode(get_option('woomss_login') . ':' . get_option('woomss_pass')), 95 | ]; 96 | 97 | $args = [ 98 | 'headers' => $header_array, 99 | ]; 100 | 101 | $get = wp_remote_get($image_url, $args); 102 | 103 | if (is_wp_error($get)) { 104 | do_action( 105 | 'wooms_logger_error', 106 | __NAMESPACE__, 107 | sprintf('Ошибка загрузки картинки: %s', $get->get_error_message()), 108 | $get->get_error_code() 109 | ); 110 | 111 | return false; 112 | } 113 | 114 | if (empty($get['response']['code'])) { 115 | return false; 116 | } 117 | 118 | if (403 == $get['response']['code']) { 119 | $http_response = $get['http_response']; 120 | 121 | if ($http_response->get_status() == 403) { 122 | $response = $http_response->get_response_object(); 123 | $url_image = $http_response->get_response_object()->url; 124 | 125 | $get2 = wp_remote_get($url_image); 126 | $mirror = wp_upload_bits($filename, '', wp_remote_retrieve_body($get2)); 127 | } 128 | } else { 129 | 130 | $mirror = wp_upload_bits($filename, '', wp_remote_retrieve_body($get)); 131 | } 132 | 133 | $type = $filename_data['type']; 134 | 135 | if (!$type) 136 | return false; 137 | 138 | 139 | $attachment = array( 140 | 'post_title' => $filename, 141 | 'post_mime_type' => $type 142 | ); 143 | 144 | $attach_id = wp_insert_attachment($attachment, $mirror['file'], $product_id); 145 | 146 | require_once(ABSPATH . 'wp-admin/includes/image.php'); 147 | 148 | $attach_data = wp_generate_attachment_metadata($attach_id, $mirror['file']); 149 | 150 | update_post_meta($attach_id, 'wooms_url', $image_url); 151 | 152 | wp_update_attachment_metadata($attach_id, $attach_data); 153 | 154 | do_action( 155 | 'wooms_logger', 156 | __NAMESPACE__, 157 | sprintf('Image is downloaded %s (ИД %s, filename: %s)', $product_id, $attach_id, $filename) 158 | ); 159 | 160 | return $attach_id; 161 | } 162 | 163 | 164 | /** 165 | * Check exist image by URL 166 | */ 167 | function check_exist_image_by_url($url_api) 168 | { 169 | $posts = get_posts('post_type=attachment&meta_key=wooms_url&meta_value=' . $url_api); 170 | if (empty($posts)) { 171 | return false; 172 | } else { 173 | 174 | do_action( 175 | 'wooms_logger', 176 | __NAMESPACE__, 177 | sprintf('We have such image (%s) already', $posts[0]->ID) 178 | ); 179 | 180 | return $posts[0]->ID; 181 | } 182 | } 183 | 184 | function product_image_download($product_id, $meta_key = 'wooms_url_for_get_thumbnail') 185 | { 186 | if (!$url = get_post_meta($product_id, $meta_key, true)) { 187 | return false; 188 | } 189 | 190 | $images_data = \WooMS\request( $url ); 191 | 192 | if (empty($images_data['rows'][0]['filename'])) { 193 | do_action( 194 | 'wooms_logger_error', 195 | __NAMESPACE__, 196 | sprintf('Ошибка получения картинки для продукта %s (url %s)', $product_id, $url), 197 | $images_data 198 | ); 199 | return false; 200 | } 201 | 202 | $image_name = $images_data['rows'][0]['filename']; 203 | $url = $images_data['rows'][0]['meta']['downloadHref']; 204 | 205 | $check_id = uploadRemoteImageAndAttach($url, $product_id, $image_name); 206 | 207 | if (!empty($check_id)) { 208 | 209 | set_post_thumbnail($product_id, $check_id); 210 | 211 | do_action( 212 | 'wooms_logger', 213 | __NAMESPACE__, 214 | sprintf('Загружена картинка для продукта %s (ИД %s, filename: %s)', $product_id, $check_id, $image_name) 215 | ); 216 | return true; 217 | } else { 218 | do_action( 219 | 'wooms_logger_error', 220 | __NAMESPACE__, 221 | sprintf('Ошибка назначения картинки для продукта %s (url %s, filename: %s)', $product_id, $url, $image_name) 222 | ); 223 | return false; 224 | } 225 | } 226 | 227 | 228 | /** 229 | * Manual start images download 230 | */ 231 | function render_ui() 232 | { 233 | if (!is_enable()) { 234 | return; 235 | } 236 | 237 | $strings = []; 238 | 239 | if (as_next_scheduled_action(HOOK_NAME)) { 240 | $strings[] = sprintf('Статус: %s', 'Выполняется очередями в фоне'); 241 | $strings[] = 'Загрузка изображений по 5 штук за раз.
'; 242 | } else { 243 | $strings[] = sprintf('Статус: %s', 'в ожидании новых задач'); 244 | $strings[] = sprintf('Время последней задачи в очереди: %s', wooms_get_timestamp_last_job_by_hook(HOOK_NAME)); 245 | } 246 | 247 | $strings[] = sprintf('Очередь задач: открыть', admin_url('admin.php?page=wc-status&tab=action-scheduler&s=wooms_product_image_sync&orderby=schedule&order=desc')); 248 | 249 | // if (defined('WC_LOG_HANDLER') && 'WC_Log_Handler_DB' == WC_LOG_HANDLER) { 250 | // $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs&source=WooMS-ProductImage')); 251 | // } else { 252 | // $strings[] = sprintf('Журнал обработки: открыть', admin_url('admin.php?page=wc-status&tab=logs')); 253 | // } 254 | 255 | echo '%s
', $string); 259 | } 260 | 261 | do_action('wooms_product_images_info'); 262 | 263 | 264 | } 265 | 266 | /** 267 | * add image to metafield for download 268 | */ 269 | function add_image_task_to_product($product, $row) 270 | { 271 | if (!is_enable()) { 272 | return $product; 273 | } 274 | 275 | //Check image 276 | if (empty($row['images']['meta']['size'])) { 277 | return $product; 278 | } else { 279 | $url = $row['images']['meta']['href']; 280 | } 281 | $product->update_meta_data('test_wooms_url_for_get_thumbnail', $url); 282 | //check current thumbnail. if isset - break, or add url for next downloading 283 | if ($id = get_post_thumbnail_id($product->get_id()) && empty(get_option('woomss_images_replace_to_sync'))) { 284 | return $product; 285 | } else { 286 | $product->update_meta_data('wooms_url_for_get_thumbnail', $url); 287 | } 288 | 289 | return $product; 290 | } 291 | 292 | function is_enable() 293 | { 294 | if (empty(get_option('woomss_images_sync_enabled'))) { 295 | return false; 296 | } 297 | 298 | return true; 299 | } 300 | 301 | 302 | 303 | function add_settings() 304 | { 305 | add_settings_section('woomss_section_images', 'Изображения', null, 'mss-settings'); 306 | 307 | register_setting('mss-settings', 'woomss_images_sync_enabled'); 308 | add_settings_field( 309 | $id = 'woomss_images_sync_enabled', 310 | $title = 'Включить синхронизацию картинок', 311 | $callback = function ($args) { 312 | $option = 'woomss_images_sync_enabled'; 313 | $desc = 'Если включить опцию, то плагин будет загружать изображения из МойСклад.'; 314 | printf(' %s', $args['key'], $args['value'], $desc); 315 | }, 316 | $page = 'mss-settings', 317 | $section = 'woomss_section_images', 318 | $args = [ 319 | 'key' => 'woomss_images_sync_enabled', 320 | 'value' => checked(1, get_option('woomss_images_sync_enabled'), false), 321 | ], 322 | ); 323 | 324 | register_setting('mss-settings', 'woomss_images_replace_to_sync'); 325 | add_settings_field( 326 | 'woomss_images_replace_to_sync', 327 | 'Замена изображении при синхронизации', 328 | $callback = function ($args) { 329 | $option = 'woomss_images_sync_enabled'; 330 | $desc = 'Если включить опцию, то плагин будет проверять и заменять изображения из МойСклад если они там обновятся.'; 331 | printf(' %s', $args['key'], $args['value'], $desc); 332 | }, 333 | $page = 'mss-settings', 334 | $section = 'woomss_section_images', 335 | $args = [ 336 | 'key' => 'woomss_images_replace_to_sync', 337 | 'value' => checked(1, get_option('woomss_images_replace_to_sync'), false), 338 | ], 339 | ); 340 | } 341 | --------------------------------------------------------------------------------