設備管理
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 参照)。
2. ユーザーストーリー
Section titled “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. ユースケース
Section titled “3. ユースケース”3.1 主シナリオ(設備グループ + 設備作成)
Section titled “3.1 主シナリオ(設備グループ + 設備作成)”- オペレーターが「設定 > 設備」を開く
equipment_groupを作成(例: 「シャンプー台」)- group 配下に
equipmentを必要台数分作成(例: 「シャンプー台 1」「シャンプー台 2」「シャンプー台 3」) - メニュー (MST-002) から
menu_equipment_requirement(equipment_group_id, quantity=1)で参照する
3.2 代替フロー
Section titled “3.2 代替フロー”- 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 不可
3.3 例外フロー
Section titled “3.3 例外フロー”- 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
4. UI仕様
Section titled “4. UI仕様”4.1 画面の目的
Section titled “4.1 画面の目的”設備グループ一覧 + 配下 concrete 設備を管理する。
4.2 主要要素
Section titled “4.2 主要要素”- equipment_group list
- group 配下の active equipment list
- terminated list
- create / edit dialog(group / equipment 両方)
- retire / restore button
- reorder UI
4.3 バリデーション
Section titled “4.3 バリデーション”- group
name: 1..100 文字、trim 後空文字不可 - equipment
name: 1..100 文字、trim 後空文字不可 bookable: boolean
5. API仕様
Section titled “5. API仕様”5.1 エンドポイント一覧
Section titled “5.1 エンドポイント一覧”| Method | Path | 用途 | permission | 楽観ロック |
|---|---|---|---|---|
| GET | /api/admin/equipment-groups | group 一覧 | admin:equipment:read | - |
| POST | /api/admin/equipment-groups | group 作成 | admin:equipment:create | - |
| PATCH | /api/admin/equipment-groups/:groupId | group 更新 | admin:equipment:update | If-Match: <version> |
| POST | /api/admin/equipment-groups/:groupId/retire | group retire | admin:equipment:retire | If-Match: <version> |
| POST | /api/admin/equipment-groups/:groupId/restore | group restore | admin:equipment:retire | If-Match: <version> |
| GET | /api/admin/equipment | active 一覧 (全グループ横断) | 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:update | If-Match: <version> |
| PUT | /api/admin/equipment/order | 全 active 並び順 | admin:equipment:update | If-Match: <collection_etag> |
| POST | /api/admin/equipment/:equipmentId/retire | 設備 retire | admin:equipment:retire | If-Match: <version> |
| POST | /api/admin/equipment/:equipmentId/restore | 設備 restore | admin:equipment:retire | If-Match: <version> |
5.2 エラーコード
Section titled “5.2 エラーコード”| code | 意味 | HTTP |
|---|---|---|
EQUIPMENT.NOT_FOUND | 行なし | 404 |
EQUIPMENT.VERSION_MISMATCH | 行 ETag 不一致 | 409 |
EQUIPMENT.COLLECTION_ETAG_MISMATCH | reorder ETag 不一致 | 409 |
EQUIPMENT.ACTIVE_ASSIGNMENTS_EXIST | future active assignment あり | 422 |
EQUIPMENT_GROUP.HAS_ACTIVE_EQUIPMENT | group retire 時に active equipment 残存 | 422 |
EQUIPMENT_GROUP.NOT_FOUND | group 行なし | 404 |
6. データモデル影響
Section titled “6. データモデル影響”6.1 スキーマ
Section titled “6.1 スキーマ”equipment_group(新規)
Section titled “equipment_group(新規)”| カラム | 型 | 制約 |
|---|---|---|
id | uuid | PK |
store_id | uuid | NOT NULL, FK |
name | varchar(100) | NOT NULL, CHECK btrim(name) <> '' |
display_order | integer | NOT NULL, CHECK >= 1 |
terminated_at | timestamptz | NULL |
version | integer | NOT NULL DEFAULT 1, CHECK >= 1 |
created_at | timestamptz | NOT NULL |
updated_at | timestamptz | NOT NULL |
| 制約 | UNIQUE(store_id, id) | |
| 制約 | UNIQUE(store_id, display_order) WHERE terminated_at IS NULL |
equipment(placeholder を昇格)
Section titled “equipment(placeholder を昇格)”| カラム | 型 | 制約 |
|---|---|---|
id | uuid | PK |
store_id | uuid | NOT NULL, FK |
group_id | uuid | NULL, 複合 FK → equipment_group(store_id, id) |
name | varchar(100) | NOT NULL, CHECK btrim(name) <> '' |
bookable | boolean | NOT NULL DEFAULT true |
display_order | integer | NOT NULL, CHECK >= 1 |
terminated_at | timestamptz | NULL |
version | integer | NOT NULL DEFAULT 1, CHECK >= 1 |
created_at | timestamptz | NOT NULL |
updated_at | timestamptz | NOT NULL |
| 制約 | UNIQUE(store_id, id) | |
| 制約 | UNIQUE(store_id, display_order) WHERE terminated_at IS NULL |
group_id は NULL 許容(group なしの単独設備も可、ただし menu_equipment_requirement から参照不可)。
capacity 列は 存在しない。
6.2 マイグレーション計画
Section titled “6.2 マイグレーション計画”0017_equipment_master_and_groups.sql で:
equipment_group新規作成equipmentplaceholder を本番 schema へ昇格(group_id,name,bookable,display_order,terminated_at,version追加)menu_equipment_requirementへの FK を有効化(0014で先行作成、0017で確定)- RLS ENABLE + FORCE
appロールへ GRANTadmin:equipment:*permission backfill (4 keys)
7. 業務ルール
Section titled “7. 業務ルール”7.1 1 台 1 行正規化
Section titled “7.1 1 台 1 行正規化”- 実設備 1 台 = 1 row
- 同種設備 3 台なら 3 row 作る + 共通
equipment_groupを 1 行作る - UI grouping は表示上の補助
- 予約競合判定単位は常に concrete
equipment.id(EXCLUDE 制約)
7.2 bookable と terminated_at
Section titled “7.2 bookable と terminated_at”| 状態 | terminated_at | bookable | 意味 |
|---|---|---|---|
| 通常 | NULL | true | 新規予約に使える |
| 一時停止 | NULL | false | 履歴維持、予約候補から除外 |
| 退役 | NOT NULL | 任意 | 新規予約に使えない |
7.3 retire のガード
Section titled “7.3 retire のガード”- equipment retire:
reservation_equipment_assignments.is_active=true AND ends_at > now()の future assignment がある設備は retire 不可 - group retire: 配下に active (
terminated_at IS NULL) equipment が残っていれば 422
7.4 RES-005 / MST-002 との整合
Section titled “7.4 RES-005 / MST-002 との整合”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 参照)
7.5 監査
Section titled “7.5 監査”equipment_group.create / update / retire / restoreequipment.create / update / retire / restore / reorder
8. 非機能要件
Section titled “8. 非機能要件”- group / active equipment list は full fetch
- retired list は cursor pagination
- no-op reorder は version / audit を増やさない
9. セキュリティ・認可
Section titled “9. セキュリティ・認可”| key | 用途 | 初期付与 |
|---|---|---|
admin:equipment:read | 設備・設備グループ閲覧 | 全 role |
admin:equipment:create | 設備・設備グループ作成 | owner / manager |
admin:equipment:update | 設備・設備グループ更新、reorder | owner / manager |
admin:equipment:retire | 設備・設備グループ retire / restore | owner / manager |
RLS / FORCE / 複合 FK / GRANT 明示は必須。
10. 受け入れ基準(Given-When-Then)
Section titled “10. 受け入れ基準(Given-When-Then)”- Given group + equipment 作成 / When 一覧取得 / Then group 配下に equipment が紐づく
- Given 同 group 2 台 / When 1 台が予約使用中 / Then もう 1 台は予約可能(EXCLUDE で弾かれない)
- Given
bookable=false/ When RES-002 候補取得 / Then 新規予約候補から除外される - Given future active assignment あり / When retire / Then 422
EQUIPMENT.ACTIVE_ASSIGNMENTS_EXIST - Given future active assignment なし / When retire / Then
terminated_atが立つ - Given retired equipment / When restore / Then active 末尾へ戻る
- Given group 配下に active equipment が残る / When group retire / Then 422
EQUIPMENT_GROUP.HAS_ACTIVE_EQUIPMENT - Given row version 不一致 / When PATCH / Then 409
EQUIPMENT.VERSION_MISMATCH - Given stale collection ETag / When reorder / Then 409
EQUIPMENT.COLLECTION_ETAG_MISMATCH - Given 店舗 X セッション / When 店舗 Y equipment 操作 / Then 404 or 0 件
- Given group A から quantity=2 のメニュー要件 / When group A の active equipment が 3 台 / Then RES-002 で 2 台が auto-assign される
11. テスト計画
Section titled “11. テスト計画”- 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 との整合
12. 関連ジョブ(graphile-worker)
Section titled “12. 関連ジョブ(graphile-worker)”- なし
13. Open Questions
Section titled “13. Open Questions”| # | 内容 | 扱い |
|---|---|---|
| OQ-MST-005-01 | 設備の店舗間共有(チェーン店、親 OQ-01) | Phase 2+ |
| OQ-MST-005-02 | メンテナンス期間の DSL(equipment_unavailable_period) | Phase 2 |
14. 変更履歴
Section titled “14. 変更履歴”| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-05-05 | Codex / yudai | 初版ドラフト(capacity 撤廃、1 台 1 行正規化) |
| 0.2 | 2026-05-05 | yudai (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 列の不在を再確認 |