跳到主要内容

审批工作流

审批工作流

审批会将一次变更挂起,等待决定。请求会显示下一位处理人、审查历史,以及决定对记录的影响。

在工作区中操作

此现场作业示例使用虚构的变更申请。承包商提出范围或价格变更,由现场作业控制团队审查。凡是需要审查的写入,都会使用相同的审批控件。

提交与审查

  1. 承包商打开已分配的任务,新增或编辑变更申请,然后选择 Request approval(请求审批)。请求待处理期间,记录保持挂起。
  2. 控制人员打开收件箱中的 Approvals(审批),进入记录,再选择 Approval(审批) 查看请求。面板显示下一位处理人及最新理由,只提供当前查看者有权执行的操作。展开 Decision history(决定历史) 可查看完整时间线。
  3. 选择 Approve(批准) 接受变更。多步骤流程会进入下一团队;最后一步批准会解除挂起。
待处理的现场作业变更申请,正在等待 Field Operations Controllers 作出决定,并显示可用的决定。
待处理:控制人员审查挂起的变更,再作出决定。

要求修正

  1. 控制人员填写理由,再选择 Request changes(要求修改)。记录保留拟议值,并显示 RFC。悬停或聚焦该标记即可看到 Request for change(修改请求)。
  2. 请求者阅读理由,选择 Edit(编辑),修正字段并保存。处于要求修改状态时,只有请求者可以编辑挂起的记录。
  3. 请求者打开 Approval(审批),选择 Resubmit(重新提交)。同一请求会从第一步重新审查。控制人员可以批准、拒绝或再次要求修改;每轮操作都会保留在时间线中。
RFC 变更申请显示修改理由,以及下一位需要处理的请求者。
RFC:修改理由保留在请求中,由请求者完成修正。
已批准的变更申请,在同一时间线中显示请求、修改、重新提交与批准。
修正后获批:重新提交会保留原请求及其完整历史。

拒绝或撤回

当变更不应继续时,选择 Reject(拒绝)。拒绝会结束请求并回滚变更。此例中,拒绝拟议的涨价会恢复原金额。若拒绝新增,新增记录会被移除;若拒绝删除,已删除记录会被重建。Decision history(决定历史) 会列出 Rolled back(已回滚) 的内容,以及任何 Not restored(未恢复) 的字段。更早的请求显示在 Previous requests(过往请求) 下。

请求者可以在审查期间选择 Withdraw(撤回),包括已收到修改请求的情况。这也会回滚变更。拒绝与撤回均为最终决定;后续提案会创建新请求。

已拒绝的变更更新,显示决定理由、回滚结果“Variations restored(变更已恢复)”及决定历史。
已拒绝:拟议更新被回滚,决定仍保留在历史中。

声明路由

在策略的集合授权中声明 approval,按 create、update 或 delete 分键。它路由策略未直接授予的写入。读取、操作与业务状态不承载审批配置。

// src/access_control/policy/+field_ops_contractor.policy.ts (templates/field-operations, excerpt)
// no direct create or update grant: each write is held for Field Operations Controllers
variation_requests: {
  read: {
    where: OWN_JOB,
    fields: ['id', 'job_assignment_id', 'requested_at', 'title', 'description', 'amount']
  },
  approval: {
    create: {
      fields: ['job_assignment_id', 'requested_at', 'title', 'description', 'amount'],
      steps: [['019f8a40-0000-7000-8000-000000000001']]
    },
    update: {
      fields: ['title', 'description', 'amount'],
      steps: [['019f8a40-0000-7000-8000-000000000001']]
    }
  }
}

approval 是一条路由或有序路由列表:{ match?, where?, fields?, steps, superseded_by? }。至多八个顺序 steps 中,每一步列出可相互替代的来源团队句柄。superseded_by 指定可以完成所有剩余步骤的团队。第一条匹配路由决定步骤;没有匹配项时,写入仍被拒绝。

match 检查 record(拟议行)、previous(已存储行)、changed(提交的字段)与 requestor(in_team、no_team 或具名团队),并可用 and、or 与 not 组合。

挂起的变更

路由到审批的写入会暂时提交,并返回带请求 id 的 pendingApproval。相关记录带有 $approval;请求保存它们在请求前的版本,作为回滚检查点。待处理时,当前步骤的审批者可以编辑这些记录。要求修改后,只有请求者可以编辑。编辑会加入同一请求。其他写入(包括管理员编辑)都会被拒绝。

在代码中作出决定

import { Effect } from 'effect';
import { bolt, fork } from '$bolt';

fork(bolt.act('variation_requests.create', input).pipe(
  Effect.tap((outcome) => Effect.sync(() => {
    if (outcome.kind === 'pendingApproval') {
      // the provisional write is held; rejection or withdrawal restores its checkpoint
      requestId = outcome.requestId;
    }
  }))
), (error) => { message = error.message; });

// alternative decisions by the current step's approver — choose one
fork(bolt.approvals.process(requestId, { status: 'APPROVED' }), (error) => { message = error.message; });
fork(bolt.approvals.process(requestId, {
  status: 'REQUEST_FOR_CHANGE', reason: 'Please specify the bracket dimensions.'
}), (error) => { message = error.message; });
fork(bolt.approvals.process(requestId, { status: 'REJECTED' }), (error) => { message = error.message; });

// the requester, after editing and saving the requested correction
fork(bolt.approvals.process(requestId, { status: 'RESUBMITTED' }), (error) => { message = error.message; });
// or withdraw the open request and roll back the change
fork(bolt.approvals.withdraw(requestId), (error) => { message = error.message; });

引擎根据请求的当前状态与查看者的来源团队检查每个决定。管理员或 superseded_by 团队可以填写理由后越级批准待处理请求。自动化、集成与 Envoy 不能审批。使用原生审批面板可获得允许的操作、必填理由及完整时间线。

结束请求

最终批准或越级批准会解除挂起,保留变更。拒绝或撤回会在与决定相同的语句中恢复检查点。无法恢复的字段会列在时间线中。挂起记录的出站发送会等待批准。已结束请求会保留历史,不能重新打开。

请求状态

未结束请求的状态为 ONGOING 或 CHANGES_REQUESTED。最终状态为 APPROVED、REJECTED、WITHDRAWN 与 SUPERSEDED。重新提交会将同一请求恢复为 ONGOING,从第一步开始。

通知下一位处理人

在集合上声明通知。把 approvalStepRequested 发给 step_approvers,让请求进入审批者收件箱;把 approvalChangesRequested 发给 requestor,将修改要求交回请求者。重新提交会触发 approvalResubmitted,并再次请求第一步审批。