# 销售品合卷备注（merge_remark）API 变更说明

> 变更日期：2026-04-06

---

## 概述

`SalesItem`（销售品）模型新增可空 JSON 字段 `merge_remark`，用于记录该销售品由多个生产子单合卷产生时的合卷信息。

本次变更涉及：
- **创建销售品**（POST）：新增可选入参 `merge_remark`，传入时进行结构校验
- **所有返回销售品对象的接口**：响应体中新增 `merge_remark` 字段
- **更新销售品**（PATCH）：**不支持**传入 `merge_remark`，不受影响

---

## 一、受影响的 API

### 1. 创建销售品（新增入参）

```
POST /api/v1/shipment/sales-items/
```

请求体新增可选字段 `merge_remark`，不传或传 `null` 均视为无合卷信息。

#### 完整请求参数

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| printing_job_id | integer | 是 | 生产任务ID |
| name | string | 是 | 销售品名称 |
| quantity | string | 是 | 数量（支持小数） |
| unit | integer | 是 | 单位：1=米, 2=件, 3=码, 4=个 |
| customer_id | integer | 否 | 客户ID |
| remark | string | 否 | 备注 |
| position | string | 否 | 货位 |
| **merge_remark** | object \| null | **否** | **合卷备注（见下方结构说明）** |

#### merge_remark 结构

传入时必须严格符合以下结构，字段不可多、不可少：

| 字段 | 类型 | 说明 |
|------|------|------|
| merge_type | string | 合卷类型标识，固定传 `"combined_jobs"` |
| jobs | integer[] | 参与本次合卷的所有子单ID（printing_job_id）列表，至少 2 个元素 |
| main_job | integer | 主子单ID，**必须是 jobs 列表中的成员** |
| quantity | string | 本次合卷销售品的最终数量，字符串形式 |
| unit | string | 数量单位，如 `"meter"` |
| job_count | integer | 参与合卷的子单数量，**必须等于 jobs 列表的长度** |

#### 请求示例（含 merge_remark）

```json
{
  "printing_job_id": 9777,
  "name": "某款合卷布料",
  "quantity": "647",
  "unit": 1,
  "merge_remark": {
    "merge_type": "combined_jobs",
    "jobs": [9777, 9776],
    "main_job": 9777,
    "quantity": "647",
    "unit": "meter",
    "job_count": 2
  }
}
```

#### 响应示例（201 Created）

```json
{
  "id": 101,
  "name": "某款合卷布料",
  "quantity": "647.00",
  "unit": 1,
  "unit_display": "米",
  "position": "",
  "remark": "",
  "printing_job_id": 9777,
  "printing_order_id": 55,
  "external_order_id": null,
  "customer_id": 12,
  "customer_name": "某客户",
  "shipment_id": null,
  "shipment_date": null,
  "merge_remark": {
    "merge_type": "combined_jobs",
    "jobs": [9777, 9776],
    "main_job": 9777,
    "quantity": "647",
    "unit": "meter",
    "job_count": 2
  },
  "created_at": "2026-04-06T10:00:00Z",
  "created_by_id": 1,
  "created_by_name": "张三"
}
```

#### merge_remark 校验错误响应（400 Bad Request）

**缺少必填字段：**
```json
{
  "merge_remark": ["缺少必填字段: ['job_count', 'main_job']"]
}
```

**包含未知字段：**
```json
{
  "merge_remark": ["包含未知字段: ['extra_field']"]
}
```

**字段类型错误（如 main_job 传了字符串）：**
```json
{
  "merge_remark": ["字段 'main_job' 类型错误，期望 int，实际 str"]
}
```

**jobs 列表元素不是整数：**
```json
{
  "merge_remark": ["jobs 列表中的元素必须为整数"]
}
```

**jobs 少于 2 个元素：**
```json
{
  "merge_remark": ["jobs 列表至少需要包含 2 个子单ID"]
}
```

**main_job 不在 jobs 中：**
```json
{
  "merge_remark": ["main_job 必须是 jobs 列表中的一个成员"]
}
```

**job_count 与 jobs 长度不一致：**
```json
{
  "merge_remark": ["job_count (3) 与 jobs 长度 (2) 不一致"]
}
```

---

### 2. 获取销售品详情（新增返回字段）

```
GET /api/v1/shipment/sales-items/<id>/
```

响应体新增 `merge_remark` 字段。无合卷信息时值为 `null`。

---

### 3. 按客户查询销售品列表（新增返回字段）

```
GET /api/v1/shipment/sales-items/by-customer/<customer_id>/
```

列表中每条销售品对象新增 `merge_remark` 字段。

---

### 4. 按生产订单查询销售品列表（新增返回字段）

```
GET /api/v1/shipment/sales-items/by-printing-order/<printing_order_id>/
```

列表中每条销售品对象新增 `merge_remark` 字段。

---

### 5. 出货单详情中的嵌套销售品列表（新增返回字段）

```
GET /api/v1/shipment/shipments/<id>/
```

响应体 `sales_items` 数组中每条销售品对象新增 `merge_remark` 字段。

---

## 二、不受影响的 API

| 端点 | 说明 |
|------|------|
| `PATCH /api/v1/shipment/sales-items/<id>/` | 更新销售品，**不接受** `merge_remark` 参数，传入会被忽略 |
| `POST /api/v1/shipment/sales-items/<id>/rebuild/` | 重建销售品，不涉及 `merge_remark` |
| `POST /api/v1/shipment/shipments/` | 创建出货单，不涉及 `merge_remark` |

---

## 三、merge_remark 为 null 的情况

普通销售品（非合卷）创建时不传 `merge_remark`，返回值中该字段为 `null`：

```json
{
  "id": 102,
  "name": "普通布料",
  "quantity": "200.00",
  ...
  "merge_remark": null
}
```

前端展示时直接判断 `merge_remark !== null` 即可区分是否为合卷销售品。
