← 成果物一覧に戻る

機能仕様書

Version 1.0.0  |  プロジェクト: PropCloud - 賃貸管理クラウド

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)
SLA99.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.xSSR/SSG対応、API Routes統合、ファイルベースルーティング
言語TypeScript5.x型安全性、大規模開発での保守性
UIライブラリshadcn/uilatestカスタマイズ性、アクセシビリティ、Radix UIベース
スタイリングTailwind CSS4.xユーティリティファースト、テーマカスタマイズ容易
状態管理(クライアント)Zustand5.xシンプルAPI、TypeScript親和性、軽量
状態管理(サーバー)TanStack Query5.xキャッシュ管理、楽観的更新、自動再取得
フォームReact Hook Form + Zod7.x / 3.xパフォーマンス、バリデーション統合
テーブルTanStack Table8.xヘッドレスUI、大量データ対応、仮想スクロール
チャートRecharts2.xReact統合、レスポンシブ、カスタマイズ性
アニメーションmotion/reactlatest宣言的API、パフォーマンス、200ms制限準拠
アイコンLucide Reactlatest統一デザイン、Tree-shaking対応
i18nnext-intllatestNext.js App Router対応、型安全
日付date-fns3.xTree-shaking対応、和暦対応

2.2.2 バックエンド

項目技術バージョン選定理由
APItRPC11.xEnd-to-End型安全、自動型推論、バリデーション統合
API(外部向け)Next.js API Routes-Webhook受信、ファイルアップロード等
ORMPrisma6.xマイグレーション管理、型生成、RLS対応
DBPostgreSQL16.xRLS、JSONB、全文検索、信頼性
キャッシュRedis7.xセッション、キャッシュ、ジョブキュー基盤
ジョブキューBullMQ5.xRedis基盤、優先度付きキュー、再試行制御
ファイルストレージAWS S3-署名付きURL、ライフサイクル管理
メール送信Amazon SES-高配信率、テナント別From対応
PDF生成React PDF (@react-pdf/renderer)4.xReact記法でPDF生成、日本語対応
バリデーションZod3.xフロント・バック共通スキーマ

2.2.3 インフラストラクチャ

項目技術選定理由
コンテナ実行ECS Fargateサーバーレスコンテナ、Auto Scaling
CDNCloudFront静的アセット配信、カスタムドメイン対応
DNSRoute 53テナント別カスタムドメイン管理
SSLACM自動証明書管理、ワイルドカード証明書
IaCAWS CDKTypeScriptでインフラ定義、再利用性
CI/CDGitHub 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説明
idUUIDNOプライマリキー
nameVARCHAR(255)NO組織名
slugVARCHAR(100)NOURLスラグ(サブドメイン用、UNIQUE)
plan_idUUIDYES契約プランID
logo_urlVARCHAR(512)YESロゴ画像URL
favicon_urlVARCHAR(512)YESファビコンURL
theme_configJSONBNOテーマ設定(カラー等)、デフォルト: `{}`
feature_flagsJSONBNO機能ON/OFF設定、デフォルト: `{}`
terminology_overridesJSONBNO用語カスタマイズ、デフォルト: `{}`
email_from_addressVARCHAR(255)YESメール送信元アドレス
email_from_nameVARCHAR(255)YESメール送信元名
storage_limit_mbINTEGERNOストレージ上限(MB)、デフォルト: 5120
storage_used_mbINTEGERNOストレージ使用量(MB)、デフォルト: 0
statusENUMNOステータス: active / suspended / trial
trial_ends_atTIMESTAMPYESトライアル終了日時
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`idx_organization_slug` (slug) UNIQUE
`idx_organization_status` (status)

3.3.2 CustomDomain(カスタムドメイン)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK)
domainVARCHAR(255)NOカスタムドメイン(UNIQUE)
ssl_statusENUMNOSSL状態: pending / active / failed
verified_atTIMESTAMPYESドメイン認証完了日時
created_atTIMESTAMPNO作成日時

**インデックス**:

`idx_custom_domain_domain` (domain) UNIQUE

