# PlateOrder API 文档

## 概述

开版订单（PlateOrder）管理接口，提供开版订单的完整 CRUD 操作、作废/恢复功能。

**基础路径**: `/api/v1/plate-orders/`

**认证方式**: 需要用户认证和相应权限

---

## 接口列表

### 1. 获取开版订单列表

**请求**
```
GET /api/v1/plate-orders/
```

**查询参数**

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| customer | integer | 否 | 客户ID（精确匹配） |
| customer_name | string | 否 | 客户名称（模糊查询） |
| customer_phone | string | 否 | 客户电话（模糊查询） |
| salesperson | integer | 否 | 业务员ID |
| merchandiser | integer | 否 | 跟单员ID |
| designer | integer | 否 | 设计师ID |
| plate_type | string | 否 | 版型（模糊查询） |
| urgency_level | string | 否 | 紧急程度（模糊查询） |
| is_invalid | boolean | 否 | 是否作废（true/false） |
| is_ordered | boolean | 否 | 是否已下单（true/false） |
| is_mark_frame | boolean | 否 | 是否打样架（true/false） |
| fabric | string | 否 | 面料（模糊查询） |
| style_name | string | 否 | 款式名称（模糊查询） |
| plate_date_from | date | 否 | 开版日期起始（YYYY-MM-DD） |
| plate_date_to | date | 否 | 开版日期结束（YYYY-MM-DD） |
| required_completion_date_from | datetime | 否 | 要求完成时间起始（ISO8601，例：2025-11-20T08:00:00Z） |
| required_completion_date_to | datetime | 否 | 要求完成时间结束（ISO8601） |
| created_date_from | date | 否 | 创建日期起始 |
| created_date_to | date | 否 | 创建日期结束 |
| search | string | 否 | 全文搜索（搜索设计编号、款式名称、客户名称、面料） |
| ordering | string | 否 | 排序字段，前加"-"表示倒序，如：`-created_at` |
| limit | integer | 否 | 每页条数 |
| offset | integer | 否 | 偏移量 |

**可排序字段**: `id`, `created_at`, `updated_at`, `plate_date`, `required_completion_date`, `completion_date`, `urgency_level`, `is_ordered`, `is_invalid`

**响应示例**
```json
{
  "count": 100,
  "next": "http://example.com/api/v1/plate-orders/?limit=20&offset=20",
  "previous": null,
  "results": [
    {
      "id": 1,
      "design_code": "DESIGN001",
      "plate_type": "圆网",
      "plate_date": "2025-11-10T10:00:00Z",
      "urgency_level": "加急",
      "is_invalid": false,
      "customer": 5,
      "customer_name": "测试客户",
      "area": "广州",
      "salesperson": 3,
      "salesperson_name": "张三",
      "merchandiser": 4,
      "merchandiser_name": "李四",
      "designer": 8,
      "designer_name": "王五",
      "style_name": "花朵印染",
      "fabric": "棉布",
      "width": "150cm",
      "required_completion_date": "2025-11-20T18:00:00Z",
      "completion_date": null,
      "is_ordered": false,
      "process": 1,
      "process_name": "开版流程",
      "status": "进行中",
      "progress_percentage": 50,
      "created_at": "2025-11-10T09:00:00Z",
      "updated_at": "2025-11-10T10:00:00Z"
    }
  ]
}
```

---

### 2. 获取开版订单详情

**请求**
```
GET /api/v1/plate-orders/{id}/
```

**路径参数**

| 参数名 | 类型 | 说明 |
|--------|------|------|
| id | integer | 开版订单ID |

