Те же три поля internalName делит форма «Реферальная программа» — Search
индексировал managed property только у одного из двух списков (см. sp-write
sitefield/field: разные Id при одинаковом имени). Тот же приём, что для
VacancyOpenReason в RecruitingSelection: переименовать в уникальное имя,
данные перенести.
CandidateName -> HireCandidateName
HireDate -> HireHireDate
ReplacedEmployee -> HireReplacedEmployee
На этот раз — genuine столбцы САЙТА (createsitefield + linksitefield в
sp-write), а не просто колонки списка: так задумано и в самом провижинере
(Ensure-SiteColumn — простые типы должны быть общим эталоном), просто на
практике это не всегда соблюдается (Position тоже разъехался по Id между
списками). Проверено: Id столбца на сайте и на списке совпадают.
Данные на живом сайте перенесены отдельным скриптом (migrate-hire-fields.sh,
разово, вне репозитория) — все 37 элементов, сверено на трёх.
Задело WorkplaceSetup/prefill.ts — оно читает CandidateName/HireDate ИЗ
загруженной заявки на приём для кросс-форменного предзаполнения. Referral
(та же коллизия, другая форма) НЕ трогал.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Колонка OpenReason/OpenReasonDetails называлась одинаково с полями в других
списках сайта, из-за чего Search индексировал managed property только у ОДНОГО
из них — не у этого (см. sp-write props/field, разбирались 2026-08-28/29 в
tnsu-approval-route). Переименование снимает коллизию: VacancyOpenReasonOWSMTXT/
VacancyOpenReasonDetailsOWSTEXT теперь индексируются нормально, проверено.
Данные из старых колонок на живом сайте уже перенесены отдельным скриптом
(migrate-openreason.sh, разово, вне репозитория). provisioning.json обновлён на
новые internalName — он только добавляет колонки, никогда не удаляет, так что
старые OpenReason/OpenReasonDetails на сайте провижининг сам не уберёт.
load.ts/submit.ts менять не пришлось — оба читают/пишут поля по ключам
IFormData обобщённо.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Причина найдена: одноимённые колонки в разных списках — разные поля со своими
crawled property, автосозданное managed property привязано к одному из них.
Добавлена таблица, показывающая, что PositionOWSTEXT отдаёт значения в одном
списке и молчит в другом, — иначе картина выглядит бессмыслицей.
Исправлено неверное утверждение из предыдущего коммита: проверять существование
managed property через SortList нельзя. Sortable — независимый от Retrievable
флаг, у автосозданных ows_* обычно выключен; SortList падает 400/500 и на
заведомо живом PositionOWSTEXT. Рабочий способ — сверка Search с REST, её делает
`sp-write props`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено на тенанте 2026-08-28, при разборе «перевести отчёт по вакансиям
на Search или нет»:
1. Часть полей индекс не отдаёт ВООБЩЕ, хотя значения в списках есть
(OpenReason/OpenReasonDetails, CandidateName/HireDate/ReplacedEmployee,
Ticket). Рядом в тех же списках PositionOWSTEXT, RequestStatusOWSCHCS,
SelectionRequestIdOWSNMBR и др. отдаются нормально — то есть дело не в
типе колонки и не в задержке crawl. Добавлена таблица с тем, какие имена
уже перепробованы, чтобы не ходить по кругу.
2. Отличить «managed property нет» от «есть, но пусто» через SelectProperties
нельзя — на несуществующее имя молча приходит null. Отличается сортировкой:
SortList по несуществующему свойству падает 400/500.
3. Права у заявок портала разорваны на КАЖДОМ элементе
(HasUniqueRoleAssignments = true), поэтому под своей учёткой в списке видно
2 элемента из 19 — не баг и не кэш браузера. App-only это не трогает.
Отсюда же: ItemCount списка считает всё, /items — только доступное вам.
Заодно исправлено утверждение «Search под app-only обычно ничего не отдаёт» —
на сертификате CN=PnP-SharePoint-Sync отдаёт, и postquery, и кастомные
managed properties.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Пункт меню виден только заданному списку почт (Тришина, Городецкий,
Горбенко) — группы HR в SharePoint пока нет, добавили TODO завести
проверку по ней отдельно (reportsAccess.ts). Скрыт из меню совсем, а
не показан неактивным, и прямой переход по ?rpnav=reports у остальных
редиректит на «Новую заявку».
Раздел — форма периода (С/По) и кнопка «Скачать xlsx», дергает
GET api/reports/vacancies через тот же AadHttpClient, что и остальные
вызовы approval-route API. Имя файла собирается на фронте по формату
бэкенда (VacancyReportWorkbook.FileName), а не читается из
Content-Disposition — approval-route не объявляет заголовок в
Access-Control-Expose-Headers, браузер бы его не отдал.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>