# 合同模块产品字段变更
## 变更说明
合同模块的产品来源从 **CRM 产品** 改为 **MDM 物料**,涉及字段名称变更。
---
## 数据库变更 SQL
```sql
-- =============================================
-- 合同产品表字段变更
-- =============================================
-- 1. 删除旧字段(如果有数据请先备份)
ALTER TABLE `crm_contract_product` DROP COLUMN `product_id`;
ALTER TABLE `crm_contract_product` DROP COLUMN `product_price`;
-- 2. 如果新字段不存在,则添加(根据实际情况判断是否需要执行)
-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_id` bigint DEFAULT NULL COMMENT 'MDM物料编号' AFTER `contract_id`;
-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_unit_id` bigint DEFAULT NULL COMMENT '计量单位编号' AFTER `item_id`;
-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_price` decimal(24,6) DEFAULT NULL COMMENT '物料原价' AFTER `item_unit_id`;
```
---
## 涉及页面
- 合同表单页面(新建/编辑合同)
- 合同详情页面
---
## API 变更
### 1. 创建/更新合同接口
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /crm/contract/create | 创建合同 |
| PUT | /crm/contract/update | 更新合同 |
### 2. 请求参数变更(products 字段)
#### 旧字段(已废弃)
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| productId | Long | 是 | CRM 产品编号 |
| productPrice | BigDecimal | 否 | 产品价格 |
| sellingPrice | BigDecimal | 否 | 销售价(用作合同价) |
#### 新字段
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| itemId | Long | 是 | MDM 物料编号 |
| itemPrice | BigDecimal | 是 | 物料原价 |
| contractPrice | BigDecimal | 是 | 合同价格 |
| count | Integer | 是 | 数量 |
### 3. 请求示例
**旧格式(不再支持):**
```json
{
"name": "合同名称",
"customerId": 1,
"products": [
{
"productId": 4,
"sellingPrice": 1111,
"count": 10
}
]
}
```
**新格式:**
```json
{
"name": "合同名称",
"customerId": 1,
"products": [
{
"itemId": 4,
"itemPrice": 1000,
"contractPrice": 1111,
"count": 10
}
]
}
```
---
## 响应变更
### 合同详情接口响应
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /crm/contract/get | 获取合同详情 |
#### 产品明细字段变更
| 旧字段 | 新字段 | 说明 |
|--------|--------|------|
| productId | itemId | MDM 物料编号 |
| productName | itemName | 物料名称 |
| productNo | itemCode | 物料编码 |
| productUnit | itemUnitName | 计量单位名称 |
| productPrice | itemPrice | 物料原价 |
| - | itemBarCode | 物料条码(新增) |
---
## 前端修改点
### 1. 物料选择组件
将 **CRM 产品选择器** 改为 **MDM 物料选择器**。
```html
```
### 2. 物料选择回调
```js
methods: {
handleItemChange(item) {
// 选择物料后自动填充价格信息
this.product.itemId = item.id
this.product.itemPrice = item.salesPrice // 物料原价
this.product.contractPrice = item.salesPrice // 合同价格(默认等于原价,可修改)
}
}
```
### 3. 表格列字段修改
```html
```
### 4. data 数据修改
```js
// 产品明细数据结构
product: {
itemId: null, // MDM 物料编号
itemPrice: null, // 物料原价
contractPrice: null, // 合同价格
count: null // 数量
}
```
---
## MDM 物料选择器 API
### 获取物料列表
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /mdm/item/page | 分页查询物料 |
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| code | String | 否 | 物料编码 |
| name | String | 否 | 物料名称 |
| barCode | String | 否 | 物料条码 |
| status | Integer | 否 | 状态(0启用 1禁用),传 0 |
**响应字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | 物料编号(即 itemId) |
| code | String | 物料编码 |
| name | String | 物料名称 |
| barCode | String | 物料条码 |
| salesPrice | BigDecimal | 销售价 |
| unitMeasureId | Long | 计量单位编号 |
| unitMeasureName | String | 计量单位名称 |
---
## 注意事项
1. **物料必须先启用**:只能选择状态为"启用"的 MDM 物料
2. **价格可修改**:`contractPrice` 默认等于物料原价,但可以手动修改
3. **过渡期**:后端暂时兼容 `productId` 和 `sellingPrice` 字段,但后续会移除兼容逻辑
4. **其他模块同步修改**:销售报价单也使用 MDM 物料,字段与合同一致
---
# 2026-09-24 增量:合同产品明细显示字段补全 + 删除合同级联清理明细
## 一、显示字段补全
### 背景
合同详情接口此前未解析产品明细的计量单位名称,条码字段名与前端列绑定不一致,导致以下位置 **单位列、物料条码列恒为空**(商机接口一直正常返回,本次对齐商机做法):
- 合同详情 →「产品」页签(`ProductDetailsList` 共用组件)
- 合同审批页 → 产品明细表
- 编辑合同弹窗 → 产品表格(已保存行次的规格型号同样为空)
### 涉及的接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /crm/contract/get | 获得合同详情(含产品明细),本次变更仅影响此接口 |
> `/crm/contract/page`、`/crm/contract/page-by-customer`、`/crm/contract/page-by-business`、导出 Excel 均**不含**产品明细,不受影响。
### 响应字段变更(data.products[])
| 字段 | 类型 | 变更 | 说明 |
|------|------|------|------|
| itemUnitName | String | 补全取值 | 计量单位名称,按 `itemUnitId` 解析(如「米」「个」),此前恒为 null |
| barCode | String | **字段更名** | 物料条码,原字段名 `itemBarCode` 改为 `barCode`,与商机接口及前端产品明细列表列绑定(field: 'barCode')一致 |
| itemSpecification | String | 新增 | 规格型号,取自 MDM 物料,与商机接口一致 |
| itemBarCode | - | 已移除 | 前端如有引用请改用 `barCode` |
**响应示例(节选):**
```json
{
"code": 0,
"data": {
"id": 19,
"no": "WTJC2025120501",
"products": [
{
"id": 42,
"itemId": 38,
"itemCode": "PHC300AB70-4.5",
"itemName": "PHC300AB70-4.5",
"barCode": null,
"itemSpecification": null,
"itemUnitId": 3,
"itemUnitName": "米",
"itemPrice": 68.000000,
"contractPrice": 68.000000,
"count": 179208.000000,
"totalPrice": 12186144.000000
}
]
}
}
```
### 字段展示规则
| 字段 | 展示位置 | 说明 |
|------|----------|------|
| itemUnitName | 合同详情产品页签、审批页产品表、编辑弹窗产品表 | 单位列,取值 MDM 计量单位名称 |
| barCode | 合同详情产品页签(物料条码列) | 与商机产品列表列绑定字段一致 |
| itemSpecification | 编辑弹窗产品表格 | 详情页产品页签无此列 |
### 业务规则说明
| 场景 | 规则 |
|------|------|
| 单位名称来源 | 由 MDM 计量单位表按产品明细的 itemUnitId 解析;导入的合同,itemUnitId 取自物料主数据的计量单位 |
| 条码取值 | 取自 MDM 物料档案的条码;物料未维护条码时为空,属正常 |
| 规格型号取值 | 取自 MDM 物料档案的规格型号;未维护时为空 |
| 编辑合同后保存 | 产品明细回传时 barCode / itemSpecification / itemUnitName 属于展示字段,后端不落库,由接口每次按 itemId / itemUnitId 实时解析,不影响保存结果 |
### 注意事项
- 本次为**响应字段名变更**(itemBarCode → barCode)。前端类型声明 `CrmContractApi.ContractProduct.itemBarCode` 建议同步改为 `barCode`(仅类型声明,运行时不读该字段,不改也不报错)。
- 前端页面代码**无需改动**:单位列、条码列、规格型号列绑定字段已与本次返回一致。
- 合同「产品」页签、审批页产品表复用同一列定义,修复后三处同时生效。
---
## 二、删除合同级联清理产品明细
### 背景
`DELETE /crm/contract/delete` 此前只删除合同主表、报价单引用与数据权限,**未删除 `crm_contract_product` 产品明细**,导致删除合同后明细成为孤儿数据(表内 deleted=0 但合同已不存在)。
### 变更后行为
| 操作 | 影响数据 |
|------|----------|
| 删除合同 | 合同主表(逻辑删除)+ 产品明细(逻辑删除)+ 报价单引用解除 + 数据权限删除 |
### 注意事项
- 级联删除为**逻辑删除**(deleted=1),与合同主表口径一致,历史数据仍可追溯。
- 合同已被回款引用时依旧禁止删除(原有校验不变)。
- 历史遗留的孤儿明细(本次修复前删除产生的)不会自动清理,如需清理请按 contract_id 核对后手工处理。