feat: добавление исполнителей при правке мебели/ПО согласующим
Маршрут планируется один раз при подаче: если мебель или ПО отметил руководитель на своём шаге, исполнитель по ним не появлялся. Теперь при сохранении правок (и перед решением) строятся два плана исполнения — со старыми и новыми значениями — и разница встаёт в конец исполнения «Ждёт очереди» с SLA из маршрута. Никого не удаляем, уже стоящих не дублируем; ошибка добавления не теряется и блокирует решение. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,13 @@ import '@pnp/sp/webs';
|
||||
import '@pnp/sp/lists';
|
||||
import { IList } from '@pnp/sp/lists';
|
||||
import '@pnp/sp/items';
|
||||
import { ApprovalListService } from './approvalList.service';
|
||||
import { ApprovalListService, IApproverToWrite } from './approvalList.service';
|
||||
import { REQUEST_STATUS, APPROVAL_PHASE } from './models/statuses';
|
||||
import { IApprovalRow } from './models/IApprovalRow';
|
||||
import { ISiteUser } from './models/ISiteUser';
|
||||
import { emitRequestsChanged } from '../requestEvents';
|
||||
import { planQueuedStepMove, StepMoveDirection } from './queuedStepMove';
|
||||
import { planStepAdd } from './routeStepAdd';
|
||||
import { IStepAddRow, planStepAdd } from './routeStepAdd';
|
||||
|
||||
function isActiveStatus(status: string): boolean {
|
||||
return status === REQUEST_STATUS.onApproval || status === REQUEST_STATUS.onExecution;
|
||||
@@ -209,6 +209,40 @@ export class ApproverService {
|
||||
return rowsAfter.filter((r) => isActiveStatus(r.status) && !activeBefore.has(r.id)).map((r) => r.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить в поданную заявку исполнителей, которых привела правка полей
|
||||
* маршрута согласующим (КОГО — решает planMissingExecutors). Каждый встаёт в
|
||||
* КОНЕЦ исполнения «Ждёт очереди», по тем же правилам, что addStep
|
||||
* (planStepAdd), но без права редактора маршрута: людей назначает маршрут из
|
||||
* админки, а не пользователь. SLA шага переносится из маршрута.
|
||||
*
|
||||
* Возвращает Id строк, ставших активными (обычно пусто); им нужно оповещение.
|
||||
*/
|
||||
public async addMissingExecutors(requestId: number, executors: IApproverToWrite[]): Promise<number[]> {
|
||||
if (executors.length === 0) return [];
|
||||
const rows = await this.approval.getForRequest(requestId);
|
||||
const activeBefore = new Set(rows.filter((r) => isActiveStatus(r.status)).map((r) => r.id));
|
||||
|
||||
// По одному и с локальным дописыванием rows: иначе второй исполнитель
|
||||
// получил бы тот же order, что первый, и стал бы параллельным шагом.
|
||||
const planned: IStepAddRow[] = rows.slice();
|
||||
for (const executor of executors) {
|
||||
const approverId = await this.approval.getUserIdByEmail(executor.email);
|
||||
const plan = planStepAdd(planned, APPROVAL_PHASE.execution, approverId);
|
||||
if (!plan.ok) throw new Error(plan.error);
|
||||
await this.approval.addApproverRow(requestId, executor, plan.order, REQUEST_STATUS.queued, APPROVAL_PHASE.execution);
|
||||
planned.push({ approverId, order: plan.order, status: REQUEST_STATUS.queued, phase: APPROVAL_PHASE.execution });
|
||||
}
|
||||
|
||||
await this.approval.activateNextQueued(requestId);
|
||||
const rowsAfter = await this.approval.getForRequest(requestId);
|
||||
|
||||
// Новым исполнителям — права на элемент данных (см. addStep).
|
||||
await this.touchRequest(requestId);
|
||||
emitRequestsChanged();
|
||||
return rowsAfter.filter((r) => isActiveStatus(r.status) && !activeBefore.has(r.id)).map((r) => r.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Шаг фазы «Исполнение», который ещё можно заменить или удалить: ждёт очереди
|
||||
* или уже активен («На исполнении»). Выполненный шаг — решение человека, его
|
||||
|
||||
Reference in New Issue
Block a user