# 包花厂集采 11-04 接口变更

> 不参与等级免报价、货架按场次品种等级共用、供货单返回配货进度、盘点不再独占；覆盖供应商小程序、仓库 PDA、运营后台

- 类型：前端对接
- 分类：包花厂集采
- 端：供应商小程序 / 仓库 PDA / 运营后台
- 后端服务：supplier + admin
- 状态：已上测
- 测试分支：supplier/test + bulky-test/admin
- 更新：2026-11-04
- 网页版：https://doc.xfun.fun/bulk-purchase-1104-api

## 总览

2026-11-04 包花厂集采四项改动里，需要前端对接的部分：

- **不参与的等级无需报价**：报价只需传可供扎数大于 0 的等级；可供为 0 或不传的等级不填价格、不保存。调价只填参与的等级。
- **货架共用**：同场次、同品种（二级分类）、同等级的货放同一个货架，不同供应商共用。货架码从「一张单一个」变成「一个等级一个」，展示要改读等级上的 `shelfCode`。
- **供货单返回配货进度**：供应商端供货单列表、详情返回已打标签扎数和已扫码扎数，整单和各等级都有。
- **盘点不再独占**：扫码不需要先「开始盘点」，任何仓库人员都能撤销任何人扫的码、都能提交盘点。

| # | 接口 | 端 | 变化 |
|----|----|----|----|
| ① | `POST /bulkPurchase/quote/submit` | 供应商小程序 | 只传参与的等级，不参与的等级不填价 |
| ② | `POST /bulkPurchase/quote/update` | 供应商小程序 | 同 ① |
| ③ | `POST /bulkPurchase/quote/adjustPrice` | 供应商小程序 | 只填参与的等级 |
| ④ | `POST /bulkPurchase/demand/detail` | 供应商小程序 | 不参与的等级 `mySupplyLabelNum`、`myPrice` 为 null |
| ⑤ | `POST /bulkPurchase/quote/history` | 供应商小程序 | 新版本 `rankPrices` 只含参与的等级 |
| ⑥ | `POST /bulkPurchase/order/list` | 供应商小程序 | 返回 `printedLabelNum`、`scannedLabelNum`；新增 `ranks[].printedLabelNum`、`ranks[].shelfCode` |
| ⑦ | `POST /bulkPurchase/order/detail` | 供应商小程序 | 同 ⑥ |
| ⑧ | `POST /wms/bulkPurchase/order/list` | 仓库 PDA | 新增 `shelfCodes`；关键字可搜任一等级货架 |
| ⑨ | `POST /wms/bulkPurchase/order/detail` | 仓库 PDA | 新增 `shelfCodes`、`ranks[].shelfCode`、`ranks[].printedLabelNum` |
| ⑩ | `POST /wms/bulkPurchase/print/page` | 仓库 PDA | 新增 `ranks[].shelfCode`；标签 `shelfCode` 改为等级货架 |
| ⑪ | `POST /wms/bulkPurchase/print/normal`、`/print/reprint` | 仓库 PDA | 返回标签的 `shelfCode` 改为等级货架 |
| ⑫ | `POST /wms/bulkPurchase/label/resolve` | 仓库 PDA | `shelfCode` 改为标签等级的货架；`inventoryName` 恒为 null |
| ⑬ | `POST /wms/bulkPurchase/scan` | 仓库 PDA | 不需要先开始盘点；按货架扫码时不传 `orderId` |
| ⑭ | `POST /wms/bulkPurchase/inventory/start`、`/inventory/release` | 仓库 PDA | 可以不再调用 |
| ⑮ | `POST /wms/bulkPurchase/scan/revoke`、`/inventory/submit` | 仓库 PDA | 不再限本人 |
| ⑯ | `POST /bulkPurchase/order/detail` | 运营后台 | 新增 `ranks[].shelfCode` |
| ⑰ | `POST /bulkPurchase/confirm/quoteList` | 运营后台 | 新报价的 `rankPrices` 只含参与的等级 |

## 通用约定

