# WooCommerce 1C Data Cleaner

[![PHP](https://img.shields.io/badge/PHP-8.3%2B-777BB4?logo=php&logoColor=white)](https://www.php.net/releases/8.3/en.php)
[![WordPress](https://img.shields.io/badge/WordPress-5.6%2B-21759B?logo=wordpress&logoColor=white)](https://wordpress.org/)
[![WooCommerce](https://img.shields.io/badge/WooCommerce-6%2B-96588A?logo=woocommerce&logoColor=white)](https://woocommerce.com/)
[![MySQL](https://img.shields.io/badge/MySQL%20%2F%20MariaDB-5.7%2B-4479A1?logo=mysql&logoColor=white)](https://www.mysql.com/)
[![Version](https://img.shields.io/badge/version-3.0.0-blue.svg)](1c-data-cleaner.php)
[![Single file](https://img.shields.io/badge/deploy-single%20file-informational)](1c-data-cleaner.php)

**Один PHP-файл для безопасного сброса устаревших ID обмена между 1С и WooCommerce: GUID товаров, цен и складов.** Инструмент предназначен для плагина **ItGalaxy WooCommerce Synchronization 1C — «WooCommerce — 1C:Предприятие — Обмен данными»** и нужен прежде всего после обновления конфигурации или версии 1С, когда идентификаторы в старом обмене перестают совпадать с актуальными данными.

Скрипт удаляет старые идентификаторы и связанные служебные данные, **не удаляя товары**. Во время следующей синхронизации плагин ItGalaxy находит товар **по артикулу** и записывает для него новый актуальный ID / GUID из 1С. Параллельно заново формируются привязки цен и складов.

> [!IMPORTANT]
> Записи товаров и вариаций в `{prefix}posts` **никогда не удаляются**. Скрипт работает только с метаданными, служебными опциями и кэш-таблицей WooCommerce. Перед запуском обязательно сделайте резервную копию базы данных.

---

## Содержание

- [Назначение и сценарий применения](#назначение-и-сценарий-применения)
- [Как решается проблема GUID](#как-решается-проблема-guid)
- [Возможности](#возможности)
- [Что изменяется в базе данных](#что-изменяется-в-базе-данных)
- [Требования](#требования)
- [Установка](#установка)
- [Первый запуск и пароль доступа](#первый-запуск-и-пароль-доступа)
- [Сброс забытого пароля](#сброс-забытого-пароля)
- [Режим отладки](#режим-отладки)
- [Рабочий процесс](#рабочий-процесс)
- [CLI-режим](#cli-режим)
- [Архитектура и безопасность](#архитектура-и-безопасность)
- [Бэкап и откат](#бэкап-и-откат)
- [Диагностика](#диагностика)
- [FAQ](#faq)
- [Дорожная карта](#дорожная-карта)
- [Лицензия и автор](#лицензия-и-автор)

---

## Назначение и сценарий применения

Скрипт разработан для работы с плагином ItGalaxy **WooCommerce Synchronization 1C / WooCommerce — 1C:Предприятие — Обмен данными**. Официальная документация и инструкции плагина доступны на сайте ITGALAXY: [«WooCommerce — 1C:Предприятие — Обмен данными — Инструкции»](https://itgalaxy.company/software/wordpress-woocommerce-1c-%D0%BF%D1%80%D0%B5%D0%B4%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B8%D0%B5-%D0%BE%D0%B1%D0%BC%D0%B5%D0%BD-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%BC%D0%B8/woocommerce-1c%D0%BF%D1%80%D0%B5%D0%B4%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B8%D0%B5-%D0%BE%D0%B1%D0%BC%D0%B5%D0%BD-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%BC%D0%B8-%D0%B8%D0%BD%D1%81/).

Типичный сценарий выглядит так:

1. На стороне 1С обновляют конфигурацию, переходят на новую версию 1С, восстанавливают базу из резервной копии, переносят справочники или меняют правила обмена.
2. В результате GUID / ID товаров, типов цен или складов, ранее сохранённые плагином в WooCommerce, могут стать неактуальными или исчезнуть из новой версии 1С.
3. Новый обмен не может корректно сопоставить часть объектов по старым ID: появляются дубли, «залипшие» цены, остатки или склады.
4. Администратор запускает этот скрипт и очищает только устаревшие идентификаторы и связанные данные обмена.
5. При следующем обмене плагин ItGalaxy выполняет сопоставление товаров **по артикулу** и прописывает новые ID / GUID из актуальной базы 1С.
6. Цены и остатки загружаются заново в соответствии с текущими типами цен и складами в 1С.

Именно поэтому это не «удалятор каталога», а сервисная утилита для контролируемого перепривязывания существующих товаров к обновлённой 1С.

### Какие симптомы устраняет

- После обновления 1С товары начали дублироваться при обмене.
- Цены не обновляются, остаются пустыми или относятся к старым типам цен.
- Остатки по складам не приходят, отображаются некорректно или «прилипли» от старой базы.
- В настройках плагина и списках WooCommerce остаются несуществующие цены, склады или GUID.
- Нужно подключить существующий каталог WooCommerce к новой или восстановленной базе 1С без ручного удаления товаров.

> [!WARNING]
> Перед очисткой убедитесь, что в 1С у товаров заполнены и уникальны артикулы. Именно артикул используется как точка повторного сопоставления товара при следующей синхронизации. Если у разных товаров одинаковый артикул или артикул отсутствует, автоматическое сопоставление может быть неоднозначным.

---

## Как решается проблема GUID

### До очистки

```text
WooCommerce                         Старая / обновлённая 1С
───────────                         ──────────────────────
Товар: «Аккумулятор X»              Товар: «Аккумулятор X»
Артикул: BAT-X-100                  Артикул: BAT-X-100
_id_1c: OLD-GUID-123   ────────X──> GUID: NEW-GUID-987
```

После обновления 1С старый `OLD-GUID-123`, ранее записанный в `wp_postmeta`, может отсутствовать или соответствовать уже другому объекту. Плагин не должен продолжать использовать устаревшую привязку.

### После очистки и нового обмена

```text
1. Скрипт удаляет старый _id_1c, старые данные цен / остатков и карты соответствий.

2. Плагин ItGalaxy выполняет обмен:

WooCommerce                         1С
───────────                         ──────────────────────
Товар: «Аккумулятор X»              Товар: «Аккумулятор X»
Артикул: BAT-X-100   <───────────>  Артикул: BAT-X-100

3. Сопоставление по артикулу успешно:
   WooCommerce postmeta._id_1c = NEW-GUID-987
   WooCommerce получает актуальные цены и остатки.
```

Таким образом, скрипт создаёт «чистую точку старта» для повторной синхронизации. Он не генерирует GUID сам и не меняет данные номенклатуры: актуальные идентификаторы задаёт 1С, а плагин ItGalaxy записывает их в WordPress во время обмена.

---

## Возможности

- **Целевое назначение:** работа с ItGalaxy WooCommerce Synchronization 1C / WooCommerce — 1C:Предприятие — Обмен данными.
- **Четыре операции:** очистка GUID 1С, цен, остатков или полная очистка всех трёх групп в одной транзакции.
- **Без удаления каталога:** записи `posts`, товары, вариации, названия, описания, изображения, категории и артикулы не удаляются.
- **Предпросмотр:** до запуска видны количества товаров, GUID, записей цен и остатков, типов цен и складов.
- **Автопоиск WordPress:** параметры БД читаются из `wp-config.php`; файл ищется в каталоге скрипта и до трёх уровней выше.
- **Транзакции:** `START TRANSACTION` → операции → `COMMIT`; при исключении выполняется `ROLLBACK`.
- **Prepared statements:** PDO MySQL приоритетно, mysqli как фолбэк; пользовательские значения не конкатенируются в SQL.
- **Пароль и ACL:** при первом запуске создаётся пароль, хранится только его хеш, авторизованный IP запоминается на 30 суток.
- **AJAX-интерфейс:** страница не перезагружается; кнопка на время операции меняет состояние, остальные блокируются, ошибки в консоли не стираются.
- **Режим отладки:** полный журнал серверных и клиентских событий, SQL, количество строк и тайминги — из переключателя интерфейса или через `?debug=1`.
- **CLI-восстановление доступа:** команды проверки, генерации и ручной установки пароля.
- **Один файл:** без Composer, дополнительных библиотек и загрузки WordPress.

---

## Что изменяется в базе данных

`{prefix}` — значение `$table_prefix` из `wp-config.php`; для стандартной установки WordPress это `wp_`.

| Данные обмена ItGalaxy | Где хранятся | Действие скрипта |
|---|---|---|
| GUID товара и вариации | `{prefix}postmeta.meta_key = '_id_1c'` | Удаление для `product` и `product_variation` |
| GUID категории и метки | `{prefix}termmeta.meta_key = '_id_1c'` | Удаление старых ID |
| GUID атрибута | `{prefix}woocommerce_attribute_taxonomies.id_1c` | Очистка поля в пустую строку |
| Карты GUID | `{prefix}options`: `all1cProducts`, `currentAll1cGroup`, `itglx_wc1c_nomenclature_categories` | Замена значения на пустой сериализованный массив |
| Цены по типам цен | `{prefix}postmeta.meta_key = '_all_prices'` | Удаление |
| Базовые цены WooCommerce | `{prefix}postmeta`: `_price`, `_regular_price`, `_sale_price` | Удаление — актуальные значения придут из 1С |
| Список типов цен | `{prefix}options.option_name = 'all_prices_types'` | Очистка массива |
| Настройки соответствий типов цен | `wc-itgalaxy-1c-exchange-settings`, ключи `price_type_*` | Удаление ключей `price_type_N`, `price_type_N_text`, `price_type_N_summ` |
| Остатки по складам | `{prefix}postmeta.meta_key = '_separate_warehouse_stock'` | Удаление |
| Суммарный остаток | `{prefix}postmeta.meta_key = '_stock'` | Удаление |
| Список складов | `{prefix}options.option_name = 'all_1c_stocks'` | Очистка массива |
| Статусы наличия | `{prefix}postmeta`: `_stock_status`, `_manage_stock`, `_backorders` | Нейтральные значения: `instock`, `no`, `no` |
| Кэш цен WooCommerce | `{prefix}wc_product_meta_lookup`: `min_price`, `max_price`, `onsale` | Обнуление |
| Кэш остатков WooCommerce | `{prefix}wc_product_meta_lookup`: `stock_quantity`, `stock_status` | `0` и `instock` |

Отсутствующие таблицы `woocommerce_attribute_taxonomies` и `wc_product_meta_lookup` определяются автоматически и пропускаются с записью в журнал.

### Соответствие кнопок и сценариев

| Кнопка | Что очищается | Когда использовать |
|---|---|---|
| **Очистить GUID 1С** | Старые `_id_1c`, GUID категорий/меток/атрибутов и карты GUID | После обновления/переноса 1С, при дублях и неверных привязках |
| **Очистить цены** | Старые цены, типы цен, `price_type_*`, кэш цен | Если после обновления 1С изменились типы цен или цены не синхронизируются |
| **Очистить остатки** | Старые остатки, склады, статусы наличия и кэш | Если изменились склады, остатки «зависли» или не приходят из 1С |
| **Очистить всё** | Все перечисленные группы в одной транзакции | После комплексного обновления/переноса базы 1С |

---

## Требования

| Компонент | Минимум | Примечание |
|---|---|---|
| PHP | 8.3 | Используются `enum`, `readonly`, `match`, строгая типизация |
| Расширения PHP | `pdo_mysql` **или** `mysqli` | PDO используется приоритетно; также нужны стандартные `json`, `session`, `hash` |
| MySQL / MariaDB | MySQL 5.7 / MariaDB 10.3 | Поддержка транзакций InnoDB и `DELETE … INNER JOIN` |
| WordPress | 5.6+ | Нужен читаемый `wp-config.php` |
| WooCommerce | 6+ | Таблица lookup необязательна, её отсутствие обработано |
| Плагин обмена | ItGalaxy WooCommerce Synchronization 1C | «WooCommerce — 1C:Предприятие — Обмен данными» |
| Права файловой системы | Запись в каталог скрипта | Для файла хеша пароля и ACL |
| Браузер | Современный | `fetch`, `URL`, опционально Clipboard API |

---

## Установка

### 1. Сделайте бэкап базы данных

```bash
# Полный резервный дамп перед очисткой
mysqldump --single-transaction --routines --triggers -u USER -p DBNAME \
  | gzip > backup-before-wc1c-clean-$(date +%F-%H%M).sql.gz
```

### 2. Скачайте скрипт в корень WordPress

Файл должен лежать рядом с `wp-config.php` либо не глубже трёх уровней от него.

```bash
cd /var/www/example.com/public_html
curl -fsSLO https://raw.githubusercontent.com/paulmann/woocommerce-1c-data-cleaner/main/1c-data-cleaner.php

# Пример прав — скорректируйте пользователя веб-сервера для своего окружения
chmod 640 1c-data-cleaner.php
chown www-data:www-data 1c-data-cleaner.php
```

### 3. Ограничьте доступ на уровне веб-сервера

Пароль скрипта не заменяет сетевую защиту. Рекомендуется дополнительно разрешить доступ только со своего IP.

**Nginx:**

```nginx
location = /1c-data-cleaner.php {
    allow 203.0.113.10; # Замените на свой внешний IP
    deny all;

    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

# Хранилище авторизации и файл-маркер никогда не должны отдаваться по HTTP
location ~ /\.wc1c-cleaner- {
    deny all;
    return 404;
}
```

**Apache / `.htaccess`:**

```apache
<Files "1c-data-cleaner.php">
    Require ip 203.0.113.10
</Files>

<FilesMatch "^\.wc1c-cleaner-">
    Require all denied
</FilesMatch>
```

### 4. Откройте интерфейс

Откройте в браузере:

```text
https://example.com/1c-data-cleaner.php
```

При первом запуске скрипт сгенерирует пароль, авторизует текущую сессию и покажет пароль только один раз.

---

## Первый запуск и пароль доступа

При первом обращении выполняются следующие действия:

1. Генерируется пароль из 20 символов криптографически безопасным генератором `random_int()`.
2. В `.wc1c-cleaner-auth.json` записывается **только хеш** пароля: Argon2id, если он доступен в PHP, иначе bcrypt через `PASSWORD_DEFAULT`.
3. Файл записывается атомарно (`.tmp` + `rename`) и получает права `0600`.
4. Текущий пользователь автоматически получает доступ, а его IP добавляется в ACL.
5. Пароль отображается один раз с возможностью копирования.

```text
┌────────────────────────────────────────────────────────────────────┐
│ Пароль доступа сгенерирован — сохраните его сейчас                 │
│                                                                    │
│ 7hK2mQ+xR9tLpZ4wNb3e                          [Скопировать]        │
│                                                                    │
│ Он нужен для входа с другого IP или другого компьютера.           │
│ На текущем IP пароль больше не спрашивается.                      │
└────────────────────────────────────────────────────────────────────┘
```

### Модель доступа

- Текущая PHP-сессия авторизована, поэтому пароль не спрашивается повторно до завершения сессии.
- Успешно авторизованный IP записывается в ACL на 30 суток. Сам IP в файле не хранится: используется `HMAC-SHA-256` с отдельной случайной солью.
- С другого IP или компьютера будет показана форма входа — потребуется сохранённый пароль.
- После 8 неудачных попыток в рамках одной сессии дальнейший ввод блокируется; на каждый неверный пароль добавляется задержка 400 мс.
- «Выйти и забыть IP» уничтожает сессию и удаляет текущий IP из ACL.

> [!NOTE]
> Если сайт находится за обратным прокси или Cloudflare, при необходимости включите `Config::TRUST_PROXY = true`. Делайте это только если прокси корректно перезаписывает клиентский IP: при прямом доступе заголовок `X-Forwarded-For` может быть подделан.

---

## Сброс забытого пароля

Сброс меняет соль ACL, поэтому всем клиентам — включая ранее доверенные IP — потребуется снова ввести новый пароль.

### Способ 1: SSH / CLI — рекомендуется

```bash
# Показать состояние хранилища доступа
php 1c-data-cleaner.php --status

# Сгенерировать новый пароль и вывести его в консоль
php 1c-data-cleaner.php --reset

# Задать свой пароль вручную, минимум 10 символов
php 1c-data-cleaner.php --set-password='НовыйСложныйПароль'
```

### Способ 2: FTP или файловый менеджер

Удалите файл хранилища рядом со скриптом:

```bash
rm /var/www/example.com/public_html/.wc1c-cleaner-auth.json
```

При следующем открытии страницы будет создан новый пароль и показан один раз.

### Способ 3: файл-маркер сброса

Если можно создавать файлы, но нежелательно удалять существующие:

```bash
touch /var/www/example.com/public_html/.wc1c-cleaner-reset
```

При следующем HTTP-запросе скрипт удалит старое хранилище, удалит маркер и создаст новую пару «пароль + ACL».

---

## Режим отладки

Режим отладки включается без изменения кода:

- переключателем **«Режим отладки»** в верхней части интерфейса;
- URL-параметром `?debug=1` для включения;
- URL-параметром `?debug=0` для выключения.

Состояние сохраняется в текущей PHP-сессии. При включённом debug скрипт показывает и отправляет в консоль браузера полный журнал:

```text
+    0.31 ms  INFO  Входящий запрос {"method":"POST","php":"8.3.14","debug":true}
+    1.08 ms  INFO  wp-config.php найден {"path":"/var/www/.../wp-config.php"}
+    3.42 ms  INFO  Подключение к БД установлено {"driver":"PDO","port":3306}
+   12.77 ms  INFO  Транзакция START
+   13.05 ms  INFO  Шаг 1/4: GUID товаров и вариаций
+  184.62 ms  DEBUG SQL выполнен {"rows":10432,"ms":171.2}
+  512.90 ms  INFO  Транзакция COMMIT
```

В журнал входят: события авторизации и ACL, проверка CSRF, поиск `wp-config.php`, драйвер БД, SQL-запросы и их тайминги, границы транзакций, число затронутых строк, предпросмотр до и после очистки, а также JavaScript-ошибки и сетевые ошибки клиента. Пароли, хеши, токены и соли маскируются как `***`.

---

## Рабочий процесс

1. Проверьте, что в актуальной 1С у товаров заполнены корректные и уникальные артикулы.
2. Создайте бэкап БД.
3. Остановите автоматический обмен с 1С на время работ, если он выполняется по расписанию.
4. Загрузите `1c-data-cleaner.php` в каталог WordPress и откройте его в браузере.
5. На первом запуске сохраните сгенерированный пароль.
6. Включите режим отладки, если хотите зафиксировать подробный ход операции.
7. Изучите таблицу **«Текущее состояние»** и выберите нужную область очистки.
8. Отметьте галочку «Я понимаю, что данные будут удалены, бэкап сделан».
9. Нажмите кнопку очистки и подтвердите действие в диалоговом окне.
10. Убедитесь, что в блоке «Результат» нет ошибок, а в debug-журнале есть `Транзакция COMMIT`.
11. Запустите синхронизацию в плагине ItGalaxy. Товары будут сопоставлены по артикулу, после чего плагин создаст актуальные GUID / ID, цены и остатки.
12. Проверьте несколько товаров, цены и остатки в витрине и панели WordPress.
13. Удалите `1c-data-cleaner.php`, `.wc1c-cleaner-auth.json` и возможный `.wc1c-cleaner-reset` с сервера.

> [!TIP]
> При комплексном обновлении 1С обычно достаточно один раз использовать «Очистить всё», затем запустить полный обмен. При локальной проблеме безопаснее выбирать целевую кнопку: например, только «Очистить цены», если изменились исключительно типы цен.

---

## CLI-режим

CLI-режим предназначен только для управления доступом, не для удаления данных БД.

```bash
$ php 1c-data-cleaner.php --status
WooCommerce 1C Data Cleaner 3.0.0
Хранилище: /var/www/example.com/public_html/.wc1c-cleaner-auth.json
Создано: 2026-08-27 15:42:10
Тип пароля: сгенерирован автоматически
Доверенных IP: 2
```

| Команда | Результат |
|---|---|
| `php 1c-data-cleaner.php --status` | Статус файла доступа, дата создания, число доверенных IP |
| `php 1c-data-cleaner.php --reset` | Создаёт и печатает новый случайный пароль, сбрасывает ACL |
| `php 1c-data-cleaner.php --set-password='…'` | Устанавливает пароль администратора длиной от 10 символов и сбрасывает ACL |
| `php 1c-data-cleaner.php --help` | Печатает краткую памятку |

---

## Архитектура и безопасность

Скрипт состоит из одного PHP-файла с `declare(strict_types=1)` и разделён на небольшие компоненты.

| Компонент | Ответственность |
|---|---|
| `Config` | Версия, метаданные, наборы ключей обмена, TTL ACL, путь к служебным файлам |
| `EventLog` | Журнал событий с таймингами и маскированием чувствительных значений |
| `Credentials` | Генерация и хеширование пароля, ACL по IP, атомарная запись хранилища, reset-marker |
| `Session` / `Debug` | Безопасные cookie, CSRF, хранение и переключение режима отладки |
| `Auth` | Авторизация по сессии, IP и паролю, лимит попыток, выход |
| `WpConfig` | Поиск и безопасный разбор параметров `wp-config.php` без его подключения |
| `Db` | PDO/mysqli, prepared statements, транзакции, валидация идентификаторов таблиц |
| `Action` | Строгое перечисление доступных операций |
| `Cleaner` | Очистка GUID, цен, складов и служебных карт |
| `Preview` | Подсчёт данных до и после действий |
| JSON API | `login`, `logout`, `ack_password`, `set_debug`, `preview`, `guid`, `prices`, `stocks`, `all` |

### Меры защиты

- нет SQL-конкатенации пользовательских значений: используются подготовленные выражения;
- имена таблиц валидируются регулярным выражением `[A-Za-z0-9_]+`;
- `unserialize()` вызывается с `allowed_classes => false`;
- CSRF-токен создаётся через `random_bytes(32)`, проверяется через `hash_equals`;
- после успешной авторизации обновляется ID сессии;
- cookie сессии имеют `HttpOnly`, `SameSite=Strict`, а при HTTPS — `Secure`;
- ответы имеют `Cache-Control: no-store`, `X-Robots-Tag: noindex, nofollow` и meta robots;
- `display_errors=0`: PHP notices и warnings попадают в EventLog, не ломая JSON-ответ;
- `.gitignore` исключает хранилище пароля, ACL и reset-marker.

> [!WARNING]
> Скрипт предоставляет прямой доступ к БД и выполняет массовые `DELETE` / `UPDATE`. Не оставляйте его в публичном web-root после завершения работ. Пароль не является заменой ограничения IP через Nginx, Apache, VPN или HTTP Basic Auth.

---

## Бэкап и откат

### Быстрый бэкап затрагиваемых таблиц

Замените `wp_` на фактический префикс таблиц, если он отличается.

```bash
mysqldump --single-transaction -u USER -p DBNAME \
  wp_postmeta \
  wp_termmeta \
  wp_options \
  wp_woocommerce_attribute_taxonomies \
  wp_wc_product_meta_lookup \
  | gzip > wc1c-before-clean-$(date +%F-%H%M).sql.gz
```

### Восстановление из дампа

```bash
gunzip < wc1c-before-clean-2026-08-27-1542.sql.gz | mysql -u USER -p DBNAME
```

После восстановления или нового полного обмена можно очистить кэш:

```bash
wp cache flush
wp wc update_db
```

Транзакция защищает от частичного применения действий только **внутри одного запуска**: если произошла ошибка до `COMMIT`, выполняется `ROLLBACK`. После успешного `COMMIT` откат возможен только восстановлением из резервной копии.

---

## Диагностика

| Симптом | Возможная причина | Решение |
|---|---|---|
| «Файл wp-config.php не найден» | Скрипт расположен дальше чем в трёх уровнях от WordPress | Переместите его в корень WordPress рядом с `wp-config.php` |
| «Не удалось распознать DB_NAME / DB_USER / $table_prefix» | В `wp-config.php` параметры заданы нестандартно, например через `getenv()` | Используйте строковые `define()` либо адаптируйте `WpConfig::parse()` под своё окружение |
| «На сервере не включено ни PDO MySQL, ни mysqli» | Отсутствуют PHP-расширения работы с MySQL | Включите `pdo_mysql` или `mysqli` в панели хостинга / `php.ini` |
| «Недействительный токен сессии» | Сессия истекла, cookie заблокированы, балансировщик не держит session affinity | Обновите страницу, разрешите cookie, проверьте конфигурацию PHP sessions |
| Пароль не сохраняется | У каталога скрипта нет прав записи, используется временная папка | Дайте веб-пользователю право записи в каталог скрипта |
| Кнопка долго показывает «Выполняется…» | Большая таблица `postmeta`, блокировка MySQL или таймаут PHP-FPM | Включите debug, найдите долгий SQL, проверьте индексы и `request_terminate_timeout` |
| После очистки цены или остатки пустые | Это ожидаемое состояние до следующего обмена | Запустите обмен в ItGalaxy и дождитесь завершения |
| После обмена появились дубли | Не совпадают или не уникальны артикулы | Исправьте артикулы в 1С и WooCommerce, удалите некорректные дубли, повторите контролируемый обмен |
| Остатки старые после обмена | Кэш объектов, страницы или CDN | `wp cache flush`, сбросьте кэш WooCommerce и CDN |

---

## FAQ

### Товары, изображения и описания точно останутся?

Да. Скрипт не содержит `DELETE` из `{prefix}posts`. Он не удаляет товары, вариации, артикулы, названия, описания, медиафайлы, категории или теги. Очищаются только служебные привязки обмена, цены, остатки и связанные кэш-значения.

### Когда использовать скрипт?

Основной случай — обновление версии или конфигурации 1С, перенос на другую базу, восстановление базы из бэкапа и другие ситуации, в которых старые GUID товаров, цен или складов перестали соответствовать данным новой 1С. Утилита удаляет старые ID; следующий обмен создаёт новые.

### Почему после очистки товары снова «находятся» в 1С?

Потому что плагин ItGalaxy при синхронизации сопоставляет товары по артикулу. Если артикулы идентичны и уникальны, плагин находит существующую карточку WooCommerce и записывает в неё актуальный ID / GUID из 1С вместо создания нового товара.

### Нужно ли очищать всё?

Нет. Выбирайте минимально необходимую операцию. Если изменились только типы цен — используйте «Очистить цены». Если проблема только со складами — «Очистить остатки». «Очистить всё» подходит после полного обновления/переноса базы 1С, когда нет доверия всем старым идентификаторам.

### Можно ли отменить очистку?

До конца HTTP-запроса ошибки откатываются транзакцией. После успешного `COMMIT` вернуть старые данные можно только из резервного дампа БД.

### Поддерживаются ли другие плагины обмена с 1С?

Нет, готовая схема ключей ориентирована на ItGalaxy WooCommerce Synchronization 1C. Для других решений требуется отдельно проверить, где они хранят GUID, цены и остатки, и адаптировать константы в `Config`.

### Поддерживается ли WordPress Multisite?

Скрипт использует один `$table_prefix` из `wp-config.php` и по умолчанию работает с основным сайтом. Для подсайтов необходимо адаптировать префикс (`wp_2_`, `wp_3_` и т. п.) после предварительного бэкапа и теста на копии.

### Почему пароль привязан к IP?

Это дополнительный уровень удобства и защиты: после единственного успешного входа с доверенного рабочего адреса пароль не приходится вводить снова. При смене IP, при работе с другого компьютера или после сброса ACL потребуется сохранённый пароль.

---

## Дорожная карта

- [ ] `dry-run`: подробный подсчёт будущих изменений без модификации БД
- [ ] Экспорт debug-журнала в JSON и текстовый файл
- [ ] Выборочная очистка по категориям, товарам и диапазонам ID
- [ ] Поддержка WordPress Multisite с выбором подсайта
- [ ] Поддержка конфигураций ключей других плагинов 1С-обмена
- [ ] Двухфакторное подтверждение деструктивных операций (TOTP)
- [ ] Автоматическая проверка уникальности артикулов перед удалением GUID

---

## Лицензия и автор

Проект распространяется по лицензии [PolyForm Noncommercial 1.0.0](LICENSE) © 2026 Mikhail Deynekin. Программа поставляется «как есть», без гарантий; пользователь самостоятельно отвечает за бэкапы и последствия операций с данными.

**Mikhail Deynekin**

- Сайт: [Deynekin.com](https://deynekin.com)
- GitHub: [paulmann (Mikhail Deynekin)](https://github.com/paulmann/)
- Репозиторий: [woocommerce-1c-data-cleaner](https://github.com/paulmann/woocommerce-1c-data-cleaner)
- Другой инструмент: [Clean_BOM_Senior](https://github.com/paulmann/Clean_BOM_Senior) — очистка невидимых UTF-8 BOM и CRLF в исходных файлах

Если инструмент помог корректно перепривязать каталог после обновления 1С — поставьте звезду репозиторию.
