# 機能設計書 7-取引先編集

## 概要

本ドキュメントは、Fat Free CRMシステムにおける取引先編集機能の設計を定義する。この機能は、既存の取引先情報を更新する。

### 本機能の処理概要

取引先編集機能は、既存の取引先レコードの情報を更新するための機能である。基本情報の変更、住所情報の更新、アクセス権限の変更が可能である。

**業務上の目的・背景**：取引先の情報は時間とともに変化する（社名変更、移転、担当者変更など）。本機能により、これらの変更を迅速にシステムに反映し、常に最新の情報を維持できる。また、アクセス権限の変更により、担当者の異動や組織変更に対応できる。

**機能の利用シーン**：
- 取引先から連絡先変更の通知を受けた際の情報更新
- 担当者異動に伴うassigned_toの変更
- アクセス権限（Public/Private/Shared）の変更
- カテゴリ分類の見直し

**主要な処理内容**：
1. 編集フォームの表示（editアクション）
2. 前回表示していた取引先IDの取得（インライン編集対応）
3. 入力データのバリデーション
4. 取引先レコードの更新
5. サイドバーデータの更新（一覧画面からの呼び出し時）

**関連システム・外部連携**：本機能は外部システムとの連携はなく、内部データベースの更新のみで構成される。

**権限による制御**：`load_and_authorize_resource`により、ユーザーが編集権限を持つ取引先のみ更新可能。所有者または適切な権限を持つユーザーのみ編集可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | 取引先編集フォーム | 主画面 | 取引先情報更新 |

## 機能種別

データ更新（UPDATE操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 取引先ID | 存在する取引先IDであること |
| account[name] | String | Yes | 取引先名 | 必須、64文字以内、一意性チェック（設定による） |
| account[category] | String | No | カテゴリ | 設定で定義されたカテゴリ値 |
| account[access] | String | No | アクセス権限 | Public/Private/Shared |
| account[user_ids] | Array | No | 共有ユーザーID | access=Sharedの場合は必須 |
| account[assigned_to] | Integer | No | 担当者ID | 存在するユーザーID |
| その他 | - | No | 作成と同様のパラメータ | 作成と同様 |
| previous | String/Integer | No | 前回表示取引先ID | インライン編集用 |

### 入力データソース

- フォーム入力
- URLパラメータ（:id、:previous）
- セッション情報（current_user）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @account | Account | 更新された取引先（成功時）/エラー情報付き取引先（失敗時） |
| @previous | Account/Integer | 前回表示していた取引先（インライン編集用） |

### 出力先

- AJAX応答（edit.js.haml、update.js.haml）
- データベース（accounts、versions）

## 処理フロー

### 処理シーケンス

