Commerce Shipping. Получить адрес доставки в функции расчёта стоимости

Главные вкладки

Аватар пользователя zheka2 zheka2 10 октября 2016 в 11:47

Здравствуйте

Пишу модуль для расчёта стоимости доставки
В магазине под который он пишется оформление заказа осуществляется на одной странице. Т.е. на ней и корзина, и информация о пользователе, об адресе доставки, о способе доставки и пр.

Как я понял в готовых модулях (смотрел Commerce EMS) для расчёта стоимости доставки надо указывать дополнительные параметры (город, объявленная стоимость и пр)
В моём случае всё это не нужно, и для расчёта я хочу использовать уже введённую информацию об адресе доставки, чтобы пользователю не приходилось её вводить дважды. Скрин ниже (город будет выбираться из списка, чтобы не было ошибок)

Но я не пойму как в callback'е расчёта стоимости получить введённый пользователем адрес
function custom_service_rate($shipping_service, $order)

доступа к форме нет

Как вариант использовать
function hook_form_commerce_checkout_form_checkout_alter(&$form, &$form_state)

И сохранять в сессии, но как-то костыльно. Подскажите пожалуйста может есть нормальный вариант?

ВложениеРазмер
Иконка изображения snimok.png35.94 КБ

Комментарии

Аватар пользователя zheka2 zheka2 10 октября 2016 в 12:12

dgastudio Спасибо за ответ
Суть понял, а то что все поля находятся на одной форме не проблема? Просто проверить смогу только вечером
Я к тому что допустим я ввёл адрес, выбрал доставку, вызвался колбэк расчёта и в $order уже будут содержаться введённые данные?

Аватар пользователя zheka2 zheka2 10 октября 2016 в 22:15

"если в панели доставки включен аякс рассчет" искал но не нашёл где включать

Вообще при смене адреса форма выполняет аякс запрос
НО в функции расчёта стоимости в параметре order введённых данных нет

Нашёл на drupal.org большое обсуждение как я понял аналогичной проблемы
https://www.drupal.org/node/1287124
Применил последний патч из 75 поста. В итоге частично заработало, в callback расчёта действительно стали приходить актуальные данные. Но на фронтэнде всё работает через Ж. В добавок если после введения адрес обновить страницу заказ вообще пропадает, т.е. пользователя перебрасывает на главную страницу. Я так понимаю связано с программной отправкой формы при изменении адреса

В итоге
Плюнул, полностью отключил весь стандартный ajax функционал
К полям Регион, Город и Способ доставки привязал ajax. При их изменении сохраняю заказ и вручную вызываю процедуру пересчёта стоимости доставки
Всё просто, я б даже сказал примитивно, но работает куда лучше того что было. Как доделаю покажу/прокомментирую результат, может кому пригодится

Аватар пользователя zheka2 zheka2 1 ноября 2016 в 12:12

Описываю конечный вариант. Некоторые места может сделал неправильно, буду благодарен за замечания
Как всё работает можно глянуть тут https://www.kir-sport.ru/node/9 . Нажмите В корзину , дальше будет понятно

<?php
function mymodule_form_alter(&$form, &$form_state$form_id)
{

    if (

$form_id == 'commerce_checkout_form_checkout') {
        
mymodule_form_checkout_alter_markups($form$form_state);

        

$attributes = array('class' => array('recalc-field'));

        

// отключаем весь стандартный ajax + в hook_js_alter отключаем commerce-shipping.js
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['administrative_area']['#ajax'] = array();
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality']['#ajax'] = array();
        
$form['commerce_shipping']['shipping_service']['#ajax'] = array();

        

// для элементов которые должны вызывать пересчёт добавляем класс .recalc-field
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['administrative_area']['#attributes'] = $attributes;
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality']['#attributes'] = $attributes;
        
$form['commerce_shipping']['shipping_service']['#attributes'] = $attributes;

        

// добавляем кнопку которая будет "нажиматься" при изменении полей и вызывать пересчёт доставки
        // убираем проверку всех полей (limit_validation_errors), иначе возникает ошибка незаполненности поля
        
$form['save-btn'] = array(
            
'#type' => 'submit',
            
'#value' => "",
            
'#attributes' => array('class' => array('recalc-btn')),
            
'#ajax' => array(
                
'callback' => 'mymodule_addresschange_callback',
                
'wrapper' => 'checkout-form-wrapper',
            ),
            
'#limit_validation_errors' => array(
                array(
'commerce_shipping''shipping_service'),
            ),
            
'#submit' => array('mymodule_checkout_form_submit'),
            
'#executes_submit_callback' => true,

        );

        

// добавляем вызов пересчёта доставки при изменении количества в форме товаров
        
$form['cart_contents_form']['cart_contents_form_view']['actions']['submit']['#submit'][] = 'mymodule_checkout_form_submit';
        
$form['cart_contents_form']['cart_contents_form_view']['actions']['submit']['#limit_validation_errors'][] = array('commerce_shipping''shipping_service');

        

// заполняем список регионов
        // если регион не указан на форме то берём из данных #address в форме (данные в этот параметр попадают из сохранённого заказа)
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['administrative_area']['#options'] = mymodule_get_regions();

        

$current_region = @$form_state['values']['customer_profile_shipping']['commerce_customer_address']['und'][0]['administrative_area'];
        if (empty(
$current_region)) {
            
$current_region = @$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['#address']['administrative_area'];
        }

        

// для поля Город меняем тип на список и заполняем одним пустым пунктом
        
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality'] += array(
            
'#type' => 'select',
            
'#options' => array(''),
            
'#default_value' => 0,
        );

        

// если указан регион то заполняем список городов по нему
        // если город был указан в форме то подставляем его, если нет то берём из #address (содрежит сохранённое в заказе значение),
        // если и там нет то берём первый город по списку (первымм идут центры областей)
        // полученное значение подставляем в #value, т.е. #default_value используется только при первом построении
        
if (!empty($current_region)) {

            

$options mymodule_get_cities($current_region);

            

$current_city = @$form_state['values']['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality'];
            if (empty(
$current_city)) {
                
$current_city $form['customer_profile_shipping']['commerce_customer_address']['und'][0]['#address']['locality'];
            }

            if (!isset(

$options[$current_city])) {
                
$current_city reset($options);
            }

            

$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality']['#options'] = &$options;
            if (!isset(
$form_state['values']['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality'])) {
               
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality']['#value'] = $current_city;
            }

        }
    }
    
}

