# 画面設計書 26-商談新規作成フォーム

## 概要

本ドキュメントは、Fat Free CRMシステムにおける商談新規作成フォームの設計を記述する。新規商談の登録、取引先・キャンペーン・連絡先との紐付け、アクセス権限設定などの機能を提供するモーダル/Ajax形式のフォームである。

### 本画面の処理概要

商談新規作成フォームは、営業パイプラインに新規商談を追加するためのフォームである。

**業務上の目的・背景**：営業担当者が新規の商談（販売機会）を発見した際、迅速にシステムへ登録し、営業パイプラインに組み込む必要がある。本フォームにより、商談情報の一元管理と営業予測の精度向上を実現する。

**画面へのアクセス方法**：
- 商談一覧画面の「新規作成」ボタンをクリック
- 取引先詳細画面の「商談を追加」リンクをクリック
- キャンペーン詳細画面の「商談を追加」リンクをクリック
- 連絡先詳細画面の「商談を追加」リンクをクリック
- ナビゲーションバーの「+」メニューから選択

**主要な操作・処理内容**：
1. 商談基本情報（名前、ステージ）の入力
2. 金額関連情報（金額、確度、割引、クローズ日）の入力
3. 取引先の選択または新規作成
4. 担当者（割り当て先）の選択
5. キャンペーンの選択
6. タグの設定
7. アクセス権限（Public/Private/Shared）の設定
8. 初期コメントの入力
9. 商談の保存

**画面遷移**：
- 遷移元：商談一覧画面、取引先詳細画面、キャンペーン詳細画面、連絡先詳細画面
- 遷移先（成功時）：商談一覧画面（一覧からの場合）、関連エンティティ詳細画面

**権限による表示制御**：ログイン済みユーザーのみアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | 商談作成 | 主機能 | 新規商談情報入力、取引先同時作成、作成実行 |
| 83 | タグ付け | 補助機能 | 商談へのタグ付け |
| 84 | アクセス権限管理 | 補助機能 | 商談のアクセス権限設定 |

## 画面種別

登録フォーム（モーダル/Ajax）

## URL/ルーティング

### 表示
- URL: `/opportunities/new`
- HTTPメソッド: GET
- コントローラ: `OpportunitiesController#new`

### 登録実行
- URL: `/opportunities`
- HTTPメソッド: POST
- コントローラ: `OpportunitiesController#create`

## 入出力項目

### 入力パラメータ（GET /opportunities/new）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| related | String | 任意 | 関連エンティティ（例: "contact_123", "account_456"） |

### 入力パラメータ（POST /opportunities）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| opportunity[name] | String | 必須 | 商談名 |
| opportunity[stage] | String | 任意 | ステージ |
| opportunity[closes_on] | Date | 任意 | クローズ予定日 |
| opportunity[probability] | Integer | 任意 | 確度（0-100） |
| opportunity[amount] | Decimal | 任意 | 金額 |
| opportunity[discount] | Decimal | 任意 | 割引額 |
| opportunity[background_info] | String | 任意 | 背景情報 |
| opportunity[assigned_to] | Integer | 任意 | 担当者ユーザーID |
| opportunity[access] | String | 必須 | アクセス権限 |
| opportunity[user_ids] | Array | 条件付き | 共有先ユーザーID |
| opportunity[tag_list] | String | 任意 | タグリスト |
| account[id] | Integer | 任意 | 既存取引先ID |
| account[name] | String | 任意 | 新規取引先名 |
| campaign | Integer | 任意 | 関連キャンペーンID |
| contact | Integer | 任意 | 関連連絡先ID |
| comment_body | String | 任意 | 初期コメント |

### 出力データ（インスタンス変数）

| 変数名 | 型 | 説明 |
|--------|-----|------|
| @opportunity | Opportunity | 新規商談オブジェクト |
| @account | Account | 新規取引先オブジェクト |
| @accounts | Array | 選択可能取引先一覧 |
| @stage | Array | ステージ選択肢 |
| @contact | Contact | 関連連絡先（relatedパラメータ指定時） |
| @campaign | Campaign | 関連キャンペーン |

## 表示項目

### 基本情報セクション

| 項目名 | フィールド名 | 入力形式 | 必須 | 説明 |
|--------|------------|---------|------|------|
| 商談名 | name | テキスト | 必須 | 商談の名称 |
| ステージ | stage | 選択 | - | 現在のステージ（デフォルト: prospecting） |
| クローズ日 | closes_on | 日付 | - | 成約予定日 |
| 確度 | probability | 数値 | - | 成約確度（%） |
| 金額 | amount | 数値 | - | 商談金額 |
| 割引 | discount | 数値 | - | 割引額 |
| 取引先 | account | 選択/入力 | - | 既存選択または新規作成 |
| 担当者 | assigned_to | 選択 | - | ユーザー選択 |
| キャンペーン | campaign_id | 選択 | - | 関連キャンペーン |
| 背景情報 | background_info | テキストエリア | - | 補足情報 |
| タグ | tag_list | タグ入力 | - | カンマ区切り |

### アクセス権限セクション

| 項目名 | フィールド名 | 入力形式 | 説明 |
|--------|------------|---------|------|
| アクセス権限 | access | ラジオボタン | Public/Private/Shared |
| 共有先 | user_ids | マルチセレクト | accessがSharedの場合のみ表示 |

## イベント仕様

### 1-作成ボタンクリック