**响应示例**
```json
{
  "id": 1,
  "design_code": "DESIGN001",
  "plate_type": "圆网",
  "plate_date": "2025-11-10T10:00:00Z",
  "plate_method": "手工",
  "plate_image": [
    {
      "file_id": 12,
      "name": "主图",
      "url": "https://example.com/media/uploads/2025/11/plate-a.jpg",
      "path": "uploads/2025/11/plate-a.jpg"
    },
    {
      "file_id": 13,
      "name": "细节图",
      "url": "https://example.com/media/uploads/2025/11/plate-b.jpg",
      "path": "uploads/2025/11/plate-b.jpg"
    }
  ],
  "plate_image_url": [
    "https://example.com/media/uploads/2025/11/plate-a.jpg",
    "https://example.com/media/uploads/2025/11/plate-b.jpg"
  ],
  "plate_notes": "注意色彩还原",
  "reprint_reason": null,
  "urgency_level": "加急",
  "is_invalid": false,
  "customer": 5,
  "customer_name": "测试客户",
  "customer_phone": "13900139000",
  "area": "广州",
  "default_address": "广州市天河区XX路XX号",
  "salesperson": 3,
  "salesperson_name": "张三",
  "merchandiser": 4,
  "merchandiser_name": "李四",
  "designer": 8,
  "designer_name": "王五",
  "style_name": "花朵印染",
  "fabric": "棉布",
  "width": "150cm",
  "production_method": "批量生产",
  "is_mark_frame": true,
  "drawing_rating": "优秀",
  "color_matching_rating": "良好",
  "sample_rating": "优秀",
  "difficulty_rating": "中等",
  "sample_meter": "5米",
  "required_sample_meters": 10.00,
  "required_completion_date": "2025-11-20T18:00:00Z",
  "completion_date": null,
  "approval_result": "通过",
  "is_ordered": false,
  "customer_feedback": "满意",
  "process": 1,
  "process_name": "开版流程",
  "status": "进行中",
  "status_id": 2,
  "is_completed": false,
  "has_started": true,
  "progress_percentage": 50,
  "business_object_id": 10,
  "created_at": "2025-11-10T09:00:00Z",
  "updated_at": "2025-11-10T10:00:00Z"
}
```

---

### 3. 创建开版订单

**请求**
```
POST /api/v1/plate-orders/
Content-Type: application/json
```

**请求体**
```json
{
  "customer": 5,
  "design_code": "DESIGN002",
  "plate_type": "平网",
  "style_name": "条纹印染",
  "fabric": "涤纶",
  "width": "160cm",
  "urgency_level": "正常",
  "salesperson": 3,
  "merchandiser": 4,
  "designer": 8,
  "plate_image": [
    {"file_id": 21, "name": "主图"},
    {"file_id": 22, "name": "细节图"}
  ],
  "required_completion_date": "2025-11-25T06:30:00Z",
  "is_mark_frame": false,
  "plate_method": "机器",
  "production_method": "小批量",
  "process": 1
}
```

**必填字段**
- `customer`: 客户ID

**可选字段**
- `design_code`: 设计编号
- `plate_type`: 版型
- `plate_date`: 开版日期
- `plate_method`: 开版方式
- `plate_image`: 开版图片引用列表（数组，元素包含 `file_id` 与可选 `name`，需要先通过 `/api/v1/upload/` 上传文件）
- `plate_notes`: 打版注意事项
- `reprint_reason`: 复版原因
- `urgency_level`: 紧急程度
- `area`: 区域
- `default_address`: 默认地址
- `salesperson`: 业务员ID
- `merchandiser`: 跟单员ID
- `designer`: 设计师ID
- `style_name`: 款式名称
- `fabric`: 面料
- `fabric_source`: 布料来源（可空字符串，如“客户提供”）
- `width`: 幅宽
- `production_method`: 做货方式
- `is_mark_frame`: 是否套唛架
- `drawing_rating`: 画图评级
- `color_matching_rating`: 调色评级
- `sample_rating`: 套样评级
- `difficulty_rating`: 难度评级
- `sample_meter`: 米样
- `required_sample_meters`: 所需样品米数
- `required_completion_date`: 要求完成时间（日期+具体时间，UTC 推荐）
- `completion_date`: 完成时间
- `approval_result`: 审批结果
- `is_ordered`: 是否已下单
- `customer_feedback`: 客户反馈
- `process`: 流程ID（如果不传递则使用默认流程）

**响应**
- 状态码: `201 Created`
- 响应体: 创建的开版订单详情（同详情接口）。若 `design_code` 为空，返回值会自动使用 6 位补零的主键（如 `000123`）。

