# 機能設計書 12-キャンペーン作成

## 概要

本ドキュメントは、Fat Free CRMにおけるキャンペーン作成機能の設計を記述する。新規マーケティングキャンペーンを作成し、データベースに登録する機能である。

### 本機能の処理概要

**業務上の目的・背景**：マーケティング活動を体系的に管理するため、キャンペーン情報を登録する必要がある。キャンペーンはリードや商談の獲得元として紐付けられ、マーケティング活動の効果測定の基盤となる。予算、目標リード数、目標収益などのKPI設定も同時に行える。

**機能の利用シーン**：マーケティング担当者が新規キャンペーン（展示会、Webセミナー、広告キャンペーン等）を企画・実行する際に、その情報をシステムに登録する場面で利用される。キャンペーン開始前の計画段階から、開始時の実行段階まで幅広く使用される。

**主要な処理内容**：
1. 新規キャンペーンフォームの表示（newアクション）
2. 入力されたキャンペーン情報のバリデーション
3. キャンペーンレコードのデータベースへの保存
4. コメントの同時追加（オプション）
5. サイドバーデータの更新
6. 成功/失敗に応じたレスポンス生成

**関連システム・外部連携**：特になし。内部のCRMデータとして管理される。

**権限による制御**：ログインユーザーのみがキャンペーンを作成可能。作成されたキャンペーンの所有者（user_id）は作成者となる。アクセス権限（Public/Private/Shared）を設定可能で、Sharedの場合は共有先ユーザーの指定が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | キャンペーン新規作成フォーム | 主画面 | 新規キャンペーン情報入力、作成実行 |
| 11 | キャンペーン一覧画面 | 参照画面 | 作成完了後の遷移先 |

## 機能種別

CRUD操作（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| campaign[name] | String | Yes | キャンペーン名（最大64文字） | 必須、ユニーク制約（user_id + name + deleted_at） |
| campaign[status] | String | No | ステータス | Setting.campaign_statusの値のみ許可 |
| campaign[budget] | Decimal | No | 予算 | 数値（12桁、小数2桁） |
| campaign[target_leads] | Integer | No | 目標リード数 | 整数 |
| campaign[target_conversion] | Float | No | 目標コンバージョン率 | 浮動小数点 |
| campaign[target_revenue] | Decimal | No | 目標収益 | 数値（12桁、小数2桁） |
| campaign[starts_on] | Date | No | 開始日 | 日付形式 |
| campaign[ends_on] | Date | No | 終了日 | 日付形式、開始日より後 |
| campaign[objectives] | Text | No | 目的・目標 | テキスト |
| campaign[access] | String | No | アクセス権限 | Public/Private/Shared |
| campaign[assigned_to] | Integer | No | 担当者ID | 存在するユーザーID |
| campaign[background_info] | String | No | 背景情報 | 最大255文字 |
| comment_body | Text | No | コメント | テキスト |

### 入力データソース

- フォーム入力（AJAX POST）
- セッション情報（現在のユーザー情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @campaign | Campaign | 作成されたキャンペーンオブジェクト |
| @campaigns | Array | キャンペーン一覧（インデックスページからの呼び出し時） |

### 出力先

- AJAX（JS）レスポンス
- JSON/XMLレスポンス（API経由）

## 処理フロー

### 処理シーケンス

