This commit is contained in:
@@ -63,35 +63,52 @@ export class ApproverService {
|
||||
return delegated ? { ...delegated, isDelegator: true } : undefined;
|
||||
}
|
||||
|
||||
// Делегирование: согласующий добавляет делеганта ПЕРЕД собой (Order — между
|
||||
// предыдущей строкой и своей). Делегат согласует раньше и сам не делегирует
|
||||
// (allowReassign всегда false у новой строки) — зато делегировавший (текущий
|
||||
// пользователь) сохраняет право переназначить эту строку ещё раз, если делегат
|
||||
// тоже окажется недоступен (см. getPendingRowForCurrentUser/isDelegator).
|
||||
// Делегирование: согласующий добавляет одного или нескольких делегантов ПЕРЕД
|
||||
// собой. Order каждому — из равных долей промежутка между предыдущей строкой и
|
||||
// своей, В ПОРЯДКЕ ВЫБОРА: выбранный первым согласует первым. Делеганты сами не
|
||||
// делегируют (allowReassign всегда false) — зато делегировавший (текущий
|
||||
// пользователь) сохраняет право переназначить строку ещё раз, если кто-то из них
|
||||
// окажется недоступен (см. getPendingRowForCurrentUser/isDelegator).
|
||||
//
|
||||
// Дробный Order — осознанно: он позволяет вклиниться между уже существующими
|
||||
// шагами, не переписывая их (в SharePoint StepOrder — Number, дробные значения
|
||||
// хранит). Делители растут с числом делегантов, но при реальных количествах
|
||||
// (единицы) точности double хватает с колоссальным запасом.
|
||||
public async delegate(
|
||||
requestId: number,
|
||||
currentRowId: number,
|
||||
user: ISiteUser
|
||||
users: ISiteUser[]
|
||||
): Promise<void> {
|
||||
if (users.length === 0) return;
|
||||
const rows = await this.approval.getForRequest(requestId);
|
||||
const current = rows.filter((r) => r.id === currentRowId)[0];
|
||||
if (!current) return;
|
||||
// Предыдущий шаг — с меньшим Order. У параллельных шагов Order общий, поэтому
|
||||
// берём именно последний меньший, а не «строку выше по списку».
|
||||
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);
|
||||
const step = (current.order - prevOrder) / (users.length + 1);
|
||||
const delegatedById = await this.approval.getCurrentUserId();
|
||||
// Делегат наследует фазу той строки, вместо которой согласует (согласование/исполнение).
|
||||
// Обе строки — делегат и прежний согласующий — уходят в «Ждёт очереди»,
|
||||
// activateNextQueued затем активирует делегата (у него меньший order).
|
||||
await this.approval.addApproverRow(
|
||||
requestId,
|
||||
{ email: user.email, position, allowReassign: false },
|
||||
newOrder,
|
||||
REQUEST_STATUS.queued,
|
||||
current.phase,
|
||||
delegatedById
|
||||
);
|
||||
|
||||
// Последовательно, а не Promise.all: добавление строки резолвит пользователя и
|
||||
// пишет в список — параллельные пачки упираются в троттлинг SharePoint, а
|
||||
// выигрыш во времени на единицах строк незаметен.
|
||||
for (let i = 0; i < users.length; i += 1) {
|
||||
const user = users[i];
|
||||
const position = await this.approval.getJobTitle(user.loginName).catch(() => '');
|
||||
// Делегат наследует фазу той строки, вместо которой согласует (согласование/исполнение).
|
||||
await this.approval.addApproverRow(
|
||||
requestId,
|
||||
{ email: user.email, position, allowReassign: false },
|
||||
prevOrder + step * (i + 1),
|
||||
REQUEST_STATUS.queued,
|
||||
current.phase,
|
||||
delegatedById
|
||||
);
|
||||
}
|
||||
|
||||
// Все строки — и делеганты, и прежний согласующий — уходят в «Ждёт очереди»,
|
||||
// activateNextQueued затем активирует первого делеганта (у него меньший order).
|
||||
await this.approval.setStatus(current.id, REQUEST_STATUS.queued);
|
||||
await this.approval.activateNextQueued(requestId);
|
||||
emitRequestsChanged();
|
||||
|
||||
Reference in New Issue
Block a user