0008. 配車コースモデル (courses / stops / assignments + 生成 RPC)¶
- ステータス: Accepted(実装済み)
- 関連: 0006 工場スコープのアクセス制御 / 0007 can_drive 能力フラグ
コンテキストと課題¶
処理業者の配車は「毎週・第 N 週の特定曜日に、決まった回収先群を、担当ドライバーが回る」という 繰り返しパターンが中心。これを毎日手で組むのは非効率なので、繰り返し定義から その日の 配車 (ドライバー列) を自動生成したい。一方で、当日に手で差し込む回収先 (繰り返し設定だけ持つ 回収先) も「未割当プール」として扱えるようにしたい。
決定¶
配車を コース (course) と 割当 (assignment) に分けてモデル化し、生成 RPC で日次の
dispatch_stops を materialize する。回収先は collection_site_id を主キーとして扱う。
テーブル (migration 40)¶
| テーブル | 役割 | 主な列 |
|---|---|---|
dispatch_courses |
コース (回収先の順序付き束) | name(tenant 内一意) / active |
dispatch_course_stops |
コース内の停車地 | course_id / collection_site_id / stop_order |
dispatch_course_assignments |
「曜日 × 週序数」へのコース割当 | course_id / driver_id / weekday / week_ordinal |
- 曜日 (
weekday):0=日 .. 6=土(extract(dow)/ JSgetDay()と一致、check (weekday between 0 and 6))。 - 週序数 (
week_ordinal):dispatch_course_assignmentsは('every','1','2','3','4','last')。 第 5 週'5'は持たない点に注意 (後述の回収先側とは異なる)。
生成 RPC (migration 45)¶
security definer。admin/normalのみ実行可。当日のdow/ 第 N 週 / 最終週を計算する。- 発火する割当を展開:
weekday = dowかつ (week_ordinal = 'every'または= 第N週または'last' かつ最終週) を満たす割当の、active コースの停車地を集める。 (driver, collection_site)の重複は (コース名, コース内stop_order) 最小の 1 件へ畳み、 ドライバー毎に 0 始まりのstop_orderを採番してdispatch_stopsに挿入する。p_forceの意味:false(既定) … 冪等。当日の stops が既にあれば0を返し二重生成しない。true… 当日 (tenant, date) のdispatch_stopsを先に削除してから現在のコース割当で 作り直す (手動 D&D の編集は破棄)。配車ボードの「再生成」が使う。
フロントは supabase.rpc('generate_dispatch_stops_from_courses', { p_date, p_force }) で呼ぶ
(useGenerateDispatchStopsFromCourses)。
未割当プールは「回収先の繰り返し」駆動 (別経路)¶
- 回収先
collection_sites(migration 36) と、その繰り返しcollection_site_schedulesを持つ。collection_site_schedules.week_ordinalは('every','1','2','3','4','5','last')で、 第 5 週'5'を含む (コース割当側と非対称)。 - 未割当プール = その日に
collection_site_schedulesで該当する active な回収先のうち、まだ ドライバー列に配置されていないもの (computePoolSites/isSiteDueOnDate)。 コース生成 (generate_dispatch_stops_from_courses) はドライバー列の事前生成のみを担い、 プールの源泉ではない。両者は独立した経路。
回収先への re-key (migration 37)¶
配車は当初 partner_id を持っていたが、migration 37 で collection_site_id に張り替えた
(回収先ごとに既定の collection_site をバックフィルし、曜日繰り返しを collection_site_schedules
へ移行)。dispatch_stops は partner_id を落とし、collection_site_id を not null + FK 化、
一意制約も (tenant, date, driver, collection_site) に更新。
影響 (Consequences)¶
良い点¶
- 繰り返し定義 (コース割当) から日次配車を 冪等に自動生成 でき、
p_forceで「割当変更を後から 反映する再生成」もできる。 - 「ドライバー列 = コース割当駆動」「プール = 回収先の繰り返し駆動」と源泉を分けたことで、 当日の手差し込み (プール) と定常配車 (コース) を別々に運用できる。
- 重複畳み込みと per-driver 採番が RPC 内で完結し、フロントは結果を受け取るだけでよい。
トレードオフ / 注意点¶
- 週序数の非対称性: コース割当は
'5'を持たず、回収先繰り返しは'5'を持つ。型もCOURSE_WEEK_ORDINALS(6 値) とWEEK_ORDINALS(7 値) に分かれており、取り違え注意。 p_force = trueは手動編集を破棄する。配車ボードの D&D 編集後に再生成すると当日の手作業が 消えるため、UI 側で「未割当状態のリセット」を伴う。- ドライバー列とプールが別経路のため、「コース専用 (繰り返し非該当) の回収先」を外したときに プールへ戻す等、両経路を突き合わせる補正ロジックがフロントに必要。
- 生成は当日単位の materialize なので、過去/未来日を見るには各日について生成/参照する設計。
根拠 (典拠)¶
supabase/migrations/00000000000040_dispatch_courses.sql:16-17,27-49,69-110,115-159— 曜日の意味、3 テーブル定義、dca_weekday_range/dca_week_ordinal_check ('every','1','2','3','4','last')/dca_unique。supabase/migrations/00000000000045_dispatch_stops_from_courses_force.sql:1-27,33-110—p_force設計コメント、RPC 本体 (security definer、admin|normal、発火条件、重複畳み込み、per-driver 採番)。supabase/migrations/00000000000038_generate_dispatch_stops_recurrence.sql/00000000000026_generate_dispatch_stops.sql/00000000000024_dispatch_route_templates.sql/00000000000025_dispatch_stops.sql— 旧generate_dispatch_stops(route templates + collection_site_schedules) と前身テーブル。supabase/migrations/00000000000036_collection_sites.sql:2-12,51-62—collection_sitesとcollection_site_schedules(week_ordinalに'5'を含む)。supabase/migrations/00000000000037_dispatch_rekey_collection_site.sql:1-8,56-64— partner_id → collection_site_id への re-key とdispatch_stopsの制約更新。supabase/migrations/00000000000041_dispatch_write_admin_normal.sql/00000000000042_dispatch_stops_from_courses_renumber.sql— 書込 admin|normal 化と per-driver 採番。apps/web/src/lib/dispatch-pool.ts:1-42/apps/web/src/pages/collector/dispatch/DispatchBoardPage.tsx:215-224,453-471— プールは collection_site_schedules 駆動 (コース生成はプールの源泉ではない)、force:trueの再生成。packages/shared/src/schemas/dispatch-course.ts:10-16,77-86/apps/web/src/lib/recurrence.ts:3-13,76-87—COURSE_WEEK_ORDINALS(6 値) vsWEEK_ORDINALS(7 値) と曜日算出。