コンテンツにスキップ

予約編集・移動

Document ID: SRS-RES-003 Parent: SRS-ROOT-001 v0.5 Version: 0.2 Status: Implemented (API + Web UI 最小) Last Updated: 2026-05-06 Depends on: SRS-RES-001 v0.2, SRS-RES-002 v0.5, SRS-RES-004 v0.3, SRS-RES-005 v0.4, SRS-MST-001/002/005 依存される: なし

本書は SRS-ROOT-001 v0.5 に従う。


既存予約の内容編集と時間移動を扱う。状態遷移は SRS-RES-004 のみが担当する。PATCHPATCH /move を分離し、予約イベント (reservation_event) は発火させず operator_action_log のみを記録する。

設備の付替えは equipment_group 要件に基づき自動再割当する。force_staff_concurrency permission は staff concurrent 上限のみに効き、equipment EXCLUDE は通せない。


  • As a 受付, I want to 顧客都合で時間を動かしたい, so that 電話変更に素早く対応できる
  • As a 店長, I want to メニューや設備を編集したい, so that 当日の実態に合わせられる
  • As a スタッフ, I want to status を変えずに備考や担当を直したい, so that 台帳整合を保てる

  1. detail panel から編集を開く
  2. 必要項目だけ patch
  3. server が version を検証
  4. staff 変更時は menu_staff_eligibility と RES-005 conflict を再判定
  5. menu 変更時は equipment_group 要件に従って equipment を auto-reassign
  6. 保存成功時に operator_action_log を 1 行追加
  • AF-1 同一 column の縦ドラッグ: PATCH /move を使う
  • AF-2 staff 変更を伴う編集: 一般 PATCH
  • AF-3 in_service / service_completed の編集: admin:reservation:update + 業務制限ガード
  • AF-4 terminal status の編集: 禁止
  • AF-5 equipment auto-reassign の不足: 422 RESERVATION.NO_EQUIPMENT_AVAILABLE
  • EF-1 request body に status: 422 RESERVATION.STATUS_EDIT_FORBIDDEN
  • EF-2 version 不一致: 409 RESERVATION.CONFLICT
  • EF-3 terminal reservation 編集: 422 RESERVATION.TERMINAL_EDIT_FORBIDDEN
  • EF-4 staff eligibility 不一致: 422
  • EF-5 equipment overlap (EXCLUDE): 409 RESERVATION.DOUBLE_BOOKED_EQUIPMENTforce_staff_concurrency でも bypass 不可)

  • 編集フォーム
  • move 操作(カレンダー D&D)
  • conflict warning
  • version conflict toast
  • status 入力禁止
  • starts_at < ends_at
  • menu_lines 1 件以上
  • equipment 自動割当に任せ、UI からの手動 equipment 指定は Phase 1 では非サポート

MethodPath用途permission
PATCH/api/admin/reservations/{id}一般編集admin:reservation:update
PATCH/api/admin/reservations/{id}/move時間平行移動admin:reservation:update

PATCH /{id}:

  • version: number required
  • customer_id?: uuid
  • staff_id?: uuid | null
  • starts_at?: ISO8601
  • ends_at?: ISO8601
  • notes?: string | null
  • menu_lines?: [{ menu_id, duration_minutes?, sort_order }]
  • force_business_hours?: boolean
  • force_staff_concurrency?: boolean
  • status は schema レベルで reject

PATCH /{id}/move:

  • version: number
  • starts_at: ISO8601
  • force_business_hours?: boolean
  • force_staff_concurrency?: boolean

response:

  • data.reservation
  • data.reservation_menu_lines
  • data.reservation_equipment_assignments
  • RESERVATION.STATUS_EDIT_FORBIDDEN
  • RESERVATION.TERMINAL_EDIT_FORBIDDEN
  • RESERVATION.CONFLICT
  • RESERVATION.DOUBLE_BOOKED_EQUIPMENT
  • RESERVATION.STAFF_CONCURRENT_LIMIT
  • RESERVATION.NO_EQUIPMENT_AVAILABLE

  • 既存 reservations aggregate を活用
  • reservation_menu_lines.menu_name_snapshot は RES-002 v0.5 で追加
  • equipment auto-reassign のため reservation_equipment_assignments の行を delete-and-recreate するか UPDATE で identity 維持するかは実装判断(推奨: 旧行 is_active=false + 新行 INSERT、EXCLUDE 制約と整合)

0019_reservation_phase1_finalize.sql で:

  • reservation_menu_lines.menu_name_snapshot varchar(200) NOT NULL 追加(既存行は menu join で backfill)
  • reservations.code varchar(20) NOT NULL, channel_code varchar(40) NOT NULL DEFAULT 'admin_manual' 追加
  • reservation_event_actor_polymorphism CHECK 修正(RES-004 §6.2)
  • force_staff_concurrency permission 追加・rename(RES-005 §9.1)
  • operator_action_log.diff GIN index 追加

  • status を含む PATCH は 422
  • 終端予約は全フィールド編集禁止
  • in_service / service_completed は時間・staff・notes のみ編集可(menu_lines 変更は禁止、原因: 料金スナップショットが既に出ている可能性)
  • If-Match: <version> 必須
  • 不一致は 409 RESERVATION.CONFLICT
  • 成功時 version + 1

