Здравствуйте
Пишу модуль для расчёта стоимости доставки
В магазине под который он пишется оформление заказа осуществляется на одной странице. Т.е. на ней и корзина, и информация о пользователе, об адресе доставки, о способе доставки и пр.
Как я понял в готовых модулях (смотрел Commerce EMS) для расчёта стоимости доставки надо указывать дополнительные параметры (город, объявленная стоимость и пр)
В моём случае всё это не нужно, и для расчёта я хочу использовать уже введённую информацию об адресе доставки, чтобы пользователю не приходилось её вводить дважды. Скрин ниже (город будет выбираться из списка, чтобы не было ошибок)
Но я не пойму как в callback'е расчёта стоимости получить введённый пользователем адрес
function custom_service_rate($shipping_service, $order)
доступа к форме нет
Как вариант использовать
function hook_form_commerce_checkout_form_checkout_alter(&$form, &$form_state)
И сохранять в сессии, но как-то костыльно. Подскажите пожалуйста может есть нормальный вариант?
Вложение | Размер |
---|---|
![]() | 35.94 КБ |
Комментарии
http://c2n.me/3D6PSkq.jpg
dgastudio Спасибо за ответ
Суть понял, а то что все поля находятся на одной форме не проблема? Просто проверить смогу только вечером
Я к тому что допустим я ввёл адрес, выбрал доставку, вызвался колбэк расчёта и в $order уже будут содержаться введённые данные?
если в панели доставки включен аякс рассчет,да
Спасибо
Попробую отпишусь
"если в панели доставки включен аякс рассчет" искал но не нашёл где включать
Вообще при смене адреса форма выполняет аякс запрос
НО в функции расчёта стоимости в параметре order введённых данных нет
Нашёл на drupal.org большое обсуждение как я понял аналогичной проблемы
https://www.drupal.org/node/1287124
Применил последний патч из 75 поста. В итоге частично заработало, в callback расчёта действительно стали приходить актуальные данные. Но на фронтэнде всё работает через Ж. В добавок если после введения адрес обновить страницу заказ вообще пропадает, т.е. пользователя перебрасывает на главную страницу. Я так понимаю связано с программной отправкой формы при изменении адреса
В итоге
Плюнул, полностью отключил весь стандартный ajax функционал
К полям Регион, Город и Способ доставки привязал ajax. При их изменении сохраняю заказ и вручную вызываю процедуру пересчёта стоимости доставки
Всё просто, я б даже сказал примитивно, но работает куда лучше того что было. Как доделаю покажу/прокомментирую результат, может кому пригодится
Описываю конечный вариант. Некоторые места может сделал неправильно, буду благодарен за замечания
Как всё работает можно глянуть тут https://www.kir-sport.ru/node/9 . Нажмите В корзину , дальше будет понятно
<?php
$attributes = array('class' => array('recalc-field'));
// отключаем весь стандартный ajax + в hook_js_alter отключаем commerce-shipping.js
// для элементов которые должны вызывать пересчёт добавляем класс .recalc-field
// добавляем кнопку которая будет "нажиматься" при изменении полей и вызывать пересчёт доставки
// добавляем вызов пересчёта доставки при изменении количества в форме товаров
// заполняем список регионов
$current_region = @$form_state['values']['customer_profile_shipping']['commerce_customer_address']['und'][0]['administrative_area'];
// для поля Город меняем тип на список и заполняем одним пустым пунктом
// если указан регион то заполняем список городов по нему
$options = mymodule_get_cities($current_region);
$current_city = @$form_state['values']['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality'];
$form['customer_profile_shipping']['commerce_customer_address']['und'][0]['locality_block']['locality']['#options'] = &$options;
// пытаемся загрузить профиль из заказа, если его нет то создаём новый
// получаем переданные из формы параметры, берём их из input, т.к. мы отключили проверку данных
// сохраняем введённый адрес для возможности дальнейшего расчёта в хуках commerce-shipping
$wrapper->save();
$commerce_order->commerce_customer_shipping = $wrapper->getIdentifier();
// заменяем загруженный заказ в form_state на обновлённый, т.к. ранее загруженный содержит неактуальные сведения
// добавляем доставку в line_items заказа
// перестраиваем форму чтобы перезаполнить города
// Delete any existing shipping line items from the order.
// Extract the unit price from the calculated rate.
// Create a new shipping line item with the calculated rate from the form.
// Save and add the line item to the order.
function mymodule_form_alter(&$form, &$form_state, $form_id)
{
if (
$form_id == 'commerce_checkout_form_checkout') {mymodule_form_checkout_alter_markups($form, $form_state);
$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();
$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();
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)) {
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);
}
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;
}
$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);
}
}
$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;
$commerce_order->save();
// по этому причине неправильно рассчитывается стоимость доставки
$tmp = entity_load('commerce_order', array($form_state['order']->order_id));
$form_state['order'] = reset($tmp);
$form_state['build_info']['args'][0] = reset($tmp);
// при добавлении нового товара, а также удаления
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");
}
commerce_shipping_delete_shipping_line_items($order, TRUE);
$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();
$line_item = commerce_shipping_line_item_new($service_name, $unit_price, $order->order_id, $rate_line_item->data, $rate_line_item->type);
$new_line_item = commerce_shipping_add_shipping_line_item($line_item, $order, TRUE);
commerce_order_save($order);
} ?>
Ну нормально работает.