Синхронизация остатков в WooCommerce через CSV-отчёт без ручного импорта

Если остатки в магазине ведутся не только в WooCommerce, а приходят из 1С, складской системы или просто из выгрузки поставщика, ручное обновление быстро становится источником ошибок. Типичный сценарий: файл с остатками уже есть, но после импорта часть товаров не обновилась, у вариаций остались старые значения, а у некоторых позиций остаток вообще стал отрицательным после продажи.

Ниже разберём рабочую схему: как принимать CSV-отчёт, сопоставлять товары по артикулу, обновлять остатки без лишних действий и проверять, что синхронизация действительно сработала.

Когда проблема проявляется на практике

Чаще всего сбой выглядит не как одна явная ошибка, а как набор мелких несоответствий:

  • товар в админке показывает старый остаток, хотя в CSV уже другое значение;
  • вариации обновляются не все, потому что импорт сопоставляет только родительский товар;
  • после импорта у части товаров включается статус outofstock, хотя остаток больше нуля;
  • артикулы в файле и в магазине записаны по-разному: пробелы, регистр, лишние символы;
  • импорт проходит, но в логах нет понятного сообщения, какие строки были пропущены.

Что проверить до настройки импорта

Сначала стоит убедиться, что у вас есть стабильный ключ сопоставления. Для WooCommerce это обычно SKU. Если артикул не заполнен или не уникален, автоматическая синхронизация будет давать случайные пропуски. Также проверьте, что в CSV остаток хранится в одном поле и без форматирования вроде 1 200 или 1,200, если импортёр ожидает целое число.

Какой способ выбрать: плагин, код или гибрид

Для большинства магазинов есть три реалистичных варианта. Если нужен быстрый запуск без разработки, подойдёт импорт через штатный CSV-импорт WooCommerce или через специализированный импортёр. Если файл приходит регулярно и структура стабильна, надёжнее сделать небольшой обработчик на PHP и запускать его по расписанию. Гибридный вариант удобен, когда импортёр уже есть, но нужно доработать сопоставление или очистку данных перед обновлением.

ПодходКогда уместенПлюсыМинусы
Штатный CSV-импорт WooCommerceРазовые или редкие обновленияНе требует кода, понятен администраторуСлабее контроль над логикой сопоставления
Импортёр с настройкой полейРегулярные выгрузки от поставщикаМожно настроить карту полей и расписаниеЗависит от конкретного плагина и его формата
Собственный PHP-скриптНужна точная логика обновленияПолный контроль над SKU, статусами и логамиТребует поддержки и аккуратной проверки

Пошаговое решение: обновляем остатки по CSV через код

Если файл приходит в предсказуемом формате, проще всего написать обработчик, который читает CSV, ищет товар по артикулу и обновляет остаток через стандартные функции WooCommerce. Такой подход полезен, когда нужно исключить ручной импорт и не зависеть от интерфейса плагина.

Пример структуры CSV

Пусть файл выглядит так:

sku,stock,status
TSHIRT-001,12,instock
TSHIRT-002,0,outofstock
TSHIRT-003,5,instock

Здесь sku — артикул, stock — количество, status — статус наличия. Если статус не нужен, его можно вычислять по остатку.

Пример импорта в WooCommerce

<?php
add_action( 'admin_post_wpdo_import_stock_csv', 'wpdo_import_stock_csv' );

function wpdo_import_stock_csv() {
    if ( ! current_user_can( 'manage_woocommerce' ) ) {
        wp_die( 'Недостаточно прав.' );
    }

    check_admin_referer( 'wpdo_import_stock_csv' );

    if ( empty( $_FILES['stock_csv']['tmp_name'] ) ) {
        wp_die( 'CSV-файл не загружен.' );
    }

    $file = fopen( $_FILES['stock_csv']['tmp_name'], 'r' );
    if ( ! $file ) {
        wp_die( 'Не удалось открыть CSV.' );
    }

    $header = fgetcsv( $file, 0, ',' );
    if ( ! $header ) {
        wp_die( 'Пустой CSV.' );
    }

    $map = array_flip( $header );
    $updated = 0;
    $skipped = 0;

    while ( $row = fgetcsv( $file, 0, ',' ) ) {
        $sku   = isset( $row[ $map['sku'] ] ) ? trim( $row[ $map['sku'] ] ) : '';
        $stock = isset( $row[ $map['stock'] ] ) ? (int) $row[ $map['stock'] ] : null;
        $status = isset( $row[ $map['status'] ] ) ? trim( $row[ $map['status'] ] ) : '';

        if ( '' === $sku || null === $stock ) {
            $skipped++;
            continue;
        }

        $product_id = wc_get_product_id_by_sku( $sku );
        if ( ! $product_id ) {
            $skipped++;
            continue;
        }

        $product = wc_get_product( $product_id );
        if ( ! $product ) {
            $skipped++;
            continue;
        }

        $product->set_manage_stock( true );
        $product->set_stock_quantity( $stock );

        if ( in_array( $status, array( 'instock', 'outofstock', 'onbackorder' ), true ) ) {
            $product->set_stock_status( $status );
        } else {
            $product->set_stock_status( $stock > 0 ? 'instock' : 'outofstock' );
        }

        $product->save();
        $updated++;
    }

    fclose( $file );

    wp_safe_redirect( add_query_arg( array(
        'updated' => $updated,
        'skipped' => $skipped,
    ), admin_url( 'tools.php' ) ) );
    exit;
}

