店舗作成・初期設定
店舗作成・初期設定
Section titled “店舗作成・初期設定”Document ID: SRS-TEN-001 Parent: SRS-ROOT-001 v0.5 Version: 0.4 Status: Approved Last Updated: 2026-05-05 Depends on: なし 依存される: Phase 1 全般
本書は SRS-ROOT-001 v0.5 に従う。
店舗(Store)を作成し、マルチテナントの起点となる初期データを一括生成する。
Phase 1 では店舗作成 UI / API は持たず、seed スクリプトで作成する。
本SRSはあわせて store_settings の Phase 1 canonical fields と store.invoice_registration_number を確定する。
2. ユーザーストーリー
Section titled “2. ユーザーストーリー”- As a 開発者, I want to seed で店舗と初期ロールを一発作成したい, so that 以後の全機能を tenant 前提で開発できる
- As a 将来のサロンオーナー, I want to 店舗作成直後から最低限の設定が揃っていてほしい, so that 追加初期化なしで運用開始できる
3. ユースケース
Section titled “3. ユースケース”3.1 主シナリオ
Section titled “3.1 主シナリオ”- 開発者が
bun run db:seedを実行 - seed が環境変数を読む
packages/authの canonical permission 集合を UPSERT- 1 トランザクションで以下を作成
storestore_settings- preset role 4 種
role_permission
- 成功時に
store.id,store.slug,store.codeを出力
3.2 代替フロー
Section titled “3.2 代替フロー”- 同じ
slugで再実行した場合は既存店舗を検出してスキップ - 複数店舗は環境変数を変えて複数回実行する
3.3 例外フロー
Section titled “3.3 例外フロー”- DB 接続失敗: exit 1
slugバリデーション違反: exit 1timezoneがAsia/Tokyo以外: exit 1store.codeUNIQUE 衝突: 最大 3 回再生成
4. UI仕様
Section titled “4. UI仕様”Phase 1 ではなし。CLI のみ。
5. API仕様
Section titled “5. API仕様”Phase 1 では店舗作成 API を公開しない。
5.1 seed 入力インターフェース
Section titled “5.1 seed 入力インターフェース”| 変数名 | 必須 | 既定 | 説明 |
|---|---|---|---|
SEED_STORE_NAME | ✅ | - | 店舗名 |
SEED_STORE_SLUG | ✅ | - | URL 用 slug、グローバル UNIQUE、冪等キー |
SEED_STORE_TIMEZONE | - | Asia/Tokyo | Phase 1 はこれのみ許可 |
DATABASE_URL_MIGRATOR | ✅ | - | migrator 接続文字列 |
6. データモデル影響
Section titled “6. データモデル影響”6.1 スキーマ
Section titled “6.1 スキーマ”| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
id | uuid | PK | UUIDv7 |
slug | varchar(50) | UNIQUE, NOT NULL | 公開 URL / seed 冪等キー |
code | varchar(11) | UNIQUE, NOT NULL | Base64url 11 文字 |
name | varchar(200) | NOT NULL | 店舗名 |
timezone | varchar(50) | NOT NULL, default Asia/Tokyo | IANA TZ |
plan | varchar(20) | NOT NULL, default free | Phase 1 は free 固定 |
invoice_registration_number | varchar(14) | NULL | 適格請求書登録番号。Phase 1 は保存のみ |
created_at | timestamptz | NOT NULL | 作成時刻 |
updated_at | timestamptz | NOT NULL | 更新時刻 |
store_settings
Section titled “store_settings”| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
store_id | uuid | PK, FK | 1:1 |
reservation_slot_minutes | smallint | NOT NULL, default 30 | 予約刻み |
max_concurrent_reservations_per_staff | smallint | NOT NULL, default 1 | スタッフ並列上限 |
tentative_expire_hours | smallint | NOT NULL, default 24, CHECK 1..168 | 仮予約失効時間 |
no_show_grace_minutes | smallint | NOT NULL, default 30, CHECK 0..180 | no-show 猶予 |
customer_required_fields | jsonb | NOT NULL, default ["name"] | 顧客必須項目 |
customer_no_seq | bigint | NOT NULL, default 0, CHECK >= 0 | 顧客番号採番カウンタ |
rounding_policy | jsonb | NOT NULL, default {"method":"round","target":"line"} | 端数処理 |
cancel_policy | jsonb | NOT NULL, default {} | キャンセル方針 |
updated_at | timestamptz | NOT NULL | 更新時刻 |
permission
Section titled “permission”- 全テナント共通マスタ
store_idなし- seed が
packages/authから Phase 1 canonical 47 keys を UPSERT
owner,manager,staff,receptionistの 4 presetUNIQUE(store_id, key)UNIQUE(store_id, id)
role_permission
Section titled “role_permission”PRIMARY KEY (role_id, permission_id)store_id冗長保持(store_id, role_id)複合 FK
6.2 マイグレーション計画
Section titled “6.2 マイグレーション計画”既存の初期作成は以下で実装済み。
0001_initial.sql0002_enable_rls.sql
Phase 1 finalization では 0011_store_settings_phase1_expansion.sql を追加し、store / store_settings を拡張するとともに tax_rate master を導入する。
6.2.1 0011_store_settings_phase1_expansion.sql
Section titled “6.2.1 0011_store_settings_phase1_expansion.sql”ALTER TABLE store ADD COLUMN invoice_registration_number varchar(14) NULL;
ALTER TABLE store_settings ADD COLUMN tentative_expire_hours smallint NOT NULL DEFAULT 24, ADD COLUMN no_show_grace_minutes smallint NOT NULL DEFAULT 30, ADD COLUMN customer_no_seq bigint NOT NULL DEFAULT 0, ADD COLUMN rounding_policy jsonb NOT NULL DEFAULT '{"method":"round","target":"line"}'::jsonb;
ALTER TABLE store_settings ADD CONSTRAINT store_settings_tentative_expire_hours_chk CHECK (tentative_expire_hours BETWEEN 1 AND 168), ADD CONSTRAINT store_settings_no_show_grace_minutes_chk CHECK (no_show_grace_minutes BETWEEN 0 AND 180), ADD CONSTRAINT store_settings_customer_no_seq_nonnegative_chk CHECK (customer_no_seq >= 0);
-- tax_rate master (Phase 1 空運用)CREATE TABLE tax_rate ( id uuid PRIMARY KEY, store_id uuid NOT NULL REFERENCES store(id), kind varchar(20) NOT NULL CHECK (kind IN ('standard','reduced')), rate_pct smallint NOT NULL CHECK (rate_pct BETWEEN 0 AND 100), effective_from date NOT NULL, effective_to date NULL, created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (store_id, id));
ALTER TABLE tax_rate ENABLE ROW LEVEL SECURITY;ALTER TABLE tax_rate FORCE ROW LEVEL SECURITY;CREATE POLICY tenant_isolation_select ON tax_rate FOR SELECT USING (store_id = current_setting('app.current_store_id')::uuid);CREATE POLICY tenant_isolation_modify ON tax_rate FOR ALL USING (store_id = current_setting('app.current_store_id')::uuid) WITH CHECK (store_id = current_setting('app.current_store_id')::uuid);GRANT SELECT, INSERT, UPDATE ON tax_rate TO app;
-- 親 SRS §7.16 に従い、既存ハードコード CHECK を撤去ALTER TABLE reservation_menu_lines DROP CONSTRAINT IF EXISTS reservation_menu_lines_tax_rate_allowed;6.3 seed 実行フロー
Section titled “6.3 seed 実行フロー”- env validate
- permission UPSERT (canonical 47 keys)
slug既存判定store.code生成storeINSERTstore_settingsINSERT- preset role 4 件 INSERT
- canonical
PRESET_ROLE_PERMISSIONSに従ってrole_permissionINSERT
7. 業務ルール
Section titled “7. 業務ルール”7.1 store.code
Section titled “7.1 store.code”- 8 バイト暗号学的乱数を Base64url 11 文字へ変換
- 冪等キーには使わない(slug が冪等キー)
- UNIQUE 衝突時は再生成
7.2 permission seed
Section titled “7.2 permission seed”- seed は
packages/authが export する Phase 1 canonical permission 全件を UPSERT する - TEN-001 自身が所有する key は次の 5 件
admin:store:readadmin:store:updateadmin:store_settings:readadmin:store_settings:updateadmin:role:read
- 既存店舗への新 permission backfill は各所有 SRS の migration 責務
7.3 store_settings 既定値
Section titled “7.3 store_settings 既定値”| 設定 | 既定 |
|---|---|
reservation_slot_minutes | 30 |
max_concurrent_reservations_per_staff | 1 |
tentative_expire_hours | 24 |
no_show_grace_minutes | 30 |
customer_required_fields | ["name"] |
customer_no_seq | 0 |
rounding_policy | {"method":"round","target":"line"} |
cancel_policy | {} |
7.4 timezone
Section titled “7.4 timezone”- Phase 1 は
Asia/Tokyoのみ許可
7.5 plan
Section titled “7.5 plan”- Phase 1 は
free固定
7.6 slug
Section titled “7.6 slug”- 正規表現:
^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$
7.7 削除方針
Section titled “7.7 削除方針”store,store_settings,permission,role,role_permissionにはdeleted_atを持たせない- 店舗削除が必要になったら別 SRS で扱う
7.8 適格請求書登録番号
Section titled “7.8 適格請求書登録番号”store.invoice_registration_numberは Phase 1 で保存のみ。表示・帳票連携は Phase 2 SRS-REG-003(領収書 PDF)で扱う- 形式は
T+ 13 桁数字を想定するが、Phase 1 では DB バリデーションを緩く(varchar(14))にする
7.9 tax_rate マスタの空運用
Section titled “7.9 tax_rate マスタの空運用”tax_rateテーブルは Phase 1 では作成のみ- 行は seed しない
- 予約・会計の税率スナップショットは行から引かず、整数値直接保持
- Phase 3 で実効期間検索を有効化(SRS-ANL-001 起票時)
8. 非機能要件
Section titled “8. 非機能要件”- seed 1 回の実行時間は 1 秒前後を目標
- 同一 slug での再実行は安全であること
9. セキュリティ・認可
Section titled “9. セキュリティ・認可”9.1 Phase 1
Section titled “9.1 Phase 1”- 店舗作成は migrator / seed 実行のため、アプリ層認可対象外
- 将来 API 化する場合の system 権限は別 SRS で定義する
9.2 RLS
Section titled “9.2 RLS”storeはid = current_setting('app.current_store_id')::uuid- その他 tenant table は
store_id = ... permissionは RLS なし
9.3 監査
Section titled “9.3 監査”- seed / bootstrap / migration は親SRS §7.8.1 に従い監査対象外
9.4 GRANT 規律
Section titled “9.4 GRANT 規律”- default privileges は default-deny
- 各テーブル migration で必要権限を明示 GRANT する
10. 受け入れ基準(Given-When-Then)
Section titled “10. 受け入れ基準(Given-When-Then)”- GWT-1: seed 実行で
store1 行、store_settings1 行、preset role 4 行、canonical permission rows、対応するrole_permissionが生成される - GWT-2:
store.codeは Base64url 11 文字 - GWT-3: 同じ slug で再実行しても重複しない
- GWT-4:
SEED_STORE_TIMEZONE=Asia/Tokyo以外は失敗 - GWT-5:
tentative_expire_hours/no_show_grace_minutes/customer_no_seq/rounding_policyが既定値で入る - GWT-6:
invoice_registration_numberは NULL を許容する - GWT-7: RLS により tenant 分離が成立する
- GWT-8:
permissionは tenant 非依存で全件取得できる(47 keys) - GWT-9: bootstrap 後、新規テーブルに対して
appは blanket DML を持たない - GWT-10:
0011適用後、reservation_menu_lines.tax_rate_pct CHECK IN (8,10)が存在しないこと - GWT-11:
0011適用後、tax_rateテーブルが RLS ENABLE + FORCE で作成されていること - GWT-12:
tentative_expire_hours = 169の更新は CHECK 違反
11. テスト計画
Section titled “11. テスト計画”- Unit
generateStoreCode()- slug validator
- timezone validator
- Integration
- seed 冪等性
- RLS
- default-deny
0011適用後の既定値確認tax_rateRLS 検証
12. 関連ジョブ
Section titled “12. 関連ジョブ”Phase 1 ではなし。
13. Open Questions
Section titled “13. Open Questions”| # | 内容 | 結論 |
|---|---|---|
| OQ-TEN-001-01 | role_permission に store_id を持たせるか | Closed |
| OQ-TEN-001-02 | seed 入力形式 | Closed |
| OQ-TEN-001-03 | store_settings canonical fields の最終集合 | Closed v0.4 |
| OQ-TEN-001-04 | tax_rate を Phase 1 で空運用とするか | Closed v0.4(空運用採用) |
14. 変更履歴
Section titled “14. 変更履歴”| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-04-23 | yudai | 初版 |
| 0.2 | 2026-04-25 | yudai | slug / role_permission / default-deny / RLS 整備 |
| 0.3 | 2026-04-25 | yudai | tentative_expire_hours / no_show_grace_minutes を子SRS同期で追記 |
| 0.4 | 2026-05-05 | yudai (with Codex co-design) | Parent v0.5 同期。invoice_registration_number、customer_no_seq、rounding_policy を追加し、0011_store_settings_phase1_expansion.sql を canonical として明記。tax_rate master を Phase 1 空運用で導入。reservation_menu_lines.tax_rate_pct CHECK IN (8,10) ハードコード撤去。permission seed を Phase 1 canonical 47 集合前提に更新 |