import { getSP } from '../pnp/pnpjs'; import '@pnp/sp/webs'; import '@pnp/sp/lists'; import '@pnp/sp/items'; import { ApprovalListService } from './approvalList.service'; import { REQUEST_STATUS } from './models/statuses'; import { IApprovalRow } from './models/IApprovalRow'; import { ISiteUser } from './models/ISiteUser'; export type ApproverDecision = 'approve' | 'return' | 'reject'; // Решение согласующего → статус его строки в списке согласования. const ROW_STATUS: Record = { approve: REQUEST_STATUS.approved, return: REQUEST_STATUS.returned, reject: REQUEST_STATUS.rejected, }; // Действия согласующего: определение его строки, применение решения, // делегирование и пересчёт статуса заявки в контентном списке. // // ВНИМАНИЕ: используется в режиме согласующего, который открывается по конкретной // заявке (requestId). Пока в портале нет маршрутизации на форму заявки, этот сервис // готов, но UI-режим согласующего ещё не подключён (см. план). export class ApproverService { constructor( private readonly approval: ApprovalListService, private readonly dataListTitle: string ) {} // Ожидающая строка текущего пользователя, если сейчас его очередь; иначе undefined. // Последовательное согласование: активна первая строка со статусом «На согласовании». public async getPendingRowForCurrentUser(requestId: number): Promise { const rows = await this.approval.getForRequest(requestId); const userId = await this.approval.getCurrentUserId(); const firstPending = rows.filter((r) => r.status === REQUEST_STATUS.onApproval)[0]; if (firstPending && firstPending.approverId === userId) return firstPending; return undefined; } // Делегирование: согласующий добавляет делеганта ПЕРЕД собой (Order — между // предыдущей строкой и своей). Делегат согласует раньше и сам не делегирует. public async delegate( requestId: number, currentRowId: number, user: ISiteUser ): Promise { const rows = await this.approval.getForRequest(requestId); const current = rows.filter((r) => r.id === currentRowId)[0]; if (!current) return; const before = rows.filter((r) => r.order < current.order); const prevOrder = before.length > 0 ? before[before.length - 1].order : current.order - 1; const newOrder = (prevOrder + current.order) / 2; const position = await this.approval.getJobTitle(user.loginName); await this.approval.addApproverRow( requestId, { email: user.email, position, allowReassign: false }, newOrder, REQUEST_STATUS.onApproval ); } // Применяет решение: обновляет строку и пересчитывает статус заявки. // Возвращает новый статус заявки. public async decide( requestId: number, rowId: number, decision: ApproverDecision, comment: string ): Promise { await this.approval.setDecision(rowId, ROW_STATUS[decision], comment); const rows = await this.approval.getForRequest(requestId); const requestStatus = this.computeRequestStatus(rows); await this.updateRequestStatus(requestId, requestStatus); return requestStatus; } private computeRequestStatus(rows: IApprovalRow[]): string { if (rows.some((r) => r.status === REQUEST_STATUS.rejected)) return REQUEST_STATUS.rejected; if (rows.some((r) => r.status === REQUEST_STATUS.returned)) return REQUEST_STATUS.returned; if (rows.length > 0 && rows.every((r) => r.status === REQUEST_STATUS.approved)) { return REQUEST_STATUS.approved; } return REQUEST_STATUS.onApproval; } private async updateRequestStatus(id: number, status: string): Promise { await getSP().web.lists.getByTitle(this.dataListTitle).items.getById(id).update({ Status: status, }); } }