Development of a 1C-Bitrix wholesale order 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
    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

Development of Bulk Order Module for 1C-Bitrix

A wholesale buyer doesn't work through the shopping cart. He uploads a list of SKUs from his ERP, specifies quantities, and waits for confirmation. If the portal asks him to search for each item manually via filter and add one by one — he'll go to a competitor with Excel import.

Quick Order Entry: Three Scenarios

The standard component bitrix:sale.basket is not suitable for wholesale orders. A separate module is developed with three modes for entering items:

Table entry with autocomplete. User enters SKU in a row, the field autocompletes via AJAX request to bitrix:catalog.search or a custom handler that searches in b_iblock_element with index by CODE and XML_ID. After selecting an item — price, stock, and unit fields are filled automatically. Table rows — as many as needed, added dynamically via JavaScript.

Import from Excel/CSV. File is uploaded via standard file field component, parsed on server via PhpSpreadsheet (or fgetcsv for CSV). Expected format: SKU, quantity, optionally — comment. Parse result: list of found/not found items. Not found — shown separately with manual mapping option. Found — added to order draft.

Order repeat. "Repeat order" button in history — takes CSaleOrder::GetByID(), iterates through CSaleBasket::GetList() with ORDER_ID, checks current stock of each item. Items with zero stock are flagged as warning but don't block draft creation.

Module Architecture

Module is located in local/modules/project.wholesale/. Structure:

install/
  index.php       — installer, event handler registration
lib/
  OrderDraft.php  — order draft entity (D7 DataManager)
  Importer.php    — Excel/CSV parsing
  PriceProvider.php — priority pricing
  StockChecker.php  — stock checking
options.php       — module settings in control panel

Order draft — intermediate storage before finalization. Table b_project_order_draft: ID, USER_ID, COMPANY_ID, ITEMS (JSON), STATUS (editing, pending_approval, confirmed), CREATED_AT, UPDATED_AT. JSON field ITEMS contains items with SKU, quantity, price at draft creation, and confirmed price. This is important: price is fixed in draft to prevent discrepancies during approval.

Draft to order conversion. When confirming draft, standard order is created via Bitrix\Sale\Order::create(). All items are added via Bitrix\Sale\Basket::create() with explicit prices from draft. Standard price recalculation is disabled for these items — otherwise personal prices may be overwritten.

Stock Control and Reservation

Wholesale order without stock check — source of conflicts with supply department. We implement two levels:

Soft check when adding item to draft: request to CCatalogStoreProduct::GetList() with filter by PRODUCT_ID and STORE_ID. If requested quantity > available — show warning but don't block. Customer sees: "Available: 45, you requested: 60. Partial shipment possible".

Reservation when moving to pending_approval status: create entry in b_catalog_store_product with negative stock change (or in separate reserves table). On order cancellation — reserve returns. This prevents double-booking of one item by two customers.

Synchronization with 1C automatically removes reserves — when stock updates via CommerceML, quantities are recalculated with reserves accounted for in b_catalog_store_product.QUANTITY_RESERVED.

Pricing in Bulk Module

Wholesale prices depend on volume: 1-10 units — price A, 11-50 — price B, 51+ — price C. In standard catalog module this is done via quantized prices (CCatalogProductPrice), but interface is inconvenient for large assortments.

In module we implement custom interface for volume discounts: Highload block wholesale_price_rules (UF_IBLOCK_SECTION_ID or UF_PRODUCT_ID, UF_QTY_FROM, UF_QTY_TO, UF_PRICE_TYPE — fixed or percent of base). When adding item to draft we determine quantity and search for matching rule.

Working with Units of Measure

Bulk catalog often has multiple units: piece, package (12 pcs.), pallet (240 pcs.). Module catalog supports measures via CCatalogMeasure and CCatalogMeasureRatio. In bulk interface customer selects unit — quantity is recalculated automatically, as is price.

Execution Timeline

Component Timeline
Table entry with AJAX search 2-3 weeks
Excel/CSV import 1-2 weeks
Order draft (D7 entity + API) 2-3 weeks
Stock control and reservation 1-2 weeks
Volume pricing 1-2 weeks
Integration with approval module 1-2 weeks

Total: 8-14 weeks including testing and acceptance.