# 機能設計書 75-アカウントキャンセル

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるアカウントのキャンセル（削除予約）機能の設計を記述する。

### 本機能の処理概要

アカウントを削除予約状態にする機能である。キャンセル処理は即座にアカウントを削除するのではなく、キャンセルレコードを作成して削除予約状態にし、30日後に完全削除（焼却処理）が実行される。これにより、誤操作による即座のデータ消失を防ぎ、猶予期間内であれば復元が可能となる。

**業務上の目的・背景**：組織解散、プロジェクト終了、サービス利用終了などの理由でアカウントを閉じる必要がある場合に使用する。ただし、即座の削除は取り返しがつかないため、猶予期間を設けることでユーザーの誤操作やビジネス判断の変更に対応できるようにしている。

**機能の利用シーン**：
- 会社やプロジェクトが終了しアカウントが不要になった場合
- 他のサービスへの移行が完了しFizzyを使用しなくなった場合
- テストアカウントを削除したい場合

**主要な処理内容**：
1. オーナーがアカウント設定画面から削除ボタンをクリック
2. 確認ダイアログで「Delete my account」を選択
3. キャンセル処理を実行（account_cancellationsレコード作成）
4. キャンセル通知メールを送信
5. セッションメニュー画面にリダイレクト

**関連システム・外部連携**：
- メール送信システム：キャンセル通知メール送信
- SaaS版：サブスクリプションのキャンセル処理

**権限による制御**：オーナー（owner）ロールを持つユーザーのみがアカウントをキャンセル可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | アカウント設定画面 | 主画面 | キャンセルボタンと確認ダイアログ |

## 機能種別

データ更新（状態変更・削除予約）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| なし | - | - | POSTリクエストのみ | - |

### 入力データソース

- セッション：Current.account（削除対象アカウント）
- セッション：Current.user（実行者、オーナー権限確認用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| リダイレクト | URL | セッションメニュー画面 |
| 通知 | String | "Account deleted" |
| メール | Email | キャンセル通知メール |

### 出力先

- 画面遷移：session_menu_path（script_name: nil）へリダイレクト
- メール送信：アカウント関係者へキャンセル通知

## 処理フロー

### 処理シーケンス

```
1. オーナーがキャンセルボタンをクリック
   └─ 確認ダイアログが表示

2. 確認ダイアログで「Delete my account」を選択
   └─ POSTリクエストでcreateアクションを呼び出し

3. 権限チェック
   └─ ensure_ownerコールバックでオーナー権限を確認
   └─ 非オーナーの場合は403 Forbiddenを返却

4. キャンセル処理実行（Account#cancel）
   └─ with_lockでロック取得
   └─ cancellable?とactive?の確認
   └─ run_callbacks :cancelでコールバック実行
   └─ account_cancellationsレコード作成

5. 通知メール送信
   └─ AccountMailer.cancellation.deliver_later

6. リダイレクト
   └─ セッションメニュー画面にリダイレクト（"Account deleted"通知付き）
```

### フローチャート

