Поступила нестандартная задача, реализовать акцию 20% на каждый 3 и 30% на каждый 4 товар. Сложность заключалась в том что скидки 20% и 30% должны были действовать на товары начиная с самого дешевого, т.е. первая скидка (20% за 3 товар) на самый дешевый, далее (30% за 4 товар) на второй товар с конца и т.д. Причем в расчете товаров для акции нужно было учитывать каждую единицу позиций (и количество каждой позиции и количество наименований) в пределах определенной категории.
Основной расчет акции (первая версия)
add_action( 'woocommerce_cart_calculate_fees','wc_custom_surcharge', 10, 1 );
function wc_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Условие категории
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product->get_type() == 'simple' ) {
$terms = get_the_terms( $_product->get_ID(), 'product_cat' );
} elseif ( $_product->get_type() == 'variation' ) {
$variation = wc_get_product($_product->get_ID());
$varproduct = wc_get_product( $variation->get_parent_id() );
$terms = get_the_terms( $varproduct->get_ID(), 'product_cat' );
}
if ($terms[0]->slug == 'compact-disc') { // если категория
$prices[] = array(
'cust_id' => $_product->get_ID(),
'cust_price' => $_product->get_price(),
'cust_quantity' => $cart_item['quantity']
);
$action_quantity[] = $cart_item['quantity'];
}
}
//asort($prices); // сортировка массива с сохранением ключей
//sort($prices); // сортировка массива без сохранения ключей
// сортировка 2-х мерного массива по ключу
usort($prices, function($a, $b) {
return $a['cust_price'] <=> $b['cust_price'];
});
$fee_descript = '';
$fee = 0;
if (array_sum($action_quantity) == 3 ) {
$fee = $prices[0]['cust_price'] * 0.2;
$fee_descript = 'Скидка 20% на третий товар';
} elseif (array_sum($action_quantity) >= 4) {
if (count($prices) == 1) {
$fee = ($prices[0]['cust_price'] * 0.2) + ($prices[0]['cust_price'] * 0.3);
$fee_descript = 'Скидка 20% на третий и 30% на четвертый товары';
} else {
if ($prices[0]['cust_quantity'] == 1) {
$fee = ($prices[0]['cust_price'] * 0.2) + ($prices[1]['cust_price'] * 0.3);
$fee_descript = 'Скидка 20% на третий и 30% на четвертый товары';
} else {
$fee = ($prices[0]['cust_price'] * 0.2) + ($prices[0]['cust_price'] * 0.3);
$fee_descript = 'Скидка 20% на третий и 30% на четвертый товары';
}
}
}
$cart->add_fee( __($fee_descript, 'woocommerce'), -$fee, true );
}
asort($prices); // сортировка массива с сохранением ключей sort($prices); // сортировка массива без сохранения ключей usort($prices, function($a, $b) { //сортировка 2-х мерного массива по ключу return $a['cust_price'] <=> $b['cust_price']; });
Вывод уведомление о сумме скидки в корзине (первая версия)
В некоторых шаблонах данный вывод не нужен, т.к. в первой функции есть вывод общей суммы скидки, но выводится он в шаблоне Оформление.
add_action( 'woocommerce_before_cart_totals','print_message_wc_custom_surcharge', 7, 1 );
function print_message_wc_custom_surcharge() {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product->get_type() == 'simple' ) {
$terms = get_the_terms( $_product->get_ID(), 'product_cat' );
} elseif ( $_product->get_type() == 'variation' ) {
$variation = wc_get_product($_product->get_ID());
$varproduct = wc_get_product( $variation->get_parent_id() );
$terms = get_the_terms( $varproduct->get_ID(), 'product_cat' );
}
if ($terms[0]->slug == 'compact-disc') { // если категория
$prices[] = array(
'cust_id' => $_product->get_ID(),
'cust_price' => $_product->get_price(),
'cust_quantity' => $cart_item['quantity']
);
$action_quantity[] = $cart_item['quantity'];
}
}
//asort($prices); // сортировка массива с сохранением ключей
//sort($prices); // сортировка массива без сохранения ключей
// сортировка 2-х мерного массива по ключу
usort($prices, function($a, $b) {
return $a['cust_price'] <=> $b['cust_price'];
});
$symbol = get_woocommerce_currency_symbol('RUB');
echo '<div class="discount-message">';
if (array_sum($action_quantity) == 3 ) {
$skidka = $prices[0]['cust_price'] * 0.2;
echo '<p>скидка 20% на третий товар: <strong>'.round($skidka, 2).' '.$symbol.'</strong></p>';
} elseif (array_sum($action_quantity) >= 4) {
if (count($prices) == 1) {
$skidka = $prices[0]['cust_price'] * 0.2;
$skidka_2 = $prices[0]['cust_price'] * 0.3;
echo '<p>скидка 20% на третий товар: <strong>'.round($skidka, 2).' '.$symbol.'</strong></p>';
echo '<p>скидка 30% на четвертый товар: <strong>'.round($skidka_2, 2).' '.$symbol.'</strong></p>';
} else {
if ($prices[0]['cust_quantity'] == 1) {
$skidka = $prices[0]['cust_price'] * 0.2;
$skidka_2 = $prices[1]['cust_price'] * 0.3;
echo '<p>скидка 20% на третий товар: <strong>'.round($skidka, 2).' '.$symbol.'</strong></p>';
echo '<p>скидка 30% на четвертый товар: <strong>'.round($skidka_2, 2).' '.$symbol.'</strong></p>';
} else {
$skidka = $prices[0]['cust_price'] * 0.2;
$skidka_2 = $prices[0]['cust_price'] * 0.3;
echo '<p>скидка 20% на третий товар: <strong>'.round($skidka, 2).' '.$symbol.'</strong></p>';
echo '<p>скидка 30% на четвертый товар: <strong>'.round($skidka_2, 2).' '.$symbol.'</strong></p>';
}
}
}
echo '</div>';
}
После этого задача была дополнена тем, что нужно было отобразить скидки в каждой строке товара к которому она применяется в виде старой и новой цены. Из-за этого пришлось частично изменить алгоритм.

