# 機能設計書 36-出荷管理

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける出荷（Shipment）管理機能の設計を定義する。出荷管理は、受注に対する出荷を作成・管理し、引当された在庫を顧客に発送するプロセスを管理する機能である。

### 本機能の処理概要

この機能では、受注に対する出荷を一覧表示し、各出荷の詳細確認・編集・出荷処理（Ship）・チェック操作を行うことができる。

**業務上の目的・背景**：受注を完了するためには、引当された在庫を実際に顧客へ発送する必要がある。出荷管理機能は、出荷単位での追跡、チェック状態の管理、出荷日・配達日・追跡番号・請求書番号の記録を可能にする。これにより、複数回に分けて出荷する場合や、出荷前のダブルチェックプロセスを実装できる。また、出荷に対する引当在庫を一覧表示でき、ピッキング・梱包作業を支援する。

**機能の利用シーン**：
- 受注詳細画面から出荷一覧を確認したい場合
- 新しい出荷を作成したい場合
- 出荷の詳細情報（追跡番号、請求書番号等）を編集したい場合
- 出荷をチェック済みにしたい場合
- 出荷を発送完了にしたい場合
- 出荷に画像を添付したい場合

**主要な処理内容**：
1. 受注IDに紐づく出荷をAPIから取得
2. 各出荷の参照番号、ステータス、日付を表示
3. 出荷タップで詳細画面へ遷移
4. 出荷の編集、チェック、発送処理
5. 出荷に対する引当在庫の表示

**関連システム・外部連携**：
- InvenTree REST API (`/api/order/so/shipment/`, `/api/order/so/shipment/{pk}/ship/`)

**権限による制御**：
- 出荷の編集権限がある場合のみ、編集・チェック・発送操作が可能
- 添付ファイルアップロードには編集権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホーム画面 | 遷移元画面 | 出荷タイル押下時に出荷一覧画面へ遷移 |
| 29 | 販売注文詳細画面 | 遷移元画面 | 出荷タブで出荷一覧表示・出荷追加 |

## 機能種別

データ表示（CRUD操作のRead・Update・Create）/ 一覧表示 / 詳細表示 / ステータス変更

## 入力仕様

### 入力パラメータ（一覧）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filters | Map<String, String> | Yes | フィルタ条件（order: 受注ID） | - |
| title | String | No | 画面タイトル | - |

### 入力パラメータ（詳細）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| shipment | InvenTreeSalesOrderShipment | Yes | 対象の出荷オブジェクト | PKが1以上であること |

### 入力データソース

- 受注詳細画面からのフィルタ条件
- InvenTree REST API (`/api/order/so/shipment/`)

## 出力仕様

### 出力データ（一覧）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| order.reference | String | 親受注の参照番号 |
| reference | String | 出荷参照番号 |
| order.description | String | 親受注の説明 |
| isShipped | bool | 出荷済みフラグ |
| shipment_date | String | 出荷日 |

### 出力データ（詳細）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| reference | String | 出荷参照番号 |
| invoice_number | String | 請求書番号 |
| tracking_number | String | 追跡番号 |
| isChecked | bool | チェック済みフラグ |
| isShipped | bool | 出荷済みフラグ |
| isDelivered | bool | 配達済みフラグ |
| shipment_date | String | 出荷日 |
| delivery_date | String | 配達日 |
| link | String | 外部リンク |

### 出力先

画面表示（一覧：ListTile、詳細：タブ構成）

## 処理フロー

### 処理シーケンス（一覧）

```
1. ウィジェット初期化
   └─ PaginatedSOShipmentListがフィルタを受け取る
2. ページデータ取得
   └─ InvenTreeSalesOrderShipment().listPaginated()でAPIリクエスト
3. 一覧表示
   └─ 各出荷をListTileとして描画（ステータスアイコン付き）
4. アイテムタップ時
   └─ 出荷詳細画面へ遷移
```

### 処理シーケンス（詳細）

```
1. ウィジェット初期化
   └─ SOShipmentDetailWidgetが出荷オブジェクトを受け取る
2. データ読み込み
   ├─ shipment.reload()
   ├─ カメラ設定確認
   ├─ 親受注の取得
   └─ 添付ファイル数カウント
3. 画面構築
   └─ タブ構成（詳細・引当在庫）
4. ユーザー操作
   ├─ 編集ボタン → 編集フォーム
   ├─ チェック → チェック済み更新
   ├─ チェック解除 → チェック解除更新
   ├─ 発送 → 発送フォーム（日付、追跡番号、請求書番号）
   └─ カメラ → 画像アップロード
```

### フローチャート

