157 lines
9.1 KiB
TypeScript
157 lines
9.1 KiB
TypeScript
// Домен вкладки «Редактирование заявок»: живёт целиком в 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[];
|
||
}
|