# 機能設計書 80-ロケーション住所管理

## 概要

本ドキュメントは、Medusaコマースプラットフォームにおける在庫ロケーションの住所情報管理機能について記載する。

### 本機能の処理概要

ロケーション住所管理機能は、在庫ロケーション（倉庫・店舗等）の物理的な所在地情報を管理する機能である。住所の作成・更新・削除を行い、ロケーションと1対1で紐付ける。

**業務上の目的・背景**：
ECサイトの在庫管理において、倉庫や店舗などの物理的な拠点の所在地情報は、配送計画や在庫配置の最適化に不可欠である。本機能は、各ロケーションに対して住所情報を紐付け、フルフィルメント業務を支援する。

**機能の利用シーン**：
- 新規倉庫設立時の住所登録
- 店舗移転時の住所更新
- 拠点閉鎖時の住所情報削除
- 配送ルート計画時の住所参照

**主要な処理内容**：
1. 住所情報の作成（ロケーション作成時）
2. 住所情報の更新（ロケーション更新時）
3. 住所情報のUpsert（存在確認と作成/更新の自動判定）
4. 住所情報の削除（ロケーション削除時のカスケード）

**関連システム・外部連携**：
- 在庫ロケーションモジュール
- フルフィルメントモジュール

**権限による制御**：
- Admin APIを通じた操作のみ許可
- 認証済み管理者のみアクセス可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 131 | ロケーション作成画面 | 補助機能 | ロケーション住所の設定 |
| 133 | ロケーション編集画面 | 補助機能 | 住所情報の編集 |

## 機能種別

データ管理 / 住所エンティティ管理

## 入力仕様

### 住所作成・更新パラメータ（UpsertStockLocationAddressInput）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| id | string | No | 住所ID（更新時は必須） |
| address_1 | string | Yes | 住所1（主要住所） |
| address_2 | string | No | 住所2（建物名等） |
| company | string | No | 会社名 |
| city | string | No | 市区町村 |
| country_code | string | Yes | 国コード |
| phone | string | No | 電話番号 |
| province | string | No | 都道府県 |
| postal_code | string | No | 郵便番号 |
| metadata | object | No | メタデータ（任意のJSON） |

### 入力データソース

- Admin API（ロケーション作成/更新エンドポイント経由）
- Workflow内部呼び出し

## 出力仕様

### 出力データ（StockLocationAddressDTO）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | 住所ID（laddr_プレフィックス） |
| address_1 | string | 住所1 |
| address_2 | string | 住所2 |
| company | string | 会社名 |
| city | string | 市区町村 |
| country_code | string | 国コード |
| phone | string | 電話番号 |
| province | string | 都道府県 |
| postal_code | string | 郵便番号 |
| metadata | object | メタデータ |
| created_at | Date | 作成日時 |
| updated_at | Date | 更新日時 |

### 出力先

- API レスポンス（ロケーション情報の一部として）
- ワークフロー処理結果

## 処理フロー

### 処理シーケンス

```
【住所Upsert処理】
1. 入力データ受信
   └─ UpsertStockLocationAddressInput[]
2. ID存在チェック
   └─ idがあれば更新対象、なければ作成対象に振り分け
3. 既存データ取得（更新時）
   └─ listStockLocationAddresses(ids)
4. 並列実行
   ├─ 新規作成: stockLocationAddressService_.create()
   └─ 更新: stockLocationAddressService_.update()
5. 結果マージ
   └─ 作成と更新の結果を結合して返却

【ロケーション更新時の住所処理】
1. ロケーション取得
   └─ useQueryGraphStep(id, address.id)
2. 住所入力の正規化
   └─ 既存address.idがあれば更新、なければ新規作成
3. 住所Upsert実行
   └─ upsertStockLocationAddressesStep()
4. ロケーション更新
   └─ updateStockLocationsStep()
```

### フローチャート

