カスタム管理ページとして add_submenu_page() で「会員管理 > 契約管理」に登録。契約情報はカスタムテーブル lms_contracts で管理(WP投稿タイプではなく独自テーブルを使用)。編集フォームは admin_post_{action} フックで処理し、保存後は wp_redirect() でリダイレクト。契約履歴は lms_contract_history テーブルから取得し、変更のたびに lms_log_contract_change() で記録。ページ上部の通知は $_GET['message'] パラメータで制御。
アクセス権限: manage_lms_contracts カスタムケイパビリティ。メニュー表示・フォーム保存・削除のそれぞれで current_user_can('manage_lms_contracts') を検証。対象法人の変更(セレクトボックス)は manage_options 権限のみ許可(誤操作防止)。利用状況メタボックスは全事務局スタッフに読み取り専用で表示。削除操作は追加確認ダイアログを必須とし、物理削除ではなくステータス deleted への論理削除で実装。
主テーブル: lms_contracts(id, corp_id, start_date, end_date, amount, max_offices, max_personal_accounts, renewal_status, status, notes, created_at, updated_at)。変更履歴テーブル: lms_contract_history(id, contract_id, field_name, old_value, new_value, changed_by, changed_at)。対象法人は lms_corporations テーブルの corp_id を外部キー参照。利用状況は lms_accounts テーブルから集計(契約IDで絞り込み)。
編集画面では行アクション不要(一覧画面 B-CTR-001 一覧側で定義)。「← 一覧に戻る」で admin.php?page=lms-contracts へ遷移。「全履歴を表示」はモーダルまたは別ページで全 lms_contract_history を表示(Phase1では別ページ遷移で実装)。「削除」は確認ダイアログ(confirm())後に admin_post_lms_delete_contract へ POST。更新ボタンは admin_post_lms_save_contract アクションへ POST。
カスタム管理ページのため WP 標準 Screen Options は自動適用されない。add_screen_option() でメタボックスの表示/非表示を独自実装する場合は set_screen_option フィルタも合わせて追加。Phase1 では全メタボックスを常時表示とし Screen Options は実装しない方針(工数削減)。メタボックスの折りたたみ状態は wp_usermeta(キー: lms_contract_edit_collapsed)にJSON形式で保存し、次回アクセス時に復元する。
フォーム送信時に wp_nonce_field('lms_save_contract_' . $contract_id, 'lms_contract_nonce') でNonce検証(契約ID付きで生成し、CSRF対策を強化)。契約金額は absint() で整数化後に保存。日付フィールドは正規表現 /^\d{4}-\d{2}-\d{2}$/ で形式検証し、DateTime クラスで妥当性チェック(存在しない日付を拒否)。終了日が開始日以前の場合はエラーを返す。テキストは sanitize_textarea_field() でサニタイズ。契約履歴テーブルへの書き込みはメイン保存と同一 DB トランザクション内で行い、履歴欠損を防ぐ($wpdb->query('START TRANSACTION'))。利用状況の集計クエリは COUNT() + GROUP BY corp_id で実装し、レスポンス改善のため set_transient()(TTL: 5分)でキャッシュ。大量法人(2,000社超え時)の法人セレクトボックスは SELECT2 相当の非同期検索に切り替えることを検討。