fix: один согласующий не дублируется на шаге маршрута заявки в 1С
База 1С — мультивыбор, поэтому на одном шаге могут сработать сразу несколько строк матрицы с одним исполнителем (Гульжан и за ЗУП, и за АТП от финдепа). Он получал бы два уведомления и решал шаг дважды. Запланированный маршрут теперь оставляет одну строку на человека в пределах шага; один человек на разных шагах не схлопывается. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,7 @@ import { RequestSection } from './sections/RequestSection';
|
||||
import { ContactSection } from './sections/ContactSection';
|
||||
import { useStableCallback } from '../../shared/useStableCallback';
|
||||
import { applyManagerOverride, managerEmail } from './managerOverride';
|
||||
import { dedupeRouteRows } from './dedupeRoute';
|
||||
import { handoffToPlanner } from './plannerHandoff';
|
||||
import { submitStatus } from './submitStatus';
|
||||
|
||||
@@ -190,8 +191,10 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
||||
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.WorkType && !!requestTypeId));
|
||||
const loadMatrix = useCallback(
|
||||
() =>
|
||||
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues)
|
||||
loadRoutePhase(approval, editId, requestStatus, 'Approval', async () =>
|
||||
dedupeRouteRows(
|
||||
await getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues)
|
||||
)
|
||||
),
|
||||
[context, userEmail, approval, editId, requestStatus, fieldValues, requestTypeId]
|
||||
);
|
||||
@@ -201,8 +204,10 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
||||
// в карточке Planner, в портале отмечает «Выполнено» (см. plannerHandoff.ts).
|
||||
const loadExecutors = useCallback(
|
||||
() =>
|
||||
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues)
|
||||
loadRoutePhase(approval, editId, requestStatus, 'Execution', async () =>
|
||||
dedupeRouteRows(
|
||||
await getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues)
|
||||
)
|
||||
),
|
||||
[context, userEmail, approval, editId, requestStatus, fieldValues, requestTypeId]
|
||||
);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { IMatrixRow } from '../../shared/approval';
|
||||
import { dedupeRouteRows } from './dedupeRoute';
|
||||
|
||||
function row(order: number, email: string): IMatrixRow {
|
||||
return { order, who: email, email, position: '', status: '—', date: '', comment: '', allowReassign: false };
|
||||
}
|
||||
|
||||
describe('dedupeRouteRows', () => {
|
||||
it('схлопывает одного человека на одном шаге (ЗУП + АТП от финдепа)', () => {
|
||||
const rows = [row(1, 'boss@tnsu.kz'), row(2, 'gulzhan@tnsu.kz'), row(2, 'Gulzhan@tnsu.kz')];
|
||||
expect(dedupeRouteRows(rows).map((r) => r.email)).toEqual(['boss@tnsu.kz', 'gulzhan@tnsu.kz']);
|
||||
});
|
||||
|
||||
it('оставляет разных людей на одном шаге', () => {
|
||||
const rows = [row(2, 'gulzhan@tnsu.kz'), row(2, 'zhanat@tnsu.kz')];
|
||||
expect(dedupeRouteRows(rows)).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('оставляет одного человека на разных шагах', () => {
|
||||
const rows = [row(1, 'gulzhan@tnsu.kz'), row(2, 'gulzhan@tnsu.kz')];
|
||||
expect(dedupeRouteRows(rows)).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('не трогает неназначенные строки', () => {
|
||||
const rows = [row(2, ''), row(2, '')];
|
||||
expect(dedupeRouteRows(rows)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { IMatrixRow } from '../../shared/approval';
|
||||
|
||||
/**
|
||||
* Убирает повторы одного и того же человека внутри одного шага маршрута.
|
||||
*
|
||||
* Зачем: «База 1С» — мультивыбор, и при нескольких базах на одном шаге могут
|
||||
* сработать несколько строк матрицы с одним исполнителем (напр. Гульжан и за
|
||||
* ЗУП, и за АТП от финдепа). Без схлопывания он получил бы две строки
|
||||
* параллельного шага — два уведомления и два решения.
|
||||
*
|
||||
* Один человек на РАЗНЫХ шагах не схлопывается — это разные согласования.
|
||||
* Строки без email (неназначенный шаг) не трогаем.
|
||||
*/
|
||||
export function dedupeRouteRows(rows: IMatrixRow[]): IMatrixRow[] {
|
||||
const seen = new Set<string>();
|
||||
return rows.filter((row) => {
|
||||
if (!row.email) return true;
|
||||
const key = `${row.order}|${row.email.toLowerCase()}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user