- 供应商小程序与仓库 PDA 走 supplier 服务（测试地址 `https://api.xunmengvip.cn/xm-bulky-supplier`），运营后台走 admin 服务。两边都有 `/bulkPurchase` 前缀，**不要混用地址**。
- 全部 `POST` + JSON；请求头 `Authorization` 携带登录 token。
- supplier 返回 `{ "code": 0, "msg": "success", "data": …, "message": "success", "valid": true }`；admin 返回 `{ "code": 0, "msg": "success", "data": … }`。`code != 0` 时 `msg` 直接 toast。
- supplier 分页入参 `pageIndex`（从 1 开始，默认 1）、`pageSize`（默认 10）；出参 `{ current, size, total, records }`。
- 金额单位元、最多两位小数；扎数为整数；时间 `yyyy-MM-dd HH:mm:ss`（北京时间）。
- 没有值的字段返回 `null`，不会省略。

## 枚举

| 枚举 | 取值 |
|----|----|
| `rankCode` 等级 | 0 无等级 · 1 A · 2 B · 3 C · 4 D · 5 E · 6 O |
| `status` 供货单状态 | 10 待签到 · 20 待盘点 · 30 待结算 · 40 已结算 · 90 已取消 |
| `rejectReason` 扫码拒绝原因 | null 成功 · 2 重复扫码 · 3 标签已作废 · 4 属于其他供货单 · 5 标签不存在 · 6 供货单状态不允许盘点 |

## 货架码口径

后面接口里的 `shelfCode`、`shelfCodes` 都按这里的口径。

| 字段 | 含义 |
|----|----|
| `ranks[].shelfCode` | **该等级的货架码**，摆货、贴标看这个。同场次、同品种、同等级的所有供货单是同一个值，格式 `A-001`：场次内流水，满 999 进位到 `B-001`，如 `A-007` |
| `shelfCodes` | 仓库端供货单列表、详情：这张单各等级的货架码，按等级顺序去重，如 `["A-007","A-008"]` |
| 整单 `shelfCode` | 第一个中选等级的货架，只用于排序和兼容旧页面。**一张单有多个等级时，不能只展示它** |
| 标签 `shelfCode` | 该标签等级的货架码 |

> [!IMPORTANT]
> 2026-11-04 下午上线前生成的供货单，等级上没有单独的货架码，后端已经回退成整单货架码返回，前端不用做兼容判断。

例：同场次两份玫瑰计划，供应商甲中选 A、B 级，乙中选 A 级：

```text
甲的供货单：ranks[A].shelfCode = A-001，ranks[B].shelfCode = A-002，shelfCodes = ["A-001","A-002"]
乙的供货单：ranks[A].shelfCode = A-001，shelfCodes = ["A-001"]
```

## 接口

### POST /bulkPurchase/quote/submit · 首次提交报价

**变更**：不参与的等级可以不传；传了且可供扎数为 0 时不填价格

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `demandId` | Long | 是 | 采购计划 ID |
| `rankPrices` | List | 是 | 各等级报价，至少一个等级的可供扎数大于 0 |
| `rankPrices[].rankCode` | Integer | 是 | 等级，见枚举 `rankCode`；只能传计划开放的等级，同一等级不能重复 |
| `rankPrices[].supplyLabelNum` | Integer | 否 | 可供扎数。**0 或不填表示不参与该等级** |
| `rankPrices[].price` | BigDecimal | 否 | 单价（元/扎）。可供扎数大于 0 时必填；可供为 0 时不填，传了也忽略 |

参与的等级（可供扎数大于 0）仍要满足：可供扎数 ≥ 该等级最小提交量、≤ 最大提交量、≤ 收购扎数（含追加）；价格大于 0、最多两位小数、不高于该等级最高价、不超过 1000 元。

#### 请求示例

计划开放 A、B、C 三个等级，只供 A、B：

```json
{
  "demandId": 58,
  "rankPrices": [
    { "rankCode": 1, "supplyLabelNum": 600, "price": 2.50 },
    { "rankCode": 2, "supplyLabelNum": 200, "price": 1.80 }
  ]
}
```

