# 機能設計書 71-請求書ダウンロード

## 概要

本ドキュメントは、Shopwareストアフロントにおける請求書ダウンロード機能の設計を記載する。顧客が自身の注文に関連するPDF請求書やその他のドキュメントをダウンロードできる機能である。

### 本機能の処理概要

請求書ダウンロード機能は、顧客がマイアカウントの注文履歴から請求書や納品書などのドキュメントをダウンロードできる機能である。ログイン済みユーザーだけでなく、ゲスト注文の場合もdeepLinkCodeを利用してドキュメントにアクセス可能。

**業務上の目的・背景**：ECサイトにおいて、顧客は注文後に請求書や納品書を必要とすることが多い。経費精算、確定申告、企業間取引における証憑として利用されるため、顧客が自由にダウンロードできる機能は必須である。紙の郵送を削減し、即座にドキュメントを取得できることで顧客満足度と業務効率の両方を向上させる。

**機能の利用シーン**：
- 顧客がマイアカウントの注文履歴から請求書をダウンロードする
- ゲスト顧客がメールのリンクから請求書にアクセスする
- 企業顧客が経理処理のために複数の請求書を取得する
- アクセシビリティ対応としてHTML形式のドキュメントを取得する

**主要な処理内容**：
1. ユーザー認証の確認（ログインユーザーまたはゲスト認証）
2. ドキュメントIDとdeepLinkCodeによるドキュメント検索
3. 注文と顧客の紐付け検証
4. ドキュメントファイルの読み込みまたは動的生成
5. PDF/HTML形式でのレスポンス返却

**関連システム・外部連携**：
- メディアサービス（ファイルストレージ）
- ドキュメント生成サービス（PDF/HTML/ZUGFeRD生成）
- 認証サービス（ログイン/ゲスト認証）

**権限による制御**：
- ログイン済み顧客：自身の注文に紐づくドキュメントのみアクセス可能
- ゲスト顧客：deepLinkCode、メールアドレス、郵便番号による認証が必要
- 他の顧客の注文ドキュメントへのアクセスは禁止

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | 注文履歴ページ | 主画面 | 注文一覧からドキュメントダウンロードリンクをクリック |
| 60 | ドキュメント設定 | 参照画面 | 請求書・納品書のテンプレート設定（管理画面） |

## 機能種別

帳票出力 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| documentId | string (UUID) | Yes | ダウンロード対象のドキュメントID | UUIDフォーマット |
| deepLinkCode | string | No（ゲストの場合は必須） | ドキュメントへの直接アクセス用コード | 32文字の英数字 |
| fileType | string | No | ファイル形式（pdf/html/xml） | pdf, html, xmlのいずれか |
| download | boolean | No | ダウンロードフラグ（true: 添付ファイル / false: インライン表示） | true/false |
| email | string | No（ゲストの場合は必須） | ゲスト認証用メールアドレス | メールアドレス形式 |
| zipcode | string | No（ゲストの場合は必須） | ゲスト認証用郵便番号 | 文字列 |

### 入力データソース

- URLパスパラメータ：documentId, deepLinkCode, fileType
- クエリパラメータ：download, fileType
- リクエストパラメータ：email, zipcode（ゲスト認証時）
- セッション：ログイン顧客情報
- HTTPヘッダー：Accept（ファイル形式の優先順位）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content | binary | ドキュメントのバイナリデータ（PDF/HTML/XML） |
| Content-Type | string | MIMEタイプ（application/pdf, text/html, application/xml） |
| Content-Disposition | string | ファイル名とダウンロード/インライン表示指定 |

### 出力先

HTTPレスポンス（ファイルダウンロードまたはインライン表示）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ DocumentController::downloadDocument がリクエストを受け取る

2. ファイル形式の決定
   └─ fileTypeパラメータまたはAcceptヘッダーからファイル形式を決定

3. 認証チェック
   ├─ ログインユーザーの場合
   │   └─ 顧客IDと注文の紐付けを確認
   └─ ゲストユーザーの場合
       └─ deepLinkCode, email, zipcodeで認証

4. ドキュメント取得
   ├─ documentIdとdeepLinkCodeでドキュメントを検索
   ├─ メディアファイルが存在する場合
   │   └─ ファイルストレージからコンテンツを取得
   └─ メディアファイルが存在しない場合（動的生成）
       └─ DocumentGeneratorでドキュメントを生成

