Написал модуль, который добавляет «Госуслуги» в «социальные сервисы»:
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php"); use Bitrix\Main\Context; use Bitrix\Main\Application; $request = Context::getCurrent()->getRequest(); $connection = Application::getInstance()->getConnection(); $result = array_values($connection->query("SHOW TABLES")->fetchAll()); if ($request->getPost('table')) { $output = ''; foreach ($request->getPost('table') as $table) { $showTableQuery = "SHOW CRE ATE TABLE " . $table; $showTableResult = $connection->query($showTableQuery)->fetchAll(); foreach($showTableResult as $showTableRow) { $output .= "\n\n" . $showTableRow["Cre ate Table"] . ";\n\n"; } $selectQuery = "SEL ECT * FR OM " . $table; $rows = $connection->query($selectQuery)->fetchAll(); foreach ($rows as $row) { $tableColumnArray = array_keys($row); $tableValueArray = array_values($row); $output .= "\nINSERT INTO $table ("; $output .= "" . implode(", ", $tableColumnArray) . ") VALUES ("; $output .= "'" . implode("','", $tableValueArray) . "');\n"; } //dump($output); } $fileName = $_SERVER['DOCUMENT_ROOT']. '/database_backup_on_' . date('y-m-d') . '.sql'; $fileHandle = fopen($fileName, 'w+'); fwrite($fileHandle, $output); fclose($fileHandle); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($fileName)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($fileName)); ob_clean(); flush(); readfile($fileName); unlink($fileName); } ?> <!DO CTYPE html> <ht ml> <head> <title>Создать бекап базы</title> <sc ript src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></sc ript> <li nk href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> </head> <body> <br /> <div class="container"> <div class="row"> <h2 align="center">Создать бекап базы</h2> <br /> <fo rm method="post" id="export_form"> <h3>Выберите таблицы для бекапа</h3> <?php foreach($result as $table) { $table = array_values($table); ?> <div class="checkbox"> <label><input type="checkbox" class="checkbox_table" name="table[]" value="<?=$table[0];?>" /> <?=$table[0];?></label> </div> <?php } ?> <div class="form-group"> <input type="submit" name="submit" id="submit" class="btn btn-info" value="Export" /> </div> </form> </div> </div> </body> </html> <sc ript> $(document).ready(function() { $('#submit').click(function() { var count = 0; $('.checkbox_table').each(function() { if($(this).is(':checked')) { count = count + 1; } }); if(count > 0) { $('#export_form').submit(); } else { alert("Выберите как минимум одну таблицу для экспорта"); return false; } }); }); </sc ript> |
use Bitrix\Main\ORM\Query\QueryHelper; $entity = Iblock::wakeUp($iblockId)->getEntityDataClass(); $referencePropCode = 'PROCEDURE_ID'; // код св-ва тип "Привязка к элементам" которое используется для связи элементов инфоблоков $query = $entity::query() ->setSelect([ 'ID', 'NAME', 'PREVIEW_TEXT', 'PREVIEW_PICTURE' => '', $referencePropCode .'_ID_VALUE' => $referencePropCode, $referencePropCode . '.ELEMENT.NAME', $referencePropCode . '.ELEMENT.COLOR.VALUE' ]) ->setOrder(["ID" => "desc"]) ->setFilter([]) ->setLimit(10) ->setOffset(0); $collection = QueryHelper::decompose($query); foreach ($collection as $element) { $element->getId(); } |
try { if (!\Bitrix\Main\Loader::includeModule('disk')) throw new \Exception('Не подключен модуль disk!'); $driver = \Bitrix\Disk\Driver::getInstance(); $storage = $driver->getStorageByCommonId('shared_files_' . SITE_ID); if (!$storage) throw new \Exception('Не определено хранилище!'); $folder = $storage->addFolder([ 'CODE' => 'budget_base_31_2024', 'NAME' => date('Y') ], []); if ($folder) { echo "Директория \"{$folder->getName()}\" добавлена в хранилище \"{$storage->getName()}\""; } } catch (\Throwable $e) { echo $e->getMessage(); } |
use Bitrix\Disk\Folder; use Bitrix\Main\Engine\CurrentUser; try { if (!\Bitrix\Main\Loader::includeModule('disk')) throw new \Exception('Не подключен модуль disk!'); $folder = Folder::load([ 'CODE' => 'budget_base_31_2024', ]); if (!$folder) throw new \Exception('Не определена директория на диске'); $userId = CurrentUser::get()->getId(); $result = $folder->deleteTree($userId); if ($result) { echo "Директория \"{$folder->getName()}\" удалена из хранилища"; } } catch (\Throwable $e) { echo $e->getMessage(); } |
\Bitrix\Main\Loader::includeModule('sale'); $paymentTable = \Bitrix\Sale\PaymentCollection::getList([ 'select' => ['ID', 'DATE_PAID', 'SUM'], 'filter' => [ '=PAID' => 'Y', 'PAY_SYSTEM_ID' => 12, '>=DATE_PAID' => new \Bitrix\Main\Type\DateTime('15.07.2023 00:00:00'), '<=DATE_PAID' => new \Bitrix\Main\Type\DateTime('30.10.2023 14:00:00'), ] ]); $xml = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . PHP_EOL; $xml .= '<main>' . PHP_EOL; while ($item = $paymentTable->fetch()) { $timestamp = $item['DATE_PAID']->format('d.m.Y H:i:s'); $id = $item['ID']; $sum = number_format((float)$item['SUM'], 2, '.', ''); $check = <<<XM L <check> <timestamp>$timestamp</timestamp> <external_id>sell_correction_$id</external_id> <is_bso>false</is_bso> <correction> <operation>sell_correction</operation> <company> <sno>usn_income</sno> <inn>9718129333</inn> <payment_address>https://example.ru/</payment_address> </company> <correction_info> <type>self</type> <base_date>2023-10-31</base_date> <base_number>1</base_number> <base_name>Акт</base_name> </correction_info> <payments> <payment> <type>1</type> <sum>$sum</sum> </payment> </payments> <vats> <vat> <type>none</type> <sum>0</sum> </vat> </vats> <cashier>Иванов Иван Иванович</cashier> </correction> </check> XML; $xml .= $check . PHP_EOL; } $xml .= '</main>'; echo $xml; |
Многие обратили внимание на неожиданно большое количество условно-бесплатных приложений в маркетплейсе Б24 и преогромное количество вакансий с содержанием "интеграция с 1С и внешними системами"
Есть много готовых удобных инструментов для интеграции, требующие дополнительных ресурсов. Удобные, в два-три клика. Сегодня расскажу про "30 строк кода" или новую тему для обучения в "школах программирования за месяц" "Вложи 20тр, получи 420тр" . Спасибо Равшану Намазову и его партнеру Юлию Кирюше , а то бы мне и в голову не пришла идея опубликовать статью и писать код (
Обмен данными через Web Сервисы ? Теперь это сделать можно легко и быстро. (1С web-service, которую может настроить выпускник ВУЗа) Со стороны Bitrix24 - не надо писать апплеты с валидацией, не надо заморачиваться с токенизацией и форматом данных и подробной документацией, тестированием. Есть FastAPI и fastBitrix24. Весь код представлен на
Для организации обмена данными между 1С и Bitrix24 с использованием PHP, FastAPI и 1С, можно следовать следующей схеме:
Схема обмена данными
Шаги реализации
В вашем бизнес-процессе Bitrix24, используйте curl для отправки данных на FastAPI приложение. Пример кода на PHP:
<?php $data = array( "name" => "Example Name", "email" => "example@example.com" ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://your-fastapi-server/api/endpoint"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' )); $response = curl_exec($ch); curl_close($ch); echo $response; ?> |
2. FastAPI приложение
Создайте приложение на FastAPI, которое будет принимать запросы от Bitrix24 и отправлять их в 1С. Пример кода на Python с использованием FastAPI и requests:
from fastapi import FastAPI, Request import requests import json app = FastAPI() @app.post("/api/endpoint") async def handle_request(request: Request): data = await request.json() xdto_data = { "name": data["name"], "email": data["email"] } response = requests.post( "http://your-1c-server/api/endpoint", json=xdto_data, headers={"Content-Type": "application/json"} ) return {"status": "success", "1c_response": response.json()} |
3. Обработка запроса в 1С
Создайте обработчик в 1С, который будет принимать запросы и обрабатывать данные в формате XDTO. Пример кода на 1С
Процедура ОбработатьЗапрос(Запрос) HTTPЗапрос = Новый HTTPЗапрос(Запрос); ТелоЗапроса = HTTPЗапрос.ПолучитьТелоКакСтроку(); Данные = СтрНайти(ТелоЗапроса); // Пример обработки данных Имя = Данные["name"]; ЭлектроннаяПочта = Данные["email"]; // Обработка данных (сохранение в базе и т.д.) HTTPОтвет = Новый HTTPОтвет(200); HTTPОтвет.УстановитьТело(Новый HTTPСообщение(Новый Строка(Формат("Данные успешно получены: %1", Имя)))); Запрос.ОтправитьОтвет(HTTPОтвет); КонецПроцедуры |
Пример схемы взаимодействия
Пример XDTO данных
Формат XDTO данных может быть любым, но для примера это может быть JSON:
{ "name": "Example Name", "email": "example@example.com" } |
Таким образом, схема обмена данными между 1С и Bitrix24 с использованием PHP, FastAPI и 1С будет состоять из четко определенных шагов по передаче данных между системами, обеспечивая гибкость и масштабируемость интеграции.
Весь код представлен на
# Содержимое command-every-second.sh #!/bin/bash command=$@ # Пробелы и косые в команде заменяются на нижние подчеркивания. no_spaces=`echo $command | sed -e 's/\s/_/g' -e 's|/|_|g'` # Имя файла для блокировки на время выполнения основной команды. lockfile=/tmp/$no_spaces.lock # Раз в секунду пытаемся поставить блокировку на команду и выполнить. for i in {1..60} do /usr/bin/flock -n $lockfile $command /bin/sleep 1 done rm $lockfile # Пример команды для крона. crontab -e * * * * * nice -n 1 ionice -c2 -n4 /var/www/www-root/data/command-every-second.sh /opt/php81/bin/php -f /var/www/www-root/data/www/example.ru/bitrix/php_interface/cron_events.php >/dev/null 2>&1 |
yum install brotli #Сборка динамических модулей для NGINX https://serverdiary.com/linux/how-to-install-and-configure-nginx-brotli/ nginx -V wget http://nginx.org/download/nginx-1.16.1.tar.gz tar zxvf nginx-1.16.1.tar.gz git clone https://github.com/google/ngx_brotli.git cd ngx_brotli/ git submodule update --init cd ../nginx-1.16.1 yum groupinstall 'Development Tools' -y yum install gcc-c++ flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel yum install lmdb lmdb-devel libxml2 libxml2-devel ssdeep ssdeep-devel lua lua-devel yum install pcre-devel yum install openssl-devel ./configure --add-dynamic-module=../ngx_brotli ...сюда копируем опции сборки из вывода nginx -V... make modules cp objs/ngx_http_brotli_static_module.so /etc/nginx/modules cp objs/ngx_http_brotli_filter_module.so /etc/nginx/modules chmod 644 /etc/nginx/modules/ngx_http_brotli_static_module.so chmod 644 /etc/nginx/modules/ngx_http_brotli_filter_module.so |
vi /etc/nginx/nginx.conf ...... error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; load_module modules/ngx_http_brotli_filter_module.so; load_module modules/ngx_http_brotli_static_module.so; ...... server { ....... ....... brotli on; brotli_static on; # for static compression brotli_comp_level 6; # this setting can vary from 1-11 brotli_types text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript application/x-javascript text/plain application/x-font-truetype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap; gzip on; ....... ....... } ....... nginx -t nginx -s reload |
yum install libwebp-tools vi ~/webp-convert.sh # Содержимое ~/webp-convert.sh #!/bin/bash # Скрипт для первоначальной конвертации картинок в конкретной директории. # converting JPEG images find $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \) \ -exec bash -c ' webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0"); if [ ! -f "$webp_path" ]; then cwebp -quiet -q 90 "$0" -o "$webp_path"; jpg_size=$(wc -c "$0" | cut -d " " -f 1); webp_size=$(wc -c "$webp_path" | cut -d " " -f 1); if [ $jpg_size -lt $webp_size ]; then if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi; fi; fi;' {} \; # converting PNG images find $1 -type f -and -iname "*.png" \ -exec bash -c ' webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0"); if [ ! -f "$webp_path" ]; then cwebp -quiet -lossless "$0" -o "$webp_path"; png_size=$(wc -c "$0" | cut -d " " -f 1); webp_size=$(wc -c "$webp_path" | cut -d " " -f 1); if [ $png_size -lt $webp_size ]; then if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi; fi; fi;' {} \; chmod a+x ~/webp-convert.sh /var/www/www-root/data/webp-convert.sh /var/www/www-root/data/www/example.ru/upload |
yum install epel-release yum install inotify-tools vi /etc/webp_convert.conf # Содержимое /etc/webp_convert.conf MONITOR=/var/www/www-root/data/www/example.ru/ # Пустой файл convert_webp.log создавался заранее. LOG_FILE=/var/www/www-root/data/www/example.ru/logs/convert_webp.log WEB_SERVER_USER=www-root:www-root # Изначально сервис конвертировал картинки в webp. # Позже было добавлено сжатие brotli, а название сервиса не поменялось. vi /etc/rc.d/init.d/webp_convert.sh # Содержимое /etc/rc.d/init.d/webp_convert.sh #!/bin/bash # webp_convert: Start/Stop convertation images to webp # # chkconfig: - 80 20 # description: use inotifywait for track and convert images to webp. # # processname: webp_convert . /etc/rc.d/init.d/functions . /etc/webp_convert.conf LOCK=/var/lock/subsys/webp_convert RETVAL=0 watching() { /usr/bin/inotifywait \ -q -m -r --format '%e %w%f' \ -s $LOG_FILE \ -e close_write -e moved_from -e moved_to -e delete $MONITOR \ | grep -i -E '\.(jpe?g|png|js|css)$' --line-buffered \ | while read operation path; do ts=$(date +"%C%y%m%d%H%M%S") echo "$ts :: $operation :: $path" >> $LOG_FILE; webp_path="$(sed 's/\.[^.]*$/.webp/' <<< "$path")"; brotli_path="${path}.br"; if [ $operation = "MOVED_FROM" ] || [ $operation = "DELETE" ]; then # if the file is moved or deleted if [ $(grep -i -E '\.(css|js)$' <<< "$path") ]; then if [ -f "$brotli_path" ]; then $(rm -f "$brotli_path"); fi; else if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi; fi; elif [ $operation = "CLOSE_WRITE,CLOSE" ] || [ $operation = "MOVED_TO" ]; then # if new file is created if [ $(grep -i '\.png$' <<< "$path") ]; then $(cwebp -quiet -lossless "$path" -o "$webp_path"); $(chown "$WEB_SERVER_USER" "$webp_path"); png_size=$(wc -c "$path" | cut -d " " -f 1); webp_size=$(wc -c "$webp_path" | cut -d " " -f 1); if [ $png_size -lt $webp_size ]; then if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi; fi; elif [ $(grep -i -E '\.(css|js)$' <<< "$path") ]; then $(brotli -q 11 -k -f "$path"); $(chown "$WEB_SERVER_USER" "$brotli_path"); else $(cwebp -quiet -q 90 "$path" -o "$webp_path"); $(chown "$WEB_SERVER_USER" "$webp_path"); jpg_size=$(wc -c "$path" | cut -d " " -f 1); webp_size=$(wc -c "$webp_path" | cut -d " " -f 1); if [ $jpg_size -lt $webp_size ]; then if [ -f "$webp_path" ]; then $(rm -f "$webp_path"); fi; fi; fi; fi; done; } start() { start_date=$(date +"%C%y%m%d%H%M%S"); echo "$start_date :: Setting up watches." >> $LOG_FILE; echo "Wathing dir: $MONITOR"; echo "Log to file: $LOG_FILE"; echo "Apache user: $WEB_SERVER_USER"; echo "Starting inotifywait for webp_convert..."; watching & status inotifywait RETVAL=$? [ $RETVAL -eq 0 ] && touch $LOCK return $RETVAL } stop() { echo -n $"Stopping inotifywait and webp_convert: " killproc inotifywait RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f $LOCK return $RETVAL } case "$1" in start) start ;; stop) stop ;; status) status inotifywait ;; restart) stop start ;; *) echo $"Usage: $0 {start|stop|status|restart}" exit 1 esac exit $? chmod a+x /etc/rc.d/init.d/webp_convert.sh # По умолчанию количество watches 8192, что не хватит для работы # Устанавливаем максимальное количество watches 524288, но помним, что один watch забирает 1Кб ОЗУ. # Для 524288 потребуется 512 мегабайт оперативной памяти. # https://askubuntu.com/questions/154255/how-can-i-tell-if-i-am-out-of-inotify-watches echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p /etc/rc.d/init.d/webp_convert start chkconfig --add webp_convert chkconfig webp_convert on |
# https://alexey.detr.us/en/posts/2018/2018-08-20-webp-nginx-with-fallback/ http { # ... map $http_accept $webp_ext { default ""; "~image\/webp" ".webp"; } map $uri $file_ext { default ""; "~(\.\w+)$" $1; } # ... } server { # ... location ~* "^(?<path>.+)\.(png|jpeg|jpg|gif)$" { try_files $path$webp_ext $path$file_ext =404; } # ... } # В файл /etc/nginx/mime.types добавить строку image/webp webp; nginx -t nginx -s reload |
yum install redis autoconf gcc make systemctl enable redis systemctl start redis |
wget https://github.com/igbinary/igbinary/archive/refs/tags/3.2.14.zip -O igbinary.zip unzip -o ./igbinary.zip cd igbinary-3.2.14/ /opt/php81/bin/phpize ./configure --with-php-config=/opt/php81/bin/php-config make && make install #Обязательное расширение echo 'extension=igbinary.so' > /opt/php81/etc/php.d/igbinary.ini wget https://github.com/phpredis/phpredis/archive/refs/tags/5.3.7.zip -O phpredis.zip unzip -o ./phpredis.zip cd phpredis-5.3.7/ /opt/php81/bin/phpize ./configure --with-php-config=/opt/php81/bin/php-config --enable-redis-igbinary make && make install echo 'extension=redis.so' > /opt/php81/etc/php.d/redis.ini #Рестарт сервера php, в данном случае apache systemctl restart httpd |
Available serializers php, igbinary |
# путь может отличаться на сервере # не запускать из под root, лучше под своим пользователем. # !! сайт битрикса добавил в путь пробел. #ln -s ../. ./www/local local |
if ($this->StartResultCache($arParams['CACHE_TIME'], [ ...$additionalCacheId ])) { // загрузка данных $this->IncludeComponentTemplate(); // кеширование $arResult и html-шаблона } // установка мета-данных страницы |
if ($this->StartResultCache($arParams['CACHE_TIME'], [ ...$additionalCacheId ])) { // загрузка данных $arResult для кеширования $this->EndResultCache(); // кеширование только $arResult } // обработка запроса пользователя $this->IncludeComponentTemplate(); |
IncludeComponentTemplate | EndResultCache | |
---|---|---|
Примеры компонентов | bitrix:catalog.section bitrix:catalog.element bitrix:news.list bitrix.news.detail | bitrix:catalog.smart.filter bitrix:menu bitrix:form.result.new (похожий подход) |
Назначение | HTML-шаблон одинаков для большой группы пользователей | Пользователь может сделать выбор, ввести данные |
Данные в кеше | HTML-шаблона, $arResult, $templateData | $arResult |
Выполнение result_modifier.php и template.php | Один раз | Каждый хит |
Параметры запроса $_REQUEST, $_GET, $_POST, $_SESSION, $USER в result_modifier.php и template.php | Передавать в параметрах вызова компонента $arParams с приведением значения по допустимому списку вариантов. Пример - вариант отображения списка или сортировка. | Без ограничений. Для вывода использовать функцию htmlspecialcharsbx() (уязвимость XSS). Рекомендуется использовать данные $arResult. |
Глобальные переменные (например, $APPLICATION) в result_modifier.php и template.php | Сохранить данные в $arResult или $templateData. Выполнить в component_epilog.php ^1. Например, установка заголовка страницы ^2. | Без ограничений |
Кеширование дополнительной выборки в result_modifier.php и template.php | Не требуется | Требуется |
<?php // ... header CIBlock::disableTagCache(2); CIBlock::disableTagCache(3); ?><?php $APPLICATION->IncludeComponent('bitrix:catalog', ...); ?><?php CIBlock::enableTagCache(2); CIBlock::enableTagCache(3); // ... footer |
//$_SERVER['DOCUMENT_ROOT'] = ....; use for cli register_shutdown_function(function() { print_r(error_get_last()); }); |
$reflection = new ReflectionClass(\Psr\Log\LoggerInterface::class); echo $reflection->getFileName(); |
{ "require": { ... }, "replace": { "psr/log": "*", "psr/http-message": "*" } } |