function 

mymodule_checkout_form_submit($form, &$form_state) {
    
$commerce_order entity_metadata_wrapper('commerce_order'$form_state['order']->order_id);

    

// пытаемся загрузить профиль из заказа, если его нет то создаём новый
    // не нашёл способ как проверить заполненность profile_id
    
try {
        
$profile_id $commerce_order->commerce_customer_shipping->profile_id->value();
        
$profile commerce_customer_profile_load($profile_id);
        
$wrapper entity_metadata_wrapper('commerce_customer_profile'$profile);
    }
    catch (
Exception $e){
        
$new_profile commerce_customer_profile_new('shipping');
        
$wrapper entity_metadata_wrapper('commerce_customer_profile'$new_profile);
        global 
$user;
        
$wrapper->uid $user->uid;
    }

    

// получаем переданные из формы параметры, берём их из input, т.к. мы отключили проверку данных
    
$commerce_customer_address $form_state['input']['customer_profile_shipping']['commerce_customer_address']['und'][0];
    
$administrative_area $commerce_customer_address['administrative_area'];
    if (!isset(
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['administrative_area']['#options'][$administrative_area])) {
        
$administrative_area '';
        
$current_city '';
    }
    else {
        
$options mymodule_get_cities($administrative_area);
        
$current_city $commerce_customer_address['locality'];
        if (!isset(
$options[$current_city])) {
            
$current_city reset($options);
        }
    }

    

// сохраняем введённый адрес для возможности дальнейшего расчёта в хуках commerce-shipping
    
$wrapper->commerce_customer_address->country 'RU';//$commerce_customer_address['country'];
    
$wrapper->commerce_customer_address->administrative_area $administrative_area;
    
$wrapper->commerce_customer_address->locality $current_city;

    

$wrapper->save();

    

$commerce_order->commerce_customer_shipping $wrapper->getIdentifier();
    
$commerce_order->save();

    

// заменяем загруженный заказ в form_state на обновлённый, т.к. ранее загруженный содержит неактуальные сведения
    // по этому причине неправильно рассчитывается стоимость доставки
    
$tmp entity_load('commerce_order', array($form_state['order']->order_id));
    
$form_state['order'] = reset($tmp);
    
$form_state['build_info']['args'][0] = reset($tmp);

    

// добавляем доставку в line_items заказа
    // при добавлении нового товара, а также удаления
    
mymodule_add_shipping_line_item($form_state['order'], $form_state['values']['commerce_shipping']['shipping_service']);

    

// перестраиваем форму чтобы перезаполнить города
    
$form_state['rebuild'] = true;
}

function 

mymodule_add_shipping_line_item($order$service_name NULL) {
    
// рассчитываем стоимость доставки
    
    
if (isset($service_name/*&& !isset($order->shipping_rates[$service_name])*/ ) {
        
// Make the chosen service available to the order.
        
commerce_shipping_service_rate_order($service_name$order);
    }
    elseif (!isset(
$service_name)) {
        throw new 
Exception("Not valid service name");
    }

    

// Delete any existing shipping line items from the order.
    
commerce_shipping_delete_shipping_line_items($orderTRUE);

    

// Extract the unit price from the calculated rate.
    
$rate_line_item $order->shipping_rates[$service_name];
    
$rate_line_item_wrapper entity_metadata_wrapper('commerce_line_item'$rate_line_item);
    
$unit_price $rate_line_item_wrapper->commerce_unit_price->value();

    

// Create a new shipping line item with the calculated rate from the form.
    
$line_item commerce_shipping_line_item_new($service_name$unit_price$order->order_id$rate_line_item->data$rate_line_item->type);

    

// Save and add the line item to the order.
    
$new_line_item commerce_shipping_add_shipping_line_item($line_item$orderTRUE);
    
commerce_order_save($order);
}

?>