Files
request-portal/src/webparts/requestPortal/shared/approval/routePatchPlan.test.ts
T
Тимур Городецкий 153c966f0b feat: история согласования — маршрут пересохраняется патчем
При доработке и повторной отправке writeRoute раньше удалял все строки
маршрута и создавал заново — кто вернул или отклонил заявку и с каким
комментарием, терялось. Теперь маршрут пересохраняется патчем
(planRoutePatch + ApprovalListService.patchSteps): строки, что остаются в
маршруте, обновляются на месте, и их прошлые решения лежат в истории
версий; недостающие шаги создаются, выпавшие и строки делегатов уходят в
корзину. Всё одним пакетом. Проверено пилотами на увольнении и рабочем
месте (обе фазы, уход в корзину). Аварийный откат для всех форм —
ROUTE_PATCH_ENABLED = false.

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 12:42:42 +05:00

95 lines
3.5 KiB
TypeScript

import { IExistingRouteRow, planRoutePatch } from './routePatchPlan';
const APPROVAL = 'Согласование';
const EXECUTION = 'Исполнение';
function row(id: number, approverId: number, phase = APPROVAL, delegatedById = 0): IExistingRouteRow {
return { id, approverId, phase, delegatedById };
}
describe('planRoutePatch', () => {
it('маршрут не изменился — все строки обновляются на месте, ничего не создаётся и не удаляется', () => {
const plan = planRoutePatch(
[row(10, 1), row(11, 2), row(12, 3)],
[
{ phase: APPROVAL, approverId: 1 },
{ phase: APPROVAL, approverId: 2 },
{ phase: APPROVAL, approverId: 3 },
]
);
expect(plan.update).toEqual([
{ rowId: 10, stepIndex: 0 },
{ rowId: 11, stepIndex: 1 },
{ rowId: 12, stepIndex: 2 },
]);
expect(plan.create).toEqual([]);
expect(plan.remove).toEqual([]);
});
it('согласующий сменился — старая строка убирается, новая создаётся', () => {
const plan = planRoutePatch(
[row(10, 1), row(11, 2)],
[
{ phase: APPROVAL, approverId: 1 },
{ phase: APPROVAL, approverId: 9 },
]
);
expect(plan.update).toEqual([{ rowId: 10, stepIndex: 0 }]);
expect(plan.create).toEqual([1]);
expect(plan.remove).toEqual([11]);
});
it('порядок шагов поменялся — строки сопоставляются по человеку, а не по позиции', () => {
const plan = planRoutePatch(
[row(10, 1), row(11, 2)],
[
{ phase: APPROVAL, approverId: 2 },
{ phase: APPROVAL, approverId: 1 },
]
);
expect(plan.update).toEqual([
{ rowId: 11, stepIndex: 0 },
{ rowId: 10, stepIndex: 1 },
]);
expect(plan.create).toEqual([]);
expect(plan.remove).toEqual([]);
});
it('один человек дважды в фазе — пары по порядку, лишняя строка убирается', () => {
const plan = planRoutePatch([row(10, 1), row(11, 1), row(12, 1)], [
{ phase: APPROVAL, approverId: 1 },
{ phase: APPROVAL, approverId: 1 },
]);
expect(plan.update).toEqual([
{ rowId: 10, stepIndex: 0 },
{ rowId: 11, stepIndex: 1 },
]);
expect(plan.remove).toEqual([12]);
});
it('фаза важна — тот же человек в другой фазе считается другим шагом', () => {
const plan = planRoutePatch([row(10, 1, APPROVAL)], [{ phase: EXECUTION, approverId: 1 }]);
expect(plan.update).toEqual([]);
expect(plan.create).toEqual([0]);
expect(plan.remove).toEqual([10]);
});
it('строка делегата не сопоставляется и убирается, даже если человек есть в новом маршруте', () => {
const plan = planRoutePatch([row(10, 1), row(11, 5, APPROVAL, 1)], [
{ phase: APPROVAL, approverId: 1 },
{ phase: APPROVAL, approverId: 5 },
]);
expect(plan.update).toEqual([{ rowId: 10, stepIndex: 0 }]);
expect(plan.create).toEqual([1]);
expect(plan.remove).toEqual([11]);
});
it('первая отправка (строк ещё нет) — всё создаётся', () => {
const plan = planRoutePatch([], [
{ phase: APPROVAL, approverId: 1 },
{ phase: EXECUTION, approverId: 2 },
]);
expect(plan).toEqual({ update: [], create: [0, 1], remove: [] });
});
});