forked from erp-dev/erp
22 KiB
22 KiB
Shipment API 文档
出货管理模块 API 文档,包含出货单和销售品相关接口。
目录
查询出货单
出货单列表
- URL:
/api/v1/shipment/shipments/ - Method:
GET - 认证: 需要登录(JWT Token)
查询参数(可选)
| 参数 | 类型 | 说明 |
|---|---|---|
| limit | int | 分页大小(LimitOffsetPagination) |
| offset | int | 偏移量 |
| customer | int | 客户ID |
| status | int | 状态(1=草稿(未发布), 2=已发布, 3=已取消, 4=已驳回, 5=已审核) |
| external_id | string | 外部订单号(精确匹配) |
| shipment_date_from | string | 出货日期起始(YYYY-MM-DD) |
| shipment_date_to | string | 出货日期结束(YYYY-MM-DD) |
响应格式
使用 LimitOffsetPagination:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"merchant_id": 1,
"merchant_name": "测试印花厂",
"customer": 1,
"customer_name": "客户A",
"shipment_date": "2026-01-14",
"status": 1,
"status_display": "草稿(未发布)",
"status_modified_at": null,
"cancelled_by_id": null,
"cancelled_by_name": null,
"approved_by_id": null,
"approved_by_name": null,
"remark": "备注信息",
"items_count": 3,
"sales_items": [],
"external_finished_products": [],
"created_by_id": 1,
"created_by_name": "张三",
"created_at": "2026-01-14T10:00:00Z",
"updated_at": "2026-01-14T10:00:00Z"
}
]
}
说明:
sales_items现在返回的是带图片字段的销售品详情结构- 若销售品关联的
PrintingJob.product存在主图,则会返回product_image_url - 若无关联图片,则
product_image_url为null
出货单详情
- URL:
/api/v1/shipment/shipments/<id>/ - Method:
GET - 认证: 需要登录(JWT Token)
查询参数(可选)
| 参数 | 类型 | 说明 |
|---|---|---|
| status | int | 可选状态过滤;取值为 1=草稿(未发布), 2=已发布, 3=已取消, 4=已驳回, 5=已审核。传入后仅当该出货单状态匹配时才返回详情 |
说明:
- 详情中的
sales_items同样返回带product_image_url的销售品详情结构
更新出货单
- URL:
/api/v1/shipment/shipments/<id>/ - Method:
PATCH/PUT - 认证: 需要登录(JWT Token)
说明:
- 仅允许修改业务数据字段:
customer、shipment_date、address、contact_name、contact_phone、area、remark、external_id - 该接口不允许修改
status - 仅
草稿(未发布)状态的出货单允许修改 - 非草稿状态会返回
400 Bad Request
请求体示例:
{
"address": "宁波市滨海路 9 号",
"contact_name": "王五",
"contact_phone": "13700137000",
"area": "更新地区",
"remark": "更新备注"
}
错误示例:
{
"detail": "仅草稿状态的出货单允许修改"
}
创建出货单
创建出货单并关联销售品。
业务规则补充:
- 当
sales_items非空时,所有销售品都必须关联有效的printing_job - 并且这些销售品必须全部来自同一个
printing_order - 如果存在缺少生产任务、生产任务不存在、或跨生产订单混装,接口会拒绝创建
- 新创建的出货单默认状态为
草稿(未发布)
接口信息
- URL:
/api/v1/shipment/shipments/ - Method:
POST - 认证: 需要登录(JWT Token)
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customer | int | 是 | 客户ID |
| shipment_date | string | 是 | 出货日期(YYYY-MM-DD) |
| address | string | 否 | 地址(可空字符串,长度<=255) |
| contact_name | string | 否 | 联系人(可空字符串,长度<=100) |
| contact_phone | string | 否 | 联系电话(可空字符串,长度<=50) |
| area | string | 否 | 出货地区(可空字符串,长度<=30) |
| remark | string | 否 | 备注 |
| sales_items | array[int] | 否 | 要关联的销售品ID列表 |
请求示例
{
"customer": 1,
"shipment_date": "2026-01-14",
"address": "杭州市测试路 1 号",
"contact_name": "张三",
"contact_phone": "13800138000",
"area": "华东",
"remark": "备注信息",
"sales_items": [1, 2, 3]
}
响应格式
{
"id": 1,
"merchant_id": 1,
"merchant_name": "测试印花厂",
"customer": 1,
"customer_name": "客户A",
"shipment_date": "2026-01-14",
"address": "杭州市测试路 1 号",
"contact_name": "张三",
"contact_phone": "13800138000",
"area": "华东",
"remark": "备注信息",
"status": 1,
"status_display": "草稿(未发布)",
"status_modified_at": null,
"cancelled_by_id": null,
"cancelled_by_name": null,
"approved_by_id": null,
"approved_by_name": null,
"items_count": 3,
"sales_items": [],
"external_finished_products": [],
"created_by_id": 1,
"created_by_name": "张三",
"created_at": "2026-01-14T10:00:00Z",
"updated_at": "2026-01-14T10:00:00Z"
}
响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 出货单ID |
| merchant_id | int | 所属商户ID(从当前登录用户推导) |
| merchant_name | string | 所属商户名称(从当前登录用户推导) |
| customer | int | 客户ID |
| customer_name | string | 客户名称 |
| shipment_date | string | 出货日期 |
| address | string | 地址 |
| contact_name | string | 联系人 |
| contact_phone | string | 联系电话 |
| area | string | 出货地区 |
| remark | string | 备注 |
| status | int | 状态枚举(1=草稿(未发布), 2=已发布, 3=已取消, 4=已驳回, 5=已审核) |
| status_display | string | 状态显示名称 |
| status_modified_at | string/null | 最后一次状态修改时间 |
| cancelled_by_id | int/null | 取消人 ID,仅当进入已取消时可能有值 |
| cancelled_by_name | string/null | 取消人名称,仅当进入已取消时可能有值 |
| approved_by_id | int/null | 审核人 ID,仅当进入已审核时可能有值 |
| approved_by_name | string/null | 审核人名称,仅当进入已审核时可能有值 |
| items_count | int | 关联的销售品数量 |
| created_by_id | int | 创建人ID |
| created_by_name | string | 创建人名称 |
| created_at | string | 创建时间 |
| updated_at | string | 更新时间 |
状态流转规则
草稿(未发布)只能流转到已发布已发布可以流转到已审核/已驳回/已取消已驳回可以流转到已审核/已取消已审核可以流转到已取消已取消不可再流转到其他状态- 重复设置同一状态保持幂等,不报错
- 进入
已审核时必须提供审核人
错误响应
400 Bad Request - 客户不存在
{
"detail": "客户 999 不存在"
}
400 Bad Request - 销售品不存在
{
"detail": "以下销售品不存在: [999]"
}
400 Bad Request - 销售品已关联其他出货单
{
"detail": "以下销售品已关联到其他出货单: [1, 2]"
}
400 Bad Request - 销售品缺少关联生产任务
{
"detail": "以下销售品缺少关联生产任务,无法创建出货单: [1]"
}
400 Bad Request - 销售品关联的生产任务不存在
{
"detail": "以下销售品关联的生产任务不存在,无法创建出货单: [1]"
}
400 Bad Request - 销售品来自不同生产订单
{
"detail": "出货单中的销售品必须来自同一个生产订单"
}
创建出货单(external 版)
创建出货单但不绑定任何销售品,同时写入并关联“外部成品表”(用于兼容外部遗留系统)。
接口信息
- URL:
/api/v1/shipment/shipments/external/ - Method:
POST - 认证: 需要登录(JWT Token)
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customer | int | 是 | 客户ID |
| shipment_date | string | 是 | 出货日期(YYYY-MM-DD) |
| address | string | 否 | 地址(可空字符串,长度<=255) |
| contact_name | string | 否 | 联系人(可空字符串,长度<=100) |
| contact_phone | string | 否 | 联系电话(可空字符串,长度<=50) |
| area | string | 否 | 出货地区(可空字符串,长度<=30) |
| remark | string | 否 | 备注 |
| external_id | string | 是 | 外部订单号(长度<=120) |
| external_finished_products | array[object] | 是 | 外部成品表结构数组(至少 1 条) |
external_finished_products 每项结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| style_name | string | 是 | 款式名称 |
| num_of_rolls | int | 是 | 卷数 |
| remark | string | 否 | 备注(可空) |
请求示例
{
"customer": 1,
"shipment_date": "2026-01-14",
"address": "绍兴市仓库 2 号",
"contact_name": "李四",
"contact_phone": "13900139000",
"area": "华南",
"remark": "external 备注(可选)",
"external_id": "EXT-ORDER-001",
"external_finished_products": [
{"style_name": "款式A", "num_of_rolls": 2, "remark": "A备注"},
{"style_name": "款式B", "num_of_rolls": 5}
]
}
响应示例
{
"id": 1,
"merchant_id": 1,
"merchant_name": "测试印花厂",
"customer": 1,
"customer_name": "客户A",
"shipment_date": "2026-01-14",
"address": "绍兴市仓库 2 号",
"contact_name": "李四",
"contact_phone": "13900139000",
"area": "华南",
"remark": "external 备注(可选)",
"status": 1,
"status_display": "草稿(未发布)",
"external_id": "EXT-ORDER-001",
"status_modified_at": null,
"cancelled_by_id": null,
"cancelled_by_name": null,
"approved_by_id": null,
"approved_by_name": null,
"items_count": 0,
"external_finished_products_count": 2,
"created_by_id": 1,
"created_by_name": "张三",
"created_at": "2026-01-14T10:00:00Z",
"updated_at": "2026-01-14T10:00:00Z"
}
错误响应
400 Bad Request - external_id 为空
{
"external_id": ["external_id 不能为空"]
}
400 Bad Request - external_finished_products 为空
{
"external_finished_products": ["external_finished_products 不能为空"]
}
查询有待出货销售品的客户
查询当前商户下“存在未出货销售品”的客户列表。
接口信息
- URL:
/api/v1/shipment/sales-items/customers/ - Method:
GET - 认证: 需要登录(JWT Token)
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | int | 否 | 分页大小(LimitOffsetPagination) |
| offset | int | 否 | 偏移量 |
业务逻辑
- 仅统计当前商户下的销售品
- 只统计
shipment为空的销售品(未出货) - 只返回至少拥有 1 条未出货销售品的客户
- 返回客户基础信息及未出货销售品数量
响应格式
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"customer_id": 12,
"customer_name": "客户A",
"mobile": "13800000000",
"area": "杭州",
"unshipped_sales_items_count": 18
}
]
}
响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| count | int | 结果总数 |
| next | string/null | 下一页链接 |
| previous | string/null | 上一页链接 |
| results[].customer_id | int | 客户ID |
| results[].customer_name | string | 客户名称 |
| results[].mobile | string/null | 客户手机号 |
| results[].area | string/null | 客户地区 |
| results[].unshipped_sales_items_count | int | 该客户未出货销售品数量 |
按客户查询销售品
查询指定客户下的销售品列表,默认仅返回未出货销售品。
接口信息
- URL:
/api/v1/shipment/sales-items/by-customer/<customer_id>/ - Method:
GET - 认证: 需要登录(JWT Token)
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customer_id | int | 是 | 客户ID |
查询参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| include_already_has_shipment | bool | 否 | false | 是否包含已关联出货单的销售品 |
| external_order_id | string | 否 | - | 按生产订单外部订单号精确筛选 |
| limit | int | 否 | 800 | 分页大小 |
| offset | int | 否 | 0 | 偏移量 |
业务逻辑
- 仅允许查询当前商户下的客户
- 仅查询当前商户下、
customer_id匹配的销售品 - 默认只返回
shipment为空的销售品(待出货) - 可通过
include_already_has_shipment=true包含已出货销售品 - 可通过
external_order_id按关联生产订单的外部订单号精确筛选
响应格式
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 101,
"name": "赛扬 190g",
"quantity": "1200.00",
"unit": 1,
"unit_display": "米",
"position": "A1-01",
"remark": "",
"printing_job_id": 88,
"printing_order_id": 23,
"external_order_id": "KD20135142",
"customer_id": 12,
"customer_name": "客户A",
"shipment_id": null,
"shipment_date": null,
"created_at": "2026-03-28T10:00:00Z",
"created_by_id": 1,
"created_by_name": "张三"
}
]
}
错误响应
404 Not Found - 客户不存在或无权限
{
"detail": "客户 999 不存在"
}
查询销售品详情
查询单个销售品详情,返回列表接口中的全部字段,并额外补充关联生产任务产品图片。
接口信息
- URL:
/api/v1/shipment/sales-items/<id>/ - Method:
GET - 认证: 需要登录(JWT Token)
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | int | 是 | 销售品ID |
业务逻辑
- 仅允许查询当前商户下的销售品
- 返回销售品基础信息、关联客户信息、关联出货信息
- 额外返回关联
PrintingJob产品图 URL:product_image_url - 若无关联图片,则
product_image_url返回null
响应格式
{
"id": 101,
"name": "赛扬 190g",
"quantity": "1200.00",
"unit": 1,
"unit_display": "米",
"position": "A1-01",
"remark": "",
"printing_job_id": 88,
"printing_order_id": 23,
"external_order_id": "KD20135142",
"customer_id": 12,
"customer_name": "客户A",
"shipment_id": null,
"shipment_date": null,
"created_at": "2026-03-28T10:00:00Z",
"created_by_id": 1,
"created_by_name": "张三",
"product_image_url": "https://cdn.example.com/products/88/main.jpg"
}
响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 销售品ID |
| name | string | 销售品名称 |
| quantity | string | 数量(Decimal,保留2位小数) |
| unit | int | 单位编码(1=米, 2=件, 3=码, 4=个) |
| unit_display | string | 单位显示名称 |
| position | string | 货位(可能为空) |
| remark | string | 备注(可能为空) |
| printing_job_id | int/null | 关联的生产任务ID |
| printing_order_id | int/null | 关联的生产订单ID |
| external_order_id | string/null | 关联生产订单的外部订单号 |
| customer_id | int/null | 销售品关联客户ID |
| customer_name | string/null | 销售品关联客户名称 |
| shipment_id | int/null | 关联的出货单ID,null 表示未出货 |
| shipment_date | string/null | 出货日期(YYYY-MM-DD),null 表示未出货 |
| created_at | string | 创建时间(ISO 8601) |
| created_by_id | int/null | 创建人ID |
| created_by_name | string/null | 创建人名称 |
| product_image_url | string/null | 关联产品主图 URL,无图时为 null |
错误响应
404 Not Found - 销售品不存在或无权限
{
"detail": "销售品 999 不存在"
}
使用示例
curl -X GET \
'https://api.example.com/api/v1/shipment/sales-items/101/' \
-H 'Authorization: Bearer <token>'
通过生产订单查询销售品
查询与指定生产订单(PrintingOrder)关联的所有销售品(SalesItem)。
接口信息
- URL:
/api/v1/shipment/sales-items/by-printing-order/<printing_order_id>/ - Method:
GET - 认证: 需要登录(JWT Token)
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| printing_order_id | string | 是 | 生产订单内部ID,或 external_order_id |
查询参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| include_already_has_shipment | bool | 否 | false | 是否包含已关联出货单的销售品 |
业务逻辑
- 先尝试将路径参数按内部生产订单ID查询
- 若内部ID未命中,则按
external_order_id查询生产订单 - 若
external_order_id命中多条生产订单,则返回400 Bad Request - 获取该生产订单下所有
PrintingJob的 ID - 查询
SalesItem,过滤printing_job_id在这些 job ID 中的记录 - 根据
include_already_has_shipment参数决定是否过滤已关联出货单的销售品:false(默认):只返回shipment为空的销售品(待出货)true:返回所有销售品(包含已出货的)
响应格式
{
"count": 2,
"results": [
{
"id": 1,
"name": "产品A - 红色",
"quantity": "100.00",
"unit": 1,
"unit_display": "米",
"position": "A1-01",
"remark": "加急处理",
"printing_job_id": 123,
"printing_order_id": 456,
"customer_id": null,
"customer_name": null,
"shipment_id": null,
"shipment_date": null,
"created_at": "2026-01-14T10:00:00Z",
"created_by_id": 1,
"created_by_name": "张三"
},
{
"id": 2,
"name": "产品B - 蓝色",
"quantity": "50.50",
"unit": 1,
"unit_display": "米",
"position": "",
"remark": "",
"printing_job_id": 124,
"printing_order_id": 456,
"customer_id": 10,
"customer_name": "客户A",
"shipment_id": 5,
"shipment_date": "2026-01-13",
"created_at": "2026-01-13T15:30:00Z",
"created_by_id": 2,
"created_by_name": "李四"
}
]
}
响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| count | int | 结果总数 |
| results | array | 销售品列表 |
| results[].id | int | 销售品ID |
| results[].name | string | 销售品名称 |
| results[].quantity | string | 数量(Decimal,保留2位小数) |
| results[].unit | int | 单位编码(1=米, 2=件, 3=码, 4=个) |
| results[].unit_display | string | 单位显示名称 |
| results[].position | string | 货位(可能为空) |
| results[].remark | string | 备注(可能为空) |
| results[].printing_job_id | int/null | 关联的生产任务ID |
| results[].printing_order_id | int/null | 关联的生产订单ID |
| results[].customer_id | int/null | 销售品级别的客户ID |
| results[].customer_name | string/null | 销售品关联客户名称 |
| results[].shipment_id | int/null | 关联的出货单ID,null 表示未出货 |
| results[].shipment_date | string/null | 出货日期(YYYY-MM-DD),null 表示未出货 |
| results[].created_at | string | 创建时间(ISO 8601) |
| results[].created_by_id | int/null | 创建人ID |
| results[].created_by_name | string/null | 创建人名称 |
错误响应
404 Not Found - 生产订单不存在
{
"detail": "生产订单 999 不存在"
}
400 Bad Request - external_order_id 匹配多条生产订单
{
"detail": "external_order_id KD20135142 匹配到多个生产订单,请改用内部ID查询"
}
401 Unauthorized - 未登录
{
"detail": "Authentication credentials were not provided."
}
使用示例
查询待出货的销售品(默认)
curl -X GET \
'https://api.example.com/api/v1/shipment/sales-items/by-printing-order/123/' \
-H 'Authorization: Bearer <token>'
使用 external_order_id 查询
curl -X GET \
'https://api.example.com/api/v1/shipment/sales-items/by-printing-order/KD20135142/' \
-H 'Authorization: Bearer <token>'
查询所有销售品(包含已出货)
curl -X GET \
'https://api.example.com/api/v1/shipment/sales-items/by-printing-order/123/?include_already_has_shipment=true' \
-H 'Authorization: Bearer <token>'
单位编码对照表
| 编码 | 名称 |
|---|---|
| 1 | 米 |
| 2 | 件 |
| 3 | 码 |
| 4 | 个 |
相关模块
shipment/services.py: 业务逻辑层api_v1/views/shipment/: API 视图层shipment/models.py: 数据模型(SalesItem, Shipment)
外部系统兼容(数据模型说明)
为兼容外部遗留系统,Shipment 模块新增了“外部成品表”模型:
- 关系:
Shipment1 → NExternalFinishedProduct- 外键在
ExternalFinishedProduct.shipment(可空)
- 外键在
- Shipment 外部字段:
external_id:外部订单号(可空)