コンテンツにスキップ

設備管理

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

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


設備マスタを 1 台 1 行 で管理する。capacity 列は持たない。 同種設備の同時利用可能台数は equipment_group でまとめ、menu_equipment_requirement から (equipment_group_id, quantity) で参照する。

EXCLUDE 制約は concrete equipment_id 単位で 0009_reservation_equipment_exclude.sql により既に実装済(SRS-RES-005 v0.4 §6.2 参照)。


  • As a 店長, I want to シャンプー台を設備として登録したい, so that 予約時に競合判定できる
  • As a 店長, I want to 同種の設備を 1 台ずつ独立に登録しつつ group でまとめたい, so that 数量要件で予約できる
  • As a 受付, I want to 故障中設備を予約候補から外したい, so that 誤割当を防げる
  • As a 店長, I want to 退役設備を履歴は残しつつ新規候補から消したい, so that 過去予約を壊さない

3.1 主シナリオ(設備グループ + 設備作成)

Section titled “3.1 主シナリオ(設備グループ + 設備作成)”
  1. オペレーターが「設定 > 設備」を開く
  2. equipment_group を作成(例: 「シャンプー台」)
  3. group 配下に equipment を必要台数分作成(例: 「シャンプー台 1」「シャンプー台 2」「シャンプー台 3」)
  4. メニュー (MST-002) から menu_equipment_requirement(equipment_group_id, quantity=1) で参照する
  • AF-1 bookable=false: 故障・清掃中等で新規割当対象から外す
  • AF-2 retire: terminated_at を立てる
  • AF-3 restore: terminated_at = NULL
  • AF-4 reorder: active row のみ
  • AF-5 group のリネーム: 既存予約への影響なし
  • AF-6 group の retire: 配下の equipment が全て terminated でないと retire 不可
  • EF-1 If-Match 不一致: 409
  • EF-2 stale reorder ETag: 409
  • EF-3 future reservation assignment を抱える設備の retire: 422 EQUIPMENT.ACTIVE_ASSIGNMENTS_EXIST
  • EF-4 group に active equipment が残る状態での group retire: 422 EQUIPMENT_GROUP.HAS_ACTIVE_EQUIPMENT
  • EF-5 他店舗設備操作: 404

設備グループ一覧 + 配下 concrete 設備を管理する。

  • equipment_group list
  • group 配下の active equipment list
  • terminated list
  • create / edit dialog(group / equipment 両方)
  • retire / restore button
  • reorder UI
  • group name: 1..100 文字、trim 後空文字不可
  • equipment name: 1..100 文字、trim 後空文字不可
  • bookable: boolean

MethodPath用途permission楽観ロック
GET/api/admin/equipment-groupsgroup 一覧admin:equipment:read-
POST/api/admin/equipment-groupsgroup 作成admin:equipment:create-
PATCH/api/admin/equipment-groups/:groupIdgroup 更新admin:equipment:updateIf-Match: <version>
POST/api/admin/equipment-groups/:groupId/retiregroup retireadmin:equipment:retireIf-Match: <version>
POST/api/admin/equipment-groups/:groupId/restoregroup restoreadmin:equipment:retireIf-Match: <version>
GET/api/admin/equipmentactive 一覧 (全グループ横断)admin:equipment:read-
GET/api/admin/equipment/terminated?cursor=&limit=retired 一覧admin:equipment:read-
GET/api/admin/equipment/:equipmentId詳細admin:equipment:read-
POST/api/admin/equipment設備作成admin:equipment:create-
PATCH/api/admin/equipment/:equipmentId設備更新admin:equipment:updateIf-Match: <version>
PUT/api/admin/equipment/order全 active 並び順admin:equipment:updateIf-Match: <collection_etag>
POST/api/admin/equipment/:equipmentId/retire設備 retireadmin:equipment:retireIf-Match: <version>
POST/api/admin/equipment/:equipmentId/restore設備 restoreadmin:equipment:retireIf-Match: <version>
code意味HTTP
EQUIPMENT.NOT_FOUND行なし404
EQUIPMENT.VERSION_MISMATCH行 ETag 不一致409
EQUIPMENT.COLLECTION_ETAG_MISMATCHreorder ETag 不一致409
EQUIPMENT.ACTIVE_ASSIGNMENTS_EXISTfuture active assignment あり422
EQUIPMENT_GROUP.HAS_ACTIVE_EQUIPMENTgroup retire 時に active equipment 残存422
EQUIPMENT_GROUP.NOT_FOUNDgroup 行なし404

