1
0
forked from erp-dev/erp

feat: allocation record for pre sales order

This commit is contained in:
2026-02-01 22:57:16 +08:00
parent a633a617d5
commit ac065e115e
14 changed files with 1955 additions and 44 deletions

View File

@@ -0,0 +1,102 @@
# 预销售单配货记录方案(需求背景与决策)
生成日期2026-02-02
## 一、需求背景
业务场景为“预销售单配货记录”。预销售单已存在且包含多个明细PreSalesOrderItem。配货行为需要被记录且允许同一明细多次配货拆分执行、分批扫码。配货记录的核心价值是对现实行为的如实记录与进度汇总而非强约束生产流程。
## 二、已确认的关键决策
### 1. 数据关系
- PreSalesOrder 1 — N PreSalesOrderItem
- PreSalesOrderItem 1 — N AllocationRecord配货记录
- 外键在 AllocationRecord 侧
### 2. 模型命名
- 配货记录模型英文名AllocationRecord
### 3. 配货记录字段设计(核心)
- pre_sales_order_itemFK
- stock_ids库存明细 ID 数组)
- 这些 ID 对应 stock.StockChangeDetail
- 由于系统已有一致做法consume_detail_ids 为字符串存储),建议使用字符串持久化,展示层转换为 List[int]
- quantitydecimal执行数量
- unitstring
- statusint1=有效2=撤销)
- scanned_byEmployee
- scanned_atdatetime
- remarks可选
### 4. 派生字段(必须是计算字段,不落库)
- allocated_quantity = Σ AllocationRecord.quantity仅统计 status=有效)
- required_quantity = PreSalesOrderItem.quantity
- progress = allocated_quantity / required_quantity
- allocation_status计算状态
- pendingallocated_quantity == 0
- allocating0 < allocated_quantity < required_quantity
- completedallocated_quantity >= required_quantity
### 5. 业务规则
- 允许超配:配货记录是事实记录,不限制现实生产过程。
- 允许撤销:撤销后不计入配货进度。
- stock_ids 的合法性与库存状态校验必须在 service 层完成:
- 必须存在
- 未冻结/锁定/消费
- 不允许将校验逻辑耦合在 model 层
- 必须通过独立函数实现,避免重复代码
### 6. 排除项(当前阶段不做)
- 任务指派与接收功能不做(前端想法不成熟)
- 任务主表TaskAllocation暂不落地
- 转销售单属于下一阶段任务
- 价格由前端在转销售单时传入,目前不考虑
## 三、与前端需求的匹配情况
当前方案满足前端“扫码录入、按明细多次执行、进度汇总”的核心需求。
暂不覆盖“任务指派、接收、完成、转单”流程,此部分已明确不在本阶段实施。
## 四、实施计划(仅规划,不含代码)
1. 新增 AllocationRecord 模型
- 外键到 PreSalesOrderItem
- stock_ids 使用字符串持久化(与 consume_detail_ids 一致)
2. 在 service 层新增库存明细校验函数
- 校验 stock_ids 存在且未冻结/锁定/消费
- 作为通用工具复用,避免重复逻辑
3. 新增配货记录创建与撤销流程service
- 允许超配
- 撤销不计入进度
4. 在 API/Serializer 层增加派生字段输出
- allocated_quantity、progress、allocation_status
5. 补充相关测试
- stock_ids 校验
- 进度计算与状态派生
- 撤销与超配逻辑
## 五、实施逻辑细节service 层校验)
### 1. 校验库存明细存在
- 传入 stock_ids数组先做整数化与正整数校验
- 使用 `StockChangeDetail``id__in` + `merchant` 拉取
- 若有缺失 ID直接报错并终止
### 2. 校验“未被消费”
- 依据 stock 模块字段 `StockChangeDetail.is_consumed`
- 若任一明细 `is_consumed=True`,视为已消费,拒绝写入配货记录
### 3. 校验“未冻结/未锁定”
- 查询 `StockFreeze`,条件:
- `stock_detail_id__in = stock_ids`
- `status = StockFreezeStatusEnum.FROZEN`
- 若存在冻结记录,拒绝写入配货记录
### 4. 约束校验位置
- 上述校验必须在 **service 层独立函数** 中完成
- 不写入 model 层,不引入重复代码
## 六、待后续阶段处理
- 转销售单流程(确认并转单)
- 任务指派/接收/完成等任务主表流程

View File

