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

Pre-order Module Development for 1C-Bitrix

A pre-order is the ability to pay for a product before it goes on sale: a new item not yet in stock, or a limited edition with a locked release date. Unlike "notify me when available", a pre-order involves a real payment or fund reservation. The standard b_sale_order does not support the state "product unavailable but pre-order accepted" — there is no deferred fulfillment status, no deadline, no mass activation.

Data Model

Module vendor.preorder:

  • b_vendor_preorder_campaign — pre-order campaigns: id, iblock_element_id (product), sku_id, name, available_from (sale start date), preorder_from, preorder_to, limit_qty (pre-order limit), reserved_qty, payment_type (full/deposit), deposit_amount, deposit_percent, status (draft/active/closed/fulfilled)
  • b_vendor_preorder_order — pre-orders: id, campaign_id, user_id, order_id (reference to b_sale_order), qty, paid_amount, status (pending/paid/confirmed/shipped/cancelled), created_at
  • b_vendor_preorder_notify_queue — notification queue on activation

Pre-order Campaign

A campaign is linked to a specific product or SKU. Parameters:

  • Payment type: full prepayment or deposit (fixed amount or percentage of price)
  • Limit: maximum number of pre-orders — limit_qty. When the limit is reached the form closes
  • Period: preorder_from / preorder_to — when pre-orders are accepted
  • Delivery date: available_from — when the product actually arrives, shown to the buyer

Creating a Pre-order

class PreorderService
{
    public function create(int $campaignId, int $userId, int $qty): PreorderResult
    {
        $campaign = CampaignTable::getById($campaignId)->fetch();

        if ($campaign['STATUS'] !== 'active') {
            return PreorderResult::error('Campaign is not active');
        }

        if ($campaign['LIMIT_QTY'] && ($campaign['RESERVED_QTY'] + $qty) > $campaign['LIMIT_QTY']) {
            return PreorderResult::error('Pre-order limit reached');
        }

        // Determine the amount to pay
        $productPrice = $this->getProductPrice($campaign['SKU_ID']);
        $payAmount    = $campaign['PAYMENT_TYPE'] === 'deposit'
            ? ($campaign['DEPOSIT_AMOUNT'] ?: $productPrice * $campaign['DEPOSIT_PERCENT'] / 100)
            : $productPrice * $qty;

        // Create an order in sale with a custom status
        $order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
        $order->setField('STATUS_ID', 'PRE'); // custom status "Pre-order"
        // ...add product to cart...
        $order->save();

        PreorderOrderTable::add([
            'CAMPAIGN_ID'  => $campaignId,
            'USER_ID'      => $userId,
            'ORDER_ID'     => $order->getId(),
            'QTY'          => $qty,
            'PAID_AMOUNT'  => $payAmount,
            'STATUS'       => 'pending',
        ]);

        // Atomically increment the reservation counter
        CampaignTable::incrementReserved($campaignId, $qty);

        return PreorderResult::success($order->getId());
    }
}

Activating Pre-orders When Stock Arrives

When the product arrives in stock, the manager sets the campaign to fulfilled. An agent processes pre-orders in bulk:

public static function activateCampaign(int $campaignId): void
{
    $preorders = PreorderOrderTable::getList([
        'filter' => ['CAMPAIGN_ID' => $campaignId, 'STATUS' => 'paid'],
        'order'  => ['CREATED_AT' => 'ASC'], // confirm earliest orders first
    ])->fetchAll();

    foreach ($preorders as $preorder) {
        // Change the main order status to standard "Processing"
        $order = \Bitrix\Sale\Order::load($preorder['ORDER_ID']);
        $order->setField('STATUS_ID', 'N');
        $order->save();

        PreorderOrderTable::update($preorder['ID'], ['STATUS' => 'confirmed']);

        // Queue a notification for the buyer
        NotifyQueueTable::add(['PREORDER_ID' => $preorder['ID']]);
    }
}

Display on the Product Card

Component vendor:preorder.button:

  • If the campaign is active: shows the pre-order form with price, delivery date, and remaining slots
  • If the limit is reached: "Pre-order closed" with a button to subscribe to a standard notification
  • Countdown to sale start date (JavaScript)
  • Limit fill progress bar: "47 of 100 reserved"

Admin Interface

  • Creating and managing campaigns
  • List of pre-orders per campaign with filter by payment status
  • "Activate campaign" button — triggers mass confirmation
  • Export pre-order list to CSV for logistics
  • Statistics: conversion to payment, average pre-order value

Development Timeline

Stage Duration
ORM tables, campaign model 1 day
Pre-order creation service, atomic reservation 2 days
Integration with b_sale_order, custom status 1 day
Campaign activation, mass confirmation agent 2 days
Product card widget 1 day
Admin interface 2 days
Testing 1 day

Total: 10 working days. For a deposit with balance payment on activation — additional payment system integration: +2 days.