← 成果物一覧に戻る PropCloud - 機能仕様書 > **プロダクト名**: PropCloud(プロップクラウド)
> **バージョン**: 1.0.0
> **作成日**: 2026-03-19
> **ステータス**: Phase 2 設計
> **作成者**: CodeGenAgent(源)
目次 1. [概要](#1-概要)
2. [システムアーキテクチャ](#2-システムアーキテクチャ)
3. [データモデル設計](#3-データモデル設計)
4. [API設計](#4-api設計)
5. [画面設計](#5-画面設計)
6. [ホワイトラベル設計](#6-ホワイトラベル設計)
7. [認証・認可設計](#7-認証認可設計)
8. [外部連携設計](#8-外部連携設計)
9. [バッチ処理設計](#9-バッチ処理設計)
10. [非機能要件への対応方針](#10-非機能要件への対応方針)
1. 概要 1.1 プロダクト概要 項目 内容 プロダクト名 PropCloud(プロップクラウド) コンセプト ホワイトラベル対応 賃貸管理SaaS ターゲット 小規模〜中規模不動産管理会社(管理戸数 50〜5,000戸) ビジネスモデル OEM/ホワイトラベル販売 + 自社直販 参考プロダクト いい生活賃貸管理クラウド(株式会社いい生活)
1.2 プロダクトビジョン PropCloudは、不動産管理会社の日常業務を包括的にデジタル化する賃貸管理クラウドシステムである。最大の特徴は**ホワイトラベル対応**であり、同業他社が自社ブランドとして販売・カスタマイズできるプラットフォームとして設計する。
ビジネスモデル概要 開発者(自社)
├── 直販: 自社ブランドとして販売・利用
└── OEM/ホワイトラベル販売
├── 販売パートナーA社 → A社ブランドとして販売
├── 販売パートナーB社 → B社ブランドとして販売
└── ...(最大1,000テナント) 1.3 対象ユーザー ロール 説明 利用チャネル 権限レベル プラットフォーム管理者 本システムの開発・運営元 管理画面 全テナント管理・システム設定 テナント管理者 ホワイトラベル販売パートナーの管理者 管理画面 テナント設定・ユーザー管理 管理会社スタッフ(管理者) 不動産管理会社の責任者 管理画面 全業務操作 + 承認権限 管理会社スタッフ(一般) 不動産管理会社の担当者 管理画面 担当業務操作 オーナー 物件オーナー オーナーポータル 物件情報閲覧・収支確認 入居者 賃貸入居者 入居者ポータル 契約情報確認・問い合わせ
1.4 開発フェーズ フェーズ 名称 主要機能 Phase 1(MVP) コア機能 物件管理、契約管理、入出金管理、オーナー送金、帳票(20種)、マルチテナント基盤、認証、ダッシュボード Phase 2 拡張機能 クレーム・修繕管理、督促管理、全銀連携、会計連携、帳票(100種)、オーナーポータル Phase 3 高度機能 入居者ポータル、電子契約連携、AI消込、分析・レポート、帳票(180種)、不動産ポータル連携
1.5 前提条件・制約 項目 内容 対応ブラウザ Chrome, Edge, Safari(最新2バージョン) 対応デバイス PC(メイン)、タブレット(対応)、スマートフォン(ポータルのみ) 言語 日本語(将来的に多言語対応可能な設計) 通貨 日本円(将来的に多通貨対応可能な設計) データセンター AWS 東京リージョン(ap-northeast-1) SLA 99.9%以上(月間ダウンタイム43分以内)
1.6 対応法令・規制 法令 対応内容 宅地建物取引業法 重説・契約書の法定記載事項対応 借地借家法 更新・解約に関する法的要件対応 民法(2020年改正) 連帯保証人の極度額設定対応 電子帳簿保存法 電子保存要件(タイムスタンプ、検索)対応 個人情報保護法 個人情報の適切な管理・暗号化 インボイス制度 適格請求書の発行対応 消費税法 課税/非課税の自動判定
2. システムアーキテクチャ 2.1 全体構成図 graph TB
subgraph "クライアント層"
Browser["ブラウザ<br/>(Chrome/Edge/Safari)"]
Mobile["モバイル<br/>(ポータル利用)"]
end
subgraph "CDN/エッジ層"
CF["CloudFront<br/>(CDN)"]
R53["Route 53<br/>(DNS)"]
ACM["ACM<br/>(SSL証明書)"]
end
subgraph "アプリケーション層"
subgraph "ECS Fargate Cluster"
NextJS_1["Next.js App<br/>Container #1"]
NextJS_2["Next.js App<br/>Container #2"]
NextJS_N["Next.js App<br/>Container #N"]
end
ALB["Application<br/>Load Balancer"]
end
subgraph "API層"
TRPC["tRPC Router"]
APIRoutes["Next.js API Routes"]
Middleware["Middleware<br/>(Auth/Tenant/Rate Limit)"]
end
subgraph "ワーカー層"
subgraph "ECS Fargate Workers"
Worker1["バッチワーカー<br/>(請求生成/消込)"]
Worker2["通知ワーカー<br/>(メール/SMS)"]
Worker3["帳票ワーカー<br/>(PDF生成)"]
end
BullMQ["BullMQ<br/>(ジョブキュー)"]
end
subgraph "データ層"
subgraph "Primary DB"
RDS_Primary["PostgreSQL<br/>RDS Primary<br/>(Multi-AZ)"]
RDS_Replica["PostgreSQL<br/>RDS Read Replica"]
end
Redis["Redis<br/>(ElastiCache)<br/>セッション/キャッシュ"]
S3["S3<br/>ファイルストレージ"]
end
subgraph "外部サービス"
SES["Amazon SES<br/>(メール送信)"]
KMS["AWS KMS<br/>(暗号化)"]
SM["Secrets Manager<br/>(シークレット)"]
SSM["SSM Parameter Store<br/>(設定値)"]
end
subgraph "監視・ログ"
CW["CloudWatch<br/>(メトリクス/ログ)"]
Sentry["Sentry<br/>(エラー追跡)"]
end
Browser --> CF
Mobile --> CF
R53 --> CF
ACM --> CF
CF --> ALB
ALB --> NextJS_1
ALB --> NextJS_2
ALB --> NextJS_N
NextJS_1 --> TRPC
NextJS_1 --> APIRoutes
TRPC --> Middleware
APIRoutes --> Middleware
Middleware --> RDS_Primary
Middleware --> RDS_Replica
Middleware --> Redis
Middleware --> S3
BullMQ --> Redis
Worker1 --> RDS_Primary
Worker2 --> SES
Worker3 --> S3
BullMQ --> Worker1
BullMQ --> Worker2
BullMQ --> Worker3
RDS_Primary --> KMS
NextJS_1 --> SM
NextJS_1 --> SSM
NextJS_1 --> CW
NextJS_1 --> Sentry 2.2 技術スタック詳細 2.2.1 フロントエンド 項目 技術 バージョン 選定理由 フレームワーク Next.js (App Router) 15.x SSR/SSG対応、API Routes統合、ファイルベースルーティング 言語 TypeScript 5.x 型安全性、大規模開発での保守性 UIライブラリ shadcn/ui latest カスタマイズ性、アクセシビリティ、Radix UIベース スタイリング Tailwind CSS 4.x ユーティリティファースト、テーマカスタマイズ容易 状態管理(クライアント) Zustand 5.x シンプルAPI、TypeScript親和性、軽量 状態管理(サーバー) TanStack Query 5.x キャッシュ管理、楽観的更新、自動再取得 フォーム React Hook Form + Zod 7.x / 3.x パフォーマンス、バリデーション統合 テーブル TanStack Table 8.x ヘッドレスUI、大量データ対応、仮想スクロール チャート Recharts 2.x React統合、レスポンシブ、カスタマイズ性 アニメーション motion/react latest 宣言的API、パフォーマンス、200ms制限準拠 アイコン Lucide React latest 統一デザイン、Tree-shaking対応 i18n next-intl latest Next.js App Router対応、型安全 日付 date-fns 3.x Tree-shaking対応、和暦対応
2.2.2 バックエンド 項目 技術 バージョン 選定理由 API tRPC 11.x End-to-End型安全、自動型推論、バリデーション統合 API(外部向け) Next.js API Routes - Webhook受信、ファイルアップロード等 ORM Prisma 6.x マイグレーション管理、型生成、RLS対応 DB PostgreSQL 16.x RLS、JSONB、全文検索、信頼性 キャッシュ Redis 7.x セッション、キャッシュ、ジョブキュー基盤 ジョブキュー BullMQ 5.x Redis基盤、優先度付きキュー、再試行制御 ファイルストレージ AWS S3 - 署名付きURL、ライフサイクル管理 メール送信 Amazon SES - 高配信率、テナント別From対応 PDF生成 React PDF (@react-pdf/renderer) 4.x React記法でPDF生成、日本語対応 バリデーション Zod 3.x フロント・バック共通スキーマ
2.2.3 インフラストラクチャ 項目 技術 選定理由 コンテナ実行 ECS Fargate サーバーレスコンテナ、Auto Scaling CDN CloudFront 静的アセット配信、カスタムドメイン対応 DNS Route 53 テナント別カスタムドメイン管理 SSL ACM 自動証明書管理、ワイルドカード証明書 IaC AWS CDK TypeScriptでインフラ定義、再利用性 CI/CD GitHub Actions + CodePipeline ブルーグリーンデプロイ、自動テスト シークレット Secrets Manager + SSM ローテーション対応、テナント別設定 ログ CloudWatch Logs 集約ログ管理、長期保存(S3連携)
2.3 マルチテナント設計 2.3.1 テナント分離方式 PropCloudは**共有データベース + Row Level Security(RLS)**方式を採用する。
graph TB
subgraph "アプリケーション層"
App["Next.js App"]
MW["テナント解決<br/>Middleware"]
end
subgraph "テナント解決"
Domain["カスタムドメイン<br/>tenant-a.example.com"]
Subdomain["サブドメイン<br/>tenant-a.propcloud.jp"]
Header["ヘッダー<br/>X-Tenant-ID"]
end
subgraph "データベース層"
subgraph "PostgreSQL(共有DB)"
RLS["Row Level Security"]
T1["tenant_id = 'tenant-a'<br/>のデータのみアクセス可能"]
T2["tenant_id = 'tenant-b'<br/>のデータのみアクセス可能"]
end
end
subgraph "ストレージ層"
S3_A["S3<br/>tenants/tenant-a/*"]
S3_B["S3<br/>tenants/tenant-b/*"]
end
Domain --> MW
Subdomain --> MW
Header --> MW
App --> MW
MW -->|"SET app.current_tenant_id"| RLS
RLS --> T1
RLS --> T2
MW -->|"prefix: tenants/tenant-a/"| S3_A
MW -->|"prefix: tenants/tenant-b/"| S3_B 2.3.2 テナント解決フロー 1. リクエスト受信
2. Middleware でテナント解決
- カスタムドメイン → domains テーブルから tenant_id 特定
- サブドメイン → サブドメインから tenant_id 特定
- ヘッダー → X-Tenant-ID から tenant_id 特定(API用)
3. PostgreSQL セッション変数に tenant_id をセット
- SET app.current_tenant_id = 'tenant-xxx'
4. RLS ポリシーが自動的にテナントデータをフィルタ
5. 以降の全クエリはテナント分離される 2.3.3 RLS ポリシー定義 -- テナント分離の基本ポリシー
ALTER TABLE buildings ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON buildings
USING (organization_id = current_setting('app.current_tenant_id')::uuid);
-- 全テーブルに同様のポリシーを適用
-- organizations テーブルのみ、プラットフォーム管理者は全テナント参照可能 2.3.4 テナント別リソース分離 リソース 分離方式 詳細 データベース RLS(論理分離) organization_id カラムによるフィルタ ファイルストレージ S3プレフィックス分離 `tenants/{tenant_id}/` プレフィックス キャッシュ Redisプレフィックス `tenant:{tenant_id}:` プレフィックス セッション テナントIDバインド セッションにテナントID紐付け ジョブキュー テナントIDタグ ジョブにテナントID付与、優先度制御 ログ テナントIDフィールド 全ログにテナントIDを含む構造化ログ
2.4 ディレクトリ構造 src/
├── app/ # Next.js App Router
│ ├── (auth)/ # 認証関連ページ
│ │ ├── login/
│ │ ├── register/
│ │ └── forgot-password/
│ ├── (dashboard)/ # 管理画面レイアウト
│ │ ├── layout.tsx
│ │ ├── page.tsx # ダッシュボード
│ │ ├── buildings/ # 物件管理
│ │ ├── units/ # 区画管理
│ │ ├── contracts/ # 契約管理
│ │ ├── payments/ # 入出金管理
│ │ ├── owners/ # オーナー管理
│ │ ├── tenants/ # 入居者管理
│ │ ├── maintenance/ # 修繕管理
│ │ ├── documents/ # 帳票・ドキュメント
│ │ ├── reports/ # レポート・分析
│ │ └── settings/ # 設定
│ ├── (portal)/ # ポータルレイアウト
│ │ ├── owner/ # オーナーポータル
│ │ └── resident/ # 入居者ポータル
│ ├── (platform)/ # プラットフォーム管理
│ │ ├── organizations/ # テナント管理
│ │ └── system/ # システム設定
│ └── api/ # API Routes
│ ├── trpc/ # tRPC ハンドラ
│ ├── webhooks/ # Webhook受信
│ └── files/ # ファイルアップロード
├── server/ # サーバーサイドロジック
│ ├── trpc/ # tRPC定義
│ │ ├── router.ts # ルートルーター
│ │ ├── context.ts # コンテキスト(テナント解決含む)
│ │ └── routers/ # 各ドメインルーター
│ │ ├── building.ts
│ │ ├── unit.ts
│ │ ├── contract.ts
│ │ ├── payment.ts
│ │ ├── owner.ts
│ │ ├── tenant.ts
│ │ ├── maintenance.ts
│ │ ├── document.ts
│ │ ├── report.ts
│ │ ├── user.ts
│ │ └── organization.ts
│ ├── services/ # ビジネスロジック
│ │ ├── billing.service.ts
│ │ ├── reconciliation.service.ts
│ │ ├── remittance.service.ts
│ │ ├── dunning.service.ts
│ │ └── ...
│ ├── jobs/ # バッチジョブ
│ │ ├── monthly-billing.job.ts
│ │ ├── reconciliation.job.ts
│ │ ├── owner-remittance.job.ts
│ │ └── ...
│ └── integrations/ # 外部連携
│ ├── zengin/ # 全銀フォーマット
│ ├── e-contract/ # 電子契約
│ ├── accounting/ # 会計ソフト
│ └── notification/ # 通知
├── components/ # 共通コンポーネント
│ ├── ui/ # shadcn/ui ベースコンポーネント
│ ├── layout/ # レイアウトコンポーネント
│ ├── forms/ # フォームコンポーネント
│ ├── tables/ # テーブルコンポーネント
│ └── charts/ # チャートコンポーネント
├── lib/ # ユーティリティ
│ ├── prisma.ts # Prismaクライアント
│ ├── redis.ts # Redisクライアント
│ ├── s3.ts # S3クライアント
│ ├── auth.ts # 認証ヘルパー
│ ├── tenant.ts # テナント解決
│ ├── utils.ts # 汎用ユーティリティ
│ └── validators/ # Zodスキーマ
├── hooks/ # カスタムフック
├── stores/ # Zustandストア
├── types/ # 型定義
└── styles/ # グローバルスタイル
├── globals.css
└── themes/ # テナントテーマ 2.5 通信フロー sequenceDiagram
participant Browser as ブラウザ
participant CF as CloudFront
participant ALB as ALB
participant Next as Next.js
participant TRPC as tRPC
participant MW as Middleware
participant DB as PostgreSQL
participant Cache as Redis
Browser->>CF: HTTPS リクエスト
CF->>ALB: リクエスト転送
ALB->>Next: リクエスト転送
Note over Next: テナント解決
Next->>MW: テナントID解決
MW->>DB: SET app.current_tenant_id
alt SSR(ページ初期表示)
Next->>TRPC: サーバーサイドデータ取得
TRPC->>Cache: キャッシュ確認
alt キャッシュヒット
Cache-->>TRPC: キャッシュデータ
else キャッシュミス
TRPC->>DB: クエリ実行(RLS適用)
DB-->>TRPC: データ返却
TRPC->>Cache: キャッシュ保存
end
TRPC-->>Next: データ返却
Next-->>Browser: HTML + ハイドレーション
else クライアントサイドAPI呼び出し
Browser->>Next: tRPC クエリ/ミューテーション
Next->>TRPC: API処理
TRPC->>DB: クエリ実行(RLS適用)
DB-->>TRPC: データ返却
TRPC-->>Browser: JSON レスポンス
end 3. データモデル設計 3.1 ER図(全体) erDiagram
Organization ||--o{ Building : "所有"
Organization ||--o{ User : "所属"
Organization ||--o{ OrganizationSetting : "設定"
Organization ||--o{ CustomDomain : "ドメイン"
Organization ||--o{ DocumentTemplate : "帳票テンプレート"
Organization ||--o{ Plan : "契約プラン"
Building ||--o{ Unit : "含む"
Building ||--o{ ParkingSpace : "含む"
Building }o--|| Owner : "所有者"
Building ||--o{ BuildingImage : "画像"
Unit ||--o{ Contract : "契約"
Unit ||--o{ MaintenanceTicket : "修繕"
Owner ||--o{ Building : "所有"
Owner ||--o{ OwnerBankAccount : "口座"
Owner ||--o{ Remittance : "送金"
Owner ||--o{ CommunicationLog : "連絡履歴"
Contract ||--|| Unit : "対象区画"
Contract }o--|| Tenant : "契約者"
Contract ||--o{ ContractGuarantor : "保証人"
Contract ||--o{ Payment : "入出金"
Contract ||--o{ Invoice : "請求"
Tenant ||--o{ Contract : "契約"
Tenant ||--o{ Cohabitant : "同居人"
Tenant ||--o{ Vehicle : "車両"
Tenant ||--o{ MaintenanceTicket : "問合せ"
Invoice ||--o{ InvoiceItem : "明細"
Invoice ||--o{ Payment : "入金"
Payment ||--o{ PaymentAllocation : "消込"
MaintenanceTicket ||--o{ MaintenanceComment : "コメント"
MaintenanceTicket ||--o{ MaintenanceEstimate : "見積"
MaintenanceTicket }o--|| Vendor : "業者"
Vendor ||--o{ MaintenanceTicket : "担当"
User ||--o{ AuditLog : "操作ログ"
User }o--|| Role : "ロール"
Remittance ||--o{ RemittanceItem : "明細" 3.2 ER図(コアドメイン詳細) erDiagram
Organization {
uuid id PK
string name
string slug
string plan_id FK
string logo_url
string favicon_url
json theme_config
json feature_flags
json terminology_overrides
string email_from_address
string email_from_name
int storage_limit_mb
int storage_used_mb
enum status "active/suspended/trial"
timestamp trial_ends_at
timestamp created_at
timestamp updated_at
}
Building {
uuid id PK
uuid organization_id FK
uuid owner_id FK
string name
string postal_code
string prefecture
string city
string address1
string address2
decimal latitude
decimal longitude
enum structure "RC/SRC/S/W/LGS/other"
date construction_date
int total_units
int floors_above
int floors_below
json shared_facilities
json nearest_stations
enum management_type "self/collection_agency/sublease/exclusive"
decimal management_fee_rate
text notes
timestamp created_at
timestamp updated_at
}
Unit {
uuid id PK
uuid building_id FK
uuid organization_id FK
string unit_number
int floor
string layout
decimal area_sqm
enum direction "N/NE/E/SE/S/SW/W/NW"
decimal rent
decimal management_fee
decimal common_fee
decimal deposit_months
decimal key_money_months
json facilities
enum status "vacant/occupied/leaving/recruiting/suspended/renovating"
date available_from
json recruitment_conditions
text notes
timestamp created_at
timestamp updated_at
}
Contract {
uuid id PK
uuid unit_id FK
uuid tenant_id FK
uuid organization_id FK
enum contract_type "new/renewal/sublease"
enum contractor_type "individual/corporation"
date start_date
date end_date
decimal rent
decimal management_fee
decimal common_fee
decimal deposit
decimal key_money
decimal renewal_fee_months
json special_terms
enum status "draft/active/renewed/terminated/cancelled"
uuid guarantee_company_id FK
date renewal_notice_date
text notes
timestamp created_at
timestamp updated_at
}
Tenant {
uuid id PK
uuid organization_id FK
string last_name
string first_name
string last_name_kana
string first_name_kana
string email
string phone
string mobile_phone
string postal_code
string address
string workplace_name
string workplace_phone
string emergency_contact_name
string emergency_contact_phone
string emergency_contact_relation
date date_of_birth
enum gender "male/female/other/unspecified"
uuid user_id FK
text notes
timestamp created_at
timestamp updated_at
}
Owner {
uuid id PK
uuid organization_id FK
enum owner_type "individual/corporation"
string name
string name_kana
string representative_name
string email
string phone
string postal_code
string address
enum tax_category "individual/corporation/non_resident"
string tax_id
uuid user_id FK
text notes
timestamp created_at
timestamp updated_at
}
Payment {
uuid id PK
uuid organization_id FK
uuid contract_id FK
uuid invoice_id FK
enum payment_type "income/expense"
decimal amount
date payment_date
enum method "bank_transfer/direct_debit/cash/credit_card/other"
string payer_name
string bank_name
string bank_branch
string account_number
enum status "pending/completed/partial/overpaid/refunded"
enum reconciliation_status "unmatched/auto_matched/manual_matched/confirmed"
uuid matched_by FK
text notes
timestamp created_at
timestamp updated_at
}
Invoice {
uuid id PK
uuid organization_id FK
uuid contract_id FK
uuid tenant_id FK
string invoice_number
date billing_date
date due_date
decimal total_amount
decimal tax_amount
decimal paid_amount
enum status "draft/issued/sent/partial_paid/paid/overdue/cancelled"
date sent_at
enum send_method "email/mail/portal"
text notes
timestamp created_at
timestamp updated_at
}
MaintenanceTicket {
uuid id PK
uuid organization_id FK
uuid unit_id FK
uuid tenant_id FK
uuid assigned_to FK
uuid vendor_id FK
string ticket_number
string title
text description
enum category "equipment/noise/leak/pest/common_area/other"
enum priority "low/medium/high/urgent"
enum status "received/in_progress/waiting_estimate/waiting_approval/ordered/completed/closed"
enum cost_bearer "owner/tenant/management_company"
decimal estimated_cost
decimal actual_cost
date scheduled_date
date completed_date
text resolution
timestamp created_at
timestamp updated_at
}
User {
uuid id PK
uuid organization_id FK
uuid role_id FK
string email
string password_hash
string first_name
string last_name
string phone
boolean is_active
boolean totp_enabled
string totp_secret
int failed_login_count
timestamp locked_until
timestamp last_login_at
timestamp password_changed_at
timestamp created_at
timestamp updated_at
} 3.3 テーブル定義詳細 3.3.1 Organization(テナント/組織) PropCloudのマルチテナント基盤の中核テーブル。各販売パートナーおよび管理会社を表す。
カラム名 型 NULL 説明 id UUID NO プライマリキー name VARCHAR(255) NO 組織名 slug VARCHAR(100) NO URLスラグ(サブドメイン用、UNIQUE) plan_id UUID YES 契約プランID logo_url VARCHAR(512) YES ロゴ画像URL favicon_url VARCHAR(512) YES ファビコンURL theme_config JSONB NO テーマ設定(カラー等)、デフォルト: `{}` feature_flags JSONB NO 機能ON/OFF設定、デフォルト: `{}` terminology_overrides JSONB NO 用語カスタマイズ、デフォルト: `{}` email_from_address VARCHAR(255) YES メール送信元アドレス email_from_name VARCHAR(255) YES メール送信元名 storage_limit_mb INTEGER NO ストレージ上限(MB)、デフォルト: 5120 storage_used_mb INTEGER NO ストレージ使用量(MB)、デフォルト: 0 status ENUM NO ステータス: active / suspended / trial trial_ends_at TIMESTAMP YES トライアル終了日時 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_organization_slug` (slug) UNIQUE
• `idx_organization_status` (status)
3.3.2 CustomDomain(カスタムドメイン) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK) domain VARCHAR(255) NO カスタムドメイン(UNIQUE) ssl_status ENUM NO SSL状態: pending / active / failed verified_at TIMESTAMP YES ドメイン認証完了日時 created_at TIMESTAMP NO 作成日時
**インデックス**:
• `idx_custom_domain_domain` (domain) UNIQUE
3.3.3 Building(建物) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) owner_id UUID YES オーナーID(FK) name VARCHAR(255) NO 建物名 postal_code VARCHAR(10) NO 郵便番号 prefecture VARCHAR(10) NO 都道府県 city VARCHAR(100) NO 市区町村 address1 VARCHAR(255) NO 住所1(番地まで) address2 VARCHAR(255) YES 住所2(建物名等) latitude DECIMAL(10,7) YES 緯度 longitude DECIMAL(10,7) YES 経度 structure ENUM NO 構造: RC / SRC / S / W / LGS / other construction_date DATE YES 築年月 total_units INTEGER NO 総戸数 floors_above INTEGER YES 地上階数 floors_below INTEGER YES 地下階数 shared_facilities JSONB NO 共有設備、デフォルト: `[]` nearest_stations JSONB NO 最寄り駅情報、デフォルト: `[]` management_type ENUM NO 管理形態: self / collection_agency / sublease / exclusive management_fee_rate DECIMAL(5,2) YES 管理手数料率(%) notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_building_org_id` (organization_id)
• `idx_building_owner_id` (owner_id)
• `idx_building_prefecture_city` (prefecture, city)
• `idx_building_name_search` GIN (name gin_trgm_ops) -- トリグラム全文検索
**JSONBスキーマ(shared_facilities)**:
[
{ "name": "オートロック", "available": true },
{ "name": "宅配ボックス", "available": true },
{ "name": "インターネット", "available": true, "type": "光回線" }
] **JSONBスキーマ(nearest_stations)**:
[
{
"line": "JR山手線",
"station": "渋谷",
"walk_minutes": 5,
"bus_minutes": null
}
] 3.3.4 Unit(区画/部屋) カラム名 型 NULL 説明 id UUID NO プライマリキー building_id UUID NO 建物ID(FK) organization_id UUID NO 組織ID(FK、RLS対象) unit_number VARCHAR(20) NO 部屋番号 floor INTEGER YES 階数 layout VARCHAR(20) YES 間取り(1K, 2LDK等) area_sqm DECIMAL(8,2) YES 面積(平米) direction ENUM YES 方位: N / NE / E / SE / S / SW / W / NW rent DECIMAL(12,0) NO 賃料(円) management_fee DECIMAL(12,0) NO 管理費(円)、デフォルト: 0 common_fee DECIMAL(12,0) NO 共益費(円)、デフォルト: 0 deposit_months DECIMAL(4,1) NO 敷金(ヶ月)、デフォルト: 0 key_money_months DECIMAL(4,1) NO 礼金(ヶ月)、デフォルト: 0 facilities JSONB NO 設備情報、デフォルト: `[]` status ENUM NO ステータス: vacant / occupied / leaving / recruiting / suspended / renovating available_from DATE YES 入居可能日 recruitment_conditions JSONB NO 募集条件、デフォルト: `{}` notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_unit_building_id` (building_id)
• `idx_unit_org_id` (organization_id)
• `idx_unit_status` (status)
• `idx_unit_building_number` (building_id, unit_number) UNIQUE
3.3.5 Contract(契約) カラム名 型 NULL 説明 id UUID NO プライマリキー unit_id UUID NO 区画ID(FK) tenant_id UUID NO 入居者ID(FK) organization_id UUID NO 組織ID(FK、RLS対象) contract_number VARCHAR(50) NO 契約番号(UNIQUE per org) contract_type ENUM NO 契約種別: new / renewal / sublease contractor_type ENUM NO 契約者区分: individual / corporation start_date DATE NO 契約開始日 end_date DATE NO 契約終了日 rent DECIMAL(12,0) NO 契約賃料 management_fee DECIMAL(12,0) NO 契約管理費 common_fee DECIMAL(12,0) NO 契約共益費 deposit DECIMAL(12,0) NO 敷金 key_money DECIMAL(12,0) NO 礼金 renewal_fee_months DECIMAL(4,1) YES 更新料(ヶ月) guarantee_company_id UUID YES 保証会社ID(FK) special_terms JSONB NO 特約事項、デフォルト: `[]` status ENUM NO ステータス: draft / active / renewed / terminated / cancelled renewal_notice_date DATE YES 更新案内送付予定日 termination_date DATE YES 解約日 termination_reason TEXT YES 解約理由 move_out_date DATE YES 退去日 notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_contract_unit_id` (unit_id)
• `idx_contract_tenant_id` (tenant_id)
• `idx_contract_org_id` (organization_id)
• `idx_contract_status` (status)
• `idx_contract_end_date` (end_date)
• `idx_contract_number_org` (organization_id, contract_number) UNIQUE
3.3.6 Owner(オーナー) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) owner_type ENUM NO 区分: individual / corporation name VARCHAR(255) NO 氏名/法人名 name_kana VARCHAR(255) YES フリガナ representative_name VARCHAR(255) YES 代表者名(法人の場合) email VARCHAR(255) YES メールアドレス phone VARCHAR(20) YES 電話番号 mobile_phone VARCHAR(20) YES 携帯電話番号 postal_code VARCHAR(10) YES 郵便番号 address TEXT YES 住所 tax_category ENUM NO 源泉徴収区分: individual / corporation / non_resident tax_id VARCHAR(20) YES 適格請求書番号/法人番号 user_id UUID YES ポータルユーザーID(FK) notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_owner_org_id` (organization_id)
• `idx_owner_name_search` GIN (name gin_trgm_ops)
3.3.7 Tenant(入居者) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) last_name VARCHAR(100) NO 姓 first_name VARCHAR(100) NO 名 last_name_kana VARCHAR(100) YES 姓(カナ) first_name_kana VARCHAR(100) YES 名(カナ) email VARCHAR(255) YES メールアドレス phone VARCHAR(20) YES 電話番号 mobile_phone VARCHAR(20) YES 携帯電話番号 postal_code VARCHAR(10) YES 郵便番号 address TEXT YES 住所(入居前住所) workplace_name VARCHAR(255) YES 勤務先名 workplace_phone VARCHAR(20) YES 勤務先電話番号 emergency_contact_name VARCHAR(255) YES 緊急連絡先氏名 emergency_contact_phone VARCHAR(20) YES 緊急連絡先電話番号 emergency_contact_relation VARCHAR(50) YES 緊急連絡先続柄 date_of_birth DATE YES 生年月日 gender ENUM YES 性別: male / female / other / unspecified user_id UUID YES ポータルユーザーID(FK) notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_tenant_org_id` (organization_id)
• `idx_tenant_name_search` GIN ((last_name || first_name) gin_trgm_ops)
• `idx_tenant_email` (organization_id, email)
3.3.8 Invoice(請求) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) contract_id UUID NO 契約ID(FK) tenant_id UUID NO 入居者ID(FK) invoice_number VARCHAR(50) NO 請求番号 billing_period_start DATE NO 請求期間開始日 billing_period_end DATE NO 請求期間終了日 billing_date DATE NO 請求日 due_date DATE NO 支払期日 subtotal_amount DECIMAL(12,0) NO 小計 tax_amount DECIMAL(12,0) NO 消費税額 total_amount DECIMAL(12,0) NO 合計金額 paid_amount DECIMAL(12,0) NO 入金済み金額、デフォルト: 0 status ENUM NO ステータス: draft / issued / sent / partial_paid / paid / overdue / cancelled sent_at TIMESTAMP YES 送付日時 send_method ENUM YES 送付方法: email / mail / portal is_invoice_qualified BOOLEAN NO 適格請求書フラグ、デフォルト: false notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_invoice_org_id` (organization_id)
• `idx_invoice_contract_id` (contract_id)
• `idx_invoice_status` (status)
• `idx_invoice_due_date` (due_date)
• `idx_invoice_number_org` (organization_id, invoice_number) UNIQUE
3.3.9 InvoiceItem(請求明細) カラム名 型 NULL 説明 id UUID NO プライマリキー invoice_id UUID NO 請求ID(FK) organization_id UUID NO 組織ID(FK、RLS対象) item_type ENUM NO 明細種別: rent / management_fee / common_fee / parking / utility / renewal_fee / other description VARCHAR(255) NO 項目名 quantity DECIMAL(8,2) NO 数量、デフォルト: 1 unit_price DECIMAL(12,0) NO 単価 amount DECIMAL(12,0) NO 金額 tax_rate DECIMAL(5,2) NO 消費税率、デフォルト: 10.00 tax_amount DECIMAL(12,0) NO 消費税額 is_taxable BOOLEAN NO 課税対象フラグ sort_order INTEGER NO 表示順 created_at TIMESTAMP NO 作成日時
**インデックス**:
• `idx_invoice_item_invoice_id` (invoice_id)
• `idx_invoice_item_org_id` (organization_id)
3.3.10 Payment(入出金) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) contract_id UUID YES 契約ID(FK) invoice_id UUID YES 請求ID(FK) payment_type ENUM NO 入出金区分: income / expense amount DECIMAL(12,0) NO 金額 payment_date DATE NO 入出金日 method ENUM NO 方法: bank_transfer / direct_debit / cash / credit_card / other payer_name VARCHAR(255) YES 入金者名(銀行振込名義) bank_name VARCHAR(100) YES 銀行名 bank_branch VARCHAR(100) YES 支店名 account_number VARCHAR(20) YES 口座番号 status ENUM NO ステータス: pending / completed / partial / overpaid / refunded reconciliation_status ENUM NO 消込ステータス: unmatched / auto_matched / manual_matched / confirmed matched_by UUID YES 消込実行者ID(FK) matched_at TIMESTAMP YES 消込日時 excess_amount DECIMAL(12,0) NO 過入金額(預かり金)、デフォルト: 0 shortage_amount DECIMAL(12,0) NO 不足金額、デフォルト: 0 notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_payment_org_id` (organization_id)
• `idx_payment_contract_id` (contract_id)
• `idx_payment_invoice_id` (invoice_id)
• `idx_payment_date` (payment_date)
• `idx_payment_reconciliation` (reconciliation_status)
• `idx_payment_payer_search` GIN (payer_name gin_trgm_ops)
3.3.11 MaintenanceTicket(修繕チケット) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) unit_id UUID YES 区画ID(FK) building_id UUID YES 建物ID(共用部の場合) tenant_id UUID YES 申告入居者ID(FK) assigned_to UUID YES 担当者ID(FK) vendor_id UUID YES 業者ID(FK) ticket_number VARCHAR(50) NO チケット番号 title VARCHAR(255) NO タイトル description TEXT NO 詳細説明 category ENUM NO カテゴリ: equipment / noise / leak / pest / common_area / other priority ENUM NO 優先度: low / medium / high / urgent status ENUM NO ステータス: received / in_progress / waiting_estimate / waiting_approval / ordered / completed / closed cost_bearer ENUM YES 費用負担者: owner / tenant / management_company estimated_cost DECIMAL(12,0) YES 見積金額 actual_cost DECIMAL(12,0) YES 実費 scheduled_date DATE YES 作業予定日 completed_date DATE YES 完了日 resolution TEXT YES 対応内容 source ENUM NO 受付経路: phone / email / portal / chat / other created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_maintenance_org_id` (organization_id)
• `idx_maintenance_unit_id` (unit_id)
• `idx_maintenance_status` (status)
• `idx_maintenance_priority` (priority)
• `idx_maintenance_assigned_to` (assigned_to)
• `idx_maintenance_ticket_number_org` (organization_id, ticket_number) UNIQUE
3.3.12 Remittance(オーナー送金) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) owner_id UUID NO オーナーID(FK) remittance_number VARCHAR(50) NO 送金番号 target_month DATE NO 対象年月(月初日) gross_income DECIMAL(12,0) NO 総収入 management_fee DECIMAL(12,0) NO 管理手数料 maintenance_cost DECIMAL(12,0) NO 修繕費 other_deductions DECIMAL(12,0) NO その他控除 withholding_tax DECIMAL(12,0) NO 源泉徴収税額 net_remittance DECIMAL(12,0) NO 送金額(手取り) status ENUM NO ステータス: draft / confirmed / sent / completed transfer_date DATE YES 振込予定日 transferred_at TIMESTAMP YES 振込実行日時 notes TEXT YES 備考 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_remittance_org_id` (organization_id)
• `idx_remittance_owner_id` (owner_id)
• `idx_remittance_month` (target_month)
• `idx_remittance_status` (status)
3.3.13 User(ユーザー) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID(FK、RLS対象) role_id UUID NO ロールID(FK) email VARCHAR(255) NO メールアドレス password_hash VARCHAR(255) NO パスワードハッシュ(bcrypt) first_name VARCHAR(100) NO 名 last_name VARCHAR(100) NO 姓 phone VARCHAR(20) YES 電話番号 avatar_url VARCHAR(512) YES アバター画像URL is_active BOOLEAN NO 有効フラグ、デフォルト: true totp_enabled BOOLEAN NO TOTP有効フラグ、デフォルト: false totp_secret VARCHAR(255) YES TOTPシークレット(暗号化保存) failed_login_count INTEGER NO ログイン失敗回数、デフォルト: 0 locked_until TIMESTAMP YES アカウントロック解除日時 last_login_at TIMESTAMP YES 最終ログイン日時 password_changed_at TIMESTAMP YES パスワード変更日時 created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
**インデックス**:
• `idx_user_org_id` (organization_id)
• `idx_user_email` (email) UNIQUE
• `idx_user_role_id` (role_id)
3.3.14 Role(ロール) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID YES 組織ID(NULL=システムロール) name VARCHAR(100) NO ロール名 slug VARCHAR(100) NO ロールスラグ description TEXT YES 説明 permissions JSONB NO 権限リスト is_system BOOLEAN NO システムロールフラグ、デフォルト: false created_at TIMESTAMP NO 作成日時 updated_at TIMESTAMP NO 更新日時
3.3.15 AuditLog(監査ログ) カラム名 型 NULL 説明 id UUID NO プライマリキー organization_id UUID NO 組織ID user_id UUID NO 操作ユーザーID action VARCHAR(50) NO アクション: create / update / delete / login / logout / export resource_type VARCHAR(100) NO リソース種別: building / contract / payment 等 resource_id UUID YES リソースID changes JSONB YES 変更内容(before/after) ip_address INET YES IPアドレス user_agent TEXT YES User-Agent created_at TIMESTAMP NO 作成日時
**インデックス**:
• `idx_audit_log_org_id` (organization_id)
• `idx_audit_log_user_id` (user_id)
• `idx_audit_log_action` (action)
• `idx_audit_log_resource` (resource_type, resource_id)
• `idx_audit_log_created_at` (created_at)
**パーティショニング**: created_at による月次パーティション
3.3.16 その他テーブル一覧 テーブル名 説明 主要カラム OwnerBankAccount オーナー振込先口座 owner_id, bank_code, branch_code, account_type, account_number, account_name ContractGuarantor 連帯保証人 contract_id, name, phone, address, relation, max_guarantee_amount GuaranteeCompany 保証会社マスタ name, contact_info, api_endpoint Cohabitant 同居人 tenant_id, name, relation, date_of_birth Vehicle 車両 tenant_id, plate_number, vehicle_type, color ParkingSpace 駐車場区画 building_id, space_number, monthly_fee, status Vendor 業者マスタ name, category, phone, email, address MaintenanceComment 修繕コメント ticket_id, user_id, content, attachments MaintenanceEstimate 修繕見積 ticket_id, vendor_id, amount, description, status MaintenanceImage 修繕画像 ticket_id, image_url, uploaded_by BuildingImage 建物画像 building_id, image_url, image_type, sort_order KeyManagement 鍵管理 unit_id, key_type, location, lent_to, lent_at, due_date DocumentTemplate 帳票テンプレート organization_id, name, category, template_data, version GeneratedDocument 生成済み帳票 template_id, resource_type, resource_id, file_url RemittanceItem 送金明細 remittance_id, building_id, unit_id, item_type, amount DunningRecord 督促記録 contract_id, dunning_level, sent_at, method Plan 料金プラン name, max_units, monthly_price, features OrganizationSetting テナント設定 organization_id, key, value Notification 通知 user_id, title, body, type, read_at CommunicationLog 連絡履歴 owner_id, user_id, method, content ScheduledInspection 定期点検 building_id, inspection_type, scheduled_date, status FileAttachment ファイル添付 resource_type, resource_id, file_url, file_name, file_size
3.4 データベース設計方針 3.4.1 命名規則 対象 規則 例 テーブル名 snake_case、複数形は使わない building, contract, payment カラム名 snake_case organization_id, created_at プライマリキー id(UUID) id 外部キー {テーブル名}_id building_id, owner_id インデックス idx_{テーブル名}_{カラム名} idx_building_org_id ENUM型 snake_case active, in_progress
3.4.2 共通カラム 全テーブルに以下のカラムを含む:
カラム 型 説明 id UUID プライマリキー(gen_random_uuid()) organization_id UUID テナントID(RLS対象) created_at TIMESTAMP 作成日時(DEFAULT now()) updated_at TIMESTAMP 更新日時(トリガーで自動更新)
3.4.3 ソフトデリート 削除操作はソフトデリートを基本とする。以下のカラムを追加:
カラム 型 説明 deleted_at TIMESTAMP 削除日時(NULLは未削除)
RLSポリシーに `deleted_at IS NULL` 条件を追加し、論理削除されたレコードを自動的に除外する。
3.4.4 暗号化対象カラム 以下の個人情報カラムはアプリケーションレベルで暗号化して保存する(AES-256、AWS KMS):
テーブル カラム Tenant email, phone, mobile_phone, date_of_birth Owner email, phone, mobile_phone, tax_id User totp_secret ContractGuarantor phone, address OwnerBankAccount account_number
4. API設計 4.1 API設計方針 4.1.1 通信プロトコル 項目 仕様 プロトコル tRPC(内部通信)+ REST(外部連携/Webhook) 認証 Bearer Token(JWT) レート制限 テナントあたり 1,000 req/min ページネーション Cursor-based pagination レスポンス形式 JSON エラー形式 tRPC標準エラー(code, message, data) バージョニング URLパスバージョニング(REST APIのみ)
4.1.2 共通レスポンス構造 // 一覧レスポンス
interface ListResponse<T> {
items: T[];
nextCursor: string | null;
totalCount: number;
}
// エラーレスポンス
interface ErrorResponse {
code: string;
message: string;
details?: Record<string, string[]>;
} 4.2 物件管理API 4.2.1 建物API HTTPメソッド パス 説明 権限 GET /api/trpc/building.list 建物一覧取得 staff, admin GET /api/trpc/building.getById 建物詳細取得 staff, admin POST /api/trpc/building.create 建物新規登録 admin PUT /api/trpc/building.update 建物情報更新 admin DELETE /api/trpc/building.delete 建物削除(ソフトデリート) admin GET /api/trpc/building.getOccupancyRate 建物稼働率取得 staff, admin GET /api/trpc/building.getVacantUnits 建物の空室一覧 staff, admin POST /api/trpc/building.uploadImage 建物画像アップロード admin DELETE /api/trpc/building.deleteImage 建物画像削除 admin
**building.list パラメータ**:
パラメータ 型 必須 説明 cursor string NO ページネーションカーソル limit number NO 取得件数(デフォルト: 25, 最大: 100) search string NO フリーテキスト検索(名称、住所) prefecture string NO 都道府県フィルタ managementType enum NO 管理形態フィルタ ownerId string NO オーナーIDフィルタ sortBy enum NO ソートキー: name / createdAt / occupancyRate sortOrder enum NO ソート順: asc / desc
**building.create リクエスト**:
フィールド 型 必須 説明 name string YES 建物名 postalCode string YES 郵便番号 prefecture string YES 都道府県 city string YES 市区町村 address1 string YES 住所1 address2 string NO 住所2 structure enum YES 構造 constructionDate date NO 築年月 totalUnits number YES 総戸数 floorsAbove number NO 地上階数 floorsBelow number NO 地下階数 ownerId string NO オーナーID managementType enum YES 管理形態 managementFeeRate number NO 管理手数料率(%) sharedFacilities array NO 共有設備 nearestStations array NO 最寄り駅情報
4.2.2 区画API HTTPメソッド パス 説明 権限 GET /api/trpc/unit.list 区画一覧取得 staff, admin GET /api/trpc/unit.getById 区画詳細取得 staff, admin POST /api/trpc/unit.create 区画新規登録 admin PUT /api/trpc/unit.update 区画情報更新 admin DELETE /api/trpc/unit.delete 区画削除 admin PUT /api/trpc/unit.updateStatus 区画ステータス更新 staff, admin GET /api/trpc/unit.searchVacant 空室検索 staff, admin POST /api/trpc/unit.bulkCreate 区画一括登録 admin
**unit.list パラメータ**:
パラメータ 型 必須 説明 cursor string NO ページネーションカーソル limit number NO 取得件数 buildingId string NO 建物IDフィルタ status enum[] NO ステータスフィルタ(複数可) layoutType string NO 間取りフィルタ rentMin number NO 賃料下限 rentMax number NO 賃料上限 search string NO フリーテキスト検索
4.2.3 駐車場API HTTPメソッド パス 説明 権限 GET /api/trpc/parking.list 駐車場一覧取得 staff, admin POST /api/trpc/parking.create 駐車場区画登録 admin PUT /api/trpc/parking.update 駐車場情報更新 admin DELETE /api/trpc/parking.delete 駐車場削除 admin
4.3 契約管理API 4.3.1 契約API HTTPメソッド パス 説明 権限 GET /api/trpc/contract.list 契約一覧取得 staff, admin GET /api/trpc/contract.getById 契約詳細取得 staff, admin POST /api/trpc/contract.create 新規契約登録 admin POST /api/trpc/contract.createQuick 契約簡易登録(一括) admin PUT /api/trpc/contract.update 契約情報更新 admin POST /api/trpc/contract.renew 更新契約作成 admin POST /api/trpc/contract.terminate 解約処理 admin GET /api/trpc/contract.getRenewalTargets 更新対象一覧取得 staff, admin GET /api/trpc/contract.calculateInitialCost 初期費用計算 staff, admin GET /api/trpc/contract.calculateSettlement 解約精算計算 staff, admin POST /api/trpc/contract.generateDocument 契約関連書類生成 staff, admin
**contract.create リクエスト**:
フィールド 型 必須 説明 unitId string YES 区画ID tenantId string YES 入居者ID(新規作成も可) contractType enum YES 契約種別 contractorType enum YES 契約者区分 startDate date YES 契約開始日 endDate date YES 契約終了日 rent number YES 賃料 managementFee number YES 管理費 commonFee number NO 共益費 deposit number YES 敷金 keyMoney number YES 礼金 renewalFeeMonths number NO 更新料(ヶ月) guaranteeCompanyId string NO 保証会社ID guarantors array NO 連帯保証人情報 specialTerms array NO 特約事項
**contract.terminate リクエスト**:
フィールド 型 必須 説明 contractId string YES 契約ID terminationDate date YES 解約日 moveOutDate date YES 退去日 terminationReason string NO 解約理由
4.4 入出金管理API 4.4.1 請求API HTTPメソッド パス 説明 権限 GET /api/trpc/invoice.list 請求一覧取得 staff, admin GET /api/trpc/invoice.getById 請求詳細取得 staff, admin POST /api/trpc/invoice.create 請求手動作成 admin PUT /api/trpc/invoice.update 請求更新 admin POST /api/trpc/invoice.generateMonthly 月次請求一括生成 admin POST /api/trpc/invoice.send 請求書送付 staff, admin POST /api/trpc/invoice.bulkSend 請求書一括送付 admin GET /api/trpc/invoice.exportPdf 請求書PDF出力 staff, admin POST /api/trpc/invoice.cancel 請求取消 admin
4.4.2 入金API HTTPメソッド パス 説明 権限 GET /api/trpc/payment.list 入金一覧取得 staff, admin GET /api/trpc/payment.getById 入金詳細取得 staff, admin POST /api/trpc/payment.create 入金手動登録 staff, admin POST /api/trpc/payment.importZengin 全銀データ取込 admin POST /api/trpc/payment.autoReconcile 一括自動消込 admin POST /api/trpc/payment.manualReconcile 手動消込 staff, admin GET /api/trpc/payment.getUnmatched 未消込一覧取得 staff, admin GET /api/trpc/payment.getSuggestedMatches 消込候補取得 staff, admin GET /api/trpc/payment.getDashboard 入金状況ダッシュボード staff, admin POST /api/trpc/payment.exportDirectDebit 口座振替データ出力 admin
4.4.3 督促API HTTPメソッド パス 説明 権限 GET /api/trpc/dunning.list 滞納一覧取得 staff, admin GET /api/trpc/dunning.getOverdueContracts 滞納契約一覧 staff, admin POST /api/trpc/dunning.generateNotices 督促データ一括作成 admin POST /api/trpc/dunning.sendNotice 督促状送付 admin GET /api/trpc/dunning.getHistory 督促履歴取得 staff, admin
4.4.4 オーナー送金API HTTPメソッド パス 説明 権限 GET /api/trpc/remittance.list 送金一覧取得 staff, admin GET /api/trpc/remittance.getById 送金詳細取得 staff, admin POST /api/trpc/remittance.generateMonthly 月次送金データ一括生成 admin PUT /api/trpc/remittance.confirm 送金データ確定 admin POST /api/trpc/remittance.exportZengin 総合振込データ(全銀)出力 admin GET /api/trpc/remittance.getStatement 送金明細書取得 staff, admin GET /api/trpc/remittance.exportStatementPdf 送金明細書PDF出力 staff, admin
4.5 オーナー管理API HTTPメソッド パス 説明 権限 GET /api/trpc/owner.list オーナー一覧取得 staff, admin GET /api/trpc/owner.getById オーナー詳細取得 staff, admin POST /api/trpc/owner.create オーナー新規登録 admin PUT /api/trpc/owner.update オーナー情報更新 admin DELETE /api/trpc/owner.delete オーナー削除 admin GET /api/trpc/owner.getBuildings オーナー所有物件一覧 staff, admin GET /api/trpc/owner.getMonthlyReport 月次収支報告書取得 staff, admin GET /api/trpc/owner.getAnnualReport 年間収支報告書取得 staff, admin GET /api/trpc/owner.getTaxStatement 支払調書取得 admin POST /api/trpc/owner.addBankAccount 振込先口座登録 admin PUT /api/trpc/owner.updateBankAccount 振込先口座更新 admin GET /api/trpc/owner.getCommunicationLog 連絡履歴取得 staff, admin POST /api/trpc/owner.addCommunicationLog 連絡履歴追加 staff, admin
4.6 入居者管理API HTTPメソッド パス 説明 権限 GET /api/trpc/tenant.list 入居者一覧取得 staff, admin GET /api/trpc/tenant.getById 入居者詳細取得 staff, admin POST /api/trpc/tenant.create 入居者新規登録 staff, admin PUT /api/trpc/tenant.update 入居者情報更新 staff, admin DELETE /api/trpc/tenant.delete 入居者削除 admin GET /api/trpc/tenant.getContracts 入居者の契約一覧 staff, admin POST /api/trpc/tenant.addCohabitant 同居人追加 staff, admin PUT /api/trpc/tenant.updateCohabitant 同居人更新 staff, admin DELETE /api/trpc/tenant.removeCohabitant 同居人削除 staff, admin POST /api/trpc/tenant.addVehicle 車両登録 staff, admin
4.7 修繕管理API HTTPメソッド パス 説明 権限 GET /api/trpc/maintenance.list チケット一覧取得 staff, admin GET /api/trpc/maintenance.getById チケット詳細取得 staff, admin POST /api/trpc/maintenance.create チケット新規作成 staff, admin PUT /api/trpc/maintenance.update チケット更新 staff, admin PUT /api/trpc/maintenance.updateStatus ステータス更新 staff, admin POST /api/trpc/maintenance.assign 担当者アサイン admin POST /api/trpc/maintenance.addComment コメント追加 staff, admin POST /api/trpc/maintenance.addEstimate 見積登録 staff, admin PUT /api/trpc/maintenance.approveEstimate 見積承認 admin POST /api/trpc/maintenance.uploadImage 画像アップロード staff, admin GET /api/trpc/maintenance.getVendors 業者一覧取得 staff, admin POST /api/trpc/maintenance.createVendor 業者登録 admin
4.8 帳票・ドキュメントAPI HTTPメソッド パス 説明 権限 GET /api/trpc/document.listTemplates テンプレート一覧取得 staff, admin GET /api/trpc/document.getTemplate テンプレート詳細取得 staff, admin PUT /api/trpc/document.updateTemplate テンプレートカスタマイズ admin POST /api/trpc/document.generate 帳票生成 staff, admin POST /api/trpc/document.bulkGenerate 帳票一括生成 admin GET /api/trpc/document.download 帳票ダウンロード staff, admin GET /api/trpc/document.listGenerated 生成済み帳票一覧 staff, admin POST /api/trpc/document.uploadFile ファイルアップロード staff, admin GET /api/trpc/document.listFiles ファイル一覧取得 staff, admin DELETE /api/trpc/document.deleteFile ファイル削除 admin
**document.generate リクエスト**:
フィールド 型 必須 説明 templateId string YES テンプレートID resourceType enum YES リソース種別(contract, invoice, remittance等) resourceId string YES リソースID format enum NO 出力形式: pdf / excel(デフォルト: pdf) options object NO 生成オプション(和暦表示等)
4.9 ユーザー管理API HTTPメソッド パス 説明 権限 GET /api/trpc/user.list ユーザー一覧取得 admin GET /api/trpc/user.getById ユーザー詳細取得 admin POST /api/trpc/user.invite ユーザー招待 admin PUT /api/trpc/user.update ユーザー情報更新 admin PUT /api/trpc/user.updateRole ロール変更 admin DELETE /api/trpc/user.deactivate ユーザー無効化 admin GET /api/trpc/user.getMe 自分の情報取得 all PUT /api/trpc/user.updateMe 自分の情報更新 all PUT /api/trpc/user.changePassword パスワード変更 all POST /api/trpc/user.enableTotp TOTP有効化 all POST /api/trpc/user.disableTotp TOTP無効化 all GET /api/trpc/user.getAuditLog 操作ログ取得 admin
4.10 テナント管理API(プラットフォーム管理者向け) HTTPメソッド パス 説明 権限 GET /api/trpc/organization.list テナント一覧取得 platform_admin GET /api/trpc/organization.getById テナント詳細取得 platform_admin, tenant_admin POST /api/trpc/organization.create テナント新規作成 platform_admin PUT /api/trpc/organization.update テナント情報更新 platform_admin, tenant_admin PUT /api/trpc/organization.updatePlan プラン変更 platform_admin PUT /api/trpc/organization.updateFeatures 機能ON/OFF変更 platform_admin PUT /api/trpc/organization.updateBranding ブランディング更新 tenant_admin POST /api/trpc/organization.addDomain カスタムドメイン追加 platform_admin DELETE /api/trpc/organization.removeDomain カスタムドメイン削除 platform_admin GET /api/trpc/organization.getUsageStats 利用状況統計 platform_admin PUT /api/trpc/organization.suspend テナント停止 platform_admin PUT /api/trpc/organization.activate テナント有効化 platform_admin
4.11 ダッシュボード・レポートAPI HTTPメソッド パス 説明 権限 GET /api/trpc/dashboard.getSummary ダッシュボードサマリー staff, admin GET /api/trpc/dashboard.getOccupancyRate 稼働率データ staff, admin GET /api/trpc/dashboard.getPaymentStatus 入金状況データ staff, admin GET /api/trpc/dashboard.getAlerts 業務アラート staff, admin GET /api/trpc/dashboard.getRecentTickets 直近の修繕案件 staff, admin GET /api/trpc/report.monthlyManagement 月次経営レポート admin GET /api/trpc/report.vacancyAnalysis 空室期間分析 admin GET /api/trpc/report.delinquencyTrend 滞納傾向分析 admin GET /api/trpc/report.maintenanceCost 修繕コスト分析 admin GET /api/trpc/report.ownerProfitability オーナー別収益分析 admin GET /api/trpc/report.workloadAnalysis 業務量分析 admin
4.12 ポータルAPI 4.12.1 オーナーポータルAPI HTTPメソッド パス 説明 権限 GET /api/trpc/portal.owner.getBuildings 所有物件一覧 owner GET /api/trpc/portal.owner.getBuildingDetail 物件詳細 owner GET /api/trpc/portal.owner.getOccupancyStatus 稼働状況 owner GET /api/trpc/portal.owner.getMonthlyReport 月次収支 owner GET /api/trpc/portal.owner.getAnnualReport 年間収支 owner GET /api/trpc/portal.owner.getMaintenanceTickets 修繕案件一覧 owner GET /api/trpc/portal.owner.getNotifications お知らせ一覧 owner GET /api/trpc/portal.owner.getDocuments 書類一覧 owner GET /api/trpc/portal.owner.downloadDocument 書類ダウンロード owner POST /api/trpc/portal.owner.sendMessage メッセージ送信 owner POST /api/trpc/portal.owner.approveEstimate 修繕見積承認 owner
4.12.2 入居者ポータルAPI HTTPメソッド パス 説明 権限 GET /api/trpc/portal.resident.getContract 契約情報取得 resident GET /api/trpc/portal.resident.getPaymentHistory 支払い履歴 resident GET /api/trpc/portal.resident.getMaintenanceTickets 修繕申請一覧 resident POST /api/trpc/portal.resident.createMaintenanceTicket 修繕申請 resident POST /api/trpc/portal.resident.uploadMaintenanceImage 修繕画像添付 resident GET /api/trpc/portal.resident.getNotifications お知らせ一覧 resident POST /api/trpc/portal.resident.submitNotice 届出提出 resident POST /api/trpc/portal.resident.requestTermination 解約申請 resident
4.13 認証API(REST) HTTPメソッド パス 説明 POST /api/auth/login ログイン POST /api/auth/logout ログアウト POST /api/auth/refresh トークンリフレッシュ POST /api/auth/forgot-password パスワードリセット要求 POST /api/auth/reset-password パスワードリセット実行 POST /api/auth/verify-totp TOTP検証 GET /api/auth/session セッション情報取得
4.14 Webhook API(REST) HTTPメソッド パス 説明 POST /api/webhooks/e-contract 電子契約ステータス通知 POST /api/webhooks/payment-notification 入金通知 POST /api/webhooks/guarantee-company 保証会社連携通知
4.15 外部連携用REST API HTTPメソッド パス 説明 POST /api/v1/zengin/import 全銀データ取込 GET /api/v1/zengin/export/direct-debit 口座振替データ出力 GET /api/v1/zengin/export/remittance 総合振込データ出力 GET /api/v1/accounting/journal-entries 仕訳データ出力 POST /api/v1/accounting/sync 会計ソフト同期 GET /api/v1/export/csv/{resource} CSV一括出力
5. 画面設計 5.1 画面一覧 graph LR
subgraph "認証"
Login["ログイン"]
ForgotPW["パスワードリセット"]
TOTP["二段階認証"]
end
subgraph "管理画面"
Dashboard["ダッシュボード"]
subgraph "物件管理"
BuildingList["建物一覧"]
BuildingDetail["建物詳細"]
UnitList["区画一覧"]
UnitDetail["区画詳細"]
end
subgraph "契約管理"
ContractList["契約一覧"]
ContractDetail["契約詳細"]
ContractCreate["新規契約"]
ContractQuick["簡易契約登録"]
RenewalList["更新対象一覧"]
TerminationCreate["解約処理"]
end
subgraph "入出金管理"
InvoiceList["請求一覧"]
InvoiceDetail["請求詳細"]
PaymentList["入金一覧"]
Reconciliation["消込処理"]
DunningList["督促管理"]
RemittanceList["オーナー送金"]
RemittanceDetail["送金詳細"]
end
subgraph "オーナー管理"
OwnerList["オーナー一覧"]
OwnerDetail["オーナー詳細"]
end
subgraph "入居者管理"
TenantList["入居者一覧"]
TenantDetail["入居者詳細"]
end
subgraph "修繕管理"
TicketList["チケット一覧"]
TicketDetail["チケット詳細"]
TicketCreate["チケット作成"]
VendorList["業者一覧"]
KeyManagement["鍵管理"]
end
subgraph "帳票・ドキュメント"
TemplateList["テンプレート一覧"]
DocumentList["ドキュメント一覧"]
FileManager["ファイル管理"]
end
subgraph "レポート"
ManagementReport["経営レポート"]
VacancyReport["空室分析"]
DelinquencyReport["滞納分析"]
end
subgraph "設定"
OrgSettings["組織設定"]
BrandingSettings["ブランディング"]
UserManagement["ユーザー管理"]
RoleManagement["ロール管理"]
AuditLogView["監査ログ"]
end
end
subgraph "ポータル"
OwnerPortalDash["オーナーダッシュボード"]
OwnerPortalProperty["物件状況"]
OwnerPortalReport["収支確認"]
ResidentPortalDash["入居者ダッシュボード"]
ResidentPortalContract["契約確認"]
ResidentPortalMaintenance["修繕申請"]
end
subgraph "プラットフォーム管理"
PlatformDash["PF ダッシュボード"]
TenantManagement["テナント管理"]
PlanManagement["プラン管理"]
SystemSettings["システム設定"]
end 5.2 ダッシュボード画面 概要 ログイン後の最初の画面。管理会社の業務状況を一目で把握できる。
画面要素 要素 説明 データソース 管理戸数カード 総戸数、入居中、空室数、稼働率 unit.status集計 入金状況カード 今月の請求額、入金額、入金率、未入金額 invoice/payment集計 滞納警告バッジ 滞納件数、滞納総額 dunning集計 更新期限アラート 3ヶ月以内に更新期限到来の契約リスト contract.end_date 解約予定リスト 今後の退去予定一覧 contract.termination_date 直近の問い合わせ 未対応の修繕チケット上位5件 maintenance.status = received 月間収支チャート 直近12ヶ月の収支推移折れ線グラフ payment集計 稼働率チャート 直近12ヶ月の稼働率推移 unit.status集計
操作 • ウィジェットの配置カスタマイズ(ドラッグ&ドロップ)
• 各カードクリックで詳細画面へ遷移
• 期間フィルタ(今月/先月/カスタム)
• CSV/PDFエクスポート
ワイヤーフレーム ┌──────────────────────────────────────────────────────────────────┐
│ ダッシュボード [期間: 今月 ▾] │
├──────────────┬──────────────┬──────────────┬──────────────────────┤
│ 📊 管理戸数 │ 💰 入金状況 │ ⚠️ 滞納 │ 🔧 未対応チケット │
│ 450戸 │ 入金率 92% │ 12件 │ 8件 │
│ 稼働率 96% │ 未入金 ¥1.2M │ ¥850K │ │
├──────────────┴──────────────┼──────────────┴──────────────────────┤
│ │ │
│ 月間収支推移チャート │ 更新期限アラート │
│ [折れ線グラフ] │ ├ ○○マンション 201 2026/05/31 │
│ │ ├ △△ハイツ 302 2026/06/15 │
│ │ └ ... │
│ │ │
├──────────────────────────────┼────────────────────────────────────┤
│ │ │
│ 稼働率推移チャート │ 解約予定・退去予定 │
│ [棒グラフ] │ ├ □□マンション 102 2026/04/30 │
│ │ └ ... │
└──────────────────────────────┴────────────────────────────────────┘ 5.3 物件一覧画面 概要 管理している全建物の一覧。検索・フィルタ・ソートで効率的に物件を探せる。
画面要素 要素 説明 検索バー 建物名・住所でフリーテキスト検索 フィルタ 都道府県、管理形態、オーナー、稼働率範囲 データテーブル 建物名、住所、総戸数、空室数、稼働率、管理形態、オーナー名 新規登録ボタン 新規建物登録フォームへ遷移 一括操作 選択した建物のCSVエクスポート ページネーション 25/50/100件切替
操作 • 行クリック → 建物詳細画面へ遷移
• 列ヘッダクリック → ソート切替
• フィルタ保存 → マイフィルタとして保存
• 右クリック → コンテキストメニュー(編集、区画一覧、削除)
5.4 建物詳細画面 概要 1つの建物に関する全情報をタブ切替で表示。
タブ構成 タブ 内容 基本情報 建物名、住所、構造、築年月、設備、最寄り駅、管理形態 区画一覧 建物内の全区画リスト(ステータス別色分け) 契約一覧 建物内の全契約リスト 入出金 建物の入出金集計・履歴 修繕履歴 建物の修繕チケット一覧 画像・図面 建物の写真、図面、間取り図 オーナー情報 所有者情報(リンク)
操作 • 編集ボタン → インラインまたはフォーム編集
• 区画追加ボタン → 区画登録フォーム
• 画像アップロード → ドラッグ&ドロップ
• CSV/PDFエクスポート → 建物レポート出力
5.5 契約一覧画面 概要 全契約の一覧表示。ステータスによる絞り込みが可能。
画面要素 要素 説明 ステータスタブ 全て / 契約中 / 更新予定 / 解約予定 / 下書き 検索バー 契約番号、入居者名、物件名で検索 フィルタ 契約種別、契約期間、建物、オーナー データテーブル 契約番号、物件名、部屋番号、入居者名、契約期間、賃料、ステータス 新規契約ボタン 新規契約登録フォームへ 簡易登録ボタン 1画面で一括登録するフォームへ
ワイヤーフレーム ┌──────────────────────────────────────────────────────────────────┐
│ 契約管理 [+ 新規契約] [簡易登録] │
├──────────────────────────────────────────────────────────────────┤
│ [全て(245)] [契約中(220)] [更新予定(15)] [解約予定(5)] [下書き(5)] │
├──────────────────────────────────────────────────────────────────┤
│ 🔍 契約番号・入居者名・物件名で検索 [フィルタ ▾] [ソート ▾] │
├──────────────────────────────────────────────────────────────────┤
│ □ │ 契約番号 │ 物件名 │ 部屋 │ 入居者 │ 期間 │ 賃料 │ 状態 │
│ ──┼────────────┼─────────────────┼──────┼──────────┼──────────┼────────┼───────│
│ □ │ C-2026-001 │ ○○マンション │ 101 │ 山田太郎 │ 26/04-28/03 │ ¥85,000│ ● 契約中│
│ □ │ C-2026-002 │ △△ハイツ │ 202 │ 佐藤花子 │ 26/01-27/12 │ ¥72,000│ ● 契約中│
│ □ │ C-2025-150 │ □□レジデンス │ 305 │ 鈴木一郎 │ 25/06-27/05 │ ¥95,000│ ○ 更新予定│
├──────────────────────────────────────────────────────────────────┤
│ ◀ 1 2 3 ... 10 ▶ │
└──────────────────────────────────────────────────────────────────┘ 5.6 契約詳細画面 タブ構成 タブ 内容 基本情報 契約番号、種別、期間、賃料条件、特約事項 入居者情報 入居者基本情報、同居人、緊急連絡先 保証情報 保証会社情報、連帯保証人情報 入出金 契約に紐づく請求・入金履歴 書類 契約書、重要事項説明書等のドキュメント 更新履歴 過去の更新・変更履歴
操作 • 契約更新 → 更新契約作成フォーム
• 解約処理 → 解約登録フォーム
• 書類生成 → テンプレート選択 → PDF生成
• 電子契約送信 → 外部サービス連携
5.7 新規契約登録画面 概要 ステップ形式の契約登録フォーム。
ステップ ステップ 内容 主要入力項目 1. 物件選択 建物・区画の選択 建物検索、区画選択 2. 入居者情報 入居者の登録/選択 氏名、連絡先、勤務先、緊急連絡先 3. 契約条件 契約内容の入力 契約期間、賃料、敷金、礼金、特約 4. 保証情報 保証会社・保証人 保証会社選択、保証人情報 5. 初期費用 初期費用の確認・調整 自動計算結果の確認、項目追加 6. 確認 入力内容の最終確認 全項目の確認、修正リンク
5.8 消込処理画面 概要 入金データと請求データのマッチング画面。
画面要素 要素 説明 未消込入金リスト 全銀データ取込後の未消込入金一覧(左パネル) 未入金請求リスト 未入金の請求一覧(右パネル) 消込候補表示 入金選択時に候補請求をハイライト 一括自動消込ボタン 口座番号+金額完全一致で自動マッチング 手動消込操作 入金と請求をドラッグ&ドロップまたは選択で紐付け 消込確認ダイアログ マッチング結果の確認・確定 過不足処理 過入金→預かり金計上、不足→差額請求生成
ワイヤーフレーム ┌──────────────────────────────────────────────────────────────────┐
│ 消込処理 [自動消込実行] [全銀取込] │
├──────────────────────────────┬───────────────────────────────────┤
│ 未消込入金 (35件) │ 未入金請求 (42件) │
│ ─────────────────────────── │ ───────────────────────────────── │
│ 🔍 入金者名で検索 │ 🔍 入居者名で検索 │
│ │ │
│ ■ 2026/03/15 ¥85,000 │ ¥85,000 山田太郎 ○○マンション 101 │
│ ヤマダ タロウ │ ¥72,000 佐藤花子 △△ハイツ 202 │
│ ○○銀行 △△支店 │ ¥95,000 鈴木一郎 □□レジ 305 │
│ → 候補: 山田太郎 ¥85,000 │ │
│ │ │
│ □ 2026/03/15 ¥70,000 │ ¥72,000 田中次郎 ○○マンション 202 │
│ サトウ ハナコ │ │
│ △△銀行 □□支店 │ │
│ → 候補: 佐藤花子 ¥72,000 │ │
│ ⚠️ 差額 ¥2,000 │ │
├──────────────────────────────┴───────────────────────────────────┤
│ 消込結果: 自動マッチ 28件 / 手動要 7件 / 過不足 3件 │
└──────────────────────────────────────────────────────────────────┘ 5.9 オーナー送金画面 概要 月次のオーナー送金データの確認・確定・振込データ出力画面。
画面要素 要素 説明 対象月選択 送金対象年月の選択 送金一覧テーブル オーナー別の送金額一覧 送金明細 選択オーナーの収入/控除の内訳 ステータス表示 未生成/下書き/確定済み/送金済み 一括生成ボタン 対象月の送金データ一括生成 確定ボタン 送金データの確定 全銀出力ボタン 総合振込データの全銀フォーマット出力 明細書PDF出力 送金明細書のPDF一括生成
5.10 修繕管理画面(チケット一覧) 概要 クレーム・修繕チケットのカンバン表示。
表示モード モード 説明 カンバン ステータス別の列でチケットカード表示 テーブル 全チケットの一覧表形式
カンバン表示 ┌──────────────────────────────────────────────────────────────────┐
│ 修繕管理 [+ 新規] [カンバン ● | テーブル ○] │
├───────────────┬───────────────┬───────────────┬──────────────────┤
│ 受付 (5) │ 対応中 (8) │ 見積待ち (3) │ 完了 (12) │
│ ┌───────────┐ │ ┌───────────┐ │ ┌───────────┐ │ ┌──────────────┐ │
│ │ #MT-0045 │ │ │ #MT-0042 │ │ │ #MT-0038 │ │ │ #MT-0035 │ │
│ │ エアコン故障│ │ │ 水漏れ修理 │ │ │ 外壁塗装 │ │ │ 給湯器交換 │ │
│ │ ○○201 │ │ │ △△302 │ │ │ □□全体 │ │ │ ◇◇105 │ │
│ │ 🔴 高 │ │ │ 🔴 緊急 │ │ │ 🟡 中 │ │ │ ✅ 2026/03/10│ │
│ └───────────┘ │ └───────────┘ │ └───────────┘ │ └──────────────┘ │
│ ┌───────────┐ │ ┌───────────┐ │ │ │
│ │ #MT-0044 │ │ │ #MT-0041 │ │ │ │
│ │ 騒音苦情 │ │ │ 鍵交換 │ │ │ │
│ │ △△205 │ │ │ ○○302 │ │ │ │
│ │ 🟡 中 │ │ │ 🟢 低 │ │ │ │
│ └───────────┘ │ └───────────┘ │ │ │
└───────────────┴───────────────┴───────────────┴──────────────────┘ 5.11 帳票管理画面 概要 帳票テンプレートの管理と帳票生成。
画面要素 要素 説明 テンプレートカテゴリ 契約書類、請求書類、報告書類、その他 テンプレート一覧 カテゴリ別のテンプレートリスト テンプレートプレビュー 選択テンプレートのプレビュー表示 カスタマイズボタン テンプレートのロゴ・社名・文面の編集 生成履歴 過去に生成した帳票の一覧・ダウンロード
5.12 設定画面 5.12.1 組織設定 設定項目 説明 組織名 管理会社名の設定 連絡先 電話番号、メールアドレス 住所 本社所在地 消費税設定 税率、端数処理方法 請求設定 請求日、支払期日デフォルト 更新通知設定 更新通知タイミング(何ヶ月前)
5.12.2 ブランディング設定 設定項目 説明 ロゴ メインロゴのアップロード(SVG/PNG) ファビコン ファビコンのアップロード プライマリカラー ブランドカラーの設定(カラーピッカー) アクセントカラー CTAカラーの設定 プレビュー リアルタイムプレビュー
5.12.3 ユーザー管理 要素 説明 ユーザー一覧 組織内の全ユーザーリスト 招待フォーム メールアドレスとロール指定で招待 ロール管理 カスタムロールの作成・権限設定 監査ログ ユーザー操作の履歴閲覧
5.13 オーナーポータル画面 5.13.1 ダッシュボード 要素 説明 所有物件サマリー 総物件数、総戸数、稼働率 今月の収支 今月の収入・支出・手取り額 お知らせ 管理会社からのお知らせ一覧 修繕案件 承認待ちの修繕案件
5.13.2 収支確認画面 要素 説明 月次収支表 月別の収入・支出・手取り一覧 収支グラフ 12ヶ月推移チャート 物件別収支 物件ごとの収支内訳 書類ダウンロード 収支報告書、支払調書のDL
5.14 入居者ポータル画面 5.14.1 ダッシュボード 要素 説明 契約情報カード 物件名、部屋番号、契約期間、月額賃料 支払い状況 今月の請求額、支払い状況 お知らせ 管理会社からのお知らせ 修繕申請 修繕申請ボタン、過去の申請一覧
5.14.2 修繕申請画面 要素 説明 カテゴリ選択 設備故障、騒音、水漏れ等 詳細入力 状況の詳細テキスト入力 画像添付 スマートフォンからの写真アップロード 進捗表示 申請後のステータス追跡
6. ホワイトラベル設計 6.1 テーマシステム 6.1.1 CSS Variables構造 テナント別のカスタマイズはCSS Custom Properties(CSS Variables)で実現する。
/* デフォルトテーマ(globals.css) */
:root {
/* Brand Colors - テナントカスタマイズ対象 */
--color-primary: 222 47% 23%; /* #1B2559 HSL */
--color-primary-light: 229 54% 36%; /* #2D3A8C HSL */
--color-primary-foreground: 0 0% 100%;
--color-accent: 217 91% 60%; /* #3B82F6 HSL */
--color-accent-foreground: 0 0% 100%;
/* Semantic Colors - 固定 */
--color-success: 160 84% 39%; /* #10B981 */
--color-warning: 38 92% 50%; /* #F59E0B */
--color-danger: 0 84% 60%; /* #EF4444 */
/* Surface Colors - 固定 */
--color-background: 0 0% 100%;
--color-surface: 210 40% 96%;
--color-border: 214 32% 91%;
/* Text Colors - 固定 */
--color-text-primary: 215 25% 17%;
--color-text-secondary: 215 16% 47%;
/* Typography */
--font-sans: 'Noto Sans JP', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* Spacing */
--spacing-unit: 8px;
/* Border Radius */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
}
/* ダークモード準備(Phase 2以降) */
.dark {
--color-background: 222 47% 11%;
--color-surface: 222 47% 15%;
--color-border: 222 47% 25%;
--color-text-primary: 0 0% 93%;
--color-text-secondary: 0 0% 63%;
} 6.1.2 テナントテーマ適用フロー sequenceDiagram
participant Browser as ブラウザ
participant MW as Middleware
participant DB as DB
participant Cache as Redis
Browser->>MW: ページリクエスト
MW->>Cache: テナントテーマ取得
alt キャッシュヒット
Cache-->>MW: テーマデータ
else キャッシュミス
MW->>DB: organization.theme_config 取得
DB-->>MW: テーマデータ
MW->>Cache: キャッシュ保存(TTL: 1h)
end
MW->>Browser: HTML + インラインCSS Variables
Note over Browser: CSS Variables上書きでテーマ適用 6.1.3 テーマデータ構造 interface TenantTheme {
colors: {
primary: string; // HSL値 "222 47% 23%"
primaryLight: string; // HSL値
accent: string; // HSL値
};
logo: {
url: string; // S3 URL
width: number; // ロゴ幅(px)
height: number; // ロゴ高さ(px)
};
favicon: {
url: string; // S3 URL
};
companyName: string; // 社名表示
} 6.1.4 コントラスト自動計算 テナントがプライマリカラーを変更した際、前景色(テキスト色)のコントラスト比を自動計算し、WCAG AA基準(4.5:1以上)を満たすように白/黒を自動選択する。
function getContrastForeground(bgHsl: string): string {
const luminance = calculateRelativeLuminance(hslToRgb(bgHsl));
// WCAG AA基準: コントラスト比 4.5:1 以上
return luminance > 0.179 ? '0 0% 0%' : '0 0% 100%';
} 6.2 カスタムドメイン設計 6.2.1 ドメイン解決フロー graph TB
subgraph "テナントA"
DomainA["kanri.tenant-a.co.jp"]
end
subgraph "テナントB"
DomainB["system.tenant-b.co.jp"]
end
subgraph "デフォルト"
SubdomainC["tenant-c.propcloud.jp"]
end
subgraph "DNS"
CNAME_A["CNAME → app.propcloud.jp"]
CNAME_B["CNAME → app.propcloud.jp"]
R53["Route 53<br/>*.propcloud.jp"]
end
subgraph "CloudFront"
CF["CloudFront<br/>Distribution"]
ACM_WC["ACM<br/>*.propcloud.jp"]
ACM_A["ACM<br/>kanri.tenant-a.co.jp"]
ACM_B["ACM<br/>system.tenant-b.co.jp"]
end
subgraph "ALB"
ALBNode["ALB"]
end
subgraph "App"
AppNode["Next.js<br/>テナント解決 Middleware"]
end
DomainA --> CNAME_A --> CF
DomainB --> CNAME_B --> CF
SubdomainC --> R53 --> CF
CF --> ALBNode --> AppNode
ACM_WC --> CF
ACM_A --> CF
ACM_B --> CF 6.2.2 カスタムドメイン設定手順 1. テナント管理者がカスタムドメインを管理画面で登録
2. システムがDNS検証用のCNAMEレコードを発行
3. テナントが自社DNSにCNAMEレコードを追加
4. システムがDNS検証を実行(polling、最大48時間)
5. 検証成功後、ACMで証明書を発行
6. CloudFrontのAlternate Domain Namesに追加
7. custom_domains テーブルの ssl_status を active に更新
6.2.3 テナント解決優先順位 1. カスタムドメイン → custom_domains.domain で organization_id 特定
2. サブドメイン → {slug}.propcloud.jp から organizations.slug で特定
3. X-Tenant-ID ヘッダー → API直接呼び出し時
4. デフォルト → 404 または ログインページ 6.3 帳票テンプレート管理 6.3.1 テンプレートカスタマイズ構造 interface DocumentTemplate {
id: string;
organizationId: string;
name: string; // テンプレート名
category: TemplateCategory; // カテゴリ
baseTemplateId: string | null; // ベーステンプレートID(システム標準)
version: number; // バージョン番号
// カスタマイズ可能な項目
customization: {
header: {
logoPosition: 'left' | 'center' | 'right';
showCompanyName: boolean;
showCompanyAddress: boolean;
showCompanyPhone: boolean;
customText: string | null;
};
footer: {
showPageNumber: boolean;
customText: string | null;
};
body: {
fontSize: number; // ポイント
fontFamily: string;
margins: { // mm
top: number;
right: number;
bottom: number;
left: number;
};
};
};
// テンプレート本体(React PDFコンポーネントのJSON定義)
templateData: object;
}
type TemplateCategory =
| 'contract' // 契約書類
| 'invoice' // 請求書類
| 'receipt' // 領収書
| 'dunning' // 督促状
| 'report' // 報告書
| 'settlement' // 精算書
| 'maintenance' // 修繕書類
| 'notification' // 通知書
| 'tax' // 税務書類
| 'other'; // その他 6.3.2 テンプレート継承モデル システム標準テンプレート(180種)
│
├── テナントA カスタムテンプレート
│ └── ロゴ・社名・フッター差し替え
│
├── テナントB カスタムテンプレート
│ └── ロゴ・社名・追加条項差し替え
│
└── テナントC(カスタマイズなし)
└── システム標準をそのまま使用 法改正等でシステム標準テンプレートが更新された場合:
• カスタマイズ部分は保持
• ベーステンプレートの更新内容はマージ
• 競合がある場合はテナント管理者に通知
6.4 機能ON/OFF設計 6.4.1 フィーチャーフラグ構造 interface FeatureFlags {
// モジュール単位
modules: {
ownerPortal: boolean; // オーナーポータル
residentPortal: boolean; // 入居者ポータル
eContract: boolean; // 電子契約連携
aiReconciliation: boolean; // AI消込アシスト
maintenanceManagement: boolean; // 修繕管理
dunningManagement: boolean; // 督促管理
accountingIntegration: boolean; // 会計ソフト連携
reportAnalysis: boolean; // 分析レポート
};
// 詳細機能単位
features: {
bulkInvoiceGeneration: boolean; // 一括請求生成
zenginImport: boolean; // 全銀データ取込
zenginExport: boolean; // 全銀データ出力
customDomain: boolean; // カスタムドメイン
smsNotification: boolean; // SMS通知
commandPalette: boolean; // コマンドパレット
};
} 6.4.2 フィーチャーフラグの適用 // サーバーサイド: tRPCルーターでの権限チェック
const maintenanceRouter = router({
create: protectedProcedure
.use(requireFeature('modules.maintenanceManagement'))
.input(maintenanceCreateSchema)
.mutation(async ({ ctx, input }) => {
// ...
}),
});
// クライアントサイド: コンポーネントの表示制御
function Sidebar() {
const { features } = useTenantFeatures();
return (
<nav>
<SidebarItem href="/buildings" icon={Building2} label="物件管理" />
<SidebarItem href="/contracts" icon={FileText} label="契約管理" />
{features.modules.maintenanceManagement && (
<SidebarItem href="/maintenance" icon={Wrench} label="修繕管理" />
)}
{features.modules.ownerPortal && (
<SidebarItem href="/owners" icon={Users} label="オーナー管理" />
)}
</nav>
);
} 6.5 用語カスタマイズ設計 interface TerminologyOverrides {
[key: string]: string;
}
// デフォルト用語 → カスタマイズ例
const defaultTerminology = {
'building': '物件',
'unit': '部屋',
'tenant': '入居者',
'owner': 'オーナー',
'contract': '契約',
'invoice': '請求',
'maintenance': '修繕',
'remittance': '送金',
};
// テナントA のカスタマイズ
const tenantAOverrides = {
'building': '管理物件',
'unit': '区画',
'tenant': '借主',
}; 用語変換はi18nフレームワーク(next-intl)の辞書をテナント別にオーバーライドして実現する。
7. 認証・認可設計 7.1 認証フロー 7.1.1 ログインフロー sequenceDiagram
participant User as ユーザー
participant App as Next.js App
participant Auth as Auth Service
participant DB as PostgreSQL
participant Redis as Redis
User->>App: メール + パスワード入力
App->>Auth: POST /api/auth/login
Auth->>DB: ユーザー検索(email)
DB-->>Auth: ユーザーデータ
alt アカウントロック中
Auth-->>App: 401 "アカウントがロックされています"
App-->>User: エラー表示
end
Auth->>Auth: パスワード検証(bcrypt.compare)
alt パスワード不一致
Auth->>DB: failed_login_count + 1
alt 10回連続失敗
Auth->>DB: locked_until = now() + 30min
end
Auth-->>App: 401 "認証に失敗しました"
App-->>User: エラー表示
end
alt TOTP有効
Auth-->>App: 200 { requireTotp: true, tempToken }
App-->>User: TOTP入力画面表示
User->>App: TOTPコード入力
App->>Auth: POST /api/auth/verify-totp
Auth->>Auth: TOTPコード検証
alt TOTP不一致
Auth-->>App: 401 "認証コードが無効です"
end
end
Auth->>DB: failed_login_count = 0, last_login_at = now()
Auth->>Redis: セッション保存(TTL: 24h)
Auth->>DB: AuditLog記録(login)
Auth-->>App: 200 { accessToken, refreshToken }
App->>App: Cookie にトークン保存(httpOnly, secure)
App-->>User: ダッシュボードへリダイレクト 7.1.2 トークン設計 トークン 有効期限 保存場所 用途 Access Token 15分 httpOnly Cookie API認証 Refresh Token 7日 httpOnly Cookie Access Tokenの更新 Session ID 24h Redis セッション管理
**Access Token(JWT)ペイロード**:
interface JwtPayload {
sub: string; // ユーザーID
org: string; // 組織ID(テナントID)
role: string; // ロールスラグ
permissions: string[]; // 権限リスト
iat: number; // 発行日時
exp: number; // 有効期限
} 7.1.3 パスワードポリシー ルール 要件 最低文字数 12文字以上 英大文字 1文字以上必須 英小文字 1文字以上必須 数字 1文字以上必須 記号 1文字以上必須 過去パスワード再利用 直近5回分と不一致であること ハッシュアルゴリズム bcrypt(cost factor: 12)
7.2 RBAC設計 7.2.1 システムロール定義 graph TB
PlatformAdmin["プラットフォーム管理者<br/>platform_admin"]
TenantAdmin["テナント管理者<br/>tenant_admin"]
CompanyAdmin["管理会社管理者<br/>company_admin"]
CompanyStaff["管理会社スタッフ<br/>company_staff"]
OwnerRole["オーナー<br/>owner"]
ResidentRole["入居者<br/>resident"]
PlatformAdmin -->|"全テナント管理"| TenantAdmin
TenantAdmin -->|"テナント内管理"| CompanyAdmin
CompanyAdmin -->|"業務管理"| CompanyStaff
CompanyAdmin -.->|"ポータル招待"| OwnerRole
CompanyAdmin -.->|"ポータル招待"| ResidentRole 7.2.2 権限マトリクス リソース 操作 platform_admin tenant_admin company_admin company_staff owner resident Organization read R(all) R(own) R(own) - - - Organization write W W(own) - - - - Building read R(all) R(own) R(own) R(own) R(own buildings) - Building write W W W - - - Unit read R(all) R(own) R(own) R(own) R(own) R(own) Unit write W W W W(assigned) - - Contract read R(all) R(own) R(own) R(own) - R(own) Contract write W W W - - - Payment read R(all) R(own) R(own) R(own) - R(own) Payment write W W W W - - Invoice read R(all) R(own) R(own) R(own) - R(own) Invoice write W W W - - - Owner read R(all) R(own) R(own) R(own) R(self) - Owner write W W W W(assigned) - - Tenant read R(all) R(own) R(own) R(own) - R(self) Tenant write W W W W - W(self,limited) Maintenance read R(all) R(own) R(own) R(own) R(own) R(own) Maintenance write W W W W - W(create only) Document read R(all) R(own) R(own) R(own) R(own) R(own) Document write W W W W - - User read R(all) R(own) R(own) R(self) R(self) R(self) User write W W W W(self) W(self) W(self) AuditLog read R(all) R(own) R(own) - - - Remittance read R(all) R(own) R(own) R(own) R(own) - Remittance write W W W - - - Report read R(all) R(own) R(own) R(limited) R(own) -
**凡例**: R = Read、W = Write(Create/Update/Delete)、(own) = テナント内のみ
7.2.3 権限チェック実装 // 権限定義
const PERMISSIONS = {
'building:read': 'building:read',
'building:create': 'building:create',
'building:update': 'building:update',
'building:delete': 'building:delete',
// ...
} as const;
// tRPC Middleware での権限チェック
const requirePermission = (permission: string) => {
return middleware(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
const hasPermission = ctx.user.permissions.includes(permission);
if (!hasPermission) {
throw new TRPCError({
code: 'FORBIDDEN',
message: `Permission denied: ${permission}`,
});
}
// 監査ログ記録
await ctx.auditLog.record({
action: permission,
userId: ctx.user.id,
organizationId: ctx.tenant.id,
});
return next();
});
}; 7.3 テナント分離における認証 7.3.1 セッションとテナントの紐付け interface Session {
userId: string;
organizationId: string; // テナントID
roleSlug: string;
permissions: string[];
expiresAt: number;
}
// Middleware でのテナント検証
async function validateTenantAccess(
session: Session,
requestTenantId: string
): boolean {
// プラットフォーム管理者は全テナントアクセス可
if (session.roleSlug === 'platform_admin') {
return true;
}
// 通常ユーザーは自テナントのみ
return session.organizationId === requestTenantId;
} 7.3.2 クロステナントアクセス防止 1. **RLSポリシー**: DB層での物理的なデータ分離
2. **Middleware検証**: リクエストのテナントIDとセッションのテナントIDの一致確認
3. **API層バリデーション**: リソースの organization_id とセッションの organization_id の一致確認
4. **フロントエンドルーティング**: テナント固有URLの強制
7.4 監査ログ設計 7.4.1 記録対象アクション カテゴリ アクション 説明 認証 login, logout, login_failed, password_change, totp_enable, totp_disable 認証関連操作 データ操作 create, update, delete CRUD操作 データ閲覧 read, export, download データアクセス 管理操作 user_invite, role_change, feature_toggle, branding_update 管理設定変更 バッチ操作 bulk_invoice, auto_reconcile, bulk_remittance バッチ処理実行
7.4.2 ログ構造 interface AuditLogEntry {
id: string;
organizationId: string;
userId: string;
action: string;
resourceType: string;
resourceId: string | null;
changes: {
before: Record<string, unknown> | null;
after: Record<string, unknown> | null;
} | null;
metadata: {
ipAddress: string;
userAgent: string;
requestId: string;
};
createdAt: Date;
} 8. 外部連携設計 8.1 電子契約連携 8.1.1 連携対象サービス サービス 連携方式 対応フェーズ GMOサイン REST API Phase 3 クラウドサイン REST API Phase 3
8.1.2 連携フロー sequenceDiagram
participant Staff as 管理スタッフ
participant App as PropCloud
participant ESign as 電子契約サービス
participant Signer as 契約者
Staff->>App: 契約書PDF生成
App->>App: 契約データから帳票生成
Staff->>App: 電子契約送信リクエスト
App->>ESign: API: 契約書PDF + 署名箇所設定
ESign-->>App: 契約ID返却
App->>App: 契約ステータス: 電子署名待ち
ESign->>Signer: 署名依頼メール送信
Signer->>ESign: 電子署名実行
ESign->>App: Webhook: 署名完了通知
App->>ESign: API: 署名済みPDF取得
App->>App: S3に保存 + 契約ステータス更新
App->>Staff: 署名完了通知 8.1.3 データマッピング PropCloudデータ 電子契約パラメータ 説明 Contract.contractNumber document_title 文書タイトル Tenant.email signer_email 署名者メール Tenant.name signer_name 署名者名 GeneratedDocument.fileUrl document_file 署名対象PDF Contract.specialTerms custom_fields カスタムフィールド
8.2 全銀フォーマット連携 8.2.1 対応フォーマット フォーマット 方向 用途 全銀協フォーマット(口座振替) 出力 口座振替データ作成→金融機関提出 全銀協フォーマット(総合振込) 出力 オーナー送金データ作成→金融機関提出 全銀協フォーマット(入出金明細) 入力 入金データ取込→消込処理
8.2.2 口座振替データ出力仕様 [ヘッダレコード]
データ区分(1): 1
種別コード(2): 91
コード区分(1): 0
委託者コード(10): テナント設定値
委託者名(40): テナント設定値
引落日(4): MMDD
取引銀行番号(4): テナント設定値
取引銀行名(15): テナント設定値
取引支店番号(3): テナント設定値
取引支店名(15): テナント設定値
口座種別(1): テナント設定値
口座番号(7): テナント設定値
[データレコード] ※契約ごとに1レコード
データ区分(1): 2
引落銀行番号(4): 契約者の銀行番号
引落銀行名(15): 契約者の銀行名
引落支店番号(3): 契約者の支店番号
引落支店名(15): 契約者の支店名
口座種別(1): 契約者の口座種別
口座番号(7): 契約者の口座番号
口座名義(30): 契約者名(半角カナ)
引落金額(10): 請求金額
新規コード(1): 0=通常 1=新規 2=変更 4=削除
顧客番号(20): 契約番号
振替結果(1): スペース(結果取込時に使用)
[トレーラレコード]
データ区分(1): 8
合計件数(6): データレコード件数
合計金額(12): 引落金額合計
[エンドレコード]
データ区分(1): 9 8.2.3 入金データ取込処理 graph TB
Upload["全銀ファイル<br/>アップロード"]
Parse["ファイル<br/>パース処理"]
Validate["データ<br/>バリデーション"]
Import["入金データ<br/>一括登録"]
AutoMatch["自動消込<br/>実行"]
Review["消込結果<br/>レビュー"]
Confirm["確定処理"]
Upload --> Parse
Parse --> Validate
Validate -->|"OK"| Import
Validate -->|"エラー"| ErrorReport["エラーレポート"]
Import --> AutoMatch
AutoMatch --> Review
Review --> Confirm
subgraph "自動マッチングロジック"
M1["完全一致<br/>(口座番号 + 金額)"]
M2["部分一致<br/>(名義揺れ補正 + 金額)"]
M3["候補提示<br/>(金額のみ一致)"]
end
AutoMatch --> M1
AutoMatch --> M2
AutoMatch --> M3 8.3 会計ソフト連携 8.3.1 対応サービス サービス 連携方式 対応フェーズ freee会計 REST API (OAuth 2.0) Phase 2 マネーフォワードクラウド会計 REST API (OAuth 2.0) Phase 2 弥生会計 CSV出力 Phase 2
8.3.2 仕訳データマッピング PropCloudイベント 借方科目 貸方科目 金額 家賃請求計上 売掛金 賃貸収入 賃料 管理費請求計上 売掛金 管理費収入 管理費 家賃入金 普通預金 売掛金 入金額 オーナー送金 預り金 普通預金 送金額 管理手数料計上 預り金 管理手数料収入 手数料 修繕費計上 修繕費/預り金 未払金 修繕費 源泉徴収計上 預り金 預り源泉税 源泉税 敷金受入 普通預金 預り敷金 敷金 敷金返還 預り敷金 普通預金 返還額
8.3.3 仕訳データ構造 interface JournalEntry {
id: string;
organizationId: string;
transactionDate: Date; // 取引日
description: string; // 摘要
entries: JournalEntryLine[]; // 仕訳行
sourceType: string; // 元データ種別(invoice, payment, remittance等)
sourceId: string; // 元データID
status: 'pending' | 'synced' | 'error';
syncedAt: Date | null;
errorMessage: string | null;
}
interface JournalEntryLine {
accountCode: string; // 勘定科目コード
accountName: string; // 勘定科目名
subAccountCode: string | null; // 補助科目コード
subAccountName: string | null; // 補助科目名
debitAmount: number; // 借方金額
creditAmount: number; // 貸方金額
taxType: string; // 税区分
taxAmount: number; // 消費税額
} 8.4 通知連携 8.4.1 メール送信 項目 仕様 送信サービス Amazon SES テンプレートエンジン React Email From設定 テナント別に設定可能 バウンス/苦情処理 SNSトピック連携で自動処理
8.4.2 通知テンプレート一覧 テンプレート トリガー 送信先 ユーザー招待 ユーザー招待時 招待されたユーザー パスワードリセット リセット要求時 対象ユーザー 請求書送付 請求書送付操作 入居者 更新案内 更新通知日到達 入居者 入金確認 消込完了時 入居者 督促通知 督促実行時 滞納入居者 送金明細 送金確定時 オーナー 修繕受付完了 チケット作成時 申告入居者 修繕完了通知 チケット完了時 申告入居者、オーナー 見積承認依頼 見積登録時 オーナー 月次レポート 月次バッチ オーナー
8.4.3 SMS送信(オプション) 項目 仕様 送信サービス Amazon SNS / Twilio 用途 緊急連絡、TOTP認証コード 文字数制限 70文字(全角)
8.4.4 Slack/Teams連携 項目 仕様 連携方式 Incoming Webhook 通知対象 滞納発生、修繕チケット作成、大口入金、システムアラート 設定 テナント別にWebhook URLを設定可能
9. バッチ処理設計 9.1 バッチ処理一覧 graph LR
subgraph "月初バッチ"
B1["月次請求<br/>データ生成"]
B2["口座振替<br/>データ作成"]
end
subgraph "日次バッチ"
B3["入金データ<br/>取込・消込"]
B4["滞納検出"]
B5["更新期限<br/>アラート"]
B6["鍵貸出<br/>期限アラート"]
end
subgraph "月末バッチ"
B7["オーナー送金<br/>データ生成"]
B8["送金明細書<br/>生成"]
B9["仕訳データ<br/>生成"]
end
subgraph "年次バッチ"
B10["支払調書<br/>データ生成"]
B11["年間収支<br/>レポート生成"]
end
subgraph "随時バッチ"
B12["督促データ<br/>一括生成"]
B13["帳票一括<br/>PDF生成"]
B14["CSV一括<br/>エクスポート"]
end
B1 --> B2
B3 --> B4
B7 --> B8
B8 --> B9 9.2 月次請求データ生成 9.2.1 処理概要 項目 内容 実行タイミング 毎月1日 0:00(テナント別に設定可能) 処理対象 ステータスが active な全契約 処理時間目標 5,000件/5分以内 リトライ 失敗時3回まで自動リトライ(指数バックオフ)
9.2.2 処理フロー graph TB
Start["バッチ開始"]
FetchContracts["対象契約取得<br/>(status=active)"]
Loop["契約ごとにループ"]
subgraph "契約ごとの処理"
CalcRent["家賃・管理費・共益費計算"]
CalcParking["駐車場料金計算"]
CalcVariable["変動費按分計算<br/>(電気・水道等)"]
CalcTax["消費税計算<br/>(課税/非課税判定)"]
CreateInvoice["請求データ作成<br/>(Invoice + InvoiceItem)"]
CheckDuplicate["重複チェック<br/>(同月の既存請求確認)"]
end
GenerateNumber["請求番号採番"]
SaveDB["DB保存"]
Notify["完了通知"]
End["バッチ終了"]
Start --> FetchContracts
FetchContracts --> Loop
Loop --> CheckDuplicate
CheckDuplicate -->|"重複なし"| CalcRent
CheckDuplicate -->|"重複あり"| Skip["スキップ"]
CalcRent --> CalcParking
CalcParking --> CalcVariable
CalcVariable --> CalcTax
CalcTax --> GenerateNumber
GenerateNumber --> CreateInvoice
CreateInvoice --> SaveDB
SaveDB --> Loop
Loop -->|"全契約処理完了"| Notify
Notify --> End 9.2.3 請求金額計算ロジック interface MonthlyBillingCalculation {
contractId: string;
billingMonth: Date;
// 固定費
rent: number; // 賃料
managementFee: number; // 管理費
commonFee: number; // 共益費
parkingFee: number; // 駐車場料金
// 変動費
variableFees: VariableFee[]; // 電気・水道等
// 日割り計算(入居月・退去月)
proRataAdjustment: number; // 日割り調整額
// 消費税
taxableAmount: number; // 課税対象額(駐車場等)
taxExemptAmount: number; // 非課税額(住居家賃)
taxAmount: number; // 消費税額
// 合計
totalAmount: number;
}
// 非課税判定: 住居用賃貸の家賃・管理費・共益費は非課税
// 課税対象: 駐車場料金、事業用賃貸、更新料等
function isTaxable(itemType: InvoiceItemType, contractType: string): boolean {
if (contractType === 'residential') {
return itemType === 'parking' || itemType === 'other';
}
return true; // 事業用は全て課税
} 9.3 消込処理 9.3.1 自動消込ロジック graph TB
ImportData["入金データ取込"]
Phase1["Phase 1: 完全一致マッチング"]
Phase2["Phase 2: 名義揺れ補正マッチング"]
Phase3["Phase 3: 金額一致候補提示"]
PhaseAI["Phase AI: AI消込アシスト<br/>(Phase 3オプション)"]
Manual["手動消込画面"]
ImportData --> Phase1
Phase1 -->|"口座番号+金額<br/>完全一致"| AutoMatch1["自動消込<br/>(auto_matched)"]
Phase1 -->|"不一致"| Phase2
Phase2 -->|"名義揺れ補正<br/>+金額一致"| AutoMatch2["自動消込<br/>(auto_matched)"]
Phase2 -->|"不一致"| Phase3
Phase3 -->|"金額一致候補あり"| SuggestMatch["候補提示<br/>→手動確認"]
Phase3 -->|"候補なし"| PhaseAI
PhaseAI -->|"AI推論マッチ"| SuggestAI["AI候補提示<br/>→手動確認"]
PhaseAI -->|"候補なし"| Manual
SuggestMatch --> Manual
SuggestAI --> Manual 9.3.2 名義揺れ補正ルール ルール 例 全角→半角変換 ヤマダ → ヤマダ スペース除去 ヤマダ タロウ → ヤマダタロウ 法人格除去 カ)ヤマダ → ヤマダ 濁点正規化 ヤマタ゛ → ヤマダ カナ変換 山田太郎 → ヤマダタロウ(入居者名から)
9.3.3 過不足処理 ケース 処理 金額完全一致 そのまま消込確定 過入金 消込 + 超過分を預かり金(excess_amount)に計上 不足入金 部分消込 + 差額請求生成(shortage_amount) 複数請求に対する一括入金 請求日順に充当
9.4 オーナー送金データ生成 9.4.1 処理フロー graph TB
Start["バッチ開始"]
FetchOwners["対象オーナー取得"]
Loop["オーナーごとにループ"]
subgraph "オーナーごとの処理"
CalcIncome["賃料収入集計<br/>(当月入金確定分)"]
CalcMgmtFee["管理手数料計算<br/>(収入 × 手数料率)"]
CalcMaintenance["修繕費集計<br/>(オーナー負担分)"]
CalcWithholding["源泉徴収計算<br/>(個人非居住者: 20.42%)"]
CalcOther["その他控除計算"]
CalcNet["送金額計算<br/>(収入 - 控除合計)"]
CreateRemittance["送金データ作成"]
CreateItems["送金明細作成"]
end
SaveDB["DB保存"]
Notify["完了通知"]
End["バッチ終了"]
Start --> FetchOwners
FetchOwners --> Loop
Loop --> CalcIncome
CalcIncome --> CalcMgmtFee
CalcMgmtFee --> CalcMaintenance
CalcMaintenance --> CalcWithholding
CalcWithholding --> CalcOther
CalcOther --> CalcNet
CalcNet --> CreateRemittance
CreateRemittance --> CreateItems
CreateItems --> SaveDB
SaveDB --> Loop
Loop -->|"全オーナー完了"| Notify
Notify --> End 9.4.2 送金額計算ロジック 送金額 = 総賃料収入
- 管理手数料(収入 × 手数料率)
- 修繕費(オーナー負担分)
- 源泉徴収税(該当する場合)
- その他控除(広告費等) 9.4.3 源泉徴収計算 オーナー区分 源泉徴収率 備考 個人(居住者) 不要 不動産所得として本人が確定申告 法人 不要 法人が確定申告 個人(非居住者) 20.42% 支払時に源泉徴収義務あり
9.5 督促データ生成 9.5.1 督促段階 段階 滞納日数 対応 テンプレート Level 1 1-7日 催告(リマインド) 督促状_催告.pdf Level 2 8-30日 警告 督促状_警告.pdf Level 3 31-60日 最終通告 督促状_最終通告.pdf Level 4 61日以上 法的措置予告 督促状_法的措置予告.pdf
9.5.2 自動督促フロー 日次バッチ(滞納検出)
│
├── 未入金請求の検出(due_date < 今日)
├── 滞納日数の計算
├── 督促レベルの判定
├── 前回督促からの経過日数確認
│ └── 最低7日間隔を空ける
├── 督促データ生成
└── 通知(メール/SMS/ポータル) 9.6 バッチ基盤設計 9.6.1 ジョブキュー構成 // BullMQ ジョブキュー定義
const queues = {
// 優先度高: 即時性が求められるジョブ
'notification': new Queue('notification', { redis }),
'document-generation': new Queue('document-generation', { redis }),
// 優先度中: 定期バッチ
'monthly-billing': new Queue('monthly-billing', { redis }),
'reconciliation': new Queue('reconciliation', { redis }),
'owner-remittance': new Queue('owner-remittance', { redis }),
// 優先度低: レポート生成等
'report-generation': new Queue('report-generation', { redis }),
'data-export': new Queue('data-export', { redis }),
};
// ジョブオプション
const defaultJobOptions: JobOptions = {
attempts: 3, // リトライ回数
backoff: {
type: 'exponential', // 指数バックオフ
delay: 5000, // 初回5秒
},
removeOnComplete: {
count: 1000, // 完了ジョブ保持数
},
removeOnFail: {
count: 5000, // 失敗ジョブ保持数
},
}; 9.6.2 スケジュール定義 ジョブ cron式 説明 monthly-billing `0 0 1 * *` 毎月1日 0:00 daily-reconciliation-check `0 9 * * *` 毎日 9:00 delinquency-detection `0 10 * * *` 毎日 10:00 renewal-alert `0 8 * * 1` 毎週月曜 8:00 key-lending-alert `0 8 * * *` 毎日 8:00 owner-remittance `0 0 25 * *` 毎月25日 0:00 journal-entry-sync `0 2 1 * *` 毎月1日 2:00 annual-tax-statement `0 0 10 1 *` 毎年1月10日 0:00 data-cleanup `0 3 * * 0` 毎週日曜 3:00
9.6.3 テナント別バッチ実行 バッチ処理はテナント単位で実行し、テナント間の影響を分離する。
async function executeMonthlyBilling() {
const organizations = await prisma.organization.findMany({
where: { status: 'active' },
});
for (const org of organizations) {
await monthlyBillingQueue.add(
'generate',
{
organizationId: org.id,
billingMonth: getNextBillingMonth(),
},
{
...defaultJobOptions,
priority: getPriorityByPlan(org.planId),
}
);
}
} 9.6.4 バッチ監視 監視項目 アラート条件 通知先 ジョブ失敗 失敗回数 > 0 Slack(運用チャンネル) 処理時間超過 SLA超過(5分以上) PagerDuty キュー滞留 待機ジョブ > 100件 CloudWatch Alarm ワーカー異常 ワーカー停止 PagerDuty
10. 非機能要件への対応方針 10.1 パフォーマンス対応 10.1.1 フロントエンド最適化 施策 対象 効果 SSR/SSG ダッシュボード、一覧ページ 初期表示3秒以内 コード分割 ルート単位でのlazy import バンドルサイズ削減 画像最適化 next/image + WebP変換 画像転送量削減 仮想スクロール TanStack Virtual 大量リスト表示の高速化 楽観的更新 TanStack Query 体感速度向上 スケルトンUI 全データ読込画面 知覚的な速度改善 CDNキャッシュ CloudFront 静的アセットの配信高速化
10.1.2 バックエンド最適化 施策 対象 効果 クエリ最適化 Prisma includeの最小化 N+1クエリ防止 インデックス設計 検索対象カラム 検索応答1秒以内 Redis キャッシュ マスタデータ、テナント設定 DB負荷軽減 接続プーリング PgBouncer DB接続管理 レスポンス圧縮 gzip/brotli 通信量削減 ページネーション Cursor-based 大量データの効率的な取得 バックグラウンド処理 BullMQ 重い処理の非同期化
10.1.3 データベース最適化 施策 詳細 パーティショニング audit_log: 月次パーティション、payment: 年次パーティション 全文検索 pg_trgm拡張による建物名・入居者名の部分一致検索 マテリアライズドビュー ダッシュボード用集計データ(15分間隔リフレッシュ) Read Replica レポート・分析クエリの分離 VACUUM設定 autovacuum_naptime: 30s(大量更新テーブル)
10.2 セキュリティ対応 10.2.1 多層防御アーキテクチャ graph TB
subgraph "Layer 1: ネットワーク"
WAF["AWS WAF<br/>(OWASP Top 10)"]
SG["Security Groups"]
VPC["VPC<br/>(Private Subnet)"]
end
subgraph "Layer 2: エッジ"
CF_Shield["CloudFront + Shield<br/>(DDoS保護)"]
RateLimit["レート制限<br/>(1,000 req/min/tenant)"]
end
subgraph "Layer 3: アプリケーション"
CSP["Content-Security-Policy"]
CSRF_Token["CSRFトークン"]
InputSanitize["入力サニタイズ"]
ParamBinding["パラメータバインディング"]
end
subgraph "Layer 4: データ"
TLS["TLS 1.3"]
KMS["AWS KMS<br/>(AES-256暗号化)"]
RLS_Security["Row Level Security"]
EncryptedCol["暗号化カラム<br/>(PII)"]
end
subgraph "Layer 5: 監査"
AuditLog_Security["監査ログ<br/>(イミュータブル)"]
AccessLog["アクセスログ"]
SecurityAlert["セキュリティアラート"]
end
WAF --> CF_Shield
CF_Shield --> RateLimit
RateLimit --> CSP
CSP --> TLS
TLS --> AuditLog_Security 10.2.2 OWASP Top 10 対応 脅威 対策 A01: アクセス制御の不備 RBAC + RLS + テナント分離 A02: 暗号化の失敗 TLS 1.3、AES-256、KMS管理 A03: インジェクション Prisma(パラメータバインディング)、Zod(入力バリデーション) A04: 安全でない設計 脅威モデリング、セキュリティレビュー A05: セキュリティ設定ミス CDK/Terraform(IaC)、Security Hub A06: 脆弱なコンポーネント Dependabot、npm audit自動化 A07: 認証の不備 bcrypt、TOTP、アカウントロック A08: ソフトウェアとデータの整合性 CI/CDパイプラインのセキュリティ、SBOMPrisma A09: セキュリティログの不足 全操作の監査ログ、1年保持 A10: SSRF 外部URL検証、許可リスト方式
10.2.3 データ暗号化 レベル 方式 対象 通信暗号化 TLS 1.3 全通信 保存時暗号化(ディスク) AWS RDS暗号化(AES-256) DB全体 保存時暗号化(カラム) アプリケーションレベル暗号化(KMS) PII(メール、電話番号、口座番号等) ファイル暗号化 S3 SSE-KMS アップロードファイル全般 シークレット Secrets Manager APIキー、DB接続情報
10.3 スケーラビリティ対応 10.3.1 水平スケーリング設計 graph TB
subgraph "Auto Scaling"
ECS_ASG["ECS Service<br/>Auto Scaling<br/>Min: 2, Max: 20"]
Worker_ASG["Worker Service<br/>Auto Scaling<br/>Min: 1, Max: 10"]
end
subgraph "スケーリングポリシー"
CPU_Policy["CPU使用率 > 70%<br/>→ スケールアウト"]
Memory_Policy["メモリ使用率 > 80%<br/>→ スケールアウト"]
Request_Policy["リクエスト数 > 500/min<br/>→ スケールアウト"]
Queue_Policy["キュー深度 > 100<br/>→ ワーカースケールアウト"]
end
CPU_Policy --> ECS_ASG
Memory_Policy --> ECS_ASG
Request_Policy --> ECS_ASG
Queue_Policy --> Worker_ASG 10.3.2 スケーリング目標値 フェーズ テナント数 管理戸数 App Instance Worker Instance RDS Redis 初期 10 5,000 2 1 db.r6g.large cache.r6g.large 成長期 100 50,000 4 2 db.r6g.xlarge cache.r6g.xlarge 成熟期 500 250,000 8 4 db.r6g.2xlarge cache.r6g.2xlarge 最大 1,000 500,000 20 10 db.r6g.4xlarge cache.r6g.4xlarge
10.3.3 データベーススケーリング戦略 戦略 実施タイミング 詳細 Read Replica追加 テナント100超過時 レポートクエリの分離 テーブルパーティショニング データ量増大時 audit_log, payment テーブル コネクションプーリング 初期から PgBouncer導入 シャーディング検討 テナント500超過時 テナント単位のDBシャーディング
10.4 可用性対応 10.4.1 冗長化構成 コンポーネント 冗長化方式 詳細 ECS Task マルチAZ配置 最低2AZに分散 RDS Multi-AZ 自動フェイルオーバー Redis ElastiCache Cluster Multi-AZ、自動フェイルオーバー S3 標準ストレージ 99.999999999%耐久性 CloudFront グローバルエッジ エッジロケーション自動分散
10.4.2 バックアップ・リカバリ 対象 バックアップ方式 頻度 保持期間 RTO RPO RDS 自動スナップショット 日次 30日 4h 1h RDS リアルタイム 継続的(PITR) 35日 15min 5min S3 バージョニング リアルタイム 90日 即時 0 Redis スナップショット 日次 7日 15min 1h
10.4.3 障害対応フロー graph TB
Detect["障害検知<br/>(CloudWatch Alarm)"]
Notify["通知<br/>(PagerDuty/Slack)"]
AutoHeal["自動復旧<br/>(ECS/RDS フェイルオーバー)"]
Manual["手動介入<br/>(オンコールエンジニア)"]
RCA["根本原因分析"]
Fix["恒久対策"]
PostMortem["ポストモーテム"]
Detect --> Notify
Detect --> AutoHeal
AutoHeal -->|"復旧OK"| RCA
AutoHeal -->|"復旧NG"| Manual
Manual --> RCA
RCA --> Fix
Fix --> PostMortem 10.5 運用監視対応 10.5.1 監視項目 カテゴリ メトリクス アラート閾値 通知先 インフラ CPU使用率 > 80% (5min) Slack インフラ メモリ使用率 > 85% (5min) Slack インフラ ディスク使用率 > 80% Slack アプリケーション エラーレート > 1% (5min) PagerDuty アプリケーション レスポンスタイム P95 > 2s (5min) Slack アプリケーション レスポンスタイム P99 > 5s (5min) PagerDuty データベース 接続数 > 80% of max Slack データベース レプリケーション遅延 > 10s PagerDuty データベース デッドロック発生 > 0 Slack Redis メモリ使用率 > 80% Slack Redis 接続数 > 80% of max Slack バッチ ジョブ失敗 > 0 Slack バッチ キュー滞留 > 100件 Slack セキュリティ ログイン失敗 > 100/h (同一IP) PagerDuty ビジネス 滞納率 > 10% (テナント) Slack(テナント管理者)
10.5.2 ログ管理 ログ種別 出力先 保持期間 アプリケーションログ CloudWatch Logs 90日(S3に長期保存: 1年) アクセスログ CloudWatch Logs 90日 監査ログ PostgreSQL + S3 1年(DB)+ 5年(S3) エラーログ Sentry 90日 バッチ実行ログ CloudWatch Logs 90日 セキュリティログ CloudWatch Logs + S3 1年
10.5.3 デプロイ戦略 graph LR
Dev["開発環境<br/>(feature branch)"]
Staging["ステージング環境<br/>(main branch)"]
Prod["本番環境<br/>(release tag)"]
Dev -->|"PR マージ"| Staging
Staging -->|"テスト通過<br/>+ 手動承認"| Prod
subgraph "本番デプロイ"
Blue["Blue (現行)"]
Green["Green (新版)"]
Switch["トラフィック<br/>切替"]
end
Prod --> Green
Green -->|"ヘルスチェック OK"| Switch
Switch -->|"100%"| Green
Blue -->|"ロールバック可"| Switch 項目 仕様 デプロイ方式 ブルーグリーンデプロイ(ECS) ゼロダウンタイム ALBのターゲットグループ切替 ロールバック 1コマンドで前バージョンに切替 DBマイグレーション ゼロダウンタイム(expand-contract パターン) カナリアリリース 必要に応じて段階的トラフィック移行
10.6 テスト戦略 テスト種別 ツール カバレッジ目標 実行タイミング ユニットテスト Vitest 80%以上 PR作成時 統合テスト Vitest + Supertest 主要API100% PR作成時 E2Eテスト Playwright 主要フロー100% ステージングデプロイ後 パフォーマンステスト k6 レスポンスタイムSLA 週次 セキュリティテスト OWASP ZAP OWASP Top 10 月次 負荷テスト k6 100同時接続/テナント リリース前
付録 付録A: 料金体系 プラン 管理戸数上限 月額(税抜) 初期費用 スタータープラン 100戸 9,800円 0円 スタンダードプラン 500戸 29,800円 0円 プロフェッショナルプラン 2,000戸 59,800円 0円 エンタープライズプラン 無制限 個別見積 個別見積
オプション 月額(税抜) オーナーポータル +5,000円 入居者ポータル +5,000円 電子契約連携 +3,000円 AI消込アシスト +5,000円 カスタムドメイン +3,000円 追加ストレージ(10GB単位) +1,000円
付録B: 用語集 用語 説明 テナント PropCloudの契約者(販売パートナーまたは管理会社) 組織 (Organization) テナントのシステム上の表現 RLS Row Level Security。PostgreSQLの行レベルセキュリティ機能 消込 入金データと請求データのマッチング処理 全銀フォーマット 全国銀行協会が定める銀行間データ交換フォーマット 総合振込 複数の振込先への一括振込 口座振替 借主の銀行口座から自動引き落としする決済方法 源泉徴収 支払者が支払時に所得税を差し引いて納付する制度 支払調書 特定の支払いについて税務署に提出する法定調書 ホワイトラベル 製品を他社ブランドで販売できる仕組み RBAC Role-Based Access Control。ロールベースのアクセス制御 tRPC TypeSafe Remote Procedure Call。型安全なAPI通信ライブラリ
付録C: 画面遷移図 graph TD
Login["ログイン画面"]
TOTP["二段階認証画面"]
ForgotPW["パスワードリセット"]
Dashboard["ダッシュボード"]
Login --> TOTP
Login --> ForgotPW
TOTP --> Dashboard
Login --> Dashboard
subgraph "物件管理"
BuildingList["建物一覧"]
BuildingCreate["建物登録"]
BuildingDetail["建物詳細"]
BuildingEdit["建物編集"]
UnitList["区画一覧"]
UnitCreate["区画登録"]
UnitDetail["区画詳細"]
UnitEdit["区画編集"]
ParkingList["駐車場一覧"]
end
Dashboard --> BuildingList
BuildingList --> BuildingCreate
BuildingList --> BuildingDetail
BuildingDetail --> BuildingEdit
BuildingDetail --> UnitList
UnitList --> UnitCreate
UnitList --> UnitDetail
UnitDetail --> UnitEdit
BuildingDetail --> ParkingList
subgraph "契約管理"
ContractList["契約一覧"]
ContractCreate["新規契約"]
ContractQuick["簡易契約登録"]
ContractDetail["契約詳細"]
ContractEdit["契約編集"]
RenewalList["更新対象一覧"]
RenewalCreate["更新契約作成"]
TerminationCreate["解約処理"]
SettlementPreview["精算書プレビュー"]
end
Dashboard --> ContractList
ContractList --> ContractCreate
ContractList --> ContractQuick
ContractList --> ContractDetail
ContractDetail --> ContractEdit
ContractList --> RenewalList
RenewalList --> RenewalCreate
ContractDetail --> TerminationCreate
TerminationCreate --> SettlementPreview
subgraph "入出金管理"
InvoiceList["請求一覧"]
InvoiceDetail["請求詳細"]
InvoiceCreate["請求手動作成"]
PaymentList["入金一覧"]
PaymentImport["全銀データ取込"]
ReconciliationView["消込処理"]
DunningList["督促管理"]
DunningDetail["督促詳細"]
RemittanceList["送金一覧"]
RemittanceDetail["送金詳細"]
RemittanceExport["全銀データ出力"]
end
Dashboard --> InvoiceList
InvoiceList --> InvoiceDetail
InvoiceList --> InvoiceCreate
Dashboard --> PaymentList
PaymentList --> PaymentImport
PaymentList --> ReconciliationView
Dashboard --> DunningList
DunningList --> DunningDetail
Dashboard --> RemittanceList
RemittanceList --> RemittanceDetail
RemittanceList --> RemittanceExport
subgraph "オーナー管理"
OwnerList["オーナー一覧"]
OwnerCreate["オーナー登録"]
OwnerDetail["オーナー詳細"]
OwnerEdit["オーナー編集"]
OwnerReport["オーナーレポート"]
end
Dashboard --> OwnerList
OwnerList --> OwnerCreate
OwnerList --> OwnerDetail
OwnerDetail --> OwnerEdit
OwnerDetail --> OwnerReport
subgraph "入居者管理"
TenantList["入居者一覧"]
TenantCreate["入居者登録"]
TenantDetail["入居者詳細"]
TenantEdit["入居者編集"]
end
Dashboard --> TenantList
TenantList --> TenantCreate
TenantList --> TenantDetail
TenantDetail --> TenantEdit
subgraph "修繕管理"
TicketKanban["チケットボード"]
TicketTable["チケット一覧"]
TicketCreate["チケット作成"]
TicketDetail["チケット詳細"]
EstimateCreate["見積登録"]
VendorList["業者一覧"]
VendorCreate["業者登録"]
KeyMgmt["鍵管理"]
InspectionList["定期点検一覧"]
end
Dashboard --> TicketKanban
TicketKanban --> TicketTable
TicketKanban --> TicketCreate
TicketKanban --> TicketDetail
TicketDetail --> EstimateCreate
TicketKanban --> VendorList
VendorList --> VendorCreate
Dashboard --> KeyMgmt
Dashboard --> InspectionList
subgraph "帳票・ドキュメント"
TemplateList["テンプレート一覧"]
TemplateEdit["テンプレート編集"]
DocList["生成済み帳票"]
DocPreview["帳票プレビュー"]
FileManager["ファイル管理"]
end
Dashboard --> TemplateList
TemplateList --> TemplateEdit
Dashboard --> DocList
DocList --> DocPreview
Dashboard --> FileManager
subgraph "設定"
OrgSettings["組織設定"]
BrandSettings["ブランディング設定"]
BrandPreview["テーマプレビュー"]
UserMgmt["ユーザー管理"]
UserInvite["ユーザー招待"]
RoleMgmt["ロール管理"]
RoleEdit["ロール編集"]
AuditLog["監査ログ"]
NotificationSettings["通知設定"]
IntegrationSettings["外部連携設定"]
end
Dashboard --> OrgSettings
OrgSettings --> BrandSettings
BrandSettings --> BrandPreview
OrgSettings --> UserMgmt
UserMgmt --> UserInvite
OrgSettings --> RoleMgmt
RoleMgmt --> RoleEdit
OrgSettings --> AuditLog
OrgSettings --> NotificationSettings
OrgSettings --> IntegrationSettings 付録D: 業務フロー詳細図 D.1 募集〜入居フロー graph TD
Start["開始"]
RegisterBuilding["物件登録"]
RegisterUnit["区画登録"]
SetRecruitment["募集条件設定"]
ChangeStatus["ステータス: 募集中"]
ReceiveApplication["申込受付"]
Screening["審査"]
ScreeningResult{審査結果}
Reject["不承認通知"]
CreateContract["契約書作成"]
CreateImportantDoc["重要事項説明書作成"]
EContract{電子契約?}
PaperSign["書面契約・署名"]
ESign["電子契約送信"]
ESignWait["署名待ち"]
CalcInitialCost["初期費用計算"]
CreateInvoice["初期費用請求"]
ConfirmPayment["入金確認"]
KeyHandover["鍵引渡"]
MoveIn["入居処理"]
StatusOccupied["ステータス: 入居中"]
End["完了"]
Start --> RegisterBuilding
RegisterBuilding --> RegisterUnit
RegisterUnit --> SetRecruitment
SetRecruitment --> ChangeStatus
ChangeStatus --> ReceiveApplication
ReceiveApplication --> Screening
Screening --> ScreeningResult
ScreeningResult -->|"不承認"| Reject
ScreeningResult -->|"承認"| CreateContract
Reject --> End
CreateContract --> CreateImportantDoc
CreateImportantDoc --> EContract
EContract -->|"Yes"| ESign
EContract -->|"No"| PaperSign
ESign --> ESignWait
ESignWait --> CalcInitialCost
PaperSign --> CalcInitialCost
CalcInitialCost --> CreateInvoice
CreateInvoice --> ConfirmPayment
ConfirmPayment --> KeyHandover
KeyHandover --> MoveIn
MoveIn --> StatusOccupied
StatusOccupied --> End D.2 月次業務フロー graph TD
MonthStart["月初"]
subgraph "請求処理(1日〜5日)"
GenInvoice["月次請求データ<br/>自動生成"]
ReviewInvoice["請求データ確認"]
AdjustInvoice["変動費調整"]
GenDirectDebit["口座振替データ<br/>生成"]
SendDirectDebit["口座振替データ<br/>送信"]
SendInvoice["請求書送付<br/>(メール/郵送)"]
end
subgraph "入金処理(15日〜20日)"
ImportPayment["入金データ取込<br/>(全銀)"]
AutoReconcile["自動消込実行"]
ManualReconcile["手動消込<br/>(未マッチ分)"]
ConfirmReconcile["消込確認"]
end
subgraph "滞納・督促(20日〜25日)"
DetectDelinquency["滞納検出"]
ClassifyDelinquency["滞納分類<br/>(日数別)"]
GenDunning["督促データ生成"]
SendDunning["督促状送付"]
RecordDunning["督促履歴記録"]
end
subgraph "オーナー送金(25日〜月末)"
CalcRemittance["送金額集計"]
ReviewRemittance["送金データ確認"]
ConfirmRemittance["送金データ確定"]
GenZengin["全銀データ<br/>(総合振込)出力"]
ExecuteTransfer["振込実行"]
GenStatement["送金明細書生成"]
SendStatement["明細書送付"]
end
subgraph "月次レポート(月末)"
GenMonthlyReport["月次レポート生成"]
GenOwnerReport["オーナー向け<br/>月次報告書生成"]
GenJournalEntry["仕訳データ生成"]
SyncAccounting["会計ソフト連携"]
end
MonthStart --> GenInvoice
GenInvoice --> ReviewInvoice
ReviewInvoice --> AdjustInvoice
AdjustInvoice --> GenDirectDebit
GenDirectDebit --> SendDirectDebit
ReviewInvoice --> SendInvoice
SendDirectDebit --> ImportPayment
ImportPayment --> AutoReconcile
AutoReconcile --> ManualReconcile
ManualReconcile --> ConfirmReconcile
ConfirmReconcile --> DetectDelinquency
DetectDelinquency --> ClassifyDelinquency
ClassifyDelinquency --> GenDunning
GenDunning --> SendDunning
SendDunning --> RecordDunning
ConfirmReconcile --> CalcRemittance
CalcRemittance --> ReviewRemittance
ReviewRemittance --> ConfirmRemittance
ConfirmRemittance --> GenZengin
GenZengin --> ExecuteTransfer
ConfirmRemittance --> GenStatement
GenStatement --> SendStatement
ExecuteTransfer --> GenMonthlyReport
SendStatement --> GenOwnerReport
GenMonthlyReport --> GenJournalEntry
GenJournalEntry --> SyncAccounting D.3 解約〜退去フロー graph TD
ReceiveTermination["解約申請受付"]
RegisterTermination["解約情報登録<br/>(解約日・退去日)"]
NotifyOwner["オーナーへ通知"]
ScheduleInspection["退去立会い<br/>日程調整"]
Inspection["退去立会い実施"]
Checklist["チェックリスト<br/>記入"]
EstimateRepair["原状回復<br/>見積取得"]
OwnerApproval{オーナー<br/>承認}
ReviseEstimate["見積修正"]
CalcSettlement["精算計算"]
SettlementItems["精算項目確定"]
CreateSettlement["精算書作成"]
DepositReturn{敷金返還<br/>あり?}
ReturnDeposit["敷金返還<br/>振込処理"]
AdditionalCharge["追加請求<br/>処理"]
UpdateUnitStatus["区画ステータス<br/>更新: 空室"]
UpdateContractStatus["契約ステータス<br/>更新: 解約済"]
StartRenovation{リフォーム<br/>必要?}
Renovation["リフォーム実施"]
RestartRecruitment["募集再開"]
End["完了"]
ReceiveTermination --> RegisterTermination
RegisterTermination --> NotifyOwner
NotifyOwner --> ScheduleInspection
ScheduleInspection --> Inspection
Inspection --> Checklist
Checklist --> EstimateRepair
EstimateRepair --> OwnerApproval
OwnerApproval -->|"承認"| CalcSettlement
OwnerApproval -->|"差戻し"| ReviseEstimate
ReviseEstimate --> OwnerApproval
CalcSettlement --> SettlementItems
SettlementItems --> CreateSettlement
CreateSettlement --> DepositReturn
DepositReturn -->|"Yes"| ReturnDeposit
DepositReturn -->|"No"| AdditionalCharge
ReturnDeposit --> UpdateUnitStatus
AdditionalCharge --> UpdateUnitStatus
UpdateUnitStatus --> UpdateContractStatus
UpdateContractStatus --> StartRenovation
StartRenovation -->|"Yes"| Renovation
StartRenovation -->|"No"| RestartRecruitment
Renovation --> RestartRecruitment
RestartRecruitment --> End D.4 修繕フロー graph TD
Receive["問い合わせ受付"]
ClassifySource{受付経路}
Phone["電話"]
Email["メール"]
Portal["ポータル"]
Chat["チャット"]
CreateTicket["チケット作成"]
Classify["カテゴリ分類"]
AssignPriority["優先度設定"]
AssignStaff["担当者アサイン"]
InitialResponse["初期対応<br/>(入居者への連絡)"]
SiteVisit{現地確認<br/>必要?}
Visit["現地確認実施"]
Assessment["状況アセスメント"]
GetEstimate["見積取得"]
CompareEstimate["見積比較<br/>(複数業者)"]
DetermineCostBearer{費用負担者<br/>判定}
OwnerBear["オーナー負担"]
TenantBear["入居者負担"]
CompanyBear["管理会社負担"]
RequestOwnerApproval["オーナー承認依頼"]
OwnerDecision{承認?}
Approved["承認済"]
Denied["却下/再検討"]
OrderVendor["業者発注"]
ScheduleWork["工事日程調整"]
ExecuteWork["工事実施"]
ConfirmCompletion["完了確認"]
RecordCost["費用計上"]
NotifyStakeholders["関係者通知"]
CloseTicket["チケットクローズ"]
Receive --> ClassifySource
ClassifySource --> Phone
ClassifySource --> Email
ClassifySource --> Portal
ClassifySource --> Chat
Phone --> CreateTicket
Email --> CreateTicket
Portal --> CreateTicket
Chat --> CreateTicket
CreateTicket --> Classify
Classify --> AssignPriority
AssignPriority --> AssignStaff
AssignStaff --> InitialResponse
InitialResponse --> SiteVisit
SiteVisit -->|"Yes"| Visit
SiteVisit -->|"No"| GetEstimate
Visit --> Assessment
Assessment --> GetEstimate
GetEstimate --> CompareEstimate
CompareEstimate --> DetermineCostBearer
DetermineCostBearer --> OwnerBear
DetermineCostBearer --> TenantBear
DetermineCostBearer --> CompanyBear
OwnerBear --> RequestOwnerApproval
RequestOwnerApproval --> OwnerDecision
OwnerDecision -->|"承認"| Approved
OwnerDecision -->|"却下"| Denied
Denied --> GetEstimate
Approved --> OrderVendor
TenantBear --> OrderVendor
CompanyBear --> OrderVendor
OrderVendor --> ScheduleWork
ScheduleWork --> ExecuteWork
ExecuteWork --> ConfirmCompletion
ConfirmCompletion --> RecordCost
RecordCost --> NotifyStakeholders
NotifyStakeholders --> CloseTicket 付録E: エラーコード一覧 E.1 認証エラー コード HTTPステータス メッセージ 対処法 AUTH_001 401 認証に失敗しました メール/パスワードを確認 AUTH_002 401 セッションが期限切れです 再ログイン AUTH_003 401 二段階認証コードが無効です コードを再確認 AUTH_004 403 アカウントがロックされています 30分後に再試行 AUTH_005 403 アカウントが無効化されています 管理者に連絡 AUTH_006 403 テナントが停止中です プラットフォーム管理者に連絡
E.2 権限エラー コード HTTPステータス メッセージ 対処法 PERM_001 403 この操作を実行する権限がありません 管理者に権限付与を依頼 PERM_002 403 このリソースへのアクセス権がありません テナント管理者に確認 PERM_003 403 この機能は現在のプランでは利用できません プランのアップグレードを検討
E.3 バリデーションエラー コード HTTPステータス メッセージ 対処法 VALID_001 400 必須項目が未入力です 必須項目を入力 VALID_002 400 入力形式が正しくありません フォーマットを確認 VALID_003 400 値が範囲外です 有効範囲を確認 VALID_004 400 重複するデータが存在します 既存データを確認 VALID_005 400 日付の前後関係が不正です 開始日/終了日を確認
E.4 ビジネスロジックエラー コード HTTPステータス メッセージ 対処法 BIZ_001 409 この区画には既に有効な契約があります 既存契約を確認 BIZ_002 409 消込対象の請求が見つかりません 請求データを確認 BIZ_003 409 送金データが既に確定済みです 確定取消が必要 BIZ_004 409 テンプレートが他のテナントで使用中です 別テンプレートを使用 BIZ_005 422 契約期間が建物の管理委託期間外です 管理委託契約を確認 BIZ_006 422 オーナーの振込先口座が未登録です 口座情報を登録 BIZ_007 422 ストレージ容量の上限に達しました プランのアップグレードまたは不要ファイルの削除
E.5 外部連携エラー コード HTTPステータス メッセージ 対処法 EXT_001 502 電子契約サービスとの通信に失敗しました 再試行またはサービス状態を確認 EXT_002 502 メール送信に失敗しました 送信先アドレスを確認、再送 EXT_003 422 全銀フォーマットのパースに失敗しました ファイル形式を確認 EXT_004 502 会計ソフトとの同期に失敗しました 連携設定を確認
E.6 システムエラー コード HTTPステータス メッセージ 対処法 SYS_001 500 内部サーバーエラーが発生しました サポートに連絡 SYS_002 503 サービスが一時的に利用できません しばらく後に再試行 SYS_003 429 リクエスト数の上限に達しました しばらく後に再試行 SYS_004 504 処理がタイムアウトしました データ量を減らして再試行
付録F: データバリデーション規則 F.1 共通バリデーション フィールド型 バリデーション規則 メールアドレス RFC 5322準拠、最大255文字 電話番号 半角数字・ハイフンのみ、10〜15文字 郵便番号 半角数字7文字(ハイフンなし)または半角数字3文字-半角数字4文字 金額 0以上の整数(小数点不可) 日付 YYYY-MM-DD形式、1900-01-01〜2099-12-31 UUID v4形式 URL https://で始まるURL、最大512文字 テキスト(短文) 最大255文字 テキスト(長文) 最大10,000文字 ファイル名 最大255文字、禁止文字: / \ : * ? " < > \
F.2 ドメイン固有バリデーション フィールド バリデーション規則 建物名 1〜255文字、空白のみ不可 部屋番号 1〜20文字、英数字・ハイフン・スラッシュ 賃料 0〜99,999,999円 敷金・礼金(ヶ月) 0〜24ヶ月、0.5刻み 面積(平米) 0.01〜9999.99 契約期間 開始日 < 終了日、最大10年 管理手数料率 0〜100%、小数第2位まで パスワード 12文字以上、英大文字・小文字・数字・記号各1文字以上 組織スラグ 3〜50文字、英数字・ハイフンのみ、先頭・末尾ハイフン不可 請求番号 自動採番、テナント内UNIQUE 契約番号 自動採番、テナント内UNIQUE
F.3 ファイルアップロードバリデーション 用途 許可拡張子 最大サイズ 備考 ロゴ画像 .svg, .png 2MB 推奨: 240x60px ファビコン .ico, .png 500KB 32x32px 建物画像 .jpg, .jpeg, .png, .webp 10MB 最大20枚/建物 修繕画像 .jpg, .jpeg, .png, .webp 10MB 最大10枚/チケット 契約書類 .pdf 20MB 電子帳簿保存法対応 全銀データ .txt, .dat 5MB Shift_JIS/UTF-8 CSVインポート .csv 10MB UTF-8推奨 Excelエクスポート .xlsx - 出力専用
付録G: 帳票テンプレート一覧(Phase 1: 20種) No テンプレート名 カテゴリ 出力形式 用途 1 賃貸借契約書 契約 PDF 新規契約時 2 重要事項説明書 契約 PDF 新規契約時 3 更新合意書 契約 PDF 更新契約時 4 解約通知書 契約 PDF 解約時 5 解約精算書 契約 PDF 退去精算時 6 退去立会い確認書 契約 PDF 退去立会い時 7 請求書 請求 PDF 月次請求 8 領収書 請求 PDF 入金確認時 9 督促状(催告) 督促 PDF 滞納Level 1 10 督促状(警告) 督促 PDF 滞納Level 2 11 督促状(最終通告) 督促 PDF 滞納Level 3 12 月次収支報告書 報告 PDF/Excel オーナー月次 13 年間収支報告書 報告 PDF/Excel オーナー年次 14 送金明細書 報告 PDF オーナー送金時 15 支払調書 税務 PDF 年次税務 16 家賃明細一覧表 管理 PDF/Excel 月次管理 17 入金一覧表 管理 PDF/Excel 月次管理 18 滞納一覧表 管理 PDF/Excel 督促管理 19 物件一覧表 管理 PDF/Excel 物件管理 20 空室一覧表 管理 PDF/Excel 募集管理
付録H: 環境変数一覧 変数名 必須 説明 例 DATABASE_URL YES PostgreSQL接続URL postgresql://user:pass@host:5432/db REDIS_URL YES Redis接続URL redis://host:6379 NEXTAUTH_SECRET YES セッション暗号化キー ランダム文字列(64文字以上) NEXTAUTH_URL YES アプリケーションURL https://app.propcloud.jp AWS_REGION YES AWSリージョン ap-northeast-1 AWS_S3_BUCKET YES S3バケット名 propcloud-files-prod AWS_SES_FROM_EMAIL YES SESデフォルト送信元 noreply@propcloud.jp AWS_KMS_KEY_ID YES KMS暗号化キーID arn:aws:kms:... SENTRY_DSN NO Sentry DSN https://xxx@sentry.io/xxx E_CONTRACT_API_KEY NO 電子契約APIキー Secrets Managerから取得 E_CONTRACT_API_URL NO 電子契約APIエンドポイント https://api.gmosign.com SLACK_WEBHOOK_URL NO Slack通知Webhook https://hooks.slack.com/...
付録I: パフォーマンスSLA詳細 操作 P50 P95 P99 最大 ダッシュボード表示 500ms 1.5s 3s 5s 一覧ページ表示(100件) 300ms 800ms 1.5s 3s 詳細ページ表示 200ms 500ms 1s 2s フォーム送信 200ms 500ms 1s 3s 検索実行 100ms 300ms 500ms 1s 全銀データ取込(1,000件) 3s 10s 20s 30s 自動消込(1,000件) 5s 30s 45s 60s 請求データ生成(5,000件) 1min 3min 4min 5min 帳票PDF生成(1件) 1s 3s 4s 5s 帳票一括生成(100件) 15s 40s 50s 60s CSVエクスポート(10,000件) 5s 15s 25s 30s オーナー送金集計(100オーナー) 5s 15s 25s 30s
付録J: セキュリティチェックリスト J.1 開発時チェック • [ ] 全てのユーザー入力をZodスキーマでバリデーション
• [ ] SQLインジェクション対策(Prismaパラメータバインディング確認)
• [ ] XSS対策(HTMLエスケープ、CSP設定確認)
• [ ] CSRF対策(CSRFトークン確認)
• [ ] 認証・認可チェックの漏れがないこと
• [ ] PIIデータの暗号化が実装されていること
• [ ] RLSポリシーが全テーブルに適用されていること
• [ ] 監査ログが重要操作で記録されていること
• [ ] エラーメッセージに内部情報が含まれていないこと
• [ ] デバッグコード・テストデータが本番コードに含まれていないこと
J.2 デプロイ時チェック • [ ] 環境変数にハードコードされた秘密情報がないこと
• [ ] Secrets Managerの設定が正しいこと
• [ ] SSL証明書の有効期限が十分あること
• [ ] Security Groupsの設定が最小権限であること
• [ ] WAFルールが有効であること
• [ ] ログ出力設定が適切であること
• [ ] バックアップ設定が有効であること
J.3 定期チェック(月次) • [ ] npm auditで脆弱性がないこと
• [ ] 依存パッケージのセキュリティアップデート適用
• [ ] ログインの異常検知(大量失敗等)
• [ ] 監査ログの異常検知
• [ ] ストレージ使用量の確認
• [ ] 証明書有効期限の確認
付録K: Zodバリデーションスキーマ定義 K.1 建物関連スキーマ import { z } from 'zod';
// 構造ENUM
const buildingStructureEnum = z.enum([
'RC', // 鉄筋コンクリート
'SRC', // 鉄骨鉄筋コンクリート
'S', // 鉄骨
'W', // 木造
'LGS', // 軽量鉄骨
'other', // その他
]);
// 管理形態ENUM
const managementTypeEnum = z.enum([
'self', // 自主管理
'collection_agency', // 集金代行
'sublease', // サブリース
'exclusive', // 専任管理
]);
// 最寄り駅スキーマ
const nearestStationSchema = z.object({
line: z.string().min(1).max(100),
station: z.string().min(1).max(100),
walkMinutes: z.number().int().min(0).max(60).nullable(),
busMinutes: z.number().int().min(0).max(60).nullable(),
});
// 共有設備スキーマ
const sharedFacilitySchema = z.object({
name: z.string().min(1).max(100),
available: z.boolean(),
type: z.string().max(100).optional(),
});
// 建物作成スキーマ
export const buildingCreateSchema = z.object({
name: z.string().min(1, '建物名は必須です').max(255),
postalCode: z.string().regex(/^\d{3}-?\d{4}$/, '郵便番号の形式が不正です'),
prefecture: z.string().min(1).max(10),
city: z.string().min(1).max(100),
address1: z.string().min(1).max(255),
address2: z.string().max(255).optional(),
structure: buildingStructureEnum,
constructionDate: z.coerce.date().optional(),
totalUnits: z.number().int().min(1, '総戸数は1以上').max(10000),
floorsAbove: z.number().int().min(1).max(100).optional(),
floorsBelow: z.number().int().min(0).max(10).optional(),
ownerId: z.string().uuid().optional(),
managementType: managementTypeEnum,
managementFeeRate: z.number().min(0).max(100).optional(),
sharedFacilities: z.array(sharedFacilitySchema).default([]),
nearestStations: z.array(nearestStationSchema).default([]),
notes: z.string().max(10000).optional(),
});
// 建物更新スキーマ
export const buildingUpdateSchema = buildingCreateSchema.partial();
// 建物一覧取得スキーマ
export const buildingListSchema = z.object({
cursor: z.string().optional(),
limit: z.number().int().min(1).max(100).default(25),
search: z.string().max(255).optional(),
prefecture: z.string().max(10).optional(),
managementType: managementTypeEnum.optional(),
ownerId: z.string().uuid().optional(),
sortBy: z.enum(['name', 'createdAt', 'occupancyRate']).default('name'),
sortOrder: z.enum(['asc', 'desc']).default('asc'),
}); K.2 区画関連スキーマ // 区画ステータスENUM
const unitStatusEnum = z.enum([
'vacant', // 空室
'occupied', // 入居中
'leaving', // 退去予定
'recruiting', // 募集中
'suspended', // 募集停止
'renovating', // リフォーム中
]);
// 方位ENUM
const directionEnum = z.enum(['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']);
// 設備スキーマ
const facilitySchema = z.object({
name: z.string().min(1).max(100),
available: z.boolean(),
memo: z.string().max(255).optional(),
});
// 募集条件スキーマ
const recruitmentConditionsSchema = z.object({
petAllowed: z.boolean().default(false),
instrumentAllowed: z.boolean().default(false),
corporateContractAllowed: z.boolean().default(true),
smokingAllowed: z.boolean().default(false),
foreignerAllowed: z.boolean().default(true),
elderlyAllowed: z.boolean().default(true),
welfareAllowed: z.boolean().default(true),
maxOccupants: z.number().int().min(1).max(10).optional(),
notes: z.string().max(1000).optional(),
});
// 区画作成スキーマ
export const unitCreateSchema = z.object({
buildingId: z.string().uuid(),
unitNumber: z.string().min(1).max(20),
floor: z.number().int().min(-10).max(100).optional(),
layout: z.string().max(20).optional(), // 1K, 2LDK等
areaSqm: z.number().min(0.01).max(9999.99).optional(),
direction: directionEnum.optional(),
rent: z.number().int().min(0).max(99999999),
managementFee: z.number().int().min(0).max(9999999).default(0),
commonFee: z.number().int().min(0).max(9999999).default(0),
depositMonths: z.number().min(0).max(24).multipleOf(0.5).default(0),
keyMoneyMonths: z.number().min(0).max(24).multipleOf(0.5).default(0),
facilities: z.array(facilitySchema).default([]),
status: unitStatusEnum.default('vacant'),
availableFrom: z.coerce.date().optional(),
recruitmentConditions: recruitmentConditionsSchema.default({}),
notes: z.string().max(10000).optional(),
});
// 区画一覧取得スキーマ
export const unitListSchema = z.object({
cursor: z.string().optional(),
limit: z.number().int().min(1).max(100).default(25),
buildingId: z.string().uuid().optional(),
status: z.array(unitStatusEnum).optional(),
layoutType: z.string().optional(),
rentMin: z.number().int().min(0).optional(),
rentMax: z.number().int().min(0).optional(),
search: z.string().max(255).optional(),
sortBy: z.enum(['unitNumber', 'rent', 'areaSqm', 'status']).default('unitNumber'),
sortOrder: z.enum(['asc', 'desc']).default('asc'),
}); K.3 契約関連スキーマ // 契約種別ENUM
const contractTypeEnum = z.enum(['new', 'renewal', 'sublease']);
const contractorTypeEnum = z.enum(['individual', 'corporation']);
const contractStatusEnum = z.enum([
'draft', // 下書き
'active', // 契約中
'renewed', // 更新済み
'terminated', // 解約済み
'cancelled', // 取消
]);
// 連帯保証人スキーマ
const guarantorSchema = z.object({
name: z.string().min(1).max(255),
nameKana: z.string().max(255).optional(),
phone: z.string().regex(/^[\d-]{10,15}$/).optional(),
address: z.string().max(500).optional(),
relation: z.string().max(50).optional(),
maxGuaranteeAmount: z.number().int().min(0), // 極度額(民法改正対応)
});
// 特約事項スキーマ
const specialTermSchema = z.object({
title: z.string().min(1).max(255),
content: z.string().min(1).max(5000),
});
// 契約作成スキーマ
export const contractCreateSchema = z.object({
unitId: z.string().uuid(),
tenantId: z.string().uuid().optional(), // 新規入居者の場合は省略可
newTenant: z.object({
lastName: z.string().min(1).max(100),
firstName: z.string().min(1).max(100),
lastNameKana: z.string().max(100).optional(),
firstNameKana: z.string().max(100).optional(),
email: z.string().email().optional(),
phone: z.string().regex(/^[\d-]{10,15}$/).optional(),
mobilePhone: z.string().regex(/^[\d-]{10,15}$/).optional(),
postalCode: z.string().regex(/^\d{3}-?\d{4}$/).optional(),
address: z.string().max(500).optional(),
workplaceName: z.string().max(255).optional(),
workplacePhone: z.string().regex(/^[\d-]{10,15}$/).optional(),
emergencyContactName: z.string().max(255).optional(),
emergencyContactPhone: z.string().regex(/^[\d-]{10,15}$/).optional(),
emergencyContactRelation: z.string().max(50).optional(),
dateOfBirth: z.coerce.date().optional(),
gender: z.enum(['male', 'female', 'other', 'unspecified']).optional(),
}).optional(),
contractType: contractTypeEnum,
contractorType: contractorTypeEnum,
startDate: z.coerce.date(),
endDate: z.coerce.date(),
rent: z.number().int().min(0).max(99999999),
managementFee: z.number().int().min(0).max(9999999),
commonFee: z.number().int().min(0).max(9999999).default(0),
deposit: z.number().int().min(0).max(99999999),
keyMoney: z.number().int().min(0).max(99999999),
renewalFeeMonths: z.number().min(0).max(24).optional(),
guaranteeCompanyId: z.string().uuid().optional(),
guarantors: z.array(guarantorSchema).default([]),
specialTerms: z.array(specialTermSchema).default([]),
notes: z.string().max(10000).optional(),
}).refine(
(data) => data.startDate < data.endDate,
{ message: '契約開始日は終了日より前である必要があります', path: ['endDate'] }
).refine(
(data) => data.tenantId || data.newTenant,
{ message: '入居者IDまたは新規入居者情報のいずれかは必須です' }
);
// 解約スキーマ
export const contractTerminateSchema = z.object({
contractId: z.string().uuid(),
terminationDate: z.coerce.date(),
moveOutDate: z.coerce.date(),
terminationReason: z.string().max(1000).optional(),
}).refine(
(data) => data.terminationDate <= data.moveOutDate,
{ message: '解約日は退去日以前である必要があります', path: ['moveOutDate'] }
); K.4 入出金関連スキーマ // 入金方法ENUM
const paymentMethodEnum = z.enum([
'bank_transfer', // 銀行振込
'direct_debit', // 口座振替
'cash', // 現金
'credit_card', // クレジットカード
'other', // その他
]);
// 消込ステータスENUM
const reconciliationStatusEnum = z.enum([
'unmatched', // 未消込
'auto_matched', // 自動消込
'manual_matched', // 手動消込
'confirmed', // 確定済み
]);
// 手動入金登録スキーマ
export const paymentCreateSchema = z.object({
contractId: z.string().uuid().optional(),
invoiceId: z.string().uuid().optional(),
paymentType: z.enum(['income', 'expense']),
amount: z.number().int().min(1, '金額は1円以上'),
paymentDate: z.coerce.date(),
method: paymentMethodEnum,
payerName: z.string().max(255).optional(),
bankName: z.string().max(100).optional(),
bankBranch: z.string().max(100).optional(),
accountNumber: z.string().max(20).optional(),
notes: z.string().max(10000).optional(),
});
// 手動消込スキーマ
export const manualReconcileSchema = z.object({
paymentId: z.string().uuid(),
invoiceId: z.string().uuid(),
amount: z.number().int().min(1),
notes: z.string().max(1000).optional(),
});
// 全銀データ取込スキーマ
export const zenginImportSchema = z.object({
file: z.instanceof(File).refine(
(f) => f.size <= 5 * 1024 * 1024,
'全銀ファイルは5MB以内'
),
encoding: z.enum(['shift_jis', 'utf8']).default('shift_jis'),
}); K.5 修繕関連スキーマ // 修繕カテゴリENUM
const maintenanceCategoryEnum = z.enum([
'equipment', // 設備故障
'noise', // 騒音
'leak', // 漏水
'pest', // 害虫
'common_area', // 共用部
'other', // その他
]);
// 優先度ENUM
const priorityEnum = z.enum(['low', 'medium', 'high', 'urgent']);
// 修繕チケット作成スキーマ
export const maintenanceCreateSchema = z.object({
unitId: z.string().uuid().optional(),
buildingId: z.string().uuid().optional(),
tenantId: z.string().uuid().optional(),
title: z.string().min(1, 'タイトルは必須です').max(255),
description: z.string().min(1, '詳細説明は必須です').max(10000),
category: maintenanceCategoryEnum,
priority: priorityEnum.default('medium'),
source: z.enum(['phone', 'email', 'portal', 'chat', 'other']),
}).refine(
(data) => data.unitId || data.buildingId,
{ message: '区画IDまたは建物IDのいずれかは必須です' }
);
// 見積登録スキーマ
export const maintenanceEstimateSchema = z.object({
ticketId: z.string().uuid(),
vendorId: z.string().uuid(),
amount: z.number().int().min(0),
description: z.string().min(1).max(5000),
estimatedDays: z.number().int().min(1).max(365).optional(),
attachmentUrls: z.array(z.string().url()).default([]),
}); 付録L: tRPCルーター定義 L.1 ルートルーター構成 // server/trpc/router.ts
import { router } from './trpc';
import { buildingRouter } from './routers/building';
import { unitRouter } from './routers/unit';
import { contractRouter } from './routers/contract';
import { invoiceRouter } from './routers/invoice';
import { paymentRouter } from './routers/payment';
import { ownerRouter } from './routers/owner';
import { tenantRouter } from './routers/tenant';
import { maintenanceRouter } from './routers/maintenance';
import { remittanceRouter } from './routers/remittance';
import { dunningRouter } from './routers/dunning';
import { documentRouter } from './routers/document';
import { reportRouter } from './routers/report';
import { dashboardRouter } from './routers/dashboard';
import { userRouter } from './routers/user';
import { organizationRouter } from './routers/organization';
import { parkingRouter } from './routers/parking';
import { portalRouter } from './routers/portal';
export const appRouter = router({
building: buildingRouter,
unit: unitRouter,
contract: contractRouter,
invoice: invoiceRouter,
payment: paymentRouter,
owner: ownerRouter,
tenant: tenantRouter,
maintenance: maintenanceRouter,
remittance: remittanceRouter,
dunning: dunningRouter,
document: documentRouter,
report: reportRouter,
dashboard: dashboardRouter,
user: userRouter,
organization: organizationRouter,
parking: parkingRouter,
portal: portalRouter,
});
export type AppRouter = typeof appRouter; L.2 tRPCコンテキスト定義 // server/trpc/context.ts
import { inferAsyncReturnType } from '@trpc/server';
import { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { prisma } from '@/lib/prisma';
import { redis } from '@/lib/redis';
import { getSession } from '@/lib/auth';
import { resolveTenant } from '@/lib/tenant';
export async function createContext({ req, res }: CreateNextContextOptions) {
const session = await getSession(req);
const tenant = await resolveTenant(req);
// PostgreSQLセッション変数にテナントIDをセット
if (tenant) {
await prisma.$executeRawUnsafe(
`SET app.current_tenant_id = '${tenant.id}'`
);
}
return {
req,
res,
prisma,
redis,
session,
user: session?.user ?? null,
tenant,
auditLog: {
record: async (entry: AuditLogInput) => {
await prisma.auditLog.create({
data: {
organizationId: tenant?.id ?? '',
userId: session?.user?.id ?? '',
action: entry.action,
resourceType: entry.resourceType ?? '',
resourceId: entry.resourceId,
changes: entry.changes,
ipAddress: req.headers['x-forwarded-for']?.toString() ?? req.socket.remoteAddress,
userAgent: req.headers['user-agent'],
},
});
},
},
};
}
export type Context = inferAsyncReturnType<typeof createContext>; L.3 共通ミドルウェア // server/trpc/middleware.ts
import { TRPCError } from '@trpc/server';
import { middleware } from './trpc';
// 認証ミドルウェア
export const isAuthenticated = middleware(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'ログインが必要です',
});
}
if (!ctx.tenant) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'テナント情報が特定できません',
});
}
return next({
ctx: {
user: ctx.user,
tenant: ctx.tenant,
},
});
});
// テナントアクティブチェック
export const isTenantActive = middleware(async ({ ctx, next }) => {
if (ctx.tenant?.status !== 'active') {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'テナントが停止中またはトライアル期限切れです',
});
}
return next();
});
// 権限チェックミドルウェア
export const requirePermission = (permission: string) => {
return middleware(async ({ ctx, next }) => {
const hasPermission = ctx.user?.permissions?.includes(permission);
if (!hasPermission) {
throw new TRPCError({
code: 'FORBIDDEN',
message: `権限が不足しています: ${permission}`,
});
}
return next();
});
};
// フィーチャーフラグチェックミドルウェア
export const requireFeature = (featurePath: string) => {
return middleware(async ({ ctx, next }) => {
const flags = ctx.tenant?.featureFlags ?? {};
const parts = featurePath.split('.');
let current: any = flags;
for (const part of parts) {
current = current?.[part];
}
if (current !== true) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'この機能は現在のプランでは利用できません',
});
}
return next();
});
};
// レート制限ミドルウェア
export const rateLimit = (maxRequests: number, windowMs: number) => {
return middleware(async ({ ctx, next }) => {
const key = `ratelimit:${ctx.tenant?.id}:${ctx.user?.id}`;
const current = await ctx.redis.incr(key);
if (current === 1) {
await ctx.redis.expire(key, Math.ceil(windowMs / 1000));
}
if (current > maxRequests) {
throw new TRPCError({
code: 'TOO_MANY_REQUESTS',
message: 'リクエスト数の上限に達しました。しばらく後に再試行してください。',
});
}
return next();
});
}; 付録M: Prismaスキーマ(抜粋) // prisma/schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema", "postgresqlExtensions"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
extensions = [pgTrgm, uuid_ossp]
}
enum BuildingStructure {
RC
SRC
S
W
LGS
other
}
enum ManagementType {
self
collection_agency
sublease
exclusive
}
enum UnitStatus {
vacant
occupied
leaving
recruiting
suspended
renovating
}
enum ContractType {
new_contract
renewal
sublease
}
enum ContractorType {
individual
corporation
}
enum ContractStatus {
draft
active
renewed
terminated
cancelled
}
enum OwnerType {
individual
corporation
}
enum TaxCategory {
individual
corporation
non_resident
}
enum InvoiceStatus {
draft
issued
sent
partial_paid
paid
overdue
cancelled
}
enum PaymentType {
income
expense
}
enum PaymentMethod {
bank_transfer
direct_debit
cash
credit_card
other
}
enum PaymentStatus {
pending
completed
partial
overpaid
refunded
}
enum ReconciliationStatus {
unmatched
auto_matched
manual_matched
confirmed
}
enum MaintenanceCategory {
equipment
noise
leak
pest
common_area
other
}
enum Priority {
low
medium
high
urgent
}
enum MaintenanceStatus {
received
in_progress
waiting_estimate
waiting_approval
ordered
completed
closed
}
enum OrganizationStatus {
active
suspended
trial
}
model Organization {
id String @id @default(uuid()) @db.Uuid
name String @db.VarChar(255)
slug String @unique @db.VarChar(100)
planId String? @map("plan_id") @db.Uuid
logoUrl String? @map("logo_url") @db.VarChar(512)
faviconUrl String? @map("favicon_url") @db.VarChar(512)
themeConfig Json @default("{}") @map("theme_config")
featureFlags Json @default("{}") @map("feature_flags")
terminologyOverrides Json @default("{}") @map("terminology_overrides")
emailFromAddress String? @map("email_from_address") @db.VarChar(255)
emailFromName String? @map("email_from_name") @db.VarChar(255)
storageLimitMb Int @default(5120) @map("storage_limit_mb")
storageUsedMb Int @default(0) @map("storage_used_mb")
status OrganizationStatus @default(trial)
trialEndsAt DateTime? @map("trial_ends_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
buildings Building[]
units Unit[]
contracts Contract[]
owners Owner[]
tenants Tenant[]
invoices Invoice[]
payments Payment[]
users User[]
maintenanceTickets MaintenanceTicket[]
remittances Remittance[]
customDomains CustomDomain[]
documentTemplates DocumentTemplate[]
plan Plan? @relation(fields: [planId], references: [id])
@@map("organization")
}
model Building {
id String @id @default(uuid()) @db.Uuid
organizationId String @map("organization_id") @db.Uuid
ownerId String? @map("owner_id") @db.Uuid
name String @db.VarChar(255)
postalCode String @map("postal_code") @db.VarChar(10)
prefecture String @db.VarChar(10)
city String @db.VarChar(100)
address1 String @db.VarChar(255)
address2 String? @db.VarChar(255)
latitude Decimal? @db.Decimal(10, 7)
longitude Decimal? @db.Decimal(10, 7)
structure BuildingStructure
constructionDate DateTime? @map("construction_date") @db.Date
totalUnits Int @map("total_units")
floorsAbove Int? @map("floors_above")
floorsBelow Int? @map("floors_below")
sharedFacilities Json @default("[]") @map("shared_facilities")
nearestStations Json @default("[]") @map("nearest_stations")
managementType ManagementType @map("management_type")
managementFeeRate Decimal? @map("management_fee_rate") @db.Decimal(5, 2)
notes String? @db.Text
deletedAt DateTime? @map("deleted_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
organization Organization @relation(fields: [organizationId], references: [id])
owner Owner? @relation(fields: [ownerId], references: [id])
units Unit[]
images BuildingImage[]
parkingSpaces ParkingSpace[]
maintenanceTickets MaintenanceTicket[]
@@index([organizationId], name: "idx_building_org_id")
@@index([ownerId], name: "idx_building_owner_id")
@@index([prefecture, city], name: "idx_building_prefecture_city")
@@map("building")
}
model Unit {
id String @id @default(uuid()) @db.Uuid
buildingId String @map("building_id") @db.Uuid
organizationId String @map("organization_id") @db.Uuid
unitNumber String @map("unit_number") @db.VarChar(20)
floor Int?
layout String? @db.VarChar(20)
areaSqm Decimal? @map("area_sqm") @db.Decimal(8, 2)
direction String? @db.VarChar(2)
rent Decimal @db.Decimal(12, 0)
managementFee Decimal @default(0) @map("management_fee") @db.Decimal(12, 0)
commonFee Decimal @default(0) @map("common_fee") @db.Decimal(12, 0)
depositMonths Decimal @default(0) @map("deposit_months") @db.Decimal(4, 1)
keyMoneyMonths Decimal @default(0) @map("key_money_months") @db.Decimal(4, 1)
facilities Json @default("[]")
status UnitStatus @default(vacant)
availableFrom DateTime? @map("available_from") @db.Date
recruitmentConditions Json @default("{}") @map("recruitment_conditions")
notes String? @db.Text
deletedAt DateTime? @map("deleted_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
building Building @relation(fields: [buildingId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id])
contracts Contract[]
maintenanceTickets MaintenanceTicket[]
@@unique([buildingId, unitNumber], name: "idx_unit_building_number")
@@index([organizationId], name: "idx_unit_org_id")
@@index([status], name: "idx_unit_status")
@@map("unit")
}
model Contract {
id String @id @default(uuid()) @db.Uuid
unitId String @map("unit_id") @db.Uuid
tenantId String @map("tenant_id") @db.Uuid
organizationId String @map("organization_id") @db.Uuid
contractNumber String @map("contract_number") @db.VarChar(50)
contractType ContractType @map("contract_type")
contractorType ContractorType @map("contractor_type")
startDate DateTime @map("start_date") @db.Date
endDate DateTime @map("end_date") @db.Date
rent Decimal @db.Decimal(12, 0)
managementFee Decimal @map("management_fee") @db.Decimal(12, 0)
commonFee Decimal @default(0) @map("common_fee") @db.Decimal(12, 0)
deposit Decimal @db.Decimal(12, 0)
keyMoney Decimal @map("key_money") @db.Decimal(12, 0)
renewalFeeMonths Decimal? @map("renewal_fee_months") @db.Decimal(4, 1)
guaranteeCompanyId String? @map("guarantee_company_id") @db.Uuid
specialTerms Json @default("[]") @map("special_terms")
status ContractStatus @default(draft)
renewalNoticeDate DateTime? @map("renewal_notice_date") @db.Date
terminationDate DateTime? @map("termination_date") @db.Date
terminationReason String? @map("termination_reason") @db.Text
moveOutDate DateTime? @map("move_out_date") @db.Date
notes String? @db.Text
deletedAt DateTime? @map("deleted_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
unit Unit @relation(fields: [unitId], references: [id])
tenant Tenant @relation(fields: [tenantId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id])
invoices Invoice[]
payments Payment[]
guarantors ContractGuarantor[]
@@unique([organizationId, contractNumber], name: "idx_contract_number_org")
@@index([unitId], name: "idx_contract_unit_id")
@@index([tenantId], name: "idx_contract_tenant_id")
@@index([organizationId], name: "idx_contract_org_id")
@@index([status], name: "idx_contract_status")
@@index([endDate], name: "idx_contract_end_date")
@@map("contract")
} 付録N: 変更履歴 バージョン 日付 変更内容 担当 1.0.0 2026-03-19 初版作成 CodeGenAgent(源)
*PropCloud - 機能仕様書 v1.0.0*
*Generated by CodeGenAgent (源) - CCAGI SDK Phase 2*
PDF保存 (Ctrl+P)