2026-07-03 3179fbb9c77eeb5260593e9df20046285dc56fe6
docs/crm_erp_integration_frontend.md
@@ -698,6 +698,10 @@
ALTER TABLE `crm_contract`
ADD COLUMN `order_id` bigint DEFAULT NULL COMMENT '关联 ERP 销售订单编号' AFTER `total_price`,
ADD COLUMN `order_no` varchar(32) DEFAULT NULL COMMENT 'ERP 销售订单单号' AFTER `order_id`;
-- 新增定金金额字段
ALTER TABLE `crm_contract`
ADD COLUMN `deposit_price` decimal(24,6) DEFAULT NULL COMMENT '定金金额,单位:元' AFTER `total_price`;
```
### 5.2 CRM 商机表
@@ -1006,3 +1010,198 @@
|------|------|
| `docs/crm_erp_integration_frontend.md` | 本文档 |
| `docs/crm_erp_integration_sql.sql` | SQL 变更脚本 |
---
## 十二、销售订单作废功能(新增)
### 12.1 功能说明
销售订单作废功能用于标记已审核的订单为"已作废"状态,实现订单全流程可追溯。
**业务规则:**
- 只有已审核的订单才能作废
- 已出库或已退货的订单不能作废
- 作废后订单状态变为"已作废"
- 作废后同步更新关联合同状态为"已作废"
- 管理员可以重新启用已作废的订单
### 12.2 状态枚举
| 状态值 | 枚举 | 说明 |
|--------|------|------|
| 10 | PROCESS | 未审核 |
| 20 | APPROVE | 已审核 |
| 30 | CANCEL | 已作废 |
### 12.3 合同状态同步
当销售订单作废/启用时,会自动同步关联合同的状态:
| 操作 | 订单状态变化 | 合同状态变化 |
|------|--------------|--------------|
| 作废订单 | 已审核 → 已作废 | 审核通过 → 已作废 |
| 启用订单 | 已作废 → 已审核 | 已作废 → 审核通过 |
**合同状态枚举:**
| 状态值 | 枚举 | 说明 |
|--------|------|------|
| 0 | DRAFT | 未提交 |
| 10 | PROCESS | 审批中 |
| 20 | APPROVE | 审核通过 |
| 30 | REJECT | 审核不通过 |
| 40 | CANCEL | 已取消 |
| 50 | INVALID | 已作废 |
### 12.4 API 接口
| 方法 | 路径 | 说明 |
|------|------|------|
| PUT | /erp/sale-order/cancel | 作废销售订单 |
| PUT | /erp/sale-order/enable | 重新启用销售订单 |
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | Long | 是 | 销售订单编号 |
**响应:**
```json
{
  "code": 0,
  "msg": "操作成功"
}
```
**错误码:**
| 错误码 | 说明 |
|--------|------|
| 1_020_201_011 | 作废失败,订单未审核 |
| 1_020_201_012 | 作废失败,已存在对应的销售出库单 |
| 1_020_201_013 | 作废失败,已存在对应的销售退货单 |
| 1_020_201_014 | 作废失败,请刷新页面后重试 |
| 1_020_201_015 | 启用失败,订单未作废 |
| 1_020_201_016 | 启用失败,请刷新页面后重试 |
### 12.5 前端修改点
**文件路径:** `/src/views/erp/sale/order/index.vue`
**操作列按钮:**
```html
<el-table-column label="操作" width="280">
  <template #default="{ row }">
    <el-button v-if="row.status === 10" type="primary" size="small" @click="handleUpdate(row)">编辑</el-button>
    <el-button v-if="row.status === 10" type="success" size="small" @click="handleApprove(row)">审核</el-button>
    <el-button v-if="row.status === 20" type="warning" size="small" @click="handleProcess(row)">反审核</el-button>
    <el-button v-if="row.status === 20" type="danger" size="small" @click="handleCancel(row)">作废</el-button>
    <el-button v-if="row.status === 30" type="success" size="small" @click="handleEnable(row)">启用</el-button>
    <el-button v-if="row.status === 10" type="danger" size="small" @click="handleDelete(row)">删除</el-button>
  </template>
</el-table-column>
```
**状态标签:**
```html
<el-table-column label="状态" prop="status" width="100">
  <template #default="{ row }">
    <el-tag v-if="row.status === 10" type="warning">未审核</el-tag>
    <el-tag v-if="row.status === 20" type="success">已审核</el-tag>
    <el-tag v-if="row.status === 30" type="info">已作废</el-tag>
  </template>
</el-table-column>
```
**关联合同列(作废时显示红色):**
```html
<el-table-column label="关联合同" prop="contractNo" width="150">
  <template #default="{ row }">
    <el-link
      v-if="row.contractId"
      :type="row.status === 30 ? 'danger' : 'primary'"
      @click="handleViewContract(row)"
    >
      {{ row.contractNo }}
    </el-link>
    <span v-else>-</span>
  </template>
</el-table-column>
```
**JS 方法:**
```js
import { cancelSaleOrder, enableSaleOrder } from '@/api/erp/saleOrder'
methods: {
  async handleCancel(row) {
    try {
      await this.$confirm('确认作废该销售订单?作废后关联合同也将标记为已作废!', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
      await cancelSaleOrder(row.id)
      this.$message.success('作废成功')
      this.getList()
    } catch (e) {
      if (e !== 'cancel') {
        this.$message.error(e.message || '作废失败')
      }
    }
  },
  async handleEnable(row) {
    try {
      await this.$confirm('确认重新启用该销售订单?启用后关联合同将恢复为审核通过状态!', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'info'
      })
      await enableSaleOrder(row.id)
      this.$message.success('启用成功')
      this.getList()
    } catch (e) {
      if (e !== 'cancel') {
        this.$message.error(e.message || '启用失败')
      }
    }
  }
}
```
**API 定义文件:** `/src/api/erp/saleOrder.js`
```js
// 作废销售订单
export function cancelSaleOrder(id) {
  return request({
    url: '/erp/sale-order/cancel',
    method: 'put',
    params: { id }
  })
}
// 重新启用销售订单
export function enableSaleOrder(id) {
  return request({
    url: '/erp/sale-order/enable',
    method: 'put',
    params: { id }
  })
}
```
### 12.6 注意事项
1. 作废操作会同步更新关联合同状态为"已作废"
2. 启用操作会同步更新关联合同状态为"审核通过"
3. 已出库/已退货的订单不能作废,需先处理相关业务单据
4. 作废后订单号显示为红色,提醒用户该订单已失效
5. 启用功能需要管理员权限(`erp:sale-order:enable`)