コンテンツにスキップ

ProductVariant の購入不可在庫しきい値

背景

在庫数が 0 になる直前まで販売すると、注文確定時の在庫引当てで不足が発生しうる。 そのため、ProductVariant ごとに安全余裕を持たせた「購入不可しきい値」を設定できることを正とする。

要件

  • ProductVariant ごとの購入不可しきい値は、Vendure 標準の outOfStockThreshold を正本とする。独自 custom field は追加しない。
  • Variant ごとに個別値を使う場合は useGlobalOutOfStockThreshold = false とし、その variant の outOfStockThreshold を設定する。
  • Shop API では、saleable stock がその variant の outOfStockThreshold 以下になった時点で stockLevel = OUT_OF_STOCK として扱う。
  • Storefront の商品カード・商品詳細・クイックオーダーは stockLevel = OUT_OF_STOCK を購入不可条件として共通利用し、 threshold 到達時点で同じ基準で購入不可にする。
  • 商品カード・商品詳細のカート投入 CTA は apps/storefront/src/lib/cart-eligibility.tsresolveVariantCartEligibility() を共通入口にする。個別 component が stockLevel や配送モード、購入可能期間、数量制限をそれぞれ独自順序で判定してはならない。
  • Storefront の購入可否判定では、stockLevel は常に ProductVariant の 状態を正本とする。bundle variant であっても OUT_OF_STOCK の場合は 通常 variant と同じく購入不可にし、商品カード・商品詳細・クイックオーダーで 個別の例外分岐を作らない。
  • 実装上の在庫切れメッセージ・ボタンラベル・低在庫停止メッセージの解決は apps/storefront/src/lib/out-of-stock-message.ts を共通入口とする。 UI コンポーネントや Quick Order 側で isBundleProduct を見て在庫切れを 上書きしてはならない。
  • stockLevelOUT_OF_STOCK/LOW_STOCK/IN_STOCK)の粗いラベルでは 「あと何個買えるか」が分からないため、Shop API は ProductVariant.saleableStockLevel (予約差引後の販売可能在庫数, Int!)も返す。正本は packages/plugins/src/standard-extensions/inventory/saleable-stock.shop-resolver.tsProductVariantService.getSaleableStockLevel に委譲)とし、storefront は この実在庫数を事前のカート投入判定に使う。
  • 事前(クリック前)の数量上限は、購入上限(maxPerOrder / 期間ルール)に加えて saleableStockLevel(カート内既存数量を差引いた残数)でも丸める。残数が最低購入 数量に満たない場合は resolveVariantCartEligibility() が事前に購入不可へ倒す。 saleableStockLevel を取得しない surface(= 値が null)では事前在庫判定を スキップし、従来どおりサーバ拒否で吸収する。
  • addItemToOrder / adjustOrderLineInsufficientStockError を返した場合は サーバが返す quantityAvailable(実際に追加できた/できる数量)を使い、数量ステッパー 上限を実在庫に丸めて再試行可能にする(CTA を恒久 disable しない)。残数が 0 なら 在庫切れとして購入不可へ倒す。quantityAvailable を返さないレスポンスは従来どおり resolveAddItemToOrderRejectionReason() で不可理由へ変換し、CTA を再クリックできない 状態へ倒す(安全側 fallback)。いずれも Shop API の最終判定を UI に反映するための fail-closed UX であり、server-side 在庫検証は引き続き必須とする。
  • threshold を上回っていても、stock display strategy により LOW_STOCK 表示になることは許容する。購入不可の境界は OUT_OF_STOCK への遷移点とし、LOW_STOCK/IN_STOCK の範囲内では saleableStockLevel で 数量上限を丸めて要求超過を事前に防ぐ。

受け入れ観点

  1. Vendure Dashboard の Product Variant 編集で outOfStockThreshold を設定できる。
  2. 在庫数と同じ値を threshold に設定すると、Shop API の stockLevelOUT_OF_STOCK になる。
  3. Storefront では該当 variant をカート追加できず、クイックオーダーでもエラーになる。
  4. bundle variant でも stockLevel = OUT_OF_STOCK なら、商品カード・商品詳細・クイックオーダーで同じ在庫切れ扱いになる。
  5. 在庫が LOW_STOCK/IN_STOCK でも、数量ステッパーの上限が saleableStockLevel(カート内既存数量を差引)に丸められ、実在庫を超える数量は選べない。
  6. 実在庫を超える数量で addItemToOrderInsufficientStockErrorquantityAvailable 付き)を返した場合、CTA は恒久 disable されず、数量上限が実在庫数に丸められて再試行できる(残数 0 のときのみ在庫切れ表示)。