3.3.3 Building(建物)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
owner_idUUIDYESオーナーID(FK)
nameVARCHAR(255)NO建物名
postal_codeVARCHAR(10)NO郵便番号
prefectureVARCHAR(10)NO都道府県
cityVARCHAR(100)NO市区町村
address1VARCHAR(255)NO住所1(番地まで)
address2VARCHAR(255)YES住所2(建物名等)
latitudeDECIMAL(10,7)YES緯度
longitudeDECIMAL(10,7)YES経度
structureENUMNO構造: RC / SRC / S / W / LGS / other
construction_dateDATEYES築年月
total_unitsINTEGERNO総戸数
floors_aboveINTEGERYES地上階数
floors_belowINTEGERYES地下階数
shared_facilitiesJSONBNO共有設備、デフォルト: `[]`
nearest_stationsJSONBNO最寄り駅情報、デフォルト: `[]`
management_typeENUMNO管理形態: self / collection_agency / sublease / exclusive
management_fee_rateDECIMAL(5,2)YES管理手数料率(%)
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
building_idUUIDNO建物ID(FK)
organization_idUUIDNO組織ID(FK、RLS対象)
unit_numberVARCHAR(20)NO部屋番号
floorINTEGERYES階数
layoutVARCHAR(20)YES間取り(1K, 2LDK等)
area_sqmDECIMAL(8,2)YES面積(平米)
directionENUMYES方位: N / NE / E / SE / S / SW / W / NW
rentDECIMAL(12,0)NO賃料(円)
management_feeDECIMAL(12,0)NO管理費(円)、デフォルト: 0
common_feeDECIMAL(12,0)NO共益費(円)、デフォルト: 0
deposit_monthsDECIMAL(4,1)NO敷金(ヶ月)、デフォルト: 0
key_money_monthsDECIMAL(4,1)NO礼金(ヶ月)、デフォルト: 0
facilitiesJSONBNO設備情報、デフォルト: `[]`
statusENUMNOステータス: vacant / occupied / leaving / recruiting / suspended / renovating
available_fromDATEYES入居可能日
recruitment_conditionsJSONBNO募集条件、デフォルト: `{}`
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
unit_idUUIDNO区画ID(FK)
tenant_idUUIDNO入居者ID(FK)
organization_idUUIDNO組織ID(FK、RLS対象)
contract_numberVARCHAR(50)NO契約番号(UNIQUE per org)
contract_typeENUMNO契約種別: new / renewal / sublease
contractor_typeENUMNO契約者区分: individual / corporation
start_dateDATENO契約開始日
end_dateDATENO契約終了日
rentDECIMAL(12,0)NO契約賃料
management_feeDECIMAL(12,0)NO契約管理費
common_feeDECIMAL(12,0)NO契約共益費
depositDECIMAL(12,0)NO敷金
key_moneyDECIMAL(12,0)NO礼金
renewal_fee_monthsDECIMAL(4,1)YES更新料(ヶ月)
guarantee_company_idUUIDYES保証会社ID(FK)
special_termsJSONBNO特約事項、デフォルト: `[]`
statusENUMNOステータス: draft / active / renewed / terminated / cancelled
renewal_notice_dateDATEYES更新案内送付予定日
termination_dateDATEYES解約日
termination_reasonTEXTYES解約理由
move_out_dateDATEYES退去日
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
owner_typeENUMNO区分: individual / corporation
nameVARCHAR(255)NO氏名/法人名
name_kanaVARCHAR(255)YESフリガナ
representative_nameVARCHAR(255)YES代表者名(法人の場合)
emailVARCHAR(255)YESメールアドレス
phoneVARCHAR(20)YES電話番号
mobile_phoneVARCHAR(20)YES携帯電話番号
postal_codeVARCHAR(10)YES郵便番号
addressTEXTYES住所
tax_categoryENUMNO源泉徴収区分: individual / corporation / non_resident
tax_idVARCHAR(20)YES適格請求書番号/法人番号
user_idUUIDYESポータルユーザーID(FK)
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`idx_owner_org_id` (organization_id)
`idx_owner_name_search` GIN (name gin_trgm_ops)

