объединение омтс, кпс, оид и дашборд в один проект

This commit is contained in:
Тимур Городецкий
2026-08-28 16:08:02 +05:00
parent 31ef6fdbb9
commit baf8fa357d
7 changed files with 1929 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
<#
.SYNOPSIS
Экспортирует схему реальных SP-списков в манифесты provisioning.json (источник
правды по колонкам форм портала ct-forms-portal).
.DESCRIPTION
Читает описание форм из sources.json и для каждой формы:
1) подключается к ИСХОДНОМУ сайту формы (sourceSite);
2) для каждого списка из формы читает пользовательские колонки
(кастомные, не системные) и их точные типы/варианты Choice/Lookup;
3) применяет правило переименования (renameSuffix, кроме renameExclude) —
и к самому имени списка, и к целевым спискам Lookup-колонок;
4) пишет манифест provisioning.json (мульти-список) в outFile формы.
Так схема снимается 1:1 с боевых списков (без ручных догадок по типам), а затем
Provision-Lists.ps1 воспроизводит её на целевом сайте (/sites/sed).
ВАЖНО: экспортируется только СХЕМА (списки + колонки). Данные справочников
(Поставщики, Курс Валют, Матрица согласования), настройка Syntex на библиотеке КП и
URL потоков Power Automate — НЕ входят в манифест и переносятся отдельно.
Вход — по сертификату «от имени приложения» (app-only, право Sites.Selected).
Приложению нужен доступ КО ВСЕМ исходным сайтам форм (напр. и /sites/kps, и /sites/sed).
.PARAMETER ClientId
Client Id приложения-провижинера (Sites.Selected).
.PARAMETER Tenant
Тенант, например tnsukz.onmicrosoft.com
.PARAMETER Thumbprint
Отпечаток сертификата в хранилище CurrentUser\My.
.PARAMETER SourcesPath
Путь к sources.json. По умолчанию — рядом со скриптом.
.PARAMETER Only
Экспортировать только указанные формы (по ключу form). По умолчанию — все.
.EXAMPLE
./Export-ListSchema.ps1 -ClientId "<id>" -Tenant "tnsukz.onmicrosoft.com" -Thumbprint "<tp>"
.EXAMPLE
# Только KPS
./Export-ListSchema.ps1 -ClientId "<id>" -Tenant "..." -Thumbprint "<tp>" -Only kps
#>
param(
[Parameter(Mandatory = $true)][string]$ClientId,
[Parameter(Mandatory = $true)][string]$Tenant,
[Parameter(Mandatory = $true)][string]$Thumbprint,
[string]$SourcesPath = "$PSScriptRoot/sources.json",
[string[]]$Only
)
$ErrorActionPreference = 'Stop'
# --- Переименование по правилу формы (суффикс, кроме исключений) ---
function Convert-ListTitle {
param([string]$Title, [string]$Suffix, [string[]]$Exclude)
if ([string]::IsNullOrEmpty($Suffix)) { return $Title }
if ($Exclude -and ($Exclude -contains $Title)) { return $Title }
if ($Title.EndsWith($Suffix)) { return $Title } # идемпотентно — не удваиваем суффикс
return "$Title$Suffix"
}
# --- SP-тип колонки → тип манифеста ---
# Возвращает hashtable колонки в формате манифеста или $null (колонку пропускаем).
function ConvertTo-ManifestColumn {
param($Field, [string]$Suffix, [string[]]$Exclude)
$t = [string]$Field.TypeAsString
$col = [ordered]@{
internalName = [string]$Field.InternalName
title = [string]$Field.Title
}
switch ($t) {
'Text' { $col.type = 'Text' }
'Note' { $col.type = 'Note' }
'Boolean' { $col.type = 'Boolean' }
'Number' {
$col.type = 'Number'
$dec = Get-DecimalsFromSchema -SchemaXml $Field.SchemaXml
if ($null -ne $dec) { $col.decimals = $dec }
}
'Currency' {
$col.type = 'Number'
$dec = Get-DecimalsFromSchema -SchemaXml $Field.SchemaXml
$col.decimals = if ($null -ne $dec) { $dec } else { 2 }
}
'DateTime' {
$col.type = 'DateTime'
# DisplayFormat: 0 = DateOnly, 1 = DateTime.
if ([int]$Field.DisplayFormat -eq 0) { $col.dateOnly = $true }
}
'Choice' {
$col.type = 'Choice'
$col.choices = @($Field.Choices)
if (Test-FillInChoice -SchemaXml $Field.SchemaXml) { $col.fillIn = $true }
}
'MultiChoice' {
$col.type = 'Choice'
$col.multiChoice = $true
$col.choices = @($Field.Choices)
if (Test-FillInChoice -SchemaXml $Field.SchemaXml) { $col.fillIn = $true }
}
'User' {
$col.type = 'User'
$col.userSelectionMode = if ([int]$Field.SelectionMode -eq 1) { 'PeopleAndGroups' } else { 'PeopleOnly' }
}
'UserMulti' {
$col.type = 'User'
$col.allowMultiple = $true
$col.userSelectionMode = if ([int]$Field.SelectionMode -eq 1) { 'PeopleAndGroups' } else { 'PeopleOnly' }
}
'Lookup' {
$col.type = 'Lookup'
$lk = Resolve-LookupTarget -Field $Field -Suffix $Suffix -Exclude $Exclude
if ($null -eq $lk) { return $null }
$col.lookupList = $lk.list
$col.lookupShowField = $lk.show
}
'LookupMulti' {
$col.type = 'Lookup'
$col.allowMultiple = $true
$lk = Resolve-LookupTarget -Field $Field -Suffix $Suffix -Exclude $Exclude
if ($null -eq $lk) { return $null }
$col.lookupList = $lk.list
$col.lookupShowField = $lk.show
}
default {
Write-Warning " [skip] $($Field.InternalName): неизвестный/несоздаваемый тип '$t'"
return $null
}
}
if ($Field.Required) { $col.required = $true }
return $col
}
# Decimals из SchemaXml числовой колонки (атрибут Decimals='N'), иначе $null.
function Get-DecimalsFromSchema {
param([string]$SchemaXml)
if ([string]::IsNullOrEmpty($SchemaXml)) { return $null }
$m = [regex]::Match($SchemaXml, "Decimals=['""](?<d>\d+)['""]")
if ($m.Success) { return [int]$m.Groups['d'].Value }
return $null
}
# Разрешён ли ввод своих значений в Choice (атрибут FillInChoice='TRUE').
function Test-FillInChoice {
param([string]$SchemaXml)
if ([string]::IsNullOrEmpty($SchemaXml)) { return $false }
return [regex]::IsMatch($SchemaXml, "FillInChoice=['""]TRUE['""]", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
}
# Целевой список Lookup: GUID → Title (с применением rename), + show-поле.
function Resolve-LookupTarget {
param($Field, [string]$Suffix, [string[]]$Exclude)
$guid = [string]$Field.LookupList
if ([string]::IsNullOrEmpty($guid)) {
Write-Warning " [skip lookup] $($Field.InternalName): пустой LookupList (зависимый lookup?)"
return $null
}
try {
$target = Get-PnPList -Identity $guid -ErrorAction Stop
$rawTitle = [string]$target.Title
$show = if ($Field.LookupField) { [string]$Field.LookupField } else { 'Title' }
return @{ list = (Convert-ListTitle -Title $rawTitle -Suffix $Suffix -Exclude $Exclude); show = $show }
}
catch {
Write-Warning " [skip lookup] $($Field.InternalName): не разрешил целевой список $guid — $($_.Exception.Message)"
return $null
}
}
# Системные типы, которые не экспортируем даже если FromBaseType=false.
$SYSTEM_TYPES = @('Computed', 'Attachments', 'File', 'Counter', 'Guid', 'ContentTypeId', 'ModStat', 'WorkflowStatus', 'ThreadIndex')
# --- Основной проход ---
$sources = Get-Content -Path $SourcesPath -Raw -Encoding UTF8 | ConvertFrom-Json
$forms = $sources.forms
if ($Only) { $forms = $forms | Where-Object { $Only -contains $_.form } }
if (-not $forms) { throw "Формы для экспорта не найдены (проверь sources.json / -Only)" }
foreach ($f in $forms) {
Write-Host "`n=== Форма '$($f.form)' — экспорт с $($f.sourceSite) ===" -ForegroundColor Cyan
Connect-PnPOnline -Url $f.sourceSite -ClientId $ClientId -Tenant $Tenant -Thumbprint $Thumbprint
$suffix = if ($f.PSObject.Properties.Name -contains 'renameSuffix') { [string]$f.renameSuffix } else { '' }
$exclude = if ($f.PSObject.Properties.Name -contains 'renameExclude') { @($f.renameExclude) } else { @() }
$manifestLists = @()
foreach ($ls in $f.lists) {
$srcTitle = [string]$ls.title
$outTitle = Convert-ListTitle -Title $srcTitle -Suffix $suffix -Exclude $exclude
Write-Host " • список «$srcTitle» → «$outTitle»" -ForegroundColor White
$list = Get-PnPList -Identity $srcTitle -ErrorAction SilentlyContinue
if (-not $list) {
Write-Warning " [!] нет списка «$srcTitle» на $($f.sourceSite) — пропуск"
continue
}
$template = if ($ls.template) { [string]$ls.template } elseif ([int]$list.BaseType -eq 1) { 'DocumentLibrary' } else { 'GenericList' }
# Отсекаем: скрытые/базовые/readonly/системные типы, а также по имени —
# 'Title' (базовая, всегда есть), '_*' (системные: _ModelId, _ExtendedDescription …)
# и 'Prime*' (колонки модели Syntex — появляются при настройке распознавания, руками не создаём).
$fields = Get-PnPField -List $srcTitle | Where-Object {
(-not $_.Hidden) -and (-not $_.FromBaseType) -and (-not $_.ReadOnlyField) `
-and ($SYSTEM_TYPES -notcontains $_.TypeAsString) `
-and ($_.InternalName -ne 'Title') `
-and ($_.InternalName -notlike '_*') `
-and ($_.InternalName -notlike 'Prime*')
}
$columns = @()
foreach ($fld in $fields) {
$c = ConvertTo-ManifestColumn -Field $fld -Suffix $suffix -Exclude $exclude
if ($null -ne $c) {
$columns += $c
Write-Host " [+] $($c.internalName) [$($c.type)]" -ForegroundColor DarkGray
}
}
$entry = [ordered]@{
title = $outTitle
template = $template
columns = $columns
}
$manifestLists += $entry
}
$manifest = [ordered]@{
form = [string]$f.form
targetSite = [string]$f.targetSite
lists = $manifestLists
}
$outPath = Join-Path $PSScriptRoot $f.outFile
$outDir = Split-Path -Parent $outPath
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null }
$json = $manifest | ConvertTo-Json -Depth 8
# ConvertTo-Json экранирует кириллицу в \uXXXX — декодируем ТОЛЬКО их (не трогая \" \\),
# чтобы манифест был читаемым UTF-8 и при этом остался валидным JSON.
$json = [regex]::Replace($json, '\\u(?<c>[0-9a-fA-F]{4})', {
param($m) [string][char][int]::Parse($m.Groups['c'].Value, [System.Globalization.NumberStyles]::HexNumber)
})
Set-Content -Path $outPath -Value $json -Encoding UTF8
Write-Host " → записан манифест $outPath" -ForegroundColor Green
Disconnect-PnPOnline
}
Write-Host "`nЭкспорт завершён." -ForegroundColor Cyan
+188
View File
@@ -0,0 +1,188 @@
<#
.SYNOPSIS
Идемпотентно создаёт списки и библиотеки форм портала ct-forms-portal на целевом
сайте (/sites/sed) по манифестам provisioning.json.
.DESCRIPTION
Читает ВСЕ манифесты provisioning/manifests/*.provisioning.json (снятые
Export-ListSchema.ps1) и в ДВА прохода:
Проход A — создаёт все списки/библиотеки (если ещё нет) по всем манифестам.
Проход B — обеспечивает колонки на каждом списке.
Два прохода нужны, чтобы к моменту создания Lookup-колонки её целевой список уже
существовал (даже если он объявлен в другом манифесте — напр. общий «Поставщики»).
Литеральные имена: имя списка берётся как есть из манифеста (никаких конвенций
«— данные/— согласование», в отличие от request-portal). Поддержаны обычные списки
(GenericList) и библиотеки документов (DocumentLibrary).
Колонки создаются прямо НА СПИСКЕ (без общих столбцов сайта) — у форм CT/OID/KPS
имена колонок совпадают между списками, а Choice-наборы могут отличаться, поэтому
общий эталон сайта тут только мешал бы.
Повторный запуск безопасен: существующие списки/колонки не пересоздаются; для Choice
синхронизируются варианты.
Вход — по сертификату «от имени приложения» (app-only, Sites.Selected на /sites/sed).
.PARAMETER SiteUrl
Целевой сайт, например https://tnsukz.sharepoint.com/sites/sed
.PARAMETER ClientId
Client Id приложения-провижинера (Sites.Selected).
.PARAMETER Tenant
Тенант, например tnsukz.onmicrosoft.com
.PARAMETER Thumbprint
Отпечаток сертификата в хранилище CurrentUser\My.
.PARAMETER ManifestsDir
Папка с манифестами. По умолчанию — provisioning/manifests рядом со скриптом.
.PARAMETER Only
Провижионить только манифесты указанных форм (по полю form). По умолчанию — все.
.EXAMPLE
./Provision-Lists.ps1 -SiteUrl "https://tnsukz.sharepoint.com/sites/sed" `
-ClientId "<id>" -Tenant "tnsukz.onmicrosoft.com" -Thumbprint "<tp>"
#>
param(
[Parameter(Mandatory = $true)][string]$SiteUrl,
[Parameter(Mandatory = $true)][string]$ClientId,
[Parameter(Mandatory = $true)][string]$Tenant,
[Parameter(Mandatory = $true)][string]$Thumbprint,
[string]$ManifestsDir = "$PSScriptRoot/manifests",
[string[]]$Only
)
$ErrorActionPreference = 'Stop'
function ConvertTo-XmlSafe {
param([string]$Value)
return $Value.Replace('&', '&amp;').Replace('<', '&lt;').Replace('>', '&gt;').Replace("'", '&apos;').Replace('"', '&quot;')
}
function Test-FieldExists {
param([string]$List, [string]$InternalName)
return $null -ne (Get-PnPField -List $List -Identity $InternalName -ErrorAction SilentlyContinue)
}
# Создаёт/обновляет колонку НА СПИСКЕ. Идемпотентно. Lookup/UserMulti/Number-с-decimals
# — через XML (иначе PnP-cmdlet не покрывает нюансы), остальное — обычным Add-PnPField.
function Add-ListColumn {
param([string]$List, $Col)
if (Test-FieldExists -List $List -InternalName $Col.internalName) {
if ($Col.type -eq 'Choice' -and $Col.choices) {
try {
$vals = @{ Choices = [string[]]$Col.choices }
if ($null -ne $Col.fillIn) { $vals.FillInChoice = [bool]$Col.fillIn }
Set-PnPField -List $List -Identity $Col.internalName -Values $vals | Out-Null
Write-Host " [~] варианты обновлены: $($Col.internalName)" -ForegroundColor DarkGray
}
catch { Write-Warning " [!] $($Col.internalName): не удалось обновить варианты — $($_.Exception.Message)" }
}
else { Write-Host " [=] есть: $($Col.internalName)" -ForegroundColor DarkGray }
return
}
try {
switch ($Col.type) {
'Text' { Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type Text -AddToDefaultView | Out-Null }
'Note' { Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type Note -AddToDefaultView | Out-Null }
'Boolean' { Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type Boolean -AddToDefaultView | Out-Null }
'DateTime' {
Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type DateTime -AddToDefaultView | Out-Null
if ($Col.dateOnly) { Set-PnPField -List $List -Identity $Col.internalName -Values @{ DisplayFormat = 0; FriendlyDisplayFormat = 1 } | Out-Null }
}
'Choice' {
$multi = [bool]$Col.multiChoice
$type = if ($multi) { 'MultiChoice' } else { 'Choice' }
Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type $type -Choices $Col.choices -AddToDefaultView | Out-Null
if ($Col.fillIn) { Set-PnPField -List $List -Identity $Col.internalName -Values @{ FillInChoice = $true } | Out-Null }
}
'Number' {
if ($null -ne $Col.decimals) {
$xml = "<Field Type='Number' DisplayName='$(ConvertTo-XmlSafe $Col.title)' Name='$($Col.internalName)' Decimals='$($Col.decimals)' />"
Add-PnPFieldFromXml -List $List -FieldXml $xml | Out-Null
}
else {
Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type Number -AddToDefaultView | Out-Null
}
}
'User' {
if ($Col.allowMultiple) {
$xml = "<Field Type='UserMulti' DisplayName='$(ConvertTo-XmlSafe $Col.title)' Name='$($Col.internalName)' Mult='TRUE' />"
Add-PnPFieldFromXml -List $List -FieldXml $xml | Out-Null
}
else {
Add-PnPField -List $List -InternalName $Col.internalName -DisplayName $Col.title -Type User -AddToDefaultView | Out-Null
}
if ($Col.userSelectionMode) {
$mode = if ($Col.userSelectionMode -eq 'PeopleAndGroups') { 1 } else { 0 }
Set-PnPField -List $List -Identity $Col.internalName -Values @{ SelectionMode = $mode } | Out-Null
}
}
'Lookup' {
$src = Get-PnPList -Identity $Col.lookupList -ErrorAction Stop
$guid = $src.Id.ToString()
$show = if ($Col.lookupShowField) { $Col.lookupShowField } else { 'Title' }
$multAttr = if ($Col.allowMultiple) { " Mult='TRUE'" } else { '' }
$ftype = if ($Col.allowMultiple) { 'LookupMulti' } else { 'Lookup' }
$xml = "<Field Type='$ftype' DisplayName='$(ConvertTo-XmlSafe $Col.title)' Name='$($Col.internalName)' ShowField='$show' List='{$guid}'$multAttr />"
Add-PnPFieldFromXml -List $List -FieldXml $xml | Out-Null
}
default { throw "Неизвестный тип колонки '$($Col.type)'" }
}
if ($Col.required -and $Col.type -ne 'Lookup') {
Set-PnPField -List $List -Identity $Col.internalName -Values @{ Required = $true } | Out-Null
}
Write-Host " [+] $($Col.internalName) — «$($Col.title)» [$($Col.type)]" -ForegroundColor Green
}
catch { Write-Warning " [!] $($Col.internalName): $($_.Exception.Message)" }
}
Write-Host "Подключение к $SiteUrl (app-only) ..." -ForegroundColor Cyan
Connect-PnPOnline -Url $SiteUrl -ClientId $ClientId -Tenant $Tenant -Thumbprint $Thumbprint
$files = Get-ChildItem -Path $ManifestsDir -Filter '*.provisioning.json' -ErrorAction SilentlyContinue
if (-not $files) { throw "Манифесты *.provisioning.json не найдены в $ManifestsDir" }
$manifests = @()
foreach ($file in $files) {
$def = Get-Content -Path $file.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
if ($Only -and ($Only -notcontains $def.form)) { continue }
$manifests += $def
}
if (-not $manifests) { throw "Нет манифестов после фильтра -Only" }
# --- Проход A: создать все списки/библиотеки ---
Write-Host "`n=== Проход A: списки ===" -ForegroundColor Cyan
foreach ($def in $manifests) {
Write-Host "Форма '$($def.form)':" -ForegroundColor White
foreach ($ls in $def.lists) {
$tmpl = if ($ls.template -eq 'DocumentLibrary') { 'DocumentLibrary' } else { 'GenericList' }
if (Get-PnPList -Identity $ls.title -ErrorAction SilentlyContinue) {
Write-Host " [=] есть: «$($ls.title)»" -ForegroundColor DarkGray
}
else {
New-PnPList -Title $ls.title -Template $tmpl -EnableVersioning | Out-Null
Write-Host " [+] создан ($tmpl): «$($ls.title)»" -ForegroundColor Green
}
}
}
# --- Проход B: колонки ---
Write-Host "`n=== Проход B: колонки ===" -ForegroundColor Cyan
foreach ($def in $manifests) {
Write-Host "Форма '$($def.form)':" -ForegroundColor White
foreach ($ls in $def.lists) {
Write-Host " список «$($ls.title)»:" -ForegroundColor White
foreach ($col in $ls.columns) {
Add-ListColumn -List $ls.title -Col $col
}
}
}
Write-Host "`nГотово." -ForegroundColor Cyan
Disconnect-PnPOnline
+138
View File
@@ -0,0 +1,138 @@
# Провижионинг списков 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 с текущего сайта):
```powershell
# один раз (если приложения ещё нет)
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"
```
Отпечаток сертификата:
```powershell
Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -like "*PnP-Provisioning-Scoped*" } | Select-Object Subject, Thumbprint
```
## Шаг 1. Снять схему с живых списков → манифесты
```powershell
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`
Сначала на **тестовом** сайте, потом на бою.
```powershell
./Provision-Lists.ps1 `
-SiteUrl "https://tnsukz.sharepoint.com/sites/sed" `
-ClientId "<client-id>" `
-Tenant "tnsukz.onmicrosoft.com" `
-Thumbprint "<отпечаток>"
```
Идёт в два прохода: сначала все списки/библиотеки (чтобы Lookup нашли цель), потом
колонки. Идемпотентно — повторный запуск ничего не ломает. `-Only kps` провижионит
только KPS (но общие `Поставщики`/`Курс Валют` должны уже существовать).
Лог: `[+]` создано, `[=]` уже было, `[~]` обновлено, `[!]` предупреждение.
## Формат манифеста
```jsonc
{
"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` продолжает работать — он не тронут.
+530
View File
@@ -0,0 +1,530 @@
{
"form": "ct",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"lists": [
{
"title": "Сравнительная таблица",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "number",
"title": "Номер СТ",
"type": "Number",
"decimals": 0
},
{
"internalName": "project",
"title": "Проект",
"type": "Text"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Note"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"Черновик",
"Согласовано",
"На согласовании"
]
},
{
"internalName": "winSupplier",
"title": "Выбранный поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "number0",
"title": "Номер СТ",
"type": "Number",
"decimals": 0
}
]
},
{
"title": "Позиции СТ",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица",
"lookupShowField": "number"
},
{
"internalName": "demandNumber",
"title": "Номер потребности",
"type": "Text"
},
{
"internalName": "position",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "disciplineCode",
"title": "Марка",
"type": "Text"
},
{
"internalName": "SMR",
"title": "Тип СМР",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "agskCode",
"title": "Код АГСК",
"type": "Text"
},
{
"internalName": "uniqueCode",
"title": "Уникальный код",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "positionType",
"title": "Тип",
"type": "Choice",
"choices": [
"ТМЦ",
"Дополнительные затраты"
]
},
{
"internalName": "deadline",
"title": "Срок поставки",
"type": "DateTime"
},
{
"internalName": "CT0",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица",
"lookupShowField": "number"
},
{
"internalName": "quantity0",
"title": "Количество",
"type": "Number",
"decimals": 3
}
]
},
{
"title": "Оценка поставщиков",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "position",
"title": "Позиция",
"type": "Lookup",
"lookupList": "Позиции СТ",
"lookupShowField": "position"
},
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "supplierPosition",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "price",
"title": "Цена",
"type": "Number",
"decimals": 2
},
{
"internalName": "summa",
"title": "Сумма",
"type": "Number",
"decimals": 2
},
{
"internalName": "valuta",
"title": "Валюта",
"type": "Lookup",
"lookupList": "Курс Валют",
"lookupShowField": "code"
},
{
"internalName": "priceTenge",
"title": "Цена в тенге",
"type": "Number",
"decimals": 2
},
{
"internalName": "summaTenge",
"title": "Сумма в тенге",
"type": "Number",
"decimals": 2
},
{
"internalName": "offer",
"title": "КП",
"type": "Lookup",
"lookupList": "Коммерческие предложения",
"lookupShowField": "FileLeafRef"
},
{
"internalName": "deliveryDistanceKm",
"title": "deliveryDistanceKm",
"type": "Number"
}
]
},
{
"title": "Критерии",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "deliveryType",
"title": "Условия поставки",
"type": "Choice",
"choices": [
"DAP",
"FOB",
"DDP",
"EXW"
]
},
{
"internalName": "paymentType",
"title": "Условия оплаты",
"type": "Text"
},
{
"internalName": "deadline",
"title": "Сроки изготовления и доставки",
"type": "Text"
},
{
"internalName": "sertificates",
"title": "Сертификаты",
"type": "Choice",
"choices": [
"Да",
"Нет"
]
},
{
"internalName": "comment",
"title": "Примечание",
"type": "Note"
},
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица",
"lookupShowField": "number"
},
{
"internalName": "supplier0",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
}
]
},
{
"title": "Согласование СТ",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "order0",
"title": "Порядок",
"type": "Number",
"decimals": 0
},
{
"internalName": "aproover",
"title": "Согласующий",
"type": "User",
"userSelectionMode": "PeopleAndGroups"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"На согласовании",
"Согласовано",
"На исполнении",
"Выполнено",
"Рекомендую",
"Черновик",
"Отклонено"
]
},
{
"internalName": "aproovDate",
"title": "Дата",
"type": "DateTime"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "CT",
"title": "CT",
"type": "Lookup",
"lookupList": "Сравнительная таблица",
"lookupShowField": "Title"
},
{
"internalName": "canAddApprover",
"title": "Добавить делеганта",
"type": "Boolean"
}
]
},
{
"title": "Не получены КП",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "CT",
"title": "CT",
"type": "Lookup",
"lookupList": "Сравнительная таблица",
"lookupShowField": "number"
}
]
},
{
"title": "Поставщики",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "name",
"title": "Наименование поставщика",
"type": "Text"
},
{
"internalName": "status",
"title": "Статус",
"type": "Text"
}
]
},
{
"title": "Курс Валют",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "code",
"title": "Код валюты",
"type": "Choice",
"choices": [
"USD",
"RUB",
"CNY",
"EUR"
]
},
{
"internalName": "courseDate",
"title": "Дата курса",
"type": "DateTime"
},
{
"internalName": "course",
"title": "Курс",
"type": "Number",
"decimals": 3
}
]
},
{
"title": "Матрица согласования СТ",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "company",
"title": "Компания",
"type": "Choice",
"choices": [
"ТОО Монтажник-Т",
"ТОО ГеоТрек",
"ТОО Selin Invest",
"ТОО ТехЛаб",
"ТОО TANSU Construction",
"ТОО АСТЕЛ-К",
"ТОО KazPromService",
"ТОО CL MANAGEMENT",
"ТОО QAZGEOMAX",
"ТОО AFL Engineering",
"ТОО СТ Global",
"ТОО ОСН Беркут"
],
"required": true
},
{
"internalName": "department",
"title": "Отдел",
"type": "Text"
},
{
"internalName": "approover",
"title": "Согласующий",
"type": "User",
"userSelectionMode": "PeopleOnly"
},
{
"internalName": "_x041f__x043e__x0440__x044f__x04",
"title": "Порядок",
"type": "Number"
},
{
"internalName": "post",
"title": "Должность",
"type": "Text"
},
{
"internalName": "canAddAproover",
"title": "добавление согласующих",
"type": "Boolean"
}
]
},
{
"title": "Коммерческие предложения",
"template": "DocumentLibrary",
"shared": false,
"columns": [
{
"internalName": "Title",
"title": "Название",
"type": "Text"
},
{
"internalName": "_ExtendedDescription",
"title": "Описание",
"type": "Note"
},
{
"internalName": "customer",
"title": "Наименование поставщика",
"type": "Text"
},
{
"internalName": "PrimeClassificationStatus",
"title": "Состояние обработки",
"type": "Text"
},
{
"internalName": "PrimeClassificationStatusDetails",
"title": "Подробные сведения об обработке",
"type": "Note"
},
{
"internalName": "PrimeLastClassified",
"title": "Обработано",
"type": "DateTime"
},
{
"internalName": "PrimeCorrectedByUser",
"title": "Исправлен",
"type": "Boolean"
},
{
"internalName": "deadline",
"title": "Срок действия КП",
"type": "DateTime",
"dateOnly": true
},
{
"internalName": "PrimeConfidenceScore",
"title": "Степень достоверности",
"type": "Number",
"decimals": 4
},
{
"internalName": "PrimeModelVersion",
"title": "Версия модели",
"type": "Text"
},
{
"internalName": "_ModelId",
"title": "_ModelId",
"type": "Text"
},
{
"internalName": "file_content",
"title": "file_content",
"type": "Note"
},
{
"internalName": "IssueDate",
"title": "Дата документа",
"type": "DateTime",
"dateOnly": true
},
{
"internalName": "DocumentNumber",
"title": "Номер документа",
"type": "Text"
}
]
}
]
}
@@ -0,0 +1,377 @@
{
"form": "kps",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"lists": [
{
"title": "Сравнительная таблица КПС",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "number",
"title": "Номер СТ",
"type": "Number",
"decimals": 0
},
{
"internalName": "project",
"title": "Проект",
"type": "Text"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"Черновик",
"Согласовано",
"На согласовании",
"Отклонено"
]
},
{
"internalName": "winSupplier",
"title": "Выбранный поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
}
]
},
{
"title": "Позиции СТ КПС",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица КПС",
"lookupShowField": "number"
},
{
"internalName": "demandNumber",
"title": "Номер заявки на закуп",
"type": "Text"
},
{
"internalName": "position",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "catalogNumber",
"title": "Кат. номер",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "uniqueCode",
"title": "Уникальный код",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "positionType",
"title": "Тип",
"type": "Choice",
"choices": [
"ТМЦ",
"Дополнительные затраты"
]
},
{
"internalName": "deadline",
"title": "Срок поставки",
"type": "DateTime"
}
]
},
{
"title": "Оценка поставщиков КПС",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "position",
"title": "Позиция",
"type": "Lookup",
"lookupList": "Позиции СТ КПС",
"lookupShowField": "position"
},
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "supplierPosition",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "price",
"title": "Цена",
"type": "Number",
"decimals": 2
},
{
"internalName": "summa",
"title": "Сумма",
"type": "Number",
"decimals": 2
},
{
"internalName": "valuta",
"title": "Валюта",
"type": "Lookup",
"lookupList": "Курс Валют",
"lookupShowField": "code"
},
{
"internalName": "priceTenge",
"title": "Цена в тенге",
"type": "Number",
"decimals": 2
},
{
"internalName": "summaTenge",
"title": "Сумма в тенге",
"type": "Number",
"decimals": 2
},
{
"internalName": "offer",
"title": "КП",
"type": "Lookup",
"lookupList": "Коммерческие предложения КПС",
"lookupShowField": "FileLeafRef"
},
{
"internalName": "deliveryDistanceKm",
"title": "deliveryDistanceKm",
"type": "Number"
}
]
},
{
"title": "Критерии КПС",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица КПС",
"lookupShowField": "number"
},
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "deliveryType",
"title": "Условия поставки",
"type": "Choice",
"choices": [
"DAP",
"FOB",
"DDP",
"EXW"
]
},
{
"internalName": "paymentType",
"title": "Условия оплаты",
"type": "Text"
},
{
"internalName": "deadline",
"title": "Сроки изготовления и доставки",
"type": "Text"
},
{
"internalName": "sertificates",
"title": "Сертификаты",
"type": "Choice",
"choices": [
"Да",
"Нет"
]
},
{
"internalName": "comment",
"title": "Примечание",
"type": "Text"
}
]
},
{
"title": "Согласование СТ КПС",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица КПС",
"lookupShowField": "number"
},
{
"internalName": "order0",
"title": "Порядок",
"type": "Number",
"decimals": 0
},
{
"internalName": "aproover",
"title": "Согласующий",
"type": "User",
"userSelectionMode": "PeopleAndGroups"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"На согласовании",
"Согласовано",
"На исполнении",
"Выполнено"
]
},
{
"internalName": "aproovDate",
"title": "Дата",
"type": "DateTime"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
}
]
},
{
"title": "Не получены КП КПС",
"template": "GenericList",
"shared": true,
"columns": [
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "CT",
"title": "CT",
"type": "Lookup",
"lookupList": "Сравнительная таблица КПС",
"lookupShowField": "Title"
}
]
},
{
"title": "Матрица согласования КПС",
"template": "GenericList",
"shared": true,
"columns": [
{
"internalName": "company",
"title": "Компания",
"type": "Choice",
"choices": [
"ТОО Монтажник-Т",
"ТОО ГеоТрек",
"ТОО Selin Invest",
"ТОО ТехЛаб",
"ТОО TANSU Construction",
"ТОО АСТЕЛ-К",
"ТОО KazPromService",
"ТОО CL MANAGEMENT",
"ТОО QAZGEOMAX",
"ТОО AFL Engineering",
"ТОО СТ Global",
"ТОО ОСН Беркут"
]
},
{
"internalName": "department",
"title": "Отдел",
"type": "Text"
},
{
"internalName": "approover",
"title": "Согласующий",
"type": "User",
"userSelectionMode": "PeopleOnly"
},
{
"internalName": "order0",
"title": "Порядок",
"type": "Number"
},
{
"internalName": "post",
"title": "Должность",
"type": "Text"
}
]
},
{
"title": "Коммерческие предложения КПС",
"template": "DocumentLibrary",
"shared": false,
"columns": [
{
"internalName": "Title",
"title": "Название",
"type": "Text"
},
{
"internalName": "_ExtendedDescription",
"title": "Описание",
"type": "Note"
},
{
"internalName": "file_content",
"title": "file_content",
"type": "Note"
}
]
}
]
}
@@ -0,0 +1,378 @@
{
"form": "oid",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"lists": [
{
"title": "Сравнительная таблица ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "number",
"title": "Номер СТ",
"type": "Number",
"decimals": 0
},
{
"internalName": "project",
"title": "Проект",
"type": "Text"
},
{
"internalName": "workName",
"title": "Наименование работ",
"type": "Text"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"Черновик",
"Согласовано",
"На согласовании"
]
},
{
"internalName": "winSupplier",
"title": "Выигравший поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
}
]
},
{
"title": "Позиции СТ ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица ОИД",
"lookupShowField": "number"
},
{
"internalName": "parentGroup",
"title": "Наименование группы позиции",
"type": "Text"
},
{
"internalName": "position",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "positionType",
"title": "Тип",
"type": "Choice",
"choices": [
"Дополнительные затраты",
"Работа"
]
},
{
"internalName": "priceSmeta",
"title": "Цена из сметы",
"type": "Number",
"decimals": 2
},
{
"internalName": "summaSmeta",
"title": "Сумма из сметы",
"type": "Number",
"decimals": 2
}
]
},
{
"title": "Оценка поставщиков ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "position",
"title": "Позиция",
"type": "Lookup",
"lookupList": "Позиции СТ ОИД",
"lookupShowField": "position"
},
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "supplierPosition",
"title": "Наименование позиции",
"type": "Text"
},
{
"internalName": "unit",
"title": "Ед. изм",
"type": "Text"
},
{
"internalName": "quantity",
"title": "Количество",
"type": "Number",
"decimals": 3
},
{
"internalName": "price",
"title": "Цена",
"type": "Number",
"decimals": 2
},
{
"internalName": "summa",
"title": "Сумма",
"type": "Number",
"decimals": 2
},
{
"internalName": "offer",
"title": "КП",
"type": "Lookup",
"lookupList": "Коммерческие предложения",
"lookupShowField": "Title"
}
]
},
{
"title": "Критерии ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица ОИД",
"lookupShowField": "number"
},
{
"internalName": "supplier",
"title": "Поставщик",
"type": "Lookup",
"lookupList": "Поставщики",
"lookupShowField": "name"
},
{
"internalName": "deliveryType",
"title": "Условия поставки",
"type": "Choice",
"choices": [
"DAP",
"FOB",
"DDP",
"EXW"
]
},
{
"internalName": "paymentType",
"title": "Условия оплаты",
"type": "Text"
},
{
"internalName": "deadline",
"title": "Сроки изготовления и доставки",
"type": "Text"
},
{
"internalName": "sertificates",
"title": "Сертификаты",
"type": "Choice",
"choices": [
"Да",
"Нет"
]
},
{
"internalName": "comment",
"title": "Примечание",
"type": "Text"
},
{
"internalName": "criteriaLabel",
"title": "Название доп.условия",
"type": "Text"
}
]
},
{
"title": "Согласование СТ ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица ОИД",
"lookupShowField": "number"
},
{
"internalName": "order0",
"title": "Порядок",
"type": "Number",
"decimals": 0
},
{
"internalName": "aproover",
"title": "Согласующий",
"type": "User",
"userSelectionMode": "PeopleAndGroups"
},
{
"internalName": "status",
"title": "Статус",
"type": "Choice",
"choices": [
"На согласовании",
"Согласовано",
"На исполнении",
"Выполнено",
"Рекомендую",
"Черновик",
"Отклонено"
]
},
{
"internalName": "aproovDate",
"title": "Дата",
"type": "DateTime"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
},
{
"internalName": "canAddAproover",
"title": "Добавить делеганта",
"type": "Boolean"
}
]
},
{
"title": "Не получены КП ОИД",
"template": "GenericList",
"shared": false,
"columns": [
{
"internalName": "CT",
"title": "Номер СТ",
"type": "Lookup",
"lookupList": "Сравнительная таблица ОИД",
"lookupShowField": "number"
},
{
"internalName": "comment",
"title": "Комментарий",
"type": "Text"
}
]
},
{
"title": "Коммерческие предложения ОИД",
"template": "DocumentLibrary",
"shared": false,
"columns": [
{
"internalName": "Title",
"title": "Название",
"type": "Text"
},
{
"internalName": "_ExtendedDescription",
"title": "Описание",
"type": "Note"
},
{
"internalName": "file_content",
"title": "file_content",
"type": "Note"
},
{
"internalName": "_ModelId0",
"title": "_ModelId",
"type": "Text"
},
{
"internalName": "customer",
"title": "Наименование поставщика",
"type": "Text"
},
{
"internalName": "DocumentNumber",
"title": "Номер документа",
"type": "Text"
},
{
"internalName": "PrimeLastClassified0",
"title": "Обработано",
"type": "DateTime",
"dateOnly": true
},
{
"internalName": "PrimeClassificationStatusDetails0",
"title": "Подробные сведения об обработке",
"type": "Note"
},
{
"internalName": "PrimeClassificationStatus0",
"title": "Состояние обработки",
"type": "Text"
},
{
"internalName": "deadline",
"title": "Срок действия КП",
"type": "DateTime",
"dateOnly": true
},
{
"internalName": "PrimeConfidenceScore0",
"title": "Степень достоверности",
"type": "Number"
},
{
"internalName": "PrimeModelVersion0",
"title": "Версия модели",
"type": "Text"
},
{
"internalName": "IssueDate0",
"title": "Дата документа",
"type": "DateTime",
"dateOnly": true
},
{
"internalName": "PrimeCorrectedByUser0",
"title": "Исправлен",
"type": "Boolean"
}
]
}
]
}
+60
View File
@@ -0,0 +1,60 @@
{
"_comment": "Что снимать Export-ListSchema.ps1. sourceSite — где списки живут СЕЙЧАС; targetSite — куда их воспроизводит Provision-Lists.ps1. Для KPS применяется renameSuffix ' КПС' ко всем спискам, КРОМЕ renameExclude (общие справочники и уже-уникальные имена). CT владеет общими 'Поставщики'/'Курс Валют'/'Матрица согласования СТ' — поэтому провижионь с CT (или все формы разом), иначе Lookup'ы KPS/OID не найдут цель.",
"forms": [
{
"form": "kps",
"sourceSite": "https://tnsukz.sharepoint.com/sites/kps",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"outFile": "manifests/kps.provisioning.json",
"renameSuffix": " КПС",
"renameExclude": ["Поставщики", "Курс Валют", "Не получены КП КПС", "Матрица согласования КПС"],
"lists": [
{ "title": "Сравнительная таблица", "template": "GenericList" },
{ "title": "Позиции СТ", "template": "GenericList" },
{ "title": "Оценка поставщиков", "template": "GenericList" },
{ "title": "Критерии", "template": "GenericList" },
{ "title": "Согласование СТ", "template": "GenericList" },
{ "title": "Не получены КП КПС", "template": "GenericList" },
{ "title": "Матрица согласования КПС", "template": "GenericList" },
{ "title": "Коммерческие предложения", "template": "DocumentLibrary" }
]
},
{
"form": "ct",
"sourceSite": "https://tnsukz.sharepoint.com/sites/sed",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"outFile": "manifests/ct.provisioning.json",
"renameSuffix": "",
"renameExclude": [],
"lists": [
{ "title": "Сравнительная таблица", "template": "GenericList" },
{ "title": "Позиции СТ", "template": "GenericList" },
{ "title": "Оценка поставщиков", "template": "GenericList" },
{ "title": "Критерии", "template": "GenericList" },
{ "title": "Согласование СТ", "template": "GenericList" },
{ "title": "Не получены КП", "template": "GenericList" },
{ "title": "Поставщики", "template": "GenericList" },
{ "title": "Курс Валют", "template": "GenericList" },
{ "title": "Матрица согласования СТ", "template": "GenericList" },
{ "title": "Коммерческие предложения", "template": "DocumentLibrary" }
]
},
{
"form": "oid",
"sourceSite": "https://tnsukz.sharepoint.com/sites/sed",
"targetSite": "https://tnsukz.sharepoint.com/sites/sed",
"outFile": "manifests/oid.provisioning.json",
"renameSuffix": "",
"renameExclude": [],
"lists": [
{ "title": "Сравнительная таблица ОИД", "template": "GenericList" },
{ "title": "Позиции СТ ОИД", "template": "GenericList" },
{ "title": "Оценка поставщиков ОИД", "template": "GenericList" },
{ "title": "Критерии ОИД", "template": "GenericList" },
{ "title": "Согласование СТ ОИД", "template": "GenericList" },
{ "title": "Не получены КП ОИД", "template": "GenericList" },
{ "title": "Коммерческие предложения ОИД", "template": "DocumentLibrary" }
]
}
]
}