# Проверка на портале

> Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, левое
меню, раскладка компонентов, агенты, таблица импорта, поведение при
обновлении. Проверять это приходится руками — и лучше по списку, потому что
забытый шаг находит не разработчик, а клиент.

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

Процедура рассчитана на **отдельный стенд**, а не на боевой портал. Шаги
«удалить модуль» и «поставить на CP1251» на рабочем портале делать нельзя:
удаление стирает таблицу импорта.

Раскладка репозитория — в [module-structure.md](/modules/insync/module-structure), сборка —
в [build-and-install.md](/modules/insync/build-and-install).

## Что понадобится

| | |
|---|---|
| портал | «коробка» Битрикс24 (для левого меню нужен `intranet`) или БУС, главный модуль **22.600.300** и выше |
| PHP | **8.2** и выше, расширения `mbstring` и `xmlreader` |
| кодировка | **только UTF-8** |
| `shef.options` | **3.0.0** и выше, установлен |
| `shef.problems` | **2.0.0** и выше, установлен |
| доступ | администратор портала, второй пользователь **без** прав администратора, доступ к файлам по ssh |
| архив | со страницы релиза либо собранный `./build.sh` |
| модуль-импорт | любой модуль с наследником `Sync\FromFile\AFileProcess` и страницей `shef.insync:import.from.file` в левом меню — например, shef.demosync |

Для сценария «обновление» нужен стенд, где уже стоит **1.2.x** — на нём
проверяется то, ради чего 2.0.0 сделана мажорной.

## Перед началом

Снимите копию каталога модуля, настроек, таблицы импорта и компонентов 1.x в
`/local` — шаги с удалением необратимы, а установщик удаляет
`/local/components/shef.insync` без проверки содержимого (в 1.x там мог
править проект):

```bash
cp -a /var/www/portal/bitrix/modules/shef.insync /tmp/shef.insync.before 2>/dev/null
cp -a /var/www/portal/local/components/shef.insync /tmp/local-components.before 2>/dev/null
mysqldump -u… portal b_option --where="MODULE_ID='shef.insync'" > /tmp/opt.before.sql
mysqldump -u… portal shef_insync_model > /tmp/model.before.sql
ls /var/www/portal/local/components/ /var/www/portal/bitrix/components/ /var/www/portal/bitrix/js/ | sort > /tmp/public.before
```

## 0. Архив — тот самый

Архив собирается **побайтово одинаково** у всех, кто взял тот же коммит:

```bash
git clone https://github.com/bx-shef/insync.git
cd insync && git checkout <тег проверяемой версии>
./build.sh                       # последняя строка напечатает sha256
sha256sum /путь/к/скачанному/shef.insync.zip
```

Хеши обязаны совпасть. Первым уровнем внутри архива — ровно `shef.insync/`:

```bash
unzip -Z1 shef.insync.zip | cut -d/ -f1 | sort -u
```

## A. Чистая установка

1. Убедиться, что `shef.options` 3.0.0+ и `shef.problems` 2.0.0+ стоят.
2. Распаковать в `bitrix/modules/`, чтобы получилось `bitrix/modules/shef.insync/`.
3. **Marketplace → Установленные решения** → «[SH] InSync» → установить.

**Ожидается:** «Модуль успешно установлен»; появились
`/bitrix/components/shef.insync/` (два компонента) и
`/bitrix/js/shef-insync/ui-anchors/`; в `/local/components/` каталога
`shef.insync` **нет**; в БД появилась таблица `shef_insync_model`; в левом меню
появился раздел «[SH] Импорт» со страницами «Статистика» и «Записи импорта».

**Отдельно:** на стенде без `shef.options` 3.x или без `shef.problems` 2.x
установка обязана отказать с текстом про модуль и версию — а не поставиться и
упасть на первой странице.

## B. Обновление с 1.2.x — главный сценарий 2.0.0

