# SalesItem 创建 API 文档

## 概述

当自动转化销售品开关（`AUTO_CREATE_SALESITEM_FROM_PRINT_ORDER`）关闭时，通过此 API 手动创建销售品。

## API 端点

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

## 请求头

```
Authorization: Bearer <token>
Content-Type: application/json
```

## 请求参数

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| printing_job_id | integer | 是 | 生产任务ID |
| name | string | 是 | 销售品名称（最大200字符） |
| quantity | string | 是 | 数量（支持小数，如："100.50"） |
| unit | integer | 是 | 单位（见下方单位可选值） |
| customer_id | integer | 否 | 客户ID（默认从生产订单获取） |
| remark | string | 否 | 备注（最大200字符） |
| position | string | 否 | 货位（最大200字符） |

## 单位可选值

| 值 | 单位 | 说明 |
|----|------|------|
| 1 | 米 | 默认单位 |
| 2 | 件 | - |
| 3 | 码 | - |
| 4 | 个 | - |

## 请求示例

```json
{
    "printing_job_id": 123,
    "name": "产品A-2026-03",
    "quantity": "150.50",
    "unit": 1,
    "customer_id": 456,
    "remark": "加急订单",
    "position": "A1-01"
}
```

## 响应示例

### 成功响应（201 Created）

```json
{
    "id": 1,
    "name": "产品A-2026-03",
    "quantity": "150.50",
    "unit": 1,
    "unit_display": "米",
    "position": "A1-01",
    "remark": "加急订单",
    "printing_job_id": 123,
    "customer_id": 456,
    "shipment_id": null,
    "shipment_date": null,
    "created_at": "2026-03-05T10:30:00Z",
    "created_by_id": 1,
    "created_by_name": "张三"
}
```

**注意**：`shipment_id` 为 `null` 表示该销售品处于"待分配"状态，尚未关联出货单。

### 错误响应

#### 400 Bad Request - 缺少必填字段

```json
{
    "printing_job_id": ["此字段为必填项。"],
    "name": ["此字段为必填项。"],
    "quantity": ["此字段为必填项。"],
    "unit": ["此字段为必填项。"]
}
```

#### 400 Bad Request - 生产任务不存在

```json
{
    "detail": "生产任务 123 不存在或不属于当前商户"
}
```

#### 400 Bad Request - 单位值无效

```json
{
    "unit": ["单位值无效。可选值：{1: '米', 2: '件', 3: '码', 4: '个'}"]
}
```

#### 400 Bad Request - 数量格式无效

```json
{
    "detail": "数量 abc 格式无效: invalid literal for int() with base 10: 'abc'"
}
```

#### 403 Forbidden - 用户未关联商户

```json
{
    "detail": "用户未关联商户，无法创建销售品"
}
```

#### 401 Unauthorized - 未认证

```json
{
    "detail": "身份认证信息未提供。"
}
```

## 权限说明

- **认证要求**：需要登录用户（`IsAuthenticated`）
- **商户隔离**：自动从当前用户的 `employee.merchant` 获取商户信息
- **Django权限**：需要 `shipment.add_salesitem` 权限（遵循Django表级权限）
- **生产任务验证**：必须验证生产任务属于当前商户

## 业务逻辑

### 创建流程

1. **验证用户商户**：从 `request.user.employee.merchant` 获取当前商户
2. **验证生产任务**：确认生产任务存在且属于当前商户
3. **数据转换**：将数量字符串转换为 Decimal
4. **获取客户ID**：如果未提供，自动从生产订单获取
5. **创建销售品**：
   - `shipment = null`（待分配状态）
   - `printing_job_id` 关联生产任务
   - `merchant` 设置为当前商户

### 状态说明

销售品没有独立的状态字段，通过 `shipment` 字段判断：

- **待分配**：`shipment = null`
- **已关联出货单**：`shipment != null`

创建后的销售品处于"待分配"状态，可通过出货单 API 关联到出货单。

## 使用场景

### 场景1：自动转化开关关闭时

当 `AUTO_CREATE_SALESITEM_FROM_PRINT_ORDER = False` 时，系统不会自动创建销售品。此时需要通过此 API 手动创建：

```python
# 生产任务完成后，手动创建销售品
POST /api/v1/shipment/sales-items/
{
    "printing_job_id": 123,
    "name": "产品A",
    "quantity": "100",
    "unit": 1
}
```

### 场景2：补充创建销售品

即使自动转化开关开启，也可以通过此 API 手动创建额外的销售品（用于特殊情况）。

### 场景3：修正销售品

如果自动创建的销售品信息有误，可以：
1. 删除自动创建的销售品
2. 使用此 API 手动创建正确的销售品

## 相关 API

### 查询销售品

```
GET /api/v1/shipment/sales-items/by-printing-order/<printing_order_id>/?include_already_has_shipment=false
```

### 创建出货单并关联销售品

```
POST /api/v1/shipment/shipments/
{
    "customer": 1,
    "shipment_date": "2026-03-05",
    "sales_items": [1, 2, 3]
}
```

## 注意事项

1. **商户隔离**：只能为当前用户所属商户创建销售品
2. **生产任务验证**：必须提供有效的生产任务ID，且该任务必须属于当前商户
3. **数量格式**：支持小数，使用字符串传递避免精度丢失
4. **单位验证**：必须提供有效的单位值（1/2/3/4）
5. **重复创建**：同一个生产任务可以创建多个销售品（如果业务需要）

## 测试示例

### curl 命令

```bash
curl -X POST http://localhost:8000/api/v1/shipment/sales-items/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "printing_job_id": 123,
    "name": "测试产品",
    "quantity": "100.50",
    "unit": 1,
    "remark": "测试备注"
  }'
```

### Python 示例

```python
import requests

data = {
    "printing_job_id": 123,
    "name": "产品A-2026-03",
    "quantity": "150.50",
    "unit": 1,  # 米
    "customer_id": 456,
    "remark": "加急订单",
    "position": "A1-01"
}

response = requests.post(
    "http://localhost:8000/api/v1/shipment/sales-items/",
    json=data,
    headers={"Authorization": "Bearer YOUR_TOKEN"}
)

if response.status_code == 201:
    sales_item = response.json()
    print(f"创建成功：{sales_item['name']} x {sales_item['quantity']} {sales_item['unit_display']}")
else:
    print(f"错误：{response.json()}")
```

## 更新日志

- **2026-03-05**: API 首次创建，支持手动创建销售品
