# 契約書機能 — 技術仕様（Monaka）

**最終更新: 2026-06-02**

## 概要

Monaka の **契約書** モジュール。GMOサインの「文書管理」「テンプレート」仕様を参考に段階実装（正本マッピング: `docs/gmo-sign-functional-spec.md`）。

## フェーズ1 — 入口・権限

`GET /contract`（`contract.index`）、`operators.perm_contract`、`module-nav` の契約書。

### 契約書トップ — 準備ハブ（初期設定同型）

`contract/index` 上部に **契約書の準備**（`contract/partials/_setup_hub`・`ContractSetupHubService`）。初期設定ハブと同様のタイル＋バッジで未設定を表示。

| タイル | リンク | 完了判定（概要） |
|--------|--------|------------------|
| 会社情報 | `setting.business` | 既定会社の会社名・代表・住所・メールが揃う（初期設定ハブと同ロジック） |
| 印影・メールテンプレート | `contract.settings.edit`（印影は `#contract-seals`） | 印影1件以上かつ送付メールの件名・本文あり（既定文面可） |

全体バッジ **準備完了**／**未完了あり**。会社情報はステップ2の送信担当者候補・メール置換 `%送信者会社名%` 等に利用。契約を締結セクションは準備ハブの下に配置。一覧・テンプレートへのカードは2列。

### 契約開始 UI（ステップ1・文書の準備）

`contract/index` + `contract/partials/_instance_create_panel` は、送付フローを **4ステップ**（文書のアップロード → 署名依頼情報 → 署名位置の設定 → 確認して送信）で案内する。**ステップ1〜4** の画面は実装済み（実メール送信・署名画面は未接続）。

### ステップ2 — 署名依頼情報（GMOサイン風 UI）

`GET contract.instances.sign-request`（`sign_request.blade.php`）

| 画面項目 | 保存先（現状） | 備考 |
|----------|----------------|------|
| 封筒名 | `contract_instances.title` | 必須 |
| 送信担当者 | `custom_field_1` | 必須。**会社情報**を左の `select` から選ぶか、右の欄に手入力（コンボボックス） |
| 所属 | `custom_field_2` | 任意。送信担当者と同一行（2列） |
| 保管先フォルダ | `contract_storage_folder_id` → `storage_location`（名称同期） | `contract_storage_folders` から選択。**文書一覧 → フォルダ管理**（`contract.folders.*`）で登録 |
| 詳細情報編集（折りたたみ） | 下表 | 「詳細情報を編集」でインライン展開（GMOサイン同型） |
| 自社ワークフロー | `internal_workflow_enabled`・`contract_instance_approvers` | **設定する**（既定 OFF）。契約書を送る**前**の社内チェック。ON で承認者追加（承認順・所属・氏名・メール）。氏名はオペレーター管理から選択または手入力。承認メール送信は未接続 |
| 署名者 | `contract_instance_signers` | **＋** で GMO 風モーダル追加・編集・削除。署名方法は **契約印タイプ**（`contract_seal`）のみ。依頼先は **自社署名者**／**送信先**。**自社署名者**の氏名は **会社情報の代表者**（対応会社で選んだ会社の `business_settings.representative`）から選択または手入力。メールは同会社の `email`。アクセスコード＝署名画面閲覧用パスワード。署名者変更＝送信先担当者による署名者差し替え許可（`allow_signer_change`）。文書ごとの依頼内容（署名／文書確認／送付しない）は `document_actions` JSON。送信・公開 URL は未接続 |
| 受領者 | `recipients_enabled`・`contract_instance_recipients` | **設定する**（既定 OFF）。締結**後**の再確認送付。ワークフロー帯と同型（＋モーダル・一覧・編集削除）。氏名・メール（オペレーター選択可）。OFF 保存時は受領者行をクリア。送信は未接続 |

