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.







