コンテンツにスキップ

顧客可視性(Customer Visibility)プラグイン

概要

顧客・顧客グループ・全対象(global scope)に対して、商品やキャンペーンなどの可視性や許可/拒否を制御するプラグインです。Shop API に可視性を考慮した取得系クエリを追加し、Admin API で表示制御ルールを管理します。

主要コンセプト

  • 対象者条件: 顧客・顧客グループ・顧客ステータス・売上掛率分類コードなど、誰に適用するかを表す typed condition
  • 商品対象条件: 商品バリアント・コレクション・キャンペーンなど、何を対象にするかを表す typed condition
  • 表示制御ルール: 対象者条件、商品対象条件、表示/非表示の判定、優先度を束ねる運用単位

運用上は次の 3 つの問いで考えると整理しやすいです。

  • 対象者条件 = 誰に適用するか
  • 商品対象条件 = 何を対象にするか
  • 表示制御ルール = どう振る舞うか

ポリシー capability contract

ポリシー種別ごとに「どの action / target を扱えるか」を descriptor として固定しています。

policyType capabilityKind 条件で使える target 評価対象 action
visibility decision productVariant / collection / campaign / campaignGroup productVariant / collection / campaign / campaignGroup decision
  • 未対応の action / target は保存時・preview 時に黙って捨てず、明示的にエラーにします。

payment policy は廃止: 旧 policyType = payment (capabilityKind: set-aggregation, action allow_payment_methods / deny_payment_methods) は PaymentMethodCollectionAssignment (PMCA) に統合済みで、Dashboard 上の policy 編集対象から除外されています。判定経路の詳細は Payment Eligibility Checkers を参照。

React Dashboard 上の見え方

ポリシー管理 UI では、内部フィールド名ではなく次の業務向けラベルで表示します。

内部値 / 概念 Dashboard 上の表示
policyType = visibility 表示制御
subjectScope = global 全対象
conditions 適用条件
actions 実行内容

slug 由来の internal name は主表示に使わず、顧客 3件表示制御 / 表示する / 対象: 顧客グループ 1件 のような派生ラベルを優先します。必要な場合のみ 内部名: ... を補助表示します。

表示制御ルール一覧の「対象者 / 商品対象」セル

/policies/visibility の一覧(rules.tsx)では、対象者 / 商品対象 の各アイテムを 次の方針で描画します。

  • アイテムのリンク化: 詳細ページを持つエンティティは値ラベルを詳細ページへの リンクにします。対応は 顧客 → /customers/$id顧客グループ → /customer-groups/$id商品バリアント → /product-variants/$idコレクション → /collections/$idキャンペーン → /commercial-rules/$idファセット(値 id で facet 詳細へ 飛べない)・キャンペーングループ(詳細ルート無し)・顧客ステータス / 売上掛率分類コード / 全対象(コード値)は遷移先が無いためテキストのまま。
  • 件数の絞り込み: 行が長くなりすぎないよう、対象者 / 商品対象 それぞれで 表示するアイテムを先頭から MAX_VISIBLE_CONDITIONS(既定 3)件までに制限し、 超過分は 他N件 として薄色で短縮表示します。上限は rules.tsx の定数 1 箇所で 調整できます。

visibility 用 conditions の受け入れ範囲

共有 PolicyConditions 型は commercial rule 向けに all / any / not の boolean combinator や customer / order / channel / shipping / selection / time など豊富なフィールドを持ちますが、visibility では conditions.targets のみ受け入れます。 これら以外のキーを送ると Unsupported policy condition keys for visibility: ... で reject されます。

conditions.targets で扱える target kind:

  • resourceSetIds — 旧データ互換の参照。通常の UI authoring では使わない
  • productVariantIds / collectionIds / campaignIds / campaignGroupIds — inline ad-hoc selector

facetValueIds は実運用 0 件のため 2026-05 のクリーンアップで撤去しました(descriptor から削除済み)。

判定仕様(visibility)

  • 既定動作: default deny(一致する allow がない場合は非表示)
  • 競合解決:
  • priority が高いポリシーを優先
  • 同一 priority かつ同一更新時刻では deny を優先(visibility/payment)
  • 判定アクション:
  • actions: [{ kind: "decision", effect: "allow" }]: 表示許可
  • actions: [{ kind: "decision", effect: "deny" }]: 表示拒否
  • 対象者の適用範囲:
  • subjectScope = global → 全顧客に適用
  • 顧客・顧客グループ・顧客属性条件のいずれかにマッチした顧客に適用(OR)

「表示制御」と呼ぶ理由

