Orgsteklo/wp-content/themes/orgsteklo/inc/price-calculator-functions.php
Jamil-Abdullayev b8246597d4 Initial commit: orgsteklo WordPress theme
Custom WooCommerce theme for orgsteklo.ru including:
- Product catalog with category/subcategory hierarchy
- Custom checkout with delivery calculation
- Price calculator
- Admin settings panel
- Search functionality
- User account pages
2026-03-05 00:48:06 +04:00

1020 lines
41 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Функции для расчета цен на оргстекло
* Реализует Алгоритм 1 (стандартные размеры) и Алгоритм 2 (заданные размеры)
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// ========== ЗАЩИТА ОТ БИТЫХ ЭЛЕМЕНТОВ КОРЗИНЫ ==========
// Удаляем битые элементы при загрузке корзины из сессии
add_action( 'woocommerce_cart_loaded_from_session', 'orgsteklo_remove_broken_items', 999 );
function orgsteklo_remove_broken_items( $cart ) {
$to_remove = array();
foreach ( $cart->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( 'Тип', 'Нестандартный размер' );
}
}