**詳細情報編集**（折りたたみ内）: `contract_date`・`contract_expires_at`・`amount`＋`amount_currency`・`custom_field_3`（保管場所）・`note1`〜`note3`

`PUT contract.instances.sign-request` — 下書き保存 / **署名位置の設定へ**（署名者0件はエラー）→ `contract.instances.sign-position`。

### ステップ3 — 署名位置の設定

`GET contract.instances.sign-position`（`sign_position.blade.php`）

| 画面項目 | 保存先 | 備考 |
|----------|--------|------|
| 配置パーツ | `contract_instance_placements` | 文書・ページ・送信者／署名者・種別（署名／テキスト／日付／チェック）・座標（%） |
| 左パレット | — | 冒頭に操作説明。**送信者**（黄）・**署名者**ごと（色分け・役割説明）に **テキスト**／**日付**／**署名（印鑑）** を縦並び表示。各欄は種別名＋補足（例: カレンダーで日付選択）付きでドラッグ配置。文書の依頼内容が署名なら印鑑枠含む、文書確認ならテキスト・日付のみ、送付しないなら非表示 |
| PDF キャンバス | — | PDF.js。**880px 以上**の大きなプレビュー。左パーツを **ドラッグ＆ドロップ** で配置（クリック配置廃止）。新規配置の既定サイズは **1行相当**（テキスト・日付 高さ約2.2%・幅13〜18%）。配置枠クリックで設定パネル（GMO 同趣旨）：**テキスト**＝入力ガイド（署名画面の案内文・**編集プレビューの枠内にも表示**・完成 PDF には非印字）・必須・フォントサイズ。**日付**＝署名日自動埋め込み・**カレンダー（`input type=date`）で日付選択**（`settings.selected_date`）・必須・表示形式・フォントサイズ。日付枠選択時も枠内カレンダー入力可。自動埋め込み ON 時は署名日プレビュー。右下ハンドルで枠サイズ変更。ドラッグで移動 |
| 文書タブ | — | 複数 PDF 切替 |
| ページ表示 | — | **全ページ縦並び**（スクロールで閲覧）。各ページに「N / 合計 ページ」見出し。配置はドロップしたページに紐づく |

`PUT contract.instances.sign-position` — `placements_json` で一括保存。下書き保存 / **確認画面へ** → `contract.instances.confirm`。署名枠未配置時は不可視署名（印影なし）想定（GMO 同趣旨・送信処理は未接続）。

### ステップ4 — 確認して送信

`GET contract.instances.confirm`（`confirm.blade.php`）

| 画面項目 | 内容 |
|----------|------|
| 署名依頼情報 | 封筒名・対応会社・送信担当者・保管先・詳細情報（読み取り専用）。各セクションに **編集** リンク（ステップ1/2へ） |
| 文書 | PDF サムネイル・文書名・配置件数サマリ。クリックでモーダルプレビュー |
| 署名者 | 一覧テーブル（順番・区分・氏名・連絡先・署名方法） |
| 自社ワークフロー | ON 時のみ承認者一覧。送付後は `pending_approval` |
| 受領者 | ON 時のみ一覧 |
| 署名位置 | 文書ごとの配置件数サマリ |
| 差込み項目 | テンプレート由来時 |
| 送付メール | `ContractEmailTemplateMergeService` でプレビュー（サンプル署名者向け） |

`POST contract.instances.send` — 下書きのみ。自社ワークフローあり → `pending_approval`、自社署名者あり → `pending_self_sign`、それ以外 → `pending_counterparty_sign`。`sent_at` 更新後 **契約書詳細** へリダイレクト。メール・署名 URL は未接続。

