Files
Тимур Городецкий 40839cc8d8 feat: Compare-Manifests.ps1 — сверка манифестов с живыми списками (#2)
Манифесты отстают от живых списков, когда колонки добавляют руками или кодом,
и пересоздание по ним ломает функции. Скрипт только читает и показывает, чего
нет в манифесте, чего нет на сайте и где отличаются тип, варианты или лукап.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 14:29:24 +05:00

10 KiB
Raw Permalink Blame History

Провижионинг списков ct-forms-portal

Списки форм CT / OID / KPS воспроизводятся на боевом сайте портала /sites/sed из манифестов provisioning.json. Манифесты снимаются 1:1 с живых списков (Export-ListSchema.ps1), затем воспроизводятся на цели (Provision-Lists.ps1). Так схема всегда «под рукой» и одинаково создаётся на тесте и в бою (по аналогии с request-portal/provisioning).

Почему так, а не копия request-portal

  • Здесь литеральные имена списков (Сравнительная таблица, Позиции СТ ОИД…), а не конвенция «Форма N — данные/согласование». Скрипт request-portal на таких именах падал бы.
  • Поддержаны библиотеки документов (Коммерческие предложения*), а не только списки.
  • Одна форма (KPS) = много списков → манифест мультисписковый.

Коллизия имён KPS ↔ CT (важно!)

CT уже живёт на /sites/sed и владеет Сравнительная таблица, Позиции СТ, Оценка поставщиков, Критерии, Согласование СТ, библиотекой Коммерческие предложения. KPS раньше жил один на /sites/kps с теми же именами. При переезде на /sites/sed data-списки KPS получают суффикс КПС (зеркально суффиксу ОИД у OID):

Роль Было (на /sites/kps) Стало (на /sites/sed)
шапка Сравнительная таблица Сравнительная таблица КПС
позиции Позиции СТ Позиции СТ КПС
офферы Оценка поставщиков Оценка поставщиков КПС
критерии Критерии Критерии КПС
согласование Согласование СТ Согласование СТ КПС
библиотека КП Коммерческие предложения Коммерческие предложения КПС
без КП Не получены КП КПС Не получены КП КПС (уже уникально)
матрица Матрица согласования КПС Матрица согласования КПС (уже уникально)
справочник Поставщики Поставщики (ОБЩИЙ с CT)
справочник Курс Валют Курс Валют (ОБЩИЙ с CT)

Суффикс применяет Export автоматически (renameSuffix/renameExclude в sources.json), включая цели Lookup-колонок. Код уже приведён к новым именам:

  • forms/kps/services/KpsCtFormService.ts — карта LISTS;
  • components/config/kpsDocumentConfig.ts — siteUrl → /sites/sed, имена списков.

Общие справочники Поставщики, Курс Валют и Матрица согласования СТ создаёт манифест CT. OID тоже опирается на них (своей матрицы у OID нет). Поэтому провижионь вместе с CT (или все формы разом) — иначе Lookup'ы KPS/OID не найдут цель.

Что провижионинг НЕ делает (перенести отдельно!)

  • Данные справочников — содержимое Поставщики, Курс Валют, Матрица согласования *. Создаются только пустые списки с колонками.
  • Настройка Syntex на библиотеках Коммерческие предложения* (колонка file_content создаётся, но модель распознавания настраивается вручную).
  • URL потоков Power Automate (в коде сервисов) — указывают на прежнее окружение.
  • Единицы измерения (OID) живёт на /sites/stroy — внешний справочник, не трогаем.

Разовая подготовка (app-only, Sites.Selected)

Как в request-portal. Приложению нужен доступ и к /sites/sed, и к /sites/kps (последнее — только для экспорта схемы KPS с текущего сайта):

# один раз (если приложения ещё нет)
Register-PnPEntraIDApp -ApplicationName "PnP-Provisioning-Scoped" -Tenant "tnsukz.onmicrosoft.com" -SharePointApplicationPermissions "Sites.Selected" -Store CurrentUser

# админ выдаёт доступ к обоим сайтам
Grant-PnPAzureADAppSitePermission -AppId "<client-id>" -DisplayName "PnP-Provisioning-Scoped" -Permissions FullControl -Site "https://tnsukz.sharepoint.com/sites/sed"
Grant-PnPAzureADAppSitePermission -AppId "<client-id>" -DisplayName "PnP-Provisioning-Scoped" -Permissions Read       -Site "https://tnsukz.sharepoint.com/sites/kps"

Отпечаток сертификата:

Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -like "*PnP-Provisioning-Scoped*" } | Select-Object Subject, Thumbprint

