Files
atlas-seller-kit/docs/api/pricing.md
T

133 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 定价试算与门店方案 API
> 2026-09-28:维护者批准 SKU 就地试算、详情弹窗、随商品草稿原子确认。本契约替换原规划的独立 `pricing-confirms` 入口。
> 稳定 DTO 位于 `packages/contracts/src/pricing.ts`;金额、汇率和比例均使用十进制字符串。
## 1. 目录
### GET `/pricing/catalog?store_id={uuid}`
需要已认证工作空间。返回 `{data: PricingCatalog}`:
- `store`:门店 ID、名称及报价币种(CNY / RUB)。
- `rate_cards[]`:该门店平台、RU / rFBS 的可用费率卡及运输方式,包含已审核且在有效期内的 active 卡,以及配置允许时的 draft 卡。`development_only=true` 为兼容现有 DTO 保留,表示所选卡尚未审核,并不代表禁止首次上架。
- `pending_rate_cards[]`:该平台 RU / rFBS 待审核草稿的名称、版本和状态;在允许草稿报价时也可能同时出现在 `rate_cards[]`,用于展示审核状态。
- `policies[]`:该门店活动方案,默认方案优先,包含完整参数和 revision。
- `exchange_rate`:CBR 最新 CNY→RUB 官方参考汇率,包含 ID、日期和 stale;缺少数据时为 null。超过 72 小时提示过期参考,不自动代填其他来源。
### GET `/pricing/categories?store_id={uuid}&q={text}`
检索门店平台下活动商品类目,返回 `{data:[{id,name,path}]}`,最多 50 条。`q` 最长 200 字;继续输入名称缩小范围。
## 2. POST `/pricing/quote-previews`
要求 `Idempotency-Key`。成功返回 201;同键同输入重放原响应,同键不同输入返回 409。
```json
{
"store_id": "<store UUID>",
"platform_product_category_id": "<category UUID>",
"fulfillment": "rfbs",
"binding": {"profile_id":"<profile UUID>","variant_id":"<variant UUID>","client_key":null},
"purchase_cost_cny": "28.00",
"package": {"shipping_weight_g":"380","length_mm":"220","width_mm":"160","height_mm":"90","status":"confirmed"},
"pricing": {
"pricing_policy_id": "<policy UUID>",
"rate_card_id": "<rate-card UUID>",
"transport_method_ids": ["<transport UUID>"],
"default_transport_method_id": "<transport UUID>",
"domestic_shipping_and_label_fee_cny":"3",
"ad_rate":"0.03","last_mile_rate":"0.02","withdrawal_rate":"0.01","other_rate":"0.02",
"desired_markup_rate":"0.50","list_price_discount_rate":"0.20",
"mode":"target_markup","selling_price":null,
"fx":{"mode":"policy_fixed"},"temporary_commission_rate":null
}
}
```
- 独立试算 `binding=null`;SKU 必须提供 profile,已有 variant_id 与新建 client_key 二选一。验证资源归属,已上架 SKU 不接受新报价。
- 包装输入是编辑草稿合并后的有效 g/mm 值,必须已确认;接口为兼容历史值允许最多三位小数,重量至少 1 g,当前编辑页与试算界面只接受正整数。保存时从实际持久化草稿重新合并并核对,不能仅信任客户端摘要。
- `mode=target_markup`:目标利润=采购成本×加价率;求解销售价。`fixed_sale`:selling_price 必填,按该售价核算利润。
- 百分比费用以销售额为基数;除加价率外每项小于 1、最多六位小数。引擎拒绝总扣费率≥1。划线价=销售价÷(1−折扣率)。
- `selling_price` 在编辑页显示为「实际售价」,利润按实际售价计算。`list_price` 显示为「划线价」,两价在试算结果中同行展示;首次上架 Ozon 的 `price` 使用划线价,不发送 `old_price`,上线后用户再在 Ozon 调整实际售价(2026-10-04 维护者要求)。人民币摘要按报价快照换算两价,不使用当前汇率改写历史报价。缺失或低于实际售价的划线价须重新确认报价后发布。
- 枚举运输方式×产品类型×佣金档,按最终币种舍入后回验货值、佣金、重量和体积重边界。默认运输不可用时 recommended_option_id=null,不替换为另一方式。
- `fx.mode=system` 读取 PostgreSQL 最新 CBR;`policy_fixed` 必须绑定有固定汇率的方案;`one_time_override` 必须明确 rate 和 reason。固定汇率取方案值,不信任客户端覆盖。采用的汇率固定在本次预览中,15 分钟内确认不重新拉取外部汇率。
- 只有不存在有效正式佣金规则且无方案或方案允许时,才能 temporary_commission_rate。已有正式规则禁止覆盖。
响应包含 `preview_id,expires_at,input_hash,recommended_option_id,options,unavailable_options,context`。
每个 option 包含目标币种销售/划线价、CNY/RUB 利润、计费重、佣金/物流引用、费用明细、warnings 和 can_confirm。
亏损可查看,但 can_confirm=false;判定使用未舍入利润,显示为 0.00 的微亏也不能确认。
context 保存规则版本、实际汇率与来源,以及系统汇率记录 ID / 日期。参数/规则快照由服务端持久化。
预览按用户、工作空间、门店、SKU、规范化输入与规则摘要绑定,15 分钟有效。Worker 删除过期两天以上的预览。
## 3. 商品保存时确认
`PUT /products/{id}/editor-draft` 沿用商品/profile revision 的 `If-Match` 和 `Idempotency-Key`,每个 variant 增加:
```json
{
"pricing_draft": "本次 PricingParameters 对象或 null",
"pricing_selection": {"preview_id":"<UUID>","option_id":"<UUID>","input_hash":"<64-char SHA256>"}
}
```
两个字段均可省略以兼容既有客户端;读取 listing 返回 `pricing_draft,pricing_result,pricing_valid`。
确认在现有商品事务中执行:先保存草稿和新 SKU,再核对实际有效输入、用户绑定、有效期、审核规则摘要、币种及候选资格,最后创建不可变报价/候选快照并切换 listing 售价引用。
新 SKU 的 client_key 在同一事务映射到真实 ID。独立预览、其他用户或其他 SKU 的预览不能用于确认。
- 携带失效确认意图:409 `PRICING_PREVIEW_STALE`,details 定位具体 SKU,整笔保存回滚。
- 不携带确认意图:允许保存未定价草稿;输入/币种变化使报价失效。用户可在冲突弹窗明确选择“仅保存草稿,稍后定价”。
- 未变化的已确认报价继续使用原快照,无时间过期,不重新读规则或汇率。方案更新/归档不改变其价格。
- 发布校验要求 pricing_valid=true,无有效报价返回 `PRICING_REQUIRED`;已导入 SKU 沿用既有不可再次首次上架边界。
- 数据库复合外键保证 quote/profile/product/variant/store/platform 归属,RLS 隔离租户。确认快照拒绝 UPDATE;成本、包装、类目、店铺或币种变化触发失效。
## 4. 门店方案
| 方法 | 路径 | 约束 |
|---|---|---|
| GET | `/stores/{id}/pricing-policies` | 返回当前门店活动方案数组 |
| POST | `/stores/{id}/pricing-policies` | Idempotency-Key;成功 201 |
| PATCH | `/pricing-policies/{id}` | Idempotency-Key + If-Match(带引号的 revision);冲突 409 |
| POST | `/pricing-policies/{id}/set-default` | Idempotency-Key;门店锁保证唯一默认;成功 200 |
| DELETE | `/pricing-policies/{id}` | Idempotency-Key;归档并清除默认标志,返回 `{id,archived:true}` |
写入 `{name,note,parameters,allow_commission_override}`。只保存费用、加价、折扣、固定/系统汇率与运输偏好;采购价、包装、手动售价及临时佣金不属于方案持久字段。
方案的默认和比较运输必须属于同一可用费率卡;正式环境默认允许有效草稿卡;可通过 `PRICING_ALLOW_DRAFT_RATES=false` 仅使用已审核卡。历史快照不会被方案修改;尚未确认的预览因方案版本改变要求重算。
2026-10-04 维护者确认:SKU「重新试算」与「按方案计算全部」主动重读目标店铺 `/pricing/catalog`,使用最新 `is_default=true` 方案的完整参数(包含加价、折扣、费用、运输与汇率偏好),替换 SKU 的旧定价参数再生成预览;沿用当前采购成本和有效包装,不自动保存商品。无默认方案或其费率卡不可用时给出明确提示,不静默使用折扣率为 0 的临时参数。已导入 SKU 不重算。自动试算、手工实际售价调整和试算详情内的重新计算继续使用当前 SKU/详情参数。默认方案折扣率为 50% 时,划线价=实际售价÷(1−50%)=实际售价×2。
## 5. 数据启用与错误
迁移中的 CEL 目录为 `draft`,覆盖六种产品类型、三种运输方式,香港空运不纳入。平台类目佣金映射仍是正式试算所需数据;草稿物流费率可按上述配置参与报价,页面保留费率来源标识,接口不会回退到未选定的固定佣金。物流费率审核用于形成有证据的已审核版本,不再是草稿报价上架预检的前置条件。
API 在开发与生产环境默认允许 draft 费率卡参与方案保存、预览和 SKU 报价确认;`PRICING_ALLOW_DRAFT_RATES=false` 可显式关闭。测试环境默认关闭,仅显式设为 `true` 才开启。使用草稿卡不会修改费率卡状态或生成审核记录;目录的 `rate_cards[].development_only` 和预览的 `context.development_only` 保留来源标识,供页面和报价快照展示。已确认的草稿卡报价满足定价确认要求,首次上架预检不再因卡片未审核返回 `PRICING_RATE_REVIEW_REQUIRED`;其他报价输入、利润和发布字段校验照常执行。佣金和汇率仍按现有输入规则处理。此变更由维护者于 2026-09-29 要求,影响 Web、API、现有响应说明和发布预检,不改变审核端点。
### 系统管理员审核 CEL 费率
系统管理「物流费率审核」使用以下接口;全部要求登录且具备实时有效的 `system_admin_grants` 授权,普通门店用户返回 403 `SYSTEM_ADMIN_REQUIRED`:
| 方法 | 路径 | 行为 |
|---|---|---|
| GET | `/system/pricing/rate-cards?platform_id={uuid}` | 查看该平台 RU/rFBS 草稿及已启用费率,含产品类型、规则、审核记录与 `rules_hash` |
| GET | `/system/pricing/rate-cards/{id}` | 重读单个费率版本 |
| POST | `/system/pricing/rate-cards/{id}/reviews` | `Idempotency-Key` UUID 必填;原子保存审核证据并启用费率,成功 201 |
审核请求包含 `rules_hash`、`source_reference`(资费合同/实际账单批次)、至少 20 字的 `review_note`、`effective_from`(含时区 ISO 时间)和 `samples`。每条样本包含 `rule_id`、唯一的 `invoice_reference`、`merchandise_value_cny`、包装重量 g / 长宽高 mm、`billed_logistics_cny`。每种 CEL 产品类型至少一条,合计不少于六条。服务端使用与正式报价相同的包装适用性和运费函数复算;样本货值须命中对应类型,账单运费与规则计算差额不得超过 0.01 CNY。真实账单来源和口径仍由审核人负责确认,系统不会凭示例数字自行审核。
仅支持 Ozon/CEL 草稿版本。规则必须仍为 16 条 draft,类型为指定六种,且 `rules_hash` 与管理员查看时一致;否则返回 409/422。通过后在同一数据库事务保存不可修改的审核记录、把规则与费率卡改为 active、设置审核人与生效日期。若生效日期在未来,直到该时间才出现在卖家端可用费率中。数据库拒绝改写已审核的费率卡、产品类型、运输方式和规则;修改资费应新建版本重新审核,不覆盖已确认 SKU 报价。已启用费率不提供再次审核入口。
系统管理不会自动配置平台类目佣金映射;缺少正式佣金规则时,卖家试算仍需满足临时佣金覆盖条件。当前 CEL 种子保持 draft,尚无真实账单证据时不得提交启用。
| HTTP / code | 用户动作 |
|---|---|
| 422 VALIDATION_ERROR | 修正输入格式或缺项 |
| 422 PRICING_RULES_UNAVAILABLE | 配置并审核物流费率 |
| 422 PRICING_COMMISSION_UNAVAILABLE | 配置正式映射;缺少正式规则时,可在允许临时佣金的方案或本次自定义参数中填写已核实的临时佣金率 |
| 422 PRICING_FX_UNAVAILABLE | 等待 CBR 同步,或明确采用临时/方案固定汇率 |
| 409 PRICING_PREVIEW_STALE | 重算、比较并重新应用,或显式仅保存草稿 |
| 409 VERSION_CONFLICT | 重新读取版本后修改 |
独立上下文、汇率信息整合在 catalog;本期不开放原规划的独立 context、exchange-rate、pricing-confirms 路由和报价历史列表。