Development of a wholesale section for an online store using 1C-Bitrix

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
    1175
  • 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

Wholesale Section Development for E-Commerce in 1C-Bitrix

A wholesale section within a retail store is not a separate website, but parallel business logic for authorized B2B clients. One catalog, one admin panel, but different prices, order rules, and documentation workflow. In Bitrix, this task is solved via price types, user groups, and refinements to the sale module.

Price Types and Access Rights: B2B Foundation

All B2B price architecture is built around the b_catalog_group table. Each record is a separate price type: "Retail", "Wholesale", "Dealer", "VIP". Binding of price type to user group is set in catalog module settings — section "Price Types", column "Buyer Groups".

The mechanism works like this:

  1. User logs in and is placed in a group (e.g., "Wholesalers")
  2. Catalog component calls CCatalogProduct::GetOptimalPrice(), which iterates through available price types for current user
  3. Returns the smallest of available prices (or highest priority price — depends on setting)

In practice, one price type mechanism is insufficient. Real wholesale price lists are more complex:

  • Individual discounts — contractor A gets 12% discount, contractor B — 18%. Solution: separate user group for each major contractor with their own price type, or cart rules (sale.discount) with group condition.
  • Threshold prices — buying from 100 units costs one price, from 1000 — another. In standard Bitrix, this is implemented via quantity ranges in product prices (b_catalog_price supports QUANTITY_FROM / QUANTITY_TO).
  • Multi-currency prices — wholesale clients from different countries see prices in their currency. Price types can be set in different currencies, conversion — via currency module.

Important nuance: price type is not directly bound to user — only to group. If individual pricing is needed for 200 contractors, creating 200 groups is impractical. In such cases, an OnGetOptimalPrice event handler is written, which pulls price from external source (1C, ERP) via cache.

Scenario Bitrix Implementation Limitations
3–5 price levels Price types + user groups Standard mechanism, works out of box
Threshold prices by quantity Ranges in b_catalog_price Not displayed in card without template refinement
Individual contractor pricing OnGetOptimalPrice event + external source Requires caching, otherwise — request to 1C per product
Category discount for group Cart rules sale.discount Not visible in catalog until cart addition

Minimum Order Quantity and Order Increment

For wholesale products, two parameters are needed: minimum order quantity and increment (step). Standard Bitrix has "Unit of Measure Coefficient" field (b_catalog_measure_ratio), but it only works as multiplier when adding to cart.

Full implementation requires:

  • Iblock property "Minimum Order Quantity" — checked when adding to cart via OnSaleBasketItemBeforeUpdate handler
  • "Order Increment" property — quantity is rounded up to nearest increment value
  • Display in product card — instead of standard quantity field, show selector with steps (10, 20, 30…) or field with validation

Quick Order by Article Number and Excel Upload

Wholesale client doesn't browse catalog — they know article numbers. Need quick order interface:

  1. "Article + Quantity" form — row list where data is entered. Server-side article matches PROPERTY_CML2_ARTICLE or XML_ID of iblock element, product is added to cart.

  2. Excel/CSV upload — client exports file from their accounting system. Parser on Bitrix side (PhpSpreadsheet library, connected via Composer) parses file, finds products, forms cart. Unmatched articles shown in report.

  3. Repeat previous order — button in personal area copying previous order composition to current cart. Implemented via Bitrix\Sale\Basket::loadItemsForOrder().

Credit Limits and Deferred Payment

In B2B, payment often happens after the fact. Credit limit mechanism:

  • In contractor profile (extended user properties or UF_* fields) limit and current debt are stored
  • When placing order, OnSaleOrderBeforeSaved handler checks: order sum + current debt ≤ credit limit
  • If limit exceeded — order not placed, user sees message with available remainder
  • After payment (payment status change via 1C exchange or manually) debt is recalculated

Document Management in Personal Area

Wholesale client expects to see in personal area:

  • Invoices — generated automatically on order creation. PDF template assembled via print handler (sale_print) or external library (TCPDF/mPDF).
  • Reconciliation acts — pulled from 1C via exchange. Stored as files bound to user via highload-block.
  • Delivery notes and UPD — likewise, exported from 1C. For legal validity can be signed with e-signature.
  • Order history with filtering — by period, status, amount. Standard sale.personal.order component is refined or replaced with custom.

Separate Cart and Context Separation

Technically in Bitrix there's one cart per user. If client can buy both retail and wholesale, separation is needed:

  • Option 1: Site type. Two sites in one Bitrix installation — retail and wholesale. Carts separated by FUSER_ID + LID (site identifier). Downside — settings duplication.
  • Option 2: Custom basket item property. Add custom property BASKET_TYPE = wholesale. At checkout, only wholesale items are filtered. Downside — more complex to implement, needs template refinement.

In practice, first option is more reliable: two sites with shared catalog, different templates, independent carts. Switching between "windows" — via subdomain or URL section.

Integration with 1C for Wholesale Section

Exchange with 1C for B2B section includes additional entities absent in standard CommerceML:

  • Contractors — synchronization of contractor cards from 1C with Bitrix user profiles. Mapping by INN or XML_ID.
  • Accounts receivable — current debt, payments, credit limit. Transmitted via additional XML nodes or separate REST request.
  • Individual price lists — if kept in 1C, need export mechanism in format "contractor → product → price". Standard exchange doesn't support this — separate exchange script is written.

Wholesale section always requires customization, not out-of-box configuration. Work volume depends on integration depth with accounting system and document workflow requirements.