столкнулся с задачей (распространенной) Выбрать все товары со скидкой (причем скидки со всеми параметрами условий скидки)
в течении года я делал такую вещь как "выкраивал" данный функционал (конструктор условий) из модуля скидок чтобы применить с своем модуле
и тут я про это вспомнил
что же надо сделать:
1. выбрать все скидки
выбираем в массив все скидки (по ходу приводим к нормальному виду массив с условиями)
2. Приведем наши товары -которые надо отфильтровать (к нужному виду) для этого
- надо их выбрать
-выделить все поля
-выделить все свойства (ведь в условиях скидки свойства и поля ВСЕ причем принимают участие)
для приведения к нужному виду используется функция
ну и собственно выбор всех и все (тестил на малом количестве)- поэтому код для выбора ВСЕГО и отовсюду
повторюсь у меня на тесте всего 10 элементов по разным инфоблокам
3. т.к. скидок может быть МНОГО то сделаем массив условий
данный код подключает класс (который работает с деревом условий скидок)
генерит из массива условий нужную нам строку и складывает ее в наш массив
4. ну и крайний шаг - это фильтруем все товары (написал топорно)-но думаю мысль всем понятна будет!
на выходе имеем массив отфильтрованный по всем условиям ВСЕХ скидок!!!!
ну вот как то так.. очень принимаются комментарии по улучшению, быстродействию и вообще СКОРЕЕ БЫ ПОЯВИЛСЯ ШТАТНЫЙ ФУНКЦИОНАЛ ВЫВОДА ТОВАРОВ СО СКИДКАМИ!!!!
в течении года я делал такую вещь как "выкраивал" данный функционал (конструктор условий) из модуля скидок чтобы применить с своем модуле
и тут я про это вспомнил
что же надо сделать:
1. выбрать все скидки
$dbProductDiscounts = CCatalogDiscount::GetList(array("SORT" => "ASC"), array("ACTIVE" => "Y"),false,false,array()); while ($arProductDiscounts = $dbProductDiscounts->Fetch()) { $arProductDiscounts["CONDITIONS"]=unserialize($arProductDiscounts["CONDITIONS"]); $disc[]=$arProductDiscounts; } |
2. Приведем наши товары -которые надо отфильтровать (к нужному виду) для этого
- надо их выбрать
-выделить все поля
-выделить все свойства (ведь в условиях скидки свойства и поля ВСЕ причем принимают участие)
для приведения к нужному виду используется функция
function CreateFields($item) { $newItem=array(); $prop=$item["PROPERTIES"];unset($item["PROPERTIES"]); $newItem=$item; foreach($prop as $id=>$props) { $newItem["PROPERTY_".$props["ID"]."_VALUE"]=$props["VALUE"]; } return $newItem; } |
$res=CIBlockElement::GetList(array(),array(),false,false,array()); while($b=$res->GetNextElement()) { $a=$b->GetFields(); $a["PROPERTIES"]=$b->GetProperty(); $offer[]=self::CreateFields($a); } |
3. т.к. скидок может быть МНОГО то сделаем массив условий
CModule::IncludeModule('catalog'); $obCond=new CCatalogCondTree(); $boolsCond=$obCond->Init(BT_COND_MODE_GENERATE,BT_COND_BUILD_CATALOG); if($boolsCond) foreach($disc as $dsc){ $filter[]=$obCond->Generate($dsc["CONDITIONS"],array("FIELD"=>'$arItems')); } |
генерит из массива условий нужную нам строку и складывает ее в наш массив
4. ну и крайний шаг - это фильтруем все товары (написал топорно)-но думаю мысль всем понятна будет!
foreach($offer as $arItems){ foreach($filter as $fltr){ $newOffers[]=(eval('return '.$fltr.';')==1) ? $arItems : ''; } } $newOffers=array_filter($newOffers); |
ну вот как то так.. очень принимаются комментарии по улучшению, быстродействию и вообще СКОРЕЕ БЫ ПОЯВИЛСЯ ШТАТНЫЙ ФУНКЦИОНАЛ ВЫВОДА ТОВАРОВ СО СКИДКАМИ!!!!
При изменении прав на типы цен, даже в случае использования одной группы пользователей, требуется полный пересчет.
смотрю код модуля... по умолчанию агент запускается 1 раз в день + на изменениях в скидках (удаление, добавление)... Изменение прав на тип цены - это явление очень редкое... можно и пересчитать вручную
Но модуль понравился мне не тем что он рассчитывает скидки и пишет в свойство, а тем что можно набросать обработчик типа (это грязный вариант, тестировал месяца 3 назад на производительность)... при большом количестве скидок и условиям совместимости логики по типам цен, группам пользователей и т.п. - можно неплохо все ускорить
а в моем случае я пишу модуль в котором надо исключить товары со скидками, причем по всем условиям
я ведь не буду в комплекте еще и модуль долганина продавать!!!
из документации
"Важно! Метод не может быть использован для выборки товаров, на которые действуют скидки."
Вызов: (new DiscountFilter)->getIds()
use Bitrix\Catalog\DiscountTable;
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Data\Cache;
use Bitrix\Main\Loader;
/**
* Class Discount
*
* Служит для создания списка идентификаторов товаров со скидкой
* для последующей филтрации где-либо
*
* @author pinguinjkeke
* @see
*/
class DiscountFilter
{
/**
* Время кэширования
*
* @var int
*/
const CACHE_TIME = 3600000;
/**
* Идентификатор инфоблока товаров
*
* @var int
*/
const PRODUCT_IBLOCK_ID = 32;
/**
* Поля товаров (если необходима фильтрация по другим полям, то их следует добавить сюда,
* а ненужные исключить)
*
* @var array
*/
private $criteriaFields = [
'ID', 'IBLOCK_SECTION_ID', 'IBLOCK_ID', 'NAME'
];
/**
* Свойства товаров (добавить необходимые, лишние исключить)
*
* @var array
*/
private $criteriaProperties = [
'ALCO', 'REGION', 'COUNTRY', 'newproduct', 'popular'
];
/**
* Дополнительная фильтрация
*
* @var array
*/
private $additionalFilter = [
'ACTIVE' => 'Y'
];
/**
* ID товаров со скидками
*
* @var array
*/
private $ids;
/**
* Конструктор
*/
public function __construct()
{
$cache = Cache::createInstance();
if ($cache->startDataCache(self::CACHE_TIME, 'MESHGROUP_DISCOUNT_FILTER')) {
Loader::includeModule('catalog');
Loader::includeModule('iblock');
Loader::includeModule('sale');
foreach ($this->criteriaProperties as $property) {
$this->criteriaFields[] = "PROPERTY_{$property}";
}
$ids = $this->processDiscountConditions($this->getDiscounts(), $this->getProducts());
if (empty($ids)) {
$cache->abortDataCache();
return;
}
$cache->endDataCache(compact('ids'));
} else {
extract($cache->getVars());
}
$this->ids = $ids;
}
/**
* Возвращает список идентификаторв товаров со скидкой
*
* @return array
*/
public function getIds()
{
return $this->ids;
}
/**
* Получение списка товаров по 1000
*
* @return array
*/
private function getProducts()
{
$products = [];
$filter = array_merge(['IBLOCK_ID' => self::PRODUCT_IBLOCK_ID], $this->additionalFilter);
$elementsCount = ElementTable::getCount($filter);
$page = 1;
while (($page * 1000) < $elementsCount) {
$get = CIBlockElement::GetList(
['ID' => 'asc'],
$filter,
false,
['nPageSize' => 1000, 'iNumPage' => $page],
$this->criteriaFields
);
while ($res = $get->Fetch()) {
if (!isset($products[$res['ID']])) {
if (isset($res['IBLOCK_SECTION_ID'])) {
$res['SECTION_ID'] = [$res['IBLOCK_SECTION_ID']];
unset($res['IBLOCK_SECTION_ID']);
}
$products[$res['ID']] = $res;
}
}
$page++;
}
return $products;
}
/**
* Получение списка скидок
*
* @return array
*/
private function getDiscounts()
{
return DiscountTable::getList([
'select' => ['ID', 'CONDITIONS_LIST'],
'filter' => ['ACTIVE' => 'Y']
])->fetchAll();
}
/**
* Обработка условий скидок
*
* @param array $discounts Массив условий скидок
* @param array $products Массив товаров
* @return array|bool
*/
private function processDiscountConditions(array $discounts, array $products)
{
$condition = new CCatalogCondTree;
if (!$bool = $condition->Init(BT_COND_MODE_GENERATE, BT_COND_BUILD_CATALOG, [])) {
return false;
}
$ids = [];
foreach ($discounts as $discount) {
$filter = $condition->Generate($discount['CONDITIONS_LIST'], ['FIELD' => '$product']);
foreach ($products as $product) {
if (eval("return {$filter};")) {
$ids[] = $product['ID'];
}
}
}
return $ids;
}
}
while (($page * 1000) < $elementsCount) {
$get = CIBlockElement::GetList(
['ID' => 'asc'],
$filter,
false,
['nPageSize' => 1000, 'iNumPage' => $page],
$this->criteriaFields
);
сейчас это уже не актуально! используем другие наработки!
ну а если товаров мало то используем доработанный аналогичный код....
но с учетом что сейчас скидки конвертируют в маркетинговые программы - и если делать универсально , то надо и скидки товара и правила работы с корзиной учитывать!!!