【Pleasanter】1on1面談シートをサイトパッケージ化する

面談の記録をExcelで作っていると、前回の話や、その後どうなったかを探し直すことになる。ファイルを共有して上位管理者に確認してもらう運用も、更新のたびに手間がかかる。

そこで、面談結果と次回へのフォローアップをプリザンターで管理する構成を考えた。閲覧するのは面談担当リーダーと上位管理者だけ。メンバー本人にはシステム上の閲覧権限を与えない前提だ。

注意:掲載するサイトパッケージは公開仕様とエクスポート例に合わせて作成したテンプレート。実際のPleasanterからエクスポートしたものではなく、対象バージョンへの実機インポート試験も未実施。テスト環境で動作確認し、権限を設定するまで実データを投入しないこと。

全体構成

                   ┌────────────────────┐
                   │  1on1面談管理     │
                   │  (フォルダ)      │
                   └──────────┬─────────┘
                              │
             ┌────────────────┼────────────────┐
             ▼                ▼                ▼
       ┌──────────┐    ┌────────────┐    ┌────────────┐
       │メンバー台帳│    │1on1面談記録 │    │フォロー課題 │
       │1人1レコード│───▶│1面談1レコード│───▶│1課題1レコード│
       └──────────┘    └────────────┘    └────────────┘
サイト テーブル種別 役割
メンバー台帳 記録テーブル 氏名、所属、役割、育成方針、面談リーダー
1on1面談記録 記録テーブル 面談内容、本人の希望、リーダー所見、上位確認
フォローアップ課題 期限付きテーブル 誰が・いつまでに・何をするか

上の矢印はテーブル間の参照関係を表している。実装では面談記録の ClassA がメンバー台帳を参照し、フォローアップ課題の ClassA が面談記録を参照する。

操作の流れ

下書き(100)──[リーダー:確認依頼]──▶ 上位確認待ち(200)
                                              │
                                  ┌───────────┴────────────┐
                                  ▼                        ▼
                      [管理者:差し戻し]         [管理者:確認完了]
                                  │                        │
                                  ▼                        ▼
                          差し戻し(300)             確認済み(900)
                                  │
                          [リーダー:修正/再依頼]
                                  │
                                  └──────────▶ 上位確認待ち(200)
状況 コード 次に操作する人
下書き 100 面談リーダー
上位確認待ち 200 上位管理者
差し戻し 300 面談リーダー
確認済み 900 原則として編集停止

「上位確認」は人事評価の承認ではなく、面談が行われたことと、必要な支援や対応が整理されていることの確認と位置付ける。

記録に持たせる項目

メンバー台帳

Pleasanterの項目名 表示名
Title 氏名
ClassA、ClassB 所属チーム、役割
Owner 面談担当リーダー
ClassC 在籍状況
DescriptionA、DescriptionB 育成目標、担当業務
DateA、CheckA 面談開始日、面談対象

1on1面談記録

Pleasanterの項目名 表示名
Title / ClassA / DateA 面談件名 / 面談対象者 / 面談日
Owner / Manager 面談リーダー / 上位確認者
ClassB / ClassC 面談区分 / 本人申告の業務状況
DescriptionA 現在の業務状況
DescriptionB 困っていること・課題
DescriptionC 本人の意向・希望
DescriptionD リーダー所見
DescriptionE 双方で合意した対応事項
DescriptionF 次回確認事項
DescriptionG 上位管理者コメント・差戻し理由
DateB / DateC / DateD 次回面談予定日 / 確認依頼日時 / 確認完了日時
ClassD / CheckA / Status 確認完了者 / フォロー要否 / 状況

本人が話した内容と、リーダー側の見解は別の欄にする。 読み返したときに、誰の認識だったのかが分からなくなることを防ぎたい。

フォローアップ課題

Pleasanterの項目名 表示名
Title 課題・対応事項
ClassA / ClassB 関連面談記録 / 対象メンバー
Owner / ClassC 担当リーダー / 対応主体
StartTime / CompletionTime 対応開始日 / 対応期限
Status / ProgressRate 対応状況 / 進捗
Body / DescriptionA 対応内容 / 結果

ここでは標準の CompletionTime を 対応期限 として使っている。実際の完了日を追跡したい場合は別項目を追加する。