```mermaid
flowchart TD
    A[画面表示開始] --> B[フィルタ条件受け取り]
    B --> C[APIからデータ取得]
    C --> D{データあり?}
    D -->|Yes| E[ListTileとして一覧表示]
    D -->|No| F[結果なしメッセージ表示]
    E --> G{ユーザー操作}
    G -->|アイテムタップ| H[SOShipmentDetailWidget表示]
    H --> I{詳細操作}
    I -->|編集| J[編集フォーム]
    I -->|チェック| K[checked_by更新]
    I -->|発送| L[発送フォーム]
    I -->|カメラ| M[画像アップロード]
    G -->|スクロール| N[次ページ取得]
    N --> C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-036-01 | ステータスアイコン | 出荷済みは緑のチェックアイコン、未出荷は黄色のキャンセルアイコン | 常時 |
| BR-036-02 | 日付表示 | 出荷済みの場合は出荷日を表示、未出荷の場合は"Pending"を表示 | 常時 |
| BR-036-03 | チェックボタン表示 | 未チェックかつ未出荷の場合のみチェックボタンを表示 | !isChecked && !isShipped |
| BR-036-04 | チェック解除ボタン | チェック済みかつ未出荷の場合のみチェック解除ボタンを表示 | isChecked && !isShipped |
| BR-036-05 | 発送ボタン表示 | 未出荷の場合のみ発送ボタンを表示 | !isShipped |
| BR-036-06 | カメラボタン表示 | INV_SO_SHOW_CAMERA設定がtrueかつ編集権限がある場合 | 設定・権限依存 |
| BR-036-07 | ページサイズ | 1ページあたり25件を取得 | 常時 |

### 計算ロジック

チェック状態判定:
```dart
bool isChecked = checked_by_id != null && checked_by_id! > 0;
bool isShipped = shipment_date != null && shipment_date!.isNotEmpty;
bool isDelivered = delivery_date != null && delivery_date!.isNotEmpty;
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | order_salesordershipment | SELECT | 受注IDで絞り込んだ出荷一覧取得 |
| 詳細取得 | order_salesordershipment | SELECT | 出荷詳細情報取得 |
| 編集 | order_salesordershipment | UPDATE | 出荷情報更新 |
| チェック | order_salesordershipment | UPDATE | checked_by更新 |
| 発送 | order_salesordershipment | UPDATE | shipment_date等更新 |

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

#### order_salesordershipment

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, reference, tracking_number, invoice_number, shipment_date, delivery_date, checked_by, order_id | order_id = 指定受注ID | order_detail含む |
| UPDATE（チェック） | checked_by | ユーザーID / null | チェック/解除 |
| UPDATE（発送） | shipment_date, tracking_number, invoice_number | フォーム入力値 | /ship/エンドポイント |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | APIトークンが無効 | ログイン画面へ遷移 |
| 403 | 権限エラー | 操作権限がない | エラーメッセージ表示 |
| 404 | 不存在エラー | 出荷が存在しない | 前画面へ戻る |
| 500 | サーバーエラー | サーバー側の問題 | リトライを促すメッセージ表示 |

### リトライ仕様

プルダウンによるリフレッシュ操作で再取得可能

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

- 一覧・詳細取得：読み取り専用
- 編集・チェック・発送：API側でトランザクション管理

## パフォーマンス要件

- 1ページあたり25件のデータを取得
- 詳細表示時に親受注を非同期取得
- 添付ファイル数は非同期カウント

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

- APIアクセスには認証トークンが必要
- 受注の閲覧権限（sales_order）が必要
- 編集・チェック・発送には編集権限が必要
- 添付ファイルアップロードはInvenTreeAttachmentで処理

## 備考

- 出荷詳細はタブ構成（Details, Allocated Stock）
- チェック機能は出荷前のダブルチェックプロセスを支援
- 発送フォームでshipment_dateのデフォルト値は現在日付

---

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

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

### 推奨読解順序

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

出荷のデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sales_order.dart | `lib/inventree/sales_order.dart` | InvenTreeSalesOrderShipmentクラスの構造（262-332行目） |

**読解のコツ**: InvenTreeSalesOrderShipmentはInvenTreeModelを継承している。SHIP_SHIPMENT_URLで発送用エンドポイントを生成。isChecked, isShipped, isDeliveredでステータスを判定。

#### Step 2: 一覧ウィジェットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | so_shipment_list.dart | `lib/widget/order/so_shipment_list.dart` | PaginatedSOShipmentListの構造全体 |

**主要処理フロー**:
1. **12-38行目**: SOShipmentListWidgetクラス（オプションのタイトル付き）
2. **40-50行目**: PaginatedSOShipmentListクラス定義
3. **52-77行目**: _PaginatedSOShipmentListStateクラス
4. **66-77行目**: requestPage()メソッド
5. **79-99行目**: buildItem()メソッド（ステータスアイコン、日付表示）