C 级也可以写成 `{ "rankCode": 3, "supplyLabelNum": 0 }`，效果相同。

#### 响应

`data` 与改动前相同，本次不变。

#### 响应示例

```json
{ "code": 0, "msg": "success", "data": "报价提交成功", "message": "success", "valid": true }
```

#### 错误码 / 失败文案

| msg | 触发场景 | 前端处理 |
|----|----|----|
| 该需求不包含等级 C | 传了计划没开放的等级 | toast；表单只展示计划开放的等级 |
| 至少要有一个等级的可供扎数大于 0 | 全部等级都不参与 | toast |
| 同一等级不能重复出现 | `rankCode` 重复 | toast |
| A 级价格必须大于0 | 参与的等级没填价或填 0 | toast，定位到该等级 |
| A 级可供 50 扎，低于该等级最小提交量 100 扎；不供这个等级请填 0 | 参与的等级可供量不足 | toast |
| A 级报价 3.10 元/扎，超过该等级最高价 3.00 元/扎 | 价格超过最高价 | toast |

#### 规则

- 表单交互建议：等级行「可供扎数」为空或 0 时，价格输入框置灰、不校验必填。
- 总可供扎数由后端按各等级求和，前端不用传。

### POST /bulkPurchase/quote/update · 修改报价

**变更**：入参、规则、失败文案同「POST /bulkPurchase/quote/submit · 首次提交报价」

成功时 `data` 为 `"报价修改成功"`。内容与当前版本完全一致时不生成新版本。

### POST /bulkPurchase/quote/adjustPrice · 定采前调价

**变更**：只需填当前报价中参与的等级（可供扎数大于 0）

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `demandId` | Long | 是 | 采购计划 ID |
| `reason` | String | 是 | 调价原因 |
| `rankPrices` | List | 是 | 必须包含当前报价中**全部参与**的等级；其他等级传了也忽略 |
| `rankPrices[].rankCode` | Integer | 是 | 等级，见枚举 `rankCode` |
| `rankPrices[].price` | BigDecimal | 是 | 新单价（元/扎） |

#### 请求示例

```json
{
  "demandId": 58,
  "reason": "市场价下调",
  "rankPrices": [
    { "rankCode": 1, "price": 2.40 },
    { "rankCode": 2, "price": 1.70 }
  ]
}
```

#### 响应

`data` 与改动前相同，本次不变。

#### 响应示例

```json
{ "code": 0, "msg": "success", "data": "调价成功", "message": "success", "valid": true }
```

#### 错误码 / 失败文案

| msg | 触发场景 | 前端处理 |
|----|----|----|
| 需要对 A、B 全部参与供货的等级填写价格 | 漏填参与的等级 | toast |
| 您没有参与供货的等级，无法调价 | 当前报价没有可供扎数大于 0 的等级 | toast |
| 同一等级不能重复出现 | `rankCode` 重复 | toast |

### POST /bulkPurchase/demand/detail · 集采需求详情

**变更**：不参与的等级我的报价为空

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `id` | Long | 是 | 采购计划 ID |

#### 请求示例

```json
{ "id": 58 }
```

#### 响应

只列本次有变化的字段，其余不变。

