feat: история согласования — маршрут пересохраняется патчем

При доработке и повторной отправке writeRoute раньше удалял все строки
маршрута и создавал заново — кто вернул или отклонил заявку и с каким
комментарием, терялось. Теперь маршрут пересохраняется патчем
(planRoutePatch + ApprovalListService.patchSteps): строки, что остаются в
маршруте, обновляются на месте, и их прошлые решения лежат в истории
версий; недостающие шаги создаются, выпавшие и строки делегатов уходят в
корзину. Всё одним пакетом. Проверено пилотами на увольнении и рабочем
месте (обе фазы, уход в корзину). Аварийный откат для всех форм —
ROUTE_PATCH_ENABLED = false.

Из версий строк маршрута и заявки собирается лента «История
согласования» (shared/approval/history): отправки, решения с
комментариями и временем на шаге, делегирование, замена исполнителя,
у кого заявка сейчас. Свёрнутый раздел в карточке и в форме доработки
всех форм, грузится только при раскрытии.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-09-29 12:42:42 +05:00
parent 5b1e94bf42
commit 153c966f0b
40 changed files with 1334 additions and 20 deletions
@@ -14,6 +14,7 @@ import {
} from '../../shared/ui';
import {
ApprovalMatrix,
ApprovalHistory,
StatusBadge,
useMatrixEditor,
getPlannedMatrix,
@@ -327,6 +328,19 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
waitingText="Укажите начальника участка и проект, чтобы построить маршрут согласования."
/>
{/* Автору на доработке важнее всего видеть, почему вернули или
отклонили. Только у уже существующей заявки: у новой истории нет. */}
{editId && (
<div className="rp-no-print">
<ApprovalHistory
approval={approval}
dataListRef={editListId || dataListTitle(FORM_ID)}
requestId={editId}
reloadKey={requestStatus}
/>
</div>
)}
<SubmitActions
onDelete={handleDelete}
submit={submit}
@@ -8,6 +8,7 @@ import {
DecisionPanel,
StatusBadge,
ApprovalMatrix,
ApprovalHistory,
useMatrixEditor,
notifyActiveStep,
useRequestTypeId
@@ -155,6 +156,12 @@ const ConcreteRequestView: React.FC<IConcreteRequestViewProps> = ({ context, ite
</div>
{/* Панель решения — ВНЕ печати и FormModeProvider (комментарий редактируемый). */}
{/* История согласования: маршрут пересохраняется патчем (см.
writeRoute): прошлые решения лежат в версиях строк. */}
<div className="rp-no-print">
<ApprovalHistory approval={approval} dataListRef={dataListRef} requestId={itemId} reloadKey={decisionSeq} />
</div>
<div className="rp-no-print">
<DecisionPanel
service={approverService}