Development of a 1C-Bitrix warehouse management 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
    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

Developing a Warehouse Management Module for 1C-Bitrix

Bitrix has built-in stock tracking through catalog module: b_catalog_store (warehouses), b_catalog_store_product (stock by warehouse/product). For simple sites this suffices. When multiple real warehouses appear with different reservation logic, shipment priorities, inter-warehouse transfers, and 1C integration — standard functionality needs significant extension.

Standard Bitrix Limitations

  • No concept of "reserved" per order — stock either exists or doesn't
  • No warehouse selection logic for shipment during checkout
  • No tracking of inter-warehouse movements with history
  • No turnover and shortage analytics

Data Schema Extension

Module adds several tables:

Table b_catalog_store_reservation — order reserves:

Field Type Purpose
ID int auto_increment
STORE_ID int FK to b_catalog_store
PRODUCT_ID int Trading offer ID
ORDER_ID int FK to b_sale_order
QUANTITY decimal(18,4) Reserved quantity
RESERVED_AT datetime When reserved
RELEASED_AT datetime When released (NULL if active)

Table b_catalog_store_movement — inter-warehouse transfers:

Field Type Purpose
ID int auto_increment
FROM_STORE_ID int Source warehouse
TO_STORE_ID int Destination warehouse
PRODUCT_ID int Product
QUANTITY decimal(18,4) Quantity
STATUS enum DRAFT, IN_TRANSIT, COMPLETED, CANCELLED
CREATED_BY int User ID
COMPLETED_AT datetime When completed

Reservation on Checkout

On order creation, goods must be reserved. Logic connects via OnSaleOrderSaved event handler:

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'sale',
    'OnSaleOrderSaved',
    [ReservationService::class, 'onOrderSaved']
);

onOrderSaved checks order status implies reserve (e.g., N — new, P — paid). For each order item determines source warehouse and creates b_catalog_store_reservation record.

Warehouse Selection Strategy

Warehouse selection strategy configured in module settings:

  • FIFO by receipt date — older goods shipped first
  • Nearest warehouse — by customer geolocation (requires warehouse coordinates)
  • Warehouse prioritySORT field in b_catalog_store, lower = higher priority
  • Minimum stock — empty warehouses with least inventory first

If one warehouse lacks required quantity, system auto-splits shipment across warehouses.

Available Stock Calculation

Available stock = physical stock minus active reserves:

public static function getAvailable(int $storeId, int $productId): float
{
    $physical = \Bitrix\Catalog\StoreProductTable::getList([
        'filter' => ['=STORE_ID' => $storeId, '=PRODUCT_ID' => $productId],
        'select' => ['AMOUNT'],
    ])->fetch()['AMOUNT'] ?? 0;

    $reserved = \Bitrix\Main\Application::getConnection()->query(
        "SELECT COALESCE(SUM(QUANTITY), 0) as RES
         FROM b_catalog_store_reservation
         WHERE STORE_ID = {$storeId}
           AND PRODUCT_ID = {$productId}
           AND RELEASED_AT IS NULL"
    )->fetch()['RES'] ?? 0;

    return max(0, (float)$physical - (float)$reserved);
}

Product page and cart show available stock, not physical.

Administrative Interface

Admin section (/bitrix/admin/) adds:

  • Stock by warehouse — product × warehouse matrix with physical and available stock, filtering by category/warehouse
  • Movements — create transfer document, confirm receipt at destination warehouse
  • Reserves — active reserves list with manual release option

1C Integration

If project has 1C integration via standard mechanism (/bitrix/admin/1c_exchange.php), stock syncs via OnSuccessCatalogImport1C event handler. Reserves aren't overwritten — 1C stock interpreted as physical, available stock recalculated.

Development Timeline

Scope Components Duration
Basic Reservation on order, available stock, reserve release 6–8 days
Standard + Inter-warehouse transfers, shipment strategies, Admin-UI 12–16 days
Extended + Turnover analytics, 1C integration, warehouse mobile interface 20–28 days