7.3 menu 変更時の料金スナップショット再計算

Section titled “7.3 menu 変更時の料金スナップショット再計算”
  • menu_lines 配列を完全置換
  • 各 menu から name_snapshot, duration_minutes, price_*, tax_*, buffer_minutes_after を再スナップショット
  • reservation.buffer_minutes_after は menu 群の最大値で再計算

7.4 staff 変更時の eligibility / conflict 再判定

Section titled “7.4 staff 変更時の eligibility / conflict 再判定”
  • menu_staff_eligibility を再評価(MST-002 §7.4)
  • RES-005 conflict-check を excludeReservationId で自己除外しつつ再実行
  • menu_lines 変更時または starts_at / ends_at 変更時、equipment_group 要件から再 auto-assign
  • 旧 assignment 行は is_active=false UPDATE
  • 新 assignment 行は is_active=true INSERT
  • EXCLUDE 制約により race-safe
  • /movestarts_at のみ変更、ends_at は duration を維持して再計算
  • /move は equipment 行を時間シフトする(reservation_equipment_assignments.starts_at/ends_at を新時間にスライド)
  • staff column 跨ぎ D&D は Phase 1 では UI 上禁止(/move ではなく /PATCH で staff_id 変更)
  • 編集・移動では reservation_event発火しない(status 不変のため)
  • operator_action_log(action='reservation.update' or 'reservation.move') のみ記録

7.8 force_staff_concurrency / force_business_hours

Section titled “7.8 force_staff_concurrency / force_business_hours”
  • admin:reservation:force_staff_concurrency: staff concurrent 上限警告のみ通せる
  • admin:reservation:force_business_hours: 営業時間外警告のみ通せる
  • equipment EXCLUDE は どの force でも通せない
  • reservation.update: diff に変更前後を記録
  • reservation.move: diff に旧 starts_at / 新 starts_at を記録
  • 力技 force 系は diff.force_business_hours: true 等で記録

  • move API p95 300ms
  • 編集保存も同値目標

key用途
admin:reservation:update編集・移動
admin:reservation:force_business_hours営業時間外押し通し
admin:reservation:force_staff_concurrencyスタッフ並列上限押し通し

operator_staff_link は編集可否の条件にしない(Phase 1)。


10. 受け入れ基準(Given-When-Then)

Section titled “10. 受け入れ基準(Given-When-Then)”
  • GWT-1: confirmed reservation / notes のみ patch / status 不変で更新
  • GWT-2: body に status / PATCH / 422 RESERVATION.STATUS_EDIT_FORBIDDEN
  • GWT-3: move request / starts_at 変更 / ends_at は duration 維持で再計算
  • GWT-4: equipment_group 要件 quantity=2、空き 1 台のみ、move で時間衝突 / 422 RESERVATION.NO_EQUIPMENT_AVAILABLE
  • GWT-5: terminal reservation / edit / 422 RESERVATION.TERMINAL_EDIT_FORBIDDEN
  • GWT-6: edit 成功 / operator_action_log のみ追加され reservation_event は追加されない
  • GWT-7: force_staff_concurrency=true で staff 並列上限超過 / 成功(diff に痕跡)
  • GWT-8: force_staff_concurrency=true で equipment overlap / 409 RESERVATION.DOUBLE_BOOKED_EQUIPMENT(force でも bypass 不可)
  • GWT-9: menu 差替 / reservation_menu_lines.menu_name_snapshot も新 menu 名で再スナップショット
  • GWT-10: staff 変更で menu_staff_eligibility 不一致 / 422

  • Unit: duration recalc, equipment auto-reassign
  • Integration: conflict recheck, terminal forbid, force* matrix
  • E2E (Phase 2): drag move, dialog edit

  • なし

#内容扱い
OQ-RES-003-01customer 差替えを Phase 1 で許可するかPhase 1 は許可(audit 記録)、明示禁止は Phase 2 で再検討
OQ-RES-003-02staff column 跨ぎ D&DPhase 2
OQ-RES-003-03menu 変更時の identity 維持アルゴリズム最適化Phase 3

VersionDateAuthorChange
0.12026-05-05Codex / yudai初版
0.22026-05-05yudai (with Codex co-design)Round 2 反映: migration 番号 0019、equipment auto-reassign を equipment_group 連動で確定、force_staff_concurrency rename、equipment EXCLUDE は force でも bypass 不可と明記、reservation_event 不発火・operator_action_log のみ記録