# 機能設計書 2-ユーザープロファイル管理

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおけるユーザープロファイル管理機能の設計を記載する。複数のサーバープロファイルを登録・選択・編集・削除する機能を提供し、複数のInvenTree環境を柔軟に管理できるようにする。

### 本機能の処理概要

複数のInvenTreeサーバー接続情報（プロファイル）をローカルデータベースに保存し、ユーザーが必要に応じて切り替えて使用できる機能である。各プロファイルにはサーバー名、サーバーURL、認証トークンが含まれる。

**業務上の目的・背景**：企業では複数のInvenTree環境（本番環境、テスト環境、開発環境等）を使い分けることが多い。また、複数の拠点や部門が独立したInvenTreeインスタンスを運用しているケースもある。本機能により、1つのモバイルアプリから複数環境にアクセスでき、業務効率と柔軟性が向上する。

**機能の利用シーン**：新規サーバー登録時、環境切り替え時、サーバー情報の更新時、不要なプロファイルの削除時、アプリ初回起動時のデモプロファイル自動作成時などに利用される。

**主要な処理内容**：
1. プロファイル一覧の取得と表示
2. 新規プロファイルの作成と保存
3. 既存プロファイルの編集（名前、サーバーURL）
4. プロファイルの選択と接続
5. プロファイルの削除
6. プロファイルからのログアウト（トークンクリア）
7. 初回起動時のデモプロファイル自動生成

**関連システム・外部連携**：sembastローカルデータベース、InvenTree REST API

**権限による制御**：プロファイル管理自体にはサーバー側の権限制御なし。各プロファイルでのサーバー接続後に権限が適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホーム画面 | 遷移元画面 | サーバー選択ボタン押下時にプロファイル選択画面へ遷移 |
| 6 | 設定画面 | 遷移元画面 | サーバー設定メニュー押下時にサーバー選択画面へ遷移 |
| 7 | サーバー選択画面 | 主画面 | サーバープロファイルの一覧表示・選択・削除 |
| 8 | プロファイル編集画面 | 主画面 | サーバープロファイルの新規作成・編集フォーム処理 |

## 機能種別

CRUD操作 / データ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | プロファイル名 | 空文字不可、重複不可 |
| server | String | Yes | サーバーURL | http://またはhttps://で開始、空白不可、有効なURI形式 |
| token | String | No | 認証トークン | ログイン後に自動設定 |

### 入力データソース

- ユーザーによるフォーム入力（プロファイル編集画面）
- ログイン処理からのトークン自動設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| key | int | プロファイルの一意識別子（自動採番） |
| name | String | プロファイル名 |
| server | String | サーバーURL |
| token | String | 認証トークン |
| selected | bool | 現在選択中かどうか |

### 出力先

- sembastローカルデータベース（profilesストア）

## 処理フロー

### 処理シーケンス