| カード | 動作 |
|--------|------|
| **文書を選択** | `POST contract.instances.store`（`creation_mode=blank`）。**PDF 複数**（`document_files[]`・各10MB以下・最大20件）をアップロードし `contract_instance_documents` に保存 → **`GET contract.instances.upload`**（ステップ1継続）。**送付件名**（`contract_instances.title`）と **PDF ごとの文書名**（`document_names[id]` → `contract_instance_documents.name`）の編集・追加アップロード・削除は `PUT contract.instances.upload` / `DELETE contract.instances.documents.destroy`。アップロード済み PDF は **1ページ目のサムネイル**を表示し、**クリックでモーダルプレビュー**（既存 `pdf.embed-viewer`・PDF.js）。フラッシュメッセージは `layouts/app` のみ（契約画面での二重表示はしない）。**署名依頼情報の入力へ**で `contract.instances.sign-request`（ステップ2）。基本情報（相手方・金額等）は `contract.instances.show`。 |
| **テンプレートから選択** | 同 `store`（`creation_mode=template`）→ **`contract.instances.sign-request`**（ステップ2）へ。 |

`GET contract.instances.create` は `contract.index?mode=blank|template#contract-create` へリダイレクト。

## フェーズ2 — テンプレート

- 基本設定（モーダル）→ 詳細（署名者・受領者・文書 PDF・差込み・配置パーツ）
- `allow_content_change_on_send`（設定固定の逆）
- `sharing_scope`（`owner` / `all_operators`）
- お気に入り、**複製**（`POST contract.templates.duplicate`）
- 署名方法: 電子署名・実印・立会人型・当事者型

## フェーズ3 — 契約書設定

`GET contract.settings.edit`（`contract/settings/edit.blade.php`）

印影最大3件、**メールテンプレート**（**自社の表示名は契約書ごと**にステップ2「送信担当者」`custom_field_1` で設定。契約書設定画面には置かない）（送付／リマインド／期限切れの **件名** `*_message_subject` と **本文** `*_message_body`）。

**メール既定文面**（`App\Support\Contract\ContractEmailTemplateDefaults`）は **送付・リマインド・期限切れの3区分すべて** に件名・本文を持つ。新規 `contract_user_settings` 作成時、および当該区分の件名・本文が**両方空**の既存行に `ContractUserSetting::forUser` で自動投入（区分ごと独立。送付だけカスタム済みでもリマインド／期限切れだけ補完する）。管理ユーザー登録（`RegisterController`）時も同メソッドで作成。送付件名は `%契約書名%のご確認と署名のお願い`、リマインド件名は `【リマインド】%契約書名%のご署名をお願いします`、期限切れ件名は `【ご案内】%契約書名%の署名期限が過ぎました`。

**置き換え文字**（送信時に差し替え・`ContractEmailTemplateMergeService`）: `%契約書名%`（送付件名・既定文面に含むが **チップ UI には出さない**）・`%受信者名%`・`%送信者会社名%`・`%契約書URL%`・`%期限日%`・`%署名方式%`・`%送信者連絡先%`。契約書設定画面のチップ UI（`contract/partials/_email_placeholder_chips`）は **画面上部に追従する sticky バー** と、送付／リマインド／期限切れ **各区分直上のインライン行** の二段構え（スクロールしても挿入可能）。クリックで最後にフォーカスした件名・本文へ挿入。

テンプレートから契約作成時は `ContractInstanceFromTemplateService` が設定値を `contract_instances` へコピー。印影追加は **アップロード** または **メディアから選ぶ**（`partials.monaka_image_picker`・`GET media.api.list`）。画面にピッカーを `@include` すること（未読込時は「メディア選択を読み込めませんでした」）。

**印影と会社の紐づけ**（`contract_seal_images.business_setting_id`・nullable）: 印影登録時に初期設定の **会社情報**（`setting.business`）から任意で1社を選べる。「紐づけない」も可。一覧に紐づけ先会社名（または「紐づけなし」）を表示。契約書作成時の印影選択フィルタは後続。

メール送信処理への接続は未実装（保存・差し替えロジックのみ）。

## フェーズ4 — 契約書一覧（文書管理）

