Files
atlas-seller-kit/docs/api/pricing.md
T
2026-09-28 17:34:33 +08:00

130 lines
10 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、在有效期内、有审核时间及审核人。`development_only=true` 表示本地开发模式临时可选的 draft 卡。
- `pending_rate_cards[]`:该平台 RU / rFBS 待审核草稿的名称、版本和状态,仅用于说明阻断原因,不作为方案或报价可选项。
- `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−折扣率)。
- 枚举运输方式×产品类型×佣金档,按最终币种舍入后回验货值、佣金、重量和体积重边界。默认运输不可用时 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}`。只保存费用、加价、折扣、固定/系统汇率与运输偏好;采购价、包装、手动售价及临时佣金不属于方案持久字段。
方案的默认和比较运输必须属于同一可用费率卡;正式环境仅允许已审核卡。历史快照不会被方案修改;尚未确认的预览因方案版本改变要求重算。
## 5. 数据启用与错误
迁移中的 CEL 目录为 `draft`,覆盖六种产品类型、三种运输方式,香港空运不纳入。真实账单复核、有效日期/审核人以及平台类目佣金映射仍是正式使用前置条件,接口不会回退到示例费率或固定佣金。
商品编辑联调期间,API 在 `NODE_ENV=development` 时默认允许 draft 费率卡参与方案保存、预览与草稿报价确认;可设 `PRICING_ALLOW_DRAFT_RATES=false` 关闭。测试环境只有显式设 `PRICING_ALLOW_DRAFT_RATES=true` 才开启;生产环境始终关闭。该模式不修改费率卡状态、不生成审核记录。目录的 `rate_cards[].development_only` 和预览的 `context.development_only` 标识试算性质;基于此类预览确认的 SKU 报价可在编辑页查看,但首次上架校验必须返回 `PRICING_RATE_REVIEW_REQUIRED`,直至使用已审核费率重新试算确认。佣金和汇率仍按现有输入规则处理。
### 系统管理员审核 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 路由和报价历史列表。