- **トリガー**: 「商談を作成」ボタンをクリック
- **処理**:
  1. account_assigned_toにopportunity_assigned_toをコピー
  2. フォームがAjaxでPOST送信される
  3. サーバー側でバリデーション実行
  4. 取引先の作成または選択処理
  5. 商談レコードの作成
  6. 連絡先との紐付け（指定時）
  7. 初期コメントの追加（入力時）
- **成功時**: 一覧画面更新または関連エンティティ詳細画面更新
- **失敗時**: エラーメッセージ表示、フォーム再表示

### 2-キャンセルリンククリック

- **トリガー**: 「キャンセル」リンクをクリック
- **処理**: Ajax経由でフォームを閉じる
- **遷移先**: フォーム表示前の状態に戻る

### 3-取引先選択/新規作成切替

- **トリガー**: 取引先ドロップダウンまたはテキスト入力
- **処理**: 既存選択または新規作成の切り替え

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 商談作成 | opportunities | INSERT | 商談レコード作成 |
| 取引先新規作成 | accounts | INSERT | 新規取引先作成（指定時） |
| 取引先紐付け | account_opportunities | INSERT | 関連レコード作成 |
| 連絡先紐付け | contact_opportunities | INSERT | 関連レコード作成（指定時） |
| キャンペーンカウント | campaigns | UPDATE | opportunities_count増加 |
| コメント作成 | comments | INSERT | 初期コメント作成（入力時） |
| タグ付与 | taggings | INSERT | タグ関連レコード作成 |
| バージョン記録 | versions | INSERT | 作成履歴記録 |

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

#### opportunities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | 作成者 |
| INSERT | campaign_id | 選択されたキャンペーンID | |
| INSERT | assigned_to | フォーム入力値 | 担当者 |
| INSERT | name | フォーム入力値 | 商談名 |
| INSERT | stage | フォーム入力値（デフォルト: prospecting） | |
| INSERT | probability | フォーム入力値 | 確度 |
| INSERT | amount | フォーム入力値 | 金額 |
| INSERT | discount | フォーム入力値 | 割引 |
| INSERT | closes_on | フォーム入力値 | クローズ日 |
| INSERT | access | フォーム入力値 | アクセス権限 |

## メッセージ仕様

| 種別 | メッセージキー | 表示条件 |
|------|---------------|---------|
| エラー | :missing_opportunity_name | 商談名が未入力 |
| エラー | :share_opportunity | Shared選択時に共有先未指定 |
| エラー | バリデーションエラー | 数値フィールドの形式エラー等 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 関連エンティティが見つからない | respond_to_related_not_foundでエラーレスポンス |
| バリデーションエラー | フォーム再表示、エラーメッセージ表示 |

## 備考

- フォームはremote: trueでAjax送信される
- デフォルトステージはSetting[:opportunity_default_stage]またはprospecting
- 取引先の担当者は商談の担当者と同期される（JavaScript）
- カスタムフィールドはFieldGroupの設定に応じて動的表示

---

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

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

### 推奨読解順序

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

商談作成時に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | opportunity.rb | `app/models/entities/opportunity.rb` | バリデーション（84-87行目）、save_with_account_and_permissions（108-119行目） |

**読解のコツ**: stageのバリデーション（87行目）でSetting.unrollから動的に許可値を取得している点に注目。

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

コントローラーのnewとcreateアクションを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | opportunities_controller.rb | `app/controllers/entities/opportunities_controller.rb` | newアクション（34-52行目）、createアクション（67-87行目） |

**主要処理フロー（new）**:
1. **36行目**: デフォルト属性の設定（stage, access）
2. **37-38行目**: 新規取引先オブジェクト生成、取引先一覧取得
3. **40-48行目**: 関連エンティティの取得（relatedパラメータ処理）

**主要処理フロー（create）**:
1. **70行目**: save_with_account_and_permissionsによる保存
2. **71行目**: コメント追加
3. **72-79行目**: 呼び出し元に応じたサイドバーデータ更新

#### Step 3: ビューテンプレートを理解する

フォームの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _new.html.haml | `app/views/opportunities/_new.html.haml` | フォーム構造、パーシャル呼び出し |
| 3-2 | _top_section.html.haml | `app/views/opportunities/_top_section.html.haml` | 入力項目の配置 |

**主要処理フロー**:
- **16行目**: 作成ボタンのonclickでaccount_assigned_toをopportunity_assigned_toからコピー

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

```
OpportunitiesController#new
    │
    ├─ before_action :load_settings
    │      └─ @stage = Setting.unroll(:opportunity_stage)
    │
    ├─ Opportunity.new (CanCanによる自動生成)
    │
    ├─ @opportunity.attributes = {...}
    │
    ├─ Account.new, Account.my.order('name')
    │
    └─ respond_with(@opportunity)

OpportunitiesController#create
    │
    ├─ @opportunity.save_with_account_and_permissions(params)
    │      │
    │      ├─ Account.create_or_select_for
    │      │
    │      ├─ AccountOpportunity.new
    │      │
    │      ├─ opportunity.save
    │      │
    │      └─ contacts << Contact.find(...)
    │
    ├─ @opportunity.add_comment_by_user
    │
    ├─ get_data_for_sidebar (呼び出し元に応じて)
    │
    └─ respond_with(@opportunity)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| opportunity.rb | `app/models/entities/opportunity.rb` | モデル | 保存ロジック |
| opportunities_controller.rb | `app/controllers/entities/opportunities_controller.rb` | コントローラ | new/createアクション |
| _new.html.haml | `app/views/opportunities/_new.html.haml` | テンプレート | 新規作成フォーム |
| _top_section.html.haml | `app/views/opportunities/_top_section.html.haml` | テンプレート | 入力項目 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
