Development of a 1C-Bitrix reporting module

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Development of Reports Module for 1C-Bitrix

Standard 1C-Bitrix reports cover basic needs: sales by period, warehouse balances, user activity. But as soon as a task appears like "show conversion funnel by manager for a quarter filtered by region" — the standard tools reach their limit. Here you need a module with custom aggregation logic.

Problem with Native Reports

In Bitrix, reports are scattered across modules: CRM (crm), e-commerce (sale), warehouse (catalog). Data is not aggregated between modules. There is no cross-module report builder. No way to save arbitrary selections and schedule delivery.

Module Structure

The module vendor.reports registers via the standard installer. Main entities:

  • ReportTable (b_vendor_report) — report registry: id, name, type, config (JSON with selection parameters), created_by, access_groups
  • ReportScheduleTable (b_vendor_report_schedule) — automatic generation schedule: report_id, cron, format, recipients, last_run
  • ReportSnapshotTable (b_vendor_report_snapshot) — cache of generated data: report_id, data (jsonb/text), generated_at, expires_at
  • ReportColumnTable (b_vendor_report_column) — column configuration: report_id, alias, source, aggregation, format

Data Sources

Each source implements DataSourceInterface:

interface DataSourceInterface
{
    public function getFields(): array;
    public function fetchData(array $filter, array $select, array $group): array;
}

Built-in sources:

  • SaleOrderSource — data from b_sale_order, b_sale_order_props, b_sale_basket
  • CrmLeadSource — from b_crm_lead, b_crm_lead_field_multi
  • CrmDealSource — from b_crm_deal, b_crm_deal_stage
  • CatalogProductSource — from b_iblock_element, b_catalog_price, b_catalog_store_product
  • UserActivitySource — from b_user, b_user_auth_log, b_stat_adv_back

Sources can be combined via JOIN strategy:

$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']);

Aggregations and Computed Fields

The module supports standard aggregates: COUNT, SUM, AVG, MIN, MAX. Computed fields are described with expressions:

// Lead to deal conversion percentage
'conversion' => [
    'expression' => 'ROUND(COUNT(deals.ID) * 100.0 / COUNT(leads.ID), 2)',
    'label'      => 'Conversion, %',
    'type'       => 'percent',
]

The resulting SQL is formed via QueryBuilder with parameter binding — no direct string concatenation.

Visualization

Data is sent to the frontend as JSON via AJAX endpoint:

GET /bitrix/components/vendor/reports.view/ajax.php?report_id=12&date_from=2024-01-01&date_to=2024-03-31

On the browser side — Chart.js or Highcharts (depending on project license). Supported types: line chart, bar chart, pie chart, table with sorting and pagination.

Tables are rendered via standard CAdminList in the admin section or via custom component on site frontend.

Export

The generated report is exported to:

  • Excel (.xlsx) via PhpSpreadsheet library — installed in /local/vendor/
  • CSV with choice of delimiter and encoding
  • PDF via mPDF or TCPDF — for reports with fixed layout
$exporter = ReportExporterFactory::create('xlsx');
$exporter->setData($reportData)->setColumns($columns)->download('report_' . date('Y-m-d') . '.xlsx');

Schedule and Delivery

The agent \Vendor\Reports\Agent\ScheduleAgent::run() runs hourly, checks b_vendor_report_schedule for records with next_run <= NOW(). Generates the report, saves snapshot to b_vendor_report_snapshot, sends recipients email with attachment via \Bitrix\Main\Mail\Event::send().

Cron expression format is standard-compatible: 0 8 * * 1 — every Monday at 8:00.

Access Control

Report access is managed via Bitrix user groups. The access_groups field in b_vendor_report stores a JSON array of group IDs. Check:

$userGroups = $USER->GetUserGroupArray();
$reportGroups = json_decode($report['ACCESS_GROUPS'], true);
if (!array_intersect($userGroups, $reportGroups) && !$USER->IsAdmin()) {
    throw new AccessDeniedException('No access to report');
}

Caching

Heavy reports (data for a year, millions of rows) are cached in b_vendor_report_snapshot. Cache lifetime is set in settings for each report. On request, the snapshot with expires_at > NOW() is checked first; if present, it's returned from cache. Force clear — via button in admin interface or on data change event (OnAfterSaleOrderUpdate).

Development Timeline

Stage Duration
Architecture, ORM tables, installer 2 days
Data sources (3-4 Bitrix modules) 3 days
QueryBuilder, aggregations, computed fields 2 days
Visualization (Chart.js + table) 2 days
Excel/CSV export 1 day
Schedule and email delivery 1 day
Access control, caching 1 day
Admin interface, testing 2 days

Total: 14 working days. Complex cross-module reports with non-standard sources are estimated separately after data structure analysis.