Contract Prices for Dealers in 1C-Bitrix: Setup and Integration

The Problem: Fixed Prices for Each Dealer A dealer receives a catalog with prices that don't match the published ones. Group discounts don't provide the needed flexibility: each wholesaler requires a personal price as per contract. The standard `CCatalogDiscount` mechanism is tied to price types

Our competencies:

Frequently Asked Questions

The Problem: Fixed Prices for Each Dealer

A dealer receives a catalog with prices that don't match the published ones. Group discounts don't provide the needed flexibility: each wholesaler requires a personal price as per contract. The standard CCatalogDiscount mechanism is tied to price types and percentages—you can't fix an absolute value that remains unchanged when the base price is recalculated. Contract prices solve this: a fixed record in a separate storage overrides any discounts, but only for a specific counterparty for the contract duration.

Our implementation uses Highload-blocks and a custom price provider, processing up to 100,000 contract records in 50ms per order. We rely on our own experience: over 7 years working with Bitrix, 50+ projects for dealer networks. We guarantee contract price priority over any promotions. Redis caching with invalidation on contract change ensures minimal response time for 500 concurrent users. First demonstration of operation—3 days after project start.

Why Standard Discounts Don't Work

The catalog module provides discounts (CCatalogDiscount) and price types (b_catalog_group). Discounts are set as a percentage or amount, tied to a price type. For contract prices this is unsuitable: if the base price changes, the contract price must remain at the previous level. Additionally, discounts apply to the whole group—you cannot set a personal price for one dealer without affecting others. More about price types can be found in the 1C-Bitrix documentation.

How We Store Contract Prices

We create a Highload-block dealer_contract_prices. The table is generated automatically based on the block name. Fields:

Field Type Description
UF_DEALER_ID Integer Dealer company ID
UF_PRODUCT_ID Integer Product ID
UF_XML_ID String Article (for 1C sync)
UF_PRICE Double Contract price
UF_CURRENCY String Currency
UF_DATE_FROM DateTime Start of validity
UF_DATE_TO DateTime End of validity
UF_ACTIVE Boolean Activity status

An index on (UF_DEALER_ID, UF_PRODUCT_ID, UF_ACTIVE) is mandatory: without it, queries on 100,000+ records degrade by tens of times. We use a composite index to cover the main scenario: WHERE dealer=... AND product=... AND active=1.

How the Application Logic Works

We implement a custom price provider hooked to the OnSaleBasketBeforeSaved event or via Bitrix\Catalog\v2\Price extension. Algorithm:

  1. Determine the current user's company from the session.
  2. Search in dealer_contract_prices for a record matching dealer, product, dates, and activity.
  3. If found, use UF_PRICE as the final price, ignoring the standard price type.
  4. If not found, fall back to the dealer's price type based on their group.

The result is cached in Redis with key contract_price_{dealerId}_{productId} for 1 hour. Invalidation occurs on record update in the Highload-block via OnAfterHLBlockElementUpdate. This approach is 5 times faster than iterating all catalog module discounts with 50,000 products.

How to Load Prices from 1C

Contract prices are managed in 1C. Synchronization via an agent:

  1. 1C exports XML with dealer (1C code), article, price, and period.
  2. Agent finds the dealer by UF_1C_ID in the company Highload-block, product by XML_ID.
  3. Creates or updates the record in dealer_contract_prices.
  4. Marks expired records with UF_ACTIVE = false.

Frequency: event-driven on price list updates, or via cron twice daily. Synchronization errors are logged: if a contract price is missing, the dealer sees the base price with a warning.

Why Redis Over File Cache for Contract Prices?

The file cache in Bitrix (BXCache) works at the page and component level. For contract prices, we need a cache that quickly serves single records. Redis in memory provides response time <1 ms. Important to configure tagged caching: invalidation by tag contract:dealer:{id} on contract change. This avoids full cache flush and saves server resources.

How We Test Load with Contract Prices

Before launch, we conduct load testing. Conditions: 500 concurrent users, 100,000 contract records, basket query with 20-30 items. Expected response time—no more than 100 ms. We use ab or locust. If issues arise, we add middleware to smooth peaks. Results are documented in a report.

What Is Included in Contract Price Setup

Component Description
Highload-block Fields for your nomenclature, indexes, cache settings
Price provider Custom with Redis support, OnSaleBasketBeforeSaved event
Sync agent Read XML from 1C, update records, log errors
Catalog display "Contract" label in result_modifier.php
Testing Load test: 500 simultaneous orders, response <100 ms
Training Documentation, tutorial on updating directories

Timeline and Team Experience

Storage and logic setup—1-2 weeks. With 1C export integration—2-3 weeks. We provide a free project assessment with an estimate broken down into stages. Our team includes certified 1C-Bitrix specialists with over 5 years of experience.

Get a consultation for your project—we'll calculate the timeline and cost for your catalog. Contact us to discuss the details.