@@ -0,0 +1,489 @@
# 任务配货功能 API 需求文档
> **业务背景:** 布行预销售单场景客户预定布料如5000米需要仓库人员配货、扫码录入每条布的米数确认后转为正式销售单。
>
> **前端项目:** app-ui移动端 Web 应用)
>
> **生成日期:** 2026-02-01
---
## 一、业务流程概述
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 创建预销售单 │ -> │ 生成任务配货 │ -> │ 仓库扫码录入 │ -> │ 确认转销售单 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
▼ ▼ ▼ ▼
客户预定 任务分配给 扫码录入每条 客户确认数量
5000米布料 仓库配货人员 布料的实际米数 转为正式销售单
```
### 角色说明
| 角色 | 操作 |
|------|------|
| 销售人员 | 创建预销售单,确认配货结果,转销售单 |
| 仓库人员 | 接收任务配货,扫码录入每条布的米数 |
| 客户 | 确认最终数量(可选,由销售人员代为确认) |
### 状态流转
```
预销售单状态:
待配货(pending) → 配货中(allocating) → 待确认(confirming) → 已完成(completed) / 已取消(cancelled)
任务配货状态:
待接收(pending) → 进行中(in_progress) → 已完成(completed) / 已取消(cancelled)
```
---
## 二、需要新增的 API
### 2.1 预销售单 API移动端
> antd-demo 已有预销售单 API需确认 app-ui 是否复用同一套接口
**复用现有接口:**
- `POST /api/v1/pre-sales-orders/` - 创建预销售单
- `GET /api/v1/pre-sales-orders/` - 预销售单列表
- `GET /api/v1/pre-sales-orders/{id}/` - 预销售单详情
**需要新增/调整:**
- 创建预销售单时**自动创建任务配货**(后端处理,或前端调用两个接口)
- 预销售单需要新增 `status` 字段表示配货状态
---
### 2.2 任务配货 API新增
#### 2.2.1 创建任务配货
> 可选择在创建预销售单时由后端自动创建,或由前端单独调用
- **路径:** `POST /api/v1/task-allocations/`
- **权限:** 需登录,需员工权限
**请求参数:**
```typescript
{
pre_sales_order_id: number; // 必填,关联的预销售单 ID
assignee_id?: number; // 可选,指派给哪个仓库员工(不填则待分配)
priority?: number; // 可选优先级1=普通2=加急默认1
expected_date?: string; // 可选期望完成日期ISO 8601
remarks?: string; // 可选,备注
}
```
**响应数据:**
```typescript
{
code: 200,
message: "success",
data: {
id: number; // 任务配货 ID
task_no: string; // 任务编号(如 PH20260201000001
pre_sales_order_id: number; // 关联预销售单 ID
pre_sales_order_no: string; // 预销售单编号(只读)
customer_id: number; // 客户 ID从预销售单继承
customer_name: string; // 客户名称(只读)
warehouse_id: number; // 仓库 ID从预销售单继承
warehouse_name: string; // 仓库名称(只读)
assignee_id: number | null; // 被指派的员工 ID
assignee_name: string | null; // 被指派的员工名称(只读)
status: number; // 状态1=待接收2=进行中3=已完成4=已取消
priority: number; // 优先级
expected_date: string | null; // 期望完成日期
remarks: string | null; // 备注
created_by: number; // 创建者 ID
created_by_name: string; // 创建者名称(只读)
created_at: string; // 创建时间
// 配货需求(从预销售单明细复制)
items: TaskAllocationItem[];
}
}
```
**任务配货明细 `TaskAllocationItem`**
```typescript
{
id: number; // 明细 ID
product_id: number; // 产品 ID
product_name: string; // 产品名称
spec: string | null; // 规格
color: string | null; // 颜色
required_quantity: string; // 需求数量(如 "5000.00"
allocated_quantity: string; // 已配货数量(如 "4800.00"
unit: string; // 单位
status: number; // 明细状态1=待配货2=配货中3=已完成
// 扫码录入的布条记录
rolls: TaskAllocationRoll[];
}
```
**布条记录 `TaskAllocationRoll`**
```typescript
{
id: number; // 记录 ID
roll_code: string; // 布条码/二维码内容
quantity: string; // 该条的米数(如 "50.00"
scanned_by: number; // 扫码人员 ID
scanned_by_name: string; // 扫码人员名称(只读)
scanned_at: string; // 扫码时间
}
```
---
#### 2.2.2 任务配货列表
- **路径:** `GET /api/v1/task-allocations/`
- **权限:** 需登录,需员工权限
**查询参数:**
```typescript
{
limit?: number; // 分页默认20最大100
offset?: number; // 偏移量默认0
status?: number; // 按状态筛选1/2/3/4
assignee_id?: number; // 按被指派人筛选(仓库人员查自己的任务)
warehouse_id?: number; // 按仓库筛选
priority?: number; // 按优先级筛选
date_from?: string; // 创建时间起始ISO 8601
date_to?: string; // 创建时间结束
}
```
**响应数据:**
```typescript
{
count: number;
next: string | null;
previous: string | null;
results: TaskAllocation[]; // 任务配货列表(不含 rolls 明细)
}
```
---
#### 2.2.3 任务配货详情
- **路径:** `GET /api/v1/task-allocations/{id}/`
- **权限:** 需登录
**响应数据:**
```typescript
{
code: 200,
data: TaskAllocation // 完整数据,包含 items 和 rolls
}
```
---
#### 2.2.4 接收任务
> 仓库人员接收任务,状态从"待接收"变为"进行中"
- **路径:** `POST /api/v1/task-allocations/{id}/accept/`
- **权限:** 需登录,需仓库员工权限
**请求参数:** 无(后端自动设置当前用户为 assignee
**响应数据:**
```typescript
{
code: 200,
message: "任务已接收",
data: TaskAllocation
}
```
---
#### 2.2.5 扫码录入布条 ⭐
> 仓库人员扫码录入每条布的米数
- **路径:** `POST /api/v1/task-allocations/{id}/scan/`
- **权限:** 需登录,需仓库员工权限
**请求参数:**
```typescript
{
item_id: number; // 哪个产品明细
roll_code: string; // 布条码/二维码内容
quantity: string | number; // 该条的米数
}
```
**响应数据:**
```typescript
{
code: 200,
message: "录入成功",
data: {
roll: TaskAllocationRoll; // 新增的布条记录
item: TaskAllocationItem; // 更新后的明细(含最新 allocated_quantity
task: {
id: number;
total_allocated: string; // 总已配货数量
total_required: string; // 总需求数量
progress: number; // 进度百分比0-100
}
}
}
```
**错误响应:**
```typescript
// 布条码重复
{
code: 400,
message: "该布条已录入",
data: {
existing_roll: TaskAllocationRoll // 已存在的记录
}
}
// 超出需求数量
{
code: 400,
message: "配货数量已超出需求,是否继续?",
data: {
required: "5000.00",
allocated: "5050.00",
overflow: "50.00"
}
}
```
---
#### 2.2.6 删除布条记录
> 录错了,删除某条扫码记录
- **路径:** `DELETE /api/v1/task-allocations/{id}/rolls/{roll_id}/`
- **权限:** 需登录,需仓库员工权限
**响应数据:**
```typescript
{
code: 200,
message: "删除成功",
data: {
item: TaskAllocationItem; // 更新后的明细
}
}
```
---
#### 2.2.7 完成配货
> 仓库人员完成配货,状态变为"已完成"
- **路径:** `POST /api/v1/task-allocations/{id}/complete/`
- **权限:** 需登录,需仓库员工权限
**请求参数:**
```typescript
{
remarks?: string; // 可选,完成备注
}
```
**响应数据:**
```typescript
{
code: 200,
message: "配货完成",
data: TaskAllocation
}
```
**校验规则:**
- 所有明细的 `allocated_quantity` 必须 > 0
- 如果某明细未配货,返回 400 错误
---
#### 2.2.8 确认配货结果并转销售单 ⭐
> 销售人员/客户确认配货数量,将预销售单转为正式销售单
- **路径:** `POST /api/v1/task-allocations/{id}/confirm-and-convert/`
- **权限:** 需登录,需销售员工权限
**请求参数:**
```typescript
{
confirmed_items: Array<{
item_id: number; // 明细 ID
confirmed_quantity: string; // 确认的数量(可能与实际配货数量不同)
}>;
remarks?: string; // 可选,确认备注
}
```
**响应数据:**
```typescript
{
code: 200,
message: "已转为销售单",
data: {
task_allocation: TaskAllocation; // 更新后的任务配货(状态变更)
sales_order: {
id: number;
human_id: string; // 销售单编号(如 XS20260201000001
// ... 其他销售单字段
}
}
}
```
---
### 2.3 预销售单状态扩展
现有预销售单 API 需要新增以下字段:
```typescript
// 响应中新增
{
// ... 原有字段 ...
allocation_status: number; // 配货状态1=待配货2=配货中3=待确认4=已完成
allocation_status_display: string; // 配货状态文字(只读)
task_allocation_id: number | null; // 关联的任务配货 ID
converted_sales_order_id: number | null; // 转换后的销售单 ID
}
```
---
## 三、状态枚举值
### 3.1 任务配货状态
| 值 | 名称 | 说明 |
|----|------|------|
| 1 | pending | 待接收 |
| 2 | in_progress | 进行中 |
| 3 | completed | 已完成 |
| 4 | cancelled | 已取消 |
### 3.2 任务配货明细状态
| 值 | 名称 | 说明 |
|----|------|------|
| 1 | pending | 待配货 |
| 2 | allocating | 配货中 |
| 3 | completed | 已完成 |
### 3.3 预销售单配货状态
| 值 | 名称 | 说明 |
|----|------|------|
| 1 | pending | 待配货 |
| 2 | allocating | 配货中 |
| 3 | confirming | 待确认 |
| 4 | completed | 已完成 |
### 3.4 优先级
| 值 | 名称 | 说明 |
|----|------|------|
| 1 | normal | 普通 |
| 2 | urgent | 加急 |
---
## 四、API 路径汇总
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/task-allocations/` | 创建任务配货 |
| GET | `/api/v1/task-allocations/` | 任务配货列表 |
| GET | `/api/v1/task-allocations/{id}/` | 任务配货详情 |
| POST | `/api/v1/task-allocations/{id}/accept/` | 接收任务 |
| POST | `/api/v1/task-allocations/{id}/scan/` | 扫码录入布条 |
| DELETE | `/api/v1/task-allocations/{id}/rolls/{roll_id}/` | 删除布条记录 |
| POST | `/api/v1/task-allocations/{id}/complete/` | 完成配货 |
| POST | `/api/v1/task-allocations/{id}/confirm-and-convert/` | 确认并转销售单 |
---
## 五、前端页面规划
### 5.1 页面路由
| 路由 | 页面 | 角色 |
|------|------|------|
| `/workstation/sales/pre-order` | 预销售单列表 | 销售 |
| `/workstation/sales/pre-order/create` | 创建预销售单 | 销售 |
| `/workstation/sales/pre-order/:id` | 预销售单详情 | 销售 |
| `/workstation/warehouse/task-allocation` | 任务配货列表 | 仓库 |
| `/workstation/warehouse/task-allocation/:id` | 任务配货详情/扫码录入 | 仓库 |
| `/workstation/warehouse/task-allocation/:id/scan` | 扫码录入页面 | 仓库 |
| `/workstation/sales/pre-order/:id/confirm` | 确认配货结果 | 销售 |
### 5.2 页面功能
**仓库人员 - 任务配货扫码页面:**
```
┌─────────────────────────────────┐
│ 任务配货 PH20260201000001 │
│ 客户XXX布业 仓库:主仓库 │
├─────────────────────────────────┤
│ 产品:涤纶面料-红色 │
│ 需求5000.00 米 │
│ 已配4800.00 米 [96%] │
│ ████████████████████░░ │
├─────────────────────────────────┤
│ ┌───────────────────────────┐ │
│ │ [扫码区域/摄像头] │ │
│ │ │ │
│ └───────────────────────────┘ │
│ │
│ 或手动输入: │
│ 布条码:[____________] │
│ 米 数:[____________] │
│ [录入] │
├─────────────────────────────────┤
│ 已录入布条3条
│ ┌───────────────────────────┐ │
│ │ R001 50.00米 张三 10:30 │ │
│ │ R002 48.50米 张三 10:32 │ │
│ │ R003 51.50米 张三 10:35 │ │
│ └───────────────────────────┘ │
├─────────────────────────────────┤
│ [完成配货] │
└─────────────────────────────────┘
```
---
## 六、确认事项
请后端同事确认:
1. [ ] 任务配货是否在创建预销售单时自动创建?还是需要手动创建?
2. [ ] 布条码的格式规范是什么?是否需要校验格式?
3. [ ] 一条布可能属于多个产品吗?还是一对一关系?
4. [ ] 配货超出需求数量时,是警告还是阻止?
5. [ ] 转销售单时,价格如何处理?(预销售单是否有价格字段?)
6. [ ] 是否需要支持"部分配货"后转销售单?
7. [ ] 任务配货是否需要支持"退回"操作(从进行中退回到待接收)?
8. [ ] 预销售单和任务配货是否一对一关系?还是一对多?
---
**前端负责人:** [待填写]
**后端负责人:** [待填写]
**评审日期:** [待填写]

