0016. 横乗り (当日ルート単位の同乗者)¶
- ステータス: Accepted(実装済み)
- 関連: 0008 配車コースモデル、0006 工場スコープのアクセス制御
コンテキストと課題¶
回収業務では、ドライバー 1 人に対し 同乗者 (横乗り) が付くことがあります。新人の同行教育や 人手の要る回収先への増員などで、別の車両は使わず ドライバーの当日ルートに「同行する人」を 記録したいという要件です。
これを既存の配車モデル (dispatch_stops) で表現しようとすると噛み合いません。dispatch_stops は
回収先 (collection_site) への訪問 を stop_order で並べたもので、生成 RPC・ドラッグ&ドロップ
編集・完了状態を持ちます。同乗者は「どの回収先に行くか」ではなく「誰のルートに同行するか」だけが
本質で、ストップの並びや生成 RPC・D&D に 干渉させたくありません。
決定¶
横乗りを dispatch_ride_alongs テーブルに独立して持ち、紐付け単位を「ドライバーの当日ルート
(driver_id × dispatch_date)」とする。配車ルート (dispatch_stops / stop_order)・生成 RPC・D&D
には一切干渉しない、当日割当の 付与と表示のみ の機能です。
- データモデル: 1 行 = ある配車日・あるドライバーの当日ルートに同乗する 1 メンバー。
dispatch_ride_alongs (id, tenant_id, dispatch_date, driver_id → profiles, profile_id → profiles, created_by → profiles nullable, created_at)。両 FK はon delete cascade。 - 本人除外: そのルートのドライバー本人は横乗りに選べない (冗長)。
driver_id <> profile_idを DB の check 制約 でも担保し、候補算出computeRideAlongCandidatesでも除外する。 - 複数可・重複不可: テナント全メンバー (admin / normal / driver) から 複数選択可。
同一ルートへ同じ人を二重登録しないよう
unique (tenant_id, dispatch_date, driver_id, profile_id)。 - グルーピング:
groupRideAlongsByDriverがdriver_id単位で束ね、配車ボードの列ヘッダーに 同乗者チップとして表示する (driver_id × dispatch_dateがルート単位のため driverId で束ねれば その列の同乗者一覧になる)。 - 候補算出:
computeRideAlongCandidates= 全メンバー − そのルートのドライバー本人 − 既存の同乗者。 候補が空なら追加導線を出さない。 - RLS (
dispatch_stopsの mig25 / 41 / 55 に倣う):- SELECT: テナント全件 (
tenant_id = current_tenant_id())。driver も自テナントの横乗りを閲覧可。 - 書込 (ALL): admin / normal のみ。WITH CHECK で
driver_id/profile_idが同一テナントに 属することをexistsで検証する (ADR 0017 の 越境参照ハードニング (c) を踏襲)。 - tenant が collector であることは
ensure_collector_tenantトリガで担保 (dispatch_stops と同様)。
- SELECT: テナント全件 (
影響 (Consequences)¶
良い点¶
- 同乗者を配車ルートから 独立 させたことで、
dispatch_stopsの生成 RPC・stop_order・D&D・ 完了状態に一切影響を与えずに付与・表示できる (責務分離)。 - 紐付けを
driver_id × dispatch_dateにしたことで、ボード上では driverId で束ねるだけで列ヘッダーの チップになり、実装がシンプル。 - 本人除外・二重登録防止を DB 制約 (check / unique) と純粋ロジック (候補算出) の 両方 で担保。
- 越境参照検証を WITH CHECK の
existsで行うため、他テナントのprofilesをdriver_id/profile_idに紐付けられない (FK は存在のみ保証で越境を防げないため)。
トレードオフ / 注意点¶
- 横乗りは「同行者」の記録であり、車両割当や capacity 計算には関与しない (別の車両を使わない前提)。 車両を伴う増車が必要な場合は配車ルート側の操作になる。
- ストップ単位ではなくルート単位のため、「特定の回収先だけ同行」のような粒度は表現しない (運用上の割り切り)。
- 列ヘッダーにチップが増えると視覚的に密になりうるが、当日 1 ルートあたりの同乗者は少数の想定。
根拠 (典拠)¶
supabase/migrations/00000000000057_dispatch_ride_alongs.sql:25-37— テーブル定義 (driver_id/profile_id→profileson delete cascade、created_bynullable、driver_id <> profile_idcheck、unique (tenant_id, dispatch_date, driver_id, profile_id))。supabase/migrations/00000000000057_dispatch_ride_alongs.sql:42-46—ensure_collector_tenantトリガ (collector テナント担保)。supabase/migrations/00000000000057_dispatch_ride_alongs.sql:51-91— RLS (SELECT テナント全件 / 書込 admin・normal + WITH CHECK でdriver_id・profile_idの同一テナントをexists検証 / service_role)。apps/web/src/lib/dispatch-ride-along.ts:33-41—groupRideAlongsByDriver(driverId 単位グルーピング・登録順保持)。apps/web/src/lib/dispatch-ride-along.ts:48-56—computeRideAlongCandidates(全メンバー − ドライバー本人 − 既存 profileId)。apps/web/src/lib/db/dispatch-ride-alongs.ts:21-66— 読み書きフック (useDispatchRideAlongs/useAddRideAlong/useRemoveRideAlong、SELECT は date でフィルタ・書込は admin/normal)。apps/web/src/pages/collector/dispatch/DispatchBoardPage.tsx:274-276,1080-1105— 列ヘッダーへのチップ表示と追加導線 (RideAlongSection)。