カラム制約
iduuidPK
store_iduuidNOT NULL, FK
namevarchar(100)NOT NULL, CHECK btrim(name) <> ''
display_orderintegerNOT NULL, CHECK >= 1
terminated_attimestamptzNULL
versionintegerNOT NULL DEFAULT 1, CHECK >= 1
created_attimestamptzNOT NULL
updated_attimestamptzNOT NULL
制約UNIQUE(store_id, id)
制約UNIQUE(store_id, display_order) WHERE terminated_at IS NULL
カラム制約
iduuidPK
store_iduuidNOT NULL, FK
group_iduuidNULL, 複合 FK → equipment_group(store_id, id)
namevarchar(100)NOT NULL, CHECK btrim(name) <> ''
bookablebooleanNOT NULL DEFAULT true
display_orderintegerNOT NULL, CHECK >= 1
terminated_attimestamptzNULL
versionintegerNOT NULL DEFAULT 1, CHECK >= 1
created_attimestamptzNOT NULL
updated_attimestamptzNOT NULL
制約UNIQUE(store_id, id)
制約UNIQUE(store_id, display_order) WHERE terminated_at IS NULL

group_id は NULL 許容(group なしの単独設備も可、ただし menu_equipment_requirement から参照不可)。

capacity 列は 存在しない

0017_equipment_master_and_groups.sql で:

  1. equipment_group 新規作成
  2. equipment placeholder を本番 schema へ昇格(group_id, name, bookable, display_order, terminated_at, version 追加)
  3. menu_equipment_requirement への FK を有効化(0014 で先行作成、0017 で確定)
  4. RLS ENABLE + FORCE
  5. app ロールへ GRANT
  6. admin:equipment:* permission backfill (4 keys)

  • 実設備 1 台 = 1 row
  • 同種設備 3 台なら 3 row 作る + 共通 equipment_group を 1 行作る
  • UI grouping は表示上の補助
  • 予約競合判定単位は常に concrete equipment.id(EXCLUDE 制約)
状態terminated_atbookable意味
通常NULLtrue新規予約に使える
一時停止NULLfalse履歴維持、予約候補から除外
退役NOT NULL任意新規予約に使えない
  • equipment retire: reservation_equipment_assignments.is_active=true AND ends_at > now() の future assignment がある設備は retire 不可
  • group retire: 配下に active (terminated_at IS NULL) equipment が残っていれば 422
  • 0009_reservation_equipment_exclude.sql の EXCLUDE 制約はそのまま活きる
  • 同一 group 内の複数 equipment は 別設備 として EXCLUDE される
  • menu_equipment_requirement(equipment_group_id, quantity) から「group 内 quantity 台」を要求し、RES-002 が auto-assign する(MST-002 §7.5 参照)
  • equipment_group.create / update / retire / restore
  • equipment.create / update / retire / restore / reorder

  • group / active equipment list は full fetch
  • retired list は cursor pagination
  • no-op reorder は version / audit を増やさない

key用途初期付与
admin:equipment:read設備・設備グループ閲覧全 role
admin:equipment:create設備・設備グループ作成owner / manager
admin:equipment:update設備・設備グループ更新、reorderowner / manager
admin:equipment:retire設備・設備グループ retire / restoreowner / manager

RLS / FORCE / 複合 FK / GRANT 明示は必須。


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

Section titled “10. 受け入れ基準(Given-When-Then)”
  1. Given group + equipment 作成 / When 一覧取得 / Then group 配下に equipment が紐づく
  2. Given 同 group 2 台 / When 1 台が予約使用中 / Then もう 1 台は予約可能(EXCLUDE で弾かれない)
  3. Given bookable=false / When RES-002 候補取得 / Then 新規予約候補から除外される
  4. Given future active assignment あり / When retire / Then 422 EQUIPMENT.ACTIVE_ASSIGNMENTS_EXIST
  5. Given future active assignment なし / When retire / Then terminated_at が立つ
  6. Given retired equipment / When restore / Then active 末尾へ戻る
  7. Given group 配下に active equipment が残る / When group retire / Then 422 EQUIPMENT_GROUP.HAS_ACTIVE_EQUIPMENT
  8. Given row version 不一致 / When PATCH / Then 409 EQUIPMENT.VERSION_MISMATCH
  9. Given stale collection ETag / When reorder / Then 409 EQUIPMENT.COLLECTION_ETAG_MISMATCH
  10. Given 店舗 X セッション / When 店舗 Y equipment 操作 / Then 404 or 0 件
  11. Given group A から quantity=2 のメニュー要件 / When group A の active equipment が 3 台 / Then RES-002 で 2 台が auto-assign される

  • Unit: retire guard, group retire guard, auto-assign (MST-002 連携)
  • Integration: CRUD, reorder, retire/restore, RLS
  • Cross-SRS: RES-005 equipment EXCLUDE と整合、MST-002 menu_equipment_requirement との整合

  • なし

#内容扱い
OQ-MST-005-01設備の店舗間共有(チェーン店、親 OQ-01)Phase 2+
OQ-MST-005-02メンテナンス期間の DSL(equipment_unavailable_periodPhase 2

VersionDateAuthorChange
0.12026-05-05Codex / yudai初版ドラフト(capacity 撤廃、1 台 1 行正規化)
0.22026-05-05yudai (with Codex co-design)Round 2 反映: migration 番号 0017 確定、equipment_group 導入、equipment.group_id 追加、menu_equipment_requirement から equipment_group_id + quantity で参照される構造を明示、auto-assign の責務を MST-002 §7.5 に委譲。capacity 列の不在を再確認