Страница оформления плагина woocommerce имеет определенную структуру и функционал. Но данная структура не совсем удобна. Доработаем страницу оформления, разбив на логические блоки: Заказ, Доставка, Форма оплаты, Информация о заказчике и т.д.
Оформление заказа (базовая структура)
Шаблон form-checkout.php
form name=»checkout»
woocommerce_checkout_billing (информация о заказчике)
woocommerce_checkout_shipping (информация о доставке)
h3 Заказ /h3
div id=»order_review» Order-review /div
/form
Шаблон review-order.php (Order-review)
table
позиции товара к покупке
итого
shipping (cart-shipping.php, ajax зависимость от введенной в woocommerce_checkout_billing информации, подробнее про доставку)
/table
div id=»payment» Форма оплаты /div (подключена хуком)
Кнопка Оформить заказ
Подготовил более наглядную схему за какую область оформления отвечает какой шаблон Woocommerce
Блок «Выбор способа доставки»
Шаблон который отвечает за вывод блока с выбором вариантов доставки находиться (почему-то) здесь: cart/cart-shipping.php.
Надпись Доставка, которая изначально там присутствует выводиться этой строкой:
echo wp_kses_post( $package_name );
Добавить заголовок «Выберите способ доставки» и общий вес заказа непосредственно в блок с доставкой, можно хуком
function action_woocommerce_review_order_before_shipping( ) {
echo '<h3>Выбор способа доставки</h3>';
global $woocommerce;
echo '<small class="total-weight">Общий вес: ';
$total_weight = $woocommerce->cart->cart_contents_weight;
$total_weight .= ' '.get_option('woocommerce_weight_unit');
echo $total_weight;
echo '</small>';
};
add_action( 'woocommerce_review_order_before_shipping', 'action_woocommerce_review_order_before_shipping', 10, 0 );
Дорабатываем элементы
Если мы не используем функционал расчета доставки и применения купонов — можно отключить ajax обновление. Если отключать ajax не нужно — ниже есть способ без отключения.
// Отключить Ajax на странице оформления
jQuery(document.body).on('update_checkout', function(e){
//e.preventDefault();
//e.stopPropagation();
e.stopImmediatePropagation();
//console.log(e);
});
Переместить блок Формы оплаты
remove_action( 'woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20 );
add_action( 'woocommerce_after_order_notes', 'woocommerce_checkout_payment', 20 );
либо подключить к собственному хуку
Отключение расчета доставки
Если доставка довольно сложна и невозможно прописать ее в автоматическом режиме (например: если мы не сможем реально подсчитать какая нужна газель), то мы отключаем расчет доставки: просто оставляем в методах доставки Самовывоз и Доставка (с 0 ценой). При этом из формы информации о заказчике удаляем поля относящиеся доставки и наоборот из формы доставки удаляем поля касающиеся заказчика. И создаем скрипт при нажатии на пункт Доставка раскрывающий форму доставки.
#shipping_method_0_flat_rate-6 — id поля с пунктом Доставка
$(document).ready(function() {
if($("<strong>#shipping_method_0_flat_rate-6</strong>").is(':checked')) { $('.shipping_address').show(); }
$('.shipping_method').click(function(){
if($("<strong>#shipping_method_0_flat_rate-6</strong>").is(':checked')) {
$('.shipping_address').show();
$('.shipping_address').html('<h3>Информация о доставке</h3><p class="form-row form-row form-row-wide address-field validate-required" id="shipping_address_1_field"><label for="shipping_address_1" class="">Адрес доставки <abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " name="shipping_address_1" id="shipping_address_1" placeholder="Адрес доставки (улица, дом, подъезд)*" autocomplete="address-line1" value=""></p><p class="form-row form-row form-row-wide address-field validate-required " id="shipping_city_field"><label for="shipping_city" class="">Населенный пункт <abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " name="shipping_city" id="shipping_city" placeholder="Населенный пункт*" autocomplete="address-level2" value=""></p><p class="form-row form-row form-row-first address-field validate-required validate-state" id="shipping_state_field"><label for="shipping_state" class="">Область/регион <abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " value="" placeholder="Область/Регион/Район" autocomplete="address-level1" name="shipping_state" id="shipping_state"></p>');
} else {
$('.shipping_address').html('<p class="form-row form-row form-row-wide address-field validate-required woocommerce-validated" id="shipping_address_1_field"><label for="shipping_address_1" class=""><abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " name="shipping_address_1" id="shipping_address_1" placeholder="Адрес доставки (улица, дом, подъезд)*" autocomplete="address-line1" value=" "></p><p class="form-row form-row form-row-wide address-field validate-required woocommerce-validated" id="shipping_city_field"><label for="shipping_city" class="">Населенный пункт <abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " name="shipping_city" id="shipping_city" placeholder="Населенный пункт*" autocomplete="address-level2" value=" "></p><p class="form-row form-row form-row-first address-field validate-required validate-state" id="shipping_state_field"><label for="shipping_state" class="">Область/регион <abbr class="required" title="обязательно">*</abbr></label><input type="text" class="input-text " value=" " placeholder="Область/Регион/Район" autocomplete="address-level1" name="shipping_state" id="shipping_state"></p>');
$('.shipping_address').hide();
}
});
});
Взаимосвязь варианта доставки и полей
Метод для 2-х вариантов доставки (для 3-х и более не подойдет)
Если у нас есть варианты доставки и самовывоз, то было бы неплохо синхронизировать вариант доставки и предлагаемые для заполнения поля. При самовывозе необходимо чтобы поля доставки скрывались, а при выборе варианта доставки появлялись.
Проблема woocommerce проявляется в том, что форма Детали доставки раскрывается при нажатии заголовка Доставка по другому адресу? (#ship-to-different-address) и вместе с сокрытием поля отключается верификация полей доставки. При двух вариантах доставки (Самовывоз и Доставка) при их переключении можно поставить с помощью jquery событие (click) на id ship-to-different-address. При этом необходимо сделать, чтоб одно из полей изначально было активным, в зависимости от необходимости.
$('#shipping_method_0_flat_rate-2').attr('checked',true);
$('input:radio[name="shipping_method[0]"]').on('change', function () {
$("#ship-to-different-address-checkbox").click();
});
Для того чтобы поля доставки были изначально открытыми необходимо установить настройку доставки в опциях woocommerce
Назначение доставки > По умолчанию для адреса доставки клиента
$(«#shipping_city»).val(«Ростов-на-Дону»);
Метод для 3-х и более вариантов доставки
Изначально делаем доставку (один из вариантов) и поля доставки активными. Далее jquery-магия
$('input:radio[name="shipping_method[0]"]').on('change', function () {
if($("#shipping_method_0_local_pickup-2").is(":checked")) {
if($("#ship-to-different-address-checkbox").is(":checked")) {
$("#ship-to-different-address-checkbox").click();
}
else {
}
}
else {
if($("#ship-to-different-address-checkbox").is(":checked")) {
}
else {
$("#ship-to-different-address-checkbox").click();
}
}
});
В этом случае мы проверяем активно ли поле Самовывоз (#shipping_method_0_local_pickup-2) и в зависимости от этого проверяем скрытый (я его скрываю) checkbox (Доставка по другому адресу? #ship-to-different-address).
Без отключения ajax
Ajax на странице оформления заказа так или иначе нужен (для тех же купонов). Поэтому немного изменим (изменения коснулись первой строки, т.к. элементы DOM были еще не созданы) универсальный скрипт отключения доставочных полей. #shipping_method_0_local_pickup-3 — id поля с самовывозом.
$('body').on('change', 'input:radio[name="shipping_method[0]"]', function(e) {
if($("#shipping_method_0_local_pickup-3").is(":checked")) {
if($("#ship-to-different-address-checkbox").is(":checked")) {
$("#ship-to-different-address-checkbox").click();
}
else {
}
}
else {
if($("#ship-to-different-address-checkbox").is(":checked")) {
}
else {
$("#ship-to-different-address-checkbox").click();
}
}
});
Два пункта самовывоза
jQuery('body').on('change', 'input:radio[name="shipping_method[0]"]', function() {
if(jQuery('#shipping_method_0_local_pickup-1').is(':checked')) {
if(jQuery('#ship-to-different-address-checkbox').is(':checked')) {
jQuery('#ship-to-different-address-checkbox').click();
}
} else if(jQuery('#shipping_method_0_local_pickup-2').is(':checked')) {
if(jQuery('#ship-to-different-address-checkbox').is(':checked')) {
jQuery('#ship-to-different-address-checkbox').click();
}
} else {
if(jQuery('#ship-to-different-address-checkbox').is(':checked')) {
} else {
jQuery('#ship-to-different-address-checkbox').click();
}
}
});
И на всякий случаем включаем изначальный выбор на какую-либо доставку
$('#shipping_method_0_flat_rate-4').attr('checked',true);
Отключение платежного адреса при Самовывозе
Данный способ сделан примитивно, на чистом jQuery, но прекрасно подойдет для простого отключения обязательного поля адрес (в блоке данные плательщика) при выборе варианта доставки Самовывоз.
jQuery('body').on('change', 'input:radio[name="shipping_method[0]"]', function() {
var address = $('#billing_address_1_field input').val();
setTimeout(function(){
if( jQuery('#shipping_method_0_local_pickup-10').is(':checked') ) {
$('#billing_address_1_field').hide();
$('#billing_city_field').hide();
if(address === ''){
$('#billing_address_1_field input').val('_');
};
} else {
$('#billing_address_1_field').show();
$('#billing_city_field').show();
if(address === '_'){
$('#billing_address_1_field input').val('');
};
}
}, 800);
});
Изначально (при загрузке страницы) должен быть выбран любой другой вариант доставки, кроме Самовывоза.
Также добавил в это решение и отключение города доставки
jQuery('body').on('change', 'input:radio[name="shipping_method[0]"]', function() {
var address = $('#billing_address_1_field input').val();
var city = $('#billing_city_field input').val();
setTimeout(function(){
if( jQuery('#shipping_method_0_local_pickup-10').is(':checked') ) {
$('#billing_address_1_field').hide();
$('#billing_city_field').hide();
if(address === ''){
$('#billing_address_1_field input').val('_');
};
if(city === ''){
$('#billing_city_field input').val('_');
};
} else {
$('#billing_address_1_field').show();
$('#billing_city_field').show();
if(address === '_'){
$('#billing_address_1_field input').val('');
};
if(city === '_'){
$('#billing_city_field input').val('');
};
}
}, 800);
});
Варианты оформления для физ. лица и юр. лица
Задача сделать 2 варианта оформления заказа для физ. лица и для юр. лица. Создание этого функционала описано в статье Юр. или физ. лицо при оформлении
Верификация телефона (ajax)
Если телефон введен не корректно, то при нажатии кнопки Оформить заказ выведется уведомление об этом. Но при этом если в поле ввести символы вместо цифр, то полю ajax добавится значение woocommerce-validated, т.е. как бы прошедшее валидацию.
Чтобы это исправить нужно в файл woocommerce/assets/js/frontend/checkout.js (причем на сайт загружается checkout.min.js) добавить внутри функции validate_field: function( e ) (примерно 200 строка) следующую проверку:
if ( $parent.is( '.validate-phone' ) ) {
if ( $this.val() ) {
var pattern = new RegExp(/^([0-9\s\/\+\-\#\_\(\)]*)$/);
if ( ! pattern.test( $this.val() ) ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-phone' );
validated = false;
}
}
}
[warning]Но при следующем обновлении WC данная проверка сотрется[/warning]
Поэтому данную функцию лучше прописать в своем файле скриптов:
jQuery('body').on('change', '.validate-phone input', function() {
var pattern = new RegExp(/^([0-9\s\/\+\-\#\_\(\)]*)$/);
if ( ! pattern.test( $('.validate-phone input').val() ) ) {
$('.validate-phone').removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-phone woocommerce-invalid-required-field' );
}
});
Но у данного метода есть небольшой изъян, при нажатии кнопки Оформить заказ (#place_order) полю, даже если оно заполнено не корректно добавляется класс woocommerce-validated. И даже при попытке привязать функцию к триггерам
jQuery(document).on( 'update_checkout', function(){ });
jQuery(document).on( 'init_checkout', function(){ });
ничего не выходит. Происходит это потому что в файле checkout.js в конце валидации есть функция проверяющая у полей параметр validated, который принимает значения внутри функции validate_field и вот перебить эту проверку внешним файлом мы не можем.
Вот доп. функция которая решает (несколько примитивно) данный вопрос:
jQuery('body').on( 'click','#place_order', function(){
var pattern = new RegExp(/^([0-9\s\/\+\-\#\_\(\)]*)$/);
if ( ! pattern.test( $('.validate-phone input').val() ) ) {
setTimeout(function(){
$('.validate-phone').removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-phone woocommerce-invalid-required-field' );
}, 1000);
}
});
Дополнительные условия магазина
Можно добавить дополнительные условия магазина, например что пользователю есть 18 лет, либо пользователь соглашается с условиями доставки и т.д.
// Add Checkboxes
add_action('woocommerce_after_checkout_billing_form', 'my_required_checkout_fields');
function my_required_checkout_fields()
{
woocommerce_form_field('age_confirm', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('I confirm that I am 21 years old or over.'),
'required' => true,
), WC()->checkout->get_value('age_confirm'));
woocommerce_form_field('tos_agreement', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('I agree to the terms of use of this website.'),
'required' => true,
), WC()->checkout->get_value('tos_agreement'));
}
// Process the checkout
add_action('woocommerce_checkout_process', 'my_custom_checkout_fields_process');
function my_custom_checkout_fields_process()
{
if (!$_POST['age_confirm']) {
wc_add_notice(__('Please confirm you are 21 years old or over'), 'error');
}
if (!$_POST['tos_agreement']) {
wc_add_notice(__('Please agree to the terms of use first'), 'error');
}
}
//Update the order meta with field values
add_action('woocommerce_checkout_update_order_meta', 'my_custom_checkout_fields_update_order_meta');
function my_custom_checkout_fields_update_order_meta($order_id)
{
if ($_POST['age_confirm']) {
update_post_meta($order_id, 'Age Confirm', esc_attr($_POST['age_confirm']));
}
if ($_POST['tos_agreement']) {
update_post_meta($order_id, 'Terms Of Use Agreement', esc_attr($_POST['tos_agreement']));
}
}
Пример согласия с условия доставки, в данном примере удалена последняя функция сохранения в БД, т.к. этого не требуется.
// Согласие с Условиями доставки
add_action('woocommerce_checkout_terms_and_conditions', 'my_required_checkout_fields');
function my_required_checkout_fields() {
woocommerce_form_field('delivery_confirm', array(
'type' => 'checkbox',
'class' => array('input-checkbox'),
'label' => __('Нажимая кнопку Подтвердить заказ, я соглашаюсь с <a href="/usloviya-dostavki/" target="_blank">Условиями поставки</a>'),
'required' => true,
), WC()->checkout->get_value('delivery_confirm'));
}
add_action('woocommerce_checkout_process', 'my_custom_checkout_fields_process');
function my_custom_checkout_fields_process() {
if (!$_POST['delivery_confirm']) {
wc_add_notice(__('Вы должны ознакомиться и согласиться с Условиями поставки'), 'error');
}
}
[site-socialshare]
ОСТОРОЖНО МНОГО ГРАМ. ОШИБОК
Хотел реализовать на странице оформления заказа поочередное заполнение формы сначала доставка потом оплата потом имя и тп.
вот скрин
К чему я пришол так как не нашел информации по етому поводу может кому-то поможет
Я использовал woocommerce saphali lite для кастомных форм
тоже использовал Checkout Address Autocomplete for WooCommerce чтоб подтягивать адреса с гугл апи
и Integrate NP Shipping для апи новой почты
мне нужно было чтоб форма доставки встала вверху страницы и була позначена 1 номером и чтоб все поля былы не активны
что я и зделал код делал для себя так что коменты с огромным количеством грам. ошибок! смотрите скрин фото
$(function(){
/*Перетаскуем способ оплати в верх */
$('.col-1').prepend( $('ul.wc_payment_methods.payment_methods.methods') );
$('ulul.wc_payment_methods.payment_methods.methods').detach().prependTo('.col-1');
$('.col-1').prepend('<h2 class="carttitle number payment">Способ оплаты</h2>');
/*Способ доставки */
$('.col-1').prepend( $('ul#shipping_method') );
$('ul#shipping_method').detach().prependTo('.col-1');
$('.col-1').prepend('<h2 class="carttitle number delivery">Способ доставки</h2>');
/*функция отставляет виконнання скрипта*/
setTimeout(function () {
/* удаление старой позицыи доставки*/
$('tfoot tr.shipping').css('display', 'none');
/*удаление старой позицыи оплаты*/
$('#payment .payment_methods').css('display', 'none');
/*оплата отключить дефолтний чек*/
$('.wc_payment_method input').prop('checked', false);
$('#wc_payment_method ').prop('checked', false);
$('#shipping_method input').prop('checked', false);
$('#shipping_method ').prop('checked', false);
}, 3000); // время в мс
/*выключаем поля*/
/*имя та фамилия*/
$(".woocommerce-billing-fields input").attr('disabled', true).css('cursor','not-allowed').css('opacity',0.3);
/*оплата чеки*/
$(".wc_payment_methods input").attr('disabled', true).css('cursor','not-allowed').css('opacity',0.3);
/*самовывоз*/
$(".woocommerce-billing-fields select").attr('disabled', true).css('cursor','not-allowed').css('opacity',0.3);
/*оплата слова*/
$(".wc_payment_methods").attr('disabled', true).css('cursor','not-allowed').css('opacity',0.3);
/*h2 оплаты*/
$(".number.payment").attr('disabled', true).css('cursor','not-allowed').css('opacity',0.3);
/*Проверяем на checked input доставки если да то открываем форму вибора оплаты*/
$('body').on('click', "ul#shipping_method", function(){
if ($("ul#shipping_method input").attr("checked") == 'checked' ) {
$(".wc_payment_methods").attr('disabled', false).css('cursor','default').css('opacity',1);
$(".wc_payment_methods input").attr('disabled', false).css('cursor','default').css('opacity',1);
$(".number.payment").attr('disabled', false).css('cursor','default').css('opacity',1);}
else
{};
});
/*проверяем на checked input оплати если да то открываем поле имя та фамилия*/
$('body').on('click', ".wc_payment_methods input", function(){
if ($(".wc_payment_methods input").attr("checked") == 'checked' ) {
$(".woocommerce-billing-fields input").attr('disabled', false).css('cursor','default').css('opacity',1).css('filter','blur(0px)');
$(".carttitle.number.info").attr('disabled', false).css('cursor','default').css('opacity',1).css('filter','blur(0px)');
$(".carttitle.number.info").attr('disabled', false).css('cursor','default').css('opacity',1).css('filter','blur(0px)');
$(".woocommerce-billing-fields select").attr('disabled', false).css('cursor','default').css('opacity',1).css('filter','blur(0px)');
};
});
/*удаляем поля другой получатель и чекбокс */
$('.woocommerce-shipping-fields .shipping_address').remove();
$('.woocommerce-form__label.woocommerce-form__label-for-checkbox.checkbox').remove();
$('.col-1 .woocommerce-account-fields').remove();
/*удаляем дефолтний ИТОГО*/
$(".order-total").css('display', 'none');
/*меняем без доставки на всего */
$(".cart-subtotal th").text('Всего :');
});
Отлично описали все!
Мне вот одного тут не хватает, как сделать так чтобы кнопка (Подтвердить заказ) была неактивна до того момента пока не будут заполнены все приоритетные поля?
Не пробовал такую фишку. Форма оформления WC сделана таким образом что кастомизировать её довольно проблематично.