fix: смена согласующего в админке «трогает» заявку, чтобы новый человек получил права #28

Merged
gorbenko.pavel merged 1 commits from fix/reassign-touch-request into main 2026-10-07 10:19:53 +00:00
3 changed files with 35 additions and 6 deletions
@@ -265,7 +265,11 @@ export const ManageByUserScreen: React.FC<IManageByUserScreenProps> = ({ api, di
// Фаза 2: «чистые» строки — параллельно, друг от друга не зависят. // Фаза 2: «чистые» строки — параллельно, друг от друга не зависят.
const replaced = await mapWithLimit( const replaced = await mapWithLimit(
clean, clean,
row => requestManage.reassignApprover(row, row.id, email, position), row =>
requestManage.reassignApprover(row, row.id, email, position, {
ticketId: row.ticketId,
dataListId: row.listId
}),
{ onSettled: advanceProgress } { onSettled: advanceProgress }
); );
replaced.forEach(result => { replaced.forEach(result => {
@@ -433,7 +433,10 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
// SharePoint, и параллельные update по одному элементу списка могут // SharePoint, и параллельные update по одному элементу списка могут
// затереть друг друга (последний выигрывает). // затереть друг друга (последний выигрывает).
return requestManage return requestManage
.reassignApprover(matrix, rowId, email, position) .reassignApprover(matrix, rowId, email, position, {
ticketId: matrix.request.id,
dataListId: matrix.request.listId
})
.then(() => requestManage.updateRowSla(matrix, rowId, slaHours, askOverdueReason)) .then(() => requestManage.updateRowSla(matrix, rowId, slaHours, askOverdueReason))
.then(() => { .then(() => {
const found = findEmployeeByEmail(employees.items, email); const found = findEmployeeByEmail(employees.items, email);
@@ -507,7 +510,7 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
setMatrix({ ...matrix, rows: [...matrix.rows, ghostRow] }); setMatrix({ ...matrix, rows: [...matrix.rows, ghostRow] });
requestManage requestManage
.addRow(matrix, matrix.request.id, phase, order, initialStatus, email, position, false) .addRow(matrix, matrix.request.id, phase, order, initialStatus, email, position, false, matrix.request.listId)
.then(() => reloadMatrix(true)) .then(() => reloadMatrix(true))
.catch((e: Error) => { .catch((e: Error) => {
setMatrix(previous); setMatrix(previous);
@@ -87,13 +87,32 @@ export class RequestManageService {
}); });
} }
/**
* Права на заявку новому человеку в маршруте выдаёт бэкенд портала по вебхуку
* на изменение элемента СПИСКА ДАННЫХ. Правка одного лишь списка согласования
* вебхук не вызывает — новый человек видел бы «Элемент не найден». Поэтому
* после смены/добавления человека пишем в заявку её текущий RequestStatus как
* есть: значение то же, но у элемента появляется новая версия и приходит
* вебхук (то же делает request-portal, ApproverService.touchRequest).
*
* dataListId — серверный относительный url списка данных (партиции), где лежит
* заявка (ManagedRequestSummary.listId / UserApprovalRow.listId).
*/
private async touchRequest(siteUrl: string, dataListId: string, ticketId: number): Promise<void> {
const item = this.clientFor(siteUrl).web.getList(dataListId).items.getById(ticketId);
const current = (await item.select('RequestStatus')()) as { RequestStatus?: string };
await item.update({ RequestStatus: current.RequestStatus || '' });
}
/** Переназначить согласующего/исполнителя строки. Вызывающая сторона обязана /** Переназначить согласующего/исполнителя строки. Вызывающая сторона обязана
* проверить isEditableRowStatus(row.status) — сервис это не перепроверяет. */ * проверить isEditableRowStatus(row.status) — сервис это не перепроверяет.
* request — заявка строки: по ней «трогается» элемент списка данных, см. touchRequest. */
public async reassignApprover( public async reassignApprover(
matrix: Pick<ManagedRequestMatrix, 'siteUrl' | 'approvalListTitle'>, matrix: Pick<ManagedRequestMatrix, 'siteUrl' | 'approvalListTitle'>,
rowId: number, rowId: number,
email: string, email: string,
position: string position: string,
request: { ticketId: number; dataListId: string }
): Promise<void> { ): Promise<void> {
const sp = this.clientFor(matrix.siteUrl); const sp = this.clientFor(matrix.siteUrl);
const approverId = await this.resolveUserId(sp, email); const approverId = await this.resolveUserId(sp, email);
@@ -101,6 +120,7 @@ export class RequestManageService {
Title: position, Title: position,
[`${FIELDS.approver}Id`]: approverId [`${FIELDS.approver}Id`]: approverId
}); });
await this.touchRequest(matrix.siteUrl, request.dataListId, request.ticketId);
} }
/** /**
@@ -148,7 +168,8 @@ export class RequestManageService {
status: string, status: string,
email: string, email: string,
position: string, position: string,
allowReassign: boolean allowReassign: boolean,
dataListId: string
): Promise<void> { ): Promise<void> {
const sp = this.clientFor(matrix.siteUrl); const sp = this.clientFor(matrix.siteUrl);
const approverId = await this.resolveUserId(sp, email); const approverId = await this.resolveUserId(sp, email);
@@ -161,6 +182,7 @@ export class RequestManageService {
AllowReassignToOtherApprovers: allowReassign, AllowReassignToOtherApprovers: allowReassign,
[FIELDS.phase]: phase [FIELDS.phase]: phase
}); });
await this.touchRequest(matrix.siteUrl, dataListId, ticketId);
} }
/** /**