# Revert Job（退回任務）功能規格

## 概覽

退回任務允許使用者將已完成的任務（COMPLETED）重新設為進行中（PROCESSING），並將後續任務狀態還原，以便重新執行。

---

## 一、判斷是否可退回：`can_revert_job`

### 取得方式

呼叫 `GET /api/1.0/grc/project/{project_uid}/job/{job_uid}` 時，response 內含 `can_revert_job: bool`。

### 判斷邏輯

```
前提條件：
  - job.status == "COMPLETED"
  - job 的 template_job_id 存在且可在 BPMN XML 中找到對應節點

單 task 流程（Start → A → End）：
  - 一律 can_revert_job = true

多 task 流程（Start → A → B → C → End）：
  ┌──────────────────────────────────────────────────────────────────┐
  │ 情境                                  │ can_revert_job           │
  ├──────────────────────────────────────────────────────────────────┤
  │ 此 task 是最後一個（無下一個 XML job） │                          │
  │   且 workflow.status == COMPLETED      │ true                     │
  │   且 workflow.status != COMPLETED      │ false                    │
  ├──────────────────────────────────────────────────────────────────┤
  │ 此 task 是第一個（無前一個 task）      │                          │
  │   且下一個 job_execution = PROCESSING  │ true（允許退第一個）     │
  │   且下一個 job_execution != PROCESSING │ false                    │
  ├──────────────────────────────────────────────────────────────────┤
  │ 此 task 是中間 task                    │                          │
  │   且下一個 job_execution = PROCESSING  │ true                     │
  │   且下一個 job_execution != PROCESSING │ false                    │
  └──────────────────────────────────────────────────────────────────┘
```

**核心規則**：`can_revert_job = true` 代表此 task 是目前流程中「最後一個完成的任務」（即緊鄰在當前 PROCESSING 任務之前），或者是流程最後一個 task 且整個流程已完成。

### 各流程狀態下的範例

流程：Start → A → B → C → End

| A | B | C | Workflow | A.can_revert | B.can_revert | C.can_revert |
|---|---|---|----------|:---:|:---:|:---:|
| PROCESSING | TODO | TODO | PROCESSING | ❌ | ❌ | ❌ |
| COMPLETED | PROCESSING | TODO | PROCESSING | ✅ | ❌ | ❌ |
| COMPLETED | COMPLETED | PROCESSING | PROCESSING | ❌ | ✅ | ❌ |
| COMPLETED | COMPLETED | COMPLETED | COMPLETED | ❌ | ❌ | ✅ |

---

## 二、執行退回：Revert Job API

### Endpoint

```
POST /api/1.0/flow-engine/task/revert/{id}
```

### Headers

| Key | Value |
|-----|-------|
| `Authorization` | `Bearer <token>` |

### Path Parameters

| 參數 | 型別 | 說明 |
|------|------|------|
| `id` | string | 目前呼叫退回的 job 的 `template_job_id`（即發起退回動作的 PROCESSING job） |

### Request Body

| 欄位 | 型別 | 必填 | 說明 |
|------|------|------|------|
| `workflow_execution_uid` | string | ✅ | 該 AO 對應的 workflow execution UID |
| `revert_to_job_id` | string | ✅ | 要退回到的目標 job 的 `template_job_id` |
| `comment` | string | | 退回原因（可空字串） |

> `template_job_id` 對應 `GET /api/1.0/grc/project/{project_uid}/job/{job_uid}` response 中的 `template_job_id` 欄位。

### Request Body 範例

```json
{
  "workflow_execution_uid": "wf-exec-uuid-...",
  "revert_to_job_id": "Activity_a1b2c3d4",
  "comment": "資料有誤，需重新填寫"
}
```

### Response

```json
{
  "status": true,
  "data": true
}
```

---

## 三、退回執行邏輯（後端）

執行退回時，後端依序完成以下操作：

### Step 1：目標 job → PROCESSING
- 找到 `revert_to_job_id` 對應的 `job_execution`，將 `status` 改為 `PROCESSING`，清除 `end_time`
- 若目標 job 為問卷類型（`actionType == "survey"`），同步將關聯問卷狀態設回 EDITING
- 通知該 job 的指派人員

