Setting up a price comparison dashboard with 1C-Bitrix competitors

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
    1173
  • 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
    745
  • 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

Configuring a Competitor Price Comparison Dashboard in 1C-Bitrix

A category manager should not have to open each individual product card just to spot a pricing problem. A dashboard is a consolidated screen that shows in 30 seconds which products are losing on price, by how much, how many of them there are, and allows a decision to be made right there. The goal of the dashboard is to reduce the time from problem detection to action.

Data Sources

The dashboard is built on three tables:

  • bl_competitor_prices — current competitor prices
  • bl_product_price_position — aggregates (competitor min/max/avg, our rank)
  • b_catalog_price — our current prices

Data in the aggregate table is updated by an agent after each competitor price synchronization.

Key Dashboard Metrics

Price position — distribution of products by rank:

SELECT rank, COUNT(*) as product_count
FROM bl_product_price_position
WHERE updated_at > NOW() - INTERVAL '24 hours'
GROUP BY rank
ORDER BY rank;

Displayed as a bar chart: "1st place — 34 products, 2nd place — 87, 3rd place — 124..."

Products where we are more expensive than the competitor minimum:

SELECT
    ie.ID,
    ie.NAME,
    ppp.our_price,
    ppp.min_comp as competitor_min,
    ROUND((ppp.our_price - ppp.min_comp) / ppp.min_comp * 100, 1) as diff_pct,
    ppp.rank
FROM bl_product_price_position ppp
JOIN b_iblock_element ie ON ie.ID = ppp.product_id
WHERE ppp.our_price > ppp.min_comp
  AND ppp.min_comp > 0
ORDER BY diff_pct DESC
LIMIT 50;

Estimated lost revenue:

SELECT
    SUM(
        (ppp.our_price - ppp.min_comp) / ppp.our_price * oe.order_count * ppp.our_price
    ) as estimated_lost_revenue
FROM bl_product_price_position ppp
JOIN (
    SELECT product_id, COUNT(DISTINCT order_id) as order_count
    FROM b_sale_basket
    WHERE date_insert > NOW() - INTERVAL '30 days'
    GROUP BY product_id
) oe ON oe.product_id = ppp.product_id
WHERE ppp.our_price > ppp.min_comp;

Dashboard Page Structure

The page at /bitrix/admin/price_dashboard.php consists of several blocks:

Top block — summary KPIs:

  • Total products under monitoring: N
  • Of those, more expensive than competitors: N (XX%)
  • Average price position: X.X
  • Products in 1st place: N

Middle block — heatmap by catalog section:

// Aggregates by section
$sectionStats = \Bitrix\Main\Application::getConnection()->query("
    SELECT
        s.NAME as section_name,
        COUNT(*) as total_products,
        COUNT(CASE WHEN ppp.rank = 1 THEN 1 END) as on_first_place,
        ROUND(AVG(ppp.rank), 1) as avg_rank,
        COUNT(CASE WHEN ppp.our_price > ppp.min_comp THEN 1 END) as losing_count
    FROM bl_product_price_position ppp
    JOIN b_iblock_element ie ON ie.ID = ppp.product_id
    JOIN b_iblock_section s ON s.ID = ie.IBLOCK_SECTION_ID
    GROUP BY s.ID, s.NAME
    ORDER BY losing_count DESC
")->fetchAll();

Each section is a row with color coding: green (>70% of products in 1st place), yellow (50–70%), red (<50%).

Bottom block — problem products table with action buttons:

Product SKU Our price Competitor min Difference Rank [Change price]

The "Change price" button enables inline editing with AJAX save to b_catalog_price. On save, bl_price_change_log records: who changed, when, from which price, to which price.

AJAX Data Refresh

The dashboard updates without a page reload — the "Refresh data" button triggers synchronization with the price source:

document.getElementById('refresh-btn').addEventListener('click', function() {
    this.disabled = true;
    fetch('/bitrix/services/main/ajax.php?action=PriceDashboard:refresh', {
        method: 'POST',
        headers: {'X-Bitrix-Csrf-Token': BX.bitrix_sessid()}
    })
    .then(r => r.json())
    .then(data => {
        if (data.status === 'ok') location.reload();
    });
});

Excel Export

The "Export to Excel" button generates a report via \PhpOffice\PhpSpreadsheet: all products with competitor prices in separate columns (one column per competitor), our price, rank, and recommended price (if the repricer is configured).

Timeline

Phase Duration
Aggregate queries and optimization 2 days
Top KPI block + chart.js 1 day
Section heatmap 1 day
Problem products table + inline editing 2 days
Excel export 1 day
Testing 1 day
Total 8–9 days