# 機能設計書 45-バーコード解除

## 概要

本ドキュメントは、InvenTreeアプリにおけるアイテムからカスタムバーコードを解除（紐付け解除）する機能の設計を記述する。登録済みのバーコードとアイテムの関連付けを削除する機能である。

### 本機能の処理概要

バーコード解除機能は、InvenTreeデータベースに登録済みのカスタムバーコードとアイテムの紐付けを解除する機能である。これにより、不要になったバーコード関連付けを削除し、バーコードを別のアイテムに再割当できるようにする。

**業務上の目的・背景**：在庫管理業務において、アイテムに割り当てられたバーコードを変更する必要が生じる場合がある。例えば、ラベルの貼り替え、誤登録の修正、アイテムの廃棄などの場面で、既存のバーコード紐付けを解除する必要がある。本機能により、柔軟なバーコード管理が可能となり、運用の変更や修正に対応できる。

**機能の利用シーン**：
- 誤って登録したバーコードの修正
- 古いバーコードラベルから新しいラベルへの切り替え
- アイテム廃棄時のバーコード関連付け削除
- バーコードの再割当（別アイテムへの移行）前の準備

**主要な処理内容**：
1. 対象アイテムの詳細画面からバーコード解除アクションを起動
2. InvenTree APIのunlinkBarcodeエンドポイントで紐付けを削除
3. 成功/失敗の通知と画面のリフレッシュ

**関連システム・外部連携**：
- InvenTree REST API（barcode/unlink/エンドポイント）

**権限による制御**：対象アイテムの編集権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 部品詳細画面 | 遷移元画面 | バーコード解除ボタン押下で解除処理実行 |
| 25 | 在庫アイテム詳細画面 | 遷移元画面 | バーコード解除ボタン押下で解除処理実行 |
| 23 | 在庫ロケーション画面 | 遷移元画面 | バーコード解除ボタン押下で解除処理実行 |

## 機能種別

データ削除 / バーコード連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| model | String | Yes | 対象モデルタイプ（例: "part", "stockitem"） | 有効なモデルタイプ |
| pk | int | Yes | 対象アイテムのプライマリキー | 0より大きい |

### 入力データソース

- 呼び出し元画面からのアイテム情報（model, pk）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 解除結果 | bool | バーコード解除の成功/失敗 |
| 通知メッセージ | String | 成功時: requestSuccessful、失敗時: requestFailed |

### 出力先

- スナックバー（結果通知）
- 呼び出し元画面のリフレッシュ

## 処理フロー

### 処理シーケンス

```
1. バーコード解除アクション起動（customBarcodeAction）
   ├─ 現在のbarcode状態を確認
   └─ barcodeが登録済みの場合、解除モードを表示

2. ユーザーが解除ボタンをタップ
   └─ onTapコールバック実行

3. バーコード解除API呼び出し
   ├─ InvenTreeAPI().unlinkBarcode({model: pk})
   ├─ 成功時: requestSuccessfulスナックバー
   └─ 失敗時: requestFailedスナックバー

4. 画面リフレッシュ
   └─ state.refresh(context)
```

### フローチャート

