Files
request-portal/src/webparts/requestPortal/forms/Clearance
Тимур Городецкий 9cacb6b271 feat: письмо инициатору при отклонении и возврате заявки
Поток RequestsNotification умеет ветки «Отклонено» и «Возврат на доработку»,
но портал их не вызывал: notifyActiveStep ищет только активные строки, а после
такого решения их нет — инициатор узнавал о решении, только открыв заявку.

notifyInitiatorOfReturn шлёт в тот же поток RowID строки, по которой решил
текущий пользователь; подключён в onDecided всех форм, кроме «Изменения
условий ТД» (доработаем позже).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 16:50:31 +05:00
..
2026-08-26 11:10:26 +00:00
2026-08-27 10:09:05 +05:00
2026-08-26 11:10:26 +00:00
2026-10-05 08:53:18 +00:00
2026-08-26 11:10:26 +00:00

Обходной лист (clearance-form)

Назначение: оформление увольнения — обход подразделений (склад, СКУД, обучение) с отметками и согласованием. Категория: Отдел кадров Списки SP: «Обходной лист — данные» / «Обходной лист — согласование» RequestTypeId (внешний API маршрутов): не хардкодится — резолвится в рантайме по pageUrl («clearance-form») через useRequestTypeId (см. «Формы больше не хардкодят RequestTypeId» в docs/forms.md). Тип уже зарегистрирован в админке approval-route (список данных «Обходной лист — данные», список SharePoint /sites/requests/Lists/List10).

Маршрут

  • Фазы: только согласование. Фазы исполнения нет — после согласования заявка просто получает статус «Согласовано» (как у «Заявки на отпуск»).
  • Матрица редактируема заявителем при новой заявке / черновике / возврате: useMatrixEditor(editable = !readOnly). Заявитель (HR) может добавлять/удалять/ переставлять согласующих поверх планируемого маршрута.
  • Условие IsManager в маршруте (если настроено в админке) резолвится по полю Position ЭТОЙ ЗАЯВКИ — «должность» увольняемого сотрудника, а не должность того, кто заполняет форму. Поэтому Position передаётся в fieldValues при планировании маршрута (getPlannedMatrix(..., [{ fieldName: 'Position', ... }])). Если поле пустое, шаг с этим условием попадёт в маршрут с предупреждением «должность не передана» (см. approval-route TemplateRouteService.ResolveAsync).

Особое поведение

  • Заявку подаёт не сам увольняемый, а HR/руководитель — за другого сотрудника. Поле «Сотрудник» — People-picker (колонка User Employee), содержит e-mail увольняемого. Планируемый маршрут резолвится по этому e-mail (getPlannedMatrix(..., data.Employee, ...)), а не по e-mail текущего пользователя — как в «Заявке на отпуск».
  • Автоподстановка должности при выборе сотрудника — только отправная точка: подтягивается текущая должность из профиля (SPS-JobTitle через approval.getJobTitle), но её можно поправить вручную (например, если человек занимал руководящую позицию до перевода на другую).
  • Да/нет-поля («Парковочная карта», «Карта СКУД») — Toggle, колонки ParkingCard/AccessCard (Boolean).
  • Сумма обучения — MoneyInput (разделитель тысяч в UI, в списке — «сырое» число), колонка TrainingAmount (Number, 2 знака), поле необязательное.
  • Дата расторжения договора — обычный input type=date, без вычислений (в отличие от даты окончания отпуска, которой нет совсем).
  • Заголовок элемента (Title): Обходной лист — {ФИО} — дд.мм.гггг.

Связь с заявлением на увольнение

Обходной лист может появиться сам, без этой формы: когда согласовано «Заявление на увольнение» (hr-dismissal), портал создаёт лист по нему и сразу отправляет на согласование — см. forms/Dismissal/createClearance.ts и README той формы. Такой лист приходит уже с заполненными сотрудником, должностью, подразделением и датой расторжения; отметки (парковка, СКУД, обучение) по-прежнему проставляют профильные согласующие. Подача этой формы руками остаётся — для увольнений, оформленных без заявления в портале.

