コンテンツにスキップ

キャンペーンと価格ルールの責務分離(補足仕様)

目的

キャンペーン.xlsx の運用と、Vendure 実装(Campaign / Policy / Pricing)の責務を明確化する。

本書は元資料を改変せず、実装上の解釈を補足する。

参照元

  • docs/specifications/キャンペーン.xlsx
  • docs/specifications/BtoB発注システム要件 (最新).xlsx
  • docs/specifications/2026-05-campaign-target-product-selection.md

設計方針(決定)

  1. 恒常的な価格制御は pricing rule で扱う
  2. 期間付き販促は campaign で扱う
  3. 可視性/決済の許可制御は access policyvisibility / payment)で扱う

責務定義

価格ルール(pricing rule)

  • 顧客・顧客グループ・グローバルに対する恒常価格制御
  • 商品/バンドル商品/コレクション単位の恒常的な割引率設定
  • 期間限定販促、特典選択、購入上限リセットは担当しない

キャンペーン(campaign)

  • 期間付きの販促ルール
  • 金額・数量しきい値での適用判定
  • 無償特典(固定/変動/選択式)と割引の付与
  • 仮想商品を使ったマイナス価格相当の注文調整
  • 達成までの進捗表示(残額/残数)
  • 顧客単位上限と手動リセット
  • campaign は販促施策の概念であり、価格変更はその実現手段の一つに過ぎない

用語整理(2026-03)

業務概念

  • pricing rule: 恒常価格・得意先別価格・常設の価格置換を扱う
  • campaign: 期間販促・特典・おまけ・注文調整を扱う
  • campaign page: 顧客向けの特設ページ・一覧導線・専用URLを扱う
  • access policy: 可視性・決済の許可/拒否を扱う

実装概念

  • CommercialRulesPluginpricing rulecampaign共通エンジン で評価する
  • campaign pagecampaigns 親配下の Vendure Collection を正本にし、WordPress は本文の任意ソースとして連携する
  • Policyaccess policy を評価する
  • Admin API / Shop API の内部名称は commercialRule(s) のまま維持する
  • React Dashboard の内部パスは /commercial-rules を維持し、業務上の導線は「価格・販促 > 価格・販促ルール」とする
  • React Dashboard の ポリシー管理 には、表示制御ルールと表示ロジックプレビューを置く
  • campaign page の運用導線は専用画面を増やさず、Collection 管理を使う

業務概念と実装概念の対応

業務概念 業務上の意味 実装上の扱い React Dashboard 上の扱い
Pricing Rule 恒常価格・得意先別価格・常設の価格置換 CommercialRulesPlugin の価格系 action で評価する 「価格・販促 > 価格・販促ルール」で Campaign と共通管理
Campaign 期間販促、特典ギフト、仮想商品による注文調整、進捗/上限管理 CommercialRulesPlugin の販促系 action と条件で評価する 「価格・販促 > 価格・販促ルール」から操作する
Campaign Page 顧客向けのキャンペーンページ、一覧導線、専用URL campaigns 親配下の Collection + WordPress 本文任意連携 Collection 管理から操作する
Access Policy 可視性・決済方法の許可/拒否 表示制御ルール / PMCA で評価する /policies / /payment-methods/collection-mapping で管理する

実装ルール(2026-02)

キャンペーン対象の指定

  • 商品単位: ProductVariant(SKU)
  • バンドル商品: customFields.setComponents が設定された親 ProductVariant
  • バンドル商品も 独立キーではなく ProductVariant として指定する
  • 対象商品の選定・除外判断・変更管理は 2026-05-campaign-target-product-selection.md を正本にする

Campaign conditions 拡張キー

  • targets.productVariantIds: string[]
  • targets.productVariantIds には通常 SKU とバンドル親 ProductVariant の両方を含めてよい。
  • targetBundleProductVariantIds は過去 migration で targets.productVariantIds へ統合済みであり、保存・更新・評価のいずれにも使用しない。

Campaign conditions 汎用 DSL

  • Campaign の conditions は固定キーに加えて、次の条件ツリーを持てる。
  • all: CampaignConditions[]
  • any: CampaignConditions[]
  • not: CampaignConditions
  • MVP で React Dashboard から編集できる条件は次の通り。
  • order.quantity.min/max
  • order.subtotal.min/max
  • customer.customerGroupIds
  • customer.fields[]
  • shipping.modes
  • 旧互換のトップレベル条件キー(minAmount, maxAmount, minQuantity, maxQuantity)は保存・更新・評価のいずれにも使用しない。
  • customer.fields[] は次の演算子を許可する。
  • eq, neq, gt, gte, lt, lte, in, not_in, contains
  • MVP の顧客フィールド対象は次の通り。
  • customerStatus
  • collectionMethod
  • discountRate
  • billingCustomerCode
  • salesRateClassCode
  • 対象商品の絞り込みは targets.productVariantIds を正本とし、通常 SKU とバンドル親 ProductVariant を同じ配列で扱う。
  • 旧互換入力として targetBundleProductVariantIds が渡されても runtime では読替しない。必要な場合は migration / repair で targets.productVariantIds に正規化する。