Делается на стенде, где стоит 1.2.x, есть строки в таблице импорта и хотя бы
один агент импорта.

1. Запомнить, что было:

```bash
ls /var/www/portal/local/components/shef.insync/      # в 1.x есть
mysql -e "SELECT COUNT(*) FROM shef_insync_model" portal
```

2. **Выключить агенты импорта** (`/bitrix/admin/agent_list.php`, модули
   импорта) и дождаться, пока текущий запуск закончится. Модели 2.0.0 знают
   колонку `ID`, которой в таблице 1.x нет: между заменой файлов и
   `SyncTable::init()` каждый запрос агента к таблице упадёт.
3. Обновить `shef.options` до 3.x и `shef.problems` до 2.x.
4. Заменить файлы модуля содержимым архива 2.0.0. **Замена файлов не запускает
   установщик**: компоненты 1.x в `/local/components/shef.insync` останутся и
   перекроют новые. Разложите файлы установщиком, не удаляя модуль (удаление
   стёрло бы таблицу импорта) — в «Командной PHP-строке» администратора:

```php
require $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/shef.insync/install/index.php';
(new shef_insync())->InstallFiles();
\Bitrix\Main\Loader::includeModule('shef.insync');
\Shef\InSync\Sync\Model\SyncTable::init();   // ключ таблицы 1.x -> 2.x
```

До перевода проверьте, что внешние коды не совпадают в первых 191 символе —
иначе уникальный индекс не встанет и `init()` откажет (строки не тронет):

```sql
SELECT ORIGINATOR_ID, LEFT(ORIGIN_ID, 191) AS K, COUNT(*) FROM shef_insync_model
GROUP BY ORIGINATOR_ID, K HAVING COUNT(*) > 1;
```

Пусто — переводите. Нет — лишние строки (обычно давно упавшие) удалить или
разобрать до перевода.

`init()` переводит таблицу импорта 1.x на ключ 2.x: первичный ключ — новая
колонка `ID`, внешний код уникален в пределах кода импорта, индексы 1.x
(`_origs`, `_orig_id`, `_originator_id`) снимаются. Строки остаются;
повторный вызов ничего не делает. Перевод — один `ALTER TABLE`, MySQL
перестраивает таблицу и на это время не пускает запись: большую таблицу
(`SELECT COUNT(*)` из п. 1) переводите в окно без импорта. Ключ таблицы не
1.x и не 2.x — `init()` отказывает и ничего не меняет.

5. Включить агенты импорта обратно.

**Ожидается:**

* `/local/components/shef.insync/` удалён, `/bitrix/components/shef.insync/` на месте;
* число строк в `shef_insync_model` то же;
* `SHOW KEYS FROM shef_insync_model` — ровно два ключа: `PRIMARY` на `ID` и
  уникальный `shef_insync_model_origin` на `ORIGINATOR_ID, ORIGIN_ID`;
  индексы, которые проект завёл на таблице сам, — на месте;
* страница «Статистика» открывается администратору, грид и список агентов на
  месте, кнопки агентов работают (см. D);
* страница настроек модуля открывается (см. C) — в 1.x она падала бы на
  shef.options 3.x с «Unknown named parameter»;
* в журнале PHP нет `Undefined array key`, `Class "Shef\UiClear\..." not found`,
  `Call to undefined function _showError()`.

6. Перенести каталог импорта за корень сайта и перенастроить обмены — по
   [security.md](/modules/insync/security), «После обновления с 1.x».

**Ожидается:** файлы из `/upload/import/` лежат в `<каталог импорта>/`,
`/upload/import` удалён, обмен кладёт новый файл в `<каталог импорта>/<код>/`,
агент его забирает.

## C. Страница настроек

**Настройки → Настройки модулей → [SH] InSync.**

**Ожидается:** вкладка «Общие», в ней «Сколько дней хранится файл в результате
импорта» со значениями 1, 2, 3, 5, 15, 30; по умолчанию — 3. Сохранить 15 —
значение сохранилось.

Если модуль-импорт выводит на своей странице настроек опции
`Options\Agent\Option` или `Options\Import\FromFile\Option`: у администратора
кнопки запуска/остановки агента и импорта работают; у пользователя с правом
«Чтение» на тот модуль кнопки агента не видны.

## D. Права на страницах и в ajax

Под **пользователем без прав** (не администратор, прав на модули нет):

1. Раздел «[SH] Импорт» в левом меню — страниц модуля не видно; прямой адрес
   `/page/shinsync/statimportlocal/` — «Недостаточно прав».
2. Прямой вызов действия из консоли браузера на любой странице портала:

```js
BX.ajax.runComponentAction('shef.insync:import.stat.local', 'stopAgent', {mode: 'class', data: {id: 1}})
```

**Ожидается:** ошибка, агент с ID 1 (агент ядра) **не** выключен —
проверить на `/bitrix/admin/agent_list.php`.

3. То же для контроллера страницы настроек: скопировать под администратором
   адрес кнопки агента (`/bitrix/services/main/ajax.php?action=…stopAgent&…`),
   открыть его под пользователем без прав, подставив `agentId=1&moduleId=main`
   и его `sessid` (`BX.bitrix_sessid()`) — ошибка, агент не тронут.
4. Пользователю дать «Запись» на `sale` (или другой модуль ядра со своими
   агентами), повторить п. 3 с ID агента `sale` и `moduleId=sale` — ошибка
   «Agent not found», агент не тронут: модуль трогает только агенты импорта.

Под **администратором:** страница статистики открывается, агент модуля-импорта
выключается и включается кнопкой, «Очистить» у строки грида удаляет только
строки этой загрузки.

## E. Импорт файла

На странице импорта из файла модуля-импорта (левое меню «[SH] Импорт»):

1. «Скачать пример» — скачивается файл-пример.
2. Загрузить этот пример — «Загрузка произведена», статистика по строкам.
3. Загрузить файл `test.php` (переименуйте любой текстовый) — отказ «Wrong
   file type», в `<каталог импорта>/<код>/` файла нет.
4. Файл, в строках которого есть `<b>жирный</b>` и ошибка разбора, — в блоке
   ошибок текст виден **как текст**, а не жирным.

**Ожидается:** каталог импорта — вне корня сайта (`/home/bitrix/sh_import` на
BitrixVM, если не задан свой); в `<каталог импорта>/copy/<код>/` архив с именем
`done_<код>_<дата>_<16 символов>.<расш>` — хвост случайный.

## F. Откуда взяты библиотеки XML

```php
\Bitrix\Main\Loader::includeModule('shef.insync');
echo (new ReflectionClass(\SbWereWolf\XmlNavigator\Extraction\HierarchyComposer::class))->getFileName();
```

**Ожидается:** проект без Composer — путь в `…/shef.insync/vendor/sbwerewolf/…`;
проект с Composer, где стоит `sbwerewolf/xml-navigator`, — путь в vendor
проекта. Во втором случае версия там обязана быть `7.2.x`: ветки 8+ требуют
PHP 8.4 и разбирают XML в тот же формат, но модуль на них не проверялся.

## G. Агент импорта

Агент модуля-импорта (наследник `Sync\FromFile\AAgent`) после загрузки файла:

**Ожидается:** строки из `shef_insync_model` уходят пачками, успешные
удаляются, ошибочные остаются со статусом `F` и сообщением; в журнале событий
проблемы с типом `SH_PROBLEMS_SYNC`; страница статистики обновляется сама
(pull), без ошибок в консоли браузера.

## G2. Драйверы каталога

Из PHP-консоли на товаре торгового каталога (ID и тип цены — свои):