5. レスポンス生成
   └─ Content-Type, Content-Dispositionヘッダーを設定して返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{ログイン済み?}
    B -->|Yes| C[顧客ID確認]
    B -->|No| D{deepLinkCode有り?}
    D -->|Yes| E[ゲスト認証]
    D -->|No| F[エラー: 未認証]
    E -->|成功| G[ドキュメント検索]
    E -->|失敗| H[ゲストログインページへリダイレクト]
    C -->|一致| G
    C -->|不一致| F
    G --> I{ドキュメント存在?}
    I -->|Yes| J{メディアファイル存在?}
    I -->|No| K[エラー: ドキュメント未発見]
    J -->|Yes| L[ファイル読み込み]
    J -->|No| M{静的ドキュメント?}
    M -->|Yes| N[エラー: ファイル未発見]
    M -->|No| O[ドキュメント動的生成]
    L --> P[レスポンス返却]
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-71-01 | アクセス権限 | 顧客は自身の注文に紐づくドキュメントのみアクセス可能 | 全てのダウンロードリクエスト |
| BR-71-02 | ゲスト認証 | ゲスト顧客はdeepLinkCode、メールアドレス、郵便番号が必要 | ゲストユーザーのアクセス |
| BR-71-03 | ファイル形式 | PDF、HTML、XMLの3形式をサポート | ファイル形式指定時 |
| BR-71-04 | デフォルト形式 | ファイル形式未指定時はPDFをデフォルトとする | fileType未指定時 |
| BR-71-05 | 動的生成 | 静的でないドキュメントはリクエスト時に動的生成可能 | メディアファイル未存在時 |

### 計算ロジック

特になし（ドキュメントの生成ロジックはドキュメントジェネレーターに委譲）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ドキュメント検索 | document | SELECT | documentIdとdeepLinkCodeで検索 |
| 注文情報取得 | order | SELECT | 注文と顧客の紐付け確認 |
| メディア取得 | media | SELECT | ドキュメントファイルの取得 |
| ドキュメント更新 | document | UPDATE | 動的生成時のメディアファイルID更新 |

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

#### document

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, deep_link_code, document_media_file_id, document_a11y_media_file_id | WHERE id = :documentId AND deep_link_code = :deepLinkCode | 主キーとdeepLinkCodeで検索 |
| UPDATE | document_media_file_id, document_a11y_media_file_id | 動的生成されたメディアファイルID | 動的生成時のみ |

#### order

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, order_customer_id, billing_address | document.order_idと結合 | 認証確認用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DOCUMENT_NOT_FOUND | ビジネスエラー | 指定されたdocumentIdのドキュメントが存在しない | 404エラーを返却 |
| CUSTOMER_NOT_LOGGED_IN | 認証エラー | ログインしていない状態でアクセス | ログインページへリダイレクト |
| GUEST_NOT_AUTHENTICATED | 認証エラー | ゲストユーザーの認証情報が不足 | ゲストログインページへリダイレクト |
| WRONG_GUEST_CREDENTIALS | 認証エラー | ゲストの認証情報が不正 | エラーメッセージを表示しゲストログインページへ |
| CUSTOMER_AUTH_THROTTLED | 認証エラー | 認証試行回数の上限超過 | 待機時間を表示 |
| FILE_TYPE_NOT_SUPPORTED | バリデーションエラー | サポートされていないファイル形式 | 406エラーを返却 |

### リトライ仕様

- 認証試行：スロットリングにより一定時間後に再試行可能
- ファイル読み込み：システムエラー時は再試行不可（再リクエストが必要）

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

- ドキュメントの動的生成時にトランザクションを使用
- ドキュメントレコードの更新とメディアファイルの保存を一括でコミット
- エラー発生時はロールバック

## パフォーマンス要件

- ドキュメントダウンロード：3秒以内
- PDF動的生成：5秒以内
- 同時リクエスト：100件/秒

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

- **認証**：ログインユーザーまたはゲスト認証が必須
- **認可**：自身の注文に紐づくドキュメントのみアクセス可能
- **deepLinkCode**：32文字のランダム英数字で推測困難
- **スロットリング**：ゲスト認証の試行回数を制限
- **ファイルパス**：ファイル名の特殊文字をサニタイズ

## 備考

- ZUGFeRDフォーマット（XML）のサポートはドイツの電子請求書規格対応
- アクセシビリティ対応としてHTML形式のドキュメントを提供
- 静的ドキュメント（手動アップロード）と動的ドキュメント（自動生成）の両方をサポート

---

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

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

### 推奨読解順序

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

まず、ドキュメントエンティティとレスポンスの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RenderedDocument.php | `src/Core/Checkout/Document/Renderer/RenderedDocument.php` | ドキュメントの内部表現（名前、コンテンツ、ファイル形式） |
| 1-2 | DocumentEntity.php | `src/Core/Checkout/Document/DocumentEntity.php` | ドキュメントのDBエンティティ構造 |

**読解のコツ**: RenderedDocumentはファイル出力用の一時的なオブジェクト、DocumentEntityはDB永続化されたエンティティという違いを意識する。

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

処理の起点となるコントローラーを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DocumentController.php | `src/Storefront/Controller/DocumentController.php` | ストアフロント側のエントリーポイント |

**主要処理フロー**:
1. **38-52行目**: ルート定義（`/account/order/document/{documentId}/{deepLinkCode}`）
2. **53-84行目**: `downloadDocument`メソッド - リクエスト処理、例外ハンドリング、リダイレクト処理
3. **55行目**: ファイルタイプの決定（パラメータまたはデフォルトPDF）
4. **58行目**: `documentRoute->download`の呼び出し

#### Step 3: Store-APIルートを理解する

実際のドキュメント取得ロジックはStore-APIルートに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractDocumentRoute.php | `src/Core/Checkout/Document/SalesChannel/AbstractDocumentRoute.php` | 抽象クラス定義 |
| 3-2 | DocumentRoute.php | `src/Core/Checkout/Document/SalesChannel/DocumentRoute.php` | 実装クラス |

**主要処理フロー**:
- **62-108行目**: `download`メソッド
- **69行目**: 認証チェック（`checkAuth`）
- **78行目**: リクエストからファイル形式を解決
- **80-85行目**: ドキュメントファイルの読み込み
- **102-107行目**: レスポンス生成

#### Step 4: ドキュメント生成サービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DocumentGenerator.php | `src/Core/Checkout/Document/Service/DocumentGenerator.php` | ドキュメント生成・読み込みサービス |

**主要処理フロー**:
- **49-87行目**: `readDocument`メソッド - ドキュメントの読み込み
- **71行目**: メディアファイルが存在しない場合の動的生成
- **77行目**: メディアサービスからファイル内容を取得
- **89-107行目**: `preview`メソッド - プレビュー生成
- **112-171行目**: `generate`メソッド - ドキュメントの新規生成

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

```
DocumentController::downloadDocument (Storefront)
    │
    ├─ DocumentRoute::download (Store-API)
    │      │
    │      ├─ DocumentRoute::checkAuth
    │      │      └─ GuestAuthenticator::validate
    │      │
    │      ├─ DocumentRoute::resolveRequest
    │      │      └─ ファイル形式の決定
    │      │
    │      └─ DocumentRoute::readDocument
    │             │
    │             └─ DocumentGenerator::readDocument
    │                    │
    │                    ├─ documentRepository::search
    │                    │
    │                    ├─ ensureDocumentMediaFileGenerated
    │                    │      └─ DocumentGenerator::generate (動的生成時)
    │                    │
    │                    └─ MediaService::loadFile
    │
    └─ 例外ハンドリング
           └─ ゲストログインページへリダイレクト
```

### データフロー図

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

documentId ─────────┐
                    │
deepLinkCode ───────┼──▶ DocumentController    ───▶ DocumentRoute
                    │         │                         │
fileType ───────────┤         │                         ▼
                    │         │                  DocumentGenerator
SalesChannelContext─┘         │                         │
                              │                         ├──▶ DB: document
                              │                         │      (SELECT)
                              │                         │
                              │                         ├──▶ FileStorage
                              │                         │      (READ)
                              ▼                         │
                         Response ◀─────────────────────┘
                    (PDF/HTML/XML binary)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DocumentController.php | `src/Storefront/Controller/DocumentController.php` | コントローラー | ストアフロントのエントリーポイント |
| AbstractDocumentRoute.php | `src/Core/Checkout/Document/SalesChannel/AbstractDocumentRoute.php` | 抽象クラス | ルートの抽象定義 |
| DocumentRoute.php | `src/Core/Checkout/Document/SalesChannel/DocumentRoute.php` | ルート | Store-APIルート実装 |
| DocumentGenerator.php | `src/Core/Checkout/Document/Service/DocumentGenerator.php` | サービス | ドキュメント生成・読み込み |
| DocumentEntity.php | `src/Core/Checkout/Document/DocumentEntity.php` | エンティティ | ドキュメントDB構造 |
| RenderedDocument.php | `src/Core/Checkout/Document/Renderer/RenderedDocument.php` | DTO | 出力用ドキュメント構造 |
| PdfRenderer.php | `src/Core/Checkout/Document/Service/PdfRenderer.php` | レンダラー | PDF生成 |
| HtmlRenderer.php | `src/Core/Checkout/Document/Service/HtmlRenderer.php` | レンダラー | HTML生成（A11y対応） |
| GuestAuthenticator.php | `src/Core/Checkout/Customer/Service/GuestAuthenticator.php` | サービス | ゲスト認証 |
| MediaService.php | `src/Core/Content/Media/MediaService.php` | サービス | ファイルストレージ操作 |
