Пользователь
0,0
рейтинг
1 июля 2013 в 15:31

Разработка → Простейший способ реализации переключения цен в 1С-Битрикс tutorial recovery mode

Довольно часто на интернет-магазинах оптовой торговли можно встретить сразу несколько типов цен — обычно их обозначают как ОПТ1, ОПТ2, ОПТ3 и т.п. В зависимости от того, на какую сумму покупатель набрал добра в корзине и(или) иных условий, для него срабатывает тот или иной тип цен.

К сожалению, не все движки сайтов предусматривают наличие сразу нескольких типов цен для товара, а даже те движки, в которых есть возможность задания более чем одной цены для продукта, зачастую не располагают гибкими механизмами их переключения. К последним относится довольно таки популярный «1С-Битрикс». С одной стороны, в этой CMS уже в «коробочном» варианте(в редакции «Бизнес» и выше) есть поддержка нескольких видов цен, а с другой стороны, совершенно непонятно как настроить динамическое переключение этих цен в зависимости от тех или иных условий. Штатный мануал по этой части тоже хранит гробовое молчание. Надеюсь, что товарищи из Битрикса исправят это досадное недоразумение, но я решил не ждать этого счастливого момента и ясное дело додумался до костыльного решения, чем собственно и решил поделиться с хабрасообществом.

Итак, поехали...

В первую очередь, следует обратить внимание на обработчик OnGetOptimalPrice. Несмотря на то, что эта функция в ядре появилась достаточно давно, в онлайн-документации её описание было добавлено совсем недавно. Между тем, этот обработчик срабатывает когда происходит автоматический выбор оптимальной цены для конкретного товара и позволяет переопределять цену для него. Мы же попытаемся использовать его для пересчета всех цен в корзине в зависимости от общей суммы покупки.

Подключаем обработчик в файле /php_interface/init.php (само собой, если этого файла нет, то его необходимо создать — битрикс подключит его автоматически)

AddEventHandler("catalog", "OnGetOptimalPrice", "MyGetOptimalPrice");

Объявление функции MyGetOptimalPrice вместе с входными параметрами будет выглядеть следующим образом:

function MyGetOptimalPrice($productID, $quantity = 1, $arUserGroups = array(), $renewal = "N", $arPrices = array(), $siteID = false, $arDiscountCoupons = false)

Как видно из приведенного кода, функция не получает на вход общую сумму стоимости товаров в корзине, а только данные конкретного товара. А значит, необходимо получить данные корзины запросом в базу данных. Т.к. обработчик вызывается в цикле в зависимости от количества товаров в корзине, то имеет смысл определить глобальную переменную, в которую будем записывать общую сумму содержимого корзины при первом вызове обработчика OnGetOptimalPrice.

На выходе должно получиться, что-то вроде этого:

global $LocalPrice;
$LocalPrice = 0;


function MyGetOptimalPrice($productID, $quantity = 1, $arUserGroups = array(), $renewal = "N", $arPrices = array(), $siteID = false, $arDiscountCoupons = false)
{
	global $LocalPrice;
	if($LocalPrice <= 0)
	{
		// Выведем актуальную корзину для текущего пользователя
		$dbBasketItems = CSaleBasket::GetList(false,
			array(
				"FUSER_ID" => CSaleBasket::GetBasketUserID(),
				"LID" => SITE_ID,
				"ORDER_ID" => "NULL"
				),
			false,
			false,
			array("ID", "MODULE", "PRODUCT_ID", "CALLBACK_FUNC", "QUANTITY", "DELAY", "CAN_BUY", "PRICE")
		);
		while ($arItem = $dbBasketItems->Fetch())
		{
			if($arItem['DELAY'] == 'N' && $arItem['CAN_BUY'] == 'Y')
			{
				$LocalPrice += $arItem['PRICE']*$arItem['QUANTITY'];
			}
		}
	}

	//ОПТ 1 при сумме заказа до 10 000 рублей
	//ОПТ 2 при сумме заказа до 20 000 рублей
	//ОПТ 3 при сумме заказа более 20 000 рублей

	// получаем все типы цен, возможные для данного товара
	$arOptPrices = CCatalogProduct::GetByIDEx($productID);

	if($LocalPrice < 10000){
		$price = $arOptPrices['PRICES'][1]['PRICE'];
		$catalog_group_id = 1;
	}
	elseif($LocalPrice >= 10000 and $LocalPrice < 20000){
		$price = $arOptPrices['PRICES'][2]['PRICE'];
		$catalog_group_id = 2;
	}
	elseif($LocalPrice >= 20000){
		$price = $arOptPrices['PRICES'][3]['PRICE'];
		$catalog_group_id = 3;
	}
	
	return array(
		'PRICE' => array(
			"ID" => $productID,
			'CATALOG_GROUP_ID' => $catalog_group_id,
			'PRICE' => $price,
			'CURRENCY' => "RUB",
			'ELEMENT_IBLOCK_ID' => $productID,
			'VAT_INCLUDED' => "Y",
		),
		'DISCOUNT' => array(
			'VALUE' => $discount,
			'CURRENCY' => "RUB",
		),
	);
}