Campaign benefits 拡張キー

  • priceRanges: CampaignPriceRange[]
  • basis: "quantity" | "subtotal"
  • unitPrice: number
  • minQuantity?, maxQuantity?
  • minSubtotal?, maxSubtotal?
  • description?
  • priceRanges は期間限定販促における「このレンジならこの単価」を表す。
  • 同時に通常の discountType / discountValue を持ってよいが、固定単価レンジを優先して割引額を計算する。
  • ギフト特典は giftItems[] を正本とし、旧互換の giftProductCodes / giftQuantity は保存・更新・評価のいずれにも使用しない。
  • giftItems[] で付与するおまけ商品は OrderLine として明細追加する。
  • 付与されたおまけ商品の販売単価・明細価格は、商品マスタ価格に関わらず 0円 とする。
  • おまけ商品の無償化は注文行価格を正本とし、注文全体を相殺するための別値引きサーチャージを正本にしない。

Catalog 割引表示フラグ(2026-06)

  • カート投入前の商品一覧・商品詳細・検索などでは、既定で上代単価(定価)のみを表示する。
  • campaign の割引を回遊面でも明示したい場合だけ、CommercialRule.catalogDiscountDisplayEnabled を有効にする。
  • このフラグが有効な campaign action が適用され、かつ最終価格が上代より低い場合のみ、Storefront の pre-cart 面で %OFF と元価格打ち消し線を表示する。
  • pre-cart 面では、このフラグが有効でも価格内訳 hover / click / i 詳細は表示しない。価格内訳はカート、checkout、注文確認、注文履歴などのカート以降に限定する。
  • pricing rule、SMILE 単価マスタ、顧客別価格、標準掛率はこのフラグの対象外。仕入れ価格に近い標準単価・顧客別単価を回遊中に露出しないため、価格差から割引表示を推測しない。

レンジ価格の責務分離

  • 期間限定の特売・販促単価は campaign.priceRanges で扱う。
  • 恒常価格・得意先別価格・常設の価格置換は pricing rulereplace_price 系アクションで扱う。
  • したがって「価格レンジ」という表現でも、期間付き販促かどうかで責務を分ける。

決済許可制御(collectionMethod)の扱い

  • collectionMethod の決済可否は PaymentMethodCollectionAssignment (PMCA) を正本に表現する。PMCA は「回収方法コード × 許可 PaymentMethod codes」を直接保持 するエンティティで、AccessPolicy や表示制御ルールは経由しない。
  • 顧客の collectionMethodCustomer.customFields.collectionMethod を正本入力 とし、SMILE CSV 上の 回収方法 ヘッダー(コード側)を SMILE 取込が書き込む。 Dashboard 表示項目は 回収方法(SMILE) / 回収方法名(SMILE) を正とする。
  • 決済方法(paymentMethod)は PMCA の allowedPaymentMethodCodes で指定し、 Dashboard /payment-methods/collection-mapping で編集する。
  • 判定は paymentCollectionMethodEligibilityCheckerpaymentOptions.paymentMethodEligibilityCheckers 経由で行う。
  • 将来要件の追加は、原則として PMCA のエンティティ更新(コード追加・許可決済方法 の差し替え)で対応する。

旧仕様(廃止): 以前は policyType = payment の AccessPolicy に allow_payment_methods / deny_payment_methods action を持たせ、subject 側で [決済ポリシー:回収方法]:1〜4 CustomerGroup を使っていたが、PMCA 移行で削除。 移行 migration は apps/vendure-server/src/migrations/1779800400000_migrate_payment_policies_to_collection_assignments.ts 参照。

クーポン(Promotion)とキャンペーン(campaign)の使い分け(2026-05)

決定