このポリシーは内部的に policyType = "visibility" で、対象外の顧客に対して カタログ表示と購入の両方をサーバ側でブロックする 不変条件を維持しています:

  • Storefront の商品取得導線(一覧 / 検索 / 詳細 / 関連 / お気に入り / クイックオーダー等)は visibilityPolicyEvaluator を経由してフィルタされる。
  • カート追加(addItemToOrder)は packages/plugins/src/standard-extensions/inventory/customer-addable-order.service.tsCustomerAddableOrderInterceptor が同じ visibility rule を再評価し、 対象外顧客が variantId を直接渡してきた場合でも reject する。

つまり「見えない ≡ 買えない」は obscurity ではなく enforce 済み。ただし RBAC や CustomerCategoryAccess とは責務が異なるため、UI / ドキュメントでは 「表示制御」 という名称に統一しています(旧称「アクセス制御」は廃止)。

React Dashboard での設定手順(Vendure Dashboard)

  1. ポリシー管理 > 表示制御ルール (/policies/visibility) で次を指定して作成する
  2. 種別 (policyType): visibility(Dashboard では 表示制御
  3. 適用対象: subjectScope = subjectSet または globalglobal全対象 と表示)
  4. 対象者条件: 顧客・顧客グループ・顧客ステータス・売上掛率分類コードなどを指定
  5. priority: 優先度
  6. 商品対象条件: 商品バリアント・コレクション・キャンペーンなどを指定
  7. 実行内容 (actions): [{ kind: "decision", effect: "allow" | "deny" }]

補足:

  • 表示制御ルール名は、slug 風の内部名よりも業務向けの名称を付けるほうが運用しやすいです。名称未設定でも UI は派生ラベルで補いますが、明示名があるほうが一覧で判別しやすくなります。

表示ロジックプレビュー

Vendure Dashboard の ポリシー管理 > 表示ロジックプレビュー (/policies/visibility-preview) は、同じ visibility rule 判定を起点別に確認する運用者向け画面です。

  • 顧客起点: 顧客を選び、その顧客に表示される商品・キャンペーン・適用ポリシーを確認する。
  • 商品起点: 商品バリアントまたは商品番号を選び、その商品が表示対象になる顧客・顧客グループ・適用ポリシーを確認する。
  • キャンペーン起点: campaign を選び、その campaign が表示対象になる顧客・顧客グループ・適用ポリシーを確認する。

キャンペーン起点は、commercial_rule_entity.kind = campaign の campaign ID を visibility target (campaign) として評価する。販促計算 (CommercialRulesPlugin) の適用結果を逆引きする画面ではなく、Storefront の campaign 導線を表示してよい対象者を visibility rule と同じロジックで確認するための画面です。

ローカルで確認用 seed を入れ直す場合は次を使う。

just vendure-seed-campaign-visibility-preview

この seed は visibility-preview-campaign を作り、対象者:個別のテスト顧客対象者:会員グループA(デモ) に対して表示可の visibility rule を作成する。

Storefront への適用範囲

visibility 判定は次の導線に適用されます。

  • ホームの商品表示
  • 商品一覧/検索結果
  • 商品詳細ページ(直リンク含む)
  • 商品詳細の関連商品
  • お気に入り一覧
  • カート内おすすめ商品
  • クイックオーダー(SKU照会)

次は適用対象外です(履歴情報保護のため)。

  • 注文履歴・注文詳細の過去購入商品表示

Storefront 回帰確認

  • policy / visibility の変更後は、apps/storefront/tests/e2e/products-page.real.spec.ts を使って、認証済み principal の /products 一覧に複数商品が表示されることを確認します。
  • ローカルで user1(test1@ritsubi-platform.com)を使って確認する場合は、Storefront と Vendure の env を同時注入したうえで次を実行します。
AWS_PROFILE=ritsubi AWS_SDK_LOAD_CONFIG=1 \
  node ./scripts/ops/secrets.mjs --env dev --services shared,storefront,vendure --allow-overrides -- \
  bash -lc 'cd apps/storefront && PORTLESS=0 PLAYWRIGHT_E2E_MODE=real PLAYWRIGHT_BROWSERS_PATH=0 E2E_LOGIN_EMAIL=test1@ritsubi-platform.com E2E_LOGIN_PASSWORD=DevCustomer123! pnpm exec playwright test tests/e2e/products-page.real.spec.ts --config playwright.config.ts --project=chromium'
  • Playwright webServer が vendure_test を再利用する場合でも、test customer seed は customerCode を stable key に既存顧客を再利用し、過去 run の email drift による duplicate key で停止しない前提です。

Shop API 拡張

  • productVariantsWithVisibility: ポリシーに基づいた商品バリアント一覧を取得
  • evaluateVisibility: 顧客と対象を指定して可視性の判定結果を確認
  • validateVisibility: 対象 ID の可視性判定を行い、有効なもののみ返却