```
1. ユーザー認証確認
   └─ authenticate_user!でログイン状態を確認
2. 新規キャンペーンオブジェクト生成（newアクション）
   └─ デフォルト値（user, access, assigned_to）を設定
3. フォーム入力受付
   └─ パラメータの受け取りとバリデーション
4. キャンペーン保存（createアクション）
   └─ バリデーション成功時にデータベースへ保存
5. コメント追加（オプション）
   └─ comment_bodyがある場合、コメントを追加
6. サイドバーデータ更新
   └─ キャンペーン一覧とステータス別カウントを再取得
7. レスポンス生成
   └─ 成功/失敗に応じたJSレスポンス
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[ユーザー認証]
    B --> C{認証OK?}
    C -->|No| D[ログインページへリダイレクト]
    C -->|Yes| E{アクション判定}
    E -->|new| F[新規キャンペーンオブジェクト生成]
    F --> G[デフォルト値設定]
    G --> H[フォーム表示]
    E -->|create| I[パラメータ取得]
    I --> J[バリデーション実行]
    J --> K{バリデーション成功?}
    K -->|No| L[エラーメッセージ表示]
    K -->|Yes| M[キャンペーン保存]
    M --> N{コメントあり?}
    N -->|Yes| O[コメント追加]
    N -->|No| P[サイドバーデータ更新]
    O --> P
    P --> Q[成功レスポンス]
    L --> R[失敗レスポンス]
    Q --> S[終了]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-12-01 | キャンペーン名必須 | キャンペーン名は必須入力 | 常時 |
| BR-12-02 | キャンペーン名ユニーク | 同一ユーザー内でキャンペーン名はユニーク | 常時 |
| BR-12-03 | 日付整合性 | 終了日は開始日より後である必要がある | 両日が入力された場合 |
| BR-12-04 | 共有時のユーザー指定 | access=Sharedの場合、共有先ユーザー必須 | Shared選択時 |
| BR-12-05 | ステータス値制約 | ステータスはSetting.campaign_statusの値のみ | ステータス入力時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| キャンペーン作成 | campaigns | INSERT | 新規キャンペーンレコード挿入 |
| コメント追加 | comments | INSERT | キャンペーンへのコメント追加 |
| 権限設定 | permissions | INSERT | Shared時の権限レコード作成 |
| アクティビティ記録 | versions | INSERT | 作成イベント記録（PaperTrail） |

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

#### campaigns

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | 現在のユーザーID | |
| INSERT | assigned_to | フォーム入力値またはNULL | |
| INSERT | name | フォーム入力値 | 必須 |
| INSERT | access | フォーム入力値（デフォルト: Setting.default_access） | |
| INSERT | status | フォーム入力値またはNULL | |
| INSERT | budget | フォーム入力値またはNULL | |
| INSERT | target_leads | フォーム入力値またはNULL | |
| INSERT | target_conversion | フォーム入力値またはNULL | |
| INSERT | target_revenue | フォーム入力値またはNULL | |
| INSERT | starts_on | フォーム入力値またはNULL | |
| INSERT | ends_on | フォーム入力値またはNULL | |
| INSERT | objectives | フォーム入力値またはNULL | |
| INSERT | background_info | フォーム入力値またはNULL | |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | 現在のユーザーID | |
| INSERT | commentable_type | 'Campaign' | |
| INSERT | commentable_id | 作成されたキャンペーンID | |
| INSERT | comment | comment_body値 | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未ログイン状態でアクセス | ログインページへリダイレクト |
| 422 | バリデーションエラー | 必須項目未入力、ユニーク制約違反等 | エラーメッセージ表示 |
| 422 | 日付整合性エラー | 終了日が開始日より前 | エラーメッセージ表示 |
| 422 | 共有設定エラー | Shared選択時に共有先未指定 | エラーメッセージ表示 |

### リトライ仕様

本機能にリトライ処理は実装されていない。バリデーションエラー時はフォームを再表示し、ユーザーに修正を促す。

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

キャンペーン保存とコメント追加は個別のトランザクションで実行される。キャンペーン保存失敗時はコメント追加は行われない。

## パフォーマンス要件

- フォーム表示は1秒以内を目標
- 保存処理は2秒以内を目標

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

- CanCanによるアクセス権限チェック実施
- CSRF対策（protect_from_forgery）
- Strong Parametersによる許可パラメータ制限
- SQLインジェクション対策（ActiveRecord使用）
- XSS対策（ERB::Util使用）

## 備考

- タグ付け機能は別機能（No.83）として管理
- アクセス権限管理は別機能（No.84）として管理

---

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

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

### 推奨読解順序

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

まず、キャンペーンエンティティの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | campaign.rb | `app/models/entities/campaign.rb` | Campaignモデルの属性、バリデーション、カスタムメソッド |

**読解のコツ**:
- **64-68行目**: バリデーション定義（名前必須、ユニーク制約、日付整合性、共有時ユーザー指定）
- **105-107行目**: `start_and_end_dates`メソッドで日付の整合性チェック
- **111-113行目**: `users_for_shared_access`メソッドで共有設定時の検証

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

処理の起点となるコントローラーのnew/createアクションを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | campaigns_controller.rb | `app/controllers/entities/campaigns_controller.rb` | new/createアクションの処理内容 |

**主要処理フロー**:
1. **69-82行目**: newアクション - デフォルト値設定、関連エンティティ処理
2. **94-104行目**: createアクション - 保存処理、コメント追加、サイドバー更新
3. **70行目**: デフォルト値設定（user, access, assigned_to）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _new.html.haml | `app/views/campaigns/_new.html.haml` | 新規作成フォームの構造 |
| 3-2 | _top_section.html.haml | `app/views/campaigns/_top_section.html.haml` | フォーム上部セクション |
| 3-3 | _objectives.html.haml | `app/views/campaigns/_objectives.html.haml` | 目的・目標入力セクション |

**主要処理フロー**:
- **1行目**: simple_form_forでフォーム生成
- **7-12行目**: 各セクションのパーシャルレンダリング
- **15-18行目**: 送信ボタンとキャンセルリンク

#### Step 4: 基底コントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | entities_controller.rb | `app/controllers/entities_controller.rb` | 共通処理（権限チェック等） |

**主要処理フロー**:
- **15行目**: `load_and_authorize_resource`でリソースロードと権限チェック

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

```
CampaignsController#new
    │
    ├─ EntitiesController (継承)
    │      └─ load_and_authorize_resource
    │
    └─ デフォルト値設定

