|
|
@@ -0,0 +1,576 @@
|
|
|
+# 转账业务备注 + 缴费确认中撤回 Implementation Plan
|
|
|
+
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
+
|
|
|
+**Goal:** 意向金与正式协议转账每笔支持业务备注录入/展示,并展示财务备注;`jfqrz` 支持业务撤回为 `dfj` 后修改再提交;`yjf` 不可撤回。
|
|
|
+
|
|
|
+**Architecture:** 复用表字段 `remark` / `finance_remark`,无 DDL。状态规则集中在 `protocol_transfer_status.can_withdraw_receipt`;预签与正式各增 `withdraw_receipt` CRUD + `POST .../withdraw` API。前端转账表统一列:备注(dfj 可编)+ 财务备注(只读)+ `jfqrz` 操作列「撤回」。
|
|
|
+
|
|
|
+**Tech Stack:** FastAPI / SQLAlchemy / unittest;Vue 3 + Element Plus;现有 `presign-order` / `protocol-sample-payment` API 模块。
|
|
|
+
|
|
|
+**Spec:** `docs/superpowers/specs/2026-10-10-transfer-remark-and-withdraw-design.md`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## File map
|
|
|
+
|
|
|
+| File | Responsibility |
|
|
|
+|------|----------------|
|
|
|
+| `backend/app/services/protocol_transfer_status.py` | `can_withdraw_receipt` |
|
|
|
+| `backend/tests/test_presign_order.py`(或同目录新测) | 撤回规则 + 预签 withdraw 行为单测 |
|
|
|
+| `backend/tests/test_protocol_sample_payment.py` | 正式转账 withdraw 行为单测(可用纯函数+mock 或既有风格) |
|
|
|
+| `backend/app/crud/presign_order.py` | `withdraw_receipt` |
|
|
|
+| `backend/app/crud/protocol_sample_payment.py` | `withdraw_receipt`(正式) |
|
|
|
+| `backend/app/api/presign_order.py` | `POST /presign-transfers/{id}/withdraw` |
|
|
|
+| `backend/app/api/protocol_sample_payment.py` | `POST /payment-transfers/{id}/withdraw` |
|
|
|
+| `web-admin/src/api/presign-order.ts` | `fetchWithdrawPresignTransfer` |
|
|
|
+| `web-admin/src/api/protocol-sample-payment.ts` | `fetchWithdrawPaymentTransfer` |
|
|
|
+| `web-admin/.../PresignOrderFormDialog.vue` | 备注列 + 撤回 |
|
|
|
+| `web-admin/.../SamplePaymentPanel.vue` | 备注列 + 撤回 |
|
|
|
+| `web-admin/.../ProtocolFormDialog.vue` | 意向金只读表补备注列(财务备注若已有则对齐) |
|
|
|
+| `web-admin/.../ProtocolViewDialog.vue` | 意向金 + 样本转账只读补列 |
|
|
|
+| `web-admin/.../payment-confirm/index.vue` | 列表补业务「备注」列 |
|
|
|
+| `web-admin/.../payment-info/PaymentExcelList.vue` | 期次转账明细卡补备注/财务备注 |
|
|
|
+
|
|
|
+**SQL:** 无需迁移。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 1: 状态规则 `can_withdraw_receipt`(TDD)
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `backend/app/services/protocol_transfer_status.py`
|
|
|
+- Modify: `backend/tests/test_presign_order.py`
|
|
|
+
|
|
|
+- [ ] **Step 1: 写失败用例**
|
|
|
+
|
|
|
+在 `TestPresignTransferStatus`(或新建 `TestWithdrawReceipt`)增加:
|
|
|
+
|
|
|
+```python
|
|
|
+def test_can_withdraw_only_jfqrz(self):
|
|
|
+ from app.services.protocol_transfer_status import can_withdraw_receipt
|
|
|
+
|
|
|
+ self.assertTrue(can_withdraw_receipt("jfqrz"))
|
|
|
+ self.assertFalse(can_withdraw_receipt("dfj"))
|
|
|
+ self.assertFalse(can_withdraw_receipt("yjf"))
|
|
|
+ self.assertFalse(can_withdraw_receipt(None))
|
|
|
+ self.assertFalse(can_withdraw_receipt(""))
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 跑测确认失败**
|
|
|
+
|
|
|
+```bash
|
|
|
+cd backend && source venv/bin/activate
|
|
|
+python -m pytest tests/test_presign_order.py::TestPresignTransferStatus::test_can_withdraw_only_jfqrz -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: `ImportError` 或 `AttributeError`(函数不存在)。
|
|
|
+
|
|
|
+- [ ] **Step 3: 最小实现**
|
|
|
+
|
|
|
+在 `protocol_transfer_status.py` 增加:
|
|
|
+
|
|
|
+```python
|
|
|
+def can_withdraw_receipt(pay_status: Optional[str]) -> bool:
|
|
|
+ """仅缴费确认中可撤回为待缴费。"""
|
|
|
+ return (pay_status or "").strip() == "jfqrz"
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: 跑测确认通过**
|
|
|
+
|
|
|
+```bash
|
|
|
+python -m pytest tests/test_presign_order.py::TestPresignTransferStatus::test_can_withdraw_only_jfqrz -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS
|
|
|
+
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add backend/app/services/protocol_transfer_status.py backend/tests/test_presign_order.py
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat: add can_withdraw_receipt for jfqrz transfer withdraw.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 2: 预签 `withdraw_receipt` CRUD + API(TDD)
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `backend/app/crud/presign_order.py`
|
|
|
+- Modify: `backend/app/api/presign_order.py`
|
|
|
+- Modify: `backend/tests/test_presign_order.py`
|
|
|
+
|
|
|
+- [ ] **Step 1: 写失败用例(纯逻辑,用 SimpleNamespace 模拟行)**
|
|
|
+
|
|
|
+```python
|
|
|
+class TestPresignWithdrawReceipt(unittest.TestCase):
|
|
|
+ def test_withdraw_sets_dfj_keeps_finance_remark(self):
|
|
|
+ from types import SimpleNamespace
|
|
|
+ from unittest.mock import MagicMock
|
|
|
+ from app.crud import presign_order as po
|
|
|
+
|
|
|
+ t = SimpleNamespace(
|
|
|
+ id="t1",
|
|
|
+ pay_status="jfqrz",
|
|
|
+ finance_remark="keep-me",
|
|
|
+ finance_confirm_attachments="http://x",
|
|
|
+ update_by=None,
|
|
|
+ update_time=None,
|
|
|
+ )
|
|
|
+ db = MagicMock()
|
|
|
+ # patch _get_transfer_or_raise
|
|
|
+ orig = po._get_transfer_or_raise
|
|
|
+ po._get_transfer_or_raise = lambda *a, **k: t
|
|
|
+ try:
|
|
|
+ out = po.withdraw_receipt(
|
|
|
+ db, transfer_id="t1", tenant_no="zkxw", user_id="u1"
|
|
|
+ )
|
|
|
+ self.assertEqual(out.pay_status, "dfj")
|
|
|
+ self.assertEqual(out.finance_remark, "keep-me")
|
|
|
+ self.assertEqual(out.finance_confirm_attachments, "http://x")
|
|
|
+ db.commit.assert_called()
|
|
|
+ finally:
|
|
|
+ po._get_transfer_or_raise = orig
|
|
|
+
|
|
|
+ def test_withdraw_rejects_non_jfqrz(self):
|
|
|
+ from types import SimpleNamespace
|
|
|
+ from unittest.mock import MagicMock
|
|
|
+ from app.crud import presign_order as po
|
|
|
+
|
|
|
+ t = SimpleNamespace(id="t1", pay_status="yjf", finance_remark="x")
|
|
|
+ db = MagicMock()
|
|
|
+ orig = po._get_transfer_or_raise
|
|
|
+ po._get_transfer_or_raise = lambda *a, **k: t
|
|
|
+ try:
|
|
|
+ with self.assertRaises(ValueError) as ctx:
|
|
|
+ po.withdraw_receipt(
|
|
|
+ db, transfer_id="t1", tenant_no="zkxw", user_id="u1"
|
|
|
+ )
|
|
|
+ self.assertIn("撤回", str(ctx.exception))
|
|
|
+ finally:
|
|
|
+ po._get_transfer_or_raise = orig
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 跑测确认失败**
|
|
|
+
|
|
|
+```bash
|
|
|
+python -m pytest tests/test_presign_order.py::TestPresignWithdrawReceipt -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: FAIL(`withdraw_receipt` 不存在)。
|
|
|
+
|
|
|
+- [ ] **Step 3: 实现 CRUD**
|
|
|
+
|
|
|
+在 `presign_order.py` 中 `submit_receipt_finance` 附近增加:
|
|
|
+
|
|
|
+```python
|
|
|
+from app.services.protocol_transfer_status import (
|
|
|
+ can_confirm_receipt,
|
|
|
+ can_submit_receipt,
|
|
|
+ can_withdraw_receipt,
|
|
|
+)
|
|
|
+
|
|
|
+def withdraw_receipt(
|
|
|
+ db: Session, *, transfer_id: str, tenant_no: str, user_id: Optional[str]
|
|
|
+) -> BizPresignTransfer:
|
|
|
+ t = _get_transfer_or_raise(db, transfer_id=transfer_id, tenant_no=tenant_no)
|
|
|
+ if not can_withdraw_receipt(t.pay_status):
|
|
|
+ raise ValueError("仅缴费确认中可撤回")
|
|
|
+ t.pay_status = "dfj"
|
|
|
+ # 故意不改 finance_remark / finance_confirm_attachments
|
|
|
+ t.update_by = user_id
|
|
|
+ t.update_time = _now()
|
|
|
+ db.add(t)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(t)
|
|
|
+ return t
|
|
|
+```
|
|
|
+
|
|
|
+API(对齐 submit 路径)在 `presign_order.py` router:
|
|
|
+
|
|
|
+```python
|
|
|
+@router.post("/presign-transfers/{transfer_id}/withdraw")
|
|
|
+async def withdraw_presign_transfer(
|
|
|
+ transfer_id: str,
|
|
|
+ user: dict = Depends(require_admin_or_super_admin),
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+):
|
|
|
+ try:
|
|
|
+ tenant_no = _get_tenant_no(db, user)
|
|
|
+ user_id = _get_current_user_id(user)
|
|
|
+ obj = po_svc.withdraw_receipt(
|
|
|
+ db, transfer_id=transfer_id, tenant_no=tenant_no, user_id=user_id
|
|
|
+ )
|
|
|
+ return success_response(
|
|
|
+ po_svc._transfer_to_dict(db, obj, tenant_no), message="已撤回"
|
|
|
+ )
|
|
|
+ except PermissionError as e:
|
|
|
+ return error_response(403, str(e))
|
|
|
+ except ValueError as e:
|
|
|
+ return error_response(400, str(e))
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: 跑测通过**
|
|
|
+
|
|
|
+```bash
|
|
|
+python -m pytest tests/test_presign_order.py::TestPresignWithdrawReceipt tests/test_presign_order.py::TestPresignTransferStatus -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS
|
|
|
+
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add backend/app/crud/presign_order.py backend/app/api/presign_order.py backend/tests/test_presign_order.py
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat: add presign transfer withdraw API for jfqrz.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 3: 正式协议转账 `withdraw_receipt` + API(TDD)
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `backend/app/crud/protocol_sample_payment.py`
|
|
|
+- Modify: `backend/app/api/protocol_sample_payment.py`
|
|
|
+- Modify: `backend/tests/test_protocol_sample_payment.py`(或新建同类用例)
|
|
|
+
|
|
|
+- [ ] **Step 1: 写失败用例**
|
|
|
+
|
|
|
+仿 Task 2,对 `protocol_sample_payment.withdraw_receipt` 写:
|
|
|
+
|
|
|
+- `jfqrz` → `dfj`,`finance_remark` 不变
|
|
|
+- `dfj` / `yjf` → `ValueError`
|
|
|
+
|
|
|
+获取转账行用该模块现有 `_get_transfer_or_raise`(若无私有 getter,则用与 `submit_receipt_finance` 相同的查找逻辑抽出或 mock)。
|
|
|
+
|
|
|
+先读 `submit_receipt_finance`(约 792 行起)确认取行方式,withdraw 与之对称。
|
|
|
+
|
|
|
+- [ ] **Step 2: 跑测确认失败**
|
|
|
+
|
|
|
+```bash
|
|
|
+python -m pytest tests/test_protocol_sample_payment.py -k withdraw -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: FAIL
|
|
|
+
|
|
|
+- [ ] **Step 3: 实现**
|
|
|
+
|
|
|
+```python
|
|
|
+def withdraw_receipt(
|
|
|
+ db: Session, *, transfer_id: str, tenant_no: str, user_id: Optional[str]
|
|
|
+) -> BizProtocolPaymentTransfer:
|
|
|
+ t = ... # 与 submit 相同取行
|
|
|
+ if not can_withdraw_receipt(t.pay_status):
|
|
|
+ raise ValueError("仅缴费确认中可撤回")
|
|
|
+ t.pay_status = "dfj"
|
|
|
+ t.update_by = user_id
|
|
|
+ t.update_time = _now()
|
|
|
+ db.add(t)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(t)
|
|
|
+ return t
|
|
|
+```
|
|
|
+
|
|
|
+API:
|
|
|
+
|
|
|
+```python
|
|
|
+@router.post("/payment-transfers/{transfer_id}/withdraw")
|
|
|
+async def withdraw_payment_transfer(...):
|
|
|
+ # 同 submit_payment_transfer_finance:取租户、调 withdraw、_protocol_accessible、返回 _transfer_to_dict
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: 跑测通过 + 确认草稿更新仍可写 remark(既有 `_apply_transfer_editable_fields` 已含 remark,无需改 schema)**
|
|
|
+
|
|
|
+```bash
|
|
|
+python -m pytest tests/test_protocol_sample_payment.py -k withdraw -q
|
|
|
+python -m pytest tests/test_presign_order.py -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS
|
|
|
+
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add backend/app/crud/protocol_sample_payment.py backend/app/api/protocol_sample_payment.py backend/tests/test_protocol_sample_payment.py
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat: add protocol payment transfer withdraw API for jfqrz.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 4: 前端 API 封装
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `web-admin/src/api/presign-order.ts`
|
|
|
+- Modify: `web-admin/src/api/protocol-sample-payment.ts`
|
|
|
+
|
|
|
+- [ ] **Step 1: 增加函数**
|
|
|
+
|
|
|
+`presign-order.ts`(紧挨 `fetchSubmitPresignTransferFinance`):
|
|
|
+
|
|
|
+```typescript
|
|
|
+export function fetchWithdrawPresignTransfer(transferId: string) {
|
|
|
+ return request({
|
|
|
+ url: `/web/admin/presign-transfers/${transferId}/withdraw`,
|
|
|
+ method: 'post'
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+`protocol-sample-payment.ts`:
|
|
|
+
|
|
|
+```typescript
|
|
|
+export function fetchWithdrawPaymentTransfer(transferId: string) {
|
|
|
+ return request({
|
|
|
+ url: `/web/admin/payment-transfers/${transferId}/withdraw`,
|
|
|
+ method: 'post'
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add web-admin/src/api/presign-order.ts web-admin/src/api/protocol-sample-payment.ts
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat(web-admin): add withdraw APIs for presign and payment transfers.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 5: 预签表单 — 备注列 + 撤回
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `web-admin/src/views/cell-storage/presign-order/PresignOrderFormDialog.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: 转账表在「转账凭证」与「状态」之间插入两列**
|
|
|
+
|
|
|
+```vue
|
|
|
+<el-table-column label="备注" min-width="160" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-if="!isTransferLocked(row) && !isConverted"
|
|
|
+ v-model="row.remark"
|
|
|
+ maxlength="512"
|
|
|
+ clearable
|
|
|
+ placeholder="选填"
|
|
|
+ />
|
|
|
+ <span v-else>{{ row.remark || '—' }}</span>
|
|
|
+ </template>
|
|
|
+</el-table-column>
|
|
|
+<el-table-column label="财务备注" min-width="140" align="center" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ row.financeRemark || '—' }}</template>
|
|
|
+</el-table-column>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: `addTransfer` 默认带 `remark: ''`;保存 create/update payload 带 `remark`**
|
|
|
+
|
|
|
+在现有保存转账逻辑里确保 `remark: tr.remark`(create/update 已支持则对齐字段名)。
|
|
|
+
|
|
|
+- [ ] **Step 3: 操作列 `jfqrz` 增加撤回**
|
|
|
+
|
|
|
+```typescript
|
|
|
+async function withdrawTransfer(row: Record<string, any>) {
|
|
|
+ if (!row.id) return
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ '确认撤回该笔意向金的财务确认申请?撤回后可修改并重新提交。',
|
|
|
+ '撤回',
|
|
|
+ { type: 'warning' }
|
|
|
+ )
|
|
|
+ await fetchWithdrawPresignTransfer(String(row.id))
|
|
|
+ row.payStatus = 'dfj'
|
|
|
+ ElMessage.success('已撤回')
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+模板:`row.payStatus === 'jfqrz' && !isConverted` 时显示「撤回」按钮(带 `RefreshLeft` 或 `Refresh` 图标)。
|
|
|
+
|
|
|
+- [ ] **Step 4: 手工/自测路径**:录入备注 → 提交 → 列表见财务待办 → 撤回 → 改备注 → 再提交。
|
|
|
+
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add web-admin/src/views/cell-storage/presign-order/PresignOrderFormDialog.vue
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat(web-admin): add presign transfer remark column and withdraw.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 6: SamplePaymentPanel — 备注列 + 撤回
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `web-admin/src/views/cell-storage/protocol/SamplePaymentPanel.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: 转账表同样插入「备注」「财务备注」列**(锁定规则用现有 `isTransferLocked` / 等价:`jfqrz`/`yjf`)。
|
|
|
+
|
|
|
+- [ ] **Step 2: `addTransfer` 初始化 `remark: ''`;`saveTransfer` payload 已有 `remark: tr.remark` 则保持,确保 UI 绑定 `row.remark`。**
|
|
|
+
|
|
|
+- [ ] **Step 3: 撤回**
|
|
|
+
|
|
|
+```typescript
|
|
|
+import { fetchWithdrawPaymentTransfer } from '@/api/protocol-sample-payment'
|
|
|
+
|
|
|
+async function withdrawTransfer(tr: Record<string, any>) {
|
|
|
+ if (!tr.id) return
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ '确认撤回该笔转账的财务确认申请?撤回后可修改并重新提交。',
|
|
|
+ '撤回',
|
|
|
+ { type: 'warning' }
|
|
|
+ )
|
|
|
+ await fetchWithdrawPaymentTransfer(String(tr.id))
|
|
|
+ tr.payStatus = 'dfj'
|
|
|
+ ElMessage.success('已撤回')
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+操作列:`payStatus === 'jfqrz'` 显示撤回。
|
|
|
+
|
|
|
+- [ ] **Step 4: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add web-admin/src/views/cell-storage/protocol/SamplePaymentPanel.vue
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat(web-admin): add sample payment transfer remark and withdraw.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 7: 协议查看 / 编辑只读表 + 财务确认 + 缴费信息
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue`(意向金关联表)
|
|
|
+- Modify: `web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue`(意向金 + `sch.transfers`)
|
|
|
+- Modify: `web-admin/src/views/cell-storage/payment-confirm/index.vue`
|
|
|
+- Modify: `web-admin/src/views/cell-storage/payment-info/PaymentExcelList.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: ProtocolFormDialog / ProtocolViewDialog**
|
|
|
+
|
|
|
+意向金子表与系列转账只读表:在状态列前增加:
|
|
|
+
|
|
|
+- `备注` → `row.remark || '—'`
|
|
|
+- `财务备注` → `row.financeRemark || '—'`(若意向金表已有财务备注列则只补「备注」)
|
|
|
+
|
|
|
+样本转账只读表同理。
|
|
|
+
|
|
|
+- [ ] **Step 2: payment-confirm 待确认表 + 已确认表**
|
|
|
+
|
|
|
+在「转账时间」与「状态/财务备注」之间增加:
|
|
|
+
|
|
|
+```vue
|
|
|
+<el-table-column
|
|
|
+ prop="remark"
|
|
|
+ label="备注"
|
|
|
+ min-width="120"
|
|
|
+ align="center"
|
|
|
+ show-overflow-tooltip
|
|
|
+>
|
|
|
+ <template #default="{ row }">{{ row.remark || '—' }}</template>
|
|
|
+</el-table-column>
|
|
|
+```
|
|
|
+
|
|
|
+确认详情若展示字段列表,同步加业务备注(财务备注已有则不动)。
|
|
|
+
|
|
|
+- [ ] **Step 3: PaymentExcelList 期次转账明细卡**
|
|
|
+
|
|
|
+在 `transfer-card__meta` 或凭证下方增加:
|
|
|
+
|
|
|
+```vue
|
|
|
+<div v-if="tr.remark" class="transfer-card__remark">备注:{{ tr.remark }}</div>
|
|
|
+<div v-if="tr.financeRemark" class="transfer-card__remark">财务备注:{{ tr.financeRemark }}</div>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue \
|
|
|
+ web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue \
|
|
|
+ web-admin/src/views/cell-storage/payment-confirm/index.vue \
|
|
|
+ web-admin/src/views/cell-storage/payment-info/PaymentExcelList.vue
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+feat(web-admin): show transfer remark and finance remark in views.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 8: 回归与图谱
|
|
|
+
|
|
|
+- [ ] **Step 1: 后端全量相关测**
|
|
|
+
|
|
|
+```bash
|
|
|
+cd backend && source venv/bin/activate
|
|
|
+python -m pytest tests/test_presign_order.py tests/test_protocol_sample_payment.py -q
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS
|
|
|
+
|
|
|
+- [ ] **Step 2: 更新图谱**
|
|
|
+
|
|
|
+```bash
|
|
|
+cd /Users/wuzilong/work/00code/zkxw && ./scripts/update-graph.sh all
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: 手工验收清单**
|
|
|
+
|
|
|
+1. 预签:填备注 → 提交 → 财务页见备注 → 撤回 → 财务备注仍在 → 改金额/备注 → 再提交 → 财务确认。
|
|
|
+2. 正式协议分期/趸交:同上。
|
|
|
+3. `yjf` 无撤回按钮;改备注控件禁用。
|
|
|
+4. 协议查看弹窗双备注可见。
|
|
|
+
|
|
|
+- [ ] **Step 4: Commit 图谱(若有 diff)**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add backend/graphify-out web-admin/graphify-out
|
|
|
+git commit -m "$(cat <<'EOF'
|
|
|
+chore: refresh graphify after transfer remark withdraw.
|
|
|
+
|
|
|
+EOF
|
|
|
+)"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## Spec coverage checklist
|
|
|
+
|
|
|
+| Spec 要求 | Task |
|
|
|
+|-----------|------|
|
|
|
+| 业务 `remark` 可写(仅 dfj) | 5, 6 |
|
|
|
+| 财务 `finance_remark` 只读展示 | 5–7 |
|
|
|
+| 列表/查看都展示双备注 | 7 |
|
|
|
+| `can_withdraw_receipt` | 1 |
|
|
|
+| 预签 withdraw API,保留 finance_remark | 2 |
|
|
|
+| 正式 withdraw API | 3 |
|
|
|
+| 前端撤回按钮 + 再提交 | 5, 6 |
|
|
|
+| yjf 不可撤回 | 1–3, 5–6 |
|
|
|
+| 无 SQL 迁移 | (全程) |
|
|
|
+| 不改催缴/样本/运费 | (不纳入 File map) |
|
|
|
+
|
|
|
+## Self-review notes
|
|
|
+
|
|
|
+- 无 TBD/占位步骤。
|
|
|
+- 路径与现网一致:`/presign-transfers/{id}/withdraw`、`/payment-transfers/{id}/withdraw`。
|
|
|
+- `can_edit_transfer` 已含 `jfqrz`,但 UI/草稿更新仍按 `dfj` 锁定;本计划以**撤回后改**为准,不放开 `jfqrz` 直接改字段。
|
|
|
+- 预签 `update_transfer_draft` 错误文案「仅已缴费状态可编辑」与实际 `dfj` 校验不一致,属既有问题,本计划不强制改文案(可选顺手改为「仅待缴费可编辑」)。
|