# 画面設計書 30-会社一覧画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションにおける「会社一覧画面」の設計仕様を定義する。会社（Company）をページネーション付きで一覧表示し、検索・フィルタリング機能を提供する画面である。サプライヤー、顧客、製造元など、異なる種類の会社を統一的に表示する。

### 本画面の処理概要

会社一覧画面は、InvenTreeサーバーに登録されている会社をページネーション付きの一覧形式で表示する画面である。コンストラクタに渡されるfiltersパラメータにより、サプライヤーのみ、顧客のみなど、表示する会社の種類を絞り込むことができる。

**業務上の目的・背景**：購買管理・販売管理において、取引先の情報を一元的に管理することは重要である。本画面は、サプライヤー（購入先）、顧客（販売先）、製造元（部品製造元）など、異なる役割の会社を統一的に検索・閲覧できる機能を提供する。

**画面へのアクセス方法**：
- ホーム画面の「サプライヤー」タイルをタップ（is_supplier: true）
- ホーム画面の「顧客」タイルをタップ（is_customer: true）
- 検索画面から会社検索結果として遷移
- ドロワーメニューからアクセス（将来対応の可能性）

**主要な操作・処理内容**：
1. 会社一覧のページネーション表示（25件ずつ取得）
2. 検索ボックスによるインクリメンタルサーチ（500msデバウンス）
3. アクティブ状態によるフィルタリング
4. 会社タップで詳細画面へ遷移
5. 新規会社の作成

**画面遷移**：
- 遷移元：ホーム画面、検索画面
- 遷移先：会社詳細画面（CompanyDetailWidget）

**権限による表示制御**：
- 新規作成ボタンは `checkPermission("company", "add")` がtrueの場合のみ表示
- 会社の閲覧には purchase_order, sales_order, return_order のいずれかの権限が必要

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 38 | 会社一覧表示 | 主機能 | 会社（サプライヤー、顧客など）のページネーション付き一覧表示 |
| 39 | 会社詳細表示 | 遷移先機能 | 会社選択時に会社詳細画面へ遷移 |

## 画面種別

一覧

## URL/ルーティング

Flutter Navigator によるプッシュ遷移。`CompanyListWidget` ウィジェットのインスタンス化時に `title` と `filters` パラメータを渡して表示内容を制御する。

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| title | 入力 | String | Yes | 画面タイトル（例: "Suppliers", "Customers"） |
| filters | 入力 | Map<String, String> | Yes | 一覧表示時のフィルタ条件（例: {"is_supplier": "true"}） |

## 表示項目

| 項目名 | 表示位置 | データ型 | 説明 |
|--------|----------|----------|------|
| 会社画像 | ListTile.leading | Image | 会社のサムネイル画像 |
| 会社名 | ListTile.title | String | 会社の名称 |
| 説明 | ListTile.subtitle | String | 会社の説明文 |
| 検索結果件数 | タイトル下 | String | "Results: N" 形式で表示 |

## イベント仕様

### 1-会社タップ

会社リストの各アイテムをタップすると、該当会社の詳細画面（CompanyDetailWidget）へ遷移する。

**処理フロー**：
1. `onTap` コールバックが発火
2. `company.goToDetailPage(context)` を呼び出し
3. `Navigator.push` で `CompanyDetailWidget(this)` へ遷移

### 2-新規作成ボタン押下

SpeedDialの「作成」ボタンをタップすると、新規会社作成フォームが表示される。

**処理フロー**：
1. `SpeedDialChild.onTap` で `_addCompany(context)` を呼び出し
2. `InvenTreeCompany().createForm()` で作成フォームを表示
3. dataパラメータにwidget.filtersを渡す（会社種別を引き継ぎ）
4. 成功時に作成された会社詳細画面へ遷移

### 3-検索ボックス入力

検索ボックスにテキストを入力すると、500msのデバウンス後にサーバーへ検索クエリを送信する。

**処理フロー**：
1. `TextFormField.onChanged` でテキスト変更を検知
2. `updateSearchTerm()` メソッドを呼び出し
3. 500msタイマー後に `_pagingController.refresh()` で再検索

### 4-フィルタリング設定

フィルタアイコンをタップすると、フィルタリングのオプションダイアログが表示される。

**処理フロー**：
1. フィルタアイコン（`Icons.filter_alt`）をタップ
2. `_setOrderingOptions(context)` を呼び出し
3. `launchApiForm` でオプションダイアログを表示
4. 設定保存後に `_pagingController.refresh()` で一覧を再取得

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | Company | SELECT | 会社データのページネーション取得 |
| 検索 | Company | SELECT | 検索条件に合致する会社の取得 |
| 新規作成 | Company | INSERT | 新規会社の作成 |

### テーブル別更新項目詳細

#### Company テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | pk, name, description, image, thumbnail, is_supplier, is_manufacturer, is_customer, active | フィルタ条件とページネーションパラメータに基づく | APIエンドポイント: /api/company/ |
| INSERT | name, description, website, is_supplier, is_manufacturer, is_customer, currency, active | フォーム入力値 | createForm()経由 |

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|---------------|----------|---------------|
| MSG_NO_RESULTS | 情報 | 検索結果が0件 | "No results" |
| MSG_RESULTS | 情報 | 検索結果表示 | "Results: {件数}" |