Шаг 1. Снять схему с живых списков → манифесты

cd provisioning
./Export-ListSchema.ps1 `
  -ClientId "<client-id>" `
  -Tenant   "tnsukz.onmicrosoft.com" `
  -Thumbprint "<отпечаток>"

Создаст manifests/kps.provisioning.json, manifests/ct.provisioning.json, manifests/oid.provisioning.json. Только одна форма — флаг -Only kps.

Проверь манифесты глазами и закоммить в репозиторий — это и есть источник правды по схеме.

Шаг 2. Воспроизвести списки на /sites/sed

Сначала на тестовом сайте, потом на бою.

./Provision-Lists.ps1 `
  -SiteUrl  "https://tnsukz.sharepoint.com/sites/sed" `
  -ClientId "<client-id>" `
  -Tenant   "tnsukz.onmicrosoft.com" `
  -Thumbprint "<отпечаток>"

Идёт в два прохода: сначала все списки/библиотеки (чтобы Lookup нашли цель), потом колонки. Идемпотентно — повторный запуск ничего не ломает. -Only kps провижионит только KPS (но общие Поставщики/Курс Валют должны уже существовать).

Лог: [+] создано, [=] уже было, [~] обновлено, [!] предупреждение.

Сверка манифестов с живыми списками

Манифест отстаёт, когда колонки добавляют руками или кодом. Перед провижинингом на рабочем сайте и после ручных правок списков — сверка (только чтение):

./Compare-Manifests.ps1 `
  -SiteUrl  "https://tnsukz.sharepoint.com/sites/sed" `
  -ClientId "<client-id>" `
  -Tenant   "tnsukz.onmicrosoft.com" `
  -CertificatePath "<путь к .pfx>" -CertificatePassword "<пароль>"

- есть в манифесте, нет в живом (создаст Provision-Lists.ps1), + есть в живом, нет в манифесте (при пересоздании пропадёт — дописать в манифест), ~ отличаются тип, варианты или лукап.

Добавить новый список на рабочий сайт

Полный прогон Provision-Lists.ps1 синхронизирует варианты Choice у ВСЕХ живых списков с манифестами. Новый список добавляй точечно — -Lists "Название": остальные списки не трогаются.

Формат манифеста

{
  "form": "kps",
  "targetSite": "https://tnsukz.sharepoint.com/sites/sed",
  "lists": [
    {
      "title": "Сравнительная таблица КПС",
      "template": "GenericList",      // или "DocumentLibrary"
      "shared": false,
      "columns": [
        { "internalName": "number", "title": "Номер СТ", "type": "Number" },
        { "internalName": "status", "title": "Статус", "type": "Choice", "choices": ["Черновик", "..."] },
        { "internalName": "winSupplier", "title": "Победитель", "type": "Lookup", "lookupList": "Поставщики", "lookupShowField": "Title" }
      ]
    }
  ]
}

Типы колонок: Text, Note, Number (+decimals), Choice (+choices, +multiChoice), DateTime (+dateOnly), Boolean, User (+userSelectionMode, +allowMultiple), Lookup (+lookupList, +lookupShowField, +allowMultiple).

Порядок миграции KPS (cutover)

  1. Экспорт (Шаг 1) → манифесты в репозитории.
  2. Провижн на /sites/sed (Шаг 2) → пустые списки … КПС + общие справочники.
  3. Перенести данные (справочники, а при необходимости — рабочие СТ) на новые списки.
  4. Задеплоить обновлённый ct-forms-portal (код уже смотрит на … КПС / /sites/sed).

До шага 4 старый kps-ct-form на /sites/kps продолжает работать — он не тронут.