# 画面設計書 38-ダイナミックグループ

## 概要

本ドキュメントは、Shopware 6 管理画面（Administration）のダイナミックグループ（プロダクトストリーム）管理画面の設計仕様を定義します。この画面は動的な商品グループを条件ベースで定義・管理するための機能を提供し、自動的に商品を収集するルールを作成します。

### 本画面の処理概要

**業務上の目的・背景**：ダイナミックグループ（プロダクトストリーム）は、条件に基づいて自動的に商品を収集する機能です。「新着商品」「セール商品」「特定カテゴリの商品」など、条件を定義することで手動での商品選択なしに商品グループを作成できます。これにより、プロモーション管理やCMSでの商品表示が効率化されます。

**画面へのアクセス方法**：左側メインナビゲーションの「カタログ」>「ダイナミックグループ」をクリックしてアクセスします。URLパスは `/admin#/sw/product/stream/index` です。権限 `product_stream.viewer` が必要です。

**主要な操作・処理内容**：
1. ダイナミックグループ一覧の表示：ページネーション付きで一覧表示
2. グループ検索：キーワードによる検索
3. 並び替え：作成日時（デフォルト降順）でソート
4. インライン編集：一覧上で直接グループ名を編集
5. グループ詳細編集：詳細ページでの条件設定
6. グループ複製：既存グループのクローン作成
7. グループ削除：不要グループの削除（カテゴリ使用チェック付き）
8. ステータス表示：条件の有効/無効状態の表示

**画面遷移**：グループ名をクリックするとグループ詳細編集画面に遷移します。「新規ダイナミックグループ」ボタンでグループ作成画面に遷移します。

**権限による表示制御**：
- `product_stream.viewer`: ダイナミックグループ一覧の閲覧
- `product_stream.editor`: インライン編集
- `product_stream.creator`: 新規グループ作成ボタンの表示
- `product_stream.deleter`: 削除ボタンの表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | ダイナミックグループ | 主機能 | 条件ベースの動的商品グループ管理 |

## 画面種別

一覧

## URL/ルーティング

- **URLパス**: `/admin#/sw/product/stream/index`
- **ルート名**: `sw.product.stream.index`
- **モジュール名**: `sw-product-stream`
- **エンティティ**: `product_stream`

### サブルート

| ルート名 | パス | コンポーネント | 説明 |
|---------|------|---------------|------|
| sw.product.stream.create | `/create` | sw-product-stream-detail | 新規グループ作成 |
| sw.product.stream.detail | `/detail/:id` | sw-product-stream-detail | グループ詳細編集 |

## 入出力項目

### 検索入力項目

| 項目名 | 項目ID | 入力形式 | 説明 |
|--------|--------|----------|------|
| 検索キーワード | term | テキスト | グループ名で検索 |

## 表示項目

### 一覧カラム

| 項目名 | property | 表示形式 | インライン編集 | デフォルト表示 | 説明 |
|--------|----------|----------|---------------|---------------|------|
| グループ名 | name | テキスト+リンク | 可（string） | Yes | 詳細へのリンク、幅250px |
| 説明 | description | テキスト | - | Yes | グループの説明、幅250px |
| 更新日時 | updatedAt | 日時 | - | Yes | 最終更新日時 |
| ステータス | invalid | バッジ | - | Yes | 条件の有効/無効状態 |

## イベント仕様

### 1-ダイナミックグループ一覧取得（getList）

画面表示時および検索・ページング変更時に実行：

1. `Criteria` に基づいてクエリを構築
2. カテゴリ読み取り権限がある場合、カテゴリ数の集計を追加
3. 自然ソート（作成日時）を設定
4. 検索条件がある場合はクエリスコアを追加
5. 新しい検索キーワードの場合はソートをリセット
6. プロダクトストリームリポジトリで検索を実行
7. 結果を `productStreams` に格納、`total` を更新

### 2-インライン編集保存（onInlineEditSave）

一覧上での直接編集保存時の処理：

1. 編集内容の保存を実行
2. 成功時：成功通知を表示（グループ名を含む）
3. 失敗時：一覧を再取得し、エラー通知を表示

### 3-グループ削除失敗（onDeleteItemFailed）

削除が失敗した際の処理：

1. 削除対象のストリームを取得
2. カテゴリ集計を確認
3. カテゴリで使用されている場合：使用数を含むエラーメッセージを表示
4. それ以外の場合：一般的なエラーメッセージを表示

### 4-グループ複製（onDuplicate）

グループの複製処理：

1. クローン動作を設定（子要素含む、名前に「コピー」を追加）
2. リポジトリのcloneメソッドを呼び出し
3. 成功時：新しいグループの詳細画面へ遷移
4. 失敗時：エラー通知を表示

### 5-言語切替（onChangeLanguage）

表示言語を変更した際の処理：

1. `getList()` を呼び出して一覧を再取得

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | product_stream | SELECT | ダイナミックグループ一覧の取得 |
| 一覧表示 | category | SELECT | カテゴリ使用数の集計（権限がある場合） |
| インライン編集 | product_stream | UPDATE | グループ名の更新 |
| グループ削除 | product_stream | DELETE | グループの削除 |
| グループ複製 | product_stream | INSERT | グループのクローン作成 |

### テーブル別更新項目詳細

