Skip to content

Package offer: parent-child orders, package constraints, and voucher fulfillment #238

Description

@xiaoland

Parent: #231
Depends on: #235

Summary

实现首个 Package Offer 能力,以泰岛蛋为 first application。该 issue 不重新实现 Commodity / SKU / Offer / Order / PaymentOrder 基础模型,而是消费 #235,负责套餐规则、父子订单编排、统一优惠分摊,以及通过 Entitlement/Voucher 作为平台履约方式让商家完成真实商品交付。

Voucher 是 Entitlement 的一种:它是可通过 QR/code 展示并由商家/Admin 核销的履约凭证。

Product Model

  • 套餐不是一种特殊 Commodity。
  • 套餐应建模为 PackageOffer:一个作用在多个 SKUs 上的 Offer,包含组合限制和优惠规则。
  • Offer 应用于 parent Order 或 user standalone Order;child Orders 通过 parent allocations 继承 parent Order 接受的 Offer 条款。
  • 多人可以合并购买以享受套餐/团购优惠。
  • 每个参与者独立选择并享用套餐范围内的一个或多个 SKU 商品单位。
  • 每个参与者独立支付自己享用商品的费用。
  • 选择必须满足套餐划定的商品种类、数量、组合等限制。
  • 套餐整体优惠统一作用于整组商品,并最终平均分摊到每个商品单位上。
  • 使用父子订单模式:draft parent order 承载多人选择草稿;离开 draft 后,parent order 表示一次 PackageOffer 接受结果和聚合快照,child orders 是每个参与者自己的订单。
  • Parent order 不能被直接支付,也不应该有 PaymentOrder。
  • Child order 通过 parent_id link 到 parent order;有 child 的 order 自动就是 parent order,不需要额外的 order type 字段。
  • Child order 不复制商品明细作为第二 source of truth;它引用 parent order allocation ids。
  • 没有 product-level 发起人/initiator 角色;每个 child order owner 只控制自己的 child order。
  • 每个 child order owner 自己点击“我选好了”离开 draft。
  • Parent order 在所有 child orders 都离开 draft 后自动离开 draft。
  • 用户选择商品时看到两个价格:正常商品叠加价格,以及叠加团购优惠后的预估价格。
  • 离开 draft 后才锁定最终价格并允许各自支付。
  • Entitlement 是本平台作为分销角色的履约权利;Taidaodan 使用 Voucher 作为可扫码/凭码核销的 Entitlement subtype;商家通过核销 Voucher 履约并交付真实商品。

Scope

  • Taidaodan PackageOffer as the first seeded scenario.
  • PackageOffer rule evaluation:
    • allowed SKU groups/options
    • min/max quantity
    • participant/item constraints
    • package completion rules
  • Parent-child order orchestration using Commercial foundation: Commodity, SKU, Offer, Order, PaymentOrder, and entitlement primitives #235 generic order primitives.
  • Draft parent order and draft child user orders created during package selection.
  • Child owner confirmation action: “我选好了”.
  • Automatic parent order draft exit after all child orders leave draft.
  • Parent order allocation rows for selected SKU units.
  • Child user order creation per participant by referencing a subset of parent allocation ids.
  • Package-level discount allocation:
    • percentage discount applies consistently to eligible items
    • fixed reduction is distributed evenly across eligible item units with deterministic remainder handling
  • Payment integration through Commercial foundation: Commodity, SKU, Offer, Order, PaymentOrder, and entitlement primitives #235:
    • each child user order has one PaymentOrder after leaving draft
    • parent order has no PaymentOrder
    • no PaymentOrder exists while an Order is DRAFT
    • no separate package-offer payment model
  • Entitlement issuance after the required child-order payment contract is satisfied; Taidaodan's Entitlement subtype is Voucher.
  • Item-unit redemption ledger for remaining entitlement from parent order allocations.
  • Admin QR scan redemption page/action, similar in principle to /bi?code=.
  • Redemption audit: redeemed_at, redeemed_by, redeemed item units.
  • PR-owned commercial attachment/link for surfacing the PackageOffer and resulting parent order from the PR surface.

Out of Scope

  • Implementing Commercial foundation: Commodity, SKU, Offer, Order, PaymentOrder, and entitlement primitives #235 Commodity/SKU/Offer/Order/PaymentOrder primitives.
  • Introducing a broad GroupBuy infrastructure domain.
  • Unified redemption of multiple Vouchers with temporary owner authorization; tracked separately in Unified voucher redemption: temporary owner authorization #239.
  • Generic voucher marketplace.
  • External Meituan/Dianping coupon import.
  • Restaurant POS integration.
  • Merchant settlement/accounting.
  • Mature anti-fraud tooling.
  • Voucher transfer/resale.
  • Repricing remaining paid child Orders after another child Order refunds before redemption.
  • Storing pr_id on Order, PaymentOrder, or Voucher/Entitlement records.
  • Adding an order type field solely to mark parent/child/standalone order state.
  • Product-level package initiator/facilitator role.

Acceptance Criteria

  • A relevant PR surface can expose the Taidaodan PackageOffer through a PR-owned commercial attachment.
  • Users can form draft parent/child Orders for the PackageOffer without a separate checkout-session object.
  • Each child order owner can confirm their own selection with “我选好了”.
  • Parent order leaves draft automatically after all child orders under it have left draft.
  • Parent order records canonical allocation rows for selected SKU units and discount allocation.
  • Child/user orders reference parent allocation ids and do not duplicate goods as a second source of truth.
  • Parent/child status is inferred from parent_id and children.
  • Parent order cannot be directly paid and does not have a PaymentOrder.
  • Package constraints are enforced before leaving draft and before payment.
  • Package-level discount is allocated evenly to eligible item units with deterministic rounding/remainder behavior.
  • Each child user order has exactly one PaymentOrder through Commercial foundation: Commodity, SKU, Offer, Order, PaymentOrder, and entitlement primitives #235 payment primitives after leaving draft.
  • The platform issues an Entitlement after the required child-order payment contract is satisfied; for Taidaodan this Entitlement is a Voucher.
  • Paid participants can view/present the entitlement.
  • Admin QR scan can partially redeem remaining entitlement by selected item units from parent order allocations.
  • Redemption is idempotent and audited.
  • If Voucher has not been redeemed at all, the parent package can be fully refunded as a bulk operation over child orders, though this is not the common path.
  • Before that child's allocation units are redeemed, a child Order can be refunded independently for personal or merchant reason; that child allocation is voided/refunded and remaining child Orders keep their locked discounted prices.
  • Commercial records do not link back to PR; PR owns the attachment/link to commodity/offer/order.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions