Разработка модуля отчетов 1С-Битрикс
Стандартные отчёты 1С-Битрикс закрывают базовые потребности: продажи по периодам, остатки склада, активность пользователей. Но как только появляется задача «покажи конверсию по воронке в разбивке по менеджерам за квартал с фильтрацией по региону» — штатные инструменты заканчиваются. Здесь нужен модуль с собственной логикой агрегации.
Проблема с нативными отчётами
В Битрикс отчёты разбросаны по модулям: CRM (crm), интернет-магазин (sale), склад (catalog). Данные не агрегируются между модулями. Нет конструктора кросс-модульных отчётов. Нет возможности сохранять произвольные выборки и расписывать их доставку.
Структура модуля
Модуль vendor.reports регистрируется через стандартный инсталлятор. Основные сущности:
-
ReportTable (
b_vendor_report) — реестр отчётов: id, name, type, config (JSON с параметрами выборки), created_by, access_groups -
ReportScheduleTable (
b_vendor_report_schedule) — расписание автоматической генерации: report_id, cron, format, recipients, last_run -
ReportSnapshotTable (
b_vendor_report_snapshot) — кэш сгенерированных данных: report_id, data (jsonb/text), generated_at, expires_at -
ReportColumnTable (
b_vendor_report_column) — конфигурация колонок: report_id, alias, source, aggregation, format
Источники данных
Каждый источник реализует DataSourceInterface:
interface DataSourceInterface
{
public function getFields(): array;
public function fetchData(array $filter, array $select, array $group): array;
}
Встроенные источники:
-
SaleOrderSource— данные изb_sale_order,b_sale_order_props,b_sale_basket -
CrmLeadSource— изb_crm_lead,b_crm_lead_field_multi -
CrmDealSource— изb_crm_deal,b_crm_deal_stage -
CatalogProductSource— изb_iblock_element,b_catalog_price,b_catalog_store_product -
UserActivitySource— изb_user,b_user_auth_log,b_stat_adv_back
Источники можно комбинировать через JOIN-стратегию:
$builder = new ReportQueryBuilder();
$builder
->from('SaleOrderSource', 'o')
->join('CrmDealSource', 'd', 'o.UF_CRM_DEAL = d.ID')
->select(['o.DATE_INSERT', 'o.PRICE', 'd.STAGE_ID', 'd.ASSIGNED_BY'])
->filter(['>=o.DATE_INSERT' => $dateFrom, '<=o.DATE_INSERT' => $dateTo])
->groupBy(['d.ASSIGNED_BY', 'd.STAGE_ID']);
Агрегации и вычисляемые поля
Модуль поддерживает стандартные агрегаты: COUNT, SUM, AVG, MIN, MAX. Вычисляемые поля описываются выражениями:
// Конверсия лид → сделка в процентах
'conversion' => [
'expression' => 'ROUND(COUNT(deals.ID) * 100.0 / COUNT(leads.ID), 2)',
'label' => 'Конверсия, %',
'type' => 'percent',
]
Результирующий SQL формируется через QueryBuilder с биндингом параметров — прямой конкатенации строк нет.
Визуализация
Данные отдаются на фронтенд в формате JSON через AJAX-эндпоинт:
GET /bitrix/components/vendor/reports.view/ajax.php?report_id=12&date_from=2024-01-01&date_to=2024-03-31
На стороне браузера — Chart.js или Highcharts (в зависимости от лицензии проекта). Поддерживаются типы: линейный график, столбчатая диаграмма, круговая, таблица с сортировкой и пагинацией.
Таблицы отрисовываются через стандартный CAdminList в административном разделе или через кастомный компонент на фронтенде сайта.
Экспорт
Сгенерированный отчёт экспортируется в:
-
Excel (
.xlsx) через библиотекуPhpSpreadsheet— устанавливается в/local/vendor/ - CSV с выбором разделителя и кодировки
-
PDF через
mPDFилиTCPDF— для отчётов с фиксированной вёрсткой
$exporter = ReportExporterFactory::create('xlsx');
$exporter->setData($reportData)->setColumns($columns)->download('report_' . date('Y-m-d') . '.xlsx');
Расписание и доставка
Агент \Vendor\Reports\Agent\ScheduleAgent::run() запускается раз в час, проверяет b_vendor_report_schedule на записи с next_run <= NOW(). Генерирует отчёт, сохраняет снапшот в b_vendor_report_snapshot, отправляет получателям письмо с вложением через \Bitrix\Main\Mail\Event::send().
Формат cron-выражения совместим со стандартом: 0 8 * * 1 — каждый понедельник в 8:00.
Права доступа
Доступ к отчётам управляется через группы пользователей Битрикс. В b_vendor_report поле access_groups хранит JSON-массив ID групп. Проверка:
$userGroups = $USER->GetUserGroupArray();
$reportGroups = json_decode($report['ACCESS_GROUPS'], true);
if (!array_intersect($userGroups, $reportGroups) && !$USER->IsAdmin()) {
throw new AccessDeniedException('Нет доступа к отчёту');
}
Кэширование
Тяжёлые отчёты (данные за год, миллионы строк) кэшируются в b_vendor_report_snapshot. Срок жизни кэша задаётся в настройках каждого отчёта. При запросе сначала проверяется снапшот с expires_at > NOW(), если есть — отдаётся из кэша. Принудительный сброс — через кнопку в административном интерфейсе или по событию изменения данных (OnAfterSaleOrderUpdate).
Сроки разработки
| Этап | Срок |
|---|---|
| Архитектура, ORM-таблицы, инсталлятор | 2 дня |
| Источники данных (3-4 модуля Битрикс) | 3 дня |
| QueryBuilder, агрегации, вычисляемые поля | 2 дня |
| Визуализация (Chart.js + таблица) | 2 дня |
| Экспорт Excel/CSV | 1 день |
| Расписание и доставка по email | 1 день |
| Права доступа, кэширование | 1 день |
| Административный интерфейс, тестирование | 2 дня |
Итого: 14 рабочих дней. Сложные кросс-модульные отчёты с нестандартными источниками оцениваются отдельно после анализа структуры данных.