サイトパッケージの取り込み

  1. 検証用のPleasanter環境に、サイト管理権限を持つユーザーでログイン。
  2. [管理] → [サイトパッケージのインポート] を開く。
  3. site-package.json を指定し、サイト・レコード・項目のアクセス制御、通知、リマインダーの取り込みは OFF にする。
  4. 新規作成されたフォルダと3テーブル、リンク、4プロセスを確認する。
  5. 次節のアクセス制御が完了するまで、テスト用の架空データだけを入力する。

サイトパッケージは新しいサイトを作る機能であり、既存サイトの上書きには使えない。トップのフォルダが継承する権限にも注意が必要だ。

最優先:アクセス制御

面談は機密情報を含む。今回はインポート先のユーザーIDやグループIDが分からないため、サイトパッケージに権限設定を埋め込んでいない。導入先で設定する必要がある。

利用者 メンバー台帳 面談記録 フォロー課題
G_1ON1_LEADER 担当分のみ読取 作成+担当記録の読取・更新 作成+担当分の読取・更新
G_1ON1_MANAGER 管轄分の読取・更新 管轄分の読取・更新 管轄分の読取・更新
G_1ON1_ADMIN 管理 管理 管理
一般メンバー 権限なし 権限なし 権限なし

設定の順番

  1. グループを作成してリーダー・上位管理者・管理担当を登録。
  2. ルートフォルダと3テーブルのアクセス権の継承を確認して、不要な継承を解除。
  3. リーダー全員にはサイト全体の読取権限を付けない。サイト「作成」と担当レコードへの「読取・更新」を分けて設定する。
  4. レコードのアクセス制御を設定し、担当リーダーに権限が自動付与されることを確認。
  5. 上位管理者には管轄対象の閲覧・更新権限を設定。
  6. 4プロセスのアクセス制御で 確認依頼・再確認依頼はリーダーのみ、差し戻し・確認完了は上位管理者のみ にする。
  7. 項目のアクセス制御を追加し、上位管理者が本文全体を勝手に書き換えられない設計にする。
  8. リーダーA・リーダーB・上位管理者・一般メンバーで、直接URLを含むアクセスを検証する。

パッケージ中の StatusControls は、ステータスごとの編集画面を制御する補助設定。API、一括更新などの操作を含めた保護には権限の設定・検証が必要だ。

プロセスで設定する内容

プロセス 遷移 入力検証・値更新
確認依頼 100 → 200 対象者/面談日/リーダー/上位確認者/業務状況/合意事項を必須。依頼日時を記録
再確認依頼 300 → 200 合意事項を必須
差し戻し 200 → 300 管理者コメントを必須
確認完了 200 → 900 確認日時と確認者を記録

プロセスの実行者制限は取り込み後に必ず追加する。ボタンが表示されるからといって権限設計が終わったわけではない。メール通知も実環境の宛先設定が必要なので同梱せず、内容をメール本文に載せずにサイトへのリンクだけを通知する。

テスト項目

  • 面談対象者の候補にメンバー台帳が表示される。
  • 面談を作り、確認依頼→確認完了、差し戻し→再確認依頼が通る。
  • 面談記録から関連フォローアップ課題を作成・参照できる。
  • リーダーAがリーダーBの記録を読めない。
  • 一般ユーザーが面談記録を開けない。
  • 確認済みの記録を想定外の操作で更新できない(画面・CSV・API・一括更新)。
  • 保管期間、バックアップ、異動時の権限剥奪が決まっている。

JSONの静的検証

Python 3.10以上で次を実行する。

python docs/pleasanter-1on1/validate-package.py

この検査は形式と意図した状態遷移をチェックするもので、実機インポートの成功を保証しない。

サイトパッケージ JSON全文

ファイル:docs/pleasanter-1on1/site-package.json(省略なし)

