# 08_case_permission.md # 案件管理 権限設計 ## 1. 概要 本仕様書は、案件管理システムにおける権限設計を定義する。 - 権限は個人単位で付与(役職ベースではない) - 権限マスタ(`master/permissions.json`)に案件管理用権限を追加 - 各画面・機能は権限に応じて表示/非表示を制御 --- ## 2. 権限一覧(案件管理用) | 権限キー | 説明 | 主な対象 | |---------|------|---------| | **閲覧系** ||| | `case_list_view` | 案件一覧を表示できる | 全従事者 | | `case_detail_view_own` | 自案件(担当案件)の詳細を表示できる | 調査員、営業 | | `case_detail_view_all` | 全案件の詳細を表示できる | 管理者、会計 | | `case_phone_duty` | 電話対応担当(終了案件以外の全件表示) | 受付担当 | | **編集系(基本情報)** ||| | `case_edit_basic` | 案件基本情報を編集できる(自案件) | 管理者、営業 | | `case_edit_basic_all` | 全案件の基本情報を編集できる | 管理者 | | **編集系(聴取内容)** ||| | `case_edit_hearing` | 聴取内容を入力・編集できる(自案件) | 調査員 | | `case_edit_hearing_all` | 全案件の聴取内容を編集できる | 管理者 | | **編集系(調査分析)** ||| | `case_edit_analysis` | 調査分析を入力・編集できる(自案件) | 調査員、管理者 | | `case_edit_analysis_all` | 全案件の調査分析を編集できる | 管理者 | | **編集系(近隣確認)** ||| | `case_edit_nearby` | 近隣確認を入力・編集できる(自案件) | 調査員 | | `case_edit_nearby_all` | 全案件の近隣確認を編集できる | 管理者 | | **編集系(経費)** ||| | `case_edit_expense_own` | 自案件の経費を入力・編集できる | 調査員、営業 | | `case_edit_expense_all` | 全案件の経費を編集できる | 管理者、会計 | | `case_expense_approve_own` | 自案件の経費を承認できる | 管理者 | | `case_expense_approve_all` | 全案件の経費を承認できる | 管理者 | | **編集系(調査経過)** ||| | `case_edit_progress` | 調査経過を入力・編集できる(自案件) | 調査員 | | `case_edit_progress_all` | 全案件の調査経過を編集できる | 管理者 | | **編集系(証拠資料)** ||| | `case_upload_evidence_own` | 自案件に証拠資料(写真等)をアップロードできる | 調査員 | | `case_upload_evidence_all` | 全案件に証拠資料をアップロードできる | 管理者 | | **編集系(担当者ID)** ||| | `case_edit_sales_person` | 営業担当IDを編集できる | 管理者のみ | | `case_edit_investigator` | 調査員IDを編集できる | 管理者のみ | | `case_edit_manager` | 管理者IDを編集できる | 管理者のみ | | **ロック系** ||| | `case_lock` | 案件をロックできる(営業確認) | 営業担当、管理者 | | `case_edit_locked` | ロック済み案件の基本情報を編集できる | 管理者のみ | | **報告書・書類系** ||| | `case_generate_document_own` | 自案件の報告書を生成できる | 調査員、管理者 | | `case_generate_document_all` | 全案件の報告書を生成できる | 管理者、会計 | | `case_invoice_issue_own` | 自案件の請求書を発行できる | 管理者、会計 | | `case_invoice_issue_all` | 全案件の請求書を発行できる | 管理者、会計 | | **経費精算系** ||| | `case_reimbursement_request` | 承認済み経費の精算請求ができる | 調査員 | | `case_reimbursement_approve` | 精算請求を承認し支払処理ができる | 管理者、会計 | | `my_expense_view` | 自分の経費精算状況をマイページで確認できる | 全従事者 | | **成果物チェック系** ||| | `case_readiness_confirm` | 成果物チェックリストの管理者確認ができる | 管理者 | | **特例アクセス系** ||| | `case_emergency_set_own` | 自案件に特例アクセスを設定できる | 管理者 | | `case_emergency_set_all` | 全案件に特例アクセスを設定できる | 管理者 | | **新規案件登録系** ||| | `case_create_from_sales` | 営業管理から新規案件登録できる(自動ロック) | 営業担当、上級調査職 | | `case_create_from_cases` | 案件管理から新規案件登録できる(未ロック) | 事務職、管理者 | | **連絡系** ||| | `case_contact_owner` | 担当者にメール連絡できる | 電話対応担当 | | **拠点別アクセス系** ||| | `branch_view_self` | 自拠点の一覧表示 | 全従事者 | | `branch_view_other` | 他拠点の一覧表示 | 管理者、本部 | | `branch_view_all` | 全拠点の一覧表示 | 事業主 | --- ## 3. 権限マスタへの追加 `master/permissions.json` に以下の権限を追加する。 ```json { "permissions": [ ...既存の権限..., { "code": "case_list_view", "name": "案件一覧表示", "category": "案件管理" }, { "code": "case_detail_view_own", "name": "自案件詳細表示", "category": "案件管理" }, { "code": "case_detail_view_all", "name": "全案件詳細表示", "category": "案件管理" }, { "code": "case_phone_duty", "name": "電話対応担当", "category": "案件管理" }, { "code": "case_edit_basic", "name": "案件基本情報編集(自案件)", "category": "案件管理" }, { "code": "case_edit_basic_all", "name": "案件基本情報編集(全案件)", "category": "案件管理" }, { "code": "case_edit_hearing", "name": "聴取内容編集(自案件)", "category": "案件管理" }, { "code": "case_edit_hearing_all", "name": "聴取内容編集(全案件)", "category": "案件管理" }, { "code": "case_edit_analysis", "name": "調査分析編集(自案件)", "category": "案件管理" }, { "code": "case_edit_analysis_all", "name": "調査分析編集(全案件)", "category": "案件管理" }, { "code": "case_edit_nearby", "name": "近隣確認編集(自案件)", "category": "案件管理" }, { "code": "case_edit_nearby_all", "name": "近隣確認編集(全案件)", "category": "案件管理" }, { "code": "case_edit_expense_own", "name": "経費編集(自案件)", "category": "案件管理" }, { "code": "case_edit_expense_all", "name": "経費編集(全案件)", "category": "案件管理" }, { "code": "case_expense_approve_own", "name": "経費承認(自案件)", "category": "案件管理" }, { "code": "case_expense_approve_all", "name": "経費承認(全案件)", "category": "案件管理" }, { "code": "case_edit_progress", "name": "調査経過編集(自案件)", "category": "案件管理" }, { "code": "case_edit_progress_all", "name": "調査経過編集(全案件)", "category": "案件管理" }, { "code": "case_upload_evidence_own", "name": "証拠資料アップロード(自案件)", "category": "案件管理" }, { "code": "case_upload_evidence_all", "name": "証拠資料アップロード(全案件)", "category": "案件管理" }, { "code": "case_edit_sales_person", "name": "営業担当ID編集", "category": "案件管理" }, { "code": "case_edit_investigator", "name": "調査員ID編集", "category": "案件管理" }, { "code": "case_edit_manager", "name": "管理者ID編集", "category": "案件管理" }, { "code": "case_lock", "name": "案件ロック", "category": "案件管理" }, { "code": "case_edit_locked", "name": "ロック済案件基本情報編集", "category": "案件管理" }, { "code": "case_generate_document_own", "name": "報告書生成(自案件)", "category": "案件管理" }, { "code": "case_generate_document_all", "name": "報告書生成(全案件)", "category": "案件管理" }, { "code": "case_invoice_issue_own", "name": "請求書発行(自案件)", "category": "案件管理" }, { "code": "case_invoice_issue_all", "name": "請求書発行(全案件)", "category": "案件管理" }, { "code": "case_reimbursement_request", "name": "経費精算請求", "category": "案件管理" }, { "code": "case_reimbursement_approve", "name": "経費精算承認・支払", "category": "案件管理" }, { "code": "my_expense_view", "name": "自分の経費精算状況確認", "category": "マイページ" }, { "code": "case_readiness_confirm", "name": "成果物チェックリスト確認", "category": "案件管理" }, { "code": "case_emergency_set_own", "name": "特例アクセス設定(自案件)", "category": "案件管理" }, { "code": "case_emergency_set_all", "name": "特例アクセス設定(全案件)", "category": "案件管理" }, { "code": "case_create_from_sales", "name": "営業管理からの新規案件登録", "category": "案件管理" }, { "code": "case_create_from_cases", "name": "案件管理からの新規案件登録", "category": "案件管理" }, { "code": "case_contact_owner", "name": "担当者にメール連絡", "category": "案件管理" }, { "code": "branch_view_self", "name": "自拠点一覧表示", "category": "案件管理" }, { "code": "branch_view_other", "name": "他拠点一覧表示", "category": "案件管理" }, { "code": "branch_view_all", "name": "全拠点一覧表示", "category": "案件管理" } ] } ``` --- ## 4. 権限の付与方法 ### 4.1 従事者マスタとの連動 - 各従事者(`employees.json`)に `permissions` 配列で権限を付与 - 権限は個別にチェックボックスで選択 ```json { "employees": [ { "id": "00001", "name": "真柴 直也", "company": "SCSPS", "branch_code": "01", "permissions": [ "case_list_view", "case_detail_view_all", "case_phone_duty", "case_edit_basic_all", "case_edit_hearing_all", "case_edit_analysis_all", "case_edit_nearby_all", "case_edit_expense_all", "case_expense_approve_all", "case_edit_progress_all", "case_upload_evidence_all", "case_edit_sales_person", "case_edit_investigator", "case_edit_manager", "case_lock", "case_edit_locked", "case_generate_document_all", "case_invoice_issue_all", "case_reimbursement_approve", "case_readiness_confirm", "case_emergency_set_all", "case_create_from_sales", "case_create_from_cases", "case_contact_owner", "branch_view_all", "my_expense_view" ] }, { "id": "00002", "name": "調査員A", "company": "SCSPS", "branch_code": "02", "permissions": [ "case_list_view", "case_detail_view_own", "case_edit_hearing", "case_edit_analysis", "case_edit_nearby", "case_edit_expense_own", "case_edit_progress", "case_upload_evidence_own", "case_generate_document_own", "case_reimbursement_request", "branch_view_self", "my_expense_view" ] }, { "id": "00003", "name": "会計担当B", "company": "SCSPS", "branch_code": "01", "permissions": [ "case_list_view", "case_detail_view_all", "case_edit_expense_all", "case_generate_document_all", "case_invoice_issue_all", "case_reimbursement_approve", "branch_view_self", "my_expense_view" ] }, { "id": "00004", "name": "管理者C", "company": "SCSPS", "branch_code": "01", "permissions": [ "case_list_view", "case_detail_view_all", "case_edit_basic_all", "case_edit_hearing_all", "case_edit_analysis_all", "case_edit_nearby_all", "case_edit_expense_all", "case_expense_approve_all", "case_edit_progress_all", "case_upload_evidence_all", "case_edit_sales_person", "case_edit_investigator", "case_edit_manager", "case_lock", "case_edit_locked", "case_generate_document_all", "case_invoice_issue_all", "case_reimbursement_approve", "case_readiness_confirm", "case_emergency_set_all", "case_create_from_cases", "branch_view_other", "my_expense_view" ] }, { "id": "00005", "name": "営業担当D", "company": "SCSPS", "branch_code": "01", "permissions": [ "case_list_view", "case_detail_view_own", "case_edit_basic", "case_edit_expense_own", "case_lock", "case_create_from_sales", "branch_view_self", "my_expense_view" ] }, { "id": "00010", "name": "電話対応担当E", "company": "SCSPS", "branch_code": "01", "permissions": [ "case_list_view", "case_detail_view_all", "case_phone_duty", "case_contact_owner", "branch_view_self", "my_expense_view" ] } ] } ``` --- ## 5. 画面制御ルール ### 5.1 案件一覧表示制御 | 権限 | 表示される案件 | |------|---------------| | `case_phone_duty` | 終了案件以外の全案件 | | `case_detail_view_all` | 全案件 | | `case_detail_view_own` | 自案件のみ(担当者ID一致) | ```javascript function getVisibleCases(cases, user) { // 電話対応担当 if (user.permissions.includes('case_phone_duty')) { return cases.filter(c => c.status !== '入金完了'); } // 全案件閲覧権限 if (user.permissions.includes('case_detail_view_all')) { return cases; } // 自案件閲覧権限 if (user.permissions.includes('case_detail_view_own')) { return cases.filter(c => isOwnCase(c, user.id)); } return []; } ``` ### 5.2 ボタン表示制御 ```javascript function hasPermission(permissionCode) { const user = JSON.parse(sessionStorage.getItem('user')); return user.permissions && user.permissions.includes(permissionCode); } // ボタン表示制御 if (!hasPermission('case_edit_basic')) { document.getElementById('btn-edit-basic').style.display = 'none'; } if (!hasPermission('case_reimbursement_request')) { document.getElementById('btn-reimbursement-request').style.display = 'none'; } if (!hasPermission('case_contact_owner')) { document.getElementById('btn-contact-owner').style.display = 'none'; } if (!hasPermission('case_lock')) { document.getElementById('btn-lock').style.display = 'none'; } ``` ### 5.3 タブ表示制御 - 権限がないタブは表示しない ### 5.4 データ範囲制御(自案件/全案件) - `case_detail_view_own` のみの場合、案件詳細表示時に担当者IDとログインユーザーIDを照合 - 一致しない場合は「権限がありません」と表示 --- ## 6. 新規案件登録時の権限分岐 | 入口 | 権限 | 営業担当ID | ロック | |------|------|-----------|--------| | 営業管理から | `case_create_from_sales` | 自動入力 | 自動ロック | | 案件管理から | `case_create_from_cases` | プルダウン選択 | 未ロック | ```javascript const urlParams = new URLSearchParams(window.location.search); const mode = urlParams.get('mode'); if (mode === 'sales' && hasPermission('case_create_from_sales')) { document.getElementById('sales_person_id').value = currentUser.id; document.getElementById('locked').value = true; } else if (hasPermission('case_create_from_cases')) { loadSalesPersonSelect(); document.getElementById('locked').value = false; } else { alert('権限がありません'); window.location.href = '/cases/index.html'; } ``` --- ## 7. 拠点別アクセス制御 ### 7.1 権限 | 権限キー | 説明 | |---------|------| | `branch_view_self` | 自拠点の一覧表示 | | `branch_view_other` | 他拠点の一覧表示 | | `branch_view_all` | 全拠点の一覧表示 | ### 7.2 制御ルール - 案件一覧表示時、自拠点の案件をデフォルト表示 - `branch_view_other` または `branch_view_all` を持つユーザーのみ、拠点切替タブを表示 --- ## 8. 会社区分制御(SPS / SCSPS) ### 8.1 ルール - ログインユーザーの `company` フィールドに基づく - SPSユーザー → SPS案件のみ表示 - SCSPSユーザー → SCSPS案件のみ表示 - 事業主(真柴直也)は両方表示可能(特別フラグ or `company: "BOTH"`) ### 8.2 実装 ```javascript const user = JSON.parse(sessionStorage.getItem('user')); function loadCases() { const url = `/master/api/load_case_list.php?company=${user.company}`; // ... } ``` --- ## 9. 特例アクセス(emergency_access_ids) ### 9.1 機能 - 案件ごとに `emergency_access_ids` 配列を保持 - この配列に自分のIDが含まれている場合、通常の権限チェックをバイパスして閲覧・編集可能 ### 9.2 設定権限 - `case_emergency_set_own` または `case_emergency_set_all` 権限を持つユーザーのみ設定可能 ### 9.3 設定画面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 特例アクセス設定 │ ├─────────────────────────────────────────────────────────────────┤ │ この案件を緊急時に閲覧/編集できるユーザーを追加します │ │ │ │ ユーザー選択: [プルダウン▼] [+ 追加] │ │ │ │ 設定済みユーザー: │ │ - サポート担当A (ID:00010) [削除] │ │ - 代理担当B (ID:00012) [削除] │ │ │ │ [保存] │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 10. 権限チェックの実装(共通関数) `/common/js/permissions.js` に共通関数を実装する。 ```javascript let currentUser = null; function setCurrentUser(user) { currentUser = user; sessionStorage.setItem('user', JSON.stringify(user)); } function getCurrentUser() { if (!currentUser) { const stored = sessionStorage.getItem('user'); if (stored) currentUser = JSON.parse(stored); } return currentUser; } function hasPermission(permissionCode) { const user = getCurrentUser(); if (!user || !user.permissions) return false; return user.permissions.includes(permissionCode); } function hasAnyPermission(permissionCodes) { const user = getCurrentUser(); if (!user || !user.permissions) return false; return permissionCodes.some(code => user.permissions.includes(code)); } function isOwnCase(caseData, userId) { return caseData.sales_person_id === userId || caseData.primary_investigator_id === userId || caseData.secondary_investigator_id === userId || caseData.manager_id === userId; } function canViewCase(caseData) { const user = getCurrentUser(); if (!user) return false; if (hasPermission('case_detail_view_all')) return true; if (hasPermission('case_detail_view_own') && isOwnCase(caseData, user.id)) return true; if (caseData.emergency_access_ids && caseData.emergency_access_ids.includes(user.id)) return true; return false; } function canRequestReimbursement(caseData) { if (!hasPermission('case_reimbursement_request')) return false; return caseData.expenses.some(e => e.approved && e.payment_status === '承認済'); } function canApproveReimbursement(caseData) { if (!hasPermission('case_reimbursement_approve')) return false; return caseData.expenses.some(e => e.payment_status === '精算請求中'); } function canConfirmReadiness(caseData) { if (!hasPermission('case_readiness_confirm')) return false; const checklist = caseData.readiness_checklist; if (!checklist) return false; const allExceptManager = checklist.basic_info_complete && checklist.hearing_complete && checklist.report_generatable && checklist.photos_complete && checklist.site_diagram_complete && checklist.expenses_complete; return allExceptManager && !checklist.manager_confirmed; } function canLockCase(caseData) { if (!hasPermission('case_lock')) return false; return !caseData.locked; } function canCreateCaseFromSales() { return hasPermission('case_create_from_sales'); } function canCreateCaseFromCases() { return hasPermission('case_create_from_cases'); } function canContactOwner() { return hasPermission('case_contact_owner'); } ``` --- ## 11. 共通仕様の参照 | 項目 | 参照先 | |------|--------| | 印刷仕様 | `02_print_spec.md` | | UIデザイン | `03_ui_design/01_design_guideline.md` | | 日付表示 | `03_ui_design/01_design_guideline.md` | --- ## 12. 関連仕様書 | # | 仕様書 | 内容 | |---|--------|------| | 1 | `01_case_management.md` | 案件管理全体設計 | | 2 | `02_case_data_structure.md` | 案件データ構造 | | 4 | `04_case_input_screens.md` | 入力画面設計 | | 7 | `07_case_document_generate.md` | 報告書自動生成設計 | | 11 | `11_case_mail.md` | メール送信機能仕様 | | 12 | `12_case_sales_link.md` | 営業管理連携仕様 | --- ## 13. 改訂履歴 | 日付 | 版 | 担当 | 内容 | 理由・影響範囲 | |------|----|------|------|--------------| | 2026-03-24 | 1.0 | 案件管理担当 | 初版作成 | 案件管理システムの権限定義 | | 2026-03-24 | 1.1 | 案件管理担当 | 経費精算関連権限、成果物チェック権限を追加 | 実務要件の反映 | | 2026-03-26 | 1.2 | 案件管理担当 | 案件一覧表示制御、営業管理連携、ロック機能、メール送信関連権限を追加 | 最終打ち合わせ内容の反映 |