催事運営
位置づけ: 第1弾(催事+アイテム+商社)
催事運営は、住宅設備商社が実施する店舗催事における代理店シフト管理、現場の開始・終了報告、シフト状態の自動判定、商談CRMとの突合を扱うドメインである。催事店舗マスタ・代理店(販売店)マスタ・催事シフト・開始報告・終了報告の5要素で構成する。
本ページの記述は試作実装(crm_app/app/src/lib/events.ts、crm_app/app/src/lib/permissions.ts、crm_app/app/src/features/crm/EventShiftDetail.tsx。/events として先行取り込み済みの event-shift-system)を正とし、フィールド名・enum・分岐条件をそのまま反映する。
シフトの状態は保存せず、開始報告・終了報告の有無と基準時刻(開始時刻・終了時刻)から都度導出する。催事と商談CRMは外部キーで直結せず、店舗名・日付の一致による疎結合の突合で対応づける。
スコープ/対象
Section titled “スコープ/対象”対象は次のとおり。
- 催事店舗マスタ、代理店(販売店)マスタ
- 催事シフト(1日×1店舗×1代理店の単位)
- 開始報告(朝礼チェックリスト)、終了報告(現場ファネル実績)
- シフト状態の自動判定
- 催事と商談CRMの突合
- 権限モデル(社内3ロール/代理店2ロール)
対象外は次のとおり。いずれも後続フェーズで扱う。
| 対象外項目 | 扱う場所 |
|---|---|
| 顧客・商談の管理本体 | CRM基本機能 |
| 見積金額・提案金額 | 価格表・見積 |
| 売上・KPI予実の集計 | KPI予実 |
| 卸金額・受発注 | 商社(卸)連携 |
終了報告は現場成果(抽選・着座・アポの件数)を一次データとして持つが、売上金額・受注金額の入力は持たない。金額系は商談モジュールおよび商社(卸)モジュールの管轄であり、これは意図的な設計上の切り分けである。個人スタッフ(代理店所属メンバー)のマスタ化・シフトへの個人アサインも対象外とし、責任者は氏名の文字列入力、人数は数値入力で足りる軽量設計とする。
データ(エンティティ表)
Section titled “データ(エンティティ表)”催事店舗マスタ(EventStore)
Section titled “催事店舗マスタ(EventStore)”| フィールド | 型・入力形式 | 必須 | 説明 |
|---|---|---|---|
id | 自動採番(例: ST01) | 必須 | 主キー |
name | 自由入力 | 必須 | 店舗名。商談側 venue との突合キーになる |
area | 自由入力 | 任意 | 都道府県目安 |
category | 自由文字列 | 任意 | 系列・チェーン(カインズ/島忠/ビバホーム等)。絞り込みに使う |
代理店マスタ(EventCompany)
Section titled “代理店マスタ(EventCompany)”代理店(催事会社)は別マスタを設けず、代理店(販売店)マスタに一本化する。代理店は催事・訪販の両方を担うため、事業区分は1値で表す。
| フィールド | 型・入力形式 | 必須 | 説明 |
|---|---|---|---|
id | 自動採番(例: CO01) | 必須 | 主キー |
name | 自由入力 | 必須 | 代理店名 |
contact | 自由入力 | 任意 | 連絡先 |
business | 選択式: 催事/訪販/催事・訪販 | 任意 | 事業区分 |
催事シフト(EventShift)
Section titled “催事シフト(EventShift)”粒度は「1日×1店舗×1代理店」。同一店舗・同日に複数代理店を入れる場合はシフトを分ける。
| フィールド | 型・入力形式 | 必須 | 説明 |
|---|---|---|---|
id | 自動採番(S-MMDD-NN 形式) | 必須 | 主キー。同日内の既存IDの最大連番+1 |
date | YYYY-MM-DD | 必須 | 催事日 |
storeId | 店舗マスタ参照 | 必須 | 実施店舗 |
companyId | 代理店マスタ参照 | 必須 | 担当代理店 |
startTime / endTime | HH:MM | 必須 | シフト時間帯 |
plannedLeader | 自由入力(氏名) | 任意 | 予定責任者 |
staffCount | 数値 | 必須 | 人数 |
note | 自由入力 | 任意 | メモ |
startReport | StartReport | 未提出時 undefined | 開始報告 |
endReport | EndReport | 未提出時 undefined | 終了報告 |
開始報告(StartReport)
Section titled “開始報告(StartReport)”| フィールド | 型・入力形式 | 必須 | 説明 |
|---|---|---|---|
leader | 自由入力(氏名) | 必須 | 当日責任者。plannedLeader とは別フィールドで記録し、予定と実績の差異を残す |
emergencyContact | 自由入力(電話番号) | 必須 | 緊急連絡先 |
checks | チェック項目id→真偽のマップ | 任意 | 朝礼チェックリスト |
comment | 自由入力 | 任意 | コメント |
photos | ReportPhoto{name,url} の配列 | 任意 | 現場写真 |
at | ISO日時 | 自動記録 | 提出日時 |
朝礼チェックリストはハードコードせず、マスタ(useEvents の startChecks)から与える。既定値は次の7項目(DEFAULT_START_CHECKS)。
| id | 項目 |
|---|---|
greeting | 挨拶・声かけ |
mind | 身だしなみ・気持ちの準備 |
booth | ブース設営の確認 |
backyard | バックヤード整理 |
private | 私語・スマホのルール確認 |
rule | 店舗ルールの確認 |
judge | 判断に迷ったら責任者へ |
終了報告(EndReport)
Section titled “終了報告(EndReport)”現場ファネル3段階(抽選→着座→アポ)の実績を、各段階とも FunnelCount{total, both, single}(合計/両面提案/片面提案の内訳)で記録する。
| フィールド | 型・入力形式 | 必須 | 説明 |
|---|---|---|---|
department | 自由入力 | 任意 | 部門(住設 等) |
lottery | FunnelCount | 必須 | 抽選:ブースでの抽選参加者数 |
seated | FunnelCount | 必須 | 着座:商談ブースへの着座数 |
appointments | FunnelCount | 必須 | アポ:後日商談アポ獲得数 |
photos | ReportPhoto{name,url} の配列 | 任意 | 現場写真 |
at | ISO日時 | 自動記録 | 提出日時 |
| 画面 | 内容 |
|---|---|
催事シフト一覧(/events) | シフトをステータス付きで一覧表示。代理店ロールは自社分のみ表示 |
催事シフト詳細(/events/$shiftId) | 店舗・時間帯・代理店・人数・責任者の帯表示、開始報告カード、終了報告カード、CRM連携カード |
| 開始報告カード | 未提出時はチェックリスト・責任者・緊急連絡先・コメント・写真の入力フォーム。提出済み時は読み取り専用表示 |
| 終了報告カード | 未提出時はファネル3段階×3区分(合計・両面・片面)の数値入力フォーム。提出済み時は着座率・アポ率を含む読み取り専用表示 |
| CRM連携カード | 終了報告提出後に表示。報告済みアポ数とCRM登録件数の差分を警告表示する |
提出済みの開始報告・終了報告は読み取り専用表示のみであり、再編集用のUIは持たない。
業務ルール・ワークフロー
Section titled “業務ルール・ワークフロー”シフト状態の自動判定
Section titled “シフト状態の自動判定”shiftStatus() は保存済みの状態を持たず、startReport / endReport の有無と基準時刻(startTime / endTime)から都度導出する。判定は now >= startAt / now >= endAt の時刻比較であり、猶予時間は設けない。基準時刻を過ぎた瞬間に状態が切り替わる。
| ステータス | 現在時刻の条件 | 開始報告 | 終了報告 | 一覧での表示トーン |
|---|---|---|---|---|
| 終了済み | 問わない | — | あり | secondary(グレー) |
| 終了未報告 | 終了時刻を過ぎた | あり | なし | 警告色 |
| 開始済み | 終了時刻前 | あり | なし | 進行中トーン |
| 未開始 | 開始時刻を過ぎた | なし | なし | 警告色(赤系) |
| 予定 | 開始時刻前 | なし | なし | secondary(標準) |
終了報告があれば時刻に関わらず無条件で「終了済み」となる。終了時刻前の早期終了報告でも成立する。異常ステータス(未開始・終了未報告)の自動通知機能は持たず、一覧の目視運用で検知する。
開始報告の提出ルール
Section titled “開始報告の提出ルール”全チェック完了は送信の必須条件ではない。未チェックの項目があっても送信ボタンは有効であり、画面には「未チェック N 件(そのまま送信できます)」という警告のみを表示する。送信に必須なのは leader(責任者)と emergencyContact(緊急連絡先)の非空入力のみである。報告時刻(at)は自動記録され、ステータス「開始済み」への遷移トリガーになる。
終了報告の提出ルール
Section titled “終了報告の提出ルール”終了報告は開始報告の提出後にのみ入力可能である。開始報告が未提出の場合、終了報告カードには「開始報告の送信後に入力できます」の案内のみを表示し、入力欄自体を出さない。提出済み表示では、着座率(着座total÷抽選total)とアポ率(アポtotal÷着座total)を自動算出する。分母が0のときは — と表示する。total と both+single の内訳一致を強制するバリデーションは持たず、各セルは独立した数値入力とする。
開始報告・終了報告とも現場写真を添付できる(ReportPhoto{name,url})。試作実装ではブラウザ内で dataURL として保持するモック実装であり、本番実装では Storage の URL に差し替える。
シフトID採番
Section titled “シフトID採番”シフトIDは S-MMDD-NN 形式で自動採番する。同日内に既存するIDの最大連番に1を加えた値を採る。
催事↔商談の突合(疎結合連携)
Section titled “催事↔商談の突合(疎結合連携)”催事と商談は外部キーで直結せず、dealsForShift() による疎結合の突合で対応づける。突合条件は次の3つの完全一致である。
- 商談側
channel === "催事" - 商談側
venue === store.name(店舗マスタのnameと完全一致) - 商談側
createdAt === shift.date(日付の完全一致。期間・前後日は対象外)
疎結合を採用する理由は、第1弾では商談CRM本体が未リリースであり、先行する催事側に商談IDを持たせられない運用開始順序の非対称性による。終了報告の提出後、シフト詳細に「CRM連携」カードが表示され、報告済みアポ数(endReport.appointments.total)とCRM登録件数の差分を「未登録 N 件(入口フォームからの登録漏れの可能性)」として警告表示する。突合キーが店舗名の文字列一致と日付の完全一致に依存するため、店舗マスタ名を正とする入力運用を前提とする。
permissions.ts 上、催事シフトに関わるロールは社内3ロール(管理者/営業/事務(BO))と、代理店2ロール(代理店=管理者、代理店(メンバー))である。代理店の両ロールは isAgencyRole() により共通の「代理店ロール」として扱う。
| 操作 | 管理者・営業・事務(BO) | 代理店 | 代理店(メンバー) |
|---|---|---|---|
| シフト作成・編集 | 可(canManageShifts) | 不可 | 不可 |
| 自社シフト閲覧 | 可 | 可(eventScopeAgency でスコープ) | 可(eventScopeAgency でスコープ) |
| 他社シフト閲覧(一覧) | 可 | 不可 | 不可 |
| 開始報告・終了報告の提出* | — | 可 | 可 |
| 店舗・代理店マスタ編集 | 可 | 不可 | 不可 |
* 提出可否は現状、画面遷移(ナビゲーション)と運用ルールで統制しており、報告提出そのものを禁止するロールガードは実装されていない(確認事項参照)。
代理店(管理者・メンバーとも)は isExternal() により外部扱いとなり、仕切原価・粗利など社内の利益情報(canSeeCost)は非表示になる。案件の提案金額(canSeeProposal)は代理店には表示され、代理店(メンバー)には表示されない。同一のロール定義は案件詳細タブの可視範囲にも適用され、代理店(メンバー)は overview/process/chat のみ、代理店はこれに items/activity を加えたタブを見る。
一覧(/events)は eventScopeAgency により代理店の自社分のみにスコープされる。ただし詳細画面(/events/$shiftId、EventShiftDetail.tsx)は shiftId からシフトを直接取得するのみで代理店・会社IDによる絞り込みを行っておらず、他社シフトのURLを直接指定した場合のアクセスを防ぐガードは未実装である。試作はナビゲーション(NAV_ALLOW)と画面・ストア層のデータフィルタで絞り込む実装であり、本番実装では Supabase の API・RLS設計を別途行う。
- 店舗名の表記ゆれ(商談側
venueとの突合キー)に対する正規化ルールは未定。 - 同一店舗×同日×同一代理店の重複シフト登録を防ぐバリデーションの有無は、
events.ts/EventShiftDetail.tsxに登録処理自体が含まれておらず未確認。 - 終了報告の締切運用(当日中か翌朝までか)と、未報告時の督促フローは未定。
- 社内担当者が代理店に代わって開始報告・終了報告を代行入力できるかは、コンポーネント内にロール分岐が無く未確認。
category(系列)を選択式に強制する運用がUI側で担保されているかは未確認(型はコード上string)。