#### Step 3: 詳細ウィジェットを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | so_shipment_detail.dart | `lib/widget/order/so_shipment_detail.dart` | SOShipmentDetailWidgetの構造全体 |

**主要処理フロー**:
1. **22-29行目**: SOShipmentDetailWidgetクラス定義
2. **31-60行目**: appBarActions()。編集ボタン構築
3. **62-76行目**: _editShipment()メソッド
4. **78-107行目**: request()。データ再読み込み
5. **109-118行目**: _uploadImage()メソッド
6. **120-143行目**: _sendShipment()メソッド。発送フォーム表示
7. **145-216行目**: actionButtons()。カメラ・チェック・発送ボタン
8. **218-364行目**: shipmentTiles()。詳細タイル構築
9. **366-380行目**: タブ構成（Details, Allocated Stock）

#### Step 4: 発送処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | so_shipment_detail.dart | `lib/widget/order/so_shipment_detail.dart` | _sendShipment()（120-143行目） |

**主要処理フロー**:
- **122-130行目**: フォームフィールド構築（shipment_date, tracking_number, invoice_number）
- **132-142行目**: launchApiForm()でSHIP_SHIPMENT_URLへPOST

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

```
PaginatedSOShipmentList
    │
    └─ _PaginatedSOShipmentListState
           ├─ prefix → "so_shipment_"
           ├─ requestPage() → InvenTreeSalesOrderShipment().listPaginated()
           └─ buildItem()
                  ├─ shipment.order取得
                  ├─ ステータスアイコン（isShipped判定）
                  └─ onTap → shipment.goToDetailPage()

SOShipmentDetailWidget
    │
    └─ _SOShipmentDetailWidgetState
           ├─ getAppBarTitle() → "Shipment"
           ├─ appBarActions() → 編集ボタン
           │      └─ _editShipment() → editForm
           │
           ├─ request()
           │      ├─ shipment.reload()
           │      ├─ カメラ設定取得
           │      ├─ 親受注取得
           │      └─ 添付ファイル数カウント
           │
           ├─ actionButtons()
           │      ├─ _uploadImage() → 画像アップロード
           │      ├─ チェック → update({checked_by: userId})
           │      ├─ チェック解除 → update({checked_by: null})
           │      └─ _sendShipment() → 発送フォーム
           │
           ├─ shipmentTiles() → 詳細タブ内容
           │      ├─ 受注情報カード
           │      ├─ 参照番号
           │      ├─ 請求書番号
           │      ├─ 追跡番号
           │      ├─ チェック状態
           │      ├─ 出荷日
           │      ├─ 配達日
           │      ├─ 外部リンク
           │      ├─ ノート
           │      └─ 添付ファイル
           │
           └─ getTabs()
                  ├─ ListView(shipmentTiles)
                  └─ PaginatedSOAllocationList
```

### データフロー図

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

フィルタ条件 ───────▶ PaginatedSOShipmentList
(order: pk)                 │
                            ▼
                    requestPage()
                            │
                            ▼
                    InvenTreeAPI
                    /api/order/so/shipment/
                            │
                            ▼
                    buildItem()
                            │
                    ┌───────┼───────┐
                    ▼       ▼       ▼
                参照番号  ステータ  日付
                         スアイコン
                            │
                            ▼
                    onTap → SOShipmentDetailWidget
                            │
                    ┌───────┴───────┐
                    ▼               ▼
                詳細タブ         引当タブ
                (shipmentTiles)  (PaginatedSOAllocationList)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| so_shipment_list.dart | `lib/widget/order/so_shipment_list.dart` | ソース | 出荷一覧ウィジェット |
| so_shipment_detail.dart | `lib/widget/order/so_shipment_detail.dart` | ソース | 出荷詳細ウィジェット |
| sales_order.dart | `lib/inventree/sales_order.dart` | ソース | 出荷データモデル |
| so_allocation_list.dart | `lib/widget/order/so_allocation_list.dart` | ソース | 引当一覧ウィジェット |
| attachment.dart | `lib/inventree/attachment.dart` | ソース | 添付ファイル管理 |
| notes_widget.dart | `lib/widget/notes_widget.dart` | ソース | ノート表示・編集 |
| attachment_widget.dart | `lib/widget/attachment_widget.dart` | ソース | 添付ファイルウィジェット |
| paginator.dart | `lib/widget/paginator.dart` | ソース | ページネーション共通処理 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ可能State基底クラス |
| api_form.dart | `lib/api_form.dart` | ソース | APIフォーム表示 |
| preferences.dart | `lib/preferences.dart` | ソース | 設定管理（INV_SO_SHOW_CAMERA） |
