В этой заметке постараюсь описать процесс реализации индивидуальных цен, для пользователей. При этом, индивидуальные цены будут синхронизироваться с 1С:Предприятие - в обе стороны.
Индивидуальная цена распостраняется не на все товары каталога, а на какие-то определенные. И мы не будем создавать собственные компоненты и по чем зря раздувать init.php
Получаем цены, для контрагентов из 1С
Все данные будем хранить в Highload-блоке, что обеспечит нам и достаточную скорость работы (так как цен и контрагентов может быть много) и гибкость при дальнейшей работе.
Создаем Highload-блок, называем, например Индивидуальные цены контрагентов c тремя полями:
- UF_XML_ID_CONTRAGENT в нем будем хранить XML-ID пользователя, этот же XML-ID используется и в 1С
- UF_GOOD_BY_CONTRAGENT здесь храним XML-ID товара, на который у пользователя индивидуальная цена
- UF_PRICE_CONTRAGENT а здесь, собственно, цена на этот товар для этого пользователя (тафталогия)
Заполняем тремя-четырмя элементами и экспортируем в файл. Этот файл отдаем программисту 1С, как образец и просим заполнить реальными данными.
Как получаем файл, выполняем импорт. В дальнейшем, 1С будет обмениваться этими данными автоматически. На стороне сайта ничего делать не надо, на стороне 1С используем дополнительный модуль обмена: который умеет выгружать справочники в HL блоки.
В итоге получаем заполненный инфоблок с индивидуальными ценами.
Выводим индивидуальную цену в каталоге
Теперь нужно вывести эти цены, авторизованному пользователю в разделе каталога и в детальной карточке товара.
В шаблоне catalog.item создаем файл result_modifier.php и добавляем в него следующий код (пояснения ниже):
if($USER->IsAuthorized()) {
$rsUser = CUser::GetByID($USER->GetID());
$arUser = $rsUser->Fetch();
$curUserXmlId = $arUser['XML_ID'];
$curItemXmlId = $arResult['ITEM']['XML_ID'];
\Bitrix\Main\Loader::includeModule('highloadblock');
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
$hlblockId = HL\HighloadBlockTable::getById(4)->fetch();
$entity = HL\HighloadBlockTable::compileEntity($hlblockId);
$entity_data_class = $entity->getDataClass();
$rsDataPrice = $entity_data_class::getList(array(
"select" => array("UF_PRICE_CONTRAGENT"),
"filter" => array(
"UF_GOOD_BY_CONTRAGENT" => $curItemXmlId,
"UF_XML_ID_CONTRAGENT" => $curUserXmlId
)
));
while ($arItemPrice = $rsDataPrice->Fetch()) {
$arResult['ITEM']['INDIVIDUAL_PRICE'] = $arItemPrice['UF_PRICE_CONTRAGENT'];
}
}
- Проверили авторизован ли пользователь, что бы почем зря не "напрягать" страницу, для не авторизованных.
- В переменную curUserXmlId загнали XML-ID текущего пользователя. В curItemXmlId - XML-ID текущего товара
- Далее: подключили модуль Highloadblock и получили из него индивидуальную цену "select" => array("UF_PRICE_CONTRAGENT"), отфильтровав ее, зная запись с XML-ID пользователя и товара.
- Добавили в массив $arResult['ITEM'] эту цену, на этот товар, для этого пользователя
В шаблоне catalog.item/ВАШ_ШАБЛОН/card/template.php просто делаем условие, обвернув в него цену. Если $arResult['ITEM']['INDIVIDUAL_PRICE'] пустая- то выводим обычную цену, иначе показываем индивидуальную.
foreach ($item['ITEM_PRICES'] as $PRICE) {
if (empty($arResult['ITEM']['INDIVIDUAL_PRICE'])) {
echo $PRICE['PRINT_RATIO_BASE_PRICE'];
} else {
echo $arResult['ITEM']['INDIVIDUAL_PRICE'] . ' руб.';
}
}
По сути, тоже самое проделываем с детальной карточкой товара в result_modifier.php и template.php шаблона catalog.element:
//// ------- result_modifier.php
if($USER->IsAuthorized()) {
$rsUser = CUser::GetByID($USER->GetID());
$arUser = $rsUser->Fetch();
$curUserXmlId = $arUser['XML_ID'];
$curItemXmlID = $arResult['XML_ID'];
\Bitrix\Main\Loader::includeModule('highloadblock');
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
$hlblockId = HL\HighloadBlockTable::getById(4)->fetch();
$entity = HL\HighloadBlockTable::compileEntity($hlblockId);
$entity_data_class = $entity->getDataClass();
$rsDataPrice = $entity_data_class::getList(array(
"select" => array("UF_PRICE_CONTRAGENT"),
"filter" => array(
"UF_GOOD_BY_CONTRAGENT" => $curItemXmlID,
"UF_XML_ID_CONTRAGENT" => $curUserXmlId
)
));
while ($arItemPrice = $rsDataPrice->Fetch()) {
$arResult['INDIVIDUAL_PRICE'] = $arItemPrice['UF_PRICE_CONTRAGENT'];
}
}
//// ------- template.php
foreach ($$arResult['ITEM_PRICES'] as $PRICE) {
if (empty($arResult['INDIVIDUAL_PRICE'])) {
echo $PRICE['PRINT_RATIO_BASE_PRICE'];
} else {
echo $arResult['INDIVIDUAL_PRICE'] . ' руб.';
}
}
Выводим индивидуальную цену в корзине
В целом, в корзине проделываем тоже самое, единственное что весь код добавляем в файл /sale.basket.basket/ВАШ_ШАБЛОН/mutator.php - здесь нужно модифицировать несколько полей массива, в которых содержатся цены и суммы каждого товара.
До foreach ($this->basketItems as $row):
\Bitrix\Main\Loader::includeModule('highloadblock');
global $USER;
$rsUser = CUser::GetByID($USER->GetID());
$arUser = $rsUser->Fetch();
$curUserXmlId = $arUser['XML_ID']; // XML ID текущего пользователя
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
$hlblockId = HL\HighloadBlockTable::getById(4)->fetch(); // Получаем запись из HL блока №4
$entity = HL\HighloadBlockTable::compileEntity($hlblockId);
$entity_data_class = $entity->getDataClass();
В самом переборе массива, в самом верху, так же проверяем наличие индивидуальной цены для этого товара, этому пользователю и подменяем значения ключей массива:
$rsDataPrice = $entity_data_class::getList(array(
"select" => array("UF_PRICE_CONTRAGENT"),
"filter" => array(
"UF_GOOD_BY_CONTRAGENT" => $row['PRODUCT_XML_ID'],
"UF_XML_ID_CONTRAGENT" => $curUserXmlId
)
));
while ($arItemPrice = $rsDataPrice->Fetch()) {
$curItemPrice = $arItemPrice['UF_PRICE_CONTRAGENT']; // Индивидуальная цена для этого товара этому контрагенту
}
if (!empty($curItemPrice)) {
$row['PRICE_FORMATED'] = $curItemPrice . ' руб.';
$row['PRICE'] = $curItemPrice;
$row['FULL_PRICE'] = $curItemPrice;
$row['FULL_PRICE_FORMATED'] = $curItemPrice . $row['CURRENCY'];
$row['SUM_PRICE'] = $curItemPrice * $row['QUANTITY'];
$row['SUM_PRICE_FORMATED'] = $curItemPrice * $row['QUANTITY'] . ' руб.';
$row['SUM'] = $curItemPrice * $row['QUANTITY'] . ' руб.';
$row['SUM_FULL_PRICE_FORMATED'] = $curItemPrice * $row['QUANTITY'] . ' руб.';
$row['SUM_FULL_PRICE'] = $curItemPrice * $row['QUANTITY'];
$row['SUM_FULL_PRICE'] = $curItemPrice * $row['QUANTITY'];
$row['SUM_VALUE'] = $curItemPrice * $row['QUANTITY'];
}
И в самом низу, перед завершением foreach ($this->basketItems as $row)
$allSumCustom += $row['PRICE'] * $row['QUANTITY'];
// И меняем сумму корзины в totalData
$totalData = array(
'DISABLE_CHECKOUT' => (int)$result['ORDERABLE_BASKET_ITEMS_COUNT'] === 0,
'PRICE' => $allSumCustom,
'PRICE_FORMATED' => $allSumCustom . ' руб',
'PRICE_WITHOUT_DISCOUNT_FORMATED' => $result['PRICE_WITHOUT_DISCOUNT'],
'CURRENCY' => $result['CURRENCY']
);
При желании, также подменяем сумму в шаблоне sale.basket.basket.line - таким же образом, как в шаблонах каталога, перемножив и сплюсовав цены и колличества товаров.
Оформление заказа с индивидуальными ценами
А теперь самый главный финт - нужно сохранить индивидуальные цены в заказ. Будем делать это методом CSaleBasket::Update - который обновляет записи корзины, связанной с заказом.
То есть: само оформление произойдет с обычными ценами, но на последнем шаге, когда заказ уже сформирован, просто получаем его ID и меняем цены на индивидуальные.
В файл /sale.order.ajax/ВАШ_ШАБЛОН/confirm.php добавляем код.
До проверки if (!empty($arResult["ORDER"]))::
$rsUser = CUser::GetByID($USER->GetID());
$arUser = $rsUser->Fetch();
$userId = $arUser['ID'];
$curUserXmlId = $arUser['XML_ID'];
\Bitrix\Main\Loader::includeModule('highloadblock');
use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
$hlblockId = HL\HighloadBlockTable::getById(4)->fetch(); // Получаем запись из HL блока №4
$entity = HL\HighloadBlockTable::compileEntity($hlblockId);
$entity_data_class = $entity->getDataClass();
Внутри if (!empty($arResult["ORDER"])):
$dbBasketItems = CSaleBasket::GetList(
false,
array(
"ORDER_ID" => $arResult["ORDER"]['ID']
),
false,
false,
array('PRODUCT_XML_ID', 'ID')
);
while ($arItems = $dbBasketItems->Fetch()) {
$rsDataPrice = $entity_data_class::getList(array(
"select" => array("UF_PRICE_CONTRAGENT"),
"filter" => array(
"UF_GOOD_BY_CONTRAGENT" => $arItems['PRODUCT_XML_ID'],
"UF_XML_ID_CONTRAGENT" => $curUserXmlId
)
));
while ($arItemPrice = $rsDataPrice->Fetch()) {
$curItemPrice = $arItemPrice['UF_PRICE_CONTRAGENT'];
}
if (!empty($curItemPrice)) {
$arFields = array(
"PRICE" => $curItemPrice,
);
CSaleBasket::Update($arItems['ID'], $arFields);
}
unset($curItemPrice);
}
- Методом CSaleBasket::GetList получили корзину (все товары), связанную с созданным заказом. От этого метода получили PRODUCT_XML_ID и ID. Обратите внимание, ID это не ID товара в каталоге, а ID записи в корзине.
- Все тем же способом, что пользовались выше, проверили наличие индивидуальной цены у каждого товара и, при наличии оной меняем цену в записи методом CSaleBasket::Update.
Все. Заказ будет формироваться с учетом индивидуальной цены, в 1С этот заказ придет с уже измененными данными - в общем, задача решена.
PS: В одной из следующих заметок расскажу о способе подмены цен в корзине, с помощью провайдера цен. При нем, не нужно будет ничего делать с шаблоном корзины и оформления заказа.
Но, если индивидуальных цен много - Провайдер цен может вызвать значительные нагрузки. Потому, оставил это на будущие заметки.
Способ описанный выше, хоть и более обширный, но с другой стороны: сделал и забыл. Не так часто меняется логика корзины и оформления.