153c966f0b
При доработке и повторной отправке writeRoute раньше удалял все строки маршрута и создавал заново — кто вернул или отклонил заявку и с каким комментарием, терялось. Теперь маршрут пересохраняется патчем (planRoutePatch + ApprovalListService.patchSteps): строки, что остаются в маршруте, обновляются на месте, и их прошлые решения лежат в истории версий; недостающие шаги создаются, выпавшие и строки делегатов уходят в корзину. Всё одним пакетом. Проверено пилотами на увольнении и рабочем месте (обе фазы, уход в корзину). Аварийный откат для всех форм — ROUTE_PATCH_ENABLED = false. Из версий строк маршрута и заявки собирается лента «История согласования» (shared/approval/history): отправки, решения с комментариями и временем на шаге, делегирование, замена исполнителя, у кого заявка сейчас. Свёрнутый раздел в карточке и в форме доработки всех форм, грузится только при раскрытии. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Заявка на услуги (wh-service-request)
Назначение: электронный аналог бумажной Service Requisition Form ПТО — заявка на договор/услуги/работы (субподряд, поставка, аренда) по проекту.
Категория: Склад / Снабжение
Списки SP: «Заявка на услуги — данные», «Заявка на услуги — согласование»
RequestTypeId (внешний API маршрутов): резолвится в рантайме по pageUrl (см. docs/forms.md)
Маршрут
- Фазы: согласование (без фазы исполнения — договор дальше ведётся вне портала)
- Особенности: маршрут зависит от выбранного проекта (
ProjectId, oid изProjectPicker) — пока проект не выбран,resolveRouteотвечает 404, и матрица не резолвится (см.matrixReadyвServiceRequestForm.tsx, тот же приём, что вTmcTransferForm)
Особое поведение
- Подача за другого: заявитель —
PeopleField, по умолчанию текущий пользователь, можно выбрать любого — маршрут резолвится на ВЫБРАННОГО (см.handleRequesterChange). - Табличная часть «Приложения» (
Annexes) — описание документа + номер приложения + сам файл, прикреплённый прямо к строке (fileName, хранится как встроенное вложение элемента списка — см. shared/attachments). Отдельного общего блока «Вложения» у формы нет: каждый файл привязан к своей строке. - Табличная часть «Смета» (
Costs) — список групп (разделов работ), внутри каждой свои позиции; название группы необязательно. Сумма по позиции, по группе и общий итог считаются на лету (qty × rate, см.cost.ts), не хранятся в структуре — правка количества/цены не может их рассинхронизировать. - Под «Объём работ» (текстовое поле) — детализация по позициям в ТОМ ЖЕ формате, что и смета: группы/позиции/цена/сумма (
WorkVolumes, см.CostGroupsEditor.tsx— общий редактор, переиспользуется и в CostsSection, и здесь). Дополняет текст итоговыми цифрами, не заменяет его. Costs/WorkVolumesсериализуются в JSON-колонки (EstimateJson/WorkVolumesJson) ОБЪЕКТОМ{ groups, total }— готовый итог кладётся прямо в JSON рядом с группами (см.serializeGroups/parseGroupedJsonвcost.ts), а не отдельной числовой колонкой: он и так пересчитывается из groups при каждом сохранении, отдельная колонка была бы лишней схемой ради того же значения. Сама форма читает толькоgroups— total из хранилища не доверяет, всегда считает заново (costTotal);totalв JSON нужен тем, кто читает колонку напрямую (отчёты, sp-query), не поднимая форму.Annexesсериализуется вAnnexesJsonпросто массивом (без обёртки total — там считать нечего) — тот же приём, что вTmcTransfer(ItemsJson).- Продолжительность работ под датами «начало/окончание» — производная величина (считается на лету), в SharePoint не пишется.
- Фазы исполнения нет:
saveServiceRequestпишет маршрут с пустым списком исполнителей.
Секции формы
HeaderSection, ScopeSection, AnnexesSection, CostsSection, RemarksSection
Точки входа
- rptype:
wh-service-request - Режим согласующего (просмотр): есть (
ServiceRequestView) - «Создать на основе»: —