{
  "HeaderInfo": {
    "AssemblyVersion": "1.5.8.1",
    "BaseSiteId": 9700000,
    "Server": "https://localhost",
    "CreatorName": "1on1 template generator (not an actual Pleasanter export)",
    "PackageTime": "2026-10-22T00:00:00+09:00",
    "Convertors": [
      {
        "SiteId": 9700000,
        "SiteTitle": "1on1面談管理",
        "ReferenceType": "Sites",
        "IncludeData": false,
        "Order": "[9700001,9700002,9700003]"
      },
      {
        "SiteId": 9700001,
        "SiteTitle": "メンバー台帳",
        "ReferenceType": "Results",
        "IncludeData": false
      },
      {
        "SiteId": 9700002,
        "SiteTitle": "1on1面談記録",
        "ReferenceType": "Results",
        "IncludeData": false
      },
      {
        "SiteId": 9700003,
        "SiteTitle": "フォローアップ課題",
        "ReferenceType": "Issues",
        "IncludeData": false
      }
    ],
    "IncludeSitePermission": false,
    "IncludeRecordPermission": false,
    "IncludeColumnPermission": false,
    "IncludeNotifications": false,
    "IncludeReminders": false
  },
  "Sites": [
    {
      "TenantId": 1,
      "SiteId": 9700000,
      "Title": "1on1面談管理",
      "SiteName": "oneonone_9700000",
      "SiteGroupName": "",
      "Body": "",
      "GridGuide": "",
      "EditorGuide": "",
      "CalendarGuide": "",
      "CrosstabGuide": "",
      "GanttGuide": "",
      "BurnDownGuide": "",
      "TimeSeriesGuide": "",
      "KambanGuide": "",
      "ImageLibGuide": "",
      "ReferenceType": "Sites",
      "ParentId": 0,
      "InheritPermission": 9700000,
      "SiteSettings": {
        "Version": 1.017,
        "ReferenceType": "Sites",
        "NoDisplayIfReadOnly": false
      },
      "Publish": false,
      "DisableCrossSearch": true,
      "Comments": []
    },
    {
      "TenantId": 1,
      "SiteId": 9700001,
      "Title": "メンバー台帳",
      "SiteName": "oneonone_9700001",
      "SiteGroupName": "",
      "Body": "",
      "GridGuide": "",
      "EditorGuide": "",
      "CalendarGuide": "",
      "CrosstabGuide": "",
      "GanttGuide": "",
      "BurnDownGuide": "",
      "TimeSeriesGuide": "",
      "KambanGuide": "",
      "ImageLibGuide": "",
      "ReferenceType": "Results",
      "ParentId": 9700000,
      "InheritPermission": 9700001,
      "SiteSettings": {
        "Version": 1.017,
        "ReferenceType": "Results",
        "GridColumns": [
          "ResultId",
          "Title",
          "ClassA",
          "ClassB",
          "Owner",
          "ClassC",
          "CheckA"
        ],
        "EditorColumnHash": {
          "General": [
            "ResultId",
            "Title",
            "ClassA",
            "ClassB",
            "Owner",
            "ClassC",
            "DescriptionA",
            "DescriptionB",
            "DateA",
            "CheckA"
          ]
        },
        "Columns": [
          {
            "ColumnName": "Title",
            "LabelText": "氏名",
            "ValidateRequired": true
          },
          {
            "ColumnName": "ClassA",
            "LabelText": "所属チーム",
            "ChoicesText": "統合マスタ\nITツール\nその他"
          },
          {
            "ColumnName": "ClassB",
            "LabelText": "役割",
            "ChoicesText": "新人\n一般\nサブリーダー\nリーダー"
          },
          {
            "ColumnName": "Owner",
            "LabelText": "面談担当リーダー"
          },
          {
            "ColumnName": "ClassC",
            "LabelText": "在籍状況",
            "ChoicesText": "在籍\n異動\n退職"
          },
          {
            "ColumnName": "DescriptionA",
            "LabelText": "育成目標"
          },
          {
            "ColumnName": "DescriptionB",
            "LabelText": "担当業務"
          },
          {
            "ColumnName": "DateA",
            "LabelText": "面談開始日",
            "GridFormat": "Ymd"
          },
          {
            "ColumnName": "CheckA",
            "LabelText": "面談対象"
          }
        ],
        "ImportEncoding": "UTF-8",
        "NoDisplayIfReadOnly": false
      },
      "Publish": false,
      "DisableCrossSearch": true,
      "Comments": []
    },
    {
      "TenantId": 1,
      "SiteId": 9700002,
      "Title": "1on1面談記録",
      "SiteName": "oneonone_9700002",
      "SiteGroupName": "",
      "Body": "",
      "GridGuide": "",
      "EditorGuide": "",
      "CalendarGuide": "",
      "CrosstabGuide": "",
      "GanttGuide": "",
      "BurnDownGuide": "",
      "TimeSeriesGuide": "",
      "KambanGuide": "",
      "ImageLibGuide": "",
      "ReferenceType": "Results",
      "ParentId": 9700000,
      "InheritPermission": 9700002,
      "SiteSettings": {
        "Version": 1.017,
        "ReferenceType": "Results",
        "GridColumns": [
          "ResultId",
          "Title",
          "ClassA",
          "DateA",
          "Owner",
          "Manager",
          "ClassB",
          "Status",
          "CheckA"
        ],
        "EditorColumnHash": {
          "General": [
            "ResultId",
            "Title",
            "ClassA",
            "DateA",
            "Owner",
            "Manager",
            "ClassB",
            "ClassC",
            "DescriptionA",
            "DescriptionB",
            "DescriptionC",
            "DescriptionD",
            "DescriptionE",
            "DescriptionF",
            "DescriptionG",
            "DateB",
            "DateC",
            "DateD",
            "ClassD",
            "CheckA",
            "Status"
          ]
        },
        "Columns": [
          {
            "ColumnName": "Title",
            "LabelText": "面談件名",
            "ValidateRequired": true
          },
          {
            "ColumnName": "ClassA",
            "LabelText": "面談対象者",
            "ChoicesText": "[[9700001]]",
            "ValidateRequired": true
          },
          {
            "ColumnName": "DateA",
            "LabelText": "面談実施日",
            "GridFormat": "Ymd",
            "ValidateRequired": true
          },
          {
            "ColumnName": "Owner",
            "LabelText": "面談リーダー",
            "ValidateRequired": true
          },
          {
            "ColumnName": "Manager",
            "LabelText": "上位確認者",
            "ValidateRequired": true
          },
          {
            "ColumnName": "ClassB",
            "LabelText": "面談区分",
            "ChoicesText": "定期面談\n臨時面談\n育成面談\nキャリア面談"
          },
          {
            "ColumnName": "ClassC",
            "LabelText": "本人申告の業務状況",
            "ChoicesText": "順調\n普通\n支援希望"
          },
          {
            "ColumnName": "DescriptionA",
            "LabelText": "現在の業務状況"
          },
          {
            "ColumnName": "DescriptionB",
            "LabelText": "困っていること・課題"
          },
          {
            "ColumnName": "DescriptionC",
            "LabelText": "本人の意向・希望"
          },
          {
            "ColumnName": "DescriptionD",
            "LabelText": "リーダー所見"
          },
          {
            "ColumnName": "DescriptionE",
            "LabelText": "双方で合意した対応事項"
          },
          {
            "ColumnName": "DescriptionF",
            "LabelText": "次回確認事項"
          },
          {
            "ColumnName": "DescriptionG",
            "LabelText": "上位管理者コメント・差戻し理由"
          },
          {
            "ColumnName": "DateB",
            "LabelText": "次回面談予定日",
            "GridFormat": "Ymd"
          },
          {
            "ColumnName": "DateC",
            "LabelText": "確認依頼日時",
            "GridFormat": "Ymdhm",
            "EditorFormat": "Ymdhm"
          },
          {
            "ColumnName": "DateD",
            "LabelText": "確認完了日時",
            "GridFormat": "Ymdhm",
            "EditorFormat": "Ymdhm"
          },
          {
            "ColumnName": "ClassD",
            "LabelText": "確認完了者",
            "ChoicesText": "[[Users]]"
          },
          {
            "ColumnName": "CheckA",
            "LabelText": "フォローアップ必要"
          },
          {
            "ColumnName": "Status",
            "LabelText": "面談記録ステータス",
            "ChoicesText": "100,下書き\n200,上位確認待ち\n300,差し戻し\n900,確認済み",
            "DefaultValue": "100"
          }
        ],
        "Processes": [
          {
            "Id": 1,
            "Name": "request_review",
            "DisplayName": "確認依頼",
            "ScreenType": 20,
            "CurrentStatus": 100,
            "ChangedStatus": 200,
            "ExecutionType": 0,
            "ActionType": 0,
            "AllowBulkProcessing": false,
            "ConfirmationMessage": "上位管理者へ確認を依頼しますか?",
            "ValidateInputs": [
              {
                "Id": 1,
                "ColumnName": "ClassA",
                "Required": true
              },
              {
                "Id": 2,
                "ColumnName": "DateA",
                "Required": true
              },
              {
                "Id": 3,
                "ColumnName": "Owner",
                "Required": true
              },
              {
                "Id": 4,
                "ColumnName": "Manager",
                "Required": true
              },
              {
                "Id": 5,
                "ColumnName": "DescriptionA",
                "Required": true
              },
              {
                "Id": 6,
                "ColumnName": "DescriptionE",
                "Required": true
              }
            ],
            "DataChanges": [
              {
                "Id": 1,
                "Type": "InputDateTime",
                "ColumnName": "DateC",
                "BaseDateTime": "CurrentTime",
                "Value": "0,Days"
              }
            ]
          },
          {
            "Id": 2,
            "Name": "request_again",
            "DisplayName": "再確認依頼",
            "ScreenType": 20,
            "CurrentStatus": 300,
            "ChangedStatus": 200,
            "ExecutionType": 0,
            "ActionType": 0,
            "AllowBulkProcessing": false,
            "ValidateInputs": [
              {
                "Id": 1,
                "ColumnName": "DescriptionE",
                "Required": true
              }
            ]
          },
          {
            "Id": 3,
            "Name": "return_review",
            "DisplayName": "差し戻し",
            "ScreenType": 20,
            "CurrentStatus": 200,
            "ChangedStatus": 300,
            "ExecutionType": 0,
            "ActionType": 0,
            "AllowBulkProcessing": false,
            "ValidateInputs": [
              {
                "Id": 1,
                "ColumnName": "DescriptionG",
                "Required": true
              }
            ]
          },
          {
            "Id": 4,
            "Name": "complete_review",
            "DisplayName": "確認完了",
            "ScreenType": 20,
            "CurrentStatus": 200,
            "ChangedStatus": 900,
            "ExecutionType": 0,
            "ActionType": 0,
            "AllowBulkProcessing": false,
            "DataChanges": [
              {
                "Id": 1,
                "Type": "InputDateTime",
                "ColumnName": "DateD",
                "BaseDateTime": "CurrentTime",
                "Value": "0,Days"
              },
              {
                "Id": 2,
                "Type": "InputUser",
                "ColumnName": "ClassD"
              }
            ]
          }
        ],
        "StatusControls": [
          {
            "Id": 1,
            "Name": "下書き",
            "Status": 100,
            "ColumnHash": {
              "Status": "ReadOnly"
            }
          },
          {
            "Id": 2,
            "Name": "確認待ち",
            "Status": 200,
            "ColumnHash": {
              "Status": "ReadOnly",
              "ClassA": "ReadOnly",
              "DateA": "ReadOnly",
              "Owner": "ReadOnly",
              "DescriptionA": "ReadOnly",
              "DescriptionB": "ReadOnly",
              "DescriptionC": "ReadOnly",
              "DescriptionD": "ReadOnly",
              "DescriptionE": "ReadOnly",
              "DescriptionF": "ReadOnly"
            }
          },
          {
            "Id": 3,
            "Name": "差し戻し",
            "Status": 300,
            "ColumnHash": {
              "Status": "ReadOnly"
            }
          },
          {
            "Id": 4,
            "Name": "確認済み",
            "Status": 900,
            "ReadOnly": true
          }
        ],
        "PermissionForCreating": {
          "Owner": 5
        },
        "PermissionForUpdating": {
          "Owner": 5
        },
        "ImportEncoding": "UTF-8",
        "NoDisplayIfReadOnly": false
      },
      "Publish": false,
      "DisableCrossSearch": true,
      "Comments": []
    },
    {
      "TenantId": 1,
      "SiteId": 9700003,
      "Title": "フォローアップ課題",
      "SiteName": "oneonone_9700003",
      "SiteGroupName": "",
      "Body": "",
      "GridGuide": "",
      "EditorGuide": "",
      "CalendarGuide": "",
      "CrosstabGuide": "",
      "GanttGuide": "",
      "BurnDownGuide": "",
      "TimeSeriesGuide": "",
      "KambanGuide": "",
      "ImageLibGuide": "",
      "ReferenceType": "Issues",
      "ParentId": 9700000,
      "InheritPermission": 9700003,
      "SiteSettings": {
        "Version": 1.017,
        "ReferenceType": "Issues",
        "GridColumns": [
          "IssueId",
          "Title",
          "ClassA",
          "ClassB",
          "Owner",
          "Status",
          "StartTime",
          "CompletionTime",
          "ProgressRate"
        ],
        "EditorColumnHash": {
          "General": [
            "IssueId",
            "Title",
            "ClassA",
            "ClassB",
            "Owner",
            "ClassC",
            "StartTime",
            "CompletionTime",
            "Status",
            "ProgressRate",
            "Body",
            "DescriptionA"
          ]
        },
        "Columns": [
          {
            "ColumnName": "Title",
            "LabelText": "課題・対応事項",
            "ValidateRequired": true
          },
          {
            "ColumnName": "ClassA",
            "LabelText": "関連する面談記録",
            "ChoicesText": "[[9700002]]"
          },
          {
            "ColumnName": "ClassB",
            "LabelText": "対象メンバー",
            "ChoicesText": "[[9700001]]"
          },
          {
            "ColumnName": "Owner",
            "LabelText": "担当リーダー"
          },
          {
            "ColumnName": "ClassC",
            "LabelText": "対応主体",
            "ChoicesText": "本人\nリーダー\nその他"
          },
          {
            "ColumnName": "StartTime",
            "LabelText": "対応開始日",
            "GridFormat": "Ymd",
            "EditorFormat": "Ymd"
          },
          {
            "ColumnName": "CompletionTime",
            "LabelText": "対応期限",
            "GridFormat": "Ymd",
            "EditorFormat": "Ymd"
          },
          {
            "ColumnName": "Body",
            "LabelText": "対応内容"
          },
          {
            "ColumnName": "DescriptionA",
            "LabelText": "結果・確認事項"
          },
          {
            "ColumnName": "Status",
            "LabelText": "対応状況",
            "ChoicesText": "100,未着手\n200,対応中\n900,完了",
            "DefaultValue": "100"
          }
        ],
        "PermissionForCreating": {
          "Owner": 5
        },
        "PermissionForUpdating": {
          "Owner": 5
        },
        "ImportEncoding": "UTF-8",
        "NoDisplayIfReadOnly": false
      },
      "Publish": false,
      "DisableCrossSearch": true,
      "Comments": []
    }
  ],
  "Data": [],
  "Permissions": [],
  "PermissionIdList": {
    "DeptIdList": [],
    "GroupIdList": [],
    "UserIdList": []
  }
}