Пара связана колонками-гиперссылками: DismissalUrl здесь → на заявление, ClearanceUrl в заявлении → на лист. Форма их не пишет (заполняет автосоздание либо вручную в SharePoint), но показывает: поле «Заявление на увольнение» — LinkField из shared/ui, read-only, открывается в той же вкладке; у листов, заведённых руками, ссылки нет и поле не рендерится.

Суммы к списанию (согласование)

Отдельная CollapseSection title="Суммы к списанию" (в стиле бывшего общего заголовка формы «Заявка», удалённого — см. docs/forms.md) объединяет визуально ДВЕ РАЗНЫЕ по правам вещи:

  1. TrainingAmount («Обучение») — доступность КАК И РАНЬШЕ: правит только КДП (hrRecordsEditable, approverRights.ts), сумма НЕ блокируется после ввода, никак не участвует в WRITE_OFF_CATEGORIES/writeOffTouchedCategories (не требует комментария решения). Изменилась ТОЛЬКО колонка хранения — пишется и читается через WriteOffTrainingAmount (ClearanceView.saveApproverFields, submit.ts.buildItem, load.ts), а не через одноимённую TrainingAmount — та осталась в схеме списка неиспользуемой. Секция видна независимо от writeOffEditableKeys (само поле — без этого условия), поэтому на форме подачи (ClearanceForm, writeOffEditableKeys не передан) поле видно и без пяти сумм ниже.
  2. 5 НЕЗАВИСИМЫХ сумм, компактной сеткой (короткие подписи, обычная 2-колоночная сетка секции), колонки WriteOff{Assets,Ppe,Accountable,Nma, Tmc}Amount (см. model.ts WRITE_OFF_CATEGORIES): «ОС», «СИЗ», «Подотчётная сумма», «НМА», «ТМЦ» — правит любой активный согласующий, см. ниже. Раньше среди них было ещё и «Обучение» — убрали как дубль TrainingAmount с ДРУГИМ поведением под тем же названием; общий заголовок группы остался.
  • Не привязаны к должности, в отличие от отметок выше: заполнить может ЛЮБОЙ согласующий, у которого сейчас активный шаг (ClearanceView.canEditWriteOffs, фаза «Согласование» — исполнителям недоступно, как и остальным отметкам).
  • Каждая сумма блокируется НАВСЕГДА после первого ввода — как только она непустая, поле становится read-only для всех, включая того, кто её ввёл. Признак блокировки — сама непустая сумма на МОМЕНТ ЗАГРУЗКИ страницы (ClearanceView.lockedWriteOffs, снимок при загрузке, а не реактивно от текущего data — иначе поле блокировалось бы прямо во время набора цифр).
  • «За что конкретно» — своего поля НЕТ. Заполнил хоть одну сумму (ClearanceView.writeOffTouchedCategories) — становится обязательным СТАНДАРТНЫЙ комментарий решения: то самое поле «Комментарий» в блоке «Ваше решение» (DecisionPanel), которым и так сопровождают Согласовать/Вернуть/ Отклонить — обычно обязательное только при возврате/отклонении, но не здесь (commentRequired у DecisionPanel, см. её же новый проп). Плейсхолдер в этот момент называет категории, которые нужно объяснить («Введите, за что сумма: ОС, ТМЦ», см. commentPlaceholder). Комментарий уходит в СТРОКУ маршрута этого согласующего (колонка Comment списка согласования) — как обычно, отдельного поля под него на данных заявки не заводили.
  • Суммы сохраняются кнопкой «Сохранить» (reviewBar) и тем же onBeforeDecide — при решении несохранённые суммы уйдут вместе с ним, ДО проверки обязательности комментария (см. DecisionPanel.decide).
  • Это ОТДЕЛЬНЫЙ механизм от TrainingAmount («Обучение» выше, привязана к КДП) — сознательно не объединялись, чтобы не менять уже установившееся поведение существующего поля.

Секции формы

RequestSection (сотрудник · должность · подразделение · дата расторжения · парковочная карта · карта СКУД — без заголовка, FormGrid) + отдельная CollapseSection «Суммы к списанию» (обучение + 5 сумм списания) + ссылка на заявление

Точки входа

  • rptype: clearance-form
  • Режим согласующего (просмотр): есть (ClearanceView)
  • «Создать на основе»: нет