# InvenTree Mobile App コードリーディングガイドライン

## はじめに

このガイドラインは、InvenTree Mobile Appのコードベースを効率的に理解するための手引きです。
Dart/Flutterに精通していないエンジニアでも、段階的に学習できるよう構成されています。

**対象読者:**
- プロジェクトに新規参画するエンジニア
- 他言語からの経験者
- コードレビューを行う担当者

**プロジェクト概要:**
InvenTree Mobile Appは、[InvenTree在庫管理システム](https://github.com/inventree/InvenTree)のコンパニオンアプリです。Flutterフレームワークを使用してDart言語で開発されており、Android/iOSの両プラットフォームをサポートしています。

---

## 1. 言語基礎

> このセクションでは、Dart言語の基本構文と概念を解説します。

### 1.1 プログラム構造

Dartプログラムは`main()`関数をエントリーポイントとして開始します。Flutterアプリでは通常、`runApp()`を呼び出してルートウィジェットを起動します。

```dart
// ファイル: lib/main.dart:21-88
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final savedThemeMode = await AdaptiveTheme.getThemeMode();

  await runZonedGuarded<Future<void>>(
    () async {
      PackageInfo info = await PackageInfo.fromPlatform();
      // ... 初期化処理

      SystemChrome.setPreferredOrientations(orientations).then((_) {
        runApp(InvenTreeApp(savedThemeMode));
      });
    },
    (Object error, StackTrace stackTrace) async {
      sentryReportError("main.runZonedGuarded", error, stackTrace);
    },
  );
}
```

**解説:**
- `async/await`: 非同期処理を同期的に書けるDartの機能
- `runZonedGuarded`: エラーハンドリングゾーンを作成
- `WidgetsFlutterBinding.ensureInitialized()`: Flutter初期化前のプラグイン使用に必要

### 1.2 データ型と変数

Dartは型安全な言語で、null安全をサポートしています。

```dart
// ファイル: lib/inventree/stock.dart:134-145
class InvenTreeStockItem extends InvenTreeModel {
  InvenTreeStockItem() : super();

  InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json);

  @override
  String get URL => "stock/";

  static const String MODEL_TYPE = "stockitem";
```

**主要なデータ型:**
| 型 | 説明 | 例 |
|---|---|---|
| `String` | 文字列 | `String name = "";` |
| `int` | 整数 | `int pk = -1;` |
| `double` | 浮動小数点 | `double quantity = 0.0;` |
| `bool` | 真偽値 | `bool selected = false;` |
| `List<T>` | リスト | `List<Widget> tiles = [];` |
| `Map<K,V>` | マップ | `Map<String, dynamic> jsondata = {};` |
| `dynamic` | 動的型 | API応答など型が不定の場合 |
| `T?` | Nullable型 | `UserProfile? profile;` |

### 1.3 制御構造

```dart
// ファイル: lib/widget/home.dart:277-407
List<Widget> getListTiles(BuildContext context) {
  List<Widget> tiles = [];

  // 条件分岐
  if (InvenTreePart().canView) {
    tiles.add(
      _listTile(
        context,
        L10().parts,
        TablerIcons.box,
        callback: () {
          _showParts(context);
        },
      ),
    );
  }

  // Purchase orders
  if (homeShowPo && InvenTreePurchaseOrder().canView) {
    tiles.add(/* ... */);
  }

  return tiles;
}
```

**制御構造:**
- `if/else`: 条件分岐
- `for/for-in`: ループ
- `switch/case`: パターンマッチング（Dart 3.0以降は拡張機能あり）
- `try/catch/finally`: 例外処理

### 1.4 関数/メソッド定義

Dartでは関数とメソッドを柔軟に定義できます。

```dart
// ファイル: lib/inventree/model.dart:119-128
// 名前付きパラメータとデフォルト値
String getString(String key, {String backup = "", String subKey = ""}) {
  dynamic value = getValue(key, backup: backup, subKey: subKey);

  if (value == null) {
    return backup;
  }

  return value.toString();
}

// ファイル: lib/api.dart:858-882
// 非同期メソッド
Future<APIResponse> patch(
  String url, {
  Map<String, dynamic> body = const {},
  int? expectedStatusCode,
}) async {
  Map<String, dynamic> _body = body;

  HttpClientRequest? request = await apiRequest(url, "PATCH");

  if (request == null) {
    return APIResponse(
      url: url,
      method: "PATCH",
      error: "HttpClientRequest is null",
    );
  }

  return completeRequest(
    request,
    data: json.encode(_body),
    statusCode: expectedStatusCode,
  );
}
```

**関数の種類:**
- 同期関数: `ReturnType functionName(params) { ... }`
- 非同期関数: `Future<ReturnType> functionName(params) async { ... }`
- アロー関数: `ReturnType get property => value;`
- コンストラクタ: `ClassName() : super();`

### 1.5 モジュール/インポート

```dart
// ファイル: lib/widget/stock/stock_detail.dart:1-31
import "package:flutter/material.dart";
import "package:flutter_speed_dial/flutter_speed_dial.dart";
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";

import "package:inventree/app_colors.dart";
import "package:inventree/barcode/barcode.dart";
import "package:inventree/barcode/stock.dart";
import "package:inventree/helpers.dart";
import "package:inventree/inventree/attachment.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/l10.dart";
import "package:inventree/api.dart";
```

**インポートパターン:**
- `package:flutter/...`: Flutterフレームワーク
- `package:inventree/...`: プロジェクト内パッケージ
- `package:<name>/...`: 外部パッケージ
- `dart:...`: Dart標準ライブラリ

---

## 2. プロジェクト固有の概念

> このセクションでは、当プロジェクト特有の概念を解説します。

### 2.1 フレームワーク固有の概念

#### Flutterウィジェット

Flutterはウィジェットベースのフレームワークで、UIはウィジェットのツリーで構築されます。

```dart
// ファイル: lib/main.dart:90-102
class InvenTreeApp extends StatefulWidget {
  const InvenTreeApp(this.savedThemeMode);

  final AdaptiveThemeMode? savedThemeMode;

  @override
  InvenTreeAppState createState() => InvenTreeAppState(savedThemeMode);

  static InvenTreeAppState? of(BuildContext context) =>
      context.findAncestorStateOfType<InvenTreeAppState>();
}
```

**ウィジェットの種類:**
| 種類 | 説明 | 使用例 |
|---|---|---|
| `StatelessWidget` | 状態を持たない | 静的なUI要素 |
| `StatefulWidget` | 状態を持つ | 動的なUI要素 |

#### BuildContextとState

```dart
// ファイル: lib/widget/refreshable_state.dart:195-248
abstract class RefreshableState<T extends StatefulWidget> extends State<T>
    with BaseWidgetProperties {
  final scaffoldKey = GlobalKey<ScaffoldState>();
  final refreshKey = GlobalKey<RefreshIndicatorState>();

  bool loading = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => onBuild(_context!));
  }

  Future<void> refresh(BuildContext context) async {
    if (!mounted) {
      return;
    }

    setState(() {
      loading = true;
    });

    await request(context);

    if (!mounted) {
      return;
    }

    setState(() {
      loading = false;
    });
  }
}
```

### 2.2 プロジェクト独自のパターン

#### シングルトンAPIクラス

```dart
// ファイル: lib/api.dart:171-179
class InvenTreeAPI {
  factory InvenTreeAPI() {
    return _api;
  }

  InvenTreeAPI._internal();

  // Ensure we only ever create a single instance of the API class
  static final InvenTreeAPI _api = InvenTreeAPI._internal();
```

**解説:** `factory`コンストラクタを使用してシングルトンパターンを実装。アプリ全体で1つのAPIインスタンスを共有します。

#### モデルベースクラス

```dart
// ファイル: lib/inventree/model.dart:40-438
class InvenTreeModel {
  InvenTreeModel();

  InvenTreeModel.fromJson(this.jsondata);

  // Override the endpoint URL for each subclass
  String get URL => "";

  // JSON data which defines this object
  Map<String, dynamic> jsondata = {};

  // Common accessors
  String get name => getString("name");
  int get pk => getInt("pk");

  // CRUD operations
  Future<bool> reload() async { ... }
  Future<bool> delete() async { ... }
  Future<APIResponse> update({...}) async { ... }
}
```

**解説:** すべてのデータモデルが継承するベースクラス。JSONデータの取得・操作、API通信の抽象化を提供します。

---

## 3. 命名規則

> このセクションでは、プロジェクト全体で使用される命名規則を解説します。

### 3.1 ファイル・ディレクトリ命名

| パターン | 意味 | 例 |
|---------|------|-----|
| `snake_case.dart` | 通常のDartファイル | `stock_detail.dart`, `part_list.dart` |
| `widget/` | UIコンポーネント | `widget/home.dart` |
| `inventree/` | データモデル | `inventree/stock.dart` |
| `barcode/` | バーコード機能 | `barcode/handler.dart` |
| `settings/` | 設定画面 | `settings/app_settings.dart` |

### 3.2 クラス・関数・変数命名

| プレフィックス/サフィックス | 意味 | 例 |
|---------------------------|------|-----|
| `InvenTree` | プロジェクト固有クラス | `InvenTreeAPI`, `InvenTreeStockItem` |
| `Widget` | UIウィジェット | `StockDetailWidget`, `PartListWidget` |
| `State` | Stateクラス | `_StockItemDisplayState` |
| `_` (アンダースコア) | プライベートメンバー | `_api`, `_connected`, `_plugins` |
| `get` | ゲッター | `getString()`, `getInt()` |
| `INV_` | 設定キー定数 | `INV_HOME_SHOW_PO`, `INV_STRICT_HTTPS` |
| `URL` | APIエンドポイント | `get URL => "stock/"` |
| `Tile` | リスト項目 | `headerTile()`, `_listTile()` |

### 3.3 プログラム分類一覧

| 接尾辞 | 分類 | 説明 |
|-------|------|------|
| `Model` | データモデル | サーバーデータの表現 |
| `Handler` | ハンドラー | イベント/バーコード処理 |
| `Manager` | マネージャー | リソース管理 |
| `Widget` | ウィジェット | UI画面 |
| `Response` | レスポンス | API応答 |

---

## 4. ディレクトリ構造

> このセクションでは、プロジェクトのディレクトリ構造を解説します。

```
inventree-app-master/
├── lib/                          # メインソースコード
│   ├── main.dart                 # アプリエントリーポイント
│   ├── api.dart                  # API通信クラス
│   ├── api_form.dart             # APIフォーム処理
│   ├── preferences.dart          # 設定管理
│   ├── user_profile.dart         # ユーザープロファイル
│   ├── helpers.dart              # ユーティリティ関数
│   ├── l10.dart                  # 国際化ヘルパー
│   ├── app_colors.dart           # カラー定義
│   ├── labels.dart               # ラベル印刷機能
│   │
│   ├── inventree/                # データモデル層
│   │   ├── model.dart            # ベースモデルクラス
│   │   ├── stock.dart            # 在庫モデル
│   │   ├── part.dart             # 部品モデル
│   │   ├── company.dart          # 会社モデル
│   │   ├── purchase_order.dart   # 購入注文モデル
│   │   ├── sales_order.dart      # 販売注文モデル
│   │   └── ...
│   │
│   ├── widget/                   # UIウィジェット層
│   │   ├── home.dart             # ホーム画面
│   │   ├── drawer.dart           # ナビゲーションドロワー
│   │   ├── search.dart           # 検索画面
│   │   ├── refreshable_state.dart # 更新可能State基底クラス
│   │   │
│   │   ├── stock/                # 在庫関連UI
│   │   │   ├── stock_detail.dart
│   │   │   ├── stock_list.dart
│   │   │   └── location_display.dart
│   │   │
│   │   ├── part/                 # 部品関連UI
│   │   │   ├── part_detail.dart
│   │   │   ├── part_list.dart
│   │   │   └── category_display.dart
│   │   │
│   │   ├── order/                # 注文関連UI
│   │   │   ├── purchase_order_*.dart
│   │   │   └── sales_order_*.dart
│   │   │
│   │   └── company/              # 会社関連UI
│   │
│   ├── barcode/                  # バーコード機能
│   │   ├── barcode.dart          # バーコード共通処理
│   │   ├── handler.dart          # バーコードハンドラー基底
│   │   ├── controller.dart       # スキャナーコントローラー
│   │   ├── stock.dart            # 在庫用バーコード処理
│   │   └── tones.dart            # 効果音
│   │
│   ├── settings/                 # 設定画面
│   │   ├── settings.dart         # 設定メイン
│   │   ├── app_settings.dart     # アプリ設定
│   │   ├── login.dart            # ログイン
│   │   └── ...
│   │
│   └── l10n/                     # 国際化ファイル
│
├── test/                         # テストコード
├── android/                      # Android固有設定
├── ios/                          # iOS固有設定
├── assets/                       # 静的リソース
└── pubspec.yaml                  # パッケージ設定
```

### 各ディレクトリの役割

| ディレクトリ | 役割 | 主要ファイル |
|-------------|------|-------------|
| `lib/` | アプリケーションソース | `main.dart`, `api.dart` |
| `lib/inventree/` | データモデル | `model.dart`, `stock.dart`, `part.dart` |
| `lib/widget/` | UIコンポーネント | `home.dart`, `refreshable_state.dart` |
| `lib/barcode/` | バーコード機能 | `handler.dart`, `controller.dart` |
| `lib/settings/` | 設定画面 | `settings.dart`, `login.dart` |
| `test/` | ユニットテスト | `api_test.dart`, `barcode_test.dart` |

---

## 5. アーキテクチャ

> このセクションでは、プロジェクトのアーキテクチャパターンを解説します。

### 5.1 全体アーキテクチャ

本プロジェクトは**レイヤードアーキテクチャ**を採用しています。

```
┌─────────────────────────────────────────────────────────────┐
│                      Presentation Layer                      │
│  (widget/, settings/)                                        │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │  HomePage   │ │ StockDetail │ │ PartDetail  │  ...      │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
├─────────────────────────────────────────────────────────────┤
│                      Business Logic Layer                    │
│  (barcode/, labels.dart)                                    │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │BarcodeHandler│ │ APIForm    │ │LabelPrinting│  ...      │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
├─────────────────────────────────────────────────────────────┤
│                      Data Model Layer                        │
│  (inventree/)                                                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │InvenTreeModel│ │ StockItem  │ │    Part    │  ...      │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
├─────────────────────────────────────────────────────────────┤
│                      API / Data Access Layer                 │
│  (api.dart, preferences.dart, user_profile.dart)            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │InvenTreeAPI │ │SettingsDB  │ │UserProfileDB│           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
├─────────────────────────────────────────────────────────────┤
│                      External Services                       │
│  ┌─────────────┐ ┌─────────────┐                           │
│  │InvenTree    │ │   Sentry    │                           │
│  │REST API     │ │   (Error)   │                           │
│  └─────────────┘ └─────────────┘                           │
└─────────────────────────────────────────────────────────────┘
```

### 5.2 レイヤー構成

| レイヤー | 責務 | 代表的なファイル |
|---------|------|-----------------|
| Presentation | UI表示・ユーザー操作 | `widget/*.dart` |
| Business Logic | ビジネスルール実装 | `barcode/handler.dart`, `api_form.dart` |
| Data Model | データ構造・ドメインロジック | `inventree/model.dart` |
| Data Access | 外部通信・永続化 | `api.dart`, `preferences.dart` |

### 5.3 データフロー

```
[User Action]
     │
     ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Widget    │───▶│   Model     │───▶│     API     │
│ (setState)  │    │ (request)   │    │ (HTTP)      │
└─────────────┘    └─────────────┘    └─────────────┘
     ▲                                       │
     │                                       ▼
     │                              ┌─────────────┐
     │                              │  InvenTree  │
     │                              │   Server    │
     │                              └─────────────┘
     │                                       │
     │                                       ▼
     │              ┌─────────────┐    ┌─────────────┐
     └──────────────│   Model     │◀───│ APIResponse │
                    │ (fromJson)  │    │             │
                    └─────────────┘    └─────────────┘
```

---

## 6. 主要コンポーネント

> このセクションでは、主要なコンポーネントとその連携を解説します。

### 6.1 エントリーポイント

```dart
// ファイル: lib/main.dart:21-88
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final savedThemeMode = await AdaptiveTheme.getThemeMode();

  await runZonedGuarded<Future<void>>(
    () async {
      // パッケージ情報取得
      PackageInfo info = await PackageInfo.fromPlatform();

      // Sentry初期化（エラー報告）
      if (SENTRY_DSN_KEY.isNotEmpty) {
        await Sentry.init((options) {
          options.dsn = SENTRY_DSN_KEY;
          // ...
        });
      }

      // 画面向き設定
      final int orientation = await InvenTreeSettingsManager()
          .getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int;

      // アプリ起動
      SystemChrome.setPreferredOrientations(orientations).then((_) {
        runApp(InvenTreeApp(savedThemeMode));
      });
    },
    // エラーハンドラ
    (Object error, StackTrace stackTrace) async {
      sentryReportError("main.runZonedGuarded", error, stackTrace);
    },
  );
}
```

**起動フロー:**
1. Flutterバインディング初期化
2. テーマモード読み込み
3. Sentry初期化（エラー報告）
4. 画面向き設定読み込み
5. `InvenTreeApp`起動

### 6.2 ビジネスロジック

#### API通信クラス

```dart
// ファイル: lib/api.dart:171-179
class InvenTreeAPI {
  factory InvenTreeAPI() {
    return _api;
  }

  // シングルトンインスタンス
  static final InvenTreeAPI _api = InvenTreeAPI._internal();

  // 接続状態
  bool _connected = false;
  bool _connecting = false;

  // プロファイル情報
  UserProfile? profile;

  // HTTPメソッド
  Future<APIResponse> get(String url, {...}) async { ... }
  Future<APIResponse> post(String url, {...}) async { ... }
  Future<APIResponse> patch(String url, {...}) async { ... }
  Future<APIResponse> delete(String url) async { ... }

  // 接続管理
  Future<bool> connectToServer(UserProfile prf) async { ... }
  void disconnectFromServer() { ... }
  bool isConnected() { ... }
}
```

#### バーコードハンドラー

```dart
// ファイル: lib/barcode/handler.dart:21-130
class BarcodeHandler {
  BarcodeHandler();

  String getOverlayText(BuildContext context) => "Barcode Overlay";

  // バーコードマッチ時
  Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
    // Override this function
  }

  // バーコード未知の場合
  Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
    barcodeFailureTone();
    showSnackIcon(
      (data["error"] ?? L10().barcodeNoMatch) as String,
      success: false,
    );
  }

  // バーコード処理
  Future<void> processBarcode(
    String barcode, {
    String url = "barcode/",
    Map<String, dynamic> extra_data = const {},
  }) async {
    // API通信でバーコード照合
    response = await InvenTreeAPI().post(url, body: {"barcode": barcode, ...});

    if (data.containsKey("success")) {
      await onBarcodeMatched(data);
    } else if (data.containsKey("error")) {
      await onBarcodeUnknown(data);
    }
  }
}
```

### 6.3 データアクセス

#### 設定管理

```dart
// ファイル: lib/preferences.dart:68-185
class InvenTreePreferencesDB {
  // NoSQLデータベース（sembast）を使用
  Future<Database> get database async {
    // ...
  }
}

class InvenTreeSettingsManager {
  factory InvenTreeSettingsManager() {
    return _manager;
  }

  // 値の取得
  Future<dynamic> getValue(String key, dynamic backup) async {
    dynamic value = await store.record(key).get(await _db);
    return value ?? backup;
  }

  // 値の保存
  Future<void> setValue(String key, dynamic value) async {
    await store.record(key).put(await _db, value);
  }
}
```

#### ユーザープロファイル

```dart
// ファイル: lib/user_profile.dart:6-57
class UserProfile {
  UserProfile({
    this.key,
    this.name = "",
    this.server = "",
    this.token = "",
    this.selected = false,
  });

  factory UserProfile.fromJson(int key, Map<String, dynamic> json, bool isSelected) =>
      UserProfile(
        key: key,
        name: (json["name"] ?? "") as String,
        server: (json["server"] ?? "") as String,
        token: (json["token"] ?? "") as String,
        selected: isSelected,
      );

  int? key;
  String name = "";
  String server = "";
  String token = "";
  bool selected = false;
}
```

### 6.4 ユーティリティ/共通機能

```dart
// ファイル: lib/helpers.dart（抜粋）
// デバッグログ出力
void debug(String message) {
  if (kDebugMode) {
    print(message);
  }
}

// 数値フォーマット
String simpleNumberString(double value) {
  // 整数なら小数点なし、そうでなければ小数点あり
}

// 通貨表示
String renderCurrency(double? value, String currency) {
  // ...
}
```

---

## 7. よく使われるパターン

> このセクションでは、コード内で頻出するパターンを解説します。

### パターン一覧

| パターン | 説明 | 出現頻度 | 代表的なファイル |
|---------|------|---------|-----------------|
| Singleton | 単一インスタンス保証 | 高 | `api.dart`, `preferences.dart` |
| Factory Constructor | 生成ロジックのカプセル化 | 高 | `model.dart`, `user_profile.dart` |
| RefreshableState | プルリフレッシュUI | 高 | `refreshable_state.dart` |
| Model Inheritance | モデル継承 | 高 | `inventree/*.dart` |
| Builder Pattern | UI構築 | 中 | `widget/*.dart` |

### 各パターンの詳細

#### パターン1: Singletonパターン

**目的:** アプリ全体で1つのインスタンスを共有し、状態の一貫性を保つ

```dart
// ファイル: lib/api.dart:171-179
class InvenTreeAPI {
  factory InvenTreeAPI() {
    return _api;
  }

  InvenTreeAPI._internal();

  static final InvenTreeAPI _api = InvenTreeAPI._internal();
}
```

**使用方法:**
```dart
// どこからでも同じインスタンスにアクセス
InvenTreeAPI().isConnected();
InvenTreeAPI().get("stock/");
```

#### パターン2: RefreshableState

**目的:** プルリフレッシュ機能を持つ画面の共通基底クラス

```dart
// ファイル: lib/widget/refreshable_state.dart:195-288
abstract class RefreshableState<T extends StatefulWidget> extends State<T>
    with BaseWidgetProperties {

  bool loading = false;

  // 子クラスでオーバーライド
  Future<void> request(BuildContext context) async {
    return;
  }

  // リフレッシュ処理
  Future<void> refresh(BuildContext context) async {
    if (!mounted) return;

    setState(() {
      loading = true;
    });

    await request(context);

    if (!mounted) return;

    setState(() {
      loading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    // RefreshIndicatorでラップされたScaffold
    return Scaffold(
      body: RefreshIndicator(
        onRefresh: () async {
          refresh(context);
        },
        child: body,
      ),
    );
  }
}
```

**使用例:**
```dart
// ファイル: lib/widget/stock/stock_detail.dart:41-45
class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {

  @override
  Future<void> request(BuildContext context) async {
    // データ取得処理
    await widget.item.reload();
    part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?;
  }
}
```

#### パターン3: Model Inheritance

**目的:** 共通のAPI操作とデータアクセスをベースクラスで提供

```dart
// ファイル: lib/inventree/model.dart:40-863
class InvenTreeModel {
  Map<String, dynamic> jsondata = {};

  // 共通アクセサ
  String getString(String key, {String backup = ""}) { ... }
  int getInt(String key, {int backup = -1}) { ... }

  // CRUD操作
  Future<bool> reload() async { ... }
  Future<bool> delete() async { ... }
  Future<List<InvenTreeModel>> list({...}) async { ... }
}

// ファイル: lib/inventree/stock.dart:134-574
class InvenTreeStockItem extends InvenTreeModel {
  @override
  String get URL => "stock/";

  // 在庫固有のプロパティ
  double get quantity => getDouble("quantity");
  String get serialNumber => getString("serial");

  // 在庫固有のメソッド
  Future<bool> countStock(double q, {String? notes}) async { ... }
  Future<bool> transferStock(int location, {...}) async { ... }
}
```

---

## 8. 業務フロー追跡の実践例

> このセクションでは、実際の業務フローをコードで追跡する方法を解説します。

### 8.1 フロー追跡の基本手順

1. エントリーポイントを特定
2. 処理の流れを追跡（呼び出し関係を追う）
3. データの変換を確認
4. 最終的な出力を確認

### 8.2 フロー追跡の実例

#### 例1: 在庫数量の更新フロー

**概要:** ユーザーが在庫アイテムの数量を追加するフロー

**処理フロー:**
```
[ユーザー: 追加ボタンタップ]
    ↓
[_addStockDialog()]
    ↓
[launchApiForm()]
    ↓
[InvenTreeAPI.post()]
    ↓
[サーバーレスポンス]
    ↓
[refresh() -> 画面更新]
```

**詳細な追跡:**

1. **ダイアログ表示** (`lib/widget/stock/stock_detail.dart:367-391`)
```dart
Future<void> _addStockDialog() async {
  Map<String, dynamic> fields = {
    "pk": {
      "parent": "items",
      "nested": true,
      "hidden": true,
      "value": widget.item.pk,
    },
    "quantity": {"parent": "items", "nested": true, "value": 0},
    "notes": {},
  };

  launchApiForm(
    context,
    L10().addStock,
    InvenTreeStockItem.addStockUrl(),  // "stock/add/"
    fields,
    method: "POST",
    icon: TablerIcons.circle_plus,
    onSuccess: (data) async {
      _stockUpdateMessage(true);
      refresh(context);
    },
  );
}
```

2. **APIフォーム送信** (`lib/api_form.dart`内)
```dart
// フォームデータをAPIに送信
response = await InvenTreeAPI().post(url, body: formData);
```

3. **API通信** (`lib/api.dart:1120-1137`)
```dart
Future<APIResponse> post(
  String url, {
  Map<String, dynamic> body = const {},
  int? expectedStatusCode = 201,
}) async {
  HttpClientRequest? request = await apiRequest(url, "POST");

  return completeRequest(
    request,
    data: json.encode(body),
    statusCode: expectedStatusCode,
  );
}
```

4. **成功時のコールバック** (`lib/widget/stock/stock_detail.dart:386-389`)
```dart
onSuccess: (data) async {
  _stockUpdateMessage(true);  // スナックバー表示
  refresh(context);           // 画面リフレッシュ
}
```

#### 例2: バーコードスキャン処理フロー

**概要:** バーコードをスキャンして対応するアイテムを表示する

**処理フロー:**
```
[カメラでバーコード検出]
    ↓
[BarcodeHandler.processBarcode()]
    ↓
[InvenTreeAPI.post("barcode/")]
    ↓
[サーバーでマッチング]
    ↓
[onBarcodeMatched() / onBarcodeUnknown()]
    ↓
[該当画面へ遷移]
```

**詳細な追跡:**

1. **バーコード処理開始** (`lib/barcode/handler.dart:57-129`)
```dart
Future<void> processBarcode(
  String barcode, {
  String url = "barcode/",
  Map<String, dynamic> extra_data = const {},
}) async {
  barcode = barcode.trim();

  // 空バーコードチェック
  if (barcode.isEmpty) {
    barcodeFailureTone();
    showSnackIcon(L10().barcodeError, success: false);
    return;
  }

  // サーバーに送信
  response = await InvenTreeAPI().post(
    url,
    body: {"barcode": barcode, ...extra_data},
    expectedStatusCode: null,
  );

  Map<String, dynamic> data = response.asMap();

  // 結果に応じて処理分岐
  if (data.containsKey("success")) {
    await onBarcodeMatched(data);
  } else if (data.containsKey("error")) {
    await onBarcodeUnknown(data);
  } else {
    await onBarcodeUnhandled(data);
  }
}
```

2. **バーコードマッチ時（子クラスでオーバーライド）**
```dart
// lib/barcode/stock.dart などの子クラスで実装
@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {
  // マッチしたアイテムの画面へ遷移
  if (data.containsKey("stockitem")) {
    var item = await InvenTreeStockItem().get(data["stockitem"]["pk"]);
    item?.goToDetailPage(context);
  }
}
```

### 8.3 フロー追跡チェックリスト

- [ ] エントリーポイントを特定したか
- [ ] 呼び出し関係を把握したか
- [ ] データの変換ポイントを確認したか
- [ ] エラーハンドリングを確認したか
- [ ] 最終的な出力を確認したか

---

## 9. 設計書の参照順序

> このセクションでは、プロジェクト理解のための設計書参照順序を案内します。

### 9.1 目的別ロードマップ

#### 全体像を把握したい場合
1. `README.md` - プロジェクト概要
2. `pubspec.yaml` - 依存関係とバージョン情報
3. `lib/main.dart` - エントリーポイント
4. `lib/widget/home.dart` - ホーム画面構成

#### 特定機能を理解したい場合
1. 対象の`widget/*.dart` - UI層
2. 対象の`inventree/*.dart` - モデル層
3. `lib/api.dart` - API通信
4. 関連テストファイル

#### 改修作業を行う場合
1. 対象画面のWidgetファイル
2. 関連するModelファイル
3. `test/`内のテストケース
4. エラーハンドリング（`sentry.dart`）

### 9.2 ドキュメント一覧

| ドキュメント | 概要 | 参照タイミング |
|-------------|------|---------------|
| `README.md` | プロジェクト概要・インストール方法 | 最初に参照 |
| `CONTRIBUTING.md` | コントリビューションガイド | 開発参加時 |
| `pubspec.yaml` | パッケージ依存関係 | 依存関係確認時 |
| `l10n.yaml` | 国際化設定 | 多言語対応時 |

---

## 10. トラブルシューティング

> このセクションでは、コードリーディング時によくある問題と解決法を解説します。

### よくある疑問と回答

#### Q: `BuildContext`とは何ですか？
A: FlutterのウィジェットツリーにおけるWidgetの位置を表すオブジェクトです。ナビゲーション、テーマ取得、国際化テキスト取得などに使用します。`context`という名前で渡されることが多いです。

#### Q: `async/await`と`Future`の関係は？
A: `Future`はDartの非同期処理を表すクラスです。`async`キーワードを付けた関数は`Future`を返し、`await`で結果を待機できます。

```dart
// 同等のコード
Future<void> method1() async {
  final result = await someAsyncOperation();
}

Future<void> method2() {
  return someAsyncOperation().then((result) {
    // 処理
  });
}
```

#### Q: `setState()`はいつ呼ぶべき？
A: StatefulWidgetの状態が変更され、UIを再描画したい時に呼びます。ただし`mounted`チェックを忘れずに。

```dart
if (mounted) {
  setState(() {
    loading = false;
  });
}
```

#### Q: `final`と`const`の違いは？
A:
- `final`: 実行時に一度だけ値が設定される（遅延初期化可能）
- `const`: コンパイル時に値が確定（コンパイル時定数のみ）

### デバッグのヒント

1. **`debug()`関数を活用**: `lib/helpers.dart`のdebug関数でログ出力
2. **Sentryエラー報告**: `lib/inventree/sentry.dart`でエラー報告の仕組みを確認
3. **APIレスポンス確認**: `APIResponse`クラスの`statusCode`と`data`をチェック
4. **Widget Inspector**: Flutter DevToolsのWidget Inspectorでツリー構造を確認

---

## 付録

### A. 用語集

| 用語 | 説明 |
|-----|------|
| InvenTree | オープンソースの在庫管理システム |
| StockItem | 在庫アイテム（物理的な在庫単位） |
| Part | 部品（製品定義） |
| PartCategory | 部品カテゴリ |
| StockLocation | 在庫保管場所 |
| PurchaseOrder | 購入注文 |
| SalesOrder | 販売注文 |
| Widget | FlutterのUI構成要素 |
| State | StatefulWidgetの状態管理オブジェクト |
| BuildContext | ウィジェットツリー上の位置情報 |

### B. ファイル一覧

| ファイル/ディレクトリ | 説明 | 主な内容 |
|---------------------|------|---------|
| `lib/main.dart` | エントリーポイント | アプリ起動、初期化 |
| `lib/api.dart` | API通信 | HTTP通信、認証 |
| `lib/inventree/model.dart` | ベースモデル | 共通CRUD操作 |
| `lib/inventree/stock.dart` | 在庫モデル | StockItem, StockLocation |
| `lib/inventree/part.dart` | 部品モデル | Part, PartCategory |
| `lib/widget/home.dart` | ホーム画面 | メインナビゲーション |
| `lib/widget/refreshable_state.dart` | 基底State | プルリフレッシュ |
| `lib/barcode/handler.dart` | バーコード処理 | スキャン処理基底 |
| `lib/preferences.dart` | 設定管理 | NoSQLデータベース |
| `lib/user_profile.dart` | プロファイル | サーバー接続情報 |

### C. 参考資料

- [Flutter公式ドキュメント](https://docs.flutter.dev/)
- [Dart言語ツアー](https://dart.dev/language)
- [InvenTree公式ドキュメント](https://inventree.readthedocs.io/)
- [InvenTreeモバイルアプリドキュメント](https://inventree.readthedocs.io/en/latest/app/app/)
- [sembast NoSQLドキュメント](https://pub.dev/packages/sembast)