CampaignsController#create
    │
    ├─ Campaign#save
    │      ├─ バリデーション実行
    │      │      ├─ validates_presence_of :name
    │      │      ├─ validates_uniqueness_of :name
    │      │      ├─ start_and_end_dates
    │      │      └─ users_for_shared_access
    │      │
    │      └─ INSERT campaigns
    │
    ├─ Campaign#add_comment_by_user
    │      └─ INSERT comments
    │
    └─ get_data_for_sidebar
           └─ SELECT campaigns (カウント集計)
```

### データフロー図

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

フォーム入力 ───────▶ CampaignsController#create ───▶ JSレスポンス
                          │
                          ├─▶ バリデーション
                          │
                          ├─▶ Campaign保存
                          │     └─▶ campaigns INSERT
                          │
                          ├─▶ コメント追加（オプション）
                          │     └─▶ comments INSERT
                          │
                          └─▶ サイドバーデータ更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| campaigns_controller.rb | `app/controllers/entities/campaigns_controller.rb` | コントローラー | キャンペーン関連アクションの処理 |
| entities_controller.rb | `app/controllers/entities_controller.rb` | コントローラー | エンティティ共通処理 |
| campaign.rb | `app/models/entities/campaign.rb` | モデル | キャンペーンエンティティ定義 |
| _new.html.haml | `app/views/campaigns/_new.html.haml` | ビュー | 新規作成フォームテンプレート |
| new.js.haml | `app/views/campaigns/new.js.haml` | ビュー | 新規フォーム表示用JSテンプレート |
| create.js.haml | `app/views/campaigns/create.js.haml` | ビュー | 作成完了用JSテンプレート |
| _top_section.html.haml | `app/views/campaigns/_top_section.html.haml` | ビュー | フォーム上部パーシャル |
| _objectives.html.haml | `app/views/campaigns/_objectives.html.haml` | ビュー | 目的入力パーシャル |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