### Step 2：發起 job → TODO
- 找到 URL `{id}` 對應的 `job_execution`，將 `status` 改為 `TODO`，清除 `end_time`
- 僅當 `job_id != revert_to_job_id` 時執行（避免退回自身時重複處理）

### Step 3：目標 job 之後的所有 jobs → TODO
- 依 BPMN XML 找出 `revert_to_job_id` 之後的所有 jobs（`reset_jobs`）
- 將每個 `reset_job` 的 `job_execution.status` 改為 `TODO`
- 寫入退回 comment 到每個 reset job 的 element_variable

### Step 4：寫入退回原因
- 將 `comment` 寫入發起 job 與目標 job 的 `element_variable`（`name="comment"`）

### Step 5：更新 Assessment Plan Task 狀態
- 將對應的 `assessment_plan_task.status` 更新為 `IN_PROGRESS`

### Step 6：更新 Workflow 狀態
- 若 sub workflow 為 COMPLETED → 更新回 PROCESSING
- 若 main workflow 為 COMPLETED → 更新回 PROCESSING，並將 project status 更新為 IN_PROGRESS

---

## 四、流程示意

### 情境 A：流程進行中，B 退回到 A

```
退回前：Start → A(COMPLETED) → B(PROCESSING) → C(TODO) → End
退回後：Start → A(PROCESSING) → B(TODO)      → C(TODO) → End
```

API 呼叫：
```
POST /api/1.0/flow-engine/task/revert/{B.template_job_id}
{
  "workflow_execution_uid": "...",
  "revert_to_job_id": "{A.template_job_id}",
  "comment": "退回原因"
}
```

### 情境 B：流程進行中，C 退回到 B

```
退回前：Start → A(COMPLETED) → B(COMPLETED) → C(PROCESSING) → End
退回後：Start → A(COMPLETED) → B(PROCESSING) → C(TODO)      → End
```

API 呼叫：
```
POST /api/1.0/flow-engine/task/revert/{C.template_job_id}
{
  "workflow_execution_uid": "...",
  "revert_to_job_id": "{B.template_job_id}",
  "comment": "退回原因"
}
```

### 情境 C：流程完成，退回最後一個 task C

```
退回前：Start → A(COMPLETED) → B(COMPLETED) → C(COMPLETED) → End  [workflow=COMPLETED]
退回後：Start → A(COMPLETED) → B(COMPLETED) → C(PROCESSING) → End [workflow=PROCESSING]
```

API 呼叫：
```
POST /api/1.0/flow-engine/task/revert/{C.template_job_id}
{
  "workflow_execution_uid": "...",
  "revert_to_job_id": "{C.template_job_id}",
  "comment": "退回原因"
}
```

> 流程完成狀態下退回最後一個 task 時，URL `{id}` 與 `revert_to_job_id` 均填入 C 的 `template_job_id`。

---

## 五、前端使用建議

1. 讀取 `GET /api/1.0/grc/project/{project_uid}/job/{job_uid}` 的 `can_revert_job` 決定是否顯示退回按鈕
2. 點擊退回時，彈出輸入框讓使用者填寫退回原因（`comment`）
3. `revert_to_job_id` 填入目標 job 的 `template_job_id`（通常為上一個 COMPLETED job；若為流程完成退回自身，則填入自身的 `template_job_id`）
4. URL `{id}` 填入當前 PROCESSING job 的 `template_job_id`；若退回最後一個（流程已完成），填入 C 自身的 `template_job_id`
5. `workflow_execution_uid` 可從 My Jobs list API（`POST /api/1.0/grc/jobs/my/list`）的 `workflow_execution_uid` 欄位取得

---

## 六、相關 API 欄位對照

| 資訊 | 取得來源 | 欄位 |
|------|----------|------|
| `template_job_id` | `GET /api/1.0/grc/project/{project_uid}/job/{job_uid}` | `template_job_id` |
| `can_revert_job` | `GET /api/1.0/grc/project/{project_uid}/job/{job_uid}` | `can_revert_job` |
| `workflow_execution_uid` | `POST /api/1.0/grc/jobs/my/list` | `workflow_execution_uid` |