3.3.7 Tenant(入居者)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
last_nameVARCHAR(100)NO
first_nameVARCHAR(100)NO
last_name_kanaVARCHAR(100)YES姓(カナ)
first_name_kanaVARCHAR(100)YES名(カナ)
emailVARCHAR(255)YESメールアドレス
phoneVARCHAR(20)YES電話番号
mobile_phoneVARCHAR(20)YES携帯電話番号
postal_codeVARCHAR(10)YES郵便番号
addressTEXTYES住所(入居前住所)
workplace_nameVARCHAR(255)YES勤務先名
workplace_phoneVARCHAR(20)YES勤務先電話番号
emergency_contact_nameVARCHAR(255)YES緊急連絡先氏名
emergency_contact_phoneVARCHAR(20)YES緊急連絡先電話番号
emergency_contact_relationVARCHAR(50)YES緊急連絡先続柄
date_of_birthDATEYES生年月日
genderENUMYES性別: male / female / other / unspecified
user_idUUIDYESポータルユーザーID(FK)
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
contract_idUUIDNO契約ID(FK)
tenant_idUUIDNO入居者ID(FK)
invoice_numberVARCHAR(50)NO請求番号
billing_period_startDATENO請求期間開始日
billing_period_endDATENO請求期間終了日
billing_dateDATENO請求日
due_dateDATENO支払期日
subtotal_amountDECIMAL(12,0)NO小計
tax_amountDECIMAL(12,0)NO消費税額
total_amountDECIMAL(12,0)NO合計金額
paid_amountDECIMAL(12,0)NO入金済み金額、デフォルト: 0
statusENUMNOステータス: draft / issued / sent / partial_paid / paid / overdue / cancelled
sent_atTIMESTAMPYES送付日時
send_methodENUMYES送付方法: email / mail / portal
is_invoice_qualifiedBOOLEANNO適格請求書フラグ、デフォルト: false
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
invoice_idUUIDNO請求ID(FK)
organization_idUUIDNO組織ID(FK、RLS対象)
item_typeENUMNO明細種別: rent / management_fee / common_fee / parking / utility / renewal_fee / other
descriptionVARCHAR(255)NO項目名
quantityDECIMAL(8,2)NO数量、デフォルト: 1
unit_priceDECIMAL(12,0)NO単価
amountDECIMAL(12,0)NO金額
tax_rateDECIMAL(5,2)NO消費税率、デフォルト: 10.00
tax_amountDECIMAL(12,0)NO消費税額
is_taxableBOOLEANNO課税対象フラグ
sort_orderINTEGERNO表示順
created_atTIMESTAMPNO作成日時

**インデックス**:

`idx_invoice_item_invoice_id` (invoice_id)
`idx_invoice_item_org_id` (organization_id)