```mermaid
flowchart TD
    A[詳細画面でバーコード解除ボタン] --> B{現在barcode登録あり?}
    B -->|No| C[割当モード表示]
    B -->|Yes| D[解除モード]
    D --> E[解除ボタン表示]
    E --> F[ユーザーがタップ]
    F --> G[unlinkBarcode API呼び出し]
    G --> H{解除成功?}
    H -->|Yes| I[requestSuccessfulスナックバー]
    H -->|No| J[requestFailedスナックバー]
    I --> K[画面リフレッシュ]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-45-01 | 登録済み条件 | barcodeが登録済みの場合のみ解除アクション表示 | アクションボタン表示時 |
| BR-45-02 | 即時解除 | 確認ダイアログなしで即座に解除を実行 | ボタンタップ時 |
| BR-45-03 | バーコード再利用 | 解除されたバーコードは別アイテムに再割当可能 | 解除後 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| バーコード解除 | Barcode | DELETE | バーコードとアイテムの紐付け削除 |

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

#### Barcode

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | model_type = model AND model_pk = pk | unlinkBarcode API経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-45-01 | 解除失敗 | unlinkBarcode APIがfalseを返却 | requestFailedスナックバー |
| E-45-02 | API通信エラー | サーバーとの通信失敗 | requestFailedスナックバー |

### リトライ仕様

自動リトライは行わない。ユーザーは再度ボタンをタップすることで手動リトライ可能。

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

REST APIベースのため、各操作は独立したHTTPリクエストとして処理される。

## パフォーマンス要件

- バーコード解除: 1秒以内

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

- 認証トークンを使用したAPIアクセス
- 対象アイテムの編集権限チェック（canEdit）

## 備考

- SpeedDialChildとしてアクションボタンを提供
- 確認ダイアログは表示しない（即時実行）
- No.44（バーコード割当）と対になる機能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | barcode.dart | `lib/barcode/barcode.dart` | customBarcodeAction関数の解除モード部分（421-438行目） |

**読解のコツ**: customBarcodeActionはバーコードの状態（空/登録済み）に応じて割当/解除のどちらかを表示する。elseブロックが解除モードの処理。

#### Step 2: 解除処理の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | barcode.dart | `lib/barcode/barcode.dart` | SpeedDialChildの解除モード |

**主要処理フロー**:
1. **422-424行目**: SpeedDialChildの生成（アイコン、ラベル設定）
2. **425行目**: onTapコールバックの設定
3. **426-428行目**: unlinkBarcode API呼び出し
4. **429-432行目**: 結果に応じたスナックバー表示
5. **434行目**: state.refresh()で画面リフレッシュ

#### Step 3: API呼び出しを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | api.dart | `lib/api.dart` | unlinkBarcodeメソッド |

**主要処理フロー**:
- unlinkBarcode APIに{model: pk}を送信
- 成功時はtrueを返却

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

```
customBarcodeAction(context, state, barcode, model, pk)
    │
    └─ barcode.isNotEmpty? → 解除モード
           │
           ├─ SpeedDialChild
           │      ├─ icon: Icons.barcode_reader
           │      ├─ label: L10().barcodeUnassign
           │      │
           │      └─ onTap:
           │             │
           │             ├─ InvenTreeAPI().unlinkBarcode({model: pk})
           │             │      │
           │             │      └─ POST barcode/unlink/
           │             │
           │             ├─ showSnackIcon(requestSuccessful/requestFailed)
           │             │
           │             └─ state.refresh(context)
           │
           └─ return SpeedDialChild
```

### データフロー図

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

詳細画面                    customBarcodeAction
(model, pk, barcode="xxx")        │
        │                         ▼
        │              barcode.isNotEmpty判定
        │                         │
        │                         ▼
        │              SpeedDialChild生成（解除モード）
        │                         │
        │                         ▼
ユーザータップ ─────────▶ onTap実行
                                  │
                                  ▼
                    InvenTreeAPI.unlinkBarcode({model: pk})
                                  │
                                  ▼
                         成功/失敗スナックバー
                                  │
                                  ▼
                         state.refresh()
                                  │
                                  ▼
                         アイテムのbarcode = "" に更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| barcode.dart | `lib/barcode/barcode.dart` | ソース | customBarcodeAction（解除モード） |
| api.dart | `lib/api.dart` | ソース | InvenTreeAPI.unlinkBarcode |
| snacks.dart | `lib/widget/snacks.dart` | ソース | スナックバー通知 |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | state.refresh()メソッド |
| part_detail.dart | `lib/widget/part/part_detail.dart` | ソース | パーツ詳細（呼び出し元） |
| stock_detail.dart | `lib/widget/stock/stock_detail.dart` | ソース | 在庫詳細（呼び出し元） |
| location_display.dart | `lib/widget/stock/location_display.dart` | ソース | ロケーション画面（呼び出し元） |
