Розробка управління складськими залишками для інтернет-магазину

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка управління складськими залишками для інтернет-магазину
Середня
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка управління складськими остатками для E-Commerce

Управління складськими остатками — це не просто лічильник поряд з товаром. Це система, яка пов'язує вітрину магазину з реальними фізичними запасами, запобігає продажам неіснуючого товару та дає дані для закупочних рішень. Без грамотно реалізованого інвентарю магазин працює всліпу.

Що входить у завдання

Мінімально жизненна система обліку остатків включає:

  • зберігання кількості одиниць на складі з розбивкою по варіантам (розмір, колір, SKU)
  • резервування остатка в момент оформлення замовлення до факту оплати
  • звільнення резерву при скасуванні або вичерпанні таймаута
  • списання при переході замовлення у статус "відправлено"
  • пороги сповіщення: "закінчується" та "немає в наявності"
  • відображення статусу на вітрині без повного перезапроса сторінки

Без резервування два покупці одночасно можуть купити останній екземпляр — класична race condition. Без звільнення резерву покинуті кошики назавжди блокують остатки.

Схема даних

Базова структура для PostgreSQL:

CREATE TABLE product_variants (
    id          BIGSERIAL PRIMARY KEY,
    product_id  BIGINT NOT NULL REFERENCES products(id),
    sku         VARCHAR(100) NOT NULL UNIQUE,
    attributes  JSONB NOT NULL DEFAULT '{}',
    stock_qty   INTEGER NOT NULL DEFAULT 0,
    reserved_qty INTEGER NOT NULL DEFAULT 0,
    low_stock_threshold INTEGER NOT NULL DEFAULT 5,
    CHECK (stock_qty >= 0),
    CHECK (reserved_qty >= 0),
    CHECK (stock_qty >= reserved_qty)
);

CREATE TABLE stock_movements (
    id          BIGSERIAL PRIMARY KEY,
    variant_id  BIGINT NOT NULL REFERENCES product_variants(id),
    delta       INTEGER NOT NULL,
    type        VARCHAR(50) NOT NULL, -- 'reserve', 'release', 'deduct', 'restock'
    reference   VARCHAR(255),         -- order_id, shipment_id и т.д.
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

Поле available_qty = stock_qty - reserved_qty вичисляється на лету або через generated column. Таблиця stock_movements — незмінний журнал усіх операцій, необхідний для аудиту та відновлення при збоях.

Резервування без перегонів

Атомарність забезпечується через SELECT ... FOR UPDATE або UPDATE ... RETURNING:

-- Атомарне резервування
UPDATE product_variants
SET reserved_qty = reserved_qty + :qty
WHERE id = :variant_id
  AND (stock_qty - reserved_qty) >= :qty
RETURNING id, stock_qty, reserved_qty;

Якщо рядок не повернувся — товара недостатньо. Ніяких SELECT перед UPDATE, ніяких перевірок на рівні програми, які брешуть при конкурентному доступі.

Для Laravel це завертається в транзакцію з песимістичною блокуванням:

DB::transaction(function () use ($variantId, $qty, $orderId) {
    $variant = ProductVariant::lockForUpdate()->findOrFail($variantId);

    if ($variant->available_qty < $qty) {
        throw new InsufficientStockException($variantId, $qty);
    }

    $variant->increment('reserved_qty', $qty);

    StockMovement::create([
        'variant_id' => $variantId,
        'delta'      => -$qty,
        'type'       => 'reserve',
        'reference'  => "order:{$orderId}",
    ]);
});

Звільнення зависших резервів

Покупатель кинув кошик — резерв повинен повернутися. Два підходи:

1. TTL через чергу. При створенні резерву диспатчується джоб з затримкою:

ReleaseStockReservation::dispatch($reservationId)
    ->delay(now()->addMinutes(30));

Якщо замовлення оплачено до вичерпання — джоб перевіряє статус та виходить без дії.

2. Запланований cleanup. Cron кожні 5 хвилин шукає давно минулі резервації:

// app/Console/Commands/ReleaseExpiredReservations.php
StockReservation::where('expires_at', '<', now())
    ->where('status', 'pending')
    ->each(fn($r) => $r->release());

Перший підхід точніший, другий простіший в інфраструктурі. Для більшості магазинів з навантаженням до 1000 замовлень/день — достатньо запланованого cleanup.

Імпорт та синхронізація з 1С / складською системою

Якщо у клієнта є облікова система (1С:Торговля, МойСклад, Мегаплан), остатки приходять звідси:

  • Webhook-режим: склад викликає endpoint /api/stock/update при зміні
  • Pull-режим: магазин раз на N хвилин запитує дифф остатків

Для pull-режиму зберігається last_sync_at та запитується тільки змінене:

GET /api/moysklad/stock?changedSince=2025-03-01T00:00:00Z

При імпорті важливо не перезаписувати reserved_qty — тільки stock_qty. Інакше при синхронізації слетять активні резерви.

Відображення статусу на вітрині

Три варіанти подачі:

Статус Умова Відображення
В наявності available_qty > threshold "В наявності", додати в кошик активно
Закінчується 0 < available_qty ≤ threshold "Залишилось 3 шт."
Немає в наявності available_qty = 0 "Немає в наявності", кнопка недоступна
Під замовлення out_of_stock_allowed = true "Під замовлення, 5–7 днів"

Для високонатовованих сторінок статус кешується в Redis з TTL 60 секунд. Точність ±1 хвилина — прийнятний компроміс для більшості магазинів.

Сповіщення про низький остаток

Менеджеру закупок потрібно знати заздалегідь. Сповіщення триггерується в observer:

class ProductVariantObserver
{
    public function updated(ProductVariant $variant): void
    {
        if ($variant->wasChanged('stock_qty') && $variant->isLowStock()) {
            LowStockAlert::dispatch($variant);
        }
    }
}

Алерт йде на email, Telegram або Slack — залежить від інфраструктури клієнта.

Сроки реалізації

  • Базове резервування + списання + журналювання: 3–5 днів
  • Інтеграція з 1С або МойСклад через API: +3–5 днів
  • Кеширование статусів через Redis + відображення на вітрині: +2 дня
  • Панель остатків в админці з фільтрами та експортом: +2–3 дня

Типовий проект: 1–2 тижня залежно від наявності зовнішної облікової системи.