Этот код лучше использовать как основу для внутреннего инструмента, а не как публичную форму. Он намеренно завязан на права manage_woocommerce и nonce-проверку, чтобы случайно не открыть импорт всем подряд.

Как не сломать вариации

У вариативных товаров артикул часто хранится именно у вариации, а не у родителя. Это нормально: wc_get_product_id_by_sku() вернёт ID той записи, где SKU действительно задан. Проблема возникает, если в CSV указан артикул родительского товара, а остаток нужно менять у конкретной вариации. В таком случае сначала нужно договориться о формате файла: либо SKU на уровне вариации, либо отдельная колонка с ID вариации.

Если нужен импорт без кода

Когда разработка не входит в план, используйте импортёр, который умеет сопоставлять поля CSV с полями товара WooCommerce. Важно не просто загрузить файл, а проверить три вещи:

  • какое поле используется для поиска товара — SKU, ID или название;
  • обновляется ли остаток у вариаций отдельно;
  • есть ли режим пропуска строк с пустым значением, а не перезапись нулями.

Если импортёр поддерживает предварительный просмотр, не пропускайте этот шаг. Именно там обычно видно, что колонка с остатком распознана как текст, а не как число.

Проверка результата после внедрения

После первого запуска не ограничивайтесь визуальной проверкой в админке. Лучше проверить синхронизацию по нескольким уровням:

  1. Откройте товар в админке и убедитесь, что остаток изменился.
  2. Проверьте карточку товара на витрине: статус наличия должен совпадать с остатком.
  3. Если включён кэш, очистите его и проверьте страницу в режиме инкогнито.
  4. Для вариативного товара откройте конкретную вариацию и сравните её SKU и остаток с CSV.
  5. Посмотрите, не появились ли в логах пропущенные строки или ошибки прав доступа.

Если у вас есть доступ к базе данных, можно дополнительно проверить метаполя товара, но для обычной диагностики достаточно админки и фронтенда. Главное — убедиться, что обновление не только прошло, но и отразилось в каталоге и в корзине.

Частые ошибки и как их исправить

SKU не найден

Обычно это означает, что в CSV есть лишние пробелы, другой регистр или артикул записан не в том поле. Сначала нормализуйте данные: trim(), удаление неразрывных пробелов, единый формат SKU. Если артикулы в магазине уже дублируются, сначала исправьте их, иначе импорт будет обновлять не тот товар.

Остаток обновился, но статус не сменился

Так бывает, если импорт меняет только число, но не вызывает set_stock_status(). В WooCommerce лучше обновлять и количество, и статус одновременно. Если остаток стал нулевым, товар должен перейти в outofstock, иначе покупатель увидит его как доступный.

Импорт ломает вариации

Причина обычно в том, что файл составлен под простые товары, а в магазине есть вариативные. Для вариаций нужен отдельный контроль: либо импорт по SKU вариации, либо отдельная логика поиска дочерних товаров. Не пытайтесь одним и тем же правилом обновлять все типы товаров без проверки.

После импорта цифры в админке верные, а на сайте старые

Это уже похоже на кэширование. Очистите кэш страницы товара, объектный кэш, если он используется, и проверьте, не отдаёт ли CDN старую версию. Для магазинов с частыми обновлениями остатков это критично: витрина должна показывать актуальное наличие, а не вчерашний снимок.

Практические советы по безопасности и производительности

Импорт остатков — операция, которая может затронуть сотни или тысячи товаров. Поэтому не стоит запускать её через открытый URL без ограничений. Лучше использовать административный экран, проверку прав и nonce. Если файл большой, разбивайте импорт на порции, иначе можно упереться в лимиты времени выполнения PHP.

Для регулярной синхронизации полезно вести лог: дата запуска, количество обновлённых строк, количество пропусков, список SKU, которые не нашли товар. Это экономит время при разборе инцидентов и помогает быстро понять, где сломалась цепочка: в выгрузке, в сопоставлении или в самом WooCommerce.

Если нужен более удобный редактор правил импорта или дополнительные блоки для описания товара, можно посмотреть в сторону решений WPShop, например Clearfy Pro для чистки и оптимизации сайта: https://wpshop.ru/plugins/clearfy. Но для самой синхронизации остатков всё равно важнее корректная структура данных и проверяемая логика обновления.

Что считать успешным результатом

Решение можно считать рабочим, если после импорта выполняются все три условия: SKU находятся без ручной правки, остатки совпадают с CSV, а статус наличия на витрине меняется без задержки после очистки кэша. Если хотя бы один из этих пунктов не выполняется, проблему нужно искать в сопоставлении полей, типе товара или кэшировании, а не в самом CSV.

Как сделать кэширование в WordPress с помощью плагинов
05.11.2025
Как создать и использовать блок Gutenberg в WordPress с примером кода
18.03.2026
Как использовать WPRemark для массового управления комментариями в WordPress
13.12.2025
Как создать свой плагин WordPress с названием wpdo
01.11.2025
Как удалить пагинацию в WordPress без плагинов
21.03.2026