Синхронізація залишків та цін між сайтом та маркетплейсами

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, 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

Синхронізація залишків і цін між сайтом та маркетплейсами

Коли магазин продає одночасно через сайт і кілька маркетплейсів, управління залишками стає критичним завданням: продали на сайті — потрібно знизити залишок на Ozon та Wildberries; прийшов новий товар — піднести скрізь. Цінова синхронізація забезпечує цінову паритет або надбавку на маркетплейсі.

Архітектура системи

Джерело істини (Основний склад / Сайт)
    ↓
Stock Manager Service
    ├── Резервування при замовленні на сайті
    ├── Звільнення при скасуванні
    └── Поповнення при надходженні товару
         ↓
    Sync Queue (Redis)
         ↓
    Marketplace Workers
    ├── Ozon Worker    → Ozon API
    ├── WB Worker      → WB API
    └── YM Worker      → Яндекс.Маркет API

Розрахунок доступного залишку

class StockCalculator
{
    public function getAvailableForMarketplace(int $productId, string $marketplace): int
    {
        $product = Product::with(['reservations', 'warehouseItems'])->findOrFail($productId);

        $totalStock    = $product->warehouseItems->sum('quantity');
        $reservedSite  = $product->reservations()->where('source', 'site')->sum('quantity');
        $reservedOther = $product->reservations()->where('source', '!=', $marketplace)->sum('quantity');

        // Для маркетплейсу доступно не більше 80% вільного залишку
        $available = $totalStock - $reservedSite - $reservedOther;
        return max(0, (int)($available * 0.8));
    }
}

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

Черга синхронізації

class StockSyncQueue
{
    public function enqueue(int $productId): void
    {
        // Дедублікація: якщо вже в черзі — оновлюємо таймер
        Redis::setex("sync:pending:{$productId}", 30, 1);
    }

    public function processQueue(): void
    {
        // Batching: збираємо всі зміни за 30 секунд і відправляємо батчем
        $keys      = Redis::keys('sync:pending:*');
        $productIds = array_map(fn($k) => (int)explode(':', $k)[2], $keys);

        if (empty($productIds)) return;

        Redis::del($keys);
        $this->syncToMarketplaces($productIds);
    }
}

Цінова синхронізація

class PriceSyncService
{
    private array $marketplacePriceRules = [
        'ozon'    => ['type' => 'markup', 'value' => 5.0],   // +5%
        'wb'      => ['type' => 'markup', 'value' => 7.0],   // +7%
        'ym'      => ['type' => 'fixed',  'value' => 0],     // без надбавки
    ];

    public function calculateMarketplacePrice(float $basePrice, string $marketplace): float
    {
        $rule = $this->marketplacePriceRules[$marketplace];

        return match($rule['type']) {
            'markup' => round($basePrice * (1 + $rule['value'] / 100), 0),
            'fixed'  => $basePrice + $rule['value'],
            default  => $basePrice,
        };
    }
}

Обробка перехресних замовлень

class OrderProcessor
{
    public function process(Order $order): void
    {
        DB::transaction(function () use ($order) {
            foreach ($order->items as $item) {
                $reserved = ProductReservation::create([
                    'product_id' => $item->product_id,
                    'quantity'   => $item->quantity,
                    'source'     => $order->source,  // 'site', 'ozon', 'wb'
                    'order_id'   => $order->id,
                ]);

                // Перевіряємо не перевищено фізичний залишок
                $totalReserved = ProductReservation::where('product_id', $item->product_id)->sum('quantity');
                $actualStock   = WarehouseItem::where('product_id', $item->product_id)->sum('quantity');

                if ($totalReserved > $actualStock) {
                    throw new InsufficientStockException($item->product_id);
                }
            }

            // Ставимо завдання на зниження залишків на всіх платформах
            StockSyncJob::dispatch($order->items->pluck('product_id')->unique()->all());
        });
    }
}

Моніторинг розбіжностей

Періодично звіряємо фактичні залишки на маркетплейсах з нашими даними:

class StockDiscrepancyChecker
{
    public function check(): array
    {
        $discrepancies = [];
        $ozonStocks = $this->ozon->getAllStocks();

        foreach ($ozonStocks as $ozonItem) {
            $ourStock = $this->calculator->getAvailableForMarketplace($ozonItem['offer_id'], 'ozon');

            if (abs($ourStock - $ozonItem['stock']) > 1) {
                $discrepancies[] = [
                    'sku'      => $ozonItem['offer_id'],
                    'our'      => $ourStock,
                    'ozon'     => $ozonItem['stock'],
                    'delta'    => $ourStock - $ozonItem['stock'],
                ];
            }
        }

        return $discrepancies;
    }
}

Строки

Система синхронізації залишків і цін для 2–3 маркетплейсів: 14–20 робочих днів.