При желании можно оптимизировать код, добавив кеширование для хранения выборки цен, избавиться от хард-кодового задания условий и вынеся условия в конфиг-файлы или в базу данных. В принципе, саму выборку данных из корзины тоже можно вынести за пределы функции обработчика и хранить сумму где-нить в сессии, куках или глобальной переменной(кому как нравится и как считает правильным). Само собой, для учебного примера не стал всего этого делать. Пользуйтесь на здоровье!
Адам Заитов @AdamZ
карма
0,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (7)

  • 0
    Как-то совсем уж мало.
    Смысл статьи можно уместить в одно простое «В 1С-Битрикс есть события и их обработчики, например OnGetOptimalPrice...»
    • 0
      Сам по себе OnGetOptimalPrice предназначен для других целей и то, что его можно использовать для игры с множественными ценами не всегда и не всем очевидно. В статье приведён пример не вполне стандартного использования данного обработчика.
      • 0
        Все обработчики Битрикса суть места вставки хаков (обычно это всеми любимые костыли), меняюших поведение того или иного функционала.
        Кстати, в приведённом примере 'CURRENCY' => "RUB" убивает мультивалютность, если она имеется.
        • 0
          Приведённый пример демонстрирует общие принципы работы с множественными ценами в Битрикс. Так-то там и цена расчитывается без учета скидок, нет кеширования выборок и т.п. Но смысл выкладывать рецепт салата, если хочешь лишь научить резать помидоры?))
          • 0
            Дело в том, что человек, плохо разбирающийся в таких нюансах просто скопирует Ваш код, а потом будет долго ругаться потому что «всё сломалось». Такая штука получается — для профессионалов Ваша статья бесполезна, а для новичков скорее вредна.
            Если вы хотели научить «резать помидоры» — рассказали бы лучше о том, какие типы событий существуют, для чего предназначены, как влияют на работу системы и как грамотно с ними работать.
            А Вы показали салат, ткнули пальцем в помидор и сказали «Вот так!».
            • 0
              С обработчиками в Битриксе работают как правило только профи, коим без лишней скромности я отношу и себя, и я лично был бы рад, если бы наткнулся на подобное(или альтернативное) готовое решение описанной проблемы и съекономил бы определённое время.

              Что касается составления статьи про все типы событий в Битриксе, то эта идея конечно интересная, но бесмысленна т.к. в документации достаточно подробно они все описаны. Конечно, очень многие из них можно использовать не по прямому назначению(как в описаном выше примере), однако их настолько много, что я не видел смысла описывать всё в одной статье. В целом я планирую написать ещё ряд статей про те или иные недокументированные возможности Битрикса. Я не ставлю цель научить новичков работать с Битриксом «с нуля» — для этого есть бесплатные онлайн-курсы и документация. Мне важно показать лишь то, что осталось за кулисами доков.
  • 0
    Этот вариант — не панацея. Нужно использовать его в связке с callback функциями, ибо здесь сначала считается сумма корзины, а потом добавляется товар, который уже, возможно, должен быть добавлен по ОПТ3

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.