```
【editアクション】
1. load_and_authorize_resourceによるリソースロード・認可
2. previousパラメータから前回取引先を取得
   └─ Account.my(current_user).find_by_id(detect_previous_id)
3. respond_with(@account)

【updateアクション】
1. load_and_authorize_resourceによるリソースロード・認可
2. accessパラメータが存在する場合、先にaccess属性を設定
   └─ user_ids=メソッドがaccess値に依存するため
3. @account.update(resource_params)実行
4. 成功時かつ一覧画面からの呼び出し時:
   └─ get_data_for_sidebarでサイドバー更新
5. respond_with(@account)
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[認可チェック]
    B --> C{認可OK?}
    C -->|No| D[403エラー]
    C -->|Yes| E[access属性設定]
    E --> F[update実行]
    F --> G{成功?}
    G -->|No| H[エラー表示]
    G -->|Yes| I{一覧画面から?}
    I -->|Yes| J[サイドバー更新]
    I -->|No| K[AJAX応答]
    J --> K
    K --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 編集権限 | 所有者または適切な権限を持つユーザーのみ編集可能 | 常時 |
| BR-002 | access先行設定 | user_ids設定前にaccess属性を設定 | access変更時 |
| BR-003 | バリデーション | 作成時と同様のバリデーションルールを適用 | 常時 |
| BR-004 | 履歴記録 | 変更内容はPaperTrailで記録 | 常時 |

### 計算ロジック

特に計算ロジックは存在しない。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 取引先更新 | accounts | UPDATE | 取引先情報の更新 |
| 住所更新 | addresses | UPDATE/INSERT/DELETE | 住所情報の更新 |
| 履歴作成 | versions | INSERT | 更新履歴（PaperTrail） |
| 権限更新 | permissions | UPDATE/INSERT/DELETE | 共有権限の更新 |

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

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | 全カラム | フォーム入力値 | バリデーション後 |

#### versions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | event, whodunnit, item_type, item_id, object_changes | event: :update, 変更内容 | PaperTrail |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未ログイン状態でアクセス | ログイン画面へリダイレクト |
| 403 | 認可エラー | 編集権限がない | エラーメッセージ表示 |
| 404 | 未検出 | 指定IDの取引先が存在しない | 一覧画面へリダイレクト |
| 422 | バリデーションエラー | 必須項目未入力、一意性違反等 | エラーメッセージ表示 |

### リトライ仕様

本機能ではリトライ処理は実装されていない。バリデーションエラー時はフォームを再表示。

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

取引先、住所、権限の更新はActiveRecordのトランザクション内で実行される。いずれかが失敗した場合はロールバック。

## パフォーマンス要件

- 更新処理時間: 2秒以内を目標

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

- 認証: Deviseによるユーザー認証が必須
- 認可: CanCanによるアクセス権限制御
- CSRF: protect_from_forgeryによる対策
- 監査ログ: PaperTrailによる変更履歴

## 備考

- インライン編集時はpreviousパラメータで前回取引先を保持
- access属性はuser_idsより先に設定する必要がある

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | account.rb | `app/models/entities/account.rb` | バリデーション、PaperTrail設定 |
| 1-2 | permission.rb | `app/models/users/permission.rb` | 権限モデル |

**読解のコツ**: has_paper_trail（68行目）で変更履歴が自動記録されることを確認。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | routes.rb | `config/routes.rb` | PATCH/PUT /accounts/:id ルート |
| 2-2 | accounts_controller.rb | `app/controllers/entities/accounts_controller.rb` | edit, updateアクション |

**主要処理フロー**:
1. **47-51行目**: editアクション - フォーム表示
2. **48行目**: detect_previous_idで前回取引先ID取得
3. **70-76行目**: updateアクション - 更新処理
4. **73行目**: access属性の先行設定
5. **74行目**: update実行とサイドバー更新

#### Step 3: 基底クラスのロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | application_controller.rb | `app/controllers/application_controller.rb` | detect_previous_idメソッド |
| 3-2 | entities_controller.rb | `app/controllers/entities_controller.rb` | resource_paramsメソッド |

**主要処理フロー**:
- **275-280行目（application_controller.rb）**: detect_previous_id - 前回ID抽出
- **124-126行目（entities_controller.rb）**: resource_params - パラメータ許可

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | edit.js.haml | `app/views/accounts/edit.js.haml` | 編集フォーム表示JS |
| 4-2 | _edit.html.haml | `app/views/accounts/_edit.html.haml` | 編集フォーム |
| 4-3 | update.js.haml | `app/views/accounts/update.js.haml` | 更新結果JS |

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

```
AccountsController#edit
    │
    ├─ load_and_authorize_resource (Account.find)
    ├─ detect_previous_id
    │   └─ params[:previous].to_i
    └─ Account.my(current_user).find_by_id(@previous)

AccountsController#update
    │
    ├─ load_and_authorize_resource (Account.find)
    │
    ├─ @account.access = params[:account][:access]
    │   └─ （user_ids設定前にaccess設定が必要）
    │
    └─ @account.update(resource_params)
           │
           ├─ 【成功時】
           │   └─ get_data_for_sidebar (if called_from_index_page?)
           │
           └─ 【失敗時】
               └─ エラー情報付き@account返却
```

### データフロー図

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

URLパラメータ ───▶ AccountsController#edit ───▶ @account
(id, previous)         │                        @previous
                       │                            │
                       ▼                            ▼
                 Account.find               edit.js.haml
                       │                            │
                       ▼                            ▼
                    編集フォーム表示

フォーム入力 ───▶ AccountsController#update ───▶ @account (成功/失敗)
(account params)       │
                       │
                       ├─ access設定
                       ├─ update実行
                       │
                       ▼
               データベース
               (accounts,
                addresses,
                permissions,
                versions)
                       │
                       ▼
               update.js.haml
                       │
                       ▼
               AJAX画面更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| accounts_controller.rb | `app/controllers/entities/accounts_controller.rb` | ソース | メインコントローラー |
| application_controller.rb | `app/controllers/application_controller.rb` | ソース | detect_previous_id |
| entities_controller.rb | `app/controllers/entities_controller.rb` | ソース | resource_params |
| account.rb | `app/models/entities/account.rb` | ソース | 取引先モデル |
| edit.js.haml | `app/views/accounts/edit.js.haml` | テンプレート | 編集フォーム表示JS |
| _edit.html.haml | `app/views/accounts/_edit.html.haml` | テンプレート | 編集フォーム |
| update.js.haml | `app/views/accounts/update.js.haml` | テンプレート | 更新結果JS |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