```mermaid
flowchart TD
    A[開始: キャンセルボタンクリック] --> B[確認ダイアログ表示]
    B --> C{Delete my account<br/>を選択?}
    C -->|No| D[キャンセル中止]
    C -->|Yes| E{オーナー権限あり?}
    E -->|No| F[403 Forbidden]
    E -->|Yes| G[ロック取得]
    G --> H{cancellable? &&<br/>active?}
    H -->|No| I[処理スキップ]
    H -->|Yes| J[コールバック実行]
    J --> K[account_cancellations<br/>レコード作成]
    K --> L[通知メール送信]
    L --> M[セッションメニューに<br/>リダイレクト]
    M --> N[終了]
    D --> N
    F --> N
    I --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-75-01 | オーナー限定 | アカウントキャンセルはオーナーのみ実行可能 | createアクション実行時 |
| BR-75-02 | キャンセル可能判定 | Account.accepting_signups?がtrueの場合のみキャンセル可能 | キャンセル処理時 |
| BR-75-03 | アクティブ状態確認 | 既にキャンセル済みまたはインポート中のアカウントはキャンセル不可 | キャンセル処理時 |
| BR-75-04 | 30日猶予期間 | キャンセル後30日間はデータが保持され、その後完全削除 | キャンセル後 |
| BR-75-05 | SaaS版サブスクリプション | SaaS版ではサブスクリプションもキャンセルされる | キャンセル処理時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| キャンセル作成 | account_cancellations | INSERT | キャンセルレコードを作成 |

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

#### account_cancellations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | 対象アカウントのID | - |
| INSERT | initiated_by_id | キャンセルを実行したユーザーのID | Current.user.id |
| INSERT | created_at | 現在時刻 | キャンセル日時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | 権限エラー | 非オーナーによるキャンセル試行 | Forbiddenエラー（ensure_ownerで制御） |
| - | 処理スキップ | cancellable?またはactive?がfalse | 何も処理せずに終了 |

### リトライ仕様

with_lockによる楽観的ロックを使用。競合時は自動的にリトライ。

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

with_lockメソッドによりアカウントレコードをロックしてトランザクション内で処理を実行。キャンセルレコード作成が失敗した場合はロールバック。

## パフォーマンス要件

- レスポンス時間：2秒以内（メール送信は非同期）
- ロック取得待ち時間を考慮する必要がある

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

- ensure_ownerによるオーナー権限チェック
- フロントエンドでの確認ダイアログ（turbo_confirm）
- with_lockによる並行処理の制御
- キャンセル操作の監査ログ（initiated_by_idで実行者を記録）

## 備考

- キャンセル処理は即座にアカウントを削除しない（ソフトデリート的な動作）
- 30日後にAccount::IncinerateDueJobにより完全削除される
- キャンセル後も猶予期間内であればreactivateメソッドで復元可能
- SaaS版ではサブスクリプションのキャンセルも行われる（set_callback :cancel）

---

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

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

### 推奨読解順序

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

キャンセル処理に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Cancellable | `app/models/account/cancellable.rb` | キャンセル機能のメインロジック |
| 1-2 | Account | `app/models/account.rb` | Cancellableのincludeとリレーション |

**読解のコツ**: CancellableモジュールはAccountモデルにincludeされ、cancel/reactivateメソッドとcancelled?判定を提供する。define_callbacksで独自コールバックを定義していることに注目。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CancellationsController | `app/controllers/account/cancellations_controller.rb` | createアクションの実装 |

**主要処理フロー**:
1. **2行目**: `before_action :ensure_owner` - オーナー権限チェック
2. **4-6行目**: createアクション
3. **5行目**: `Current.account.cancel` - キャンセル処理呼び出し
4. **6行目**: セッションメニューにリダイレクト
5. **9-11行目**: ensure_ownerメソッドでowner?チェック

#### Step 3: キャンセルロジックを理解する

Cancellableモジュールの詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Cancellable | `app/models/account/cancellable.rb` | cancelメソッドの実装 |

**主要処理フロー**:
- **4-5行目**: has_one :cancellationとコールバック定義
- **11-20行目**: cancelメソッド
- **12行目**: with_lockでロック取得
- **13行目**: cancellable? && active?の確認
- **14-16行目**: コールバック実行とキャンセルレコード作成
- **18行目**: AccountMailer.cancellation.deliver_later
- **33-35行目**: cancelled?メソッド
- **37-39行目**: cancellable?メソッド（Account.accepting_signups?を確認）

#### Step 4: ビューを理解する

画面表示の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | _cancellation.html.erb | `app/views/account/settings/_cancellation.html.erb` | キャンセルセクション |

**読解のコツ**:
- 1行目でCurrent.account.cancellable? && Current.user.owner?を確認
- 確認ダイアログはdata-controller="dialog"とdata-dialog-modal-value="true"で実装
- turbo: falseでTurboを無効化している点に注目

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

```
Account::CancellationsController#create
    │
    ├─ ensure_owner (before_action)
    │      └─ Current.user.owner? ─▶ 403 if false
    │
    └─ Current.account.cancel
           │
           ├─ with_lock（ロック取得）
           │
           ├─ cancellable? && active?
           │      │
           │      ├─ cancellable? ─▶ Account.accepting_signups?
           │      │
           │      └─ active? ─▶ !cancelled? && !importing?
           │
           ├─ run_callbacks :cancel
           │      └─ SaaS版: サブスクリプションキャンセル
           │
           ├─ create_cancellation!(initiated_by: Current.user)
           │      └─ account_cancellations INSERT
           │
           └─ AccountMailer.cancellation.deliver_later
```

### データフロー図

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

POSTリクエスト ───▶ Account::CancellationsController#create ─┬─▶ リダイレクト
                          │                                   │   (session_menu_path)
                          │                                   │
                          ├──▶ 権限チェック                   │
                          │                                   └─▶ 通知メール
                          ├──▶ Current.account.cancel              (非同期送信)
                          │        │
                          │        ├──▶ ロック取得
                          │        │
                          │        ├──▶ 状態確認
                          │        │
                          │        └──▶ account_cancellations ─▶ INSERT
                          │
                          └──▶ セッションメニュー画面へ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CancellationsController | `app/controllers/account/cancellations_controller.rb` | コントローラー | キャンセル処理のエントリーポイント |
| Cancellable | `app/models/account/cancellable.rb` | モデル（Concern） | キャンセル・復元ロジック |
| Account | `app/models/account.rb` | モデル | アカウント情報管理 |
| _cancellation.html.erb | `app/views/account/settings/_cancellation.html.erb` | ビュー（パーシャル） | キャンセルセクション・確認ダイアログ |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（5行目） |
| AccountMailer | `app/mailers/account_mailer.rb` | メーラー | キャンセル通知メール |
