Розроблення особистого кабінету постачальника для дропшипінгу в 1С-Бітрікс
У моделі дропшипінгу постачальники повинні бачити замовлення, що містять їхні товари, оновлювати рівні запасів і ціни, підтверджувати відправлення. Стандартний особистий кабінет Бітрікс (my) розроблений для покупців. Кабінет постачальника — це окремий розділ із доступом за ролями, власними компонентами та обмеженою видимістю даних.
Модель доступу та ролі
У Бітріксі контроль доступу реалізується через групи користувачів. Створіть групу "Постачальники" через API або адміністративний інтерфейс:
$groupId = CGroup::Add([
'ACTIVE' => 'Y',
'NAME' => 'Постачальники',
'STRING_ID' => 'SUPPLIERS',
]);
Зв'язування конкретного постачальника з його товарами здійснюється через HL-блок SupplierProduct або властивість інформаційного блоку. Найпростіший підхід — використання властивості SUPPLIER_ID типу "Посилання на користувача" (E) на кожному товарі. Таким чином, постачальник бачить лише товари, де SUPPLIER_ID = поточний користувач.
Розділ кабінету постачальника захищений перевіркою групи:
if (!$USER->IsAuthorized() || !$USER->IsInGroup($supplierGroupId)) {
LocalRedirect('/auth/?backurl=' . urlencode($_SERVER['REQUEST_URI']));
}
Розділ "Мої товари"
Відображає список товарів постачальника з поточними рівнями запасів і цінами. Запит з використанням CIBlockElement::GetList з фільтром SUPPLIER_ID:
$userId = $USER->GetID();
$res = CIBlockElement::GetList(
['NAME' => 'ASC'],
[
'IBLOCK_ID' => CATALOG_IBLOCK_ID,
'ACTIVE' => 'Y',
'PROPERTY_SUPPLIER_ID' => $userId,
],
false,
false,
['ID', 'NAME', 'DETAIL_PAGE_URL', 'PREVIEW_PICTURE', 'PROPERTY_SUPPLIER_ID']
);
Для кожного товару відобразіть поточні запаси з b_catalog_store_product та ціну з b_catalog_price:
$quantityRes = CCatalogStoreProduct::GetList(
[],
['PRODUCT_ID' => $productId]
);
$totalQuantity = 0;
while ($qRow = $quantityRes->GetNext()) {
$totalQuantity += (int)$qRow['AMOUNT'];
}
Форма оновлення цін та запасів
Постачальники повинні мати можливість оновлювати ціни та запаси без адміністративного доступу. Форма відправляє AJAX-запит:
// /local/ajax/supplier-update.php
$productId = (int)$_POST['product_id'];
$newPrice = (float)$_POST['price'];
$newQty = (int)$_POST['quantity'];
// Перевірте, що товар належить поточному постачальнику
$ownerCheck = CIBlockElement::GetProperty(
CATALOG_IBLOCK_ID,
$productId,
'sort',
'asc',
['CODE' => 'SUPPLIER_ID', 'VALUE' => $USER->GetID()]
);
if (!$ownerCheck->Fetch()) {
echo json_encode(['error' => 'Доступ заборонений']);
die();
}
// Оновіть ціну
CCatalogProduct::Update($productId, []); // оновлення без зміни полів товару
$priceRow = CCatalogPrice::GetList(
[], ['PRODUCT_ID' => $productId, 'CATALOG_GROUP_ID' => BASE_PRICE_GROUP_ID]
)->Fetch();
if ($priceRow) {
CCatalogPrice::Update($priceRow['ID'], ['PRICE' => $newPrice, 'CURRENCY' => 'RUB']);
} else {
CCatalogPrice::Add([
'PRODUCT_ID' => $productId,
'CATALOG_GROUP_ID' => BASE_PRICE_GROUP_ID,
'PRICE' => $newPrice,
'CURRENCY' => 'RUB',
]);
}
// Оновіть запаси на складі постачальника
$storeProductRow = CCatalogStoreProduct::GetList(
[], ['PRODUCT_ID' => $productId, 'STORE_ID' => getSupplierStoreId($userId)]
)->Fetch();
if ($storeProductRow) {
CCatalogStoreProduct::Update($storeProductRow['ID'], ['AMOUNT' => $newQty]);
} else {
CCatalogStoreProduct::Add([
'PRODUCT_ID' => $productId,
'STORE_ID' => getSupplierStoreId($userId),
'AMOUNT' => $newQty,
]);
}
echo json_encode(['success' => true]);
Кожен постачальник має свій склад (b_catalog_store) — це дозволяє відстежувати запаси кожного постачальника незалежно.
Розділ "Мої замовлення"
Постачальник бачить лише замовлення, що містять його товари. Прямий запит до b_sale_order неприйнятний — необхідний зв'язок через кошик:
$supplierId = $USER->GetID();
$connection = \Bitrix\Main\Application::getConnection();
$orders = $connection->query("
SELECT DISTINCT
o.ID,
o.DATE_INSERT,
o.PRICE,
o.STATUS_ID,
o.USER_ID,
u.NAME,
u.LAST_NAME,
u.EMAIL
FROM b_sale_order o
JOIN b_sale_basket b ON b.ORDER_ID = o.ID
JOIN b_iblock_element_property ep
ON ep.IBLOCK_ELEMENT_ID = b.PRODUCT_ID
AND ep.IBLOCK_PROPERTY_ID = " . SUPPLIER_PROP_ID . "
AND ep.VALUE_NUM = {$supplierId}
LEFT JOIN b_user u ON u.ID = o.USER_ID
WHERE o.DATE_INSERT >= DATE_SUB(NOW(), INTERVAL 90 DAY)
ORDER BY o.DATE_INSERT DESC
LIMIT 100
");
На сторінці деталей замовлення постачальник бачить лише позиції кошика, пов'язані з його товарами — не все замовлення:
$basketItems = $connection->query("
SELECT b.ID, b.NAME, b.QUANTITY, b.PRICE, b.PRODUCT_ID
FROM b_sale_basket b
JOIN b_iblock_element_property ep
ON ep.IBLOCK_ELEMENT_ID = b.PRODUCT_ID
AND ep.IBLOCK_PROPERTY_ID = " . SUPPLIER_PROP_ID . "
AND ep.VALUE_NUM = {$supplierId}
WHERE b.ORDER_ID = {$orderId}
");
Підтвердження відправлення
Постачальник підтверджує відправлення своєї частини замовлення. Статус записується в HL-блок SupplierShipment:
-
UF_ORDER_ID— ID замовлення -
UF_SUPPLIER_ID— ID постачальника -
UF_STATUS—pending/confirmed/shipped/delivered -
UF_TRACKING— номер відстеження -
UF_DATE_SHIPPED— дата відправлення
Коли всі постачальники замовлення установлять статус shipped, агент автоматично змінює статус замовлення Бітрікс на "Відправлено".
Масове завантаження цін та запасів через CSV
Для постачальників із великим асортиментом — надайте форму завантаження CSV-файлу:
Артикул;Ціна;Запас
SKU-001;1500;25
SKU-002;2800;10
Обробник PHP читає CSV за допомогою SplFileObject, знаходить товари за CML2_ARTICLE (властивість артикулу), перевіряє належність постачальнику та оновлює дані. Обмеження розміру файлу та кількості рядків (до 5000) запобігають таймауту PHP.
Тривалість реалізації
| Обсяг | Склад | Тривалість |
|---|---|---|
| Базовий кабінет (список замовлень + оновлення запасів) | Компоненти + AJAX-форма + роль | 1–1.5 тижня |
| Повнофункціональний кабінет (склад, ціни, відправлення, CSV) | + завантаження файлів + агент статусів | 2–3 тижня |
| Багатомовний кабінет з аналітикою продажів | + звіти продажів + i18n | 3–4 тижня |