```
1. プロファイル一覧取得（getAllProfiles）
   └─ profilesストアから全レコード取得
   └─ 選択状態の付与
   └─ プロファイルなしの場合、デモプロファイル自動生成

2. プロファイル追加（addProfile）
   └─ 名前の重複チェック
   └─ 新規レコードの挿入
   └─ キーの自動採番

3. プロファイル更新（updateProfile）
   └─ キーの存在確認
   └─ レコードの更新
   └─ キーがnullの場合は新規追加

4. プロファイル削除（deleteProfile）
   └─ キーに基づくレコード削除
   └─ 接続中の場合は切断

5. プロファイル選択（selectProfile）
   └─ selectedレコードの更新
   └─ サーバー接続処理の呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[プロファイル一覧表示] --> B{プロファイルあり?}
    B -->|No| C[デモプロファイル作成]
    C --> A
    B -->|Yes| D[一覧表示]
    D --> E{ユーザー操作}
    E -->|タップ| F[プロファイル選択]
    E -->|長押し| G[オプションダイアログ表示]
    E -->|+ボタン| H[新規作成画面へ]
    F --> I{トークンあり?}
    I -->|No| J[ログイン画面へ]
    I -->|Yes| K[サーバー接続]
    G --> L{選択操作}
    L -->|接続| F
    L -->|編集| M[編集画面へ]
    L -->|ログアウト| N[トークンクリア]
    L -->|削除| O[削除確認]
    O -->|確認| P[プロファイル削除]
    J --> Q[トークン取得]
    Q --> K
    K --> R[接続完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | プロファイル名一意性 | 同名のプロファイルは登録不可 | プロファイル追加時 |
| BR-02 | デモプロファイル自動生成 | プロファイルが0件かつ初回の場合、demo.inventree.orgを自動登録 | getAllProfiles時 |
| BR-03 | 選択状態の排他性 | 選択できるプロファイルは常に1つのみ | プロファイル選択時 |
| BR-04 | 接続中削除の制御 | 接続中プロファイルを削除する場合は先に切断 | プロファイル削除時 |

### 計算ロジック

該当なし

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | profiles | SELECT | 全プロファイルの取得 |
| 追加 | profiles | INSERT | 新規プロファイルの挿入 |
| 更新 | profiles | UPDATE | 既存プロファイルの更新 |
| 削除 | profiles | DELETE | プロファイルの削除 |
| 選択 | profiles | UPDATE | selectedレコードの更新 |

### テーブル別操作詳細

#### profiles（sembast store）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name, server, token | ユーザー入力値 | keyは自動採番 |
| UPDATE | name, server | ユーザー入力値 | keyで特定 |
| UPDATE | token | 認証トークンまたは空文字 | ログイン/ログアウト時 |
| UPDATE | selected | プロファイルのkey | 選択時に更新 |
| DELETE | - | keyで特定 | 全フィールド削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | プロファイル名が空 | エラーメッセージ表示、保存不可 |
| - | バリデーションエラー | サーバーURLが空 | エラーメッセージ表示、保存不可 |
| - | バリデーションエラー | URLに空白含む | エラーメッセージ表示、保存不可 |
| - | バリデーションエラー | URL形式不正 | エラーメッセージ表示、保存不可 |
| - | バリデーションエラー | http/https以外のスキーム | エラーメッセージ表示、保存不可 |
| - | 重複エラー | 同名プロファイル存在 | 既存プロファイルをそのまま返す |

### リトライ仕様

- リトライなし（ローカルDB操作のため）

## トランザクション仕様

- sembastの単一レコード操作は自動的にアトミック
- 複数操作の一貫性は保証されない

## パフォーマンス要件

- ローカルDB操作のため、即座に完了（ミリ秒単位）

## セキュリティ考慮事項

- 認証トークンは端末内のsembastデータベースに平文保存
- デバイス暗号化に依存
- トークンクリア機能（ログアウト）を提供

## 備考

- 初回起動時にdemo.inventree.orgへのデモプロファイルが自動作成される（一度のみ）
- デモプロファイル作成済みフラグ: `demo_profile_added`設定

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、プロファイルデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user_profile.dart | `lib/user_profile.dart` | UserProfileクラス（6-57行目）の構造 |

**読解のコツ**:
- **15-25行目**: `fromJson`ファクトリコンストラクタでDBからの復元方法を確認
- **47-51行目**: `toJson`メソッドでDB保存形式を確認
- `selected`フィールドはDB保存ではなくランタイムで計算される点に注意

#### Step 2: データベース操作を理解する

DB管理クラスの操作を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | user_profile.dart | `lib/user_profile.dart` | UserProfileDBManagerクラス（62-278行目） |

**主要処理フロー**:
- **63行目**: `StoreRef("profiles")`でsembastストア定義
- **70-81行目**: `profileNameExists` - 名前重複チェック
- **86-110行目**: `addProfile` - プロファイル追加
- **116-131行目**: `updateProfile` - プロファイル更新
- **136-140行目**: `deleteProfile` - プロファイル削除
- **146-166行目**: `getSelectedProfile` - 選択中プロファイル取得
- **171-213行目**: `getAllProfiles` - 全プロファイル取得（デモプロファイル自動生成含む）
- **254-256行目**: `selectProfile` - プロファイル選択

#### Step 3: UI層を理解する

画面実装を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | select_server.dart | `lib/settings/select_server.dart` | InvenTreeSelectServerWidgetクラス（14-279行目） |
| 3-2 | select_server.dart | `lib/settings/select_server.dart` | ProfileEditWidgetクラス（284-422行目） |

**主要処理フロー**:
- **29-37行目**: `_reload` - プロファイル一覧再読み込み
- **42-55行目**: `_logoutProfile` - ログアウト処理
- **60-71行目**: `_editProfile` - 編集画面遷移
- **73-124行目**: `_selectProfile` - プロファイル選択と接続
- **126-139行目**: `_deleteProfile` - プロファイル削除
- **141-158行目**: `_getProfileIcon` - 接続状態アイコン表示
- **160-279行目**: `build` - 一覧表示とダイアログ

### プログラム呼び出し階層図

```
InvenTreeSelectServerWidget
    │
    ├─ _reload()
    │      └─ UserProfileDBManager().getAllProfiles()
    │
    ├─ _selectProfile(profile)
    │      ├─ UserProfileDBManager().selectProfile(key)
    │      ├─ UserProfileDBManager().getProfileByKey(key)
    │      │
    │      ├─ [トークンなし] Navigator.push → InvenTreeLoginWidget
    │      └─ [トークンあり] InvenTreeAPI().connectToServer(profile)
    │
    ├─ _editProfile()
    │      └─ Navigator.push → ProfileEditWidget
    │             └─ UserProfileDBManager().addProfile() / updateProfile()
    │
    ├─ _logoutProfile()
    │      ├─ UserProfileDBManager().updateProfile(token="")
    │      └─ InvenTreeAPI().disconnectFromServer()
    │
    └─ _deleteProfile()
           ├─ UserProfileDBManager().deleteProfile()
           └─ InvenTreeAPI().disconnectFromServer()
```

### データフロー図

```
[入力]               [処理]                    [出力]

ユーザー入力 ───▶ ProfileEditWidget ───▶ sembast DB
  │                     │                    │
  ├─ name              ├─ バリデーション       ├─ profiles store
  └─ server            └─ DB操作              └─ selected record

DB ───▶ UserProfileDBManager ───▶ UI表示
  │           │                        │
  └─ profiles └─ getAllProfiles()      └─ ListView
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| user_profile.dart | `lib/user_profile.dart` | ソース | UserProfileモデルとDBManager |
| select_server.dart | `lib/settings/select_server.dart` | ソース | サーバー選択・編集画面UI |
| login.dart | `lib/settings/login.dart` | ソース | ログイン画面（トークン取得） |
| preferences.dart | `lib/preferences.dart` | ソース | デモプロファイル作成済みフラグ管理 |
| api.dart | `lib/api.dart` | ソース | サーバー接続・切断処理 |
| dialogs.dart | `lib/widget/dialogs.dart` | ソース | 削除確認ダイアログ |
