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

104 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.
# SKU 定价试算与确认:产品及实施提案
> 2026-09-28 · 维护者已批准本方案,进入实施。第 5 节的契约变更同步到 API 文档与共享契约;原型示例数据不作为生产费率。
## 1. 结论
建议采用「SKU 就地自动试算 + 详细试算弹窗 + 商品页统一保存」。采购价失焦或按 Enter 后,用该 SKU 当前方案计算并回填销售价、划线价和预估净利润;保留“计算”按钮供主动重算。用户可以关闭自动试算。弹窗用来查看费用、比较物流、调整本次参数,点击“应用到 SKU”回到未保存表单。商品页“保存”才确认报价并持久化。
把用户的两种意图分清:输入成本时需要立即看到结果;保存商品时才承诺这次售价。避免每个 SKU 都弹出一次确认框,也避免一次失焦就写入正式报价。
## 2. 已阅读的依据与当前差距
- 新项目:`requirements/pricing-tool.md`、`requirements/cel-ozon-rfbs-rates-review.md`、`api/pricing.md`、`api/product-editor-and-publishing.md`、`architecture/exchange-rates.md`,以及现有 pricing 表和 SKU 表单。
- 旧项目:`studio/src/pages/trial/TrialPage.tsx` 的「2 价格试算」、`TrialPricingPanel.tsx`、`studio/src/pricing/pricing.ts`。
- 旧页面通过 `recalc()` 在参数变化时计算,并通过 `onSave()` 保存参数、售价、划线价与汇率。左右布局、包装数据带入、费用明细和中俄币种对照值得保留。
- 旧公式把采购成本上的加价率叫“净利率”,使用固定 12%/18% 佣金和 3.5% 其他扣费;low/high/high2 不能表达新的 CEL 货值类型与运输方式,不适合直接迁入 Atlas。
- Atlas 目前的 SKU 摘要已有采购价输入、只读售价和“净利润 —”;定价工具还是占位页。已有 pricing 基础表,但预览服务、报价确认、汇率 Worker 和方案管理尚需实现。
- 现有 `pricing-confirms` 契约独立落库,而本提案建议随草稿保存确认;不能只改前端按钮文案,必须先调整保存事务与契约。
## 3. SKU 与弹窗交互
### 3.1 SKU 卡片
摘要行保留:采购价 CNY、销售价(目标店铺币种)、预估净利润 CNY、“计算”、“试算详情”。第二行以较轻的文字显示方案、采用的运输方式、报价状态;包装缺失时显示具体缺项和补充入口。
自动算价默认开启,作为用户的编辑偏好。有效值发生变化且字段失焦/按 Enter 后触发,不在每次键入时创建请求。采购价、有效包装规格、目标店铺、类目或 SKU 参数变化均使之前的结果失效;同值失焦不重复请求。包装继承关系仍由 SPU/SKU 原有模型解析。
SKU 状态为:未试算 → 计算中 → 已试算·待保存 → 已确认。缺少资料、请求失败或参数已变时显示可修复状态,不把旧利润继续当成当前结果。
“按方案计算全部”逐 SKU 返回结果;成功项更新表单,失败项保留输入并标出原因。手动售价不被批量推荐价覆盖,手动项按原售价重新核算利润。
### 3.2 手动售价
用户可直接修改售价,系统切换到“手动售价”模式,按这个价格计算物流适用性、佣金及利润。此后修改采购价或包装数据,保留售价,仅更新成本和利润。“使用建议售价”是显式退出手动模式的动作。
预估净利润只读,不接受手填。标签使用“预估净利润”;它是扣除已配置费用后的预计利润,不是账单结算利润。低于目标或为负时明确显示差额。首期建议亏损报价可保留为草稿,但不得自动成为可发布报价;后续若允许强制确认亏损价,需另行确定权限与确认规则。
### 3.3 试算弹窗
- 顶部:SKU 名称、当前目标店铺及币种、定价方案。进入时复制该 SKU 的表单输入与参数,取消不修改 SKU。
- 左侧:采购价、有效包装重量/尺寸、目标加价率、划线折扣率;高级费用折叠展示国内运费/贴单、广告、尾程、提现、其他费、汇率来源和临时覆盖。
- 页面中的加价率、折扣率、广告、尾程、提现、其他费用率及临时佣金率均按百分数展示和输入,统一保留两位小数;API 仍使用十进制比例字符串,例如页面 `50.00%` 对应请求 `"0.5"`。
- 右侧:大号销售价、划线价、预估净利润及利润占销售额比例;费用明细与规则来源按需展开。
- 物流比较:展示方案默认运输方式,同时允许比较其他可用方式。最低价只是标签,不自动替换方案指定的运输方式;指定方式不可用时要求用户选择。
- 修改参数后延迟约 400 ms 试算,输入未完整时不发送请求;取消旧请求并校验返回版本,防止旧结果覆盖新参数。
- 底部:取消 / 应用到 SKU。应用只更新当前 SKU 的待保存报价和本次参数,不修改门店默认方案。
- 用户在弹窗修正包装时,明确写入该 SKU 的包装覆盖值,不修改 SPU 默认规格;原型只演示数值,正式组件需显示继承来源并提供恢复继承操作。
### 3.4 独立定价工具与定价方案
独立“即时试算”和 SKU 弹窗使用同一输入组件、结果组件与服务端引擎。独立试算不要求已有商品,不提供“应用到 SKU”。
“定价方案”负责门店范围的默认扣费、利润目标、折扣、汇率策略和运输偏好;每个门店最多一个默认方案。新 SKU 首次计算时采用它,已有 SKU 继续使用自己的方案/本次覆盖。方案编辑不重算历史已确认报价。将临时参数另存方案应是独立明确动作,采购价和包装尺寸不随之保存。
## 4. 保存、变化与失效
| 操作或事件 | 表单结果 | 已确认快照 |
|---|---|---|
| 成本失焦,输入完整 | 自动预览并回填,标记待保存 | 保留 |
| 弹窗点击应用 | 覆盖当前 SKU 的待保存报价 | 保留 |
| 手改售价 | 保留手动价,重算费用/利润 | 保留 |
| 包装/类目/店铺变化 | 旧预览失效;重算或提示缺项 | 保留历史,新草稿标记未满足发布报价条件 |
| 方案或系统汇率更新 | 不静默替换已确认价格 | 保持原输入、版本、汇率与结果 |
| 点击商品页保存 | 服务端核对并确认本次报价 | 生成新不可变快照,并切换 listing 引用 |
| 预览过期或规则在保存前变化 | 重算并展示新旧价格/费用差异 | 未经重新接受不替换 |
| 取消弹窗/离开未保存页面 | 放弃相应临时修改 | 不变 |
保存不能因为某个尚未定价的 SKU 阻止保存整个商品草稿;该 SKU 可保存为“待定价”,发布时阻断。对于携带了报价确认意图的 SKU,只要存在过期/版本冲突,就返回具体 SKU 错误,整个保存事务回滚,不悄悄丢弃确认意图。界面可提供“仅保存草稿,稍后定价”的明确后续操作。
已确认报价不因时间经过而自动过期;15 分钟有效期只作用于未确认预览。影响报价的商品输入改变后,当前草稿必须重新确认。发布时检查快照与待发布输入一致,已导入 SKU 沿用既有编辑边界。
## 5. 后端及契约建议
维护者已批准以下变化:
1. **预览上下文**:扩展现有 preview 契约以识别 `profile_id + variant_id`,并接收明确的未保存采购价、包装覆盖和本次参数。服务端校验资源归属,并按与草稿保存相同的逻辑合并包装。未保存的新 SKU 需支持当前商品内的客户端草稿标识,保存时原子映射到真实 SKU ID;或首版在首次保存后才开放报价确认。
2. **两种定价意图**:支持目标加价率求售价,以及固定售价反算利润。输出统一的售价、划线价、利润、费用和规则快照。金额使用十进制定点计算,HTTP 仍传字符串。
3. **预览标识**:预览绑定用户/工作空间、平台/店铺、SKU、规范化输入摘要、规则/方案/汇率版本及有效期。可保存在有过期清理的 PostgreSQL 预览表;不得只凭客户端提供的金额确认,不以 Redis 作为确认事实来源。
4. **统一保存**:在 `editor-draft` 中增加每个 SKU 的可选 `pricing_selection`(preview id、候选 id、输入摘要),并为包含报价确认的保存提供幂等键。草稿、SKU、报价/候选快照、listing 售价与报价引用在同一事务提交,沿用 product/profile revision 乐观锁。
5. **复用确认逻辑**:将当前规划的 `pricing-confirms` 行为放入可被草稿保存事务调用的领域服务。是否保留独立 HTTP 入口需要统一决策;不能采用前端“先保存草稿,再逐个确认报价”来伪装原子保存。
6. **严格的同归属约束**:补齐报价与 product/profile/variant/listing 的复合外键和工作空间 RLS;现有基础 DDL 中 `pricing_quotes.product_variant_id` 仅是逻辑关联,不能直接当作完整领域约束。
7. **确认校验**:检查预览输入和待保存草稿一致、版本可用、候选有效、币种及舍入后结果一致;不在确认请求中重新抓取外部汇率。未变化的已确认 SKU 不重复创建报价。
8. **币种与单位**:售价和划线价以目标店铺支持的币种持久化,CNY 成本为统一输入,RUB 阶梯按本次汇率判定;明确现有 RUB-only 报价列与 CNY 店铺售价的映射。界面可切换展示单位,内部统一 g/mm 或规范化 kg/cm,禁止混算。
## 6. 计算引擎
复用旧项目的拆分方式与展示经验,重写纯函数计算器。服务端引擎是业务事实来源,前端只负责预览展示。所有入口调用同一引擎。
目标加价率基于采购成本:目标利润 = 采购成本 × 加价率。固定成本包含采购、最终跨境物流、国内运费/代贴单及固定附加费。各百分比费用基于最终销售价;划线价 = 销售价 ÷ (1 − 划线折扣率)。
引擎枚举“运输方式 × CEL 产品类型 × 佣金档”,计算售价后回验货值范围、重量、尺寸、计抛、佣金边界和最终舍入价格。固定售价模式直接以输入售价确定区间并计算利润。无自洽候选时返回原因,不任意选档或无限迭代。
CEL 表尚需真实账单复核;佣金映射与规则也需审核版本。开发可先使用明确标记的测试规则与方案固定汇率推进全链路;正式环境缺少有效规则时禁止伪造价格。CBR 更新、规则发布和过期提示按现有文档落地。
## 7. 开发顺序与验收
1. **确认交互及契约**:先确认自动失焦回填、手动价保护、随草稿保存这三个选择;再更新需求/API/contracts。影响 Web、API、Worker 汇率任务、database 和 contracts,不复用旧 SQLite 或无鉴权 API。
2. **引擎和规则**:实现 Decimal 计算、CEL/佣金版本读取、单位转换、候选枚举和固定价反算;测试价格跨档、重量/体积重边界、取整后跨档、总费率 ≥100%、负利润及不可报价。
3. **首个垂直交付**:一个已保存 SKU、一个门店方案、采购价失焦预览、弹窗改参数、统一保存、刷新重读及发布预检引用报价。此阶段就验证租户隔离、旧响应丢弃、幂等重放、并发版本冲突、确认失败整笔回滚、方案变更不改历史报价。
4. **扩展编辑效率**:多 SKU 批量试算、缺项定位、包装继承、新 SKU 保存映射、手动售价保护、预览过期后的差异接受。
5. **完整工具页**:复用同一组件和引擎补独立试算、方案 CRUD、默认方案、汇率同步及真实账单复核。试算记录列表后置。
首轮验收:修改采购价后能在 SKU 行看到新售价与利润;弹窗取消不改草稿;应用只改对应 SKU;成本变化不覆盖手动价;保存后重读能解释该价格所用的包装、汇率、佣金和物流规则;失败 SKU 不阻塞其他 SKU 的试算;缺少报价的草稿可保存但不可发布。