3.3.10 Payment(入出金)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
contract_idUUIDYES契約ID(FK)
invoice_idUUIDYES請求ID(FK)
payment_typeENUMNO入出金区分: income / expense
amountDECIMAL(12,0)NO金額
payment_dateDATENO入出金日
methodENUMNO方法: bank_transfer / direct_debit / cash / credit_card / other
payer_nameVARCHAR(255)YES入金者名(銀行振込名義)
bank_nameVARCHAR(100)YES銀行名
bank_branchVARCHAR(100)YES支店名
account_numberVARCHAR(20)YES口座番号
statusENUMNOステータス: pending / completed / partial / overpaid / refunded
reconciliation_statusENUMNO消込ステータス: unmatched / auto_matched / manual_matched / confirmed
matched_byUUIDYES消込実行者ID(FK)
matched_atTIMESTAMPYES消込日時
excess_amountDECIMAL(12,0)NO過入金額(預かり金)、デフォルト: 0
shortage_amountDECIMAL(12,0)NO不足金額、デフォルト: 0
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
unit_idUUIDYES区画ID(FK)
building_idUUIDYES建物ID(共用部の場合)
tenant_idUUIDYES申告入居者ID(FK)
assigned_toUUIDYES担当者ID(FK)
vendor_idUUIDYES業者ID(FK)
ticket_numberVARCHAR(50)NOチケット番号
titleVARCHAR(255)NOタイトル
descriptionTEXTNO詳細説明
categoryENUMNOカテゴリ: equipment / noise / leak / pest / common_area / other
priorityENUMNO優先度: low / medium / high / urgent
statusENUMNOステータス: received / in_progress / waiting_estimate / waiting_approval / ordered / completed / closed
cost_bearerENUMYES費用負担者: owner / tenant / management_company
estimated_costDECIMAL(12,0)YES見積金額
actual_costDECIMAL(12,0)YES実費
scheduled_dateDATEYES作業予定日
completed_dateDATEYES完了日
resolutionTEXTYES対応内容
sourceENUMNO受付経路: phone / email / portal / chat / other
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
owner_idUUIDNOオーナーID(FK)
remittance_numberVARCHAR(50)NO送金番号
target_monthDATENO対象年月(月初日)
gross_incomeDECIMAL(12,0)NO総収入
management_feeDECIMAL(12,0)NO管理手数料
maintenance_costDECIMAL(12,0)NO修繕費
other_deductionsDECIMAL(12,0)NOその他控除
withholding_taxDECIMAL(12,0)NO源泉徴収税額
net_remittanceDECIMAL(12,0)NO送金額(手取り)
statusENUMNOステータス: draft / confirmed / sent / completed
transfer_dateDATEYES振込予定日
transferred_atTIMESTAMPYES振込実行日時
notesTEXTYES備考
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`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説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID(FK、RLS対象)
role_idUUIDNOロールID(FK)
emailVARCHAR(255)NOメールアドレス
password_hashVARCHAR(255)NOパスワードハッシュ(bcrypt)
first_nameVARCHAR(100)NO
last_nameVARCHAR(100)NO
phoneVARCHAR(20)YES電話番号
avatar_urlVARCHAR(512)YESアバター画像URL
is_activeBOOLEANNO有効フラグ、デフォルト: true
totp_enabledBOOLEANNOTOTP有効フラグ、デフォルト: false
totp_secretVARCHAR(255)YESTOTPシークレット(暗号化保存)
failed_login_countINTEGERNOログイン失敗回数、デフォルト: 0
locked_untilTIMESTAMPYESアカウントロック解除日時
last_login_atTIMESTAMPYES最終ログイン日時
password_changed_atTIMESTAMPYESパスワード変更日時
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

**インデックス**:

`idx_user_org_id` (organization_id)
`idx_user_email` (email) UNIQUE
`idx_user_role_id` (role_id)

3.3.14 Role(ロール)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDYES組織ID(NULL=システムロール)
nameVARCHAR(100)NOロール名
slugVARCHAR(100)NOロールスラグ
descriptionTEXTYES説明
permissionsJSONBNO権限リスト
is_systemBOOLEANNOシステムロールフラグ、デフォルト: false
created_atTIMESTAMPNO作成日時
updated_atTIMESTAMPNO更新日時

3.3.15 AuditLog(監査ログ)

カラム名NULL説明
idUUIDNOプライマリキー
organization_idUUIDNO組織ID
user_idUUIDNO操作ユーザーID
actionVARCHAR(50)NOアクション: create / update / delete / login / logout / export
resource_typeVARCHAR(100)NOリソース種別: building / contract / payment 等
resource_idUUIDYESリソースID
changesJSONBYES変更内容(before/after)
ip_addressINETYESIPアドレス
user_agentTEXTYESUser-Agent
created_atTIMESTAMPNO作成日時

**インデックス**:

`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_caseorganization_id, created_at
プライマリキーid(UUID)id
外部キー{テーブル名}_idbuilding_id, owner_id
インデックスidx_{テーブル名}_{カラム名}idx_building_org_id
ENUM型snake_caseactive, in_progress

3.4.2 共通カラム

全テーブルに以下のカラムを含む:

カラム説明
idUUIDプライマリキー(gen_random_uuid())
organization_idUUIDテナントID(RLS対象)
created_atTIMESTAMP作成日時(DEFAULT now())
updated_atTIMESTAMP更新日時(トリガーで自動更新)

3.4.3 ソフトデリート

削除操作はソフトデリートを基本とする。以下のカラムを追加:

カラム説明
deleted_atTIMESTAMP削除日時(NULLは未削除)

RLSポリシーに `deleted_at IS NULL` 条件を追加し、論理削除されたレコードを自動的に除外する。

3.4.4 暗号化対象カラム

以下の個人情報カラムはアプリケーションレベルで暗号化して保存する(AES-256、AWS KMS):

テーブルカラム
Tenantemail, phone, mobile_phone, date_of_birth
Owneremail, phone, mobile_phone, tax_id
Usertotp_secret
ContractGuarantorphone, address
OwnerBankAccountaccount_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 パラメータ**:

パラメータ必須説明
cursorstringNOページネーションカーソル
limitnumberNO取得件数(デフォルト: 25, 最大: 100)
searchstringNOフリーテキスト検索(名称、住所)
prefecturestringNO都道府県フィルタ
managementTypeenumNO管理形態フィルタ
ownerIdstringNOオーナーIDフィルタ
sortByenumNOソートキー: name / createdAt / occupancyRate
sortOrderenumNOソート順: asc / desc

**building.create リクエスト**:

フィールド必須説明
namestringYES建物名
postalCodestringYES郵便番号
prefecturestringYES都道府県
citystringYES市区町村
address1stringYES住所1
address2stringNO住所2
structureenumYES構造
constructionDatedateNO築年月
totalUnitsnumberYES総戸数
floorsAbovenumberNO地上階数
floorsBelownumberNO地下階数
ownerIdstringNOオーナーID
managementTypeenumYES管理形態
managementFeeRatenumberNO管理手数料率(%)
sharedFacilitiesarrayNO共有設備
nearestStationsarrayNO最寄り駅情報

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 パラメータ**:

パラメータ必須説明
cursorstringNOページネーションカーソル
limitnumberNO取得件数
buildingIdstringNO建物IDフィルタ
statusenum[]NOステータスフィルタ(複数可)
layoutTypestringNO間取りフィルタ
rentMinnumberNO賃料下限
rentMaxnumberNO賃料上限
searchstringNOフリーテキスト検索

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 リクエスト**:

フィールド必須説明
unitIdstringYES区画ID
tenantIdstringYES入居者ID(新規作成も可)
contractTypeenumYES契約種別
contractorTypeenumYES契約者区分
startDatedateYES契約開始日
endDatedateYES契約終了日
rentnumberYES賃料
managementFeenumberYES管理費
commonFeenumberNO共益費
depositnumberYES敷金
keyMoneynumberYES礼金
renewalFeeMonthsnumberNO更新料(ヶ月)
guaranteeCompanyIdstringNO保証会社ID
guarantorsarrayNO連帯保証人情報
specialTermsarrayNO特約事項

**contract.terminate リクエスト**:

フィールド必須説明
contractIdstringYES契約ID
terminationDatedateYES解約日
moveOutDatedateYES退去日
terminationReasonstringNO解約理由

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 リクエスト**:

フィールド必須説明
templateIdstringYESテンプレートID
resourceTypeenumYESリソース種別(contract, invoice, remittance等)
resourceIdstringYESリソースID
formatenumNO出力形式: pdf / excel(デフォルト: pdf)
optionsobjectNO生成オプション(和暦表示等)

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.enableTotpTOTP有効化all
POST/api/trpc/user.disableTotpTOTP無効化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-totpTOTP検証
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 Token15分httpOnly CookieAPI認証
Refresh Token7日httpOnly CookieAccess Tokenの更新
Session ID24hRedisセッション管理