View File

@@ -0,0 +1,273 @@
# API v1预销售单 / 预采购单(前端对接)
本文档面向前端描述预销售单PreSalesOrder与预采购单PrePurchaseOrder的 API、字段与参数。
- API 前缀:`/api/v1/`
- 认证:接口均要求登录(`IsAuthenticated`)。
- 权限:要求当前用户绑定员工(`request.user.employee`),否则返回 `403`
## 分页Limit/Offset
预销售单列表、预采购单列表均使用 DRF `LimitOffsetPagination`
- `limit`:返回条数,默认 `20`,最大 `100`
- `offset`:偏移量,默认 `0`
分页响应结构:
```json
{
"count": 123,
"next": "http://.../api/v1/pre-sales-orders/?limit=20&offset=20",
"previous": null,
"results": [
{ "id": 1, "human_id": "YS...", "items": [] }
]
}
```
## 列表查询参数(已支持)
预销售单列表 `GET /pre-sales-orders/`
- `limit` / `offset`
- `customer` / `customer_id`int
- `warehouse` / `warehouse_id`int
- `kind`int
- `human_id__icontains`string
- `created_at_from`datetime 或 date
- `created_at_to`datetime 或 date
预采购单列表 `GET /pre-purchase-orders/`
- `limit` / `offset`
- `supplier` / `supplier_id`int
- `warehouse` / `warehouse_id`int
- `kind`int
- `human_id__icontains`string
- `created_at_from`datetime 或 date
- `created_at_to`datetime 或 date
---
# 预销售单 PreSalesOrder
## 1) 列表
- `GET /api/v1/pre-sales-orders/?limit=20&offset=0`
- 查询参数:
- `limit`可选int默认 20最大 100
- `offset`可选int默认 0
- 响应:`200 OK`,分页结构,`results` 为预销售单数组
## 2) 创建
- `POST /api/v1/pre-sales-orders/`
请求体参数JSON
- `customer` / `customer_id`必填int客户 ID
- `warehouse` / `warehouse_id`必填int仓库 ID
- `kind`可选int预销售单类型
- `1`:大货
- `2`:样板
- 默认 `1`
- `remarks`可选string
- `items`必填array非空明细列表
`items[]` 字段:
- `product_id` / `product`必填int产品 ID
- `quantity`必填string 或 number数量会被转换为 Decimal
- `unit`可选string单位为空时会尝试使用产品默认单位
- `product_name`可选string产品名称弱关联承载
- `color`可选string
- `spec`可选string
- `quantity_of_rolls`可选string各条数数量原样保存
- `num_of_rolls`可选int条数必须 > 0默认 `1`
- `order_quantity`可选int下单数量必须为非负整数
- `remarks`可选string明细备注
响应:`201 CREATED`,返回预销售单详情(含 `items`)。
示例请求:
```json
{
"customer": 1,
"warehouse": 2,
"kind": 1,
"remarks": "预销售单备注",
"items": [
{
"product_id": 10,
"quantity": "12.5",
"unit": "米",
"order_quantity": 7,
"remarks": "明细备注"
}
]
}
```
## 3) 详情
- `GET /api/v1/pre-sales-orders/{id}/`
- 响应:`200 OK`,返回预销售单详情
## 4) 更新(全量/部分)
- `PUT /api/v1/pre-sales-orders/{id}/`
- `PATCH /api/v1/pre-sales-orders/{id}/`
请求体参数:
- `customer` / `customer_id`可选int
- `warehouse` / `warehouse_id`可选int
- `kind`可选int
- `remarks`可选string
- `items`必填array非空更新时必须提供否则会返回 `400`
响应:`200 OK`,返回更新后的详情。
## 5) 删除
- `DELETE /api/v1/pre-sales-orders/{id}/`
- 响应:`204 NO CONTENT`
## 预销售单字段说明(响应)
预销售单对象字段(`results[]` 与详情一致):
- `id`int主键
- `human_id`string人类可读编号`YSYYYYMMDD000001`
- `merchant`int所属商户 ID
- `merchant`int商户 ID调试字段
- `customer`int客户 ID
- `customer_name`string只读
- `warehouse`int仓库 ID
- `warehouse_name`string只读
- `operator`int|null经办人员工ID创建时由后端从登录用户推导
- `operator_name`string只读
- `kind`int类型1/2
- `remarks`string|null
- `created_at`datetime string
- `items`array明细只读
`items[]` 字段(响应):
- `id`int
- `product_id`int|null
- `product_name`string
- `color`string|null
- `quantity`string|null通常为两位小数`"20.00"`
- `unit`string
- `spec`string|null
- `remarks`string|null
---
# 预采购单 PrePurchaseOrder
## 1) 列表
- `GET /api/v1/pre-purchase-orders/?limit=20&offset=0`
- 查询参数:
- `limit`可选int默认 20最大 100
- `offset`可选int默认 0
- 响应:`200 OK`,分页结构
## 2) 创建
- `POST /api/v1/pre-purchase-orders/`
请求体参数JSON
- `supplier` / `supplier_id`必填int供应商 ID
- `warehouse` / `warehouse_id`必填int仓库 ID
- `kind`可选int预采购单类型
- `1`:大货
- `2`:样板
- 默认 `1`
- `remarks`可选string
- `items`必填array非空明细列表字段同预销售明细
响应:`201 CREATED`,返回预采购单详情(含 `items`)。
示例请求:
```json
{
"supplier": 1,
"warehouse": 2,
"kind": 1,
"remarks": "预采购单备注",
"items": [
{
"product_id": 10,
"quantity": "12.5",
"unit": "米",
"order_quantity": 7,
"remarks": "明细备注"
}
]
}
```
## 3) 详情
- `GET /api/v1/pre-purchase-orders/{id}/`
- 响应:`200 OK`
## 4) 更新(全量/部分)
- `PUT /api/v1/pre-purchase-orders/{id}/`
- `PATCH /api/v1/pre-purchase-orders/{id}/`
请求体参数:
- `supplier` / `supplier_id`可选int
- `warehouse` / `warehouse_id`可选int
- `kind`可选int
- `remarks`可选string
- `items`必填array非空更新时必须提供否则会返回 `400`
响应:`200 OK`
## 5) 删除
- `DELETE /api/v1/pre-purchase-orders/{id}/`
- 响应:`204 NO CONTENT`
## 预采购单字段说明(响应)
预采购单对象字段(`results[]` 与详情一致):
- `id`int
- `human_id`string人类可读编号`YCYYYYMMDD000001`
- `merchant`int
- `merchant`int商户 ID调试字段
- `supplier`int
- `supplier_name`string只读
- `warehouse`int
- `warehouse_name`string只读
- `operator`int|null
- `operator_name`string只读
- `kind`int1/2
- `remarks`string|null
- `created_at`
- `items`array只读
`items[]` 字段同预销售明细响应。
---
# 错误响应约定(两类接口通用)
- `400 BAD REQUEST`:参数校验失败
- 示例:`{"error": "items 需要为非空数组"}`
- `401 UNAUTHORIZED`:未登录
- `403 FORBIDDEN`:无员工信息/无权限
- 示例:`{"error": "无权限访问"}`
- `404 NOT FOUND`:订单不存在
- 示例:`{"error": "预销售单不存在"}` / `{"error": "预采购单不存在"}`

