fix(oid): оповещение об отклонении/согласовании (Step3 Teams-ссылка и Step4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Тимур Городецкий
2026-09-04 11:13:50 +05:00
parent 04faca37ae
commit 5f45b7da20
2 changed files with 16 additions and 6 deletions
@@ -103,8 +103,6 @@ export function useStep3Approval(args: UseStep3ApprovalArgs) {
: m, : m,
); );
onSave({ approvalChain: updatedChain }); onSave({ approvalChain: updatedChain });
const nextPending = updatedChain.find((m) => m.status === "pending");
if (nextPending?.spApprovalId) await notify(nextPending.spApprovalId);
const allDone = updatedChain.every( const allDone = updatedChain.every(
(m) => (m) =>
m.status === "approved" || m.status === "approved" ||
@@ -112,12 +110,19 @@ export function useStep3Approval(args: UseStep3ApprovalArgs) {
m.status === "recommends", m.status === "recommends",
); );
if (allDone) { if (allDone) {
// Финал: статус «Согласовано» ДО оповещения, и шлём по currentMember (у него
// spApprovalId точно есть) — иначе на финале «Согласована» не уходит, а поток
// читает ещё «На согласовании». Порядок как в ветке отклонения / в Step4.
await service.updateOIDStatus(oid.id, "approved"); await service.updateOIDStatus(oid.id, "approved");
onSave({ onSave({
approvalChain: updatedChain, approvalChain: updatedChain,
oid: { ...oid, status: "approved" }, oid: { ...oid, status: "approved" },
}); });
if (currentMember.spApprovalId) await notify(currentMember.spApprovalId);
onStepComplete(4); onStepComplete(4);
} else {
const nextPending = updatedChain.find((m) => m.status === "pending");
if (nextPending?.spApprovalId) await notify(nextPending.spApprovalId);
} }
} else { } else {
const othersToDelete = approvalChain.filter( const othersToDelete = approvalChain.filter(
@@ -145,8 +150,10 @@ export function useStep3Approval(args: UseStep3ApprovalArgs) {
approvalChain: finalChain, approvalChain: finalChain,
oid: { ...oid, status: "rejected" }, oid: { ...oid, status: "rejected" },
}); });
const initiator = finalChain.find((m) => m.role === "Инициатор"); // Шлём по отклонившему (currentMember) — у него spApprovalId гарантированно есть
if (initiator?.spApprovalId) await notify(initiator.spApprovalId); // (проверен в начале). У инициатора он мог отсутствовать после манипуляций с цепочкой,
// из-за чего reject по Teams-ссылке вообще не дёргал поток.
await notify(currentMember.spApprovalId);
} }
setActionDialog(null); setActionDialog(null);
} catch (err) { } catch (err) {
@@ -304,8 +304,11 @@ export function useStep4Approval(args: UseStep4ApprovalArgs) {
approvalChain: finalChain, approvalChain: finalChain,
oid: { ...oid!, status: "rejected" }, oid: { ...oid!, status: "rejected" },
}); });
const initiator = finalChain.find((m) => m.role === "Инициатор"); // Уведомляем по spId ОТКЛОНИВШЕГО (currentMember) — он гарантированно есть (проверен в
if (initiator?.spApprovalId) await notify(initiator.spApprovalId); // начале обработчика). У инициатора spApprovalId мог отсутствовать после манипуляций с
// цепочкой, из-за чего reject вообще не дёргал поток. Ветку письма определяет статус СТ
// («Отклонено»), получателя — автор СТ, поэтому чей элемент согласования передан — не важно.
await notify(currentMember.spApprovalId);
} }
closeActionDialog(); closeActionDialog();