From: Aleksey Filippov Date: Wed, 29 Apr 2026 20:29:06 +0000 (+0300) Subject: fix(task_22): расширить окно выборки админов с 1 до 4 месяцев X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=0663af50a2e4d4b0ebda1a3c17b769c117aca503;p=erp24_rep%2Fyii-erp24%2F.git fix(task_22): расширить окно выборки админов с 1 до 4 месяцев Сотрудники с пустым guid выпадали из синхронизации с 1С после месяца — расширяем окно date_add до 4 месяцев. Добавлена документация по каналу ERP→1С create_employee (pull через API2 DataController::actionRequest). --- diff --git a/erp24/docs/integrations/1c/employee-sync.md b/erp24/docs/integrations/1c/employee-sync.md new file mode 100644 index 00000000..559dff0c --- /dev/null +++ b/erp24/docs/integrations/1c/employee-sync.md @@ -0,0 +1,200 @@ +# Интеграция ERP → 1С: создание сотрудников + +**Назначение.** Канал передачи новых сотрудников (кассиров) из ERP в 1С. Работает по **pull-модели**: ERP накапливает кандидатов в БД через шедулер, 1С опрашивает API2 и забирает их. + +--- + +## 1. Цепочка вызовов + +``` +[cron] + └─ erp24/scripts/scheduler.php (раз в минуту) + └─ glob(scripts/tasks/*.php) → exec(php scheduler.php $time {file}) + └─ task_22_create_employee_for_1c_with_admins_with_empty_guid.php + ├─ выбор Admin без guid + ├─ создание EmployeeOnShift (status_source = NOT_CREATED_IN_1C) + ├─ generation guid через DataHelper::createGuidMy("06") + ├─ Admin.guid := новый guid + └─ запись в ExportImportTable (entity='admin', export_id=1) + +[1С опрашивает] + POST /api2/data/request + └─ DataController::actionRequest + └─ getCreateEmployee() + └─ EmployeeOnShift WHERE status_source=NOT_CREATED_IN_1C, status=ACCEPT, active=ON + → $mess['create_employee'] = [...] +``` + +--- + +## 2. Условия выборки кандидатов + +[task_22_create_employee_for_1c_with_admins_with_empty_guid.php:72-82](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L72) + +```php +$compareDate = date('Y-m-d H:i:s', strtotime("-1 month", time())); + +$entityCityStore = ExportImportService::getEntityByType(); +$exportCityStore = ArrayHelper::map($entityCityStore, 'entity_id', 'export_val'); + +$admins = Admin::find() + ->select(['id', 'guid', 'name', 'store_id', 'mobile as phone']) + ->where(['and', ['guid' => ''], ['!=', 'mobile', '']]) + ->andWhere(['in', 'group_id', AdminGroup::getGroupsForEmployeeOnCashbox()]) + ->andWhere(['>=', 'date_add', $compareDate]) + ->all(); +``` + +### Что фильтруется + +| Условие | Смысл | +|---|---| +| `guid = ''` | Сотрудник ещё не выгружался в 1С (пустой GUID — маркер «новый») | +| `mobile != ''` | Без телефона в 1С создавать нельзя (это бизнес-ключ) | +| `group_id IN getGroupsForEmployeeOnCashbox()` | Только кассиры — другие группы (админы, бухгалтеры, ИТ) не отправляются | +| `date_add >= now() - 4 month` | Скользящее окно: учитываются только сотрудники, добавленные за последние 4 месяца | + +### Маппинг магазинов + +`ExportImportService::getEntityByType()` (без аргумента — дефолт = `city_store`) даёт массив соответствий «store_id ERP → guid магазина в 1С». Через `ArrayHelper::map()` строится lookup `entity_id → export_val`, который позже подставляется в `EmployeeOnShift.store_id` ([task_22:96-98](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L96)). Если магазин не найден в маппинге — пишется заглушка `'--'`. + +--- + +## 3. Выводы по логике выборки + +### 3.1. Окно «–4 месяца» — ловушка остаётся, но шире + +Сотрудник с пустым `guid`, добавленный **более 4 месяцев назад**, в 1С **никогда не попадёт** — он навсегда выпадает из выборки. Окно расширено с 1 до 4 месяцев, чтобы покрыть типовые провалы шедулера и поздно валидируемые телефоны, но проблема «потерянных» в принципе сохраняется. + +Возможные причины «зависания» сотрудника: + +- Сотрудника зарегистрировали, но `mobile` стал валидным позже (например, исправили опечатку — но `date_add` старая и за пределы окна уехала). +- Шедулер был выключен / упал на несколько месяцев. +- В прошлом запуске возникла ошибка валидации `EmployeeOnShift` — запись в `Admin.guid` не сохранилась. + +**Рекомендация.** Помимо расширенного окна, завести разовую console-команду для добивки «потерянных»: тот же запрос, но без `date_add >= compareDate`, с явным флагом `--dry-run`. + +### 3.2. `guid = ''` vs `guid IS NULL` + +Используется строгое сравнение со строкой. По схеме [Admin](../../../records/Admin.php) поле `guid` — `string`, валидация `max:36`, default — пустая строка ([Admin.php:291](../../../records/Admin.php#L291): `$admin->guid = ''` в фабричном методе). Если в БД появятся `NULL`-значения (через прямой SQL/миграцию) — они **не попадут в выборку**. На текущей схеме безопасно, но фрагилно. + +### 3.3. `mobile != ''` не отсекает `NULL` + +В Yii2 Query Builder `['!=', 'mobile', '']` транслируется в SQL `mobile != ''`, и `NULL != ''` даёт `UNKNOWN` (т.е. строка отбрасывается). На практике поле `mobile` валидируется как `required` ([Admin.php:114](../../../records/Admin.php#L114)) и проходит `PhoneValidator`, поэтому `NULL` не должен встречаться. Но если запись попадёт через миграцию или `save(false)` — она будет молча пропущена. + +### 3.4. Группы кассиров — единственный источник + +Метод `AdminGroup::getGroupsForEmployeeOnCashbox()` определяет, кого вообще считать «сотрудником, которого нужно завести в 1С». Любая новая группа кассиров требует **обязательного** добавления в этот метод — иначе её сотрудники в 1С не появятся. + +### 3.5. Производительность + +- Запрос один, без N+1. +- `select` ограничен 5 полями. +- Скользящее окно в месяц + фильтр по `guid=''` отсекают подавляющее большинство записей. +- На таблице `admin` за месяц новых сотрудников — десятки/сотни. Безопасно. +- **Индексы**: для эффективности нужен составной индекс `(guid, date_add)` или `(group_id, date_add) WHERE guid = ''`. Проверить в [database/SCHEMA.md](../../database/SCHEMA.md). + +### 3.6. Идемпотентность + +После успешной обработки `Admin.guid` заполняется новым GUID ([task_22:121-122](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L121)). Повторный запуск той же задачи — no-op для уже обработанных Admin (не пройдут условие `guid = ''`). **Безопасно запускать руками много раз.** + +### 3.7. Конфликт с `EmployeeOnShift` + +[task_22:85](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L85) дополнительно отсекает админов, чей `phone` уже присутствует в активных `EmployeeOnShift`. Логика: если сотрудник уже в смене — не создавать ему дубль. Однако фильтрация **в PHP**, а не в SQL — вся выборка `Admin` грузится в память. На текущих объёмах не страшно, но на росте таблицы стоит переписать через `NOT EXISTS`. + +--- + +## 4. Структура payload в 1С + +[DataController::getCreateEmployee():775-816](../../../api2/controllers/DataController.php#L775) + +```json +{ + "create_employee": [ + { + "id": "06...guid...", + "admin_id": 12345, + "store_id": "", + "first_name": "Имя", + "last_name": "", + "phone": "+79991234567", + "comment": "Добавлен через чат-бот сотрудником <создатель> <дата>", + "sex": "f", + "bday": "16.08.2000" + } + ] +} +``` + +### Известные технические долги + +| Проблема | Где | Что сделать | +|---|---|---| +| `sex` захардкожено `'f'` | [DataController.php:811](../../../api2/controllers/DataController.php#L811) | Брать из `Admin.pol` | +| `bday` захардкожено `'16.08.2000'` | [DataController.php:812](../../../api2/controllers/DataController.php#L812) | Брать из `Admin.birthdate` | +| `last_name` всегда пустое | [task_22:102](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L102) | Парсить из `Admin.name` или из отдельного поля | +| `$force = true` в задаче | [task_22:38](../../../scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php#L38) | Убрать после стабилизации, оставить только модуль 5 минут | + +--- + +## 5. Запуск задачи вручную + +### CLI на dev (Docker) +```bash +docker exec yii_erp24-php-yii_erp24-1 bash -c \ + "cd /www && php erp24/scripts/scheduler.php $(date +%s) \ + /www/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php" +``` + +### CLI на prod +```bash +cd /var/www/erp24_deploy && php erp24/scripts/scheduler.php $(date +%s) \ + /var/www/erp24_deploy/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php +``` + +### Через веб-UI +`/scheduler_task/test` — поле `task_num` = `task_22_create_employee_for_1c_with_admins_with_empty_guid` (полное имя файла без `.php`). Реализация: [SchedulerTaskController::actionRunTask:132-159](../../../controllers/SchedulerTaskController.php#L132). + +--- + +## 6. Проверка результата + +```sql +-- Лог шедулера +SELECT task_num, date_start, date_stop, error, log +FROM erp24.scheduler_task_log +WHERE task_num = 22 +ORDER BY id DESC LIMIT 3; + +-- Новые сотрудники, ждущие выгрузки в 1С +SELECT id, guid, phone, store_id, status_source, created_at +FROM erp24.employee_on_shift +WHERE status_source = 0 -- STATUS_SOURCE_NOT_CREATED_IN_1C +ORDER BY created_at DESC LIMIT 20; + +-- Проверка outbox +SELECT * FROM erp24.export_import_table +WHERE entity = 'admin' AND export_id = 1 +ORDER BY id DESC LIMIT 20; + +-- Сотрудники, которым GUID был проставлен +SELECT id, name, mobile, guid, date_add +FROM erp24.admin +WHERE guid != '' AND date_add >= NOW() - INTERVAL '1 day' +ORDER BY date_add DESC; +``` + +--- + +## 7. Связанная документация + +- [database/SCHEMA.md](../../database/SCHEMA.md) — схема таблиц `admin`, `employee_on_shift`, `export_import_table` +- [optimization/1c-sync-optimization.md](../../optimization/1c-sync-optimization.md) — оптимизации входящих потоков из 1С +- [services/UploadService.md](../../services/UploadService.md) — обработка ВХОДЯЩИХ данных из 1С +- [models/ApiCron.md](../../models/ApiCron.md) — очередь запросов от 1С +- [models/ExportImport.md](../../models/ExportImport.md) — outbox таблица для всех сущностей + +--- + +_Версия: 1.0.0_ +_Создано: 2026-04-29 на основе анализа task_22 и DataController::getCreateEmployee_ diff --git a/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php b/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php index 9aa1e849..3ed7ee79 100644 --- a/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php +++ b/erp24/scripts/tasks/task_22_create_employee_for_1c_with_admins_with_empty_guid.php @@ -69,7 +69,7 @@ try { ////////////////////////////////////////////// - $compareDate = date('Y-m-d H:i:s', strtotime("-1 month", time())); + $compareDate = date('Y-m-d H:i:s', strtotime("-4 month", time())); $entityCityStore = ExportImportService::getEntityByType(); $exportCityStore = ArrayHelper::map($entityCityStore, 'entity_id', 'export_val');