検証スクリプト全文

ファイル:docs/pleasanter-1on1/validate-package.py(省略なし)

#!/usr/bin/env python3
# UTF-8 / Python 3.10+ / 追加依存なし
import json
import sys
from pathlib import Path

EXPECTED = {"Sites": 1, "Results": 2, "Issues": 1}

def main():
    path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).with_name("site-package.json")
    data = json.loads(path.read_text(encoding="utf-8-sig"))
    assert data["HeaderInfo"]["IncludeSitePermission"] is False, "権限を同梱しない方針"
    assert data["Data"] == [], "実データを含めないこと"
    ids = [site["SiteId"] for site in data["Sites"]]
    assert len(ids) == 4 and len(set(ids)) == 4, "サイトIDの重複"
    for name, count in EXPECTED.items():
        assert sum(s["ReferenceType"] == name for s in data["Sites"]) == count, name
    meetings = next(s for s in data["Sites"] if s["Title"] == "1on1面談記録")
    conf = meetings["SiteSettings"]
    transitions = {(p["CurrentStatus"], p["ChangedStatus"]) for p in conf["Processes"]}
    assert transitions == {(100, 200), (300, 200), (200, 300), (200, 900)}
    assert conf["StatusControls"][-1]["ReadOnly"] is True
    for s in data["Sites"]:
        names = [c["ColumnName"] for c in s["SiteSettings"].get("Columns", [])]
        assert len(names) == len(set(names)), "項目名重複: " + s["Title"]
    print("PASS: JSON構造・テーブル・状態遷移(静的検査)")
    print("NOTE: Pleasanter実機でのインポート・権限・動作は未検証")

if __name__ == "__main__":
    main()

参考

Previous Post Next Post