予約編集・移動
予約編集・移動
Section titled “予約編集・移動”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 のみが担当する。PATCH と PATCH /move を分離し、予約イベント (reservation_event) は発火させず operator_action_log のみを記録する。
設備の付替えは equipment_group 要件に基づき自動再割当する。force_staff_concurrency permission は staff concurrent 上限のみに効き、equipment EXCLUDE は通せない。
2. ユーザーストーリー
Section titled “2. ユーザーストーリー”- As a 受付, I want to 顧客都合で時間を動かしたい, so that 電話変更に素早く対応できる
- As a 店長, I want to メニューや設備を編集したい, so that 当日の実態に合わせられる
- As a スタッフ, I want to status を変えずに備考や担当を直したい, so that 台帳整合を保てる
3. ユースケース
Section titled “3. ユースケース”3.1 主シナリオ
Section titled “3.1 主シナリオ”- detail panel から編集を開く
- 必要項目だけ patch
- server が
versionを検証 - staff 変更時は
menu_staff_eligibilityと RES-005 conflict を再判定 - menu 変更時は
equipment_group要件に従って equipment を auto-reassign - 保存成功時に
operator_action_logを 1 行追加
3.2 代替フロー
Section titled “3.2 代替フロー”- 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
3.3 例外フロー
Section titled “3.3 例外フロー”- EF-1 request body に
status: 422RESERVATION.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_EQUIPMENT(force_staff_concurrencyでも bypass 不可)
4. UI仕様
Section titled “4. UI仕様”4.1 主要要素
Section titled “4.1 主要要素”- 編集フォーム
- move 操作(カレンダー D&D)
- conflict warning
- version conflict toast
4.2 バリデーション
Section titled “4.2 バリデーション”status入力禁止starts_at < ends_at- menu_lines 1 件以上
- equipment 自動割当に任せ、UI からの手動 equipment 指定は Phase 1 では非サポート
5. API仕様
Section titled “5. API仕様”5.1 エンドポイント一覧
Section titled “5.1 エンドポイント一覧”| Method | Path | 用途 | permission |
|---|---|---|---|
| PATCH | /api/admin/reservations/{id} | 一般編集 | admin:reservation:update |
| PATCH | /api/admin/reservations/{id}/move | 時間平行移動 | admin:reservation:update |
5.2 zodスキーマ
Section titled “5.2 zodスキーマ”PATCH /{id}:
version: numberrequiredcustomer_id?: uuidstaff_id?: uuid | nullstarts_at?: ISO8601ends_at?: ISO8601notes?: string | nullmenu_lines?: [{ menu_id, duration_minutes?, sort_order }]force_business_hours?: booleanforce_staff_concurrency?: booleanstatusは schema レベルで reject
PATCH /{id}/move:
version: numberstarts_at: ISO8601force_business_hours?: booleanforce_staff_concurrency?: boolean
response:
data.reservationdata.reservation_menu_linesdata.reservation_equipment_assignments
5.3 エラーコード
Section titled “5.3 エラーコード”RESERVATION.STATUS_EDIT_FORBIDDENRESERVATION.TERMINAL_EDIT_FORBIDDENRESERVATION.CONFLICTRESERVATION.DOUBLE_BOOKED_EQUIPMENTRESERVATION.STAFF_CONCURRENT_LIMITRESERVATION.NO_EQUIPMENT_AVAILABLE
6. データモデル影響
Section titled “6. データモデル影響”6.1 スキーマ
Section titled “6.1 スキーマ”- 既存
reservationsaggregate を活用 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 制約と整合)
6.2 マイグレーション計画
Section titled “6.2 マイグレーション計画”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_polymorphismCHECK 修正(RES-004 §6.2)force_staff_concurrencypermission 追加・rename(RES-005 §9.1)operator_action_log.diffGIN index 追加
7. 業務ルール
Section titled “7. 業務ルール”7.1 編集可能フィールド
Section titled “7.1 編集可能フィールド”statusを含む PATCH は 422- 終端予約は全フィールド編集禁止
in_service/service_completedは時間・staff・notes のみ編集可(menu_lines 変更は禁止、原因: 料金スナップショットが既に出ている可能性)
7.2 楽観ロック
Section titled “7.2 楽観ロック”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で自己除外しつつ再実行
7.5 equipment auto-reassign
Section titled “7.5 equipment auto-reassign”- menu_lines 変更時または
starts_at/ends_at変更時、equipment_group 要件から再 auto-assign - 旧 assignment 行は
is_active=falseUPDATE - 新 assignment 行は
is_active=trueINSERT - EXCLUDE 制約により race-safe
7.6 PATCH vs PATCH /move
Section titled “7.6 PATCH vs PATCH /move”/moveはstarts_atのみ変更、ends_atは duration を維持して再計算/moveは equipment 行を時間シフトする(reservation_equipment_assignments.starts_at/ends_atを新時間にスライド)- staff column 跨ぎ D&D は Phase 1 では UI 上禁止(
/moveではなく/PATCHで staff_id 変更)
7.7 reservation_event 発火なし
Section titled “7.7 reservation_event 発火なし”- 編集・移動では
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 でも通せない
7.9 監査
Section titled “7.9 監査”reservation.update: diff に変更前後を記録reservation.move: diff に旧 starts_at / 新 starts_at を記録- 力技 force 系は
diff.force_business_hours: true等で記録
8. 非機能要件
Section titled “8. 非機能要件”- move API p95 300ms
- 編集保存も同値目標
9. セキュリティ・認可
Section titled “9. セキュリティ・認可”| 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 / 422RESERVATION.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 / 409RESERVATION.DOUBLE_BOOKED_EQUIPMENT(force でも bypass 不可) - GWT-9: menu 差替 /
reservation_menu_lines.menu_name_snapshotも新 menu 名で再スナップショット - GWT-10: staff 変更で
menu_staff_eligibility不一致 / 422
11. テスト計画
Section titled “11. テスト計画”- Unit: duration recalc, equipment auto-reassign
- Integration: conflict recheck, terminal forbid, force* matrix
- E2E (Phase 2): drag move, dialog edit
12. 関連ジョブ
Section titled “12. 関連ジョブ”- なし
13. Open Questions
Section titled “13. Open Questions”| # | 内容 | 扱い |
|---|---|---|
| OQ-RES-003-01 | customer 差替えを Phase 1 で許可するか | Phase 1 は許可(audit 記録)、明示禁止は Phase 2 で再検討 |
| OQ-RES-003-02 | staff column 跨ぎ D&D | Phase 2 |
| OQ-RES-003-03 | menu 変更時の identity 維持アルゴリズム最適化 | Phase 3 |
14. 変更履歴
Section titled “14. 変更履歴”| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-05-05 | Codex / yudai | 初版 |
| 0.2 | 2026-05-05 | yudai (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 のみ記録 |