View File

@@ -0,0 +1,164 @@
# 预销售单/预采购单 API 字段分析报告
> **文档目的:** 对比 API 响应字段与前端实际使用情况,优化接口返回数据
>
> **生成日期:** 2026-02-01
---
## 一、问题概述
- **业务场景:** 预销售单/预采购单的列表、详情展示
- **涉及 API**
- `GET /api/v1/pre-sales-orders/`
- `GET /api/v1/pre-sales-orders/{id}/`
- `GET /api/v1/pre-purchase-orders/`
- `GET /api/v1/pre-purchase-orders/{id}/`
- **问题类型:** API 返回了前端未使用的冗余字段
---
## 二、单据主体字段分析
### 2.1 API 返回但前端未使用的字段(建议移除或设为可选)
| 字段 | 类型 | 说明 | 前端使用情况 |
|------|------|------|-------------|
| `merchant` | int | 商户 ID | ❌ 未使用,前端用户已在自己商户下 |
| `merchant_name` | string | 商户名称 | ❌ 未使用 |
| `created_by` | int\|null | 创建者用户 ID | ❌ 未使用,只用 username |
| `operator` | int\|null | 经办人 ID | ❌ 未使用,只用 name |
| `updated_at` | datetime | 更新时间 | ❌ 未使用 |
### 2.2 前端实际使用的字段
| 字段 | 使用场景 |
|------|----------|
| `id` | 主键,编辑/删除操作 |
| `human_id` | 列表显示、详情标题 |
| `customer` / `supplier` | 表单回显(编辑时) |
| `customer_name` / `supplier_name` | 列表列、详情显示 |
| `warehouse` | 表单回显(编辑时) |
| `warehouse_name` | 列表列、详情显示 |
| `created_by_username` | 详情抽屉显示 |
| `operator_name` | 列表列、详情显示 |
| `kind` | 列表列(标签)、详情显示 |
| `remarks` | 列表列、详情显示 |
| `created_at` | 列表列(格式化显示) |
| `items` | 详情明细表格 |
---
## 三、明细 items 字段分析
### 3.1 API 返回但前端未使用的字段
| 字段 | 类型 | 说明 | 前端使用情况 |
|------|------|------|-------------|
| `quantity_of_rolls` | string\|null | 各条数数量 | ❌ 页面未显示 |
| `num_of_rolls` | int | 条数 | ❌ 页面未显示 |
| `order_quantity` | int\|null | 下单数量 | ❌ 页面未显示 |
| `created_at` | datetime | 明细创建时间 | ❌ 页面未显示 |
| `updated_at` | datetime | 明细更新时间 | ❌ 页面未显示 |
### 3.2 前端实际使用的字段
| 字段 | 使用场景 |
|------|----------|
| `id` | 表格 row key |
| `product_id` | 编辑时回显 |
| `product_name` | 明细表格列 |
| `spec` | 明细表格列 |
| `color` | 明细表格列 |
| `quantity` | 明细表格列 |
| `unit` | 明细表格列 |
| `remarks` | 明细表格列 |
---
## 四、建议方案
### 方案 A精简默认响应推荐
移除以下字段的默认返回,减少数据传输量:
**单据主体移除:**
```diff
{
"id": 1,
"human_id": "YS20260201000001",
- "merchant": 1,
- "merchant_name": "XX商户",
"customer": 123,
"customer_name": "客户A",
"warehouse": 1,
"warehouse_name": "主仓库",
- "created_by": 10,
"created_by_username": "admin",
- "operator": 5,
"operator_name": "张三",
"kind": 1,
"remarks": "备注",
"created_at": "2026-02-01T10:00:00Z",
- "updated_at": "2026-02-01T10:00:00Z",
"items": [...]
}
```
**明细 items 移除:**
```diff
{
"id": 1,
"product_id": 100,
"product_name": "产品A",
"color": "红色",
"quantity": "20.00",
"unit": "米",
"spec": "规格1",
- "quantity_of_rolls": null,
- "num_of_rolls": 1,
- "order_quantity": null,
"remarks": "明细备注"
- "created_at": "2026-02-01T10:00:00Z",
- "updated_at": "2026-02-01T10:00:00Z"
}
```
### 方案 B支持 fields 参数按需返回
如果其他客户端可能需要这些字段,可以支持 `fields` 查询参数:
```
GET /api/v1/pre-sales-orders/?fields=id,human_id,customer_name,items
```
---
## 五、预估收益
假设每条单据平均 5 个明细项:
| 优化项 | 移除字段数 | 预估节省字节 |
|--------|-----------|-------------|
| 单据主体 | 5 个字段 | ~150 bytes/单据 |
| 明细项 | 5 个字段 × 5 项 | ~250 bytes/单据 |
| **总计** | - | ~400 bytes/单据 |
列表页默认 20 条:**节省约 8KB/请求**
---
## 六、确认事项
请后端同事确认:
1. [ ] 上述"未使用字段"是否可以从默认响应中移除?
2. [ ] 是否有其他客户端(如小程序、管理后台)依赖这些字段?
3. [ ] 如果有其他依赖,是否采用方案 Bfields 参数)?
4. [ ] `quantity_of_rolls``num_of_rolls``order_quantity` 这三个明细字段是否有业务场景需要?如果暂时不用,前端类型定义中会保留但标记为可选。
---
**前端负责人:** [待填写]
**后端负责人:** [待填写]
**预计完成:** [待填写]