# 画面設計書 39-顧客一覧

## 概要

本ドキュメントは、Shopware 6 管理画面（Administration）の顧客一覧画面の設計仕様を定義します。この画面は登録されている顧客情報を一覧表示・管理するための機能を提供し、顧客の検索、フィルタリング、編集、削除、一括編集を行います。

### 本画面の処理概要

**業務上の目的・背景**：顧客一覧画面は、ECショップの顧客データベースを管理するための中核機能です。顧客情報の確認、顧客グループの管理、アフィリエイト/キャンペーンコードのトラッキング、顧客の有効化/無効化などを行います。顧客関係管理（CRM）の基盤として、顧客ロイヤルティの向上とマーケティング施策の効果測定に活用されます。

**画面へのアクセス方法**：左側メインナビゲーションの「顧客」>「概要」をクリックしてアクセスします。URLパスは `/admin#/sw/customer/index` です。権限 `customer.viewer` が必要です。

**主要な操作・処理内容**：
1. 顧客一覧の表示：ページネーション付きで顧客を一覧表示
2. 多彩なフィルタリング：顧客番号、アフィリエイトコード、キャンペーンコード、顧客グループ申請、敬称、アカウント状態、デフォルト支払方法、顧客グループ、請求国、配送国、タグでフィルタ
3. 並び替え：作成日時（デフォルト降順）でソート
4. インライン編集：一覧上で直接顧客名、顧客番号、メール、アフィリエイト/キャンペーンコード、アクティブ状態を編集
5. 顧客詳細編集：詳細ページでの編集
6. 顧客削除：顧客データの削除
7. 一括編集：複数顧客の同時編集機能へのアクセス
8. 言語切替：表示言語の変更

**画面遷移**：顧客名をクリックすると顧客詳細編集画面に遷移します。「新規顧客」ボタンで顧客作成画面に遷移します。一括編集ボタンで一括編集画面に遷移します。

**権限による表示制御**：
- `customer.viewer`: 顧客一覧の閲覧
- `customer.editor`: インライン編集
- `customer.creator`: 新規顧客作成ボタンの表示
- `customer.deleter`: 削除ボタンの表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | 顧客アカウント概要 | 主機能 | 顧客の一覧表示・検索・管理 |

## 画面種別

一覧

## URL/ルーティング

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

### サブルート

| ルート名 | パス | コンポーネント | 説明 |
|---------|------|---------------|------|
| sw.customer.create | `/create` | sw-customer-create | 新規顧客作成 |
| sw.customer.detail | `/detail/:id` | sw-customer-detail | 顧客詳細編集 |
| sw.customer.detail.base | `/detail/:id/base` | sw-customer-detail-base | 基本情報タブ |
| sw.customer.detail.addresses | `/detail/:id/addresses` | sw-customer-detail-addresses | 住所タブ |
| sw.customer.detail.order | `/detail/:id/order` | sw-customer-detail-order | 注文タブ |

## 入出力項目

### フィルタ入力項目

| 項目名 | 項目ID | 入力形式 | 説明 |
|--------|--------|----------|------|
| 顧客番号 | customer-number-filter | テキスト | 顧客番号での完全一致検索 |
| アフィリエイトコード | affiliate-code-filter | テキスト | アフィリエイトコードでの絞り込み |
| キャンペーンコード | campaign-code-filter | テキスト | キャンペーンコードでの絞り込み |
| 顧客グループ申請 | customer-group-request-filter | 存在フィルタ | 顧客グループ変更申請の有無 |
| 敬称 | salutation-filter | エンティティ選択 | 敬称での絞り込み |
| アカウント状態 | account-status-filter | セレクト | 有効/無効での絞り込み |
| デフォルト支払方法 | default-payment-method-filter | エンティティ選択 | デフォルト支払方法での絞り込み |
| 顧客グループ | group-filter | エンティティ選択 | 顧客グループでの絞り込み |
| 請求国 | billing-address-country-filter | エンティティ選択 | 請求先住所の国での絞り込み |
| 配送国 | shipping-address-country-filter | エンティティ選択 | 配送先住所の国での絞り込み |
| タグ | tags-filter | エンティティ選択 | タグでの絞り込み |

### 検索入力項目

| 項目名 | 項目ID | 入力形式 | 説明 |
|--------|--------|----------|------|
| 検索キーワード | term | テキスト | 顧客名、メールアドレス、顧客番号で検索 |

## 表示項目

### 一覧カラム

| 項目名 | property | 表示形式 | インライン編集 | デフォルト表示 | 説明 |
|--------|----------|----------|---------------|---------------|------|
| 顧客名 | firstName | テキスト+リンク | 可（string） | Yes（primary） | 姓名、詳細へのリンク、幅250px |
| 会社名 | company | テキスト | - | No | 法人名 |
| 住所 | defaultBillingAddress.street | テキスト | - | Yes | 請求先住所の通り |
| 郵便番号 | defaultBillingAddress.zipcode | テキスト | - | Yes | 請求先郵便番号 |
| 市区町村 | defaultBillingAddress.city | テキスト | - | Yes | 請求先市区町村 |
| 顧客番号 | customerNumber | テキスト | 可（string） | Yes | 自然ソート対応 |
| 顧客グループ | group | テキスト | - | Yes | 顧客グループ名 |
| メール | email | テキスト | 可（string） | Yes | メールアドレス（IDN対応） |
| アフィリエイトコード | affiliateCode | テキスト | 可（string） | No | アフィリエイト追跡コード |
| キャンペーンコード | campaignCode | テキスト | 可（string） | No | キャンペーン追跡コード |
| 販売チャネル | boundSalesChannelId | テキスト | - | No | 紐付け販売チャネル |
| アクティブ | active | チェックボックス | 可（boolean） | No | アカウント有効/無効 |
| 作成日時 | createdAt | 日時 | - | Yes | 顧客登録日時 |

## イベント仕様

### 1-顧客一覧取得（getList）

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

1. `defaultCriteria` に基づいてクエリを構築
2. `filterService.mergeWithStoredFilters` でフィルタ条件をマージ
3. 検索スコアを追加
4. 関連データ（defaultBillingAddress, group, requestedGroup, boundSalesChannel）をアソシエーション
5. 顧客リポジトリで検索を実行
6. 結果を `customers` に格納、`total` を更新

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

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

1. 編集内容の保存を実行
2. 成功時：成功通知を表示（salutation mixinで顧客名生成）
3. 失敗時：一覧を再取得し、エラー通知を表示

### 3-顧客削除（onConfirmDelete）

顧客の削除処理：

1. 削除モーダルを閉じる
2. 顧客リポジトリで削除を実行
3. `getList()` を呼び出して一覧を再取得

### 4-フィルタ更新（updateCriteria）

フィルタが変更された際の処理：

1. ページを1にリセット
2. `filterCriteria` を更新
3. `defaultCriteria` のウォッチャーにより `getList()` が自動実行

### 5-一括編集（onBulkEditItems）

複数顧客の一括編集画面への遷移：

1. 次のティックで処理
2. `sw.bulk.edit.customer` へルーティング

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | customer | SELECT | 顧客一覧データの取得 |
| 一覧表示 | customer_address | SELECT | 請求先/配送先住所の取得 |
| 一覧表示 | customer_group | SELECT | 顧客グループ情報の取得 |
| インライン編集 | customer | UPDATE | 顧客情報の更新 |
| 顧客削除 | customer | DELETE | 顧客の削除 |

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

#### customer

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | *, defaultBillingAddress, group, requestedGroup | フィルタ・ソート条件 | 関連テーブル含む |
| UPDATE | first_name, last_name, email, customer_number, affiliate_code, campaign_code, active | インライン編集値 | インライン編集時 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|----------|--------------|
| sw-customer.detail.messageSaveSuccess | 成功 | インライン編集成功 | {salutation}が保存されました |
| sw-customer.detail.messageSaveError | エラー | 保存失敗 | 保存中にエラーが発生しました |

## 例外処理

| 例外種別 | 発生条件 | 処理内容 |
|---------|---------|---------|
| 検索結果なし | 条件に一致する顧客なし | 空の状態を表示 |
| インライン編集エラー | バリデーション失敗 | エラー通知表示、一覧再取得 |
| 削除エラー | API通信失敗 | エラー通知表示 |

## 備考

- デフォルトソートは作成日時の降順（DESC）
- 顧客番号は自然ソート（naturalSorting）を使用
- メールアドレスはIDNエンコード対応（国際化ドメイン名）
- フィルタ状態は `grid.filter.customer` キーで保存
- `loadFilterValues` は v6.8.0 で非推奨

---

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

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

### 推奨読解順序

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

顧客エンティティと関連データの構造を理解します。

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

**読解のコツ**: 顧客は`customer`テーブルに保存され、`customer_address`（住所）、`customer_group`（顧客グループ）、`sales_channel`（販売チャネル）などと関連します。

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

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

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

**主要処理フロー**:
1. **33行目**: `Module.register('sw-customer', {...})` でモジュール登録
2. **45-57行目**: `routes.index` で一覧ページのルーティング定義
3. **51行目**: `meta.privilege: 'customer.viewer'` で閲覧権限を設定
4. **68-99行目**: 詳細ページとタブルートの定義
5. **117-134行目**: ナビゲーションメニューの定義

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

顧客一覧ページのメインロジックを確認します。

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

**主要処理フロー**:
- **27-65行目**: `data()` で状態変数を定義（customers, filterCriteria, defaultFilters等）
- **83-105行目**: `defaultCriteria` でクエリ条件を構築（アソシエーション含む）
- **124-193行目**: `listFilterOptions` でフィルタ定義
- **244-277行目**: `getList()` で顧客データを取得
- **300-405行目**: `getCustomerColumns()` で一覧カラムを定義
- **432-435行目**: `onBulkEditItems()` で一括編集画面への遷移

#### Step 4: 権限設定を理解する

アクセス制御の設定を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | acl/index.js | `src/Administration/Resources/app/administration/src/module/sw-customer/acl/index.js` | viewer, editor, creator, deleterの権限定義 |

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

```
sw-customer (モジュール)
    |
    +-- sw-customer-list (一覧ページコンポーネント)
           |
           +-- watch: defaultCriteria
           |      +-- getList()
           |
           +-- getList()
           |      +-- filterService.mergeWithStoredFilters()
           |      +-- addQueryScores()
           |      +-- customerRepository.search(criteria)
           |
           +-- onInlineEditSave(promise, customer)
           |      +-- salutation(customer)
           |      +-- [成功] createNotificationSuccess()
           |      +-- [失敗] getList() + createNotificationError()
           |
           +-- onConfirmDelete(id)
           |      +-- customerRepository.delete()
           |      +-- getList()
           |
           +-- updateCriteria(criteria)
           |      +-- page = 1
           |      +-- filterCriteria = criteria
           |
           +-- onBulkEditItems()
                  +-- router.push('sw.bulk.edit.customer')
```

### データフロー図

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

検索キーワード  ───▶      sw-customer-list           ───▶   顧客一覧表示
フィルタ条件              ・defaultCriteria構築              フィルタバッジ
ページ番号                ・filterService統合               activeFilterNumber
                          ・customerRepository.search()

フィルタ変更   ───▶       updateCriteria             ───▶   フィルタ適用
                          ・page リセット                   一覧再取得
                          ・filterCriteria更新

一括編集      ───▶        onBulkEditItems            ───▶   一括編集画面へ
                          ・router.push()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `src/Administration/Resources/app/administration/src/module/sw-customer/index.js` | ソース | モジュール登録・ルーティング定義 |
| index.js | `src/Administration/Resources/app/administration/src/module/sw-customer/page/sw-customer-list/index.js` | ソース | 一覧ページのメインロジック |
| sw-customer-list.html.twig | `src/Administration/Resources/app/administration/src/module/sw-customer/page/sw-customer-list/sw-customer-list.html.twig` | テンプレート | 一覧ページのHTMLテンプレート |
| sw-customer-detail | `src/Administration/Resources/app/administration/src/module/sw-customer/page/sw-customer-detail/` | ソース | 詳細ページコンポーネント |
| sw-customer-create | `src/Administration/Resources/app/administration/src/module/sw-customer/page/sw-customer-create/` | ソース | 作成ページコンポーネント |
| acl/index.js | `src/Administration/Resources/app/administration/src/module/sw-customer/acl/index.js` | ソース | 権限マッピング定義 |
| default-search-configuration.js | `src/Administration/Resources/app/administration/src/module/sw-customer/default-search-configuration.js` | ソース | 検索設定 |