**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_admintenant_admincompany_admincompany_staffownerresident
OrganizationreadR(all)R(own)R(own)---
OrganizationwriteWW(own)----
BuildingreadR(all)R(own)R(own)R(own)R(own buildings)-
BuildingwriteWWW---
UnitreadR(all)R(own)R(own)R(own)R(own)R(own)
UnitwriteWWWW(assigned)--
ContractreadR(all)R(own)R(own)R(own)-R(own)
ContractwriteWWW---
PaymentreadR(all)R(own)R(own)R(own)-R(own)
PaymentwriteWWWW--
InvoicereadR(all)R(own)R(own)R(own)-R(own)
InvoicewriteWWW---
OwnerreadR(all)R(own)R(own)R(own)R(self)-
OwnerwriteWWWW(assigned)--
TenantreadR(all)R(own)R(own)R(own)-R(self)
TenantwriteWWWW-W(self,limited)
MaintenancereadR(all)R(own)R(own)R(own)R(own)R(own)
MaintenancewriteWWWW-W(create only)
DocumentreadR(all)R(own)R(own)R(own)R(own)R(own)
DocumentwriteWWWW--
UserreadR(all)R(own)R(own)R(self)R(self)R(self)
UserwriteWWWW(self)W(self)W(self)
AuditLogreadR(all)R(own)R(own)---
RemittancereadR(all)R(own)R(own)R(own)R(own)-
RemittancewriteWWW---
ReportreadR(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, deleteCRUD操作
データ閲覧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 APIPhase 3
クラウドサインREST APIPhase 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.contractNumberdocument_title文書タイトル
Tenant.emailsigner_email署名者メール
Tenant.namesigner_name署名者名
GeneratedDocument.fileUrldocument_file署名対象PDF
Contract.specialTermscustom_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 11-7日催告(リマインド)督促状_催告.pdf
Level 28-30日警告督促状_警告.pdf
Level 331-60日最終通告督促状_最終通告.pdf
Level 461日以上法的措置予告督促状_法的措置予告.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 バッチ監視

監視項目アラート条件通知先
ジョブ失敗失敗回数 > 0Slack(運用チャンネル)
処理時間超過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負荷軽減
接続プーリングPgBouncerDB接続管理
レスポンス圧縮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 ManagerAPIキー、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 InstanceWorker InstanceRDSRedis
初期105,00021db.r6g.largecache.r6g.large
成長期10050,00042db.r6g.xlargecache.r6g.xlarge
成熟期500250,00084db.r6g.2xlargecache.r6g.2xlarge
最大1,000500,0002010db.r6g.4xlargecache.r6g.4xlarge

10.3.3 データベーススケーリング戦略

戦略実施タイミング詳細
Read Replica追加テナント100超過時レポートクエリの分離
テーブルパーティショニングデータ量増大時audit_log, payment テーブル
コネクションプーリング初期からPgBouncer導入
シャーディング検討テナント500超過時テナント単位のDBシャーディング

10.4 可用性対応

10.4.1 冗長化構成

コンポーネント冗長化方式詳細
ECS TaskマルチAZ配置最低2AZに分散
RDSMulti-AZ自動フェイルオーバー
RedisElastiCache ClusterMulti-AZ、自動フェイルオーバー
S3標準ストレージ99.999999999%耐久性
CloudFrontグローバルエッジエッジロケーション自動分散

10.4.2 バックアップ・リカバリ

対象バックアップ方式頻度保持期間RTORPO
RDS自動スナップショット日次30日4h1h
RDSリアルタイム継続的(PITR)35日15min5min
S3バージョニングリアルタイム90日即時0
Redisスナップショット日次7日15min1h

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 maxSlack
データベースレプリケーション遅延> 10sPagerDuty
データベースデッドロック発生> 0Slack
Redisメモリ使用率> 80%Slack
Redis接続数> 80% of maxSlack
バッチジョブ失敗> 0Slack
バッチキュー滞留> 100件Slack
セキュリティログイン失敗> 100/h (同一IP)PagerDuty
ビジネス滞納率> 10% (テナント)Slack(テナント管理者)

10.5.2 ログ管理

ログ種別出力先保持期間
アプリケーションログCloudWatch Logs90日(S3に長期保存: 1年)
アクセスログCloudWatch Logs90日
監査ログPostgreSQL + S31年(DB)+ 5年(S3)
エラーログSentry90日
バッチ実行ログCloudWatch Logs90日
セキュリティログCloudWatch Logs + S31年

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 テスト戦略

テスト種別ツールカバレッジ目標実行タイミング
ユニットテストVitest80%以上PR作成時
統合テストVitest + Supertest主要API100%PR作成時
E2EテストPlaywright主要フロー100%ステージングデプロイ後
パフォーマンステストk6レスポンスタイムSLA週次
セキュリティテストOWASP ZAPOWASP Top 10月次
負荷テストk6100同時接続/テナントリリース前

付録

付録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)テナントのシステム上の表現
RLSRow Level Security。PostgreSQLの行レベルセキュリティ機能
消込入金データと請求データのマッチング処理
全銀フォーマット全国銀行協会が定める銀行間データ交換フォーマット
総合振込複数の振込先への一括振込
口座振替借主の銀行口座から自動引き落としする決済方法
源泉徴収支払者が支払時に所得税を差し引いて納付する制度
支払調書特定の支払いについて税務署に提出する法定調書
ホワイトラベル製品を他社ブランドで販売できる仕組み
RBACRole-Based Access Control。ロールベースのアクセス制御
tRPCTypeSafe 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_001401認証に失敗しましたメール/パスワードを確認
AUTH_002401セッションが期限切れです再ログイン
AUTH_003401二段階認証コードが無効ですコードを再確認
AUTH_004403アカウントがロックされています30分後に再試行
AUTH_005403アカウントが無効化されています管理者に連絡
AUTH_006403テナントが停止中ですプラットフォーム管理者に連絡

E.2 権限エラー

コードHTTPステータスメッセージ対処法
PERM_001403この操作を実行する権限がありません管理者に権限付与を依頼
PERM_002403このリソースへのアクセス権がありませんテナント管理者に確認
PERM_003403この機能は現在のプランでは利用できませんプランのアップグレードを検討

E.3 バリデーションエラー

コードHTTPステータスメッセージ対処法
VALID_001400必須項目が未入力です必須項目を入力
VALID_002400入力形式が正しくありませんフォーマットを確認
VALID_003400値が範囲外です有効範囲を確認
VALID_004400重複するデータが存在します既存データを確認
VALID_005400日付の前後関係が不正です開始日/終了日を確認