```mermaid
flowchart TD
    A[住所データ入力] --> B{IDあり?}
    B -->|Yes| C[更新対象に分類]
    B -->|No| D[作成対象に分類]
    C --> E[既存データ取得]
    D --> F[作成処理]
    E --> G[更新処理]
    F --> H[結果マージ]
    G --> H
    H --> I[シリアライズ]
    I --> J[結果返却]

    K[ロケーション更新リクエスト] --> L[既存ロケーション取得]
    L --> M{address入力あり?}
    M -->|No| N[ロケーション更新のみ]
    M -->|Yes| O{既存address.idあり?}
    O -->|Yes| P[更新用データ作成]
    O -->|No| Q[新規作成用データ作成]
    P --> R[upsertStockLocationAddressesStep]
    Q --> R
    R --> S[updateStockLocationsStep]
    N --> S
    S --> T[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-80-01 | 1対1関係 | 1ロケーションに対し1住所のみ設定可能 | 住所設定時 |
| BR-80-02 | IDによるUpsert判定 | IDありは更新、なしは作成 | Upsert処理時 |
| BR-80-03 | カスケード削除 | ロケーション削除時に住所も削除 | ロケーション削除時 |
| BR-80-04 | 必須フィールド | address_1とcountry_codeは必須 | 作成時 |
| BR-80-05 | 検索可能フィールド | address_1, city, country_code, province, postal_codeは検索対象 | 検索時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 住所作成 | stock_location_address | INSERT | 住所レコード作成 |
| 住所更新 | stock_location_address | UPDATE | 住所情報更新 |
| 住所削除 | stock_location_address | DELETE | 住所レコード削除 |
| 住所一覧取得 | stock_location_address | SELECT | ID指定で取得 |

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

#### stock_location_address

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | 全フィールド | 入力値 | id自動生成（laddr_プレフィックス） |
| UPDATE | address_1, address_2, company, city, country_code, phone, province, postal_code, metadata | 入力値 | id指定 |
| DELETE | - | id指定 | ロケーション削除時のカスケード |
| SELECT | * | id IN (ids) | 更新前データ取得用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | NOT_FOUND | 更新対象の住所が存在しない | 正しいIDを指定 |
| - | INVALID_DATA | 必須フィールド未設定 | address_1, country_codeを設定 |
| - | DB_ERROR | データベース制約違反 | 入力データの確認 |

### リトライ仕様

- トランザクション失敗時はワークフローの補償アクションで復旧
- 補償アクション：作成したものは削除、更新したものは元データに復元

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

- Upsert処理はトランザクション内で実行（@InjectTransactionManager）
- 作成と更新は並列実行（promiseAll）
- ロケーション更新ワークフローでは住所Upsertとロケーション更新が順次実行

## パフォーマンス要件

- 住所操作は単一ロケーションに対して1件のため低負荷
- 一括更新時は並列処理で効率化

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

- 住所情報は個人情報に該当する可能性あり
- Admin API経由のみでアクセス可能
- 適切なアクセス制御の実施

## 備考

- 住所IDは「laddr_」プレフィックスで自動生成される
- StockLocationAddressモデルはcascades設定によりstock_locationsの削除を伝播
- searchable設定により、住所フィールドでの検索が可能

---

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

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

### 推奨読解順序

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

住所モデルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | stock-location-address.ts | `packages/modules/stock-location/src/models/stock-location-address.ts` | 住所モデル定義 |

**読解のコツ**:
- **6行目**: idプレフィックス「laddr」の設定
- **7行目**: address_1が searchable設定
- **11行目**: country_codeが searchable設定（必須）
- **16-18行目**: stock_locationsとのhasOne関係（mappedBy: "address"）
- **20-22行目**: cascades設定でstock_locationsの削除を伝播

#### Step 2: サービス層を理解する

StockLocationModuleServiceの住所関連メソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stock-location-module.ts | `packages/modules/stock-location/src/services/stock-location-module.ts` | モジュールサービス |

**主要処理フロー**:
1. **54-56行目**: stockLocationAddressService_の定義
2. **237-274行目**: updateStockLocationAddresses - 住所更新処理
3. **276-302行目**: upsertStockLocationAddresses - Upsert処理（公開メソッド）
4. **304-333行目**: upsertStockLocationAddresses_ - Upsert内部実装

**Upsertロジック詳細（309-332行目）**:
```
1. input.filter(location => !!location.id) → toUpdate配列
2. input.filter(location => !location.id) → toCreate配列
3. toCreate.length > 0 → stockLocationAddressService_.create()
4. toUpdate.length > 0 → stockLocationAddressService_.update()
5. promiseAll(operations).flat() → 結果マージ
```

#### Step 3: ワークフローステップを理解する

住所Upsertのワークフローステップを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | upsert-stock-location-addresses.ts | `packages/core/core-flows/src/stock-location/steps/upsert-stock-location-addresses.ts` | Upsertステップ |

**主要処理フロー**:
1. **30行目**: 入力から既存IDを抽出
2. **33-36行目**: 既存データの取得（更新前の状態保存）
3. **40-41行目**: upsertStockLocationAddresses呼び出し
4. **43-45行目**: 新規作成分の特定（補償用）
5. **47行目**: StepResponse返却（dataToUpdate, dataToDelete）

**補償アクション（49-77行目）**:
1. **60-66行目**: 新規作成分の削除（deleteStockLocationAddresses）
2. **68-74行目**: 更新分の復元（元データでupsert）

#### Step 4: 更新ワークフローとの連携を理解する

ロケーション更新時の住所処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update-stock-locations.ts | `packages/core/core-flows/src/stock-location/workflows/update-stock-locations.ts` | 更新ワークフロー |

**主要処理フロー**:
1. **62-66行目**: 既存ロケーションとaddress.idの取得
2. **73-100行目**: 住所入力の正規化（transform）
   - **76行目**: address, address_idをupdate入力から分離
   - **79-90行目**: 各ロケーションに対して住所データ準備
   - **81-85行目**: 既存address.idがあれば更新用、なければ新規作成用
3. **102行目**: upsertStockLocationAddressesStep実行
4. **104-106行目**: updateStockLocationsStep実行

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

```
Admin API (POST /admin/stock-locations/{id})
    │
    └─ updateStockLocationsWorkflow
           │
           ├─ useQueryGraphStep
           │      └─ 既存ロケーション + address.id取得
           │
           ├─ transform (normalizedData)
           │      └─ addressesInput配列の構築
           │
           ├─ upsertStockLocationAddressesStep
           │      │
           │      └─ StockLocationModuleService.upsertStockLocationAddresses()
           │             │
           │             ├─ stockLocationAddressService_.create()
           │             │      └─ 新規住所作成
           │             │
           │             └─ stockLocationAddressService_.update()
           │                    └─ 既存住所更新
           │
           └─ updateStockLocationsStep
                  └─ ロケーション情報の更新