テーブル: **`contract_instances`**（画面名は契約書一覧）

### データ項目（GMO 1.1 対応）

`document_number`, `title`, `file_name`, `status`, `counterparty`, `contract_date`, `contract_expires_at`, `amount`, `amount_currency`, `storage_location`, `note1`〜`note3`, `custom_field_1`〜`5`, `auto_renewal`, `renewal_period_months`, `cancellation_notice_at`, `created_at`, `completed_at`

### ステータス（GMO 1.2 + 下書き）

| status | 表示 |
|--------|------|
| `draft` | 下書き |
| `pending_approval` | 承認待ち |
| `pending_self_sign` | 自社署名待ち |
| `pending_counterparty_sign` | 送信先署名待ち |
| `completed` | 署名完了 |
| `expired` | 期限切れ |
| `declined` | 辞退 |
| `cancelled` | 取消 |

旧値 `sent` / `signing` / `rejected` は表示時に正規化。

### 対応会社（`business_setting_id`）

`contract_instances.business_setting_id`（nullable・`business_settings.id`）は、**この契約書をどの会社名義で送付するか**を表す。初期設定の **会社情報**（`setting.business`）から選ぶ。

- **作成時**（契約書トップの PDF アップロード／テンプレートから作成）: 「次へ」押下時に **対応会社選択モーダル**（`contract/partials/_company_select_modal`）を表示。会社が1件だけでも必ず選ばせる。選択後 `business_setting_id` を保存し、送信担当者（`custom_field_1`）に会社の表示名を反映。
- **文書確認**（`contract.instances.upload`）: 未選択のまま「署名依頼情報の入力へ」を押すと同モーダル。選択済みは画面上部に表示（署名依頼画面でも変更可）。
- **署名依頼情報**（`contract.instances.sign-request`）: フォーム上部に **対応会社**（必須・select）。変更時は送信担当者名にも連動。契約書設定画面ではなくここで契約ごとに確定する。

会社情報が0件のユーザーはモーダル／必須バリデーションは出さない（先に会社情報登録を案内）。

### 一覧の会社別タブ

`contract.instances.index` 上部に **すべて**／**会社ごと**／（あれば）**未設定** のタブ。件数バッジ付き。`?company=all|{business_setting_id}|unassigned` で `ContractInstanceSearchService` が絞り込む。検索フォーム送信時も `company` を維持。「すべて」タブの表に **対応会社** 列を表示。

### 検索（GMO 1.3）

`ContractInstanceSearchService` — フリーワード、ステータス、日付範囲、金額範囲、自動更新、**対応会社**（`company` クエリ）。

### CSV（GMO 1.4）

`GET contract.instances.export` — UTF-8 BOM、上限 `config('putage.contract_document_csv_max_rows')`（既定 500）。

### 保管先フォルダ（GMO フォルダ管理）

| ルート | 動作 |
|--------|------|
| `GET contract.folders.index` | 文書一覧から遷移。フォルダ名一覧・新規作成・編集・削除 |
| `POST/PUT/DELETE contract.folders.*` | `contract_storage_folders`（`user_id`・`name` unique） |

削除時は当該フォルダに紐づく契約書の `contract_storage_folder_id` / `storage_location` をクリア。

### ルート

`contract.instances.index|create|store|upload|sign-request|sign-position|confirm|show|update|send|status|cancel|destroy|export`

送付はステータス遷移のみ（メール・署名 URL 未接続）。

## 未実装

- 実際の署名／送付処理（メール送信・署名者向け公開 URL・電子署名）
- 配置パーツのリサイズ・テンプレート座標の自動引き継ぎ
- 承認ワークフロー自動遷移
- 期限切れバッチ
- 外部 API（GMO 3章）
- 非同期 CSV メール通知

## 関連

- `docs/gmo-sign-functional-spec.md` — MANUS 整理稿との対応表
- `docs/manual.md` — 操作説明