Admin API 拡張

  • ポリシー: policies, evaluatePolicy, customerPolicyOverview, productPolicyOverview, campaignPolicyOverview
  • 表示制御ルールの CRUD ミューテーション

PolicyevaluatePolicy は判定結果だけを返します。 capability metadata(capabilityKind / supportedActionKinds / supportedConditionTargetKinds / supportedEvaluationTargetKinds / explainable / previewable / reverseLookupable)は visibility 一種類しか policyType がない現状では冗長なため、2026-05 に GraphQL から撤去しました。 Dashboard 側はローカル descriptor (policy-display-utils.ts) を正本にしています。

互換性(重要)

  • previewVisibilitypreviewPolicy は廃止済みです。
  • 現行実装は evaluateVisibilityevaluatePolicy のみを提供します。
  • 後方互換エイリアスは提供していないため、呼び出し側は新しい API 名へ更新が必要です。

evaluatePolicy preview / explain

evaluatePolicy は、最終判定に加えて trace を返します。

  • trace.matchedSubjectSets: 一致した対象者条件(旧 schema 名)
  • trace.candidatePolicies: 候補になった Policy 一覧
  • trace.selectedPolicyId: 最終的に採用された Policy
  • trace.steps: 人間が読める説明ステップ

決済可否の確認は evaluatePolicy ではなく、Vendure 標準 eligiblePaymentMethods および Dashboard /payment-methods/collection-mapping 一覧で行います (Payment Eligibility Checkers 参照)。 trace.allowPaymentMethodCodes / trace.denyPaymentMethodCodes は payment policy 廃止に伴い旧 schema の遺物です。

データ保存

以下のエンティティで管理します。

  • VisibilityRule
  • VisibilityConditionGroup
  • VisibilityCondition
  • 旧データ互換のため、一部 API / schema 名には Policy 由来の名前が残る

連携

  • 決済可否判定: 本プラグインの policy ではなく、 paymentCollectionMethodEligibilityChecker + PaymentMethodCollectionAssignment が正本。詳細は Payment Eligibility Checkers
  • Vendure Dashboard: ポリシー管理 UI が Admin API を利用します。

動作確認(API)

可視性判定の詳細確認には evaluateVisibility を利用します。

Shop API: 自分(認証済み customer)に対する評価のみ可能。customerId 引数は受け付けません。

query EvaluateVisibility($productVariantId: ID, $sku: String) {
  evaluateVisibility(productVariantId: $productVariantId, sku: $sku) {
    allowed
    matchedPolicyId
    reason
  }
}

Admin API: preview 用に任意の customerId を指定して評価できます(Permission.SuperAdmin などポリシー管理者権限が必要)。

query EvaluateVisibilityAsAdmin($customerId: ID, $productVariantId: ID, $sku: String) {
  evaluateVisibility(customerId: $customerId, productVariantId: $productVariantId, sku: $sku) {
    allowed
    matchedPolicyId
    reason
  }
}
  • 入力: 判定対象(productVariantId または sku)。Admin では判定主体として任意の customerId を指定可。Shop では常に呼び出し元 customer を主体とする。
  • 出力: 可視性(allowed)と判定理由(reason)、一致ポリシーID

補足として、一覧のフィルタ用途では従来どおり validateVisibility も利用できます。

対象者条件タイプ一覧

タイプ 照合キー 主な用途
user Customer.id 特定顧客への個別適用
customerGroup CustomerGroup.id 手動グループ・auto group による一括適用
customerStatus Customer.customFields.customerStatus ステータス(VIP・定期会員など)による分類
salesRateClassCode Customer.customFields.salesRateClassCode SMILE 売上掛率分類コードによる分類(掛率ルール適用に主に使う)

salesRateClassCode タイプは SMILE 得意先インポートで保存された掛率分類コードと照合する。コードは自由入力形式で複数登録可能(例: A01, B02)。詳細は pricing-system.md の「売上掛率分類コードによる顧客ターゲット指定」を参照。

auto CustomerGroup との関係

customerGroup 条件は、手動メンバの CustomerGroupauto group(述語ベース)の両方を区別せず参照できる。auto group は CustomerGroup.customFields.isAuto=true で識別され、predicate JSON によって Customer 保存時にメンバが自動更新される。本プラグインの可視性判定(evaluateVisibility / validateVisibility)は通常の CustomerGroup ID で評価するため、auto / manual の区別を意識する必要はない。仕様 SSOT: docs/specifications/2026-05-predicate-based-customer-group.md

関連ドキュメント