## 例外処理

| 例外条件 | 処理内容 | ユーザーへの表示 |
|---------|---------|-----------------|
| APIエラー | エラーをSentryに報告 | 一覧表示が停止、エラー状態を表示 |
| ネットワークエラー | 再試行可能な状態を維持 | プルダウンでリフレッシュ可能 |
| 権限不足 | 作成ボタン非表示 | 閲覧のみ可能 |

## 備考

- ページサイズは25件固定（`_pageSize = 25`）
- 検索のデバウンス時間は500ms
- フィルタオプションはactiveのみ（`supportsCompanyActiveStatus`対応APIの場合）
- ソートオプションは提供されていない
- 画面タイトルはコンストラクタのtitleパラメータで動的に設定
  - サプライヤー一覧: L10().suppliers
  - 顧客一覧: L10().customers
  - 会社検索結果: L10().companies
- 新規作成時、filtersパラメータがdataとして渡されるため、作成する会社種別が自動的に設定される
- InvenTreeCompanyのrolesRequiredはpurchase_order, sales_order, return_orderのいずれか

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | company.dart | `lib/inventree/company.dart` | InvenTreeCompanyクラス（14-112行）。URL="company/"、isSupplier, isManufacturer, isCustomer, activeゲッター |
| 1-2 | model.dart | `lib/inventree/model.dart` | InvenTreeModel基底クラス。共通機能（reload, list, listPaginated等） |

**読解のコツ**: `InvenTreeCompany` は `InvenTreeModel` を継承している。URL="company/" でAPIエンドポイントが定義されている。formFields()で作成・編集フォームのフィールドを定義。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | company_list.dart | `lib/widget/company/company_list.dart` | CompanyListWidget（17-74行）とPaginatedCompanyList（76-135行）の関係 |

**主要処理フロー**:
1. **17-27行**: `CompanyListWidget` StatefulWidgetの定義。title, filtersをコンストラクタで受け取る
2. **33行**: `getAppBarTitle()` で `widget.title` を返す（動的タイトル）
3. **70-73行**: `getBody()` で `PaginatedCompanyList` を返す
4. **76-135行**: `_CompanyListState` で実際のページネーションロジックを実装

#### Step 3: フィルタオプションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | company_list.dart | `lib/widget/company/company_list.dart` | filterOptions（93-105行）の定義 |

**主要処理フロー**:
- **93-105行**: activeフィルタオプション（supportsCompanyActiveStatus対応時のみ）
- ソートオプションは定義されていない

#### Step 4: リストアイテム構築を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | company_list.dart | `lib/widget/company/company_list.dart` | buildItem()メソッド（122-134行）。ListTileの構築方法 |

**主要処理フロー**:
- **124行**: InvenTreeModelをInvenTreeCompanyにキャスト
- **126-133行**: ListTileを構築（name, description, image）
- **130-132行**: onTapでgoToDetailPage()を呼び出し

#### Step 5: 新規作成機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | company_list.dart | `lib/widget/company/company_list.dart` | actionButtons()（51-68行）と_addCompany()（35-49行） |

**主要処理フロー**:
- **55行**: checkPermission("company", "add")で権限チェック
- **36-48行**: createForm()で作成フォームを表示、成功時に詳細画面へ遷移
- **39行**: dataにwidget.filtersを渡すことで、会社種別（is_supplier等）を引き継ぎ

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

```
CompanyListWidget (StatefulWidget)
    |
    +-- _CompanyListWidgetState (RefreshableState)
           |
           +-- getBody() --> PaginatedCompanyList
           |                    |
           |                    +-- _CompanyListState (PaginatedSearchState)
           |                           |
           |                           +-- requestPage()
           |                           |      |
           |                           |      +-- InvenTreeCompany().listPaginated()
           |                           |                      |
           |                           |                      +-- API GET /api/company/
           |                           |
           |                           +-- buildItem() --> ListTile
           |                                              |
           |                                              +-- onTap --> company.goToDetailPage()
           |
           +-- actionButtons()
                  |
                  +-- _addCompany() --> InvenTreeCompany().createForm()
                                              |
                                              +-- onSuccess --> goToDetailPage()
```

### データフロー図

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

title (String)   ---> CompanyListWidget                 ---> AppBarTitle
                             |
filters (Map)    ---> PaginatedCompanyList              ---> ListView
                             |
                             +-- requestPage()
                             |      |
                             |      +-- API: /api/company/?limit=25&offset=N&{filters}
                             |             |
                             |             +-- InvenTreePageResponse
                             |
                             +-- buildItem()
                                    |
                                    +-- InvenTreeCompany --> ListTile
                                             |
                                             +-- company.image --> leading
                                             +-- company.name --> title
                                             +-- company.description --> subtitle
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| company_list.dart | `lib/widget/company/company_list.dart` | ソース | 会社一覧画面のメインウィジェット |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通基盤クラス |
| company.dart | `lib/inventree/company.dart` | ソース | 会社データモデル |
| model.dart | `lib/inventree/model.dart` | ソース | モデル基底クラス |
| api.dart | `lib/api.dart` | ソース | API通信クライアント、権限チェック |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能なState基底クラス |
| company_detail.dart | `lib/widget/company/company_detail.dart` | ソース | 会社詳細（遷移先） |