E.4 ビジネスロジックエラー

コードHTTPステータスメッセージ対処法
BIZ_001409この区画には既に有効な契約があります既存契約を確認
BIZ_002409消込対象の請求が見つかりません請求データを確認
BIZ_003409送金データが既に確定済みです確定取消が必要
BIZ_004409テンプレートが他のテナントで使用中です別テンプレートを使用
BIZ_005422契約期間が建物の管理委託期間外です管理委託契約を確認
BIZ_006422オーナーの振込先口座が未登録です口座情報を登録
BIZ_007422ストレージ容量の上限に達しましたプランのアップグレードまたは不要ファイルの削除

E.5 外部連携エラー

コードHTTPステータスメッセージ対処法
EXT_001502電子契約サービスとの通信に失敗しました再試行またはサービス状態を確認
EXT_002502メール送信に失敗しました送信先アドレスを確認、再送
EXT_003422全銀フォーマットのパースに失敗しましたファイル形式を確認
EXT_004502会計ソフトとの同期に失敗しました連携設定を確認

E.6 システムエラー

コードHTTPステータスメッセージ対処法
SYS_001500内部サーバーエラーが発生しましたサポートに連絡
SYS_002503サービスが一時的に利用できませんしばらく後に再試行
SYS_003429リクエスト数の上限に達しましたしばらく後に再試行
SYS_004504処理がタイムアウトしましたデータ量を減らして再試行

付録F: データバリデーション規則

F.1 共通バリデーション

フィールド型バリデーション規則
メールアドレスRFC 5322準拠、最大255文字
電話番号半角数字・ハイフンのみ、10〜15文字
郵便番号半角数字7文字(ハイフンなし)または半角数字3文字-半角数字4文字
金額0以上の整数(小数点不可)
日付YYYY-MM-DD形式、1900-01-01〜2099-12-31
UUIDv4形式
URLhttps://で始まる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, .png2MB推奨: 240x60px
ファビコン.ico, .png500KB32x32px
建物画像.jpg, .jpeg, .png, .webp10MB最大20枚/建物
修繕画像.jpg, .jpeg, .png, .webp10MB最大10枚/チケット
契約書類.pdf20MB電子帳簿保存法対応
全銀データ.txt, .dat5MBShift_JIS/UTF-8
CSVインポート.csv10MBUTF-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_URLYESPostgreSQL接続URLpostgresql://user:pass@host:5432/db
REDIS_URLYESRedis接続URLredis://host:6379
NEXTAUTH_SECRETYESセッション暗号化キーランダム文字列(64文字以上)
NEXTAUTH_URLYESアプリケーションURLhttps://app.propcloud.jp
AWS_REGIONYESAWSリージョンap-northeast-1
AWS_S3_BUCKETYESS3バケット名propcloud-files-prod
AWS_SES_FROM_EMAILYESSESデフォルト送信元noreply@propcloud.jp
AWS_KMS_KEY_IDYESKMS暗号化キーIDarn:aws:kms:...
SENTRY_DSNNOSentry DSNhttps://xxx@sentry.io/xxx
E_CONTRACT_API_KEYNO電子契約APIキーSecrets Managerから取得
E_CONTRACT_API_URLNO電子契約APIエンドポイントhttps://api.gmosign.com
SLACK_WEBHOOK_URLNOSlack通知Webhookhttps://hooks.slack.com/...

付録I: パフォーマンスSLA詳細

操作P50P95P99最大
ダッシュボード表示500ms1.5s3s5s
一覧ページ表示(100件)300ms800ms1.5s3s
詳細ページ表示200ms500ms1s2s
フォーム送信200ms500ms1s3s
検索実行100ms300ms500ms1s
全銀データ取込(1,000件)3s10s20s30s
自動消込(1,000件)5s30s45s60s
請求データ生成(5,000件)1min3min4min5min
帳票PDF生成(1件)1s3s4s5s
帳票一括生成(100件)15s40s50s60s
CSVエクスポート(10,000件)5s15s25s30s
オーナー送金集計(100オーナー)5s15s25s30s

付録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.02026-03-19初版作成CodeGenAgent(源)

*PropCloud - 機能仕様書 v1.0.0*

*Generated by CodeGenAgent (源) - CCAGI SDK Phase 2*