get_cart() as $cart_item_key => $cart_item ) { // Проверяем что элемент корректный if ( ! isset( $cart_item['product_id'] ) || ! isset( $cart_item['data'] ) || ! is_object( $cart_item['data'] ) ) { $to_remove[] = $cart_item_key; } } // Удаляем битые элементы if ( ! empty( $to_remove ) ) { foreach ( $to_remove as $key ) { unset( $cart->cart_contents[ $key ] ); } $cart->set_session(); } } // ВРЕМЕННАЯ ОЧИСТКА И КОНВЕРТАЦИЯ - ЗАКОММЕНТИРОВАНО! /* add_action( 'init', 'orgsteklo_clear_and_convert', 1 ); function orgsteklo_clear_and_convert() { global $wpdb; // Очищаем корзину $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_wc_session_%'" ); $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wc_session_%'" ); $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '_woocommerce_persistent_cart%'" ); if ( function_exists( 'WC' ) && WC()->cart ) { WC()->cart->empty_cart(); } // Находим все товары с калькулятором и меняем их тип на simple $products_with_calculator = $wpdb->get_results( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_orgsteklo_price_table' AND meta_value != ''" ); foreach ( $products_with_calculator as $row ) { $product_id = $row->post_id; // Меняем тип на simple wp_set_object_terms( $product_id, 'simple', 'product_type' ); update_post_meta( $product_id, '_product_type', 'simple' ); // Удаляем все вариации если есть $variations = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'product_variation'", $product_id ) ); foreach ( $variations as $variation ) { wp_delete_post( $variation->ID, true ); } } } */ // ========== КОНЕЦ - ВРЕМЕННАЯ ОЧИСТКА ЗАКОММЕНТИРОВАНА ========== /** * Получение констант из настроек (с возможностью изменения в админке) */ function orgsteklo_get_discount_percent_from_tags( int $product_id ): int { $tags = wp_get_post_terms( $product_id, 'product_tag', ['fields' => 'names'] ); if ( empty( $tags ) || is_wp_error( $tags ) ) { return 0; } foreach ( $tags as $tag ) { if ( preg_match('/-(\d{1,2})%/', $tag, $m) ) { return min( (int) $m[1], 90 ); // защита от -100% } } return 0; } function orgsteklo_get_material_density() { return floatval( get_option( 'orgsteklo_material_density', 1.19 ) ); } function orgsteklo_get_custom_size_coefficient() { return floatval( get_option( 'orgsteklo_custom_size_coefficient', 1.2 ) ); } /** * Получить данные таблицы для товара * * @param int $product_id ID товара * @return array|null Данные таблицы или null если не найдено */ function orgsteklo_get_product_table_data( $product_id ) { $table_number = get_post_meta( $product_id, '_orgsteklo_price_table', true ); if ( empty( $table_number ) ) { return null; } $tables = get_option( 'orgsteklo_price_tables', [] ); if ( ! isset( $tables[ $table_number ] ) ) { return null; } return $tables[ $table_number ]; } /** * Получить строку данных из таблицы по толщине * * @param array $table_data Данные таблицы * @param float $thickness Толщина листа в мм * @return array|null Строка данных или null если не найдено */ function orgsteklo_get_table_row_by_thickness( $table_data, $thickness ) { if ( empty( $table_data['rows'] ) ) { return null; } foreach ( $table_data['rows'] as $row ) { if ( abs( $row['thickness'] - $thickness ) < 0.01 ) { return $row; } } return null; } /** * Расчет веса 1 листа * Формула: В = А × В × С × 1.19 / 1000000 * * @param float $width Ширина в мм * @param float $length Длина в мм * @param float $thickness Толщина в мм * @return float Вес в кг (округлено до 3 знаков) */ function orgsteklo_calculate_sheet_weight( $width, $length, $thickness ) { $density = orgsteklo_get_material_density(); $weight = ( $width * $length * $thickness * $density ) / 1000000; return round( $weight, 3 ); } /** * Расчет веса 1 м² * Формула: В_М² = С × 1.19 * * @param float $thickness Толщина в мм * @return float Вес в кг (округлено до 3 знаков) */ function orgsteklo_calculate_sqm_weight( $thickness ) { $density = orgsteklo_get_material_density(); $weight = $thickness * $density; return round( $weight, 3 ); } /** * Расчет стоимости 1 кг для листа стандартного размера * Формула: Ст.кг_ЛСР = Ст.кг_баз × К1 × К2 × К3 × К4 × К5 * * @param array $row Строка данных из таблицы * @return float Стоимость в рублях (округлено до целых) */ function orgsteklo_calculate_standard_price_per_kg( $row ) { $base_price = get_option( 'orgsteklo_base_price_per_kg', 300 ); $k1 = $row['k1'] ?? 1; $k2 = $row['k2'] ?? 1; $k3 = $row['k3'] ?? 1; $k4 = $row['k4'] ?? 1; $k5 = $row['k5'] ?? 1; $price = $base_price * $k1 * $k2 * $k3 * $k4 * $k5; return round( $price ); } /** * АЛГОРИТМ 1: Расчет для листа стандартного размера * * @param int $product_id ID товара * @param float $thickness Толщина в мм * @param int $quantity Количество листов * @return array|WP_Error Массив с результатами расчета или ошибка */ function orgsteklo_calculate_standard_size( $product_id, $thickness, $quantity = 1 ) { // Получаем данные таблицы $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data ) { return new WP_Error( 'no_table', 'Для товара не выбрана таблица расчета цен' ); } // Получаем строку данных по толщине $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); if ( ! $row ) { return new WP_Error( 'no_thickness', 'В таблице нет данных для толщины ' . $thickness . ' мм' ); } // Базовая стоимость 1 кг $base_price_kg = get_option( 'orgsteklo_base_price_per_kg', 300 ); // Шаг 2: Стоимость 1 кг для листа стандартного размера $price_per_kg_standard = orgsteklo_calculate_standard_price_per_kg( $row ); // Шаг 3: Вес 1 листа стандартного размера $weight_standard_sheet = orgsteklo_calculate_sheet_weight( $row['width'], $row['length'], $row['thickness'] ); // Шаг 4: Стоимость 1 листа стандартного размера $price_standard_sheet = round( $price_per_kg_standard * $weight_standard_sheet ); // Шаг 6: Вес заказа $order_weight = round( $weight_standard_sheet * $quantity, 3 ); // Шаг 7: Стоимость заказа $order_price = round( $price_standard_sheet * $quantity ); // Шаг 8: Вес 1 м² $weight_sqm = orgsteklo_calculate_sqm_weight( $row['thickness'] ); // Шаг 9: Стоимость 1 м² $price_sqm = round( ( $price_standard_sheet * 1000000 ) / ( $row['width'] * $row['length'] ) ); return [ 'is_standard_size' => true, 'base_price_kg' => $base_price_kg, 'price_per_kg_standard' => $price_per_kg_standard, 'weight_standard_sheet' => $weight_standard_sheet, 'price_standard_sheet' => $price_standard_sheet, 'width' => $row['width'], 'length' => $row['length'], 'thickness' => $row['thickness'], 'price_per_kg_current' => $price_per_kg_standard, // Для стандартного = standard 'weight_current_sheet' => $weight_standard_sheet, // Для стандартного = standard 'price_current_sheet' => $price_standard_sheet, // Для стандартного = standard 'weight_sqm' => $weight_sqm, 'price_sqm' => $price_sqm, 'quantity' => $quantity, 'order_weight' => $order_weight, 'order_price' => $order_price, ]; } /** * АЛГОРИТМ 2: Расчет для листа заданного размера * * @param int $product_id ID товара * @param float $thickness Толщина в мм * @param float $width Заданная ширина в мм * @param float $length Заданная длина в мм * @param int $quantity Количество листов * @return array|WP_Error Массив с результатами расчета или ошибка */ function orgsteklo_calculate_custom_size( $product_id, $thickness, $width, $length, $quantity = 1 ) { // Сначала получаем все значения из Алгоритма 1 (они не меняются) $standard_calc = orgsteklo_calculate_standard_size( $product_id, $thickness, $quantity ); if ( is_wp_error( $standard_calc ) ) { return $standard_calc; } // Получаем данные таблицы для надбавки N $table_data = orgsteklo_get_product_table_data( $product_id ); $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); // Шаг 1: Вес 1 листа заданного размера $weight_custom_sheet = orgsteklo_calculate_sheet_weight( $width, $length, $thickness ); // Шаг 2: Вес заказа $order_weight = round( $weight_custom_sheet * $quantity, 3 ); // Шаг 3: Стоимость 1 листа заданного размера // Формула: Ст.ЛЗР = (Ст.кг_ЛСР × коэфф × В_ЛЗР) + N $n_surcharge = $row['n'] ?? 0; $custom_coeff = orgsteklo_get_custom_size_coefficient(); $price_custom_sheet = round( ( $standard_calc['price_per_kg_standard'] * $custom_coeff * $weight_custom_sheet ) + $n_surcharge ); // Шаг 4: Стоимость заказа и стоимость 1 м² $order_price = round( $price_custom_sheet * $quantity ); $price_sqm = round( ( $price_custom_sheet * 1000000 ) / ( $width * $length ) ); // Шаг 5: Стоимость 1 кг для листа заданного размера $price_per_kg_custom = $order_weight > 0 ? round( $order_price / $order_weight ) : 0; // Обновляем результаты $result = $standard_calc; $result['is_standard_size'] = false; $result['width'] = $width; $result['length'] = $length; $result['weight_current_sheet'] = $weight_custom_sheet; $result['price_current_sheet'] = $price_custom_sheet; $result['price_per_kg_current'] = $price_per_kg_custom; $result['price_sqm'] = $price_sqm; $result['order_weight'] = $order_weight; $result['order_price'] = $order_price; $result['n_surcharge'] = $n_surcharge; return $result; } /** * Универсальная функция расчета - автоматически определяет алгоритм * * @param int $product_id ID товара * @param float $thickness Толщина в мм * @param float|null $width Ширина в мм (null = стандартная) * @param float|null $length Длина в мм (null = стандартная) * @param int $quantity Количество листов * @return array|WP_Error Массив с результатами расчета или ошибка */ function orgsteklo_calculate_price( $product_id, $thickness, $width = null, $length = null, $quantity = 1 ) { // Получаем данные таблицы $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data ) { return new WP_Error( 'no_table', 'Для товара не выбрана таблица расчета цен' ); } // Получаем строку данных по толщине $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); if ( ! $row ) { return new WP_Error( 'no_thickness', 'В таблице нет данных для толщины ' . $thickness . ' мм' ); } // Определяем стандартные размеры $standard_width = $row['width']; $standard_length = $row['length']; // Если размеры не заданы или равны стандартным - используем Алгоритм 1 if ( ( $width === null && $length === null ) || ( abs( $width - $standard_width ) < 0.01 && abs( $length - $standard_length ) < 0.01 ) ) { return orgsteklo_calculate_standard_size( $product_id, $thickness, $quantity ); } // Иначе используем Алгоритм 2 return orgsteklo_calculate_custom_size( $product_id, $thickness, $width, $length, $quantity ); } /** * Получить все доступные толщины для товара * * @param int $product_id ID товара * @return array Массив толщин */ function orgsteklo_get_available_thicknesses( $product_id ) { $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data || empty( $table_data['rows'] ) ) { return []; } $thicknesses = []; foreach ( $table_data['rows'] as $row ) { $thicknesses[] = $row['thickness']; } return $thicknesses; } /** * Получить стандартные размеры для толщины * * @param int $product_id ID товара * @param float $thickness Толщина в мм * @return array|null Массив ['width' => ..., 'length' => ...] или null */ function orgsteklo_get_standard_dimensions( $product_id, $thickness ) { $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data ) { return null; } $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); if ( ! $row ) { return null; } return [ 'width' => $row['width'], 'length' => $row['length'], ]; } /** * AJAX обработчик для расчета цен */ add_action( 'wp_ajax_orgsteklo_calculate', 'orgsteklo_ajax_calculate' ); add_action( 'wp_ajax_nopriv_orgsteklo_calculate', 'orgsteklo_ajax_calculate' ); function orgsteklo_ajax_calculate() { check_ajax_referer( 'orgsteklo_calc_nonce', 'nonce' ); $product_id = intval( $_POST['product_id'] ?? 0 ); $thickness = floatval( $_POST['thickness'] ?? 0 ); $width = isset($_POST['width']) ? floatval($_POST['width']) : null; $length = isset($_POST['length']) ? floatval($_POST['length']) : null; $quantity = intval( $_POST['quantity'] ?? 1 ); if ( ! $product_id || ! $thickness ) { wp_send_json_error( [ 'message' => 'Не указаны обязательные параметры', 'debug' => 'params missing' ] ); } /** 1️⃣ Получаем данные из таблицы калькулятора (как раньше!) */ $result = orgsteklo_calculate_price( $product_id, $thickness, $width, $length, $quantity ); if ( is_wp_error( $result ) ) { wp_send_json_error( [ 'message' => $result->get_error_message() ] ); } /** 2️⃣ Получаем стандартные размеры из таблицы для вывода */ $table_data = orgsteklo_get_product_table_data( $product_id ); $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); // Добавляем стандартные размеры в результат $result['standard_width'] = $row['width']; $result['standard_length'] = $row['length']; /** 3️⃣ СКИДКА ПО МЕТКАМ */ $discount_percent = orgsteklo_get_discount_percent_from_tags( $product_id ); $has_discount = $discount_percent > 0; $result['has_discount'] = $has_discount; // гарантируем ключи $result['price_standard_sheet_old'] = null; $result['price_per_kg_standard_old'] = null; $result['price_per_kg_current_old'] = null; $result['price_sqm_old'] = null; $result['price_current_sheet_old'] = null; $result['order_price_old'] = null; if ( $has_discount ) { // сохраняем старые цены $result['price_standard_sheet_old'] = $result['price_standard_sheet']; $result['price_per_kg_standard_old'] = $result['price_per_kg_standard']; $result['price_sqm_old'] = $result['price_sqm']; $result['price_current_sheet_old'] = $result['price_current_sheet']; $result['order_price_old'] = $result['order_price']; // Сохраняем старую цену за кг для заданного размера (если есть) if ( isset( $result['price_per_kg_current'] ) ) { $result['price_per_kg_current_old'] = $result['price_per_kg_current']; } $coef = (100 - $discount_percent) / 100; // применяем скидку $result['price_standard_sheet'] = round($result['price_standard_sheet'] * $coef); $result['price_per_kg_standard'] = round($result['price_per_kg_standard'] * $coef); $result['price_sqm'] = round($result['price_sqm'] * $coef); $result['price_current_sheet'] = round($result['price_current_sheet'] * $coef); $result['order_price'] = round($result['order_price'] * $coef); // Применяем скидку к цене за кг для заданного размера (если есть) if ( isset( $result['price_per_kg_current'] ) ) { $result['price_per_kg_current'] = round($result['price_per_kg_current'] * $coef); } } /** 4️⃣ Отправляем результат */ wp_send_json_success( $result ); } /** * AJAX обработчик для получения стандартных размеров */ add_action( 'wp_ajax_orgsteklo_get_standard_dimensions', 'orgsteklo_ajax_get_standard_dimensions' ); add_action( 'wp_ajax_nopriv_orgsteklo_get_standard_dimensions', 'orgsteklo_ajax_get_standard_dimensions' ); function orgsteklo_ajax_get_standard_dimensions() { check_ajax_referer( 'orgsteklo_calc_nonce', 'nonce' ); $product_id = intval( $_POST['product_id'] ?? 0 ); $thickness = floatval( $_POST['thickness'] ?? 0 ); if ( ! $product_id || ! $thickness ) { wp_send_json_error( [ 'message' => 'Не указаны обязательные параметры' ] ); } /** Получаем стандартные размеры из таблицы (как раньше!) */ $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data ) { wp_send_json_error( [ 'message' => 'Для товара не выбрана таблица расчета цен' ] ); } $row = orgsteklo_get_table_row_by_thickness( $table_data, $thickness ); if ( ! $row ) { wp_send_json_error( [ 'message' => 'В таблице нет данных для толщины ' . $thickness . ' мм' ] ); } wp_send_json_success( [ 'width' => floatval( $row['width'] ), 'length' => floatval( $row['length'] ), ] ); } /** * Подключение скриптов калькулятора на странице товара */ add_action( 'wp_enqueue_scripts', 'orgsteklo_enqueue_calculator_scripts' ); function orgsteklo_enqueue_calculator_scripts() { // Только на странице товара if ( ! is_product() ) { return; } global $post; $product_id = $post->ID; // Проверяем что у товара выбрана таблица $table_number = get_post_meta( $product_id, '_orgsteklo_price_table', true ); if ( empty( $table_number ) ) { return; } // Подключаем скрипт wp_enqueue_script( 'orgsteklo-price-calculator', get_template_directory_uri() . '/assets/js/price-calculator.js', [ 'jquery' ], '1.0.1', true ); // Передаем данные в JS wp_localize_script( 'orgsteklo-price-calculator', 'orgstekloCalc', [ 'productId' => $product_id, 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'orgsteklo_calc_nonce' ), ] ); } /** * Подключение шаблона калькулятора для товаров с выбранной таблицей */ add_filter( 'woocommerce_locate_template', 'orgsteklo_use_calculator_template', 10, 3 ); function orgsteklo_use_calculator_template( $template, $template_name, $template_path ) { // Проверяем что это шаблон вариативного или простого товара if ( $template_name !== 'single-product/add-to-cart/variable.php' && $template_name !== 'single-product/add-to-cart/simple.php' ) { return $template; } // Проверяем что мы на странице товара if ( ! is_product() ) { return $template; } global $post; if ( ! $post ) { return $template; } // Проверяем что у товара выбрана таблица калькулятора $table_number = get_post_meta( $post->ID, '_orgsteklo_price_table', true ); if ( empty( $table_number ) ) { return $template; } // Используем наш шаблон калькулятора $calculator_template = get_template_directory() . '/woocommerce/single-product/add-to-cart/orgsteklo-calculator.php'; if ( file_exists( $calculator_template ) ) { return $calculator_template; } return $template; } /** * Отключаем проверку атрибутов для универсальных вариаций калькулятора */ add_filter( 'woocommerce_add_to_cart_validation', 'orgsteklo_skip_variation_validation', 10, 3 ); function orgsteklo_skip_variation_validation( $passed, $product_id, $quantity ) { // Если это товар с калькулятором - пропускаем валидацию атрибутов $table_number = get_post_meta( $product_id, '_orgsteklo_price_table', true ); if ( ! empty( $table_number ) ) { // Очищаем ошибки связанные с атрибутами wc_clear_notices(); return true; } return $passed; } /** * Создаем вариации из таблицы калькулятора */ function orgsteklo_create_variations_from_table( $product_id ) { // Проверяем что товар использует калькулятор $table_number = get_post_meta( $product_id, '_orgsteklo_price_table', true ); if ( empty( $table_number ) ) { return array( 'success' => false, 'message' => 'Таблица не выбрана' ); } // Получаем данные таблицы $table_data = orgsteklo_get_product_table_data( $product_id ); if ( ! $table_data || empty( $table_data['rows'] ) ) { return array( 'success' => false, 'message' => 'Нет данных в таблице' ); } // Убеждаемся что товар вариативный wp_set_object_terms( $product_id, 'variable', 'product_type' ); // Получаем родительский товар $product = wc_get_product( $product_id ); // Собираем все значения толщин как строки $thickness_values = array(); foreach ( $table_data['rows'] as $row ) { if ( ! empty( $row['thickness'] ) ) { $thickness_values[] = floatval( $row['thickness'] ) . ' мм'; } } // Создаем атрибут на уровне товара (кастомный, не глобальный) $attribute = new WC_Product_Attribute(); $attribute->set_id( 0 ); $attribute->set_name( 'Толщина листа' ); // Имя без pa_ $attribute->set_options( $thickness_values ); // Массив строк $attribute->set_position( 0 ); $attribute->set_visible( true ); $attribute->set_variation( true ); $product->set_attributes( array( $attribute ) ); $product->save(); // Создаем вариации $count = 0; foreach ( $table_data['rows'] as $row ) { if ( empty( $row['thickness'] ) ) continue; $thickness = floatval( $row['thickness'] ); $thickness_string = $thickness . ' мм'; // Создаем вариацию $variation = new WC_Product_Variation(); $variation->set_parent_id( $product_id ); $variation->set_status( 'publish' ); $variation->set_virtual( true ); $variation->set_manage_stock( false ); $variation->set_stock_status( 'instock' ); // Устанавливаем минимальную цену 1 ₽ и вес 0.001 кг чтобы WooCommerce не ругался // Реальная цена и вес будут установлены через хуки $variation->set_regular_price( 1 ); $variation->set_weight( 0.001 ); // Устанавливаем атрибут - slug атрибута и значение $variation->set_attributes( array( sanitize_title( 'Толщина листа' ) => $thickness_string ) ); $variation_id = $variation->save(); if ( $variation_id ) { // Сохраняем ТОЛЬКО толщину для поиска вариации (вариация = заглушка для WooCommerce) // Все расчеты делаются из таблицы, НЕ из вариации! update_post_meta( $variation_id, '_orgsteklo_thickness', $thickness ); $count++; } } return array( 'success' => true, 'message' => 'Создано вариаций: ' . $count, 'count' => $count ); } /** * Сохраняем данные калькулятора в корзине через cart_item_data * Теперь данные передаются через 6-й параметр add_to_cart() */ add_filter( 'woocommerce_add_cart_item_data', 'orgsteklo_add_calculator_to_cart', 10, 3 ); function orgsteklo_add_calculator_to_cart( $cart_item_data, $product_id, $variation_id ) { // Данные уже переданы через параметр cart_item_data в add_to_cart() // Просто возвращаем их как есть return $cart_item_data; } /** * Устанавливаем цену и вес СРАЗУ при добавлении в корзину */ add_filter( 'woocommerce_add_cart_item', 'orgsteklo_set_price_on_add', 10, 2 ); function orgsteklo_set_price_on_add( $cart_item, $cart_item_key ) { if ( isset( $cart_item['orgsteklo_calculator'] ) && isset( $cart_item['data'] ) ) { $calc = $cart_item['orgsteklo_calculator']; // Округляем до целых для гарантии отсутствия копеек $price = isset( $calc['price_per_unit'] ) ? round(floatval( $calc['price_per_unit'] )) : 0; $price_old = isset( $calc['price_per_unit_old'] ) ? round(floatval( $calc['price_per_unit_old'] )) : 0; $weight = isset( $calc['weight_per_unit'] ) ? floatval( $calc['weight_per_unit'] ) : 0; if ( $price > 0 ) { $variation_id = $cart_item['data']->get_id(); if ( $variation_id ) { // Если есть старая цена (скидка), устанавливаем: // regular_price = старая цена (зачеркнутая) // sale_price = новая цена (финальная) if ( $price_old > 0 && $price_old > $price ) { update_post_meta( $variation_id, '_regular_price', $price_old ); update_post_meta( $variation_id, '_sale_price', $price ); update_post_meta( $variation_id, '_price', $price ); $cart_item['data']->set_regular_price( $price_old ); $cart_item['data']->set_sale_price( $price ); } else { // Нет скидки - обе цены одинаковые update_post_meta( $variation_id, '_regular_price', $price ); update_post_meta( $variation_id, '_sale_price', $price ); update_post_meta( $variation_id, '_price', $price ); $cart_item['data']->set_regular_price( $price ); $cart_item['data']->set_sale_price( $price ); } } // Устанавливаем финальную цену $cart_item['data']->set_price( $price ); } if ( $weight > 0 ) { $cart_item['data']->set_weight( $weight ); } } return $cart_item; } /** * Восстановление данных калькулятора из сессии */ add_filter( 'woocommerce_get_cart_item_from_session', 'orgsteklo_get_cart_item_from_session', 10, 3 ); function orgsteklo_get_cart_item_from_session( $cart_item, $values, $key ) { if ( isset( $values['orgsteklo_calculator'] ) ) { $cart_item['orgsteklo_calculator'] = $values['orgsteklo_calculator']; } return $cart_item; } /** * Устанавливаем цену и вес для вариаций с калькулятором при пересчете корзины */ add_action( 'woocommerce_before_calculate_totals', 'orgsteklo_set_cart_item_price', 10, 1 ); function orgsteklo_set_cart_item_price( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { return; } foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { if ( isset( $cart_item['orgsteklo_calculator'] ) && isset( $cart_item['data'] ) ) { $calc = $cart_item['orgsteklo_calculator']; // Округляем до целых для гарантии отсутствия копеек $price = isset( $calc['price_per_unit'] ) ? round(floatval( $calc['price_per_unit'] )) : 0; $price_old = isset( $calc['price_per_unit_old'] ) ? round(floatval( $calc['price_per_unit_old'] )) : 0; $weight = isset( $calc['weight_per_unit'] ) ? floatval( $calc['weight_per_unit'] ) : 0; if ( $price > 0 ) { $variation_id = $cart_item['data']->get_id(); if ( $variation_id ) { // Если есть старая цена (скидка), устанавливаем: // regular_price = старая цена (зачеркнутая) // sale_price = новая цена (финальная) if ( $price_old > 0 && $price_old > $price ) { update_post_meta( $variation_id, '_regular_price', $price_old ); update_post_meta( $variation_id, '_sale_price', $price ); update_post_meta( $variation_id, '_price', $price ); $cart_item['data']->set_regular_price( $price_old ); $cart_item['data']->set_sale_price( $price ); } else { // Нет скидки - обе цены одинаковые update_post_meta( $variation_id, '_regular_price', $price ); update_post_meta( $variation_id, '_sale_price', $price ); update_post_meta( $variation_id, '_price', $price ); $cart_item['data']->set_regular_price( $price ); $cart_item['data']->set_sale_price( $price ); } } // Устанавливаем финальную цену $cart_item['data']->set_price( $price ); } if ( $weight > 0 ) { $cart_item['data']->set_weight( $weight ); } } } } /** * Отображение данных калькулятора в корзине */ add_filter( 'woocommerce_get_item_data', 'orgsteklo_display_cart_item_data', 10, 2 ); function orgsteklo_display_cart_item_data( $item_data, $cart_item ) { if ( ! isset( $cart_item['orgsteklo_calculator'] ) ) { return $item_data; } $calc = $cart_item['orgsteklo_calculator']; if ( isset( $calc['thickness'] ) ) { $item_data[] = [ 'name' => 'Толщина', 'value' => $calc['thickness'] . ' мм', ]; } if ( isset( $calc['width'] ) && isset( $calc['length'] ) ) { $item_data[] = [ 'key' => 'Ширина', 'name' => 'Ширина', 'value' => $calc['width'] . ' мм', 'display' => $calc['width'] . ' мм', ]; $item_data[] = [ 'key' => 'Длина', 'name' => 'Длина', 'value' => $calc['length'] . ' мм', 'display' => $calc['length'] . ' мм', ]; } if ( isset( $calc['weight_per_unit'] ) ) { $item_data[] = [ 'name' => 'Вес 1 листа', 'value' => number_format( (float)$calc['weight_per_unit'], 3, '.', ' ' ) . ' кг', ]; } if ( isset( $calc['is_standard_size'] ) && ! $calc['is_standard_size'] ) { $item_data[] = [ 'name' => 'Тип размера', 'value' => 'Нестандартный', ]; } return $item_data; } /** * Вариации больше не нужны - товары стали simple */ /** * AJAX handler для добавления товара с калькулятором в корзину * Использует стандартный add_to_cart() с реальной вариацией */ add_action( 'wp_ajax_add_orgsteklo_to_cart', 'orgsteklo_ajax_add_to_cart' ); add_action( 'wp_ajax_nopriv_add_orgsteklo_to_cart', 'orgsteklo_ajax_add_to_cart' ); function orgsteklo_ajax_add_to_cart() { try { $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0; $quantity = isset( $_POST['quantity'] ) ? absint( $_POST['quantity'] ) : 1; $thickness = isset( $_POST['orgsteklo_thickness'] ) ? floatval( $_POST['orgsteklo_thickness'] ) : 0; $width = isset( $_POST['orgsteklo_width'] ) ? floatval( $_POST['orgsteklo_width'] ) : 0; $length = isset( $_POST['orgsteklo_length'] ) ? floatval( $_POST['orgsteklo_length'] ) : 0; if ( ! $product_id || ! $thickness || ! $width || ! $length ) { throw new Exception( 'Не указаны обязательные параметры' ); } // Получаем продукт $product = wc_get_product( $product_id ); if ( ! $product ) { throw new Exception( 'Товар не найден' ); } // Находим вариацию по толщине через наше поле $variations = get_posts( array( 'post_type' => 'product_variation', 'post_parent' => $product_id, 'numberposts' => -1, 'fields' => 'ids' ) ); $variation_id = 0; foreach ( $variations as $var_id ) { $var_thickness = get_post_meta( $var_id, '_orgsteklo_thickness', true ); if ( abs( floatval( $var_thickness ) - $thickness ) < 0.01 ) { $variation_id = $var_id; break; } } if ( ! $variation_id ) { throw new Exception( 'Не найдена вариация для толщины ' . $thickness . ' мм. Пересохраните товар в админке.' ); } // Получаем атрибуты вариации $variation_obj = wc_get_product( $variation_id ); $variation_attributes = $variation_obj ? $variation_obj->get_variation_attributes() : array(); // Рассчитываем цену из таблицы (НЕ из вариации!) $calc_result = orgsteklo_calculate_price( $product_id, $thickness, $width, $length, 1 ); if ( is_wp_error( $calc_result ) ) { throw new Exception( $calc_result->get_error_message() ); } // Применяем скидку $discount_percent = orgsteklo_get_discount_percent_from_tags( $product_id ); $price_current_sheet = $calc_result['price_current_sheet']; $price_current_sheet_old = 0; // Старая цена (до скидки) if ( $discount_percent > 0 ) { $price_current_sheet_old = $price_current_sheet; // Сохраняем старую цену $coef = (100 - $discount_percent) / 100; $price_current_sheet = round( $price_current_sheet * $coef ); } $is_standard_size = $calc_result['is_standard_size']; $weight_current_sheet = $calc_result['weight_current_sheet']; // Данные калькулятора для сохранения // Явно округляем цены до целых, чтобы избежать расхождений в копейках $calc_data = array( 'thickness' => $thickness, 'width' => $width, 'length' => $length, 'price_per_unit' => round($price_current_sheet), 'price_per_unit_old' => $price_current_sheet_old ? round($price_current_sheet_old) : null, 'weight_per_unit' => $weight_current_sheet, 'is_standard_size'=> $is_standard_size, ); // Добавляем в корзину через стандартный метод с найденной вариацией $cart_item_key = WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation_attributes, array( 'orgsteklo_calculator' => $calc_data ) ); if ( ! $cart_item_key ) { // Получаем ошибки WooCommerce $notices = wc_get_notices( 'error' ); $error_message = 'Не удалось добавить товар в корзину (variation_id: ' . $variation_id . ')'; if ( ! empty( $notices ) ) { $errors = array(); foreach ( $notices as $notice ) { $errors[] = is_array( $notice ) && isset( $notice['notice'] ) ? $notice['notice'] : $notice; } $error_message .= ': ' . implode( ', ', $errors ); } wc_clear_notices(); throw new Exception( $error_message ); } wp_send_json( array( 'success' => true, 'message' => 'Товар успешно добавлен в корзину', 'cart_item_count' => WC()->cart->get_cart_contents_count(), 'fragments' => apply_filters( 'woocommerce_add_to_cart_fragments', array() ), 'cart_hash' => WC()->cart->get_cart_hash() ) ); } catch ( Exception $e ) { wp_send_json_error( $e->getMessage() ); } } /** * Сохранение данных калькулятора в заказе */ add_action( 'woocommerce_checkout_create_order_line_item', 'orgsteklo_add_calculator_to_order', 10, 4 ); function orgsteklo_add_calculator_to_order( $item, $cart_item_key, $values, $order ) { if ( ! isset( $values['orgsteklo_calculator'] ) ) { return; } $calc = $values['orgsteklo_calculator']; if ( isset( $calc['thickness'] ) ) { $item->add_meta_data( 'Толщина', $calc['thickness'] . ' мм' ); } if ( isset( $calc['width'] ) && isset( $calc['length'] ) ) { $item->add_meta_data( 'Размер', $calc['width'] . ' × ' . $calc['length'] . ' мм' ); } if ( isset( $calc['weight_per_unit'] ) ) { $item->add_meta_data( 'Вес 1 листа', number_format( $calc['weight_per_unit'], 3, '.', '' ) . ' кг' ); } if ( isset( $calc['is_standard_size'] ) && ! $calc['is_standard_size'] ) { $item->add_meta_data( 'Тип', 'Нестандартный размер' ); } }