**错误响应**
```json
{
  "customer": ["客户不能为空"]
}
```

---

### 4. 更新开版订单（完整更新）

**请求**
```
PUT /api/v1/plate-orders/{id}/
Content-Type: application/json
```

**请求体**: 同创建接口，需提供所有字段

**响应**
- 状态码: `200 OK`
- 响应体: 更新后的开版订单详情

---

### 5. 更新开版订单（部分更新）

**请求**
```
PATCH /api/v1/plate-orders/{id}/
Content-Type: application/json
```

**请求体示例**
```json
{
  "urgency_level": "特急",
  "is_mark_frame": true,
  "completion_date": "2025-11-15"
}
```

**说明**: 只需提供要更新的字段

**响应**
- 状态码: `200 OK`
- 响应体: 更新后的开版订单详情

---

### 6. 删除开版订单（不支持）

**请求**
```
DELETE /api/v1/plate-orders/{id}/
```

**响应**
```json
{
  "detail": "开版订单不支持删除操作，请使用作废功能"
}
```
- 状态码: `405 Method Not Allowed`

**说明**: 开版订单不支持物理删除，请使用作废功能进行软删除。

---

### 7. 作废开版订单

**请求**
```
POST /api/v1/plate-orders/{id}/invalidate/
```

**权限要求**: `printing.can_invalidate_plateorder`

**响应示例**
```json
{
  "detail": "开版订单已作废",
  "data": {
    "id": 1,
    "is_invalid": true,
    // ... 其他字段
  }
}
```

**错误响应**

1. 无权限
```json
{
  "detail": "您没有权限作废开版订单"
}
```
- 状态码: `403 Forbidden`

2. 订单已作废
```json
{
  "detail": "该开版订单已经作废"
}
```
- 状态码: `400 Bad Request`

---

### 8. 恢复开版订单

**请求**
```
POST /api/v1/plate-orders/{id}/activate/
```

**权限要求**: `printing.can_activate_plateorder`

**响应示例**
```json
{
  "detail": "开版订单已恢复",
  "data": {
    "id": 1,
    "is_invalid": false,
    // ... 其他字段
  }
}
```

**错误响应**

1. 无权限
```json
{
  "detail": "您没有权限恢复开版订单"
}
```
- 状态码: `403 Forbidden`

2. 订单未作废
```json
{
  "detail": "该开版订单未作废，无需恢复"
}
```
- 状态码: `400 Bad Request`

---

### 9. 推进到下一个状态

**请求**
```
POST /api/v1/plate-orders/{id}/advance-to-next-state/
```

**权限要求**: 需要登录和相应权限

**响应示例**
```json
{
  "detail": "已推进到下一个状态",
  "data": {
    "id": 1,
    "status": "调色",
    "status_id": 2,
    "progress_percentage": 33,
    // ... 其他字段
  }
}
```

**错误响应**

1. 没有流程实例
```json
{
  "detail": "该开版订单没有关联的流程实例"
}
```
- 状态码: `400 Bad Request`

2. 流程已完成或其他错误
```json
{
  "detail": "流程已完成，无法继续推进"
}
```
- 状态码: `400 Bad Request`

---

### 10. 回退一步

**请求**
```
POST /api/v1/plate-orders/{id}/step-back-one-state/
```

**权限要求**: 需要登录和相应权限

**响应示例**
```json
{
  "detail": "已回退到上一个状态",
  "data": {
    "id": 1,
    "status": "画图",
    "status_id": 1,
    "progress_percentage": 0,
    // ... 其他字段
  }
}
```

**错误响应**

1. 没有流程实例
```json
{
  "detail": "该开版订单没有关联的流程实例"
}
```
- 状态码: `400 Bad Request`

2. 没有可回退的状态
```json
{
  "detail": "没有可回退的状态"
}
```
- 状态码: `400 Bad Request`

---

### 11. 查询已完成的流程列表

**请求**
```
GET /api/v1/plate-orders/{id}/completed-states/
```

**查询参数**

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| include_cancelled | boolean | 否 | 是否包含已撤销的流程（true/false），默认 false |

**响应示例**
```json
{
  "count": 2,
  "results": [
    {
      "id": 1,
      "state_id": 1,
      "state_name": "画图",
      "completed_at": "2025-11-11T10:00:00Z",
      "completed_by": "user123",
      "completed_by_name": "张三",
      "is_cancelled": false,
      "cancelled_at": null
    },
    {
      "id": 2,
      "state_id": 2,
      "state_name": "调色",
      "completed_at": "2025-11-12T15:30:00Z",
      "completed_by": "user456",
      "completed_by_name": "李四",
      "is_cancelled": false,
      "cancelled_at": null
    }
  ]
}
```

---

### 12. 获取流程时间线

**请求**
```
GET /api/v1/plate-orders/{id}/timeline/
```

**说明**: 返回完整的流程时间线，包括未开始、进行中和已完成的状态，不包含已撤销的记录

**响应示例**
```json
{
  "count": 3,
  "results": [
    {
      "state_id": 1,
      "state_name": "画图",
      "state_description": "设计图纸绘制",
      "order": 0,
      "status": "completed",
      "completed_at": "2025-11-11T10:00:00Z",
      "completed_by": "user123",
      "completed_by_name": "张三"
    },
    {
      "state_id": 2,
      "state_name": "调色",
      "state_description": "颜色调配",
      "order": 1,
      "status": "in_progress",
      "completed_at": null,
      "completed_by": null,
      "completed_by_name": null
    },
    {
      "state_id": 3,
      "state_name": "套样",
      "state_description": "样品套制",
      "order": 2,
      "status": "not_started",
      "completed_at": null,
      "completed_by": null,
      "completed_by_name": null
    }
  ]
}
```

**状态说明**
- `not_started`: 未开始
- `in_progress`: 进行中
- `completed`: 已完成

---

## 数据模型

### PlateOrder（开版订单）

| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | integer | 主键ID |
| design_code | string | 设计编号 |
| plate_type | string | 版型（首版/复版等） |
| plate_date | datetime | 开版时间 |
| plate_method | string | 开版方式 |
| plate_image | array<object> | 开版图片引用列表（通过 `UploadedFile` ID 关联） |
| plate_notes | text | 打版注意事项 |
| reprint_reason | text | 复版原因 |
| urgency_level | string | 紧急程度 |
| is_invalid | boolean | 是否作废 |
| customer | FK | 客户（外键） |
| area | string | 区域 |
| default_address | string | 默认地址 |
| salesperson | FK | 业务员（外键） |
| merchandiser | FK | 跟单员（外键） |
| designer | FK | 设计师（外键） |
| process | integer | 流程ID（无外键约束） |
| style_name | string | 款式名称 |
| fabric | string | 面料 |
| width | string | 幅宽 |
| production_method | string | 做货方式 |
| is_mark_frame | boolean | 是否套唛架 |
| drawing_rating | string | 画图评级 |
| color_matching_rating | string | 调色评级 |
| sample_rating | string | 套样评级 |
| difficulty_rating | string | 难度评级 |
| sample_meter | string | 米样 |
| required_sample_meters | decimal | 所需样品米数 |
| required_completion_date | datetime | 要求完成时间（ISO8601 字符串） |
| completion_date | datetime | 完成时间 |
| approval_result | string | 审批结果 |
| is_ordered | boolean | 是否已下单 |
| customer_feedback | text | 客户反馈 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |

---

## 权限说明

### 基础权限（DjangoModelPermissions）

- `printing.view_plateorder` - 查看开版订单
- `printing.add_plateorder` - 添加开版订单
- `printing.change_plateorder` - 修改开版订单
- `printing.delete_plateorder` - 删除权限（但实际不允许删除）

### 自定义权限

- `printing.can_invalidate_plateorder` - 作废开版订单
- `printing.can_activate_plateorder` - 恢复开版订单

---

## 使用示例

### 示例1: 搜索特定客户的加急订单

```bash
curl -X GET "http://api.example.com/api/v1/plate-orders/?customer_name=测试&urgency_level=加急" \
  -H "Authorization: Token your-auth-token"
```

### 示例2: 创建开版订单

```bash
curl -X POST "http://api.example.com/api/v1/plate-orders/" \
  -H "Authorization: Token your-auth-token" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": 5,
    "design_code": "DESIGN003",
    "plate_type": "圆网",
    "style_name": "新款印染",
    "fabric": "纯棉",
    "urgency_level": "加急",
    "required_completion_date": "2025-11-20T18:00:00Z",
    "process": 1
  }'
```

### 示例3: 作废订单

```bash
curl -X POST "http://api.example.com/api/v1/plate-orders/1/invalidate/" \
  -H "Authorization: Token your-auth-token"
```

### 示例4: 按日期范围查询

```bash
curl -X GET "http://api.example.com/api/v1/plate-orders/?plate_date_from=2025-11-01&plate_date_to=2025-11-30&ordering=-plate_date" \
  -H "Authorization: Token your-auth-token"
```

### 示例5: 推进流程状态

```bash
curl -X POST "http://api.example.com/api/v1/plate-orders/1/advance-to-next-state/" \
  -H "Authorization: Token your-auth-token"
```

### 示例6: 回退流程状态

```bash
curl -X POST "http://api.example.com/api/v1/plate-orders/1/step-back-one-state/" \
  -H "Authorization: Token your-auth-token"
```

### 示例7: 获取流程时间线

```bash
curl -X GET "http://api.example.com/api/v1/plate-orders/1/timeline/" \
  -H "Authorization: Token your-auth-token"
```

---

## 错误代码

| 状态码 | 说明 |
|--------|------|
| 200 | 请求成功 |
| 201 | 创建成功 |
| 400 | 请求参数错误 |
| 403 | 权限不足 |
| 404 | 资源不存在 |
| 405 | 方法不允许 |
| 500 | 服务器内部错误 |

---

## 注意事项

1. **软删除机制**: 开版订单不支持物理删除，使用 `is_invalid` 字段进行软删除
2. **权限控制**: 作废和恢复操作需要特定权限
3. **日期格式**: 所有日期字段使用 ISO 8601 格式（YYYY-MM-DD 或 YYYY-MM-DDTHH:MM:SSZ）
4. **分页**: 列表接口默认支持分页，使用 `limit` 和 `offset` 参数控制
5. **文件引用**: 需先调用 `/api/v1/upload/` 上传文件并获取 `file_id`，再通过 `plate_image` 数组提交引用（无需 multipart）
6. **自动创建流程实例**: 创建开版订单时，如果提供了 `process` 字段，系统会自动创建对应的 BusinessObject 流程实例。如果不提供则使用默认流程
7. **流程验证**: 提供的 `process` ID 必须在系统中存在，否则会返回验证错误
8. **流程操作**: 
   - 推进/回退操作需要订单已关联 BusinessObject
   - timeline 接口不包含已撤销的流程记录
   - completed-states 接口默认不包含已撤销记录，可通过 `include_cancelled=true` 参数包含
9. **设计编号回退**: 所有响应中若 `design_code` 为空，会自动使用主键生成 6 位补零字符串（如 `000123`），确保前端始终有值可显示
10. **布料来源**: `fabric_source` 为可选字段，建议用于区分客户提供、仓库调拨等不同来源信息

---

## 更新日志

### v1.2.0 (2025-11-14)
- 添加流程控制接口：
  - `advance-to-next-state`: 推进到下一个状态
  - `step-back-one-state`: 回退一步
  - `completed-states`: 查询已完成的流程列表
  - `timeline`: 获取流程时间线

### v1.1.0 (2025-11-14)
- 添加 `process` 字段：支持指定流程ID
- 添加 `process_name` 只读字段：自动返回流程名称
- 自动创建 BusinessObject：创建订单时根据 process 自动创建流程实例
- 流程验证：确保提供的流程ID有效

### v1.0.0 (2025-11-13)
- 初始版本
- 支持完整的 CRUD 操作
- 添加作废/恢复功能
- 支持多维度过滤和搜索