| 字段 | 类型 | 说明 |
|----|----|----|
| `ranks[].rankCode` | Integer | 等级，列出计划开放的全部等级 |
| `ranks[].mySupplyLabelNum` | Integer | 我在该等级报的可供扎数；**不参与或未报为 null**，表单回填为空 |
| `ranks[].myPrice` | BigDecimal | 我在该等级报的单价；**不参与或未报为 null** |

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": {
    "id": 58,
    "ranks": [
      { "rankCode": 1, "mySupplyLabelNum": 600, "myPrice": 2.50 },
      { "rankCode": 2, "mySupplyLabelNum": 200, "myPrice": 1.80 },
      { "rankCode": 3, "mySupplyLabelNum": null, "myPrice": null }
    ]
  }
}
```

> [!NOTE]
> 改动前报过「可供 0 + 价格」的老报价，回填时仍会有 `mySupplyLabelNum = 0` 和价格。前端按「可供为 0 或 null 即不参与」统一处理。

### POST /bulkPurchase/quote/history · 我的报价版本历史

**变更**：新版本的 `rankPrices` 只含参与的等级

改动前的历史版本可能带可供 0 的等级行，新版本没有。同一份历史里各版本等级条数可能不同，按 `rankCode` 对齐展示，缺的等级显示「不参与」。

### POST /bulkPurchase/order/list · 我的供货单列表

**变更**：返回配货进度 `printedLabelNum`、`scannedLabelNum`；新增 `ranks[].printedLabelNum`、`ranks[].shelfCode`

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `pageIndex` | Integer | 否 | 页码，从 1 开始，默认 1 |
| `pageSize` | Integer | 否 | 每页条数，默认 10 |
| `status` | Integer | 否 | 见枚举 `status`；不传为全部 |
| `roundNo` | String | 否 | 场次 `yyyy-MM-dd` |
| `keyword` | String | 否 | 供货单号 / 需求编号 |

#### 请求示例

```json
{ "pageIndex": 1, "pageSize": 10, "status": 30 }
```

#### 响应

只列本次新增的字段。

| 字段 | 类型 | 说明 |
|----|----|----|
| `records[].printedLabelNum` | Integer | **变更**：原来供应商端恒为 null，现在返回。整单已打标签扎数，各等级 `printedLabelNum` 之和；配货进度的分母 |
| `records[].scannedLabelNum` | Integer | **变更**：原来供应商端恒为 null，现在返回。整单已扫码入库扎数，各等级 `scannedLabelNum` 之和；配货进度的分子 |
| `records[].shelfCode` | String | 整单货架码，口径见「货架码口径」 |
| `records[].ranks[].shelfCode` | String | **新增**。该等级的货架码 |
| `records[].ranks[].printedLabelNum` | Integer | **新增**。该等级已打标签扎数（包花厂打印生成的标签张数，一张一扎） |
| `records[].ranks[].scannedLabelNum` | Integer | 该等级已扫码入库扎数（盘点扫码合格） |

> [!NOTE]
> 配货进度 = `scannedLabelNum` / `printedLabelNum`。`printedLabelNum` 为 0 时还没打标签，显示「未打标签」，不要算百分比。撤销扫码后 `scannedLabelNum` 会减少；补打、增量打印会让 `printedLabelNum` 增加，重打不变。

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": {
    "current": 1, "size": 10, "total": 1,
    "records": [
      {
        "id": 43,
        "orderNo": "GH20261026364310",
        "shelfCode": "A-001",
        "printedLabelNum": 40,
        "scannedLabelNum": 18,
        "ranks": [
          { "rankCode": 2, "rankName": "B", "shelfCode": "A-001", "confirmLabelNum": 500, "printedLabelNum": 30, "scannedLabelNum": 15 },
          { "rankCode": 3, "rankName": "C", "shelfCode": "A-002", "confirmLabelNum": 300, "printedLabelNum": 10, "scannedLabelNum": 3 }
        ]
      }
    ]
  }
}
```

### POST /bulkPurchase/order/detail · 我的供货单详情

**变更**：返回 `printedLabelNum`、`scannedLabelNum`；新增 `ranks[].printedLabelNum`、`ranks[].shelfCode`，含义同「POST /bulkPurchase/order/list · 我的供货单列表」

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `id` | Long | 是 | 供货单 ID |

#### 请求示例

```json
{ "id": 43 }
```

### POST /wms/bulkPurchase/order/list · 待作业供货单列表

**变更**：新增 `shelfCodes`；关键字可搜任一等级的货架码；排序改为整单货架码 + ID

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `pageIndex` | Integer | 否 | 页码，从 1 开始，默认 1 |
| `pageSize` | Integer | 否 | 每页条数，默认 10 |
| `keyword` | String | 否 | 供货单号 / 货架码 / 供应商 / 品种 / 需求编号；**输入任一等级的货架码都能搜到这张单**，所以按货架找单直接搜货架码 |
| `roundNo` | String | 否 | 场次 `yyyy-MM-dd` |
| `status` | Integer | 否 | 见枚举 `status` |

