| Server IP : 104.171.130.249 / Your IP : 216.73.216.146 Web Server : nginx/1.25.5 System : Linux 0dac65491b7e 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : root ( 0) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/html/wp-content/themes/matreshka/inc/ |
Upload File : |
<?php
/**
* Кастомные методы доставки Matreshka
*
* Два метода:
* – Доставка (matreshka_delivery): 389 ₽ доставка + 49.99 ₽ упаковка
* – Самовывоз (matreshka_pickup): 49.99 ₽ упаковка, клиент выбирает магазин
*
* @package Matreshka
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/* ──────────────────────────────────────────────
* 1. Регистрация методов доставки
* ────────────────────────────────────────────── */
add_filter( 'woocommerce_shipping_methods', 'matreshka_register_shipping_methods' );
function matreshka_register_shipping_methods( $methods ) {
$methods['matreshka_delivery'] = 'WC_Matreshka_Delivery';
$methods['matreshka_pickup'] = 'WC_Matreshka_Pickup';
return $methods;
}
/* ──────────────────────────────────────────────
* 2. Классы методов доставки (init после WC)
* ────────────────────────────────────────────── */
add_action( 'woocommerce_shipping_init', 'matreshka_shipping_init' );
function matreshka_shipping_init() {
/* ── Доставка ─────────────────────────────── */
class WC_Matreshka_Delivery extends WC_Shipping_Method {
public function __construct( $instance_id = 0 ) {
$this->id = 'matreshka_delivery';
$this->instance_id = absint( $instance_id );
$this->method_title = 'Доставка Matreshka';
$this->method_description = 'Курьерская доставка: 389 ₽ + 49.99 ₽ упаковка';
$this->supports = array( 'shipping-zones', 'instance-settings' );
$this->enabled = 'yes';
$this->title = 'Доставка';
$this->init();
}
public function init() {
$this->init_form_fields();
$this->init_settings();
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init_form_fields() {
$this->instance_form_fields = array(
'delivery_cost' => array(
'title' => 'Стоимость доставки (₽)',
'type' => 'number',
'default' => 389,
),
'packaging_cost' => array(
'title' => 'Стоимость упаковки (₽)',
'type' => 'number',
'default' => 49.99,
'custom_attributes' => array( 'step' => '0.01' ),
),
);
}
public function calculate_shipping( $package = array() ) {
$delivery = floatval( $this->get_option( 'delivery_cost', 389 ) );
$packaging = floatval( $this->get_option( 'packaging_cost', 49.99 ) );
$this->add_rate( array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => $delivery + $packaging,
'meta_data' => array(
'delivery_cost' => $delivery,
'packaging_cost' => $packaging,
),
) );
}
}
/* ── Самовывоз ────────────────────────────── */
class WC_Matreshka_Pickup extends WC_Shipping_Method {
public function __construct( $instance_id = 0 ) {
$this->id = 'matreshka_pickup';
$this->instance_id = absint( $instance_id );
$this->method_title = 'Самовывоз Matreshka';
$this->method_description = 'Самовывоз: 49.99 ₽ упаковка, клиент выбирает магазин';
$this->supports = array( 'shipping-zones', 'instance-settings' );
$this->enabled = 'yes';
$this->title = 'Самовывоз';
$this->init();
}
public function init() {
$this->init_form_fields();
$this->init_settings();
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init_form_fields() {
// Собираем все магазины из matreshka_stores для мультиселекта
$all_stores = matreshka_get_all_stores();
$store_opts = array();
foreach ( $all_stores as $i => $store ) {
$store_opts[ $i ] = ( $store['name'] ?? '—' ) . ' — ' . ( $store['address'] ?? '' );
}
$this->instance_form_fields = array(
'packaging_cost' => array(
'title' => 'Стоимость упаковки (₽)',
'type' => 'number',
'default' => 49.99,
'custom_attributes' => array( 'step' => '0.01' ),
),
'pickup_stores' => array(
'title' => 'Доступные магазины для самовывоза',
'type' => 'multiselect',
'class' => 'wc-enhanced-select',
'description' => 'Выберите магазины из списка (источник: метабокс «Адреса магазинов» на страницах).',
'desc_tip' => true,
'options' => $store_opts,
'default' => array_keys( $store_opts ), // по умолчанию все
),
);
}
/**
* Получить индексы выбранных магазинов.
*/
public function get_enabled_store_indices() {
$saved = $this->get_option( 'pickup_stores', array() );
if ( empty( $saved ) ) {
// Если ничего не сохранено — все
return array_keys( matreshka_get_all_stores() );
}
return array_map( 'intval', (array) $saved );
}
public function calculate_shipping( $package = array() ) {
$packaging = floatval( $this->get_option( 'packaging_cost', 49.99 ) );
$this->add_rate( array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => $packaging,
'meta_data' => array(
'packaging_cost' => $packaging,
),
) );
}
}
}
/* ──────────────────────────────────────────────
* 3. Хелпер: получить все магазины из мета-полей страниц
* ────────────────────────────────────────────── */
function matreshka_get_all_stores() {
static $cache = null;
if ( $cache !== null ) {
return $cache;
}
$stores = array();
$pages = get_pages();
foreach ( $pages as $page ) {
$page_stores = get_post_meta( $page->ID, '_matreshka_stores', true );
if ( is_array( $page_stores ) && ! empty( $page_stores ) ) {
foreach ( $page_stores as $store ) {
if ( ! empty( $store['name'] ) ) {
$stores[] = $store;
}
}
}
}
$cache = $stores;
return $stores;
}
/**
* Получить только те магазины, которые включены для самовывоза
* в настройках метода Самовывоз Matreshka.
*/
function matreshka_get_pickup_stores() {
static $cache = null;
if ( $cache !== null ) {
return $cache;
}
$all = matreshka_get_all_stores();
if ( empty( $all ) ) {
$cache = array();
return $cache;
}
// Находим instance метода matreshka_pickup
$enabled_indices = null;
$zones = WC_Shipping_Zones::get_zones();
foreach ( $zones as $zone_data ) {
foreach ( $zone_data['shipping_methods'] as $m ) {
if ( $m->id === 'matreshka_pickup' ) {
$enabled_indices = $m->get_enabled_store_indices();
break 2;
}
}
}
// Проверяем Rest of World (zone 0)
if ( $enabled_indices === null ) {
$rest = WC_Shipping_Zones::get_zone( 0 );
foreach ( $rest->get_shipping_methods() as $m ) {
if ( $m->id === 'matreshka_pickup' ) {
$enabled_indices = $m->get_enabled_store_indices();
break;
}
}
}
if ( $enabled_indices === null ) {
$cache = $all;
return $cache;
}
$filtered = array();
foreach ( $enabled_indices as $idx ) {
if ( isset( $all[ $idx ] ) ) {
$filtered[ $idx ] = $all[ $idx ];
}
}
$cache = $filtered;
return $cache;
}
/* ──────────────────────────────────────────────
* 3b. Хелпер: получить стоимости доставки из настроек
* ────────────────────────────────────────────── */
function matreshka_get_shipping_costs() {
static $cache = null;
if ( $cache !== null ) {
return $cache;
}
$costs = array( 'delivery' => 389.0, 'packaging' => 49.99 );
$all_zones = WC_Shipping_Zones::get_zones();
$all_zones[] = array( 'shipping_methods' => WC_Shipping_Zones::get_zone( 0 )->get_shipping_methods() );
foreach ( $all_zones as $zone_data ) {
foreach ( $zone_data['shipping_methods'] as $m ) {
if ( $m->id === 'matreshka_delivery' ) {
$costs['delivery'] = floatval( $m->get_option( 'delivery_cost', 389 ) );
$costs['packaging'] = floatval( $m->get_option( 'packaging_cost', 49.99 ) );
break 2;
}
}
}
$cache = $costs;
return $costs;
}
/* ──────────────────────────────────────────────
* 4. AJAX: переключение метода доставки
* ────────────────────────────────────────────── */
add_action( 'wp_ajax_matreshka_set_shipping', 'matreshka_ajax_set_shipping' );
add_action( 'wp_ajax_nopriv_matreshka_set_shipping', 'matreshka_ajax_set_shipping' );
function matreshka_ajax_set_shipping() {
check_ajax_referer( 'matreshka_shipping_nonce', 'nonce' );
$method = sanitize_text_field( $_POST['method'] ?? '' );
if ( ! in_array( $method, array( 'delivery', 'pickup' ), true ) ) {
wp_send_json_error( array( 'message' => 'Invalid method' ) );
}
// 1. Save shipping type to session
WC()->session->set( 'matreshka_shipping_type', $method );
// 2. Save pickup store index
$store_index = 0;
if ( $method === 'pickup' ) {
$store_index = absint( $_POST['store_index'] ?? 0 );
$stores = array_values( matreshka_get_pickup_stores() );
if ( ! isset( $stores[ $store_index ] ) ) {
$store_index = 0;
}
WC()->session->set( 'matreshka_pickup_store', $store_index );
}
// 3. Persist to user meta (remember for future orders)
if ( is_user_logged_in() ) {
$uid = get_current_user_id();
update_user_meta( $uid, '_matreshka_shipping_type', $method );
if ( $method === 'pickup' ) {
update_user_meta( $uid, '_matreshka_pickup_store', $store_index );
}
}
// 4. Calculate shipping to populate available rates
$chosen_prefix = $method === 'pickup' ? 'matreshka_pickup' : 'matreshka_delivery';
WC()->cart->calculate_shipping();
$packages = WC()->shipping()->get_packages();
// 5. Find the actual rate id (e.g. matreshka_pickup:3)
$rate_id = $chosen_prefix;
foreach ( $packages as $package ) {
if ( ! empty( $package['rates'] ) ) {
foreach ( $package['rates'] as $rid => $rate ) {
if ( strpos( $rid, $chosen_prefix ) === 0 ) {
$rate_id = $rid;
break 2;
}
}
}
}
// 6. Set chosen method, then recalculate totals
WC()->session->set( 'chosen_shipping_methods', array( $rate_id ) );
WC()->cart->calculate_totals();
wp_send_json_success( array(
'method' => $method,
'rate_id' => $rate_id,
'total' => WC()->cart->get_total( 'edit' ),
'total_html' => WC()->cart->get_total(),
) );
}
/* ──────────────────────────────────────────────
* 5. Добавляем Зону доставки по умолчанию при активации
* (если методов ещё нет)
* ────────────────────────────────────────────── */
add_action( 'admin_init', 'matreshka_maybe_add_default_shipping_zone', 20 );
function matreshka_maybe_add_default_shipping_zone() {
if ( get_option( 'matreshka_shipping_zone_created' ) ) {
return;
}
// Проверяем, есть ли уже зоны с нашими методами
$zones = WC_Shipping_Zones::get_zones();
foreach ( $zones as $zone_data ) {
foreach ( $zone_data['shipping_methods'] as $m ) {
if ( in_array( $m->id, array( 'matreshka_delivery', 'matreshka_pickup' ), true ) ) {
update_option( 'matreshka_shipping_zone_created', 1 );
return;
}
}
}
// Также проверяем Rest of World (zone 0)
$rest = WC_Shipping_Zones::get_zone( 0 );
foreach ( $rest->get_shipping_methods() as $m ) {
if ( in_array( $m->id, array( 'matreshka_delivery', 'matreshka_pickup' ), true ) ) {
update_option( 'matreshka_shipping_zone_created', 1 );
return;
}
}
// Создаём зону «Все регионы» и добавляем оба метода
$zone = new WC_Shipping_Zone();
$zone->set_zone_name( 'Все регионы' );
$zone->save();
$zone->add_shipping_method( 'matreshka_delivery' );
$zone->add_shipping_method( 'matreshka_pickup' );
update_option( 'matreshka_shipping_zone_created', 1 );
}
/* ──────────────────────────────────────────────
* 6. Метод по умолчанию: Доставка
* ────────────────────────────────────────────── */
add_filter( 'woocommerce_shipping_chosen_method', 'matreshka_default_shipping_method', 10, 3 );
function matreshka_default_shipping_method( $default, $rates, $chosen_method ) {
// 1. Check session for user's current choice
$session_type = WC()->session ? WC()->session->get( 'matreshka_shipping_type' ) : null;
// 2. If no session — try user meta (persisted from previous orders)
if ( ! $session_type && is_user_logged_in() ) {
$saved = get_user_meta( get_current_user_id(), '_matreshka_shipping_type', true );
if ( $saved ) {
$session_type = $saved;
WC()->session->set( 'matreshka_shipping_type', $saved );
if ( $saved === 'pickup' ) {
$store = get_user_meta( get_current_user_id(), '_matreshka_pickup_store', true );
WC()->session->set( 'matreshka_pickup_store', absint( $store ) );
}
}
}
// 3. Return the rate matching user's choice
if ( $session_type ) {
$prefix = $session_type === 'pickup' ? 'matreshka_pickup' : 'matreshka_delivery';
foreach ( $rates as $rate_id => $rate ) {
if ( strpos( $rate_id, $prefix ) === 0 ) {
return $rate_id;
}
}
}
// 4. Default — Доставка
foreach ( $rates as $rate_id => $rate ) {
if ( strpos( $rate_id, 'matreshka_delivery' ) === 0 ) {
return $rate_id;
}
}
return $default;
}
/* ──────────────────────────────────────────────
* 7. Сохраняем данные самовывоза (магазин) в заказе
* ────────────────────────────────────────────── */
add_action( 'woocommerce_checkout_create_order', 'matreshka_save_shipping_to_order', 20, 2 );
function matreshka_save_shipping_to_order( $order, $data ) {
$type = WC()->session->get( 'matreshka_shipping_type', 'delivery' );
$order->update_meta_data( '_matreshka_shipping_type', $type );
if ( $type === 'pickup' ) {
$store_index = WC()->session->get( 'matreshka_pickup_store', 0 );
$stores = array_values( matreshka_get_pickup_stores() );
if ( isset( $stores[ $store_index ] ) ) {
$store = $stores[ $store_index ];
$order->update_meta_data( '_matreshka_pickup_store_name', $store['name'] ?? '' );
$order->update_meta_data( '_matreshka_pickup_store_address', $store['address'] ?? '' );
$order->update_meta_data( '_matreshka_pickup_store_phone', $store['phone'] ?? '' );
$order->update_meta_data( '_matreshka_pickup_store_hours', $store['hours'] ?? '' );
}
}
}
/* ──────────────────────────────────────────────
* 8. Показываем инфо о доставке в админке заказа
* ────────────────────────────────────────────── */
/* Old shipping admin display removed — now in inc/admin-order-metaboxes.php */
/* Old pickup thankyou display removed — now in woocommerce/checkout/thankyou.php */
/* ──────────────────────────────────────────────
* 10. Скрываем стандартный WC калькулятор доставки
* ────────────────────────────────────────────── */
add_filter( 'woocommerce_shipping_show_shipping_calculator', '__return_false' );
/* ──────────────────────────────────────────────
* 11. Подключаем скрипт и стили на корзину/чекаут
* ────────────────────────────────────────────── */
add_action( 'wp_enqueue_scripts', 'matreshka_shipping_assets' );
function matreshka_shipping_assets() {
if ( ! is_cart() && ! is_checkout() ) {
return;
}
wp_enqueue_style(
'matreshka-shipping',
get_template_directory_uri() . '/assets/css/shipping.css',
array(),
filemtime( get_template_directory() . '/assets/css/shipping.css' )
);
wp_enqueue_script(
'matreshka-shipping',
get_template_directory_uri() . '/assets/js/shipping.js',
array( 'jquery' ),
filemtime( get_template_directory() . '/assets/js/shipping.js' ),
true
);
$costs = function_exists( 'matreshka_get_shipping_costs' ) ? matreshka_get_shipping_costs() : array( 'delivery' => 389, 'packaging' => 49.99 );
wp_localize_script( 'matreshka-shipping', 'matreshkaShipping', array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'matreshka_shipping_nonce' ),
'stores' => array_values( matreshka_get_pickup_stores() ),
'current' => WC()->session ? WC()->session->get( 'matreshka_shipping_type', 'delivery' ) : 'delivery',
'currentStore' => WC()->session ? WC()->session->get( 'matreshka_pickup_store', 0 ) : 0,
'costs' => $costs,
) );
}