コンテンツにスキップ

催事運営

位置づけ: 第1弾(催事+アイテム+商社)

催事運営は、住宅設備商社が実施する店舗催事における代理店シフト管理、現場の開始・終了報告、シフト状態の自動判定、商談CRMとの突合を扱うドメインである。催事店舗マスタ・代理店(販売店)マスタ・催事シフト・開始報告・終了報告の5要素で構成する。

本ページの記述は試作実装(crm_app/app/src/lib/events.tscrm_app/app/src/lib/permissions.tscrm_app/app/src/features/crm/EventShiftDetail.tsx/events として先行取り込み済みの event-shift-system)を正とし、フィールド名・enum・分岐条件をそのまま反映する。

シフトの状態は保存せず、開始報告・終了報告の有無と基準時刻(開始時刻・終了時刻)から都度導出する。催事と商談CRMは外部キーで直結せず、店舗名・日付の一致による疎結合の突合で対応づける。

対象は次のとおり。

  • 催事店舗マスタ、代理店(販売店)マスタ
  • 催事シフト(1日×1店舗×1代理店の単位)
  • 開始報告(朝礼チェックリスト)、終了報告(現場ファネル実績)
  • シフト状態の自動判定
  • 催事と商談CRMの突合
  • 権限モデル(社内3ロール/代理店2ロール)

対象外は次のとおり。いずれも後続フェーズで扱う。

対象外項目扱う場所
顧客・商談の管理本体CRM基本機能
見積金額・提案金額価格表・見積
売上・KPI予実の集計KPI予実
卸金額・受発注商社(卸)連携

終了報告は現場成果(抽選・着座・アポの件数)を一次データとして持つが、売上金額・受注金額の入力は持たない。金額系は商談モジュールおよび商社(卸)モジュールの管轄であり、これは意図的な設計上の切り分けである。個人スタッフ(代理店所属メンバー)のマスタ化・シフトへの個人アサインも対象外とし、責任者は氏名の文字列入力、人数は数値入力で足りる軽量設計とする。