```php
\Bitrix\Main\Loader::includeModule('shef.insync');
$price = new \Shef\InSync\Sync\Model\Catalog\Driver\Price();
var_dump($price->save(['PRODUCT_ID' => 1, 'CATALOG_GROUP_ID' => 1], ['PRICE' => 10, 'CURRENCY' => 'BYN'])->isSuccess());
var_dump($price->save(['PRODUCT_ID' => 1, 'CATALOG_GROUP_ID' => 1], ['PRICE' => 12.5, 'CURRENCY' => 'BYN'])->isSuccess());
var_dump((new \Shef\InSync\Sync\Model\Catalog\Driver\Product())->save(['ID' => 1], ['WEIGHT' => 250])->isSuccess());
var_dump((new \Shef\InSync\Sync\Model\Catalog\Driver\Amount())->save(['PRODUCT_ID' => 1], ['STORE_ID' => 1, 'AMOUNT' => 7])->isSuccess());
```

**Ожидается:** четыре `true`, ни одного `Warning`; у товара **одна** цена
этого типа — 12.50, `PRICE_SCALE` заполнен; вес 250; остаток на складе 1 — 7
(при включённом складском учёте ядро остаток так не примет — это верно).

## H. Удаление

1. **Marketplace → Установленные решения** → «[SH] InSync» → удалить.

**Ожидается:**

* `/bitrix/components/shef.insync/`, `/bitrix/js/shef-insync/` и
  `/local/components/shef.insync/` (если был) удалены, чужие компоненты на месте;
* таблицы `shef_insync_model` нет, настроек модуля в `b_option` нет,
  настройки `shef.options` — на месте;
* раздел «[SH] Импорт» из левого меню ушёл;
* файлы в каталоге импорта **остались**: это данные проекта.

Формы «сохранить данные?» у модуля нет: удаление из админки стирает таблицу
импорта всегда. Оставить данные — только из PHP-консоли:
`(new shef_insync())->UnInstallDB(['savedata' => 'Y'])` после подключения
`/bitrix/modules/shef.insync/install/index.php`.

Если модуль зависит от других (`shef.*` с `shef.insync` в `requireModules`),
удаление обязано отказать и назвать их.

## I. Портал в CP1251

Установка обязана отказать с текстом про UTF-8. На современных ядрах ветка
недостижима — `Application::isUtfMode()` возвращает `true` без условий, — тогда
в бланке отмечается «пропущено», и это верный ответ.

## J. Примеры на живом ядре

```bash
for e in agent xml; do DOCUMENT_ROOT=/var/www/portal php examples/$e.php || echo "FAIL $e"; done
```

**Ожидается:** два раза `ГОТОВО: …`, ни одного `FAIL`, `Warning`,
`Deprecated`.

## Бланк результата

```
Версия: ____  Коммит: ____  sha256 архива сошёлся: да / нет
Ядро main: ____  PHP: ____  shef.options: ____  shef.problems: ____
Composer в проекте: да / нет   intranet: да / нет

0. Архив .................................. ок / не ок
A. Чистая установка ....................... ок / не ок
   без shef.options 3 / shef.problems 2 — отказ .. ок / не ок
B. Обновление с 1.2.x ..................... ок / не ок / нет стенда
C. Страница настроек ...................... ок / не ок
D. Права: без прав — отказ ................ ок / не ок
   агент ядра не тронут ................... ок / не ок
   «W» на sale — агенты sale не тронуты ... ок / не ок
E. Импорт файла ........................... ок / не ок
   .php отклонён .......................... ок / не ок
F. Библиотеки XML взяты из ................ модуль / Composer
G. Агент импорта .......................... ок / не ок
G2. Драйверы каталога ..................... ок / не ок
H. Удаление ............................... ок / не ок
I. CP1251 ................................. ок / пропущено
J. Примеры на живом ядре .................. ок / не ок

Замечания:
```

::note
Источник: [insync/docs/portal-check.md](https://github.com/bx-shef/insync/blob/main/docs/portal-check.md) — правки туда, сайт пересобирается сам.
::
