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

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, 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

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

Управление складскими остатками — это не просто счётчик рядом с товаром. Это система, которая связывает витрину магазина с реальными физическими запасами, предотвращает продажи несуществующего товара и даёт данные для закупочных решений. Без грамотно реализованного инвентаря магазин работает вслепую.

Что входит в задачу

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

  • хранение количества единиц на складе с разбивкой по вариантам (размер, цвет, 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, никаких application-level проверок, которые врут при конкурентном доступе.

Для 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 через очередь. При создании резерва диспатчится джоб с delay:

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

Если заказ оплачен до истечения — джоб проверяет статус и выходит без действия.

2. Scheduled cleanup. Cron каждые 5 минут ищет просроченные резервации:

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

Первый подход точнее, второй проще в инфраструктуре. Для большинства магазинов с нагрузкой до 1000 заказов/день — достаточно scheduled 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 недели в зависимости от наличия внешней учётной системы.