# バッチ設計書 19-check_overdue_purchase_orders

## 概要

本ドキュメントは、InvenTreeシステムにおける期限切れ発注書（PurchaseOrder）のチェックバッチ処理について記述します。このバッチは、目標日（target_date）が昨日に設定されている未完了の発注書を検出し、関係者に通知を送信します。

### 本バッチの処理概要

**業務上の目的・背景**：発注管理において、納期（目標日）の管理は重要な業務課題です。納期を過ぎた発注書は、サプライヤーへのフォローアップや代替調達の検討が必要になります。本バッチは、期限切れとなった発注書を自動検出し、発注書の作成者、担当者、および購読者に通知することで、遅延への迅速な対応を促します。

**バッチの実行タイミング**：日次（毎日1回）。Django-Qスケジューラによりスケジュールされ、バックグラウンドワーカーによって実行されます。

**主要な処理内容**：
1. 目標日が昨日かつステータスがオープンの発注書を検索
2. 目標日が昨日かつ未受領の発注明細行を検索
3. 重複を排除しながら各発注書に対して通知を送信
4. プラグインシステムへのイベント発火

**前後の処理との関連**：本バッチは独立して動作します。check_overdue_sales_orders、check_overdue_return_ordersと同様の構造を持つ関連バッチです。

**影響範囲**：通知テーブルに対して挿入操作を行います。発注書データ自体の変更は行いません。

## バッチ種別

通知配信

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 日次 |
| 実行時刻 | Django-Qスケジューラによる自動実行 |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron（Django-Qスケジューラ） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Django-Qワーカー稼働中 | バックグラウンドワーカーが稼働している必要がある |
| データベース接続可能 | PurchaseOrder、通知テーブルへのアクセスが可能であること |

### 実行可否判定

- スケジューラに登録されたタイミングで自動実行
- 特別な実行可否判定ロジックは存在しない

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | 本バッチはパラメータを取らない |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| order_purchaseorder | DB | 発注書テーブル |
| order_purchaseorderlineitem | DB | 発注明細行テーブル |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| 通知テーブル | DB | ユーザーへの期限切れ通知 |
| メール | メッセージ | 関係者へのメール通知（設定による） |
| プラグインイベント | イベント | OVERDUE イベントの発火 |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. バッチ開始
   └─ 昨日の日付を計算: yesterday = datetime.now().date() - 1日
2. 期限切れ発注書の検索
   └─ PurchaseOrder.objects.filter(target_date=yesterday, status__in=OPEN)
3. 期限切れ発注明細の検索
   └─ PurchaseOrderLineItem.objects.filter(
         target_date=yesterday,
         order__status__in=OPEN,
         received__lt=F('quantity')
       )
4. 重複排除用セット初期化
   └─ notified_orders = set()
5. 発注書レベルの通知送信
   └─ 各overdue_orderに対してnotify_overdue_purchase_order()呼び出し
   └─ notified_ordersに追加
6. 明細行レベルの通知送信
   └─ 各overdue_lineに対して:
      ├─ order.pkがnotified_ordersにない場合
      └─ notify_overdue_purchase_order(line.order)呼び出し
7. バッチ終了
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[昨日の日付計算]
    B --> C[期限切れ発注書検索]
    C --> D[期限切れ発注明細検索]
    D --> E[重複排除セット初期化]
    E --> F[発注書ごとに通知送信]
    F --> G{未通知の発注書?}
    G -->|はい| H[notify_overdue_purchase_order]
    G -->|いいえ| I[スキップ]
    H --> J[セットに追加]
    J --> K{次の発注書?}
    I --> K
    K -->|あり| G
    K -->|なし| L[明細行ごとに処理]
    L --> M{order.pkが未通知?}
    M -->|はい| N[notify_overdue_purchase_order]
    M -->|いいえ| O[スキップ]
    N --> P[セットに追加]
    P --> Q{次の明細行?}
    O --> Q
    Q -->|あり| M
    Q -->|なし| R[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 発注書検索 | order_purchaseorder | SELECT | 期限切れ発注書の検索 |
| 明細行検索 | order_purchaseorderlineitem | SELECT | 期限切れ明細行の検索 |
| 通知作成 | 通知テーブル | INSERT | 通知レコードの作成 |

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

#### order_purchaseorder

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | target_date, status | target_date=yesterday, status IN OPEN | 期限切れ発注書の検索 |

#### order_purchaseorderlineitem

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | target_date, order, received, quantity | target_date=yesterday, order.status IN OPEN, received < quantity | 未受領の期限切れ明細 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | DatabaseError | データベース接続エラー | 例外発生、リトライ |
| - | 通知送信エラー | 通知処理中のエラー | エラーログ出力して継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Django-Qのデフォルト設定に依存 |
| リトライ間隔 | Django-Qのデフォルト設定に依存 |
| リトライ対象エラー | 一時的なデータベースエラー |

### 障害時対応

1. ログを確認してエラー原因を特定
2. データベース接続状態を確認
3. 必要に応じて手動で再実行

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 読み取り専用（通知は別処理） |
| コミットタイミング | - |
| ロールバック条件 | - |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 数件〜数十件/日 |
| 目標処理時間 | 1分以内 |
| メモリ使用量上限 | 特に制限なし |

## 排他制御

- 読み取り専用のため同時実行可能
- ただし、通常は日次1回の実行のため同時実行は想定しない

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| なし | - | 本バッチは明示的なログ出力を行わない |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| タスク失敗 | 1回 | システム管理者 |
| 処理時間 | 5分超過 | システム管理者 |

## 備考

- 「昨日」を基準にすることで、期限切れとなった時点で通知が送信される設計
- 発注書レベルと明細行レベルの両方で期限切れをチェック
- received < quantityで未受領を判定（部分受領も期限切れ対象）
- notify_overdue_purchase_order()は以下の宛先に通知を送信:
  - created_by: 発注書作成者
  - responsible: 担当者
  - subscribed_users(): 購読者
- メールテンプレート: email/overdue_purchase_order.html
- プラグインイベント: PurchaseOrderEvents.OVERDUE
- OpenTelemetryによるトレーシングに対応
- PurchaseOrderStatusGroups.OPENには保留中・処理中などのオープン状態が含まれる
