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:
- Determine the current user's company from the session.
- Search in
dealer_contract_prices for a record matching dealer, product, dates, and activity.
- If found, use
UF_PRICE as the final price, ignoring the standard price type.
- 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:
- 1C exports XML with dealer (1C code), article, price, and period.
- Agent finds the dealer by
UF_1C_ID in the company Highload-block, product by XML_ID.
- Creates or updates the record in
dealer_contract_prices.
- 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.
B2B Portal Development on 1C-Bitrix
A dealer in Krasnoyarsk enters 50 SKUs, needs an instant invoice with his own price, credit limit, and delivery from the nearest warehouse. A retail catalog won’t cut it. We build such portals on 1C-Bitrix — with personalized price matrices, dealer cabinets, and real-time 1C integration. Over 50 B2B portals launched, 12-month warranty, certified 1C-Bitrix specialists with 10+ years in development. Submit a request for a free audit of your current pricing system — we will analyze your structure and propose a tailored architecture.
How does pricing work in a B2B portal?
Retail has one price. B2B has a matrix: price types in b_catalog_price multiplied by user groups, cumulative discounts, currency conversions, contractual conditions. A mistake here sinks the project.
Price types and user groups. Bitrix sets types via CCatalogGroup. Standard set: retail, small wholesale, wholesale, dealer, distributor. Each counterparty belongs to a user group, the group to a price type. Reality is more complex: one dealer may see wholesale prices for electronics and distributor prices for accessories. That requires custom logic in the OnSaleBasketItemBeforePriceSave handler.
Discounts — progressive by volume (from 100 pieces → minus 5%, from 500 → minus 12%), cumulative over a period, seasonal, category-based. Combined through priorities in b_sale_discount. With 20+ rules, debugging becomes a quest.
Credit limits. The counterparty gets a shipping credit threshold. Current debt syncs from 1C via the register РасчетыСКонтрагентами (Settlements with Counterparties). Exceeding the limit blocks order placement.
Contract prices — the price list is tied to a specific contract: validity period, number, prolongation conditions. Expiration switches prices to base ones automatically. Implemented through order custom fields and the OnSaleComponentOrderProperties handler.
Currency — mandatory for foreign trade. Conversion at the Central Bank rate (CCurrencyRates::ConvertCurrency()) or a fixed contract rate.
Compare CommerceML and REST for price sync: CommerceML is simpler but 4x slower on catalogs over 10,000 items, and it can’t handle credit limits. REST API via 1C HTTP service is 3x faster and fully flexible but requires 1C-side modifications. According to the 1C-Bitrix Developer's Guide, real-time sync of directories and balances is critical for B2B portals.
What does the dealer cabinet include?
-
Orders — full history with filtering by statuses, dates, amounts. Repeat previous order in one click — saves hours for regular purchases.
- Finances — balance of mutual settlements, reconciliation statement, payment history. No more waiting three days for accounting — data available instantly, pulled from 1C via REST or CommerceML.
- Documents — invoices, waybills, sales invoices, UPD, certificates. Generated in 1C, PDF pushed to the portal via integration. One-click download.
- Dealer employee management — admin creates accounts with role permissions: purchasing manager places orders, accountant sees only finances, director sees the big picture. Extended standard Bitrix user groups.
Quick order: SKU + quantity = invoice
A B2B client doesn’t browse beautiful cards. They need a form: SKU, quantity, next line.
- Quick order form — auto-suggest of name and price when entering SKU. AJAX search by
b_iblock_element.XML_ID or PROPERTY_ARTICLE. 50 items in 3 minutes.
- Import from Excel/CSV — client exports from their system, uploads to the portal. Auto-matching of SKUs, availability check, order generation. Parsing via
PHPExcel or PhpSpreadsheet.
- Basket with full information — weight, volume, number of packages, estimated delivery cost before checkout.
Why is 1C integration critical for a B2B portal?
Without up-to-date data from 1C, the portal is useless. Manager changed the price of nails — in 15 minutes a dealer in Krasnoyarsk sees the new price.
| Data |
Direction |
Mechanism |
| Catalog, characteristics |
1C → Portal |
CommerceML or REST, 15–60 min |
| Prices by type and counterparty |
1C → Portal |
REST API, by event or schedule |
| Stock balances by warehouse |
1C → Portal |
REST, 5–15 min or real-time via 1C HTTP service |
| Orders |
Portal → 1C |
REST, real-time |
| Statuses, shipments |
1C → Portal |
By event |
| Mutual settlements |
1C → Portal |
1–2 times per day |
| Documents (PDF) |
1C → Portal |
By event |
We often use a hybrid: CommerceML for catalog, REST for prices, stocks, and documents.
EDI: legally significant paperless exchange
For large B2B projects:
- Providers — Kontur.Diadoc, SBIS, Kaluga Astral. Invoices, certificates, waybills in electronic form with legal force.
- CEP — qualified electronic signature. The counterparty signs right in the cabinet.
- Roaming between operators — without it, half of partners with a different EDI operator are left out.
Multi-branch operation
- Regional warehouses — client sees stock of the nearest warehouse and can choose the shipping one. Product available in Novosibirsk but not in Moscow → portal shows both options with different lead times.
- Automatic manager assignment — dealer from Krasnodar works with Ivan, from Yekaterinburg with Marina. Based on
UF_REGION in the counterparty card.
- Local conditions — minimum order amount, delivery terms, lead times — differ by region.
B2B Portal Development Process
B2B portal development includes five stages.
| Stage |
Timeline |
Result |
| Process audit |
1–2 weeks |
Business process diagram, integration map |
| Design |
2–3 weeks |
Architecture, prototypes, 1C exchange specification |
| Development |
4–8 weeks |
Cabinets, pricing mechanics, integrations, document flow |
| Testing |
1–2 weeks |
Functional, integration, load testing on real data |
| Pilot |
2–3 weeks |
5–10 dealers, feedback, refinements |
What is included in the work
- Complete project documentation (terms of reference, architecture diagram, integration protocols)
- Server environment setup and deployment (On-Premise or cloud)
- Migration of all user data and configurations
- Portal administrator training (2 online sessions)
- Warranty support for 12 months with response within 4 hours
After launch — technical support and development. A B2B portal is a living system that evolves with the business.
Average time savings for a manager on order processing — up to 20 hours per week, which translates to approximately $15,000 annual savings per dealer. A self-built portal typically takes 6 months to develop, while a Bitrix-based solution is ready in 2 months.
Typical mistakes to avoid at the start
- Verify price types and user groups before going live.
- Keep discount rules to a minimum (3–4) — 20+ rules cause debugging nightmares.
- Test 1C integration on real data during pilot.
- Grant dealer access only after load testing with 50 concurrent users.
Contact us to discuss your project — we will prepare a preliminary estimate and propose the optimal solution. Get your free audit of pricing mechanics today.