Также, и что более важно, первая версия функции работала только с 3-м и 4-м товарами, а не с каждым 3 и каждым 4 товаром.
Основной расчет акции (вторая версия)
add_action( 'woocommerce_cart_calculate_fees','wc_custom_surcharge', 10, 1 );
function wc_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product->get_type() == 'simple' ) {
$terms = get_the_terms( $_product->get_ID(), 'product_cat' );
} elseif ( $_product->get_type() == 'variation' ) {
$variation = wc_get_product($_product->get_ID());
$varproduct = wc_get_product( $variation->get_parent_id() );
$terms = get_the_terms( $varproduct->get_ID(), 'product_cat' );
}
if ($terms[0]->slug == 'compact-disc') { // если категория
// создаем столько цен сколько единиц товара
$prices[] = array_fill(0, $cart_item['quantity'], $_product->get_price());
}
}
// объединяем в один массив цен
$all_prices = array();
foreach ($prices as $child) {
$all_prices = array_merge($all_prices, $child);
}
sort($all_prices);
$action_20_count = floor(count($all_prices) / 3 );
$action_30_count = floor(count($all_prices) / 4 );
$actions_count = $action_20_count + $action_30_count;
// Убием каждую 7 скидку т.к. они дублируются на одном товаре (12, 24, 36 и т.д.)
if ($actions_count % 7 == 0) {
$actions_count = $actions_count - ($actions_count / 7);
}
if ( count($all_prices) >= 3 ) {
$fee_descript = 'Скидка 20% за каждый третий и 30% за каждый четвертый товары';
$fee = 0;
$i = 0;
foreach ($all_prices as $cus_price) {
$i++;
if ($i <= $actions_count) { // соответствует количеству примененных скидок
if ($i % 2 != 0) { // нечетные
$all_actions_price[] = $cus_price * 0.2;
} else {
$all_actions_price[] = $cus_price * 0.3;
}
}
}
$fee = round(array_sum($all_actions_price), 2);
$cart->add_fee( __($fee_descript, 'woocommerce'), -$fee, true );
}
}
В данном случае функция работает на каждый 3 и 4 товары. Также внесена корректировка на каждое 7 срабатывание скидки, т.к. в этот момент происходит дублирование из-за того что каждый 12 товар является и третьи и четвертым (12 шт, 24 шт, 36 и т.д.).
Вывод уведомление о сумме скидки в корзине (вторая версия)
// Вывести информацию о скидке в корзине перед Итого
add_action( 'woocommerce_before_cart_totals','print_message_wc_custom_surcharge', 7, 1 );
function print_message_wc_custom_surcharge() {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product->get_type() == 'simple' ) {
$terms = get_the_terms( $_product->get_ID(), 'product_cat' );
} elseif ( $_product->get_type() == 'variation' ) {
$variation = wc_get_product($_product->get_ID());
$varproduct = wc_get_product( $variation->get_parent_id() );
$terms = get_the_terms( $varproduct->get_ID(), 'product_cat' );
}
if ($terms[0]->slug == 'compact-disc') { // если категория
// создаем столько цен сколько единиц товара
$prices[] = array_fill(0, $cart_item['quantity'], $_product->get_price());
}
}
// объединяем в один массив цен
$all_prices = array();
foreach ($prices as $child) {
$all_prices = array_merge($all_prices, $child);
}
sort($all_prices);
$action_20_count = floor(count($all_prices) / 3 );
$action_30_count = floor(count($all_prices) / 4 );
$actions_count = $action_20_count + $action_30_count;
// Убием каждую 7 скидку т.к. они дублируются на одном товаре (12, 24, 36 и т.д.)
if ($actions_count % 7 == 0) {
$actions_count = $actions_count - ($actions_count / 7);
}
$symbol = get_woocommerce_currency_symbol('RUB');
if ( count($all_prices) >= 3 ) {
echo '<div class="discount-message">';
$i = 0;
foreach ($all_prices as $cus_price) {
$i++;
if ($i <= $actions_count) { // соответствует количеству примененных скидок
if ($i % 2 != 0) { // нечетные
$all_actions_price[] = $cus_price * 0.2;
} else {
$all_actions_price[] = $cus_price * 0.3;
}
}
}
$skidka = round(array_sum($all_actions_price), 2);
echo '<p>Скидка 20% за каждый третий и 30% за каждый четвертый товары: <strong><bdi>'.$skidka.' '.$symbol.'</bdi></strong></p>';
echo '</div>';
}
}
$prices[] = array_fill(0, $cart_item['quantity'], $_product->get_price());
Изменение итоговой суммы по позициям участвующих в акции
add_filter( 'woocommerce_cart_item_subtotal', 'bbloomer_if_coupon_slash_item_subtotal', 99, 3 );
function bbloomer_if_coupon_slash_item_subtotal( $subtotal, $cart_item_cus ){
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product->get_type() == 'simple' ) {
$terms = get_the_terms( $_product->get_ID(), 'product_cat' );
} elseif ( $_product->get_type() == 'variation' ) {
$variation = wc_get_product($_product->get_ID());
$varproduct = wc_get_product( $variation->get_parent_id() );
$terms = get_the_terms( $varproduct->get_ID(), 'product_cat' );
}
if ($terms[0]->slug == 'compact-disc') { // если категория
// создаем столько цен сколько единиц товаров
for ($i = 1; $i <= $cart_item['quantity']; $i++) {
$prices[] = array(
'cust_id' => $_product->get_ID(),
'cust_price' => $_product->get_price()
);
}
}
}
// сортировка 2-х мерного массива по ключу
usort($prices, function($a, $b) {
return $a['cust_price'] <=> $b['cust_price'];
});
$action_20_count = floor(count($prices) / 3 );
$action_30_count = floor(count($prices) / 4 );
$actions_count = $action_20_count + $action_30_count;
// Убием каждую 7 скидку т.к. они дублируются на одном товаре (12, 24, 36 и т.д.)
if ($actions_count % 7 == 0) {
$actions_count = $actions_count - ($actions_count / 7);
}
if ( count($prices) >= 3 ) {
$i = 0;
foreach ($prices as $cust_price) {
$i++;
if ($i <= $actions_count) { // соответствует количеству примененных скидок
if ($i % 2 != 0) { // нечетные
$all_actions_price[] = array (
'cust_id' => $cust_price['cust_id'],
'cust_action' => $cust_price['cust_price'] * 0.2
);
} else {
$all_actions_price[] = array (
'cust_id' => $cust_price['cust_id'],
'cust_action' => $cust_price['cust_price'] * 0.3
);
}
}
}
}
foreach ($all_actions_price as $row) {
if ( !isset( $results[$row['cust_id']] ) ) {
$results[$row['cust_id']] = $row;
} else {
$results[$row['cust_id']]['cust_action'] .= ',' . $row['cust_action'];
}
}
foreach ($results as $result) {
//количество применений акции к каждой позиции
//$action_quantity = substr_count($result['cust_action'], ',') + 1;
// можно собрать красивый массив
//$product_action[$result['cust_id']] = array_sum( explode( ',', $result['cust_action'] ) );
$line_total_discount = array_sum( explode( ',', $result['cust_action'] ) );
if ( $cart_item_cus['data']->get_ID() == $result['cust_id'] ) {
$line_total = $cart_item_cus['data']->get_price() * $cart_item_cus['quantity'];
$newsubtotal = wc_price( $line_total - $line_total_discount );
$subtotal = sprintf( '<del>%s</del> %s', $subtotal, $newsubtotal );
}
}
return $subtotal;
}
Данная функция создана на основе решения Rodolfo Melogli об предварительном показе цены со скидкой (купоном) и не влияющем на сумму корзины:
add_filter( 'woocommerce_cart_item_subtotal', 'bbloomer_if_coupon_slash_item_subtotal', 99, 3 );
function bbloomer_if_coupon_slash_item_subtotal( $subtotal, $cart_item, $cart_item_key ){
global $woocommerce;
// Note: use your own coupon code here
$coupon_code = 'barmada';
if ( $woocommerce->cart->has_discount( $coupon_code )) {
// Note: apply your own coupon discount multiplier here
// In this case, it's a 99% discount, hence I multiply by 0.01
$newsubtotal = wc_price( $cart_item['data']->get_price() * 0.01 * $cart_item['quantity'] );
$subtotal = sprintf( '<s>%s</s> %s', $subtotal, $newsubtotal );
}
return $subtotal;
}
[site-socialshare]