フィールド型・入力形式必須説明
id自動採番(例: ST01必須主キー
name自由入力必須店舗名。商談側 venue との突合キーになる
area自由入力任意都道府県目安
category自由文字列任意系列・チェーン(カインズ/島忠/ビバホーム等)。絞り込みに使う

代理店(催事会社)は別マスタを設けず、代理店(販売店)マスタに一本化する。代理店は催事・訪販の両方を担うため、事業区分は1値で表す。

フィールド型・入力形式必須説明
id自動採番(例: CO01必須主キー
name自由入力必須代理店名
contact自由入力任意連絡先
business選択式: 催事/訪販/催事・訪販任意事業区分

粒度は「1日×1店舗×1代理店」。同一店舗・同日に複数代理店を入れる場合はシフトを分ける。

フィールド型・入力形式必須説明
id自動採番(S-MMDD-NN 形式)必須主キー。同日内の既存IDの最大連番+1
dateYYYY-MM-DD必須催事日
storeId店舗マスタ参照必須実施店舗
companyId代理店マスタ参照必須担当代理店
startTime / endTimeHH:MM必須シフト時間帯
plannedLeader自由入力(氏名)任意予定責任者
staffCount数値必須人数
note自由入力任意メモ
startReportStartReport未提出時 undefined開始報告
endReportEndReport未提出時 undefined終了報告
フィールド型・入力形式必須説明
leader自由入力(氏名)必須当日責任者。plannedLeader とは別フィールドで記録し、予定と実績の差異を残す
emergencyContact自由入力(電話番号)必須緊急連絡先
checksチェック項目id→真偽のマップ任意朝礼チェックリスト
comment自由入力任意コメント
photosReportPhoto{name,url} の配列任意現場写真
atISO日時自動記録提出日時

朝礼チェックリストはハードコードせず、マスタ(useEventsstartChecks)から与える。既定値は次の7項目(DEFAULT_START_CHECKS)。

id項目
greeting挨拶・声かけ
mind身だしなみ・気持ちの準備
boothブース設営の確認
backyardバックヤード整理
private私語・スマホのルール確認
rule店舗ルールの確認
judge判断に迷ったら責任者へ

現場ファネル3段階(抽選→着座→アポ)の実績を、各段階とも FunnelCount{total, both, single}(合計/両面提案/片面提案の内訳)で記録する。

フィールド型・入力形式必須説明
department自由入力任意部門(住設 等)
lotteryFunnelCount必須抽選:ブースでの抽選参加者数
seatedFunnelCount必須着座:商談ブースへの着座数
appointmentsFunnelCount必須アポ:後日商談アポ獲得数
photosReportPhoto{name,url} の配列任意現場写真
atISO日時自動記録提出日時
画面内容
催事シフト一覧(/eventsシフトをステータス付きで一覧表示。代理店ロールは自社分のみ表示
催事シフト詳細(/events/$shiftId店舗・時間帯・代理店・人数・責任者の帯表示、開始報告カード、終了報告カード、CRM連携カード
開始報告カード未提出時はチェックリスト・責任者・緊急連絡先・コメント・写真の入力フォーム。提出済み時は読み取り専用表示
終了報告カード未提出時はファネル3段階×3区分(合計・両面・片面)の数値入力フォーム。提出済み時は着座率・アポ率を含む読み取り専用表示
CRM連携カード終了報告提出後に表示。報告済みアポ数とCRM登録件数の差分を警告表示する

提出済みの開始報告・終了報告は読み取り専用表示のみであり、再編集用のUIは持たない。

shiftStatus() は保存済みの状態を持たず、startReport / endReport の有無と基準時刻(startTime / endTime)から都度導出する。判定は now >= startAt / now >= endAt の時刻比較であり、猶予時間は設けない。基準時刻を過ぎた瞬間に状態が切り替わる。

ステータス現在時刻の条件開始報告終了報告一覧での表示トーン
終了済み問わないありsecondary(グレー)
終了未報告終了時刻を過ぎたありなし警告色
開始済み終了時刻前ありなし進行中トーン
未開始開始時刻を過ぎたなしなし警告色(赤系)
予定開始時刻前なしなしsecondary(標準)

終了報告があれば時刻に関わらず無条件で「終了済み」となる。終了時刻前の早期終了報告でも成立する。異常ステータス(未開始・終了未報告)の自動通知機能は持たず、一覧の目視運用で検知する。

全チェック完了は送信の必須条件ではない。未チェックの項目があっても送信ボタンは有効であり、画面には「未チェック N 件(そのまま送信できます)」という警告のみを表示する。送信に必須なのは leader(責任者)と emergencyContact(緊急連絡先)の非空入力のみである。報告時刻(at)は自動記録され、ステータス「開始済み」への遷移トリガーになる。

終了報告は開始報告の提出後にのみ入力可能である。開始報告が未提出の場合、終了報告カードには「開始報告の送信後に入力できます」の案内のみを表示し、入力欄自体を出さない。提出済み表示では、着座率(着座total÷抽選total)とアポ率(アポtotal÷着座total)を自動算出する。分母が0のときは — と表示する。totalbothsingle の内訳一致を強制するバリデーションは持たず、各セルは独立した数値入力とする。

開始報告・終了報告とも現場写真を添付できる(ReportPhoto{name,url})。試作実装ではブラウザ内で dataURL として保持するモック実装であり、本番実装では Storage の URL に差し替える。

シフト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/$shiftIdEventShiftDetail.tsx)は shiftId からシフトを直接取得するのみで代理店・会社IDによる絞り込みを行っておらず、他社シフトのURLを直接指定した場合のアクセスを防ぐガードは未実装である。試作はナビゲーション(NAV_ALLOW)と画面・ストア層のデータフィルタで絞り込む実装であり、本番実装では Supabase の API・RLS設計を別途行う。

  • 店舗名の表記ゆれ(商談側 venue との突合キー)に対する正規化ルールは未定。
  • 同一店舗×同日×同一代理店の重複シフト登録を防ぐバリデーションの有無は、events.ts / EventShiftDetail.tsx に登録処理自体が含まれておらず未確認。
  • 終了報告の締切運用(当日中か翌朝までか)と、未報告時の督促フローは未定。
  • 社内担当者が代理店に代わって開始報告・終了報告を代行入力できるかは、コンポーネント内にロール分岐が無く未確認。
  • category(系列)を選択式に強制する運用がUI側で担保されているかは未確認(型はコード上 string)。