Files
tnsu-approval-route/frontend/src/webparts/approvalAdmin/models/requestManage.ts
T
2026-08-19 15:04:41 +05:00

157 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Домен вкладки «Редактирование заявок»: живёт целиком в SharePoint-списках
// «<форма> — данные» / «<форма> — согласование», которыми управляет
// request-portal (см. request_type.list_id/site_url — ссылки именно на список
// данных; список согласования вычисляется по конвенции имени, см. approvalListTitle
// в requestManageService.ts). Это НЕ сущности Tnsu.ApprovalRoute.Api — совпадений
// с models/templateRoutes.ts нет, здесь другое хранилище.
//
// Значения статусов — Choice-колонки SharePoint, 1:1 с
// request-portal/src/webparts/requestPortal/shared/approval/models/statuses.ts
// (тот же самый список, то же самое хранилище — просто другой клиент).
export const REQUEST_STATUS = {
draft: 'Черновик',
onApproval: 'На согласовании',
queued: 'Ждёт очереди',
onExecution: 'На исполнении',
executed: 'Выполнено',
approved: 'Согласовано',
rejected: 'Отклонено',
returned: 'Возвращена на доработку',
execution: 'Выполнение',
closed: 'Закрыта'
} as const;
export const APPROVAL_PHASE = {
approval: 'Согласование',
execution: 'Исполнение'
} as const;
// Строка маршрута в одном из этих статусов ЕЩЁ НЕ решена согласующим/исполнителем
// ОКОНЧАТЕЛЬНО — её можно менять (переназначить человека, удалить, добавить новую
// рядом). «Возвращена на доработку» сюда включена нарочно: это не финальное
// решение (заявка вернётся на новый круг после доработки), поэтому согласующего
// на этом шаге всё ещё можно переназначить. approved/executed/rejected —
// финальные решения, трогать нельзя ни при каких условиях.
//
// ВАЖНО: это НЕ то же самое, что «можно переставить» (см. isMovableRowStatus
// ниже) — «Возвращена на доработку» уже означает, что своё слово по этому шагу
// согласующий сказал (просто мы разрешаем потом переназначить человека), поэтому
// двигать такой шаг/менять его параллельность нельзя, а переназначить — можно.
const EDITABLE_ROW_STATUSES: string[] = [
REQUEST_STATUS.draft,
REQUEST_STATUS.queued,
REQUEST_STATUS.onApproval,
REQUEST_STATUS.onExecution,
REQUEST_STATUS.returned
];
export function isEditableRowStatus(status: string): boolean {
return EDITABLE_ROW_STATUSES.indexOf(status) !== -1;
}
// Строка в одном из этих статусов можно двигать (стрелки вверх/вниз) и менять её
// параллельность с соседней — строго подмножество EDITABLE_ROW_STATUSES, без
// «Возвращена на доработку»: решение по шагу уже принято, менять его МЕСТО в
// маршруте (а значит и то, когда он активируется повторно) не должно быть можно,
// хотя переназначить исполнителя на нём — можно (см. isEditableRowStatus).
const MOVABLE_ROW_STATUSES: string[] = [
REQUEST_STATUS.draft,
REQUEST_STATUS.queued,
REQUEST_STATUS.onApproval,
REQUEST_STATUS.onExecution
];
export function isMovableRowStatus(status: string): boolean {
return MOVABLE_ROW_STATUSES.indexOf(status) !== -1;
}
// Заявка в этом статусе прошла полностью (все фазы завершены) — маршрут можно
// только посмотреть, редактирование выключается целиком независимо от статусов
// отдельных строк.
export function isFullyClosedRequestStatus(status: string): boolean {
return status === REQUEST_STATUS.closed;
}
export interface ManagedRequestSummary {
id: number;
title: string;
status: string;
created: string; // ISO
authorName: string;
/**
* Уникальный код заявки (колонка RequestUniqueCode списка данных) — то, что
* реально видит заявитель, в отличие от служебного числового Id списка
* SharePoint. null — у заявки его нет (старая заявка, колонка не заполнена)
* или список данных этой колонки не заводит; тогда в UI показывается id.
*/
// eslint-disable-next-line @rushstack/no-new-null -- зеркалит SharePoint: колонки может не быть
uniqueCode: string | null;
/**
* Список-партиция, в котором физически лежит эта заявка. У разных партиций
* независимая нумерация Id (см. ТЗ по архивации), поэтому один и тот же
* requestId в разных партициях — это РАЗНЫЕ заявки: без listId открыть
* заявку однозначно нельзя.
*/
listId: string;
}
export interface ManagedRequestPage {
items: ManagedRequestSummary[];
hasMore: boolean;
}
// Строка списка «… — согласование». approverId/approverEmail нужны и для
// отображения, и как исходное значение при открытии формы переназначения.
export interface ManagedApprovalRow {
id: number;
ticketId: number;
// eslint-disable-next-line @rushstack/no-new-null -- lookup-поле SharePoint, у ghost-строки (см. handleAddApprover) значения ещё нет
approverId: number | null;
approverName: string;
approverEmail: string;
position: string;
status: string;
date: string;
/**
* Та же дата, что в `date`, но необработанная ISO-строка (или '', если решения
* ещё не было) — нужна для расчёта просрочки (см. utils/sla.ts/isRowOverdue):
* `date` отформатирована в «дд.мм.гггг» и назад в Date уже не превращается
* надёжно (зависит от локали при парсинге).
*/
dateIso: string;
comment: string;
order: number;
allowReassign: boolean;
phase: string;
/**
* SLA именно ЭТОЙ строки в часах. Значение проставляется при создании
* маршрута (портал берёт его из resolve, где наследование «шаг перекрывает
* тип» уже применено), и здесь его можно поправить для конкретной заявки.
* null — в строке не задано, работает SLA типа заявки.
*/
// eslint-disable-next-line @rushstack/no-new-null -- пусто в SharePoint приходит как null и значит «не задано», это не 0
slaHours: number | null;
/** Спрашивать ли причину просрочки по этой строке. null — берётся от типа заявки. */
// eslint-disable-next-line @rushstack/no-new-null -- см. slaHours
askOverdueReason: boolean | null;
}
export interface ManagedRequestMatrix {
request: ManagedRequestSummary;
/** Абсолютный URL сайта, где лежат списки данных/согласования этого типа заявки. */
siteUrl: string;
/** Title списка «… — данные» (для тела запроса к потоку оповещения, см. RequestManageService.notifyRow). */
dataListTitle: string;
/**
* Абсолютный URL списка данных — заявки нумерованные (списки-партиции, см.
* ТЗ по архивации), и по мере роста текущий список меняется. Ссылка нужна,
* чтобы администратор видел, в каком физическом списке лежит открытая
* заявка, и мог перейти туда напрямую, не выясняя это вручную.
*/
dataListUrl: string;
/** Title списка «… — согласование» (вычислен из Title списка данных). */
approvalListTitle: string;
/** Обе фазы вместе, упорядочены по StepOrder. */
rows: ManagedApprovalRow[];
}