6.7 KiB
Raw Blame History

directory-import Specification

Purpose

Импорт справочников ГК / ЮЛ / ГСК / ГСЗ / филиалов из выгрузки ЕЦП CRM (xlsx или csv) по правилам §13 05-domain-rules.md: построчные решения с кодами, одна транзакция, dry_run, стратегия дублей, отчёт в ответе, в audit_event(DIRECTORY_IMPORT) и файл в S3.

Requirements

Requirement: Приём файла и формат

POST /api/v1/directories/import (multipart: file; поля dry_run (по умолчанию true), on_existing ∈ {skip, update}, по умолчанию skip; право DIRECTORY_ADMIN) SHALL принимать xlsx (первый лист) или csv (UTF-8, разделитель ; или ,) размером до 20 МБ, сопоставлять заголовки по нормализованному тексту (ИНН, iD ГК, Базовый РФ ГК в CRM, Группа компаний, Организация, ГСК, ГСЗ, Тип; порядок и лишние колонки не важны). Отсутствие любой из обязательных колонок (ИНН, iD ГК, Группа компаний, Организация, Тип) → 422 MISSING_COLUMNS с перечнем; нечитаемый файл → 422 UNSUPPORTED_FILE; превышение размера → 413.

Scenario: Реальная выгрузка принята

  • WHEN загружается fixtures/directories/ecp-export-russkiy-biskvit.xlsx с dry_run = true
  • THEN ответ 200 с отчётом: rows = 21, ошибок файла нет

Scenario: Нет колонки «Тип»

  • WHEN в csv отсутствует колонка Тип
  • THEN ответ 422 MISSING_COLUMNS, в отчёте перечислена недостающая колонка, в БД ничего не изменено

Requirement: Построчные правила и коды отчёта

Для каждой строки импорт SHALL применять §13.2 и присваивать ровно один итоговый код из перечня: UNSUPPORTED_TYPE, INVALID_INN, DUPLICATE_IN_FILE, LE_IN_OTHER_GROUP, GK_NAME_CONFLICT (строка пропущена) или LE_CREATED, LE_UPDATED, LE_EXISTS (строка применена), плюс сопутствующие события RF_CREATED, GK_CREATED, GK_UPDATED, GK_EXISTS, GSK_CREATED, GSZ_CREATED, MEMBER_ADDED, UNKNOWN_FLAG. Тип вне {ЮЛ, ИП} (ASSUMPTION(OQ-87)), ИНН не 10/12 цифр (ASSUMPTION(OQ-88)) и ИНН, уже встречавшийся в файле, пропускаются. Филиал по имени создаётся при отсутствии (ASSUMPTION(OQ-89)). ЮЛ, существующее в другой ГК, не переносится (ASSUMPTION(OQ-90)). Флаги «входит в ГСК»/«входит в ГСЗ» создают не более одной ГСК и одной ГСЗ на ГК с именем ГК (ASSUMPTION(OQ-91)). Отсутствие флага членство NOT закрывает.

Scenario: Срез «Русский Бисквит», пустая база

  • WHEN импорт ecp-export-russkiy-biskvit.xlsx с dry_run = false, on_existing = skip на базе после seed
  • THEN создана 1 ГК (crm_id = 1-AZFL6BQ, имя «Русский Бисквит»), 1 филиал «Санкт-Петербургский РФ» (RF_CREATED), 20 ЮЛ/ИП (LE_CREATED: 18 ЮЛ и 2 ИП), 1 ГСК с именем ГК и двумя членами (АО «Русский бисквит», ООО «Пекарня Счастливочная»), 0 ГСЗ; строка типа ФЛ (идёт после ИП с тем же ИНН) → UNSUPPORTED_TYPE (тип проверяется первым, DUPLICATE_IN_FILE в этом файле не возникает)

Scenario: Повторный импорт идемпотентен

  • WHEN тот же файл импортируется второй раз с on_existing = skip
  • THEN все применённые строки получают LE_EXISTS/GK_EXISTS, число строк в таблицах не меняется

Scenario: Обновление имени

  • WHEN в файле изменено имя существующего ЮЛ и импорт идёт с on_existing = update
  • THEN имя обновлено, в legal_entity_history снимок UPDATE, код строки LE_UPDATED

Scenario: ЮЛ в другой ГК

  • WHEN ИНН уже состоит в другой ГК
  • THEN строка пропущена с LE_IN_OTHER_GROUP, ЮЛ не переведено, импорт продолжается

Requirement: Транзакция, dry_run, след

Импорт SHALL выполняться в одной транзакции: ошибки строк не прерывают его, при dry_run = true транзакция откатывается, а отчёт идентичен боевому. При dry_run = false SHALL быть записан audit_event(action = DIRECTORY_IMPORT, section = DIRECTORIES, object_type = 'directory_import', after_value = отчёт, description = имя файла) и файл сохранён в S3 uploads/imports/{audit_event_id}/{original_name} с регистрацией в s3_object (bucket_role = uploads, created_by = user:{id}); при dry_run ни аудита, ни файла. Отчёт SHALL содержать счётчики (rows, groups_created, groups_updated, groups_skipped, entities_created, entities_updated, entities_skipped, memberships_created, offices_created, errors) и построчный список {row, inn, code, message}.

Scenario: dry_run не оставляет следов

  • WHEN импорт выполнен с dry_run = true
  • THEN число строк в company_group, legal_entity, regional_office, audit_event, s3_object не изменилось, отчёт возвращён

Scenario: Полная выгрузка

  • WHEN docs/source/biq/ecp-export-lagal-entity.xlsx импортируется с dry_run = true (маркер fixtures, ARM_KA_DOCS)
  • THEN ответ 200 за ≤ 60 с, rows = 8400, в отчёте есть коды UNSUPPORTED_TYPE (≥ 500), INVALID_INN, DUPLICATE_IN_FILE, RF_CREATED = 53, groups_created = 344 (из 345 ГК файла одна целиком состоит из строк-дублей и не создаётся)