fix: смена согласующего в админке «трогает» заявку, чтобы новый человек получил права (#28)
Tests / frontend-test (push) Successful in 1m32s
Tests / backend-test (push) Successful in 1m40s

Права на заявку выдаёт бэкенд по вебхуку на изменение элемента списка данных.
Массовая и одиночная замена писали только в список согласования, вебхук не
приходил, и новый человек видел «Элемент не найден». Теперь после смены
(и добавления шага) в заявку пишется её текущий RequestStatus — появляется
новая версия элемента.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/28
This commit was merged in pull request #28.
This commit is contained in:
2026-10-07 10:19:52 +00:00
parent 82c2dc36a6
commit 05f73d356a
3 changed files with 35 additions and 6 deletions
@@ -265,7 +265,11 @@ export const ManageByUserScreen: React.FC<IManageByUserScreenProps> = ({ api, di
// Фаза 2: «чистые» строки — параллельно, друг от друга не зависят.
const replaced = await mapWithLimit(
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 }
);
replaced.forEach(result => {
@@ -433,7 +433,10 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
// SharePoint, и параллельные update по одному элементу списка могут
// затереть друг друга (последний выигрывает).
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(() => {
const found = findEmployeeByEmail(employees.items, email);
@@ -507,7 +510,7 @@ export const ManageRequestsScreen: React.FC<IManageRequestsScreenProps> = ({ api
setMatrix({ ...matrix, rows: [...matrix.rows, ghostRow] });
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))
.catch((e: Error) => {
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(
matrix: Pick<ManagedRequestMatrix, 'siteUrl' | 'approvalListTitle'>,
rowId: number,
email: string,
position: string
position: string,
request: { ticketId: number; dataListId: string }
): Promise<void> {
const sp = this.clientFor(matrix.siteUrl);
const approverId = await this.resolveUserId(sp, email);
@@ -101,6 +120,7 @@ export class RequestManageService {
Title: position,
[`${FIELDS.approver}Id`]: approverId
});
await this.touchRequest(matrix.siteUrl, request.dataListId, request.ticketId);
}
/**
@@ -148,7 +168,8 @@ export class RequestManageService {
status: string,
email: string,
position: string,
allowReassign: boolean
allowReassign: boolean,
dataListId: string
): Promise<void> {
const sp = this.clientFor(matrix.siteUrl);
const approverId = await this.resolveUserId(sp, email);
@@ -161,6 +182,7 @@ export class RequestManageService {
AllowReassignToOtherApprovers: allowReassign,
[FIELDS.phase]: phase
});
await this.touchRequest(matrix.siteUrl, dataListId, ticketId);
}
/**