#### 请求示例

```json
{ "pageIndex": 1, "pageSize": 10, "roundNo": "2026-11-05", "keyword": "A-001" }
```

#### 响应

只列本次新增的字段。

| 字段 | 类型 | 说明 |
|----|----|----|
| `records[].shelfCodes` | List<String> | **新增**。这张单各等级的货架码，按等级顺序去重；列表上展示这个，不要只展示整单 `shelfCode` |

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": {
    "current": 1, "size": 10, "total": 2,
    "records": [
      { "id": 31, "orderNo": "GH2026110510019000001", "supplierName": "甲花卉", "shelfCode": "A-001", "shelfCodes": ["A-001", "A-002"] },
      { "id": 32, "orderNo": "GH2026110510019000002", "supplierName": "乙花卉", "shelfCode": "A-001", "shelfCodes": ["A-001"] }
    ]
  }
}
```

### POST /wms/bulkPurchase/order/detail · 供货单详情

**变更**：新增 `shelfCodes`、`ranks[].shelfCode`，含义见「货架码口径」

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `id` | Long | 是 | 供货单 ID |

#### 请求示例

```json
{ "id": 31 }
```

### POST /wms/bulkPurchase/print/page · 打印页数据

**变更**：新增 `ranks[].shelfCode`；`ranks[].labels[].shelfCode` 改为该等级的货架码

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `id` | Long | 是 | 供货单 ID |

#### 请求示例

```json
{ "id": 31 }
```

#### 响应

| 字段 | 类型 | 说明 |
|----|----|----|
| `shelfCode` | String | 整单货架码，口径见「货架码口径」 |
| `ranks[].shelfCode` | String | **新增**。该等级的货架码，打印页按等级分组展示 |
| `ranks[].labels[].shelfCode` | String | **变更**。标签上印的货架码，改为该标签等级的货架 |

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": {
    "orderId": 31,
    "orderNo": "GH2026110510019000001",
    "shelfCode": "A-001",
    "ranks": [
      { "rankCode": 1, "rankName": "A", "shelfCode": "A-001",
        "labels": [ { "labelCode": "GH2026110510019000001-1-0001", "rankCode": 1, "seqNo": 1, "shelfCode": "A-001" } ] },
      { "rankCode": 2, "rankName": "B", "shelfCode": "A-002",
        "labels": [ { "labelCode": "GH2026110510019000001-2-0001", "rankCode": 2, "seqNo": 1, "shelfCode": "A-002" } ] }
    ]
  }
}
```

### POST /wms/bulkPurchase/print/normal · 正常/增量打印

**变更**：返回标签的 `shelfCode` 改为该标签等级的货架码，入参不变

`POST /wms/bulkPurchase/print/reprint`（重打）返回的标签同样按等级货架码。

### POST /wms/bulkPurchase/label/resolve · 首码识别供货单

**变更**：`shelfCode` 改为标签等级的货架；`inventoryName` 恒为 null

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `labelCode` | String | 是 | 标签码 |

#### 请求示例

```json
{ "labelCode": "GH2026110510019000001-2-0001" }
```

#### 响应

只列本次有变化的字段。

| 字段 | 类型 | 说明 |
|----|----|----|
| `shelfCode` | String | **变更**。该标签等级所在货架码 |
| `inventoryName` | String | **变更**。恒为 null，不再提示「某某正在盘点」 |

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": { "scannable": true, "orderId": 31, "orderNo": "GH2026110510019000001", "rankCode": 2, "seqNo": 1, "shelfCode": "A-002", "inventoryName": null }
}
```

### POST /wms/bulkPurchase/scan · 扫码盘点

**变更**：不需要先调「开始盘点」；按货架连续扫码时不传 `orderId`

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `labelCode` | String | 是 | 标签码 |
| `orderId` | Long | 否 | 当前作业的供货单 ID。**传了**：扫到别家标签会被拒（`rejectReason = 4`），适合按单盘点；**不传**：扫到哪张单的标签就计入哪张单，适合按货架扫（一个货架上有多家的货） |

#### 请求示例

按货架扫：

```json
{ "labelCode": "GH2026110510019000002-1-0003" }
```

#### 响应

| 字段 | 类型 | 说明 |
|----|----|----|
| `success` | Boolean | 是否计数成功 |
| `rejectReason` | Integer | 拒绝原因，见枚举 `rejectReason` |
| `message` | String | 提示文案，直接展示 |
| `actualLabelNum` | Integer | 传了 `orderId` 时返回该单累计实际产出；不传为 null |
| `ranks` | List | 传了 `orderId` 时返回该单各等级进度（含 `ranks[].shelfCode`）；不传为 null |

#### 响应示例

```json
{
  "code": 0, "msg": "success", "message": "success", "valid": true,
  "data": { "success": true, "rejectReason": null, "message": "扫码成功", "actualLabelNum": null, "ranks": null }
}
```

> [!WARNING]
> 扫码响应外层 `code` 恒为 0，被拒时看 `data.success = false` 和 `data.message`，不能只看外层。

### POST /wms/bulkPurchase/inventory/start · 开始盘点

**变更**：不再占用供货单，扫码不依赖它，可以不调

只校验供货单处于待盘点，返回 `"已开始盘点"`。`POST /wms/bulkPurchase/inventory/release`（暂停盘点）直接返回 `"已暂停"`，没有任何作用。两个接口保留只为兼容旧版 PDA，新版可以去掉「开始 / 暂停盘点」按钮。

### POST /wms/bulkPurchase/scan/revoke · 撤销扫码

**变更**：任何仓库人员都能撤销任何人扫的标签

入参 `labelCode` 不变。供货单须仍在待盘点。`POST /wms/bulkPurchase/inventory/submit`（提交盘点）同样不再限本人，任何仓库人员都能提交。

### POST /bulkPurchase/order/detail · 运营后台供货单详情

**变更**：新增 `ranks[].shelfCode`

#### 请求参数

| 字段 | 类型 | 必填 | 说明 |
|----|----|----|----|
| `id` | Long | 是 | 供货单 ID |

#### 请求示例

```json
{ "id": 31 }
```

#### 响应

| 字段 | 类型 | 说明 |
|----|----|----|
| `shelfCode` | String | 整单货架码，口径见「货架码口径」 |
| `ranks[].shelfCode` | String | **新增**。该等级的货架码 |

#### 响应示例

```json
{
  "code": 0, "msg": "success",
  "data": {
    "id": 31, "orderNo": "GH2026110510019000001", "shelfCode": "A-001",
    "ranks": [
      { "rankCode": 1, "rankName": "A", "shelfCode": "A-001", "confirmLabelNum": 100 },
      { "rankCode": 2, "rankName": "B", "shelfCode": "A-002", "confirmLabelNum": 0 }
    ]
  }
}
```

运营后台供货单列表 `POST /bulkPurchase/order/list` 只返回整单 `shelfCode`，不带等级货架，也不支持按货架码搜索。

### POST /bulkPurchase/confirm/quoteList · 定采报价列表

**变更**：新报价的 `rankPrices` 只含参与的等级

改动后提交的报价不再带可供 0 的等级行。按计划等级画固定列时，缺的等级显示「不参与」或「-」，不要显示 0 元。

## 联调注意

- 测试环境：supplier `supplier/test`、admin `bulky-test/admin` 已部署；测试库 DDL 已执行。
- 造数：同一场次发两份同品种的采购计划，让不同供应商中选同一个等级，两张供货单该等级的 `shelfCode` 应相同，打出的标签货架码也相同。
- 配货进度：供应商 35069 的供货单 `GH20261026364310`，整单应为已打标签 40、已扫码 18；B 级 30 / 15，C 级 10 / 3。
- 改动前生成的供货单：可能带可供 0 的等级行，等级货架码都等于整单货架码，属正常。

> [!WARNING]
> 一个货架上会有多家的货。PDA 如果「先选单再连续扫」并传了 `orderId`，扫到同货架别家的标签会提示「该标签属于其他供货单」。按货架扫码请不传 `orderId`。