```

### データフロー図

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

StockLocation更新             transform
リクエスト                       │
  │                            ├─ address抽出
  ├─ name                      │
  ├─ address ────────────>     ├─ 既存address.id確認
  │   ├─ address_1             │     │
  │   ├─ city                  │     ├─ あり → {id, ...address}
  │   └─ country_code          │     └─ なし → {...address}
  │                            │
  └─ ...                       └─ addressesInput[]
                                         │
                                         v
                               upsertStockLocationAddressesStep
                                         │
                                         ├─ ID判定
                                         │   ├─ IDあり → toUpdate[]
                                         │   └─ IDなし → toCreate[]
                                         │
                                         ├─ promiseAll
                                         │   ├─ create(toCreate)
                                         │   └─ update(toUpdate)
                                         │
                                         v
                               StockLocationAddressDTO[]
                                         │
                                         v
                               stock_location_address テーブル
                                 ├─ id (laddr_xxx)
                                 ├─ address_1
                                 ├─ address_2
                                 ├─ city
                                 ├─ country_code
                                 ├─ province
                                 ├─ postal_code
                                 └─ ...
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stock-location-address.ts | `packages/modules/stock-location/src/models/stock-location-address.ts` | ソース | 住所モデル定義 |
| stock-location-module.ts | `packages/modules/stock-location/src/services/stock-location-module.ts` | ソース | モジュールサービス（住所CRUD） |
| upsert-stock-location-addresses.ts | `packages/core/core-flows/src/stock-location/steps/upsert-stock-location-addresses.ts` | ソース | Upsertワークフローステップ |
| update-stock-locations.ts | `packages/core/core-flows/src/stock-location/workflows/update-stock-locations.ts` | ソース | 更新ワークフロー |
| stock-location.ts | `packages/modules/stock-location/src/models/stock-location.ts` | ソース | ロケーションモデル（住所との関係） |
| types/index.ts | `packages/core/types/src/stock-location/index.ts` | ソース | 型定義 |