#### product_stream

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, name, description, invalid, updated_at, created_at | ページネーション、ソート条件 | 一覧取得 |
| UPDATE | name | インライン編集値 | インライン編集時 |
| DELETE | id | 指定ID | 削除時（カテゴリ使用チェック） |
| INSERT | * | クローン元からコピー | 複製時 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|----------|--------------|
| sw-product-stream.detail.messageSaveSuccess | 成功 | インライン編集成功 | {name}が保存されました |
| sw-product-stream.detail.messageSaveError | エラー | 保存失敗 | 保存中にエラーが発生しました |
| sw-product-stream.general.errorCategory | エラー | カテゴリ使用中で削除不可 | {name}は{count}個のカテゴリで使用されているため削除できません |
| global.notification.unspecifiedSaveErrorMessage | エラー | 複製失敗 | 保存中にエラーが発生しました |

## 例外処理

| 例外種別 | 発生条件 | 処理内容 |
|---------|---------|---------|
| 検索結果なし | 条件に一致するグループなし | 空の状態を表示 |
| 削除制約エラー | カテゴリで使用されているグループ | カスタムエラーメッセージ（使用カテゴリ数含む） |
| インライン編集エラー | バリデーション失敗 | エラー通知表示、一覧再取得 |
| 複製エラー | クローン処理失敗 | エラー通知表示 |

## 備考

- デフォルトソートは作成日時の降順（DESC）
- カテゴリ読み取り権限がある場合のみ、カテゴリ使用数の集計を実行
- 複製時は名前に「コピー」が自動追加される
- `invalid` フラグは条件が有効かどうかを示す（true = 無効な条件）
- `dateFilter` は v6.8.0 で非推奨

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

プロダクトストリームエンティティの構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | default-search-configuration.js | `src/Administration/Resources/app/administration/src/module/sw-product-stream/default-search-configuration.js` | 検索対象フィールドの設定 |

**読解のコツ**: プロダクトストリームは`api_filter`（条件）と`filters`（フィルタ設定）を持ちます。`invalid`フラグが`true`の場合、条件に問題があることを示します。

#### Step 2: エントリーポイントを理解する

モジュール登録とルーティング設定を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-product-stream/index.js` | モジュール登録、ルート定義、権限設定 |

**主要処理フロー**:
1. **26行目**: `Module.register('sw-product-stream', {...})` でモジュール登録
2. **38-47行目**: `routes.index` で一覧ページのルーティング定義
3. **44行目**: `meta.privilege: 'product_stream.viewer'` で閲覧権限を設定
4. **71-81行目**: ナビゲーションメニューの定義

#### Step 3: 一覧コンポーネントを理解する

ダイナミックグループ一覧ページのメインロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-product-stream/page/sw-product-stream-list/index.js` | 一覧表示のdata、computed、methodsの実装 |

**主要処理フロー**:
- **27-35行目**: `data()` で状態変数を定義
- **44-47行目**: `productStreamRepository` でリポジトリを取得
- **77-121行目**: `getList()` でグループデータを取得（カテゴリ集計含む）
- **83-93行目**: カテゴリ読み取り権限がある場合の集計追加
- **123-153行目**: `getProductStreamColumns()` で一覧カラムを定義
- **165-195行目**: `onDeleteItemFailed()` で削除失敗時のカテゴリ使用エラー処理
- **203-230行目**: `onDuplicate()` でグループ複製処理

### プログラム呼び出し階層図

```
sw-product-stream (モジュール)
    |
    +-- sw-product-stream-list (一覧ページコンポーネント)
           |
           +-- getList()
           |      +-- [category:read権限あり] aggregation追加
           |      +-- addQueryScores()
           |      +-- productStreamRepository.search(criteria)
           |
           +-- onInlineEditSave(promise, productStream)
           |      +-- [成功] createNotificationSuccess()
           |      +-- [失敗] getList() + createNotificationError()
           |
           +-- onDeleteItemFailed({ id, errorResponse })
           |      +-- aggregations.product_stream.buckets確認
           |      +-- [カテゴリ使用中] createNotificationError(count含む)
           |
           +-- onDuplicate(item)
           |      +-- productStreamRepository.clone()
           |      +-- router.push() → 詳細画面
           |
           +-- onChangeLanguage()
                  +-- getList()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

検索キーワード  ───▶      sw-product-stream-list     ───▶   グループ一覧表示
ページ番号                ・Criteria構築                     カテゴリ使用数
                          ・aggregation追加（権限時）
                          ・productStreamRepository.search()

複製ボタン     ───▶       onDuplicate                ───▶   詳細画面へ遷移
                          ・clone()
                          ・router.push()

削除失敗      ───▶        onDeleteItemFailed         ───▶   エラー通知
                          ・カテゴリ使用数確認              （使用数含む）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `src/Administration/Resources/app/administration/src/module/sw-product-stream/index.js` | ソース | モジュール登録・ルーティング定義 |
| index.js | `src/Administration/Resources/app/administration/src/module/sw-product-stream/page/sw-product-stream-list/index.js` | ソース | 一覧ページのメインロジック |
| sw-product-stream-list.html.twig | `src/Administration/Resources/app/administration/src/module/sw-product-stream/page/sw-product-stream-list/sw-product-stream-list.html.twig` | テンプレート | 一覧ページのHTMLテンプレート |
| sw-product-stream-detail | `src/Administration/Resources/app/administration/src/module/sw-product-stream/page/sw-product-stream-detail/` | ソース | 詳細ページコンポーネント |
| sw-product-stream-filter | `src/Administration/Resources/app/administration/src/module/sw-product-stream/component/sw-product-stream-filter/` | ソース | 条件フィルタコンポーネント |
| sw-product-stream-modal-preview | `src/Administration/Resources/app/administration/src/module/sw-product-stream/component/sw-product-stream-modal-preview/` | ソース | プレビューモーダルコンポーネント |