要件『BtoB発注システム要件』#59 のキャンペーンコードは、適用トリガで 2 系統に分離する。

  • 手入力クーポンは Vendure 標準 Promotion を正本にする。
  • 顧客/営業がコードを入力して適用する割引。
  • 使用上限・有効期限・「注文検索」でのコード抽出は Vendure 標準機能を使う。
  • 自動適用の販促(コード不要)は campaignCommercialRulesPlugin)を正本にする。
  • 期間販促・数量/金額しきい値・おまけ(giftItems)・進捗/上限・仮想商品による注文調整。
  • 顧客にコードを見せず自動適用し、注文へキャンペーンコードを登録」(#94-95) は campaign 側の責務とする。Order の customField にキャンペーンコードを記録し、成果抽出に使う。 Vendure 標準 Promotion の coupon code では代替しない。

Promotion の再有効化(最小範囲)

  • promotionOptions.promotionConditions / promotionActions の空殻化([])を解除する。
  • 登録するのは手入力クーポン割引に必要な標準アクションのみに限定する。
  • orderPercentageDiscount / orderFixedDiscount / productsPercentageDiscount
  • 数量割引・おまけ等、campaign と責務が重複するアクションは Promotion 側に登録しない。
  • Dashboard 標準 Promotion ナビは、専任責務(手入力クーポン)を持つため表示する(非表示化しない)。

併用と重ね順

  • クーポンと campaign基本併用可能とする。
  • 重ね順: campaign(価格エンジン)で行価格を確定したに、クーポン(Promotion)を最後に適用する。
  • 下限: クーポン+campaign 割引+おまけの重複適用後も、注文行・注文合計が負値にならない floor を必須とする。
  • 個別排他: 既定は併用可。例外として併用を排他にしたい場合の precedence は「クーポン優先(campaign が譲る)」を既定とする。
  • 排他指定が有効なクーポンが注文に適用されている間は、campaign の割引・おまけ(surcharge / giftItems)を withhold する。
  • 顧客が入力したクーポンを checkout で剥がす/拒否することは UX 事故になるため行わない。silently 抑制してよいのは自動適用の campaign 側のみ。
  • 排他フラグ自体の保存先(CommercialRuleEntity 新カラム or Promotion customField)・Admin API・Dashboard UI は、最初の具体的な排他ケースが発生した時点で実装する。 既定が併用可で機能するため、例外要件が未確定の段階でスキーマ/UI を先行実装しない(YAGNI)。

別概念(混同注意)

  • クーポン割引用仮想商品 800000882026-03-virtual-product-assignment-settings.md)は、 SMILE 注文 CSV のクーポン割引行を取り込むためのデータ連携であり、上記ランタイム 2 機構とは別レイヤー。

未確定(クライアント確認)

  • 手入力クーポンの割引種別(注文%引き/固定額引き/対象商品%引き 等)は要件未記載。 確定までは標準アクション 3 種を登録し、クーポン作成時に Dashboard で選択する運用とする。

残課題の解消状況(2026-05-29)

実装・staging 実機検証(floor)完了後の残課題を、投機実装を避けて以下のとおり確定する。

  • 併用排他フラグ(フェーズ3): precedence は「クーポン優先(campaign が譲る)」で確定済み。 実カラム/Admin API/Dashboard UI の実装は、最初の具体的な排他ケースが発生した時点で行う(YAGNI)。 既定の併用可で運用上問題は出ないため、投機的なスキーマ/UI 追加はしない。
  • クーポン割引種別: クライアント確認待ち。標準 3 アクション+Dashboard 選択の暫定運用で機能するため、 コード対応は不要。確定後に登録アクションを絞るかは運用判断。
  • floor の厳密ゼロ化: floor は「合計を負にしない」契約を満たす(staging 実機で確認)。 割引が注文額を大きく超える極端ケースでのみ厳密 0 でなく小さい正値へ補正されるが、常に非負(安全側)。 realistic な割引額では floor 自体が発火しない。反復収束は決済計算の安定性リスクがあるため、 現挙動を accepted とする
  • キャンペーンコードの注文記録(#94-95)= SMILE 受注CSV へ出力(2026-05-29 マッピング確定):
  • SMILE 受注取込CSV の 摘要 / 摘要1 / 摘要2 列(位置 32-34)にキャンペーン情報を行ごとに出力する。 SMILE 取込は位置ベースのためヘッダー名は 摘要/摘要1/摘要2 のまま保持し、値のみを変更する (運用上は「適用 / 適用1 / 適用2」と呼ぶ=同音異字。ヘッダーは改名しない=既存フォーマット非破壊)。
    • 摘要(適用) = 業務キャンペーンコード(適用された campaign の CommercialRule.code
    • 摘要1(適用1) = キャンペーン内容の説明文字列(その1)
    • 摘要2(適用2) = キャンペーン内容の説明文字列(その2)
  • 行ごと(OrderLine 単位)に、その行へ適用された campaign の code / 説明を出力する。 キャンペーン未適用の行は空文字(旧 placeholder の "0" は廃止)。
  • 出所の整理: appliedCampaigns(#803 削除済み)は rule ID(UUID) の dead field でこれとは別物。 本要件は CommercialRule.code(業務コード) を行ごとに出力するもので、surcharge の ruleId(UUID) や 仮想商品コードとは別軸。
  • 手入力クーポン(Promotion)の couponCode はここには出さない(摘要/適用 は適用された campaign(CommercialRule) の業務コードを正とする。クーポン単独適用行の扱いは別途追補)。

判断基準(運用)

次の条件に 1 つでも当てはまる場合は campaign を優先する。

  • 期間限定である
  • しきい値(数量/金額)達成が必要
  • 無償特典や選択式特典がある
  • 上限管理・例外管理が必要

次の条件に当てはまる場合は pricing rule を優先する。

  • 常時適用される価格調整
  • 顧客属性に応じた恒常割引
  • 監査可能な権限制御として保持したい価格上書き

備考

  • 元資料(xlsx/pdf)は編集禁止。要件変更は本補足仕様に追記し、必要に応じて実装仕様へ反映する。