Catalog 26.500.0: старый REST каталога строже проверяет права, а модуль получил REST V3
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Catalog 26.500.0 (снят на БУС) закрывает дыры в правах старого REST catalog.*: цены, товары, свойства, складские документы и пакетная запись остатков. Обмен с 1С, синхронизация цен и внешний учёт, если они работают под пользователем с неполными правами, после обновления могут получать отказы там, где раньше всё проходило. Заодно у модуля появился полный REST V3 из 28 контроллеров.
Новое
REST V3 для всего каталога
В .settings.php модуля появилась секция rest с defaultNamespace = \Bitrix\Catalog\Infrastructure\Rest\Controller. По ней модуль rest сам находит наследников RestController и публикует их действия. Имя метода складывается из модуля, пути класса и действия в нижнем регистре: Product\Image::listAction() становится catalog.product.image.list.
Многие имена совпадают со старыми, catalog.product.get есть и там, и там. Различает их адрес. Старый REST отвечает на /rest/<id>/<секрет>/, V3 на /rest/api/<id>/<секрет>/ и ждёт параметры JSON-телом.
Что покрыто:
- торговые каталоги (
catalog.catalog.*), в том числеisoffers, который проверяет, каталог ли это предложений; - товары, SKU, предложения и услуги (
catalog.product.*,catalog.product.{sku,offer,service}.*), их картинки и свойства; свойства передаются вpropertiesс ключамиPROPERTY_{id}; - складские документы со строками, контрагентами, пользовательскими полями и статусом складского учёта (
catalog.document.*), включая проведение и отмену пачкой; - цены, типы цен с группами и переводами, НДС, единицы измерения, коэффициенты, правила округления, разделы, наценки и контрагенты.
catalog.product.image.list требует фильтр по productId. Справочники catalog.product.type.list, catalog.product.property.type.list и catalog.document.type.list отклоняют filter, order и pagination. catalog.document.delete принимает id или фильтр только по id с операторами = и in. Типа TYPE_UNDO_RESERVE среди доступных типов документов нет.
Права V3 проверяет внутренний класс AccessSupport. Старый REST использует его же для складских документов и их контрагентов, остальные проверки из раздела ниже написаны в контроллерах старого REST. Сами типы цен (catalog.pricetype.add, update, delete) и ставки НДС (catalog.vat.*) V3 записывает через старые CCatalogGroup и CCatalogVat, поэтому ваши обработчики их событий сработают и на вызов из V3. Группы и переводы типов цен (catalog.pricetype.group.*, catalog.pricetype.lang.*) пишутся напрямую в GroupAccessTable и GroupLangTable, и события CCatalogGroup на них не срабатывают.
Из PHP-интеграции метод V3 вызывается обычным POST с JSON-телом:
<?php
declare(strict_types=1);
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
// Адрес вебхука V3: https://<портал>/rest/api/<id пользователя>/<секрет>/
function callCatalogV3(string $webhook, string $method, array $params): array
{
$http = new HttpClient(['socketTimeout' => 10, 'streamTimeout' => 30]);
$http->setHeader('Content-Type', 'application/json');
$body = $http->post($webhook . $method, Json::encode($params));
if ($body === false) {
throw new RuntimeException('REST V3 недоступен: ' . implode('; ', $http->getError()));
}
return Json::decode($body);
}
$answer = callCatalogV3(
'https://portal.example/rest/api/1/0123456789abcdef/',
'catalog.catalog.isoffers',
['id' => 3],
);
Если пишете свой метод V3, каталог годится как образец. DTO в lib/Infrastructure/Rest/Dto/ описаны атрибутами #[OrmEntity] или #[MappedBy], у полей стоят #[Editable], #[Filterable], #[Sortable] и #[Required].
Деньги в формате валюты
Bitrix\Catalog\Component\MoneyPropertyValueNormalizer::normalize($amount, $currency) приводит сумму к формату валюты (например, меняет десятичную точку на запятую) и склеивает её с валютой в строку сумма|валюта. Если у валюты десятичный разделитель — точка (так у RUB, USD и EUR в стандартных настройках) или сумму привести не удалось, вернёт исходную сумму, склеенную так же. Карточки товара и вариации теперь сохраняют свойства-деньги через него, а при битой структуре AMOUNT/CURRENCY бросают InvalidMoneyPropertyValueException и показывают ошибку у конкретного поля.
В своём импорте нормализатор пригодится, когда в настройках валюты разделитель — запятая, а суммы приходят с точкой:
<?php
declare(strict_types=1);
use Bitrix\Catalog\Component\MoneyPropertyValueNormalizer;
use Bitrix\Main\Loader;
Loader::requireModule('catalog');
Loader::requireModule('currency');
// В выгрузке сумма с точкой: "1500.5". Если в настройках валюты десятичный разделитель — запятая,
// normalize вернёт "1500,50|RUB"; при стандартной точке — "1500.5|RUB" без изменений.
function saveMoneyProperty(int $iblockId, int $elementId, string $code, string $amount, string $currency): void
{
\CIBlockElement::SetPropertyValuesEx($elementId, $iblockId, [
$code => MoneyPropertyValueNormalizer::normalize($amount, $currency),
]);
}
saveMoneyProperty(2, 1045, 'OLD_PRICE', '1500.5', 'RUB');
Отчёты по складам: округление и методы для наследников
Количества в складских отчётах теперь округляет QuantityNormalizer, до 6 знаков и не больше 15 значащих цифр. Хвосты вроде 2.9999999999 из таблиц уйдут.
В ReportStoreList методы getMeasures(), prepareItemColumn() и prepareOverallTotalRow() стали protected, в CatalogReportStoreProfitGridComponent так же открыли prepareMeasureField(), getMeasures() и getDefaultMeasureId(). В ReportProductList появился protected applyRowQuantities(), он считает количества строки товара. Колонку или итоговую строку отчёта теперь можно поменять в наследнике, не копируя класс целиком.
Что сломается
Проверьте пользователя интеграции
В catalog 26.400.0 правами начали резать выдачу остатков по складам, теперь очередь цен, товаров, свойств и документов. Всё ниже относится к старому REST (Bitrix\Catalog\Controller\*). Прогоните обмен и выгрузки под тем пользователем, под которым они работают, а не под администратором.
- Цены.
catalog.price.modifyсначала проверяет право на правку цен элемента (element_edit_price) и то, что всеPRICES[].IDпринадлежат этому товару, и только потом удаляет цены, которых нет в списке. Раньше лишние цены удалялись ещё до проверки прав. Чужой, нулевой или нечисловой ID цены даётAccess Deniedс кодом200040300030.catalog.price.updateберёт товар из сохранённой цены, а не изfields.PRODUCT_ID.catalog.price.deleteтеперь тоже требуетelement_edit_priceна товар, которому принадлежит цена. - Товары.
catalog.product.get,update,deleteиdownloadбольше не находят элементы обычных инфоблоков, только товары торговых каталогов.downloadвдобавок требует право чтения элемента. - Свойства.
catalog.productproperty.updateиdelete,catalog.productpropertyenum.updateиdelete,catalog.productpropertysection.setтребуют правоiblock_editна инфоблок свойства. ЕслиupdateменяетIBLOCK_ID, новый инфоблок должен быть каталогом («The specified iblock is not a product catalog»), и права нужны на него тоже. - Поля документов.
catalog.userfield.document.updateберёт тип документа из БД.DOC_TYPEстал необязательным, а несовпадающий даёт «The DOC_TYPE field does not match the document». Раньше при неизвестномDOC_TYPEметод молча возвращал[], теперь отвечает этой ошибкой. - Складские документы.
catalog.document.conduct,cancel,confirm,unconfirm,conductListиcancelListтребуютACTION_CATALOG_READиACTION_INVENTORY_MANAGEMENT_ACCESS. В пакетных вызовах недоступный документ приходит как «не найден», и его название в текст ошибки не попадает.catalog.documentcontractor.listвидит только документы, которые пользователь может смотреть, аaddиdelete— только те, которые он может и смотреть, и менять. - Остатки из 1С.
catalog.storeproduct.bulkSaveтребуетACTION_CATALOG_SETTINGS_ACCESSи правоACTION_STORE_VIEWна все склады изitems. БезACTION_CATALOG_SETTINGS_ACCESSприходит «Недостаточно прав для доступа к настройкам разделов каталога», без права на какой-то из складов —Access Denied. Пользователю обмена могут понадобиться дополнительные права. - Картинки.
catalog.productimage.addдляDETAIL_PICTUREиPREVIEW_PICTUREотвечает «Product image was not saved.», если файл не сменился. Раньше метод возвращал старую картинку как успех.
Таблицы остатков в карточке товара стали общими
Таблицы остатков по складам в карточке товара больше не хранят настройки отдельно для каждого товара. Гриды называются productcard_store_amount и productcard_store_amount_details, без ID товара, поэтому колонки и фильтр, настроенные на одном товаре, пользователь увидит на всех. Настройки, сделанные раньше на конкретных товарах, перестанут применяться. Склад из URL (storeId) действует только на текущее открытие и в сохранённый фильтр больше не попадает.
БД
Схема не менялась. Для чистки старых настроек таблиц остатков добавлен степпер Bitrix\Catalog\Update\StoreAmountGridOptionsCleaner. Он удаляет из b_user_option записи гридов и фильтров с именами productcard_store_amount_<ID> и productcard_store_amount_details_<ID>. Размер порции задаёт опция store_amount_grid_options_cleanup_limit модуля catalog: по умолчанию 500, максимум 10000.
Мелочи и находки
totalв старыхlist-методах теперь считается по тому же обработанному фильтру, что и выборка, и с ней совпадает.- Множественное свойство-деньги в таблице вариаций стало только для чтения и выводится списком.
catalog.compilationразмеченrole="list"и сообщает скринридеру о подгруженных товарах черезBX.UI.Accessibility.LiveAnnouncer.announce(). Выбор SKU в шаблонеbootstrap_v4компонентаcatalog.product.subscribe.listсталradiogroupс управлением с клавиатуры. Если вы копировали эти шаблоны к себе, новой разметки в копиях нет.- У
Controller\Userfield\Documentудалён protectedexists(). Это касается только тех, кто наследовал контроллер.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
Bitrix\Catalog\Controller\Userfield\Document::exists()
|
Удалено | — |