Skip to content

Instantly share code, notes, and snippets.

@livevasiliy
Last active March 29, 2023 10:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save livevasiliy/936874d5b828246d9584b4c160404302 to your computer and use it in GitHub Desktop.
Save livevasiliy/936874d5b828246d9584b4c160404302 to your computer and use it in GitHub Desktop.
Персональные скидки Битрикс
Для создания кастомизированного правила работы с корзиной на сайт необходимо:
1. Скопировать файл saleactiondiscountfromdirectory.php в папку сайта рядом с init.php (либо в /local/php_interface/init.php либо /bitrix/php_interface/init.php либо /bitrix/php_interface/ID сайта/init.php).
2. Подключить файл saleactiondiscountfromdirectory.php в init.php:
include "saleactiondiscountfromdirectory.php";
3. Создать Highload блок. Импортировать файл discount_hlb.xml со структурой hl-блока на странице ваш-сайт/bitrix/admin/highloadblock_import.php
4. Создать записи в hl-блоке.
5. Создать правило работы с корзиной.
На странице ваш-сайт/bitrix/admin/sale_discount.php нажать на кнопку "добавить правило".
В настройках правила на вкладке "Общие параметры" указать Сайт, Название (например, "Персональная скидка") и Активность.
На вкладке "Действия и условия" добавить действие "Применить скидку из справочника" и выбрать в выпадающем списке созданный hl-блок со скидками.
Сохранить правило.
6. Создайте нового агента.
На странице ваш-сайт/bitrix/admin/agent_list.php нажать на кнопку "добавить агента". В настройках укажите в время для следующего запуска агента, в параметре функция агента укажите updateDiscountValueHlb(); в параметре "Периодичность выполнения" выберите: через заданный интервал.
И укажите нужный вам интервал обновления агента. В файле init.php расскоментировать вызов функции updateDiscountValueHlb.
ИЛИ
Подключение ajax.
Вставить и вызовите ajax-запрос.
Для этого сделайте в корне проекта/или где удобно папку для хранения ajax-обработчика. (Файл handlePersonalDiscount.php)
Затем подключите и вызовите файл ajax.js
7. Результат работы правила можно увидеть в корзине, положив в нее товары указанные в hl-блоке для текущегно пользователя.
// Можно заменить на аналог от jQuery.
BX.ready(function(){
// Увеличиваем скидку каждую минуту, отправляя ajax запрос
let timeId = setInterval(function () {
BX.ajax({
url: '/ajax/handlePersonalDiscount.php', // путь где лежит этот файл
method: 'POST',
timeout: 60,
async: true,
processData: true,
scriptsRunFirst: true,
emulateOnload: true,
start: true,
cache: false,
onsuccess: function(data){
console.log(data);
},
onfailure: function(){
}
})
}, 60000)
// Очищаем таймер после 5 минут
setTimeout(function () {
clearInterval(timeId)
}, 300000)
});
<?xml version="1.0" encoding="UTF-8"?>
<hiblock>
<hiblock>
<id>4</id>
<name>DISCOUNT</name>
<table_name>discount</table_name>
</hiblock>
<langs>
</langs>
<fields>
<field>
<id>38</id>
<entity_id>HLBLOCK_4</entity_id>
<field_name>UF_PRODUCT</field_name>
<user_type_id>string</user_type_id>
<xml_id />
<sort>100</sort>
<multiple>N</multiple>
<mandatory>N</mandatory>
<show_filter>N</show_filter>
<show_in_list>Y</show_in_list>
<edit_in_list>Y</edit_in_list>
<is_searchable>N</is_searchable>
<settings>
<size>20</size>
<rows>1</rows>
<regexp />
<min_length>0</min_length>
<max_length>0</max_length>
<default_value />
</settings>
<edit_form_label>
<en />
<ru>ID товара</ru>
</edit_form_label>
<list_column_label>
<en />
<ru>ID товара</ru>
</list_column_label>
<list_filter_label>
<en />
<ru />
</list_filter_label>
<error_message>
<en />
<ru />
</error_message>
<help_message>
<en />
<ru />
</help_message>
<base_type>string</base_type>
</field>
<field>
<id>39</id>
<entity_id>HLBLOCK_4</entity_id>
<field_name>UF_DISCOUNT</field_name>
<user_type_id>string</user_type_id>
<xml_id />
<sort>100</sort>
<multiple>N</multiple>
<mandatory>N</mandatory>
<show_filter>N</show_filter>
<show_in_list>Y</show_in_list>
<edit_in_list>Y</edit_in_list>
<is_searchable>N</is_searchable>
<settings>
<size>20</size>
<rows>1</rows>
<regexp />
<min_length>0</min_length>
<max_length>0</max_length>
<default_value />
</settings>
<edit_form_label>
<en>Discount (%)</en>
<ru>Размер скидки (%)</ru>
</edit_form_label>
<list_column_label>
<en>Discount (%)</en>
<ru>Размер скидки (%)</ru>
</list_column_label>
<list_filter_label>
<en />
<ru />
</list_filter_label>
<error_message>
<en />
<ru />
</error_message>
<help_message>
<en>Скидка на товар в процентах</en>
<ru>Скидка на товар в процентах</ru>
</help_message>
<base_type>string</base_type>
</field>
<field>
<id>40</id>
<entity_id>HLBLOCK_4</entity_id>
<field_name>UF_USER</field_name>
<user_type_id>string</user_type_id>
<xml_id />
<sort>100</sort>
<multiple>N</multiple>
<mandatory>N</mandatory>
<show_filter>N</show_filter>
<show_in_list>Y</show_in_list>
<edit_in_list>Y</edit_in_list>
<is_searchable>N</is_searchable>
<settings>
<size>20</size>
<rows>1</rows>
<regexp />
<min_length>0</min_length>
<max_length>0</max_length>
<default_value />
</settings>
<edit_form_label>
<en>User id</en>
<ru>ID Пользователя</ru>
</edit_form_label>
<list_column_label>
<en>User id</en>
<ru>ID Пользователя</ru>
</list_column_label>
<list_filter_label>
<en>User id</en>
<ru>ID Пользователя</ru>
</list_filter_label>
<error_message>
<en />
<ru />
</error_message>
<help_message>
<en />
<ru />
</help_message>
<base_type>string</base_type>
</field>
<field>
<id>48</id>
<entity_id>HLBLOCK_4</entity_id>
<field_name>UF_UPDATED_AT</field_name>
<user_type_id>datetime</user_type_id>
<xml_id />
<sort>100</sort>
<multiple>N</multiple>
<mandatory>N</mandatory>
<show_filter>N</show_filter>
<show_in_list>Y</show_in_list>
<edit_in_list>Y</edit_in_list>
<is_searchable>N</is_searchable>
<settings>
<default_value>
<type>NONE</type>
<value />
</default_value>
<use_second>Y</use_second>
<use_timezone>N</use_timezone>
</settings>
<edit_form_label>
<en />
<ru />
</edit_form_label>
<list_column_label>
<en>Update date</en>
<ru>Дата обновления</ru>
</list_column_label>
<list_filter_label>
<en />
<ru />
</list_filter_label>
<error_message>
<en />
<ru />
</error_message>
<help_message>
<en />
<ru />
</help_message>
<base_type>datetime</base_type>
</field>
</fields>
</hiblock>
<? require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
include "local/php_interface/update_discount_value_hlb.php";
try
{
updateDiscountValueHlb();
echo 'success';
} catch (Exception $e)
{
\Bitrix\Main\Diag\Debug::writeToFile($e->getMessage());
echo $e->getMessage();
}
<?php
use Bitrix\Main\Loader;
use \Bitrix\Highloadblock\HighloadBlockTable as HLB;
/**
* Helper функция для работы с Highload блоками
*
*
* @param $HlBlockId
* @return \Bitrix\Main\ORM\Data\DataManager|bool
* @throws \Bitrix\Main\ArgumentException
* @throws \Bitrix\Main\LoaderException
* @throws \Bitrix\Main\ObjectPropertyException
* @throws \Bitrix\Main\SystemException
*/
function GetEntityDataClass($HlBlockId) {
Loader::includeModule('highloadblock');
if (empty($HlBlockId) || $HlBlockId < 1)
{
return false;
}
$hlblock = HLB::getById($HlBlockId)->fetch();
$entity = HLB::compileEntity($hlblock);
$entity_data_class = $entity->getDataClass();
return $entity_data_class;
}
<?php
include 'helpers.php';
include "update_discount_value_hlb.php";
include "saleactiondiscountfromdirectory.php";
// updateDiscountValueHlb(); // Раскомментировать, если будет использоваться, через Агенты.
<?
use Bitrix\Main\ArgumentException;
use Bitrix\Main\EventManager;
use \Bitrix\Main\Loader,
\Bitrix\Highloadblock\HighloadBlockTable as HLB,
\Bitrix\Highloadblock\HighloadBlockLangTable;
use Bitrix\Main\LoaderException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\SystemException;
if (Loader::includeModule('sale'))
{
$eventManager = EventManager::getInstance();
$eventManager->addEventHandlerCompatible("sale",
"OnCondSaleActionsControlBuildList",
[
"SaleActionDiscountFromDirectory",
"GetControlDescr"
]);
class SaleActionDiscountFromDirectory extends CSaleActionCtrlBasketGroup
{
public static function GetClassName()
{
return __CLASS__;
}
public static function GetControlID()
{
return "DiscountFromDirectory";
}
public static function GetControlDescr()
{
return parent::GetControlDescr();
}
public static function GetAtoms()
{
return static::GetAtomsEx(false, false);
}
public static function GetControlShow($arParams)
{
$arAtoms = static::GetAtomsEx(false, false);
$arResult = [
"controlId" => static::GetControlID(),
"group" => false,
"label" => "Применить скидку из справочника",
"defaultText" => "",
"showIn" => static::GetShowIn($arParams["SHOW_IN_GROUPS"]),
"control" => [
"Применить скидку из HighLoad блока",
$arAtoms["HLB"]
]
];
return $arResult;
}
public static function GetAtomsEx($strControlID = false, $boolEx = false)
{
$boolEx = (true === $boolEx ? true : false);
$hlbList = [];
if (Loader::includeModule('highloadblock'))
{
$dbRes = HLB::GetList([]);
while ($el = $dbRes->fetch())
{
$hlbList[$el['ID']] = $el['NAME'];
}
$res = HighloadBlockLangTable::GetList(['filter' => ['=LID' => LANGUAGE_ID]]);
while ($el = $res->fetch())
{
if ($hlbList[$el['ID']])
$hlbList[$el['ID']] = $el['NAME'] . " [" . $hlbList[$el['ID']] . "]";
}
}
$arAtomList = [
"HLB" => [
"JS" => [
"id" => "HLB",
"name" => "extra",
"type" => "select",
"values" => $hlbList,
"defaultText" => "...",
"defaultValue" => "",
"first_option" => "..."
],
"ATOM" => [
"ID" => "HLB",
"FIELD_TYPE" => "string",
"FIELD_LENGTH" => 255,
"MULTIPLE" => "N",
"VALIDATE" => "list"
]
],
];
if (!$boolEx)
{
foreach ($arAtomList as &$arOneAtom)
{
$arOneAtom = $arOneAtom["JS"];
}
if (isset($arOneAtom))
{
unset($arOneAtom);
}
}
return $arAtomList;
}
public static function Generate($arOneCondition, $arParams, $arControl, $arSubs = false)
{
$mxResult = __CLASS__ . "::applyProductDiscount(" . $arParams["ORDER"] . ", " . "\"" . $arOneCondition["HLB"] . "\"" . ");";
return $mxResult;
}
/**
* Возвращает название класса Highload Block
*
* @param $HlBlockId
* @return DataManager|bool
* @throws ArgumentException
* @throws ObjectPropertyException
* @throws SystemException
*/
private static function GetEntityDataClass($HlBlockId)
{
if (empty($HlBlockId) || $HlBlockId < 1)
{
return false;
}
$hlblock = HLB::getById($HlBlockId)->fetch();
$entity = HLB::compileEntity($hlblock);
$entity_data_class = $entity->getDataClass();
return $entity_data_class;
}
/**
* Применяет скидку из справочника к товарам из корзины
* @param $arOrder
* @param $hlb - Highload block
* @return void
* @throws ArgumentException
* @throws LoaderException
* @throws ObjectPropertyException
* @throws SystemException
* @throws Exception
*/
public static function applyProductDiscount(&$arOrder, $hlb)
{
$userId = $arOrder['USER_ID'];
if ($userId && Loader::includeModule('highloadblock'))
{
$productIds = [];
foreach ($arOrder['BASKET_ITEMS'] as &$product)
{
$productIds[] = $product['PRODUCT_ID'];
}
unset($product);
$hlblock = HLB::getById($hlb)->fetch();
if (!$hlblock)
{
return;
}
$entity = HLB::compileEntity($hlblock);
$entityClass = $entity->getDataClass();
//Находим записи в справочнике с нужными параметрами - товар/пользователь
$dbRes = $entityClass::getList([
'filter' => [
'=UF_PRODUCT' => $productIds,
'=UF_USER' => $userId
],
'order' => [
'ID' => 'DESC'
]
]);
$discounts = [];
while ($el = $dbRes->fetch())
{
if (!$discounts[$el['UF_PRODUCT']])
{
$discounts[$el['UF_PRODUCT']] = $el;
}
}
//Применяем скидку
foreach ($arOrder['BASKET_ITEMS'] as &$product)
{
$productId = $product['PRODUCT_ID'];
if ($discounts[$productId])
{
$product['DISCOUNT_PRICE'] = $product['BASE_PRICE'] * $discounts[$productId]['UF_DISCOUNT'] / 100;
$product['PRICE'] = $product['BASE_PRICE'] - $product['DISCOUNT_PRICE'];
}
}
unset($product);
}
}
}
}
<?php
use Bitrix\Main\Context;
use Bitrix\Main\Loader;
use Bitrix\Sale\Basket;
const DISCOUNT_HLB = 4; // ID HighLoad Block с персональными скидками
const MAX_DISCOUNT_PERCENT = 5; // До скольки процентов будем расти скидку.
const STEP_INCREMENT_DISCOUNT_PERCENT = 1; // Значение шага увеличения скидки в указанным период в агенте
/**
* Обработчик для обновления значения скидки и даты обновления в
* Highload блоке с персональными скидками
*
* @throws Exception
*/
function updateDiscountValueHlb()
{
global $USER;
$USER = new CUser();
$currentDateTimeOnSite = new \Bitrix\Main\Type\DateTime();
Loader::includeModule('highloadblock');
if ($USER->IsAuthorized())
{
$userId = $USER->GetID();
$basket = Basket::loadItemsForFUser(
$userId,
Context::getCurrent()->getSite()
);
$basketItems = $basket->getBasketItems();
$arProductIds = [];
/** @var \Bitrix\Sale\BasketItem $basketItem */
foreach ($basketItems as $basketItem)
{
$arProductIds[] = $basketItem->getProductId();
}
$arFilter = [
'filter' => [
'=UF_USER' => $userId,
'=UF_PRODUCT' => $arProductIds
],
'order' => [
'ID' => 'DESC'
]
];
$entity_data_class = GetEntityDataClass(DISCOUNT_HLB);
$rsData = $entity_data_class::getList($arFilter);
$discounts = [];
$arProductIdsFromDiscounts = [];
$currentDiscountSize = 1;
while($el = $rsData->fetch()){
{
if (!$discounts[$el['UF_PRODUCT']])
{
$discounts[$el['UF_PRODUCT']] = $el;
$arProductIdsFromDiscounts[] = $el['UF_PRODUCT'];
$currentDiscountSize = $el['UF_DISCOUNT'];
}
}
}
$missingProductIds = array_diff($arProductIds, $arProductIdsFromDiscounts);
foreach ($missingProductIds as $missingProductId)
{
$entity_data_class::add([
'UF_PRODUCT' => $missingProductId,
'UF_USER' => $userId,
'UF_DISCOUNT' => $currentDiscountSize,
'UF_UPDATED_AT' => $currentDateTimeOnSite
]);
}
foreach($discounts as $discount)
{
$sizeDiscount = $discount['UF_DISCOUNT'];
$lastDateTimeUpdate = new \Bitrix\Main\Type\DateTime($discount['UF_UPDATED_AT']);
if ($sizeDiscount < MAX_DISCOUNT_PERCENT && $currentDateTimeOnSite > $lastDateTimeUpdate)
{
$sizeDiscount += STEP_INCREMENT_DISCOUNT_PERCENT;
$arFields = $discount;
$arFields['UF_DISCOUNT'] = $sizeDiscount;
$arFields['UF_UPDATED_AT'] = $currentDateTimeOnSite;
$entity_data_class::update($discount['ID'], $arFields);
}
}
}
return "updateDiscountValueHlb();"; // для вызова функции, через Битрикс.Агенты
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment