forked from erp-dev/erp
264 lines
6.1 KiB
Markdown
264 lines
6.1 KiB
Markdown
# 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 首次创建,支持手动创建销售品
|