# 機能設計書 19-住所削除

## 概要

本ドキュメントは、Shopwareストアフロントにおける「住所削除」機能の設計内容を記載する。顧客が自身の登録済み住所を削除する機能であり、デフォルト住所の削除制限やアクティブ住所の自動切替などの制御を行う。

### 本機能の処理概要

**業務上の目的・背景**：顧客が不要になった住所を削除できることは、住所管理の基本機能である。ただし、デフォルトの請求先・配送先住所は削除不可とし、注文処理への影響を防ぐ。本機能は、これらの制約を守りながら安全に住所を削除する手段を提供する。

**機能の利用シーン**：
- 使用しなくなった住所を削除したい場合
- 引っ越し後に旧住所を削除したい場合
- 誤って登録した住所を削除したい場合

**主要な処理内容**：
1. ログイン認証の確認
2. 住所所有権の確認
3. デフォルト住所でないことの確認
4. 住所の削除
5. アクティブ住所がある場合はデフォルト住所に切替
6. 成功・エラーメッセージの表示とリダイレクト

**関連システム・外部連携**：Store API（AbstractDeleteAddressRoute）を経由してデータを削除。

**権限による制御**：ログイン必須（`LOGIN_REQUIRED`属性）。ゲストユーザーは利用不可。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | 住所帳ページ | 主画面 | 住所一覧からの削除操作 |

## 機能種別

CRUD操作（Delete）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| addressId | string | Yes | 削除対象の住所ID | 有効なUUID、顧客所有確認、デフォルト住所でないこと |
| request | Request | Yes | HTTPリクエストオブジェクト | なし |
| context | SalesChannelContext | Yes | 販売チャネルコンテキスト | ログイン顧客情報を含む |
| customer | CustomerEntity | Yes | ログイン中の顧客エンティティ | セッションから自動取得 |

### 入力データソース

- URLパラメータ（addressId）
- セッション情報（ログイン顧客情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| flashMessage | string | 成功/エラーメッセージ |
| NoContentResponse | Response | 削除成功時のレスポンス（Store API） |

### 出力先

- データベース（customer_address テーブルからDELETE）
- セッション（フラッシュメッセージ）
- 画面リダイレクト（frontend.account.address.page）

## 処理フロー

### 処理シーケンス

```
1. ルーティング処理
   └─ /account/address/delete/{addressId} パスへのPOSTリクエストを受信

2. コントローラー処理 (AddressController::deleteAddress)
   ├─ addressIdの検証
   └─ DeleteAddressRouteを呼び出し

3. 住所削除ルート処理 (DeleteAddressRoute::delete)
   ├─ 住所所有権の検証（validateAddress）
   ├─ デフォルト住所でないことの確認
   ├─ addressリポジトリのdelete実行
   └─ アクティブ住所の切替処理

4. レスポンス生成
   ├─ 成功時：成功メッセージ設定、住所一覧ページへリダイレクト
   └─ エラー時：エラーメッセージ設定、住所一覧ページへリダイレクト
```

### フローチャート

```mermaid
flowchart TD
    A[POST /account/address/delete/{addressId}] --> B{ログイン済み?}
    B -->|No| C[ログイン画面へリダイレクト]
    B -->|Yes| D{addressId有効?}
    D -->|No| E[RoutingException発生]
    D -->|Yes| F[DeleteAddressRoute::delete]
    F --> G[validateAddress - 所有権確認]
    G --> H{所有権OK?}
    H -->|No| I[CustomerException発生]
    H -->|Yes| J{デフォルト住所?}
    J -->|Yes| K[CannotDeleteDefaultAddressException]
    K --> L[エラーメッセージ設定]
    J -->|No| M[addressRepository.delete]
    M --> N{アクティブ請求先 = 削除住所?}
    N -->|Yes| O[activeBillingAddress = defaultBillingAddress]
    N -->|No| P{アクティブ配送先 = 削除住所?}
    O --> P
    P -->|Yes| Q[activeShippingAddress = defaultShippingAddress]
    P -->|No| R[NoContentResponse返却]
    Q --> R
    R --> S[成功メッセージ設定]
    I --> L
    S --> T[住所一覧へリダイレクト]
    L --> T
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-19-01 | ログイン必須 | 未ログインユーザーはアクセス不可 | 全リクエスト |
| BR-19-02 | 所有権確認 | 削除対象の住所が自分のものであることを確認 | 全リクエスト |
| BR-19-03 | デフォルト住所削除禁止 | デフォルト請求先・配送先住所は削除不可 | 全リクエスト |
| BR-19-04 | アクティブ住所切替 | 削除住所がアクティブ住所の場合、デフォルト住所に自動切替 | アクティブ住所削除時 |

### 計算ロジック

**アクティブ住所の切替**:
```php
// 削除した住所がアクティブ請求先の場合
if ($addressId === $customer->getActiveBillingAddress()?->getId()) {
    $customer->setActiveBillingAddress($customer->getDefaultBillingAddress());
}

// 削除した住所がアクティブ配送先の場合
if ($addressId === $customer->getActiveShippingAddress()?->getId()) {
    $customer->setActiveShippingAddress($customer->getDefaultShippingAddress());
}
```

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 住所削除 | customer_address | DELETE | 住所レコードの物理削除 |

### テーブル別操作詳細

#### customer_address

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | id | 削除対象のaddressId | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | RoutingException | addressIdが空 | パラメータ不足エラー表示 |
| - | InvalidUuidException | 無効なUUID形式 | エラーメッセージ表示 |
| - | AddressNotFoundException | 住所が存在しないまたは所有権なし | エラーメッセージ表示 |
| - | CannotDeleteDefaultAddressException | デフォルト住所を削除しようとした | エラーメッセージ表示 |
| - | CustomerException | その他の顧客関連エラー | エラーメッセージ表示 |

### リトライ仕様

特になし

## トランザクション仕様

DAL（Data Abstraction Layer）のdeleteメソッドにより、1件のDELETEがトランザクション内で実行される。

## パフォーマンス要件

- 単一レコードの削除のため、特別なパフォーマンス要件なし

## セキュリティ考慮事項

- LOGIN_REQUIRED属性によりログイン必須を強制
- CustomerAddressValidationTraitにより住所所有権を検証
- 他の顧客の住所は削除不可
- デフォルト住所の削除制限により、注文処理への影響を防止

## 備考

- XmlHttpRequest属性により、AJAX削除にも対応
- 削除後は必ず住所一覧ページにリダイレクト
- アクティブ住所の切替はメモリ上のCustomerEntityに対して行われる（次回リクエスト時にはセッションから再取得）

---

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

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

### 推奨読解順序

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

住所エンティティとデフォルト住所の関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CustomerEntity.php | `src/Core/Checkout/Customer/CustomerEntity.php` | defaultBillingAddressId, defaultShippingAddressId, activeBillingAddress, activeShippingAddressの関係を確認 |
| 1-2 | CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | 住所エンティティの構造を確認 |

**読解のコツ**: CustomerEntityには「デフォルト住所（ID）」と「アクティブ住所（エンティティ）」の両方があり、アクティブ住所は現在のセッションで使用中の住所を指す。

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

コントローラーでのリクエスト処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AddressController.php | `src/Storefront/Controller/AddressController.php` | deleteAddressメソッド（263-291行目）がエントリーポイント |

**主要処理フロー**:
1. **279-281行目**: addressIdの存在確認
2. **284行目**: `$this->deleteAddressRoute->delete($addressId, $context, $customer)` で住所削除ルート呼び出し
3. **285行目**: 成功時、成功メッセージをフラッシュに設定
4. **286行目**: 例外発生時（InvalidUuid, AddressNotFound, CannotDeleteDefault, CustomerException）、エラーメッセージ設定
5. **290行目**: 住所一覧ページへリダイレクト

#### Step 3: Store APIルートを理解する

実際の住所削除ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DeleteAddressRoute.php | `src/Core/Checkout/Customer/SalesChannel/DeleteAddressRoute.php` | deleteメソッド（47-73行目）の処理フローを確認 |

**主要処理フロー**:
- **49行目**: validateAddressで所有権確認
- **51-55行目**: デフォルト住所の場合、CannotDeleteDefaultAddressException
- **58行目**: addressリポジトリのdelete実行
- **60-63行目**: アクティブ請求先が削除住所の場合、デフォルトに切替
- **66-69行目**: アクティブ配送先が削除住所の場合、デフォルトに切替

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

```
AddressController::deleteAddress
    │
    ├─ addressIdの検証
    │
    └─ DeleteAddressRoute::delete
           │
           ├─ validateAddress (所有権確認)
           │      └─ CustomerAddressValidationTrait
           │
           ├─ デフォルト住所チェック
           │      └─ CannotDeleteDefaultAddressException
           │
           ├─ addressRepository.delete
           │
           ├─ アクティブ請求先の切替（必要に応じて）
           │
           └─ アクティブ配送先の切替（必要に応じて）
```

### データフロー図

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

POST /account/address/delete/{addressId}
        │
        ▼
SalesChannelContext ───▶ AddressController::deleteAddress
        │                         │
        ▼                         ▼
CustomerEntity ─────────▶ DeleteAddressRoute::delete
                                  │
                          ┌───────┴───────┐
                          ▼               ▼
                   所有権確認      デフォルト住所チェック
                          │               │
                          └───────┬───────┘
                                  │
                                  ▼
                          成功/失敗判定
                                  │
                     ┌────────────┴────────────┐
                     ▼                         ▼
                 成功時                    失敗時
                     │                         │
                     ▼                         ▼
           addressRepository.delete     Exception発生
                     │                         │
                     ▼                         │
           アクティブ住所切替                  │
                     │                         │
                     ▼                         │
           NoContentResponse                   │
                     │                         │
                     └────────────┬────────────┘
                                  │
                                  ▼
                     フラッシュメッセージ設定
                                  │
                                  ▼
                     住所一覧へリダイレクト
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AddressController.php | `src/Storefront/Controller/AddressController.php` | コントローラー | HTTPリクエスト処理のエントリーポイント |
| DeleteAddressRoute.php | `src/Core/Checkout/Customer/SalesChannel/DeleteAddressRoute.php` | Store API | 住所削除ビジネスロジック |
| AbstractDeleteAddressRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractDeleteAddressRoute.php` | 抽象クラス | Store APIの抽象定義 |
| CustomerAddressValidationTrait.php | `src/Core/Checkout/Customer/SalesChannel/CustomerAddressValidationTrait.php` | トレイト | 住所所有権検証 |
| CannotDeleteDefaultAddressException.php | `src/Core/Checkout/Customer/Exception/CannotDeleteDefaultAddressException.php` | 例外 | デフォルト住所削除エラー |
| CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | エンティティ | 住所データモデル |
| NoContentResponse.php | `src/Core/System/SalesChannel/NoContentResponse.php` | レスポンス | 204 No Contentレスポンス |
