Обновление цен и остатков из текстового файла, в каталоге 1C-Битрикс.

Просмотров: 1308

Задача: прочитать txt-файл с ценами и остатками, который выгружает программа учета по FTP, и обновить эти данные в каталоге. Скрипт должен автоматически запускаться каждую ночь в 00:00 по расписанию.

Считываем txt файл по строчно, с формированием массива

Каждый день, в 11 вечера, программа учета выгружает по FTP текстовый файл в котором каждая строка содержит:

  • Уникальный ID товара, который хранится в свойстве элемента инфоблока. В моем случае, свойство имеет код ATT_UNICAL_ID
  • Число с остатком товара
  • Число с новой ценой товара
Обновление цен и остатков, в каталоге 1C-Битрикс, из текстового файла.

Создаем файл update_products_cron.php и размещаем его в директории /bitrix/php_interface/cron_events. В этом файле, сразу подключаем ядро 1С-Битрикс и модули инфоблоков и каталога, пригодятся дальше. Отключаем сбор статистики и сбрасываем лимит времени на выполнение, в переменной $IBlockId храниться ID инфоблока товары которого нужно обновлять.

Для начала, чтобы считать файл с помощью PHP и преобразовать каждую строку в элемент массива, можно использовать функцию file():.

$_SERVER['DOCUMENT_ROOT'] = realpath(dirname(__FILE__) . '/../../..');

define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_NO_ACCELERATOR_RESET', true);

require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');

@set_time_limit(0);
@ignore_user_abort(true);

use Bitrix\Main\Loader;
Loader::includeModule('iblock');
Loader::includeModule('catalog');

$Update_Catalog = $_SERVER['DOCUMENT_ROOT'] . '/upload/txt_exchange/Update_Catalog.txt';

if (file_exists($Update_Catalog)) {
    $IBlockId = 1;
    $arFileLines = file($Update_Catalog, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    var_dump($arFileLines)
}
Функция file() считывает файл построчно и возвращает массив строк. Опции FILE_IGNORE_NEW_LINES и FILE_SKIP_EMPTY_LINES удаляют переносы строк и пустые строки соответственно. В примере добавлена проверка на существование файла с помощью функции file_exists().

В итоге, каждая строка из текстового файла станет элементом массива $arFileLines

Получаем элементы раздела зная значения свойства

Дальше нам нужно разбить каждую строку на массив, с помощью функции explode(), в качестве разделителя использовав пробел. Прходимся с помощью foreach по массиву со строками ($arFileLines) и превращаем каждую строку в массив:

foreach ($arFileLines as $arFileLine) {
    $arItem = explode(' ', $arFileLine); // Разбираем каждую строку на массив
    var_dump($arItem)
}

Теперь нужно найти элементы инфоблока, зная первый ключ этого массива, по значению свойства ATT_UNICAL_ID. Внутри вывода каждого элемента, обновляем доступное количество товара и стоимость товара. Привожу скрипт целиком, пояснения ниже.

$_SERVER['DOCUMENT_ROOT'] = realpath(dirname(__FILE__) . '/../../..');

define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_NO_ACCELERATOR_RESET', true);

require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');

@set_time_limit(0);
@ignore_user_abort(true);

use Bitrix\Main\Loader;

Loader::includeModule('iblock');
Loader::includeModule('catalog');

$Update_Catalog = $_SERVER['DOCUMENT_ROOT'] . '/upload/txt_exchange/Update_Catalog.txt';
if (file_exists($Update_Catalog)) {
    $IBlockId = 1;
    $arFileLines = file($Update_Catalog, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

    foreach ($arFileLines as $arFileLine) {
        $arItem = explode(' ', $arFileLine);

        $getItem = CIBlockElement::GetList (
            false,
            Array(
                'IBLOCK_ID' => $IBlockId,
                'PROPERTY_ATT_UNICAL_ID' => $arItem['0']
            ),
            false,
            false,
            Array(
                'ID'
            )
        );
        while($arFields = $getItem->Fetch())
        {
            $productFields['QUANTITY'] =$arItem['1'];
            CCatalogProduct::Update(
                $arFields['ID'],
                $productFields
            );
            CPrice::SetBasePrice(
                $arFields['ID'], 
                $arItem['2'],
                "RUB"
            );
        }
    }
}
  • В переборе массива $arFileLines приводим каждую строку к массиву $arItem и с помощью CIBlockElement::GetList получаем ID товара с кодом ATT_UNICAL_ID (значение в первом элементе массива $arItem['0'], из строки).
  • С помощью CCatalogProduct::Update устанавливаем доступное количество товара из второго элемента массива $arItem['1']
  • С помощью CPrice::SetBasePrice устанавливаем цену товара из третьего элемента массива $arItem['2']

Далее, вешаем этот скрипт на выполнение, с помощью crontab на каждый день в 00:00. Скрипт отрабатывает достаточно быстро и не подвешивает сайт. В моем случае, в каталоге около 50 000 товаров, обрабатываются за 7-8 минут.

Михаил Базаров 22.03.2023
Если не нужно обновлять минусовые остатки, если минус - то ставим в 0
Код
if ($arItem['1'] > 0) {
    $ostatok = $arItem['